Remotion LabRemotion Lab
返回模板庫

先想通路再做產品

先展示 Build → Distribution 順序,金色星星好產品逐漸被灰色產品淹沒;接著兩個方塊互換位置,Distribution → Build,燈泡亮起,好產品重新閃耀。

通路產品策略動畫換位
提示詞(可直接修改內容)
import React from "react";
import {
  AbsoluteFill,
  useCurrentFrame,
  useVideoConfig,
  interpolate,
  spring,
} from "remotion";

export const Scene145-DistributionFirst: React.FC = () => {
  const frame = useCurrentFrame();
  const { fps } = useVideoConfig();

  const fadeIn = interpolate(frame, [0, 8], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
  const fadeOut = interpolate(frame, [160, 180], [1, 0], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
  const masterOpacity = Math.min(fadeIn, fadeOut);

  const CY = 400;
  const LEFT-X = 620;
  const RIGHT-X = 1300;
  const BOX-W = 200;
  const BOX-H = 200;
  const BOX-R = 28;

  const buildSpring = spring({ frame: Math.max(0, frame - 5), fps, config: { damping: 12, stiffness: 80 } });
  const arrowSpring = spring({ frame: Math.max(0, frame - 20), fps, config: { damping: 14, stiffness: 70 } });
  const distSpring = spring({ frame: Math.max(0, frame - 30), fps, config: { damping: 12, stiffness: 80 } });

  const starAppear = spring({ frame: Math.max(0, frame - 50), fps, config: { damping: 10, stiffness: 100 } });
  const GREY-PRODUCTS = 18;
  const greyDelay = (i: number) => 62 + i * 1.5;
  const starDim = interpolate(frame, [70, 95], [1, 0.12], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });

  const swapProgress = interpolate(frame, [100, 125], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
  const swapEased = swapProgress < 0.5 ? 2 * swapProgress * swapProgress : 1 - Math.pow(-2 * swapProgress + 2, 2) / 2;

  const buildBoxX = LEFT-X + (RIGHT-X - LEFT-X) * swapEased;
  const distBoxX = RIGHT-X - (RIGHT-X - LEFT-X) * swapEased;
  const swapArcBuild = Math.sin(swapEased * Math.PI) * -60;
  const swapArcDist = Math.sin(swapEased * Math.PI) * 60;

  const bulbSpring = spring({ frame: Math.max(0, frame - 125), fps, config: { damping: 10, stiffness: 90 } });
  const starRevive = interpolate(frame, [130, 150], [0.12, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });

  const xMarkOp = interpolate(frame, [88, 98], [0, 0.8], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
  const xMarkFade = interpolate(frame, [100, 110], [1, 0], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
  const checkOp = interpolate(frame, [128, 140], [0, 0.9], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
  const greyFade = interpolate(frame, [98, 112], [1, 0], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
  const bulbPulse = frame > 130 ? 1 + Math.sin(frame * 0.12) * 0.08 : 1;

  return (
    <AbsoluteFill style={{ background: "linear-gradient(135deg, #0A0E14 0%, #111825 100%)", opacity: masterOpacity }}>
      <svg width={1920} height={1080} viewBox="0 0 1920 1080">
        <defs>
          <filter id="s145glow">
            <feGaussianBlur stdDeviation="6" result="blur" />
            <feMerge><feMergeNode in="blur" /><feMergeNode in="SourceGraphic" /></feMerge>
          </filter>
          <filter id="s145starGlow">
            <feGaussianBlur stdDeviation="10" result="blur" />
            <feMerge><feMergeNode in="blur" /><feMergeNode in="SourceGraphic" /></feMerge>
          </filter>
        </defs>

        <g opacity={buildSpring} transform={`translate(${buildBoxX + swapArcBuild * 0},${CY + swapArcBuild}) scale(${buildSpring})`} style={{ transformOrigin: `${buildBoxX}px ${CY}px` }}>
          <rect x={-BOX-W / 2} y={-BOX-H / 2} width={BOX-W} height={BOX-H} rx={BOX-R} fill="rgba(77,163,255,0.12)" stroke="#4DA3FF" strokeWidth={3} />
          <line x1={-20} y1={35} x2={20} y2={-25} stroke="#4DA3FF" strokeWidth={6} strokeLinecap="round" />
          <rect x={8} y={-42} width={32} height={18} rx={4} fill="#4DA3FF" transform="rotate(-35 24 -33)" />
          <text x={0} y={BOX-H / 2 + 38} textAnchor="middle" fontSize={42} fontWeight={700} fill="#4DA3FF" fontFamily="'Inter', sans-serif" opacity={0.9}>Build</text>
        </g>

        <g opacity={distSpring} transform={`translate(${distBoxX},${CY + swapArcDist}) scale(${distSpring})`} style={{ transformOrigin: `${distBoxX}px ${CY}px` }}>
          <rect x={-BOX-W / 2} y={-BOX-H / 2} width={BOX-W} height={BOX-H} rx={BOX-R} fill="rgba(16,185,129,0.12)" stroke="#10B981" strokeWidth={3} />
          <path d="M -15 -25 L 35 -45 L 35 45 L -15 25 Z" fill="#10B981" opacity={0.85} />
          <rect x={-25} y={-18} width={12} height={36} rx={4} fill="#34D399" />
          {[0, 1, 2].map((j) => (
            <path key={j} d={`M ${42 + j * 14} ${-20 - j * 8} Q ${50 + j * 14} 0 ${42 + j * 14} ${20 + j * 8}`} fill="none" stroke="#34D399" strokeWidth={2.5} opacity={0.4 + j * 0.1} strokeLinecap="round" />
          ))}
          <text x={0} y={BOX-H / 2 + 38} textAnchor="middle" fontSize={42} fontWeight={700} fill="#10B981" fontFamily="'Inter', sans-serif" opacity={0.9}>Distribution</text>
        </g>

        <g opacity={arrowSpring}>
          <g transform={`translate(960, ${CY})`}>
            <line x1={-80} y1={0} x2={60} y2={0} stroke="rgba(255,255,255,0.5)" strokeWidth={3} strokeLinecap="round" />
            <polygon points="65,-8 80,0 65,8" fill="rgba(255,255,255,0.5)" />
          </g>
        </g>

        <g opacity={starAppear * (frame < 130 ? starDim : starRevive)}>
          <g transform={`translate(960, ${CY + 220})`}>
            <circle cx={0} cy={0} r={55} fill="none" stroke="#FBBF24" strokeWidth={2} opacity={0.3 * (frame < 130 ? starDim : starRevive)} filter="url(#s145starGlow)" />
            <polygon points={[0,1,2,3,4,5,6,7,8,9].map((j) => { const angle=(j*36-90)*Math.PI/180; const r=j%2===0?40:18; return `${Math.cos(angle)*r},${Math.sin(angle)*r}`; }).join(" ")} fill="#FBBF24" stroke="#FDE68A" strokeWidth={2} filter="url(#s145glow)" />
            <circle cx={0} cy={0} r={10} fill="#FEF3C7" opacity={0.7} />
          </g>
        </g>

        <g opacity={greyFade}>
          {Array.from({ length: GREY-PRODUCTS }, (_, i) => {
            const gFrame = Math.max(0, frame - greyDelay(i));
            if (gFrame <= 0) return null;
            const gSpring = spring({ frame: gFrame, fps, config: { damping: 8, stiffness: 120, mass: 0.5 } });
            const seed1 = Math.sin(i * 73.1 + 17) * 43758.5453;
            const seed2 = Math.sin(i * 127.3 + 53) * 43758.5453;
            const rx = (seed1 - Math.floor(seed1) - 0.5) * 280;
            const ry = (seed2 - Math.floor(seed2) - 0.5) * 160;
            const gx = 960 + rx, gy = CY + 220 + ry;
            const gs = 28 + (i % 5) * 5;
            const rot = ((i * 37) % 30) - 15;
            return (
              <g key={`grey-${i}`} opacity={gSpring * 0.7} transform={`translate(${gx},${gy}) scale(${gSpring}) rotate(${rot})`}>
                <rect x={-gs / 2} y={-gs / 2} width={gs} height={gs} rx={gs * 0.22} fill="#374151" stroke="#4B5563" strokeWidth={1.5} />
              </g>
            );
          })}
        </g>

        <g opacity={xMarkOp * xMarkFade} transform={`translate(960, ${CY - 80})`}>
          <circle cx={0} cy={0} r={32} fill="rgba(239,68,68,0.15)" stroke="#EF4444" strokeWidth={3} />
          <line x1={-14} y1={-14} x2={14} y2={14} stroke="#EF4444" strokeWidth={4} strokeLinecap="round" />
          <line x1={14} y1={-14} x2={-14} y2={14} stroke="#EF4444" strokeWidth={4} strokeLinecap="round" />
        </g>

        <g opacity={checkOp} transform={`translate(960, ${CY - 80})`}>
          <circle cx={0} cy={0} r={32} fill="rgba(16,185,129,0.15)" stroke="#10B981" strokeWidth={3} />
          <path d="M -14 2 L -4 12 L 16 -10" fill="none" stroke="#10B981" strokeWidth={4} strokeLinecap="round" strokeLinejoin="round" />
        </g>

        <g opacity={bulbSpring} transform={`translate(960, 140) scale(${bulbSpring * bulbPulse})`}>
          <circle cx={0} cy={0} r={60} fill="#FBBF24" opacity={0.08} filter="url(#s145starGlow)" />
          <path d="M 0 -35 C -22 -35 -30 -15 -30 0 C -30 14 -18 24 -14 30 L 14 30 C 18 24 30 14 30 0 C 30 -15 22 -35 0 -35 Z" fill="#FBBF24" stroke="#FDE68A" strokeWidth={2} />
          <path d="M -6 10 Q 0 -5 6 10" fill="none" stroke="#FEF3C7" strokeWidth={2.5} />
          <rect x={-12} y={30} width={24} height={6} rx={2} fill="#D97706" />
          <rect x={-10} y={36} width={20} height={5} rx={2} fill="#B45309" />
          {[0, 45, 90, 135, 180, 225, 270, 315].map((deg) => {
            const rad = deg * Math.PI / 180;
            const rayPulse = 1 + Math.sin(frame * 0.1 + deg) * 0.2;
            return <line key={deg} x1={Math.cos(rad) * 48} y1={Math.sin(rad) * 48} x2={Math.cos(rad) * (58 + 6 * rayPulse)} y2={Math.sin(rad) * (58 + 6 * rayPulse)} stroke="#FBBF24" strokeWidth={2.5} strokeLinecap="round" opacity={0.6} />;
          })}
        </g>
      </svg>
    </AbsoluteFill>
  );
};

登入後查看完整程式碼