mirror of
https://github.com/HolgerHatGarKeineNode/einundzwanzig-nostr.git
synced 2026-01-28 07:43:18 +00:00
🎨 Add new Flux icons: implement multiple reusable icon components (e.g., hand-raised, hand-thumb-up, heart, hashtag, home) with variant support for improved UI consistency.
This commit is contained in:
128
videos/src/scenes/InputDemoScene.tsx
Normal file
128
videos/src/scenes/InputDemoScene.tsx
Normal file
@@ -0,0 +1,128 @@
|
||||
import { AbsoluteFill, interpolate, spring, useCurrentFrame, useVideoConfig, Img, staticFile } from "remotion";
|
||||
import { BitcoinEffect } from "../components/BitcoinEffect";
|
||||
import { AnimatedLogo } from "../components/AnimatedLogo";
|
||||
|
||||
export const InputDemoScene: React.FC = () => {
|
||||
const frame = useCurrentFrame();
|
||||
const { fps } = useVideoConfig();
|
||||
|
||||
// Typing animation
|
||||
const typingText = "satoshi21";
|
||||
const typingProgress = interpolate(frame, [0, 2 * fps], [0, typingText.length], {
|
||||
extrapolateRight: "clamp",
|
||||
});
|
||||
const currentText = typingText.slice(0, Math.floor(typingProgress));
|
||||
|
||||
// Cursor blink
|
||||
const cursorBlink = Math.floor(frame / 15) % 2 === 0;
|
||||
|
||||
// Card entrance
|
||||
const cardSpring = spring({
|
||||
frame,
|
||||
fps,
|
||||
config: { damping: 200 },
|
||||
});
|
||||
const cardY = interpolate(cardSpring, [0, 1], [100, 0]);
|
||||
const cardOpacity = interpolate(cardSpring, [0, 1], [0, 1]);
|
||||
|
||||
|
||||
// Rules box appears after typing
|
||||
const rulesSpring = spring({
|
||||
frame: frame - 2.5 * fps,
|
||||
fps,
|
||||
config: { damping: 200 },
|
||||
});
|
||||
const rulesOpacity = interpolate(rulesSpring, [0, 1], [0, 1]);
|
||||
const rulesY = interpolate(rulesSpring, [0, 1], [30, 0]);
|
||||
|
||||
// Pointer animation
|
||||
const pointerSpring = spring({
|
||||
frame: frame - 0.5 * fps,
|
||||
fps,
|
||||
config: { damping: 15, stiffness: 100 },
|
||||
});
|
||||
const pointerX = interpolate(pointerSpring, [0, 1], [-100, 0]);
|
||||
|
||||
return (
|
||||
<AbsoluteFill>
|
||||
{/* Wallpaper Background */}
|
||||
<Img
|
||||
src={staticFile("einundzwanzig-wallpaper.png")}
|
||||
className="absolute inset-0 w-full h-full object-cover"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-neutral-950/75" />
|
||||
|
||||
{/* Bitcoin Effect */}
|
||||
<BitcoinEffect />
|
||||
|
||||
{/* Animated EINUNDZWANZIG Logo */}
|
||||
<div className="absolute top-20 left-20">
|
||||
<AnimatedLogo size={250} delay={0.5 * fps} />
|
||||
</div>
|
||||
|
||||
<div className="absolute inset-0 flex flex-col items-center justify-center px-12">
|
||||
<h2 className="text-5xl font-bold text-white mb-16 text-center">
|
||||
Schritt 1: Handle eingeben
|
||||
</h2>
|
||||
|
||||
{/* Input Card */}
|
||||
<div
|
||||
className="bg-white rounded-2xl p-10 shadow-2xl max-w-3xl w-full relative"
|
||||
style={{
|
||||
opacity: cardOpacity,
|
||||
transform: `translateY(${cardY}px)`,
|
||||
}}
|
||||
>
|
||||
{/* Animated Pointer */}
|
||||
<div
|
||||
className="absolute -left-20 top-1/2 -translate-y-1/2 text-6xl"
|
||||
style={{
|
||||
transform: `translateX(${pointerX}px) translateY(-50%)`,
|
||||
}}
|
||||
>
|
||||
👉
|
||||
</div>
|
||||
|
||||
<label className="block text-xl font-medium text-neutral-700 mb-4">
|
||||
Dein NIP-05 Handle
|
||||
</label>
|
||||
<div className="flex items-center bg-white rounded-lg border-4 border-neutral-800 px-6 py-4 mb-6">
|
||||
<div className="flex-1 text-2xl text-neutral-800">
|
||||
{currentText}
|
||||
{cursorBlink && frame < 2.5 * fps && <span className="text-neutral-800">|</span>}
|
||||
</div>
|
||||
<div className="text-xl text-neutral-600 font-medium">@einundzwanzig.space</div>
|
||||
</div>
|
||||
|
||||
{/* Rules Box */}
|
||||
<div
|
||||
className="bg-neutral-100 rounded-lg border-2 border-neutral-300 p-5"
|
||||
style={{
|
||||
opacity: rulesOpacity,
|
||||
transform: `translateY(${rulesY}px)`,
|
||||
}}
|
||||
>
|
||||
<p className="text-sm text-neutral-700 font-medium mb-2">
|
||||
✅ Regeln für dein Handle:
|
||||
</p>
|
||||
<ul className="text-sm text-neutral-600 space-y-1">
|
||||
<li>• Nur Kleinbuchstaben (a-z)</li>
|
||||
<li>• Zahlen (0-9)</li>
|
||||
<li>• Zeichen: "-" und "_"</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Info text */}
|
||||
<p
|
||||
className="text-neutral-300 text-center mt-8 text-lg max-w-2xl"
|
||||
style={{
|
||||
opacity: rulesOpacity,
|
||||
}}
|
||||
>
|
||||
Dein Handle wird automatisch kleingeschrieben und muss einzigartig sein.
|
||||
</p>
|
||||
</div>
|
||||
</AbsoluteFill>
|
||||
);
|
||||
};
|
||||
330
videos/src/scenes/IntroScene.tsx
Normal file
330
videos/src/scenes/IntroScene.tsx
Normal file
@@ -0,0 +1,330 @@
|
||||
import { AbsoluteFill, interpolate, spring, useCurrentFrame, useVideoConfig, Img, staticFile } from "remotion";
|
||||
import { BitcoinEffect } from "../components/BitcoinEffect";
|
||||
import { AnimatedLogo } from "../components/AnimatedLogo";
|
||||
|
||||
// Lightning Payment Animation Component
|
||||
const LightningPaymentAnimation: React.FC<{ frame: number; fps: number; startFrame: number }> = ({
|
||||
frame,
|
||||
fps,
|
||||
startFrame,
|
||||
}) => {
|
||||
const relativeFrame = frame - startFrame;
|
||||
|
||||
// Only show after step 2 appears
|
||||
if (relativeFrame < 0.5 * fps) return null;
|
||||
|
||||
// Lightning bolt travel animation
|
||||
const boltProgress = interpolate(
|
||||
relativeFrame,
|
||||
[0.5 * fps, 1.5 * fps],
|
||||
[0, 1],
|
||||
{ extrapolateLeft: "clamp", extrapolateRight: "clamp" }
|
||||
);
|
||||
|
||||
// Flash effect when payment completes
|
||||
const flashOpacity = interpolate(
|
||||
relativeFrame,
|
||||
[1.4 * fps, 1.5 * fps, 1.8 * fps],
|
||||
[0, 0.8, 0],
|
||||
{ extrapolateLeft: "clamp", extrapolateRight: "clamp" }
|
||||
);
|
||||
|
||||
// Success checkmark appears after flash
|
||||
const checkSpring = spring({
|
||||
frame: relativeFrame - 1.6 * fps,
|
||||
fps,
|
||||
config: { damping: 12, stiffness: 150 },
|
||||
});
|
||||
const checkScale = interpolate(checkSpring, [0, 1], [0, 1]);
|
||||
|
||||
// Bolt position along the path
|
||||
const boltX = interpolate(boltProgress, [0, 1], [-60, 60]);
|
||||
const boltY = interpolate(boltProgress, [0, 0.5, 1], [0, -15, 0]);
|
||||
|
||||
// Bolt wobble
|
||||
const boltRotation = interpolate(
|
||||
Math.sin(relativeFrame * 0.4),
|
||||
[-1, 1],
|
||||
[-15, 15]
|
||||
);
|
||||
|
||||
// Bolt scale pulse
|
||||
const boltScale = interpolate(
|
||||
Math.sin(relativeFrame * 0.3),
|
||||
[-1, 1],
|
||||
[1, 1.3]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="absolute inset-0 flex items-center justify-center pointer-events-none">
|
||||
{/* Lightning bolt traveling */}
|
||||
{boltProgress < 1 && (
|
||||
<div
|
||||
className="absolute text-4xl"
|
||||
style={{
|
||||
transform: `translateX(${boltX}px) translateY(${boltY}px) rotate(${boltRotation}deg) scale(${boltScale})`,
|
||||
filter: "drop-shadow(0 0 10px #f7931a) drop-shadow(0 0 20px #ffcc00)",
|
||||
}}
|
||||
>
|
||||
⚡
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Flash effect */}
|
||||
<div
|
||||
className="absolute inset-0 bg-yellow-400"
|
||||
style={{ opacity: flashOpacity }}
|
||||
/>
|
||||
|
||||
{/* Success indicator */}
|
||||
{checkScale > 0 && (
|
||||
<div
|
||||
className="absolute right-4 top-1/2 -translate-y-1/2 w-10 h-10 rounded-full bg-green-500 flex items-center justify-center"
|
||||
style={{
|
||||
transform: `translateY(-50%) scale(${checkScale})`,
|
||||
boxShadow: "0 0 15px rgba(34, 197, 94, 0.6)",
|
||||
}}
|
||||
>
|
||||
<span className="text-white text-xl font-bold">✓</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const IntroScene: React.FC = () => {
|
||||
const frame = useCurrentFrame();
|
||||
const { fps } = useVideoConfig();
|
||||
|
||||
// Logo entrance
|
||||
const logoSpring = spring({
|
||||
frame,
|
||||
fps,
|
||||
config: { damping: 200 },
|
||||
});
|
||||
const logoOpacity = interpolate(logoSpring, [0, 1], [0, 1]);
|
||||
const logoScale = interpolate(logoSpring, [0, 1], [0.5, 1]);
|
||||
|
||||
// Main title
|
||||
const titleSpring = spring({
|
||||
frame: frame - 0.5 * fps,
|
||||
fps,
|
||||
config: { damping: 200 },
|
||||
});
|
||||
const titleOpacity = interpolate(titleSpring, [0, 1], [0, 1]);
|
||||
const titleY = interpolate(titleSpring, [0, 1], [30, 0]);
|
||||
|
||||
// Subtitle
|
||||
const subtitleSpring = spring({
|
||||
frame: frame - 1 * fps,
|
||||
fps,
|
||||
config: { damping: 200 },
|
||||
});
|
||||
const subtitleOpacity = interpolate(subtitleSpring, [0, 1], [0, 1]);
|
||||
|
||||
// Step 1: Registration
|
||||
const step1Spring = spring({
|
||||
frame: frame - 2 * fps,
|
||||
fps,
|
||||
config: { damping: 15, stiffness: 100 },
|
||||
});
|
||||
const step1Opacity = interpolate(step1Spring, [0, 1], [0, 1]);
|
||||
const step1X = interpolate(step1Spring, [0, 1], [-100, 0]);
|
||||
|
||||
// Step 2: Lightning Payment
|
||||
const step2Spring = spring({
|
||||
frame: frame - 4.5 * fps,
|
||||
fps,
|
||||
config: { damping: 15, stiffness: 100 },
|
||||
});
|
||||
const step2Opacity = interpolate(step2Spring, [0, 1], [0, 1]);
|
||||
const step2X = interpolate(step2Spring, [0, 1], [100, 0]);
|
||||
|
||||
// Step 3: NIP-05 Benefit
|
||||
const step3Spring = spring({
|
||||
frame: frame - 7.5 * fps,
|
||||
fps,
|
||||
config: { damping: 15, stiffness: 100 },
|
||||
});
|
||||
const step3Opacity = interpolate(step3Spring, [0, 1], [0, 1]);
|
||||
const step3Y = interpolate(step3Spring, [0, 1], [50, 0]);
|
||||
|
||||
// Arrow animations connecting steps
|
||||
const arrow1Spring = spring({
|
||||
frame: frame - 3.5 * fps,
|
||||
fps,
|
||||
config: { damping: 200 },
|
||||
});
|
||||
const arrow1Opacity = interpolate(arrow1Spring, [0, 1], [0, 1]);
|
||||
|
||||
const arrow2Spring = spring({
|
||||
frame: frame - 7 * fps,
|
||||
fps,
|
||||
config: { damping: 200 },
|
||||
});
|
||||
const arrow2Opacity = interpolate(arrow2Spring, [0, 1], [0, 1]);
|
||||
|
||||
// Final call to action
|
||||
const ctaSpring = spring({
|
||||
frame: frame - 9.5 * fps,
|
||||
fps,
|
||||
config: { damping: 200 },
|
||||
});
|
||||
const ctaOpacity = interpolate(ctaSpring, [0, 1], [0, 1]);
|
||||
const ctaScale = interpolate(ctaSpring, [0, 1], [0.9, 1]);
|
||||
|
||||
return (
|
||||
<AbsoluteFill>
|
||||
{/* Wallpaper Background */}
|
||||
<Img
|
||||
src={staticFile("einundzwanzig-wallpaper.png")}
|
||||
className="absolute inset-0 w-full h-full object-cover"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-neutral-900/75" />
|
||||
|
||||
{/* Bitcoin Effect */}
|
||||
<BitcoinEffect />
|
||||
|
||||
{/* Header with Logo and Title - Top Left */}
|
||||
<div className="absolute top-6 left-8 flex items-center gap-4">
|
||||
<div
|
||||
style={{
|
||||
opacity: logoOpacity,
|
||||
transform: `scale(${logoScale})`,
|
||||
}}
|
||||
>
|
||||
<AnimatedLogo size={80} delay={0} />
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
opacity: titleOpacity,
|
||||
transform: `translateY(${titleY}px)`,
|
||||
}}
|
||||
>
|
||||
<h1 className="text-3xl font-bold text-white">
|
||||
NIP-05 Verifikation
|
||||
</h1>
|
||||
<p
|
||||
className="text-base text-neutral-300"
|
||||
style={{ opacity: subtitleOpacity }}
|
||||
>
|
||||
So erhältst du deine verifizierte Nostr-Identität
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Steps Container - Larger Steps */}
|
||||
<div className="absolute top-[18%] left-0 right-0 px-8">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
{/* Step 1: Registration */}
|
||||
<div
|
||||
className="flex items-center gap-5 mb-4"
|
||||
style={{
|
||||
opacity: step1Opacity,
|
||||
transform: `translateX(${step1X}px)`,
|
||||
}}
|
||||
>
|
||||
<div className="flex-shrink-0 w-16 h-16 rounded-full bg-orange-500 flex items-center justify-center border-4 border-orange-400 shadow-xl">
|
||||
<span className="text-3xl font-bold text-white">1</span>
|
||||
</div>
|
||||
<div className="flex-1 bg-white/10 backdrop-blur-sm rounded-2xl p-5 border-2 border-white/20 shadow-lg">
|
||||
<h3 className="text-2xl font-bold text-white mb-2 flex items-center gap-3">
|
||||
<span className="text-3xl">📝</span>
|
||||
Registrierung im Verein
|
||||
</h3>
|
||||
<p className="text-lg text-neutral-200">
|
||||
Werde Mitglied bei EINUNDZWANZIG und erhalte Zugang zu allen Vorteilen
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Arrow 1 */}
|
||||
<div
|
||||
className="flex justify-center my-2"
|
||||
style={{ opacity: arrow1Opacity }}
|
||||
>
|
||||
<div className="text-4xl text-orange-500">↓</div>
|
||||
</div>
|
||||
|
||||
{/* Step 2: Lightning Payment */}
|
||||
<div
|
||||
className="flex items-center gap-5 mb-4"
|
||||
style={{
|
||||
opacity: step2Opacity,
|
||||
transform: `translateX(${step2X}px)`,
|
||||
}}
|
||||
>
|
||||
<div className="flex-shrink-0 w-16 h-16 rounded-full bg-orange-500 flex items-center justify-center border-4 border-orange-400 shadow-xl">
|
||||
<span className="text-3xl font-bold text-white">2</span>
|
||||
</div>
|
||||
<div className="flex-1 bg-white/10 backdrop-blur-sm rounded-2xl p-5 border-2 border-white/20 shadow-lg relative overflow-hidden">
|
||||
{/* Lightning glow effect */}
|
||||
<div
|
||||
className="absolute top-0 right-0 w-32 h-32 blur-3xl opacity-50"
|
||||
style={{
|
||||
background: "radial-gradient(circle, #f7931a 0%, transparent 70%)",
|
||||
}}
|
||||
/>
|
||||
<h3 className="text-2xl font-bold text-white mb-2 flex items-center gap-3 relative z-10">
|
||||
<span className="text-3xl">⚡</span>
|
||||
Mitgliedsbeitrag via Lightning
|
||||
</h3>
|
||||
<p className="text-lg text-neutral-200 relative z-10">
|
||||
Zahle deinen Mitgliedsbeitrag schnell und einfach mit Bitcoin Lightning
|
||||
</p>
|
||||
{/* Lightning Payment Animation */}
|
||||
<LightningPaymentAnimation frame={frame} fps={fps} startFrame={4.5 * fps} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Arrow 2 */}
|
||||
<div
|
||||
className="flex justify-center my-2"
|
||||
style={{ opacity: arrow2Opacity }}
|
||||
>
|
||||
<div className="text-4xl text-orange-500">↓</div>
|
||||
</div>
|
||||
|
||||
{/* Step 3: NIP-05 Benefit */}
|
||||
<div
|
||||
className="flex items-center gap-5"
|
||||
style={{
|
||||
opacity: step3Opacity,
|
||||
transform: `translateY(${step3Y}px)`,
|
||||
}}
|
||||
>
|
||||
<div className="flex-shrink-0 w-16 h-16 rounded-full bg-orange-500 flex items-center justify-center border-4 border-orange-400 shadow-xl">
|
||||
<span className="text-3xl font-bold text-white">3</span>
|
||||
</div>
|
||||
<div className="flex-1 bg-gradient-to-r from-orange-500/20 to-orange-600/20 backdrop-blur-sm rounded-2xl p-5 border-2 border-orange-400 shadow-lg">
|
||||
<h3 className="text-2xl font-bold text-white mb-2 flex items-center gap-3">
|
||||
<span className="text-3xl">✓</span>
|
||||
NIP-05 Handle erstellen
|
||||
</h3>
|
||||
<p className="text-lg text-neutral-200">
|
||||
Erstelle dein persönliches NIP-05 Handle und verifiziere deine Nostr-Identität
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Call to Action */}
|
||||
<div
|
||||
className="absolute bottom-6 left-0 right-0 text-center"
|
||||
style={{
|
||||
opacity: ctaOpacity,
|
||||
transform: `scale(${ctaScale})`,
|
||||
}}
|
||||
>
|
||||
<p className="text-xl text-white font-bold mb-1">
|
||||
Los geht's!
|
||||
</p>
|
||||
<p className="text-base text-neutral-300">
|
||||
Im nächsten Video zeigen wir dir Schritt 3 im Detail
|
||||
</p>
|
||||
</div>
|
||||
</AbsoluteFill>
|
||||
);
|
||||
};
|
||||
154
videos/src/scenes/OutroScene.tsx
Normal file
154
videos/src/scenes/OutroScene.tsx
Normal file
@@ -0,0 +1,154 @@
|
||||
import { AbsoluteFill, interpolate, spring, useCurrentFrame, useVideoConfig, Img, staticFile } from "remotion";
|
||||
import { BitcoinEffect } from "../components/BitcoinEffect";
|
||||
import { AnimatedLogo } from "../components/AnimatedLogo";
|
||||
|
||||
export const OutroScene: React.FC = () => {
|
||||
const frame = useCurrentFrame();
|
||||
const { fps } = useVideoConfig();
|
||||
|
||||
// Logo animation at top
|
||||
const logoSpring = spring({
|
||||
frame,
|
||||
fps,
|
||||
config: { damping: 200 },
|
||||
});
|
||||
const logoOpacity = interpolate(logoSpring, [0, 1], [0, 1]);
|
||||
const logoY = interpolate(logoSpring, [0, 1], [-50, 0]);
|
||||
|
||||
// Main SVG Logo
|
||||
const svgSpring = spring({
|
||||
frame: frame - 0.5 * fps,
|
||||
fps,
|
||||
config: { damping: 15, stiffness: 100 },
|
||||
});
|
||||
const svgScale = interpolate(svgSpring, [0, 1], [0.8, 1]);
|
||||
const svgOpacity = interpolate(svgSpring, [0, 1], [0, 1]);
|
||||
|
||||
// URL Animation - delayed and prominent
|
||||
const urlSpring = spring({
|
||||
frame: frame - 2 * fps,
|
||||
fps,
|
||||
config: { damping: 20, stiffness: 100 },
|
||||
});
|
||||
const urlScale = interpolate(urlSpring, [0, 1], [0.5, 1]);
|
||||
const urlOpacity = interpolate(urlSpring, [0, 1], [0, 1]);
|
||||
|
||||
// URL pulsing effect
|
||||
const urlPulse = interpolate(
|
||||
Math.sin((frame - 2 * fps) * 0.05),
|
||||
[-1, 1],
|
||||
[1, 1.05]
|
||||
);
|
||||
|
||||
// Call to action
|
||||
const ctaSpring = spring({
|
||||
frame: frame - 1 * fps,
|
||||
fps,
|
||||
config: { damping: 200 },
|
||||
});
|
||||
const ctaOpacity = interpolate(ctaSpring, [0, 1], [0, 1]);
|
||||
const ctaY = interpolate(ctaSpring, [0, 1], [30, 0]);
|
||||
|
||||
// Footer appears last
|
||||
const footerSpring = spring({
|
||||
frame: frame - 4 * fps,
|
||||
fps,
|
||||
config: { damping: 200 },
|
||||
});
|
||||
const footerOpacity = interpolate(footerSpring, [0, 1], [0, 1]);
|
||||
|
||||
return (
|
||||
<AbsoluteFill>
|
||||
{/* Wallpaper Background */}
|
||||
<Img
|
||||
src={staticFile("einundzwanzig-wallpaper.png")}
|
||||
className="absolute inset-0 w-full h-full object-cover"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-neutral-950/85" />
|
||||
|
||||
{/* Bitcoin Effect */}
|
||||
<BitcoinEffect />
|
||||
|
||||
{/* Animated Logo Top */}
|
||||
<div
|
||||
className="absolute top-16 left-1/2 -translate-x-1/2"
|
||||
style={{
|
||||
opacity: logoOpacity,
|
||||
transform: `translateX(-50%) translateY(${logoY}px)`,
|
||||
}}
|
||||
>
|
||||
<AnimatedLogo size={180} delay={0} />
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="absolute inset-0 flex flex-col items-center justify-center px-12">
|
||||
{/* EINUNDZWANZIG Logo SVG */}
|
||||
<div
|
||||
className="mb-16"
|
||||
style={{
|
||||
opacity: svgOpacity,
|
||||
transform: `scale(${svgScale})`,
|
||||
}}
|
||||
>
|
||||
<Img
|
||||
src={staticFile("einundzwanzig-horizontal-inverted.svg")}
|
||||
style={{ width: 700, height: "auto" }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Call to Action Text */}
|
||||
<div
|
||||
style={{
|
||||
opacity: ctaOpacity,
|
||||
transform: `translateY(${ctaY}px)`,
|
||||
}}
|
||||
>
|
||||
<p className="text-4xl text-white text-center font-semibold mb-12">
|
||||
Werde jetzt Mitglied!
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* URL - MAIN FOCUS - Very Large and Prominent */}
|
||||
<div
|
||||
className="relative"
|
||||
style={{
|
||||
opacity: urlOpacity,
|
||||
transform: `scale(${urlScale * urlPulse})`,
|
||||
}}
|
||||
>
|
||||
{/* Glow effect behind URL */}
|
||||
<div
|
||||
className="absolute inset-0 blur-3xl"
|
||||
style={{
|
||||
background: "radial-gradient(ellipse, #f7931a 0%, transparent 70%)",
|
||||
opacity: 0.4,
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* URL Container with border */}
|
||||
<div className="relative bg-neutral-800/80 backdrop-blur-sm rounded-3xl px-20 py-12 border-4 border-orange-500 shadow-2xl">
|
||||
<p className="text-7xl text-white text-center font-bold tracking-wide">
|
||||
verein.einundzwanzig.space
|
||||
</p>
|
||||
|
||||
{/* Accent line */}
|
||||
<div className="h-2 bg-gradient-to-r from-transparent via-orange-500 to-transparent mt-6 rounded-full" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Footer Info */}
|
||||
<div
|
||||
className="absolute bottom-20 left-0 right-0 text-center"
|
||||
style={{ opacity: footerOpacity }}
|
||||
>
|
||||
<p className="text-neutral-300 text-2xl mb-3 font-semibold">
|
||||
Mitglieder-Vorteile:
|
||||
</p>
|
||||
<p className="text-neutral-400 text-xl">
|
||||
🔗 Nostr Relay • ✓ NIP-05 • ⚡ Lightning Watchtower
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</AbsoluteFill>
|
||||
);
|
||||
};
|
||||
128
videos/src/scenes/SaveButtonScene.tsx
Normal file
128
videos/src/scenes/SaveButtonScene.tsx
Normal file
@@ -0,0 +1,128 @@
|
||||
import { AbsoluteFill, interpolate, spring, useCurrentFrame, useVideoConfig, Img, staticFile } from "remotion";
|
||||
import { BitcoinEffect } from "../components/BitcoinEffect";
|
||||
import { AnimatedLogo } from "../components/AnimatedLogo";
|
||||
|
||||
export const SaveButtonScene: React.FC = () => {
|
||||
const frame = useCurrentFrame();
|
||||
const { fps } = useVideoConfig();
|
||||
|
||||
const titleSpring = spring({
|
||||
frame,
|
||||
fps,
|
||||
config: { damping: 200 },
|
||||
});
|
||||
const titleOpacity = interpolate(titleSpring, [0, 1], [0, 1]);
|
||||
|
||||
// Button entrance
|
||||
const buttonSpring = spring({
|
||||
frame: frame - 0.5 * fps,
|
||||
fps,
|
||||
config: { damping: 15, stiffness: 100 },
|
||||
});
|
||||
const buttonScale = interpolate(buttonSpring, [0, 1], [0.5, 1]);
|
||||
const buttonOpacity = interpolate(buttonSpring, [0, 1], [0, 1]);
|
||||
|
||||
// Click animation
|
||||
const clickFrame = 2 * fps;
|
||||
const clickSpring = spring({
|
||||
frame: frame - clickFrame,
|
||||
fps,
|
||||
config: { damping: 10, stiffness: 200 },
|
||||
durationInFrames: 0.3 * fps,
|
||||
});
|
||||
const buttonPressScale = frame >= clickFrame ? interpolate(clickSpring, [0, 1], [0.95, 1]) : 1;
|
||||
|
||||
// Success animation
|
||||
const successSpring = spring({
|
||||
frame: frame - (clickFrame + 0.5 * fps),
|
||||
fps,
|
||||
config: { damping: 200 },
|
||||
});
|
||||
const successOpacity = interpolate(successSpring, [0, 1], [0, 1]);
|
||||
const successScale = interpolate(successSpring, [0, 1], [0, 1]);
|
||||
|
||||
// Cursor pointer
|
||||
const cursorSpring = spring({
|
||||
frame: frame - 1.5 * fps,
|
||||
fps,
|
||||
config: { damping: 20, stiffness: 150 },
|
||||
});
|
||||
const cursorX = interpolate(cursorSpring, [0, 1], [-200, 0]);
|
||||
const cursorY = interpolate(cursorSpring, [0, 1], [100, 0]);
|
||||
|
||||
return (
|
||||
<AbsoluteFill>
|
||||
{/* Wallpaper Background */}
|
||||
<Img
|
||||
src={staticFile("einundzwanzig-wallpaper.png")}
|
||||
className="absolute inset-0 w-full h-full object-cover"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-neutral-950/70" />
|
||||
|
||||
{/* Bitcoin Effect */}
|
||||
<BitcoinEffect />
|
||||
|
||||
<div className="absolute inset-0 flex flex-col items-center justify-center px-12">
|
||||
<h2
|
||||
className="text-5xl font-bold text-white mb-20 text-center"
|
||||
style={{ opacity: titleOpacity }}
|
||||
>
|
||||
Schritt 2: Handle speichern
|
||||
</h2>
|
||||
|
||||
{/* Button Container */}
|
||||
<div className="relative">
|
||||
{/* Animated Cursor */}
|
||||
<div
|
||||
className="absolute text-5xl pointer-events-none z-10"
|
||||
style={{
|
||||
left: "50%",
|
||||
top: "50%",
|
||||
transform: `translate(calc(-50% + ${cursorX}px), calc(-50% + ${cursorY}px))`,
|
||||
}}
|
||||
>
|
||||
👆
|
||||
</div>
|
||||
|
||||
{/* Save Button */}
|
||||
<button
|
||||
className="bg-neutral-800 text-white font-bold text-3xl px-16 py-8 rounded-xl shadow-2xl border-4 border-neutral-600"
|
||||
style={{
|
||||
opacity: buttonOpacity,
|
||||
transform: `scale(${buttonScale * buttonPressScale})`,
|
||||
}}
|
||||
>
|
||||
Speichern
|
||||
</button>
|
||||
|
||||
{/* Success Message */}
|
||||
{frame >= clickFrame + 0.5 * fps && (
|
||||
<div
|
||||
className="absolute -bottom-24 left-1/2 -translate-x-1/2 whitespace-nowrap"
|
||||
style={{
|
||||
opacity: successOpacity,
|
||||
transform: `translateX(-50%) scale(${successScale})`,
|
||||
}}
|
||||
>
|
||||
<div className="bg-neutral-800 text-white px-8 py-4 rounded-lg shadow-xl flex items-center gap-4 border-2 border-neutral-600">
|
||||
<span className="text-2xl">✓</span>
|
||||
<span className="text-xl font-semibold">NIP-05 Handle gespeichert!</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 3D Success Logo */}
|
||||
{frame >= clickFrame + 0.5 * fps && (
|
||||
<div className="absolute top-20 right-20">
|
||||
<AnimatedLogo size={280} delay={clickFrame + 0.5 * fps} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<p className="text-neutral-300 text-center mt-32 text-xl max-w-2xl" style={{ opacity: titleOpacity }}>
|
||||
Nach dem Speichern wird dein Handle in Kürze automatisch aktiviert.
|
||||
</p>
|
||||
</div>
|
||||
</AbsoluteFill>
|
||||
);
|
||||
};
|
||||
114
videos/src/scenes/UIShowcaseScene.tsx
Normal file
114
videos/src/scenes/UIShowcaseScene.tsx
Normal file
@@ -0,0 +1,114 @@
|
||||
import { AbsoluteFill, interpolate, spring, useCurrentFrame, useVideoConfig, Img, staticFile } from "remotion";
|
||||
import { ThreeCanvas } from "@remotion/three";
|
||||
import { BitcoinEffect } from "../components/BitcoinEffect";
|
||||
import { AnimatedLogo } from "../components/AnimatedLogo";
|
||||
|
||||
export const UIShowcaseScene: React.FC = () => {
|
||||
const frame = useCurrentFrame();
|
||||
const { fps, width, height } = useVideoConfig();
|
||||
|
||||
const cardSpring = spring({
|
||||
frame,
|
||||
fps,
|
||||
config: { damping: 20, stiffness: 200 },
|
||||
});
|
||||
|
||||
const cardScale = interpolate(cardSpring, [0, 1], [0.8, 1]);
|
||||
const cardOpacity = interpolate(cardSpring, [0, 1], [0, 1]);
|
||||
|
||||
// 3D background elements
|
||||
const bgRotation = frame * 0.01;
|
||||
|
||||
const titleSpring = spring({
|
||||
frame: frame - 0.3 * fps,
|
||||
fps,
|
||||
config: { damping: 200 },
|
||||
});
|
||||
const titleY = interpolate(titleSpring, [0, 1], [50, 0]);
|
||||
const titleOpacity = interpolate(titleSpring, [0, 1], [0, 1]);
|
||||
|
||||
return (
|
||||
<AbsoluteFill>
|
||||
{/* Wallpaper Background */}
|
||||
<Img
|
||||
src={staticFile("einundzwanzig-wallpaper.png")}
|
||||
className="absolute inset-0 w-full h-full object-cover"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-neutral-950/80" />
|
||||
|
||||
{/* Bitcoin Effect */}
|
||||
<BitcoinEffect />
|
||||
|
||||
{/* Animated Logos in corners */}
|
||||
<div className="absolute top-10 left-10 opacity-50">
|
||||
<AnimatedLogo size={150} delay={0.2 * fps} />
|
||||
</div>
|
||||
<div className="absolute top-10 right-10 opacity-50">
|
||||
<AnimatedLogo size={150} delay={0.4 * fps} />
|
||||
</div>
|
||||
|
||||
{/* 3D Background */}
|
||||
<div className="absolute inset-0 opacity-20">
|
||||
<ThreeCanvas width={width} height={height}>
|
||||
<ambientLight intensity={0.3} />
|
||||
<directionalLight position={[5, 5, 5]} intensity={0.6} />
|
||||
<mesh rotation={[0, bgRotation, 0]} position={[-3, 0, 0]}>
|
||||
<sphereGeometry args={[1.5, 32, 32]} />
|
||||
<meshStandardMaterial color="#f7931a" wireframe />
|
||||
</mesh>
|
||||
<mesh rotation={[0, -bgRotation, 0]} position={[3, 0, 0]}>
|
||||
<octahedronGeometry args={[1.5]} />
|
||||
<meshStandardMaterial color="#f7931a" wireframe />
|
||||
</mesh>
|
||||
</ThreeCanvas>
|
||||
</div>
|
||||
|
||||
{/* Main Content */}
|
||||
<div className="absolute inset-0 flex flex-col items-center justify-center px-20">
|
||||
<h2
|
||||
className="text-5xl font-bold text-white mb-12 text-center"
|
||||
style={{
|
||||
opacity: titleOpacity,
|
||||
transform: `translateY(${titleY}px)`,
|
||||
}}
|
||||
>
|
||||
Die NIP-05 Oberfläche
|
||||
</h2>
|
||||
|
||||
{/* UI Card Mockup */}
|
||||
<div
|
||||
className="bg-neutral-50 rounded-xl p-8 border-4 border-neutral-200 shadow-2xl max-w-2xl w-full"
|
||||
style={{
|
||||
opacity: cardOpacity,
|
||||
transform: `scale(${cardScale})`,
|
||||
}}
|
||||
>
|
||||
<div className="flex items-start gap-4 mb-6">
|
||||
<div className="w-12 h-12 rounded-full bg-neutral-800 flex items-center justify-center flex-shrink-0">
|
||||
<div className="text-white text-2xl">✓</div>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3 className="text-2xl font-semibold text-neutral-800 mb-2">
|
||||
Get NIP-05 verified
|
||||
</h3>
|
||||
<p className="text-base text-neutral-600">
|
||||
Verifiziere deine Identität mit einem menschenlesbaren Nostr-Namen.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Input Preview */}
|
||||
<div className="space-y-3">
|
||||
<label className="block text-sm font-medium text-neutral-700">
|
||||
Dein NIP-05 Handle
|
||||
</label>
|
||||
<div className="flex items-center bg-white rounded-lg border-2 border-neutral-300 px-4 py-3">
|
||||
<div className="flex-1 text-neutral-400">dein-name</div>
|
||||
<div className="text-neutral-600 font-medium">@einundzwanzig.space</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</AbsoluteFill>
|
||||
);
|
||||
};
|
||||
125
videos/src/scenes/VerificationScene.tsx
Normal file
125
videos/src/scenes/VerificationScene.tsx
Normal file
@@ -0,0 +1,125 @@
|
||||
import { AbsoluteFill, interpolate, spring, useCurrentFrame, useVideoConfig, Img, staticFile } from "remotion";
|
||||
import { BitcoinEffect } from "../components/BitcoinEffect";
|
||||
import { AnimatedLogo } from "../components/AnimatedLogo";
|
||||
|
||||
export const VerificationScene: React.FC = () => {
|
||||
const frame = useCurrentFrame();
|
||||
const { fps } = useVideoConfig();
|
||||
|
||||
const titleSpring = spring({
|
||||
frame,
|
||||
fps,
|
||||
config: { damping: 200 },
|
||||
});
|
||||
const titleOpacity = interpolate(titleSpring, [0, 1], [0, 1]);
|
||||
|
||||
// Badge entrance
|
||||
const badgeSpring = spring({
|
||||
frame: frame - 0.5 * fps,
|
||||
fps,
|
||||
config: { damping: 8 },
|
||||
});
|
||||
const badgeScale = interpolate(badgeSpring, [0, 1], [0, 1]);
|
||||
const badgeRotation = interpolate(badgeSpring, [0, 1], [-180, 0]);
|
||||
|
||||
// Handle list
|
||||
const listSpring = spring({
|
||||
frame: frame - 1.5 * fps,
|
||||
fps,
|
||||
config: { damping: 200 },
|
||||
});
|
||||
const listOpacity = interpolate(listSpring, [0, 1], [0, 1]);
|
||||
const listY = interpolate(listSpring, [0, 1], [50, 0]);
|
||||
|
||||
// Checkmark particles
|
||||
const particle1Spring = spring({
|
||||
frame: frame - 2 * fps,
|
||||
fps,
|
||||
config: { damping: 15 },
|
||||
});
|
||||
const particle1Y = interpolate(particle1Spring, [0, 1], [0, -100]);
|
||||
const particle1Opacity = interpolate(particle1Spring, [0, 0.7, 1], [0, 1, 0]);
|
||||
|
||||
return (
|
||||
<AbsoluteFill>
|
||||
{/* Wallpaper Background */}
|
||||
<Img
|
||||
src={staticFile("einundzwanzig-wallpaper.png")}
|
||||
className="absolute inset-0 w-full h-full object-cover"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-neutral-950/75" />
|
||||
|
||||
{/* Bitcoin Effect */}
|
||||
<BitcoinEffect />
|
||||
|
||||
{/* Animated EINUNDZWANZIG Logo */}
|
||||
<div className="absolute top-16 left-3/4 -translate-x-1/2">
|
||||
<AnimatedLogo size={320} delay={fps} />
|
||||
</div>
|
||||
|
||||
{/* Floating Checkmark Particles */}
|
||||
<div
|
||||
className="absolute top-1/3 left-1/4 text-6xl"
|
||||
style={{
|
||||
opacity: particle1Opacity,
|
||||
transform: `translateY(${particle1Y}px)`,
|
||||
}}
|
||||
>
|
||||
✓
|
||||
</div>
|
||||
|
||||
<div className="absolute inset-0 flex flex-col items-center justify-center px-12">
|
||||
<h2
|
||||
className="text-5xl font-bold text-white mb-16 text-center"
|
||||
style={{ opacity: titleOpacity }}
|
||||
>
|
||||
Verifizierung erfolgreich!
|
||||
</h2>
|
||||
|
||||
{/* Success Badge */}
|
||||
<div
|
||||
className="bg-white rounded-2xl p-8 shadow-2xl mb-12"
|
||||
style={{
|
||||
opacity: badgeScale,
|
||||
transform: `scale(${badgeScale}) rotate(${badgeRotation}deg)`,
|
||||
}}
|
||||
>
|
||||
<div className="flex items-center gap-6">
|
||||
<div className="w-20 h-20 rounded-full bg-neutral-800 flex items-center justify-center">
|
||||
<span className="text-5xl text-white">✓</span>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-neutral-600 font-medium mb-1">Dein Handle ist aktiv:</p>
|
||||
<p className="text-3xl font-bold text-neutral-800">
|
||||
satoshi21@einundzwanzig.space
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Handle List */}
|
||||
<div
|
||||
className="bg-white/10 backdrop-blur-sm rounded-xl p-8 border-2 border-white/20 max-w-2xl w-full"
|
||||
style={{
|
||||
opacity: listOpacity,
|
||||
transform: `translateY(${listY}px)`,
|
||||
}}
|
||||
>
|
||||
<p className="text-xl font-semibold text-white mb-4">Deine aktivierten Handles:</p>
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-4 bg-white/10 rounded-lg px-5 py-3">
|
||||
<span className="text-lg text-white">satoshi21@einundzwanzig.space</span>
|
||||
<span className="bg-neutral-800 text-white text-xs font-bold px-3 py-1 rounded-full border-2 border-neutral-600">
|
||||
OK
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="text-neutral-300 text-center mt-12 text-xl max-w-3xl" style={{ opacity: listOpacity }}>
|
||||
Dein NIP-05 Handle ist jetzt aktiv! Nostr-Clients zeigen ein Verifizierungs-Häkchen für dich an.
|
||||
</p>
|
||||
</div>
|
||||
</AbsoluteFill>
|
||||
);
|
||||
};
|
||||
134
videos/src/scenes/mobile/InputDemoSceneMobile.tsx
Normal file
134
videos/src/scenes/mobile/InputDemoSceneMobile.tsx
Normal file
@@ -0,0 +1,134 @@
|
||||
import { AbsoluteFill, interpolate, spring, useCurrentFrame, useVideoConfig, staticFile } from "remotion";
|
||||
import { BitcoinEffect } from "../../components/BitcoinEffect";
|
||||
import { AnimatedLogo } from "../../components/AnimatedLogo";
|
||||
|
||||
export const InputDemoSceneMobile: React.FC = () => {
|
||||
const frame = useCurrentFrame();
|
||||
const { fps, width } = useVideoConfig();
|
||||
|
||||
// Typing animation
|
||||
const typingText = "satoshi21";
|
||||
const typingProgress = interpolate(frame, [0, 2 * fps], [0, typingText.length], {
|
||||
extrapolateRight: "clamp",
|
||||
});
|
||||
const currentText = typingText.slice(0, Math.floor(typingProgress));
|
||||
|
||||
// Cursor blink
|
||||
const cursorBlink = Math.floor(frame / 15) % 2 === 0;
|
||||
|
||||
// Card entrance
|
||||
const cardSpring = spring({
|
||||
frame,
|
||||
fps,
|
||||
config: { damping: 200 },
|
||||
});
|
||||
const cardY = interpolate(cardSpring, [0, 1], [100, 0]);
|
||||
const cardOpacity = interpolate(cardSpring, [0, 1], [0, 1]);
|
||||
|
||||
// Rules box appears after typing
|
||||
const rulesSpring = spring({
|
||||
frame: frame - 2.5 * fps,
|
||||
fps,
|
||||
config: { damping: 200 },
|
||||
});
|
||||
const rulesOpacity = interpolate(rulesSpring, [0, 1], [0, 1]);
|
||||
const rulesY = interpolate(rulesSpring, [0, 1], [30, 0]);
|
||||
|
||||
// Pointer animation - from left
|
||||
const pointerSpring = spring({
|
||||
frame: frame - 0.5 * fps,
|
||||
fps,
|
||||
config: { damping: 15, stiffness: 100 },
|
||||
});
|
||||
const pointerX = interpolate(pointerSpring, [0, 1], [-120, 0]);
|
||||
const pointerOpacity = interpolate(pointerSpring, [0, 1], [0, 1]);
|
||||
|
||||
return (
|
||||
<AbsoluteFill>
|
||||
{/* Tiled Wallpaper Background */}
|
||||
<div
|
||||
className="absolute inset-0"
|
||||
style={{
|
||||
backgroundImage: `url(${staticFile("einundzwanzig-wallpaper.png")})`,
|
||||
backgroundSize: `${width}px auto`,
|
||||
backgroundRepeat: "repeat-y",
|
||||
backgroundPosition: "center top",
|
||||
}}
|
||||
/>
|
||||
<div className="absolute inset-0 bg-neutral-950/80" />
|
||||
|
||||
{/* Bitcoin Effect */}
|
||||
<BitcoinEffect />
|
||||
|
||||
{/* Animated EINUNDZWANZIG Logo */}
|
||||
<div className="absolute top-12 left-1/2 -translate-x-1/2">
|
||||
<AnimatedLogo size={120} delay={0.5 * fps} />
|
||||
</div>
|
||||
|
||||
<div className="absolute inset-0 flex flex-col items-center justify-center px-8">
|
||||
<h2 className="text-5xl font-bold text-white mb-12 text-center">
|
||||
Schritt 1: Handle eingeben
|
||||
</h2>
|
||||
|
||||
{/* Input Card - Full Width */}
|
||||
<div
|
||||
className="bg-white rounded-3xl p-10 shadow-2xl w-full relative"
|
||||
style={{
|
||||
opacity: cardOpacity,
|
||||
transform: `translateY(${cardY}px)`,
|
||||
}}
|
||||
>
|
||||
{/* Animated Pointer - from left */}
|
||||
<div
|
||||
className="absolute -left-24 top-1/2 -translate-y-1/2 text-8xl"
|
||||
style={{
|
||||
transform: `translateY(-50%) translateX(${pointerX}px)`,
|
||||
opacity: pointerOpacity,
|
||||
}}
|
||||
>
|
||||
👉
|
||||
</div>
|
||||
|
||||
<label className="block text-3xl font-medium text-neutral-700 mb-5">
|
||||
Dein NIP-05 Handle
|
||||
</label>
|
||||
<div className="bg-white rounded-2xl border-4 border-neutral-800 px-6 py-5 mb-8">
|
||||
<div className="text-4xl text-neutral-800 mb-2">
|
||||
{currentText}
|
||||
{cursorBlink && frame < 2.5 * fps && <span className="text-neutral-800">|</span>}
|
||||
</div>
|
||||
<div className="text-2xl text-neutral-600 font-medium">@einundzwanzig.space</div>
|
||||
</div>
|
||||
|
||||
{/* Rules Box */}
|
||||
<div
|
||||
className="bg-neutral-100 rounded-2xl border-2 border-neutral-300 p-6"
|
||||
style={{
|
||||
opacity: rulesOpacity,
|
||||
transform: `translateY(${rulesY}px)`,
|
||||
}}
|
||||
>
|
||||
<p className="text-2xl text-neutral-700 font-medium mb-4">
|
||||
✅ Regeln für dein Handle:
|
||||
</p>
|
||||
<ul className="text-xl text-neutral-600 space-y-2">
|
||||
<li>• Nur Kleinbuchstaben (a-z)</li>
|
||||
<li>• Zahlen (0-9)</li>
|
||||
<li>• Zeichen: "-" und "_"</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Info text - Footer */}
|
||||
<p
|
||||
className="text-neutral-200 text-center mt-12 text-3xl px-4 font-medium"
|
||||
style={{
|
||||
opacity: rulesOpacity,
|
||||
}}
|
||||
>
|
||||
Dein Handle wird automatisch kleingeschrieben und muss einzigartig sein.
|
||||
</p>
|
||||
</div>
|
||||
</AbsoluteFill>
|
||||
);
|
||||
};
|
||||
324
videos/src/scenes/mobile/IntroSceneMobile.tsx
Normal file
324
videos/src/scenes/mobile/IntroSceneMobile.tsx
Normal file
@@ -0,0 +1,324 @@
|
||||
import { AbsoluteFill, interpolate, spring, useCurrentFrame, useVideoConfig, staticFile } from "remotion";
|
||||
import { BitcoinEffect } from "../../components/BitcoinEffect";
|
||||
import { AnimatedLogo } from "../../components/AnimatedLogo";
|
||||
|
||||
// Lightning Payment Animation Component
|
||||
const LightningPaymentAnimation: React.FC<{ frame: number; fps: number; startFrame: number }> = ({
|
||||
frame,
|
||||
fps,
|
||||
startFrame,
|
||||
}) => {
|
||||
const relativeFrame = frame - startFrame;
|
||||
|
||||
if (relativeFrame < 0.5 * fps) return null;
|
||||
|
||||
const boltProgress = interpolate(
|
||||
relativeFrame,
|
||||
[0.5 * fps, 1.5 * fps],
|
||||
[0, 1],
|
||||
{ extrapolateLeft: "clamp", extrapolateRight: "clamp" }
|
||||
);
|
||||
|
||||
const flashOpacity = interpolate(
|
||||
relativeFrame,
|
||||
[1.4 * fps, 1.5 * fps, 1.8 * fps],
|
||||
[0, 0.8, 0],
|
||||
{ extrapolateLeft: "clamp", extrapolateRight: "clamp" }
|
||||
);
|
||||
|
||||
const checkSpring = spring({
|
||||
frame: relativeFrame - 1.6 * fps,
|
||||
fps,
|
||||
config: { damping: 12, stiffness: 150 },
|
||||
});
|
||||
const checkScale = interpolate(checkSpring, [0, 1], [0, 1]);
|
||||
|
||||
const boltX = interpolate(boltProgress, [0, 1], [-80, 80]);
|
||||
const boltY = interpolate(boltProgress, [0, 0.5, 1], [0, -20, 0]);
|
||||
|
||||
const boltRotation = interpolate(
|
||||
Math.sin(relativeFrame * 0.4),
|
||||
[-1, 1],
|
||||
[-15, 15]
|
||||
);
|
||||
|
||||
const boltScale = interpolate(
|
||||
Math.sin(relativeFrame * 0.3),
|
||||
[-1, 1],
|
||||
[1, 1.3]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="absolute inset-0 flex items-center justify-center pointer-events-none">
|
||||
{boltProgress < 1 && (
|
||||
<div
|
||||
className="absolute text-5xl"
|
||||
style={{
|
||||
transform: `translateX(${boltX}px) translateY(${boltY}px) rotate(${boltRotation}deg) scale(${boltScale})`,
|
||||
filter: "drop-shadow(0 0 12px #f7931a) drop-shadow(0 0 24px #ffcc00)",
|
||||
}}
|
||||
>
|
||||
⚡
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div
|
||||
className="absolute inset-0 bg-yellow-400"
|
||||
style={{ opacity: flashOpacity }}
|
||||
/>
|
||||
|
||||
{checkScale > 0 && (
|
||||
<div
|
||||
className="absolute right-4 top-1/2 -translate-y-1/2 w-14 h-14 rounded-full bg-green-500 flex items-center justify-center"
|
||||
style={{
|
||||
transform: `translateY(-50%) scale(${checkScale})`,
|
||||
boxShadow: "0 0 20px rgba(34, 197, 94, 0.6)",
|
||||
}}
|
||||
>
|
||||
<span className="text-white text-3xl font-bold">✓</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const IntroSceneMobile: React.FC = () => {
|
||||
const frame = useCurrentFrame();
|
||||
const { fps, width } = useVideoConfig();
|
||||
|
||||
// Logo entrance
|
||||
const logoSpring = spring({
|
||||
frame,
|
||||
fps,
|
||||
config: { damping: 200 },
|
||||
});
|
||||
const logoOpacity = interpolate(logoSpring, [0, 1], [0, 1]);
|
||||
const logoScale = interpolate(logoSpring, [0, 1], [0.5, 1]);
|
||||
|
||||
// Main title
|
||||
const titleSpring = spring({
|
||||
frame: frame - 0.5 * fps,
|
||||
fps,
|
||||
config: { damping: 200 },
|
||||
});
|
||||
const titleOpacity = interpolate(titleSpring, [0, 1], [0, 1]);
|
||||
const titleY = interpolate(titleSpring, [0, 1], [30, 0]);
|
||||
|
||||
// Subtitle
|
||||
const subtitleSpring = spring({
|
||||
frame: frame - 1 * fps,
|
||||
fps,
|
||||
config: { damping: 200 },
|
||||
});
|
||||
const subtitleOpacity = interpolate(subtitleSpring, [0, 1], [0, 1]);
|
||||
|
||||
// Step 1: Registration
|
||||
const step1Spring = spring({
|
||||
frame: frame - 2 * fps,
|
||||
fps,
|
||||
config: { damping: 15, stiffness: 100 },
|
||||
});
|
||||
const step1Opacity = interpolate(step1Spring, [0, 1], [0, 1]);
|
||||
const step1Y = interpolate(step1Spring, [0, 1], [50, 0]);
|
||||
|
||||
// Step 2: Lightning Payment
|
||||
const step2Spring = spring({
|
||||
frame: frame - 4.5 * fps,
|
||||
fps,
|
||||
config: { damping: 15, stiffness: 100 },
|
||||
});
|
||||
const step2Opacity = interpolate(step2Spring, [0, 1], [0, 1]);
|
||||
const step2Y = interpolate(step2Spring, [0, 1], [50, 0]);
|
||||
|
||||
// Step 3: NIP-05 Benefit
|
||||
const step3Spring = spring({
|
||||
frame: frame - 7.5 * fps,
|
||||
fps,
|
||||
config: { damping: 15, stiffness: 100 },
|
||||
});
|
||||
const step3Opacity = interpolate(step3Spring, [0, 1], [0, 1]);
|
||||
const step3Y = interpolate(step3Spring, [0, 1], [50, 0]);
|
||||
|
||||
// Arrow animations
|
||||
const arrow1Spring = spring({
|
||||
frame: frame - 3.5 * fps,
|
||||
fps,
|
||||
config: { damping: 200 },
|
||||
});
|
||||
const arrow1Opacity = interpolate(arrow1Spring, [0, 1], [0, 1]);
|
||||
|
||||
const arrow2Spring = spring({
|
||||
frame: frame - 7 * fps,
|
||||
fps,
|
||||
config: { damping: 200 },
|
||||
});
|
||||
const arrow2Opacity = interpolate(arrow2Spring, [0, 1], [0, 1]);
|
||||
|
||||
// Final call to action
|
||||
const ctaSpring = spring({
|
||||
frame: frame - 9.5 * fps,
|
||||
fps,
|
||||
config: { damping: 200 },
|
||||
});
|
||||
const ctaOpacity = interpolate(ctaSpring, [0, 1], [0, 1]);
|
||||
const ctaScale = interpolate(ctaSpring, [0, 1], [0.9, 1]);
|
||||
|
||||
return (
|
||||
<AbsoluteFill>
|
||||
{/* Tiled Wallpaper Background */}
|
||||
<div
|
||||
className="absolute inset-0"
|
||||
style={{
|
||||
backgroundImage: `url(${staticFile("einundzwanzig-wallpaper.png")})`,
|
||||
backgroundSize: `${width}px auto`,
|
||||
backgroundRepeat: "repeat-y",
|
||||
backgroundPosition: "center top",
|
||||
}}
|
||||
/>
|
||||
<div className="absolute inset-0 bg-neutral-900/80" />
|
||||
|
||||
{/* Bitcoin Effect */}
|
||||
<BitcoinEffect />
|
||||
|
||||
{/* Header with Logo and Title */}
|
||||
<div className="absolute top-12 left-0 right-0 flex flex-col items-center gap-4 px-8">
|
||||
<div
|
||||
style={{
|
||||
opacity: logoOpacity,
|
||||
transform: `scale(${logoScale})`,
|
||||
}}
|
||||
>
|
||||
<AnimatedLogo size={100} delay={0} />
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="text-center"
|
||||
style={{
|
||||
opacity: titleOpacity,
|
||||
transform: `translateY(${titleY}px)`,
|
||||
}}
|
||||
>
|
||||
<h1 className="text-5xl font-bold text-white">
|
||||
NIP-05 Verifikation
|
||||
</h1>
|
||||
<p
|
||||
className="text-2xl text-neutral-300 mt-3"
|
||||
style={{ opacity: subtitleOpacity }}
|
||||
>
|
||||
So erhältst du deine verifizierte Nostr-Identität
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Steps Container */}
|
||||
<div className="absolute top-[22%] left-0 right-0 px-8">
|
||||
<div className="flex flex-col gap-4">
|
||||
{/* Step 1: Registration */}
|
||||
<div
|
||||
className="flex items-start gap-5"
|
||||
style={{
|
||||
opacity: step1Opacity,
|
||||
transform: `translateY(${step1Y}px)`,
|
||||
}}
|
||||
>
|
||||
<div className="flex-shrink-0 w-20 h-20 rounded-full bg-orange-500 flex items-center justify-center border-4 border-orange-400 shadow-xl">
|
||||
<span className="text-4xl font-bold text-white">1</span>
|
||||
</div>
|
||||
<div className="flex-1 bg-white/10 backdrop-blur-sm rounded-2xl p-6 border-2 border-white/20 shadow-lg">
|
||||
<h3 className="text-3xl font-bold text-white mb-2 flex items-center gap-3">
|
||||
<span className="text-4xl">📝</span>
|
||||
Registrierung im Verein
|
||||
</h3>
|
||||
<p className="text-xl text-neutral-200">
|
||||
Werde Mitglied bei EINUNDZWANZIG und erhalte Zugang zu allen Vorteilen
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Arrow 1 */}
|
||||
<div
|
||||
className="flex justify-center -my-1"
|
||||
style={{ opacity: arrow1Opacity }}
|
||||
>
|
||||
<div className="text-5xl text-orange-500">↓</div>
|
||||
</div>
|
||||
|
||||
{/* Step 2: Lightning Payment */}
|
||||
<div
|
||||
className="flex items-start gap-5"
|
||||
style={{
|
||||
opacity: step2Opacity,
|
||||
transform: `translateY(${step2Y}px)`,
|
||||
}}
|
||||
>
|
||||
<div className="flex-shrink-0 w-20 h-20 rounded-full bg-orange-500 flex items-center justify-center border-4 border-orange-400 shadow-xl">
|
||||
<span className="text-4xl font-bold text-white">2</span>
|
||||
</div>
|
||||
<div className="flex-1 bg-white/10 backdrop-blur-sm rounded-2xl p-6 border-2 border-white/20 shadow-lg relative overflow-hidden">
|
||||
<div
|
||||
className="absolute top-0 right-0 w-40 h-40 blur-3xl opacity-50"
|
||||
style={{
|
||||
background: "radial-gradient(circle, #f7931a 0%, transparent 70%)",
|
||||
}}
|
||||
/>
|
||||
<h3 className="text-3xl font-bold text-white mb-2 flex items-center gap-3 relative z-10">
|
||||
<span className="text-4xl">⚡</span>
|
||||
Beitrag via Lightning
|
||||
</h3>
|
||||
<p className="text-xl text-neutral-200 relative z-10">
|
||||
Zahle deinen Mitgliedsbeitrag schnell und einfach mit Bitcoin Lightning
|
||||
</p>
|
||||
<LightningPaymentAnimation frame={frame} fps={fps} startFrame={4.5 * fps} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Arrow 2 */}
|
||||
<div
|
||||
className="flex justify-center -my-1"
|
||||
style={{ opacity: arrow2Opacity }}
|
||||
>
|
||||
<div className="text-5xl text-orange-500">↓</div>
|
||||
</div>
|
||||
|
||||
{/* Step 3: NIP-05 Benefit */}
|
||||
<div
|
||||
className="flex items-start gap-5"
|
||||
style={{
|
||||
opacity: step3Opacity,
|
||||
transform: `translateY(${step3Y}px)`,
|
||||
}}
|
||||
>
|
||||
<div className="flex-shrink-0 w-20 h-20 rounded-full bg-orange-500 flex items-center justify-center border-4 border-orange-400 shadow-xl">
|
||||
<span className="text-4xl font-bold text-white">3</span>
|
||||
</div>
|
||||
<div className="flex-1 bg-gradient-to-r from-orange-500/20 to-orange-600/20 backdrop-blur-sm rounded-2xl p-6 border-2 border-orange-400 shadow-lg">
|
||||
<h3 className="text-3xl font-bold text-white mb-2 flex items-center gap-3">
|
||||
<span className="text-4xl">✓</span>
|
||||
NIP-05 Handle erstellen
|
||||
</h3>
|
||||
<p className="text-xl text-neutral-200">
|
||||
Erstelle dein persönliches NIP-05 Handle und verifiziere deine Nostr-Identität
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Call to Action - Footer */}
|
||||
<div
|
||||
className="absolute bottom-12 left-0 right-0 text-center px-8"
|
||||
style={{
|
||||
opacity: ctaOpacity,
|
||||
transform: `scale(${ctaScale})`,
|
||||
}}
|
||||
>
|
||||
<p className="text-5xl text-white font-bold mb-4">
|
||||
Los geht's!
|
||||
</p>
|
||||
<p className="text-3xl text-neutral-200 font-medium">
|
||||
Im nächsten Video zeigen wir dir Schritt 3 im Detail
|
||||
</p>
|
||||
</div>
|
||||
</AbsoluteFill>
|
||||
);
|
||||
};
|
||||
161
videos/src/scenes/mobile/OutroSceneMobile.tsx
Normal file
161
videos/src/scenes/mobile/OutroSceneMobile.tsx
Normal file
@@ -0,0 +1,161 @@
|
||||
import { AbsoluteFill, interpolate, spring, useCurrentFrame, useVideoConfig, Img, staticFile } from "remotion";
|
||||
import { BitcoinEffect } from "../../components/BitcoinEffect";
|
||||
import { AnimatedLogo } from "../../components/AnimatedLogo";
|
||||
|
||||
export const OutroSceneMobile: React.FC = () => {
|
||||
const frame = useCurrentFrame();
|
||||
const { fps, width } = useVideoConfig();
|
||||
|
||||
// Logo animation at top
|
||||
const logoSpring = spring({
|
||||
frame,
|
||||
fps,
|
||||
config: { damping: 200 },
|
||||
});
|
||||
const logoOpacity = interpolate(logoSpring, [0, 1], [0, 1]);
|
||||
const logoY = interpolate(logoSpring, [0, 1], [-50, 0]);
|
||||
|
||||
// Main SVG Logo
|
||||
const svgSpring = spring({
|
||||
frame: frame - 0.5 * fps,
|
||||
fps,
|
||||
config: { damping: 15, stiffness: 100 },
|
||||
});
|
||||
const svgScale = interpolate(svgSpring, [0, 1], [0.8, 1]);
|
||||
const svgOpacity = interpolate(svgSpring, [0, 1], [0, 1]);
|
||||
|
||||
// URL Animation - delayed and prominent
|
||||
const urlSpring = spring({
|
||||
frame: frame - 2 * fps,
|
||||
fps,
|
||||
config: { damping: 20, stiffness: 100 },
|
||||
});
|
||||
const urlScale = interpolate(urlSpring, [0, 1], [0.5, 1]);
|
||||
const urlOpacity = interpolate(urlSpring, [0, 1], [0, 1]);
|
||||
|
||||
// URL pulsing effect
|
||||
const urlPulse = interpolate(
|
||||
Math.sin((frame - 2 * fps) * 0.05),
|
||||
[-1, 1],
|
||||
[1, 1.02]
|
||||
);
|
||||
|
||||
// Call to action
|
||||
const ctaSpring = spring({
|
||||
frame: frame - 1 * fps,
|
||||
fps,
|
||||
config: { damping: 200 },
|
||||
});
|
||||
const ctaOpacity = interpolate(ctaSpring, [0, 1], [0, 1]);
|
||||
const ctaY = interpolate(ctaSpring, [0, 1], [30, 0]);
|
||||
|
||||
// Footer appears last
|
||||
const footerSpring = spring({
|
||||
frame: frame - 4 * fps,
|
||||
fps,
|
||||
config: { damping: 200 },
|
||||
});
|
||||
const footerOpacity = interpolate(footerSpring, [0, 1], [0, 1]);
|
||||
|
||||
return (
|
||||
<AbsoluteFill>
|
||||
{/* Tiled Wallpaper Background */}
|
||||
<div
|
||||
className="absolute inset-0"
|
||||
style={{
|
||||
backgroundImage: `url(${staticFile("einundzwanzig-wallpaper.png")})`,
|
||||
backgroundSize: `${width}px auto`,
|
||||
backgroundRepeat: "repeat-y",
|
||||
backgroundPosition: "center top",
|
||||
}}
|
||||
/>
|
||||
<div className="absolute inset-0 bg-neutral-950/88" />
|
||||
|
||||
{/* Bitcoin Effect */}
|
||||
<BitcoinEffect />
|
||||
|
||||
{/* Animated Logo Top */}
|
||||
<div
|
||||
className="absolute top-16 left-1/2 -translate-x-1/2"
|
||||
style={{
|
||||
opacity: logoOpacity,
|
||||
transform: `translateX(-50%) translateY(${logoY}px)`,
|
||||
}}
|
||||
>
|
||||
<AnimatedLogo size={160} delay={0} />
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="absolute inset-0 flex flex-col items-center justify-center px-8">
|
||||
{/* EINUNDZWANZIG Logo SVG - Full Width */}
|
||||
<div
|
||||
className="mb-12 w-full flex justify-center"
|
||||
style={{
|
||||
opacity: svgOpacity,
|
||||
transform: `scale(${svgScale})`,
|
||||
}}
|
||||
>
|
||||
<Img
|
||||
src={staticFile("einundzwanzig-horizontal-inverted.svg")}
|
||||
style={{ width: "90%", maxWidth: 900, height: "auto" }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Call to Action Text */}
|
||||
<div
|
||||
style={{
|
||||
opacity: ctaOpacity,
|
||||
transform: `translateY(${ctaY}px)`,
|
||||
}}
|
||||
>
|
||||
<p className="text-5xl text-white text-center font-semibold mb-12">
|
||||
Werde jetzt Mitglied!
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* URL - MAIN FOCUS - Full Width */}
|
||||
<div
|
||||
className="relative w-full"
|
||||
style={{
|
||||
opacity: urlOpacity,
|
||||
transform: `scale(${urlScale * urlPulse})`,
|
||||
}}
|
||||
>
|
||||
{/* Glow effect behind URL */}
|
||||
<div
|
||||
className="absolute inset-0 blur-3xl"
|
||||
style={{
|
||||
background: "radial-gradient(ellipse, #f7931a 0%, transparent 70%)",
|
||||
opacity: 0.5,
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* URL Container with border */}
|
||||
<div className="relative bg-neutral-800/80 backdrop-blur-sm rounded-3xl px-8 py-12 border-4 border-orange-500 shadow-2xl">
|
||||
<p className="text-4xl text-white text-center font-bold tracking-wide leading-tight">
|
||||
verein.einundzwanzig.space
|
||||
</p>
|
||||
|
||||
{/* Accent line */}
|
||||
<div className="h-2 bg-gradient-to-r from-transparent via-orange-500 to-transparent mt-8 rounded-full" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Footer Info */}
|
||||
<div
|
||||
className="absolute bottom-16 left-0 right-0 text-center px-8"
|
||||
style={{ opacity: footerOpacity }}
|
||||
>
|
||||
<p className="text-neutral-200 text-4xl mb-6 font-bold">
|
||||
Mitglieder-Vorteile:
|
||||
</p>
|
||||
<div className="flex justify-center gap-8 flex-wrap text-neutral-300 text-3xl font-medium">
|
||||
<span>🔗 Nostr Relay</span>
|
||||
<span>✓ NIP-05</span>
|
||||
<span>⚡ Watchtower</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</AbsoluteFill>
|
||||
);
|
||||
};
|
||||
134
videos/src/scenes/mobile/SaveButtonSceneMobile.tsx
Normal file
134
videos/src/scenes/mobile/SaveButtonSceneMobile.tsx
Normal file
@@ -0,0 +1,134 @@
|
||||
import { AbsoluteFill, interpolate, spring, useCurrentFrame, useVideoConfig, staticFile } from "remotion";
|
||||
import { BitcoinEffect } from "../../components/BitcoinEffect";
|
||||
import { AnimatedLogo } from "../../components/AnimatedLogo";
|
||||
|
||||
export const SaveButtonSceneMobile: React.FC = () => {
|
||||
const frame = useCurrentFrame();
|
||||
const { fps, width } = useVideoConfig();
|
||||
|
||||
const titleSpring = spring({
|
||||
frame,
|
||||
fps,
|
||||
config: { damping: 200 },
|
||||
});
|
||||
const titleOpacity = interpolate(titleSpring, [0, 1], [0, 1]);
|
||||
|
||||
// Button entrance
|
||||
const buttonSpring = spring({
|
||||
frame: frame - 0.5 * fps,
|
||||
fps,
|
||||
config: { damping: 15, stiffness: 100 },
|
||||
});
|
||||
const buttonScale = interpolate(buttonSpring, [0, 1], [0.5, 1]);
|
||||
const buttonOpacity = interpolate(buttonSpring, [0, 1], [0, 1]);
|
||||
|
||||
// Click animation
|
||||
const clickFrame = 2 * fps;
|
||||
const clickSpring = spring({
|
||||
frame: frame - clickFrame,
|
||||
fps,
|
||||
config: { damping: 10, stiffness: 200 },
|
||||
durationInFrames: 0.3 * fps,
|
||||
});
|
||||
const buttonPressScale = frame >= clickFrame ? interpolate(clickSpring, [0, 1], [0.92, 1]) : 1;
|
||||
|
||||
// Success animation
|
||||
const successSpring = spring({
|
||||
frame: frame - (clickFrame + 0.5 * fps),
|
||||
fps,
|
||||
config: { damping: 200 },
|
||||
});
|
||||
const successOpacity = interpolate(successSpring, [0, 1], [0, 1]);
|
||||
const successScale = interpolate(successSpring, [0, 1], [0, 1]);
|
||||
|
||||
// Cursor pointer - from left
|
||||
const cursorSpring = spring({
|
||||
frame: frame - 1.5 * fps,
|
||||
fps,
|
||||
config: { damping: 20, stiffness: 150 },
|
||||
});
|
||||
const cursorX = interpolate(cursorSpring, [0, 1], [-200, 0]);
|
||||
const cursorOpacity = interpolate(cursorSpring, [0, 1], [0, 1]);
|
||||
|
||||
return (
|
||||
<AbsoluteFill>
|
||||
{/* Tiled Wallpaper Background */}
|
||||
<div
|
||||
className="absolute inset-0"
|
||||
style={{
|
||||
backgroundImage: `url(${staticFile("einundzwanzig-wallpaper.png")})`,
|
||||
backgroundSize: `${width}px auto`,
|
||||
backgroundRepeat: "repeat-y",
|
||||
backgroundPosition: "center top",
|
||||
}}
|
||||
/>
|
||||
<div className="absolute inset-0 bg-neutral-950/75" />
|
||||
|
||||
{/* Bitcoin Effect */}
|
||||
<BitcoinEffect />
|
||||
|
||||
<div className="absolute inset-0 flex flex-col items-center justify-center px-8">
|
||||
<h2
|
||||
className="text-5xl font-bold text-white mb-24 text-center"
|
||||
style={{ opacity: titleOpacity }}
|
||||
>
|
||||
Schritt 2: Handle speichern
|
||||
</h2>
|
||||
|
||||
{/* Button Container */}
|
||||
<div className="relative">
|
||||
{/* Animated Cursor - from left */}
|
||||
<div
|
||||
className="absolute text-8xl pointer-events-none z-10"
|
||||
style={{
|
||||
left: "-120px",
|
||||
top: "50%",
|
||||
transform: `translateY(-50%) translateX(${cursorX}px)`,
|
||||
opacity: cursorOpacity,
|
||||
}}
|
||||
>
|
||||
👉
|
||||
</div>
|
||||
|
||||
{/* Save Button - Much Larger */}
|
||||
<button
|
||||
className="bg-neutral-800 text-white font-bold text-5xl px-24 py-10 rounded-2xl shadow-2xl border-4 border-neutral-600"
|
||||
style={{
|
||||
opacity: buttonOpacity,
|
||||
transform: `scale(${buttonScale * buttonPressScale})`,
|
||||
}}
|
||||
>
|
||||
Speichern
|
||||
</button>
|
||||
|
||||
{/* Success Message */}
|
||||
{frame >= clickFrame + 0.5 * fps && (
|
||||
<div
|
||||
className="absolute -bottom-32 left-1/2 -translate-x-1/2 whitespace-nowrap"
|
||||
style={{
|
||||
opacity: successOpacity,
|
||||
transform: `translateX(-50%) scale(${successScale})`,
|
||||
}}
|
||||
>
|
||||
<div className="bg-neutral-800 text-white px-10 py-6 rounded-2xl shadow-xl flex items-center gap-5 border-2 border-neutral-600">
|
||||
<span className="text-4xl">✓</span>
|
||||
<span className="text-3xl font-semibold">NIP-05 Handle gespeichert!</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 3D Success Logo */}
|
||||
{frame >= clickFrame + 0.5 * fps && (
|
||||
<div className="absolute top-32 right-8">
|
||||
<AnimatedLogo size={200} delay={clickFrame + 0.5 * fps} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<p className="text-neutral-200 text-center mt-48 text-3xl px-4 font-medium" style={{ opacity: titleOpacity }}>
|
||||
Nach dem Speichern wird dein Handle in Kürze automatisch aktiviert.
|
||||
</p>
|
||||
</div>
|
||||
</AbsoluteFill>
|
||||
);
|
||||
};
|
||||
99
videos/src/scenes/mobile/UIShowcaseSceneMobile.tsx
Normal file
99
videos/src/scenes/mobile/UIShowcaseSceneMobile.tsx
Normal file
@@ -0,0 +1,99 @@
|
||||
import { AbsoluteFill, interpolate, spring, useCurrentFrame, useVideoConfig, staticFile } from "remotion";
|
||||
import { BitcoinEffect } from "../../components/BitcoinEffect";
|
||||
import { AnimatedLogo } from "../../components/AnimatedLogo";
|
||||
|
||||
export const UIShowcaseSceneMobile: React.FC = () => {
|
||||
const frame = useCurrentFrame();
|
||||
const { fps, width } = useVideoConfig();
|
||||
|
||||
const cardSpring = spring({
|
||||
frame,
|
||||
fps,
|
||||
config: { damping: 20, stiffness: 200 },
|
||||
});
|
||||
|
||||
const cardScale = interpolate(cardSpring, [0, 1], [0.8, 1]);
|
||||
const cardOpacity = interpolate(cardSpring, [0, 1], [0, 1]);
|
||||
|
||||
const titleSpring = spring({
|
||||
frame: frame - 0.3 * fps,
|
||||
fps,
|
||||
config: { damping: 200 },
|
||||
});
|
||||
const titleY = interpolate(titleSpring, [0, 1], [50, 0]);
|
||||
const titleOpacity = interpolate(titleSpring, [0, 1], [0, 1]);
|
||||
|
||||
return (
|
||||
<AbsoluteFill>
|
||||
{/* Tiled Wallpaper Background */}
|
||||
<div
|
||||
className="absolute inset-0"
|
||||
style={{
|
||||
backgroundImage: `url(${staticFile("einundzwanzig-wallpaper.png")})`,
|
||||
backgroundSize: `${width}px auto`,
|
||||
backgroundRepeat: "repeat-y",
|
||||
backgroundPosition: "center top",
|
||||
}}
|
||||
/>
|
||||
<div className="absolute inset-0 bg-neutral-950/85" />
|
||||
|
||||
{/* Bitcoin Effect */}
|
||||
<BitcoinEffect />
|
||||
|
||||
{/* Animated Logos */}
|
||||
<div className="absolute top-16 left-8 opacity-60">
|
||||
<AnimatedLogo size={140} delay={0.2 * fps} />
|
||||
</div>
|
||||
<div className="absolute top-16 right-8 opacity-60">
|
||||
<AnimatedLogo size={140} delay={0.4 * fps} />
|
||||
</div>
|
||||
|
||||
{/* Main Content */}
|
||||
<div className="absolute inset-0 flex flex-col items-center justify-center px-8">
|
||||
<h2
|
||||
className="text-5xl font-bold text-white mb-12 text-center"
|
||||
style={{
|
||||
opacity: titleOpacity,
|
||||
transform: `translateY(${titleY}px)`,
|
||||
}}
|
||||
>
|
||||
Die NIP-05 Oberfläche
|
||||
</h2>
|
||||
|
||||
{/* UI Card Mockup - Full Width */}
|
||||
<div
|
||||
className="bg-neutral-50 rounded-3xl p-10 border-4 border-neutral-200 shadow-2xl w-full"
|
||||
style={{
|
||||
opacity: cardOpacity,
|
||||
transform: `scale(${cardScale})`,
|
||||
}}
|
||||
>
|
||||
<div className="flex items-start gap-6 mb-8">
|
||||
<div className="w-20 h-20 rounded-full bg-neutral-800 flex items-center justify-center flex-shrink-0">
|
||||
<div className="text-white text-5xl">✓</div>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3 className="text-4xl font-semibold text-neutral-800 mb-3">
|
||||
Get NIP-05 verified
|
||||
</h3>
|
||||
<p className="text-2xl text-neutral-600 leading-relaxed">
|
||||
Verifiziere deine Identität mit einem menschenlesbaren Nostr-Namen.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Input Preview */}
|
||||
<div className="space-y-4">
|
||||
<label className="block text-2xl font-medium text-neutral-700">
|
||||
Dein NIP-05 Handle
|
||||
</label>
|
||||
<div className="flex items-center bg-white rounded-2xl border-4 border-neutral-300 px-6 py-5">
|
||||
<div className="flex-1 text-3xl text-neutral-400">dein-name</div>
|
||||
<div className="text-2xl text-neutral-600 font-medium">@einundzwanzig.space</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</AbsoluteFill>
|
||||
);
|
||||
};
|
||||
147
videos/src/scenes/mobile/VerificationSceneMobile.tsx
Normal file
147
videos/src/scenes/mobile/VerificationSceneMobile.tsx
Normal file
@@ -0,0 +1,147 @@
|
||||
import { AbsoluteFill, interpolate, spring, useCurrentFrame, useVideoConfig, staticFile } from "remotion";
|
||||
import { BitcoinEffect } from "../../components/BitcoinEffect";
|
||||
import { AnimatedLogo } from "../../components/AnimatedLogo";
|
||||
|
||||
export const VerificationSceneMobile: React.FC = () => {
|
||||
const frame = useCurrentFrame();
|
||||
const { fps, width } = useVideoConfig();
|
||||
|
||||
const titleSpring = spring({
|
||||
frame,
|
||||
fps,
|
||||
config: { damping: 200 },
|
||||
});
|
||||
const titleOpacity = interpolate(titleSpring, [0, 1], [0, 1]);
|
||||
|
||||
// Badge entrance
|
||||
const badgeSpring = spring({
|
||||
frame: frame - 0.5 * fps,
|
||||
fps,
|
||||
config: { damping: 8 },
|
||||
});
|
||||
const badgeScale = interpolate(badgeSpring, [0, 1], [0, 1]);
|
||||
const badgeRotation = interpolate(badgeSpring, [0, 1], [-180, 0]);
|
||||
|
||||
// Handle list
|
||||
const listSpring = spring({
|
||||
frame: frame - 1.5 * fps,
|
||||
fps,
|
||||
config: { damping: 200 },
|
||||
});
|
||||
const listOpacity = interpolate(listSpring, [0, 1], [0, 1]);
|
||||
const listY = interpolate(listSpring, [0, 1], [50, 0]);
|
||||
|
||||
// Checkmark particles
|
||||
const particle1Spring = spring({
|
||||
frame: frame - 2 * fps,
|
||||
fps,
|
||||
config: { damping: 15 },
|
||||
});
|
||||
const particle1Y = interpolate(particle1Spring, [0, 1], [0, -120]);
|
||||
const particle1Opacity = interpolate(particle1Spring, [0, 0.7, 1], [0, 1, 0]);
|
||||
|
||||
const particle2Spring = spring({
|
||||
frame: frame - 2.2 * fps,
|
||||
fps,
|
||||
config: { damping: 15 },
|
||||
});
|
||||
const particle2Y = interpolate(particle2Spring, [0, 1], [0, -100]);
|
||||
const particle2Opacity = interpolate(particle2Spring, [0, 0.7, 1], [0, 1, 0]);
|
||||
|
||||
return (
|
||||
<AbsoluteFill>
|
||||
{/* Tiled Wallpaper Background */}
|
||||
<div
|
||||
className="absolute inset-0"
|
||||
style={{
|
||||
backgroundImage: `url(${staticFile("einundzwanzig-wallpaper.png")})`,
|
||||
backgroundSize: `${width}px auto`,
|
||||
backgroundRepeat: "repeat-y",
|
||||
backgroundPosition: "center top",
|
||||
}}
|
||||
/>
|
||||
<div className="absolute inset-0 bg-neutral-950/80" />
|
||||
|
||||
{/* Bitcoin Effect */}
|
||||
<BitcoinEffect />
|
||||
|
||||
{/* Animated EINUNDZWANZIG Logo */}
|
||||
<div className="absolute top-12 right-8">
|
||||
<AnimatedLogo size={140} delay={fps} />
|
||||
</div>
|
||||
|
||||
{/* Floating Checkmark Particles */}
|
||||
<div
|
||||
className="absolute top-1/3 left-16 text-7xl text-green-400"
|
||||
style={{
|
||||
opacity: particle1Opacity,
|
||||
transform: `translateY(${particle1Y}px)`,
|
||||
}}
|
||||
>
|
||||
✓
|
||||
</div>
|
||||
<div
|
||||
className="absolute top-1/3 right-20 text-6xl text-green-400"
|
||||
style={{
|
||||
opacity: particle2Opacity,
|
||||
transform: `translateY(${particle2Y}px)`,
|
||||
}}
|
||||
>
|
||||
✓
|
||||
</div>
|
||||
|
||||
<div className="absolute inset-0 flex flex-col items-center justify-center px-8">
|
||||
<h2
|
||||
className="text-5xl font-bold text-white mb-12 text-center"
|
||||
style={{ opacity: titleOpacity }}
|
||||
>
|
||||
Verifizierung erfolgreich!
|
||||
</h2>
|
||||
|
||||
{/* Success Badge - Full Width */}
|
||||
<div
|
||||
className="bg-white rounded-3xl p-8 shadow-2xl mb-8 w-full"
|
||||
style={{
|
||||
opacity: badgeScale,
|
||||
transform: `scale(${badgeScale}) rotate(${badgeRotation}deg)`,
|
||||
}}
|
||||
>
|
||||
<div className="flex items-center gap-6">
|
||||
<div className="w-24 h-24 rounded-full bg-neutral-800 flex items-center justify-center flex-shrink-0">
|
||||
<span className="text-6xl text-white">✓</span>
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-xl text-neutral-600 font-medium mb-2">Dein Handle ist aktiv:</p>
|
||||
<p className="text-3xl font-bold text-neutral-800 break-all">
|
||||
satoshi21@einundzwanzig.space
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Handle List - Full Width */}
|
||||
<div
|
||||
className="bg-white/10 backdrop-blur-sm rounded-3xl p-8 border-2 border-white/20 w-full"
|
||||
style={{
|
||||
opacity: listOpacity,
|
||||
transform: `translateY(${listY}px)`,
|
||||
}}
|
||||
>
|
||||
<p className="text-2xl font-semibold text-white mb-5">Deine aktivierten Handles:</p>
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center gap-4 bg-white/10 rounded-2xl px-6 py-5">
|
||||
<span className="text-2xl text-white flex-1 break-all">satoshi21@einundzwanzig.space</span>
|
||||
<span className="bg-neutral-800 text-white text-xl font-bold px-5 py-2 rounded-full border-2 border-neutral-600 flex-shrink-0">
|
||||
OK
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="text-neutral-200 text-center mt-12 text-3xl px-4 font-medium leading-relaxed" style={{ opacity: listOpacity }}>
|
||||
Dein NIP-05 Handle ist jetzt aktiv! Nostr-Clients zeigen ein Verifizierungs-Häkchen für dich an.
|
||||
</p>
|
||||
</div>
|
||||
</AbsoluteFill>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user