← GPT-AI-VIDEO
CODE · 4.1 KB

remotion/FiveRoutesHook.jsx

Workspace snapshot · 09/04 14:52

import React from "react";
import { AbsoluteFill, Easing, Img, interpolate, useCurrentFrame } from "remotion";
import directions from "../assets/public/pengin-corp-renewal-2025/design-directions.png";

const ink = "#07162d";
const paper = "#f7f6f1";
const blue = "#1757a6";
const orange = "#ff5d32";
const acid = "#d9ff43";
const clamp = { extrapolateLeft: "clamp", extrapolateRight: "clamp" };
const easeOut = Easing.bezier(0.16, 1, 0.3, 1);

const routes = [
  { label: "足跡", cropX: 14, cropW: 160, width: 148, x: 26, phase: 0.1, amp: -16 },
  { label: "冒険 / 海に潜る", cropX: 185, cropW: 302, width: 250, x: 178, phase: 0.6, amp: 12 },
  { label: "冒険 / 深海制覇", cropX: 497, cropW: 161, width: 190, x: 445, phase: 0, amp: 0, selected: true },
  { label: "進化するキャンバス", cropX: 669, cropW: 160, width: 176, x: 651, phase: 1.1, amp: -13 },
  { label: "無限のワクワク", cropX: 840, cropW: 162, width: 168, x: 846, phase: 1.8, amp: 17 },
];

const cropHeight = 342;
const sourceWidth = 1010;

function RouteBoard({ route, frame }) {
  const drop = interpolate(frame, [0, 16, 23], [-190, 20, 0], { ...clamp, easing: easeOut });
  const elapsed = Math.max(0, frame - 15);
  const damping = Math.exp(-elapsed / 42);
  const swing = route.selected ? Math.sin(elapsed * 0.33) * 2.2 * Math.exp(-elapsed / 18) : Math.sin(elapsed * 0.24 + route.phase) * route.amp * damping;
  const boardScale = route.width / route.cropW;
  const boardHeight = cropHeight * boardScale;
  return <div style={{ position: "absolute", left: route.x, top: 300 + drop, width: route.width, height: boardHeight, transformOrigin: "50% -235px", transform: `rotate(${swing}deg)`, zIndex: route.selected ? 6 : 3 }}>
    <div style={{ position: "absolute", left: "50%", bottom: "100%", width: 4, height: 235, marginLeft: -2, background: route.selected ? ink : "#967a5a" }} />
    <div style={{ position: "absolute", left: "50%", top: -7, width: 15, height: 15, marginLeft: -7.5, borderRadius: "50%", border: `4px solid ${route.selected ? ink : "#967a5a"}`, background: paper, zIndex: 4 }} />
    <div style={{ position: "relative", width: route.width, height: boardHeight, overflow: "hidden", background: "#eee", boxShadow: "0 22px 50px rgba(7,22,45,.18)" }}>
      <Img src={directions} style={{ position: "absolute", width: sourceWidth * boardScale, height: boardHeight, left: -route.cropX * boardScale, top: 0 }} />
    </div>
    {route.selected && <div style={{ position: "absolute", top: -58, left: "50%", width: 92, height: 64, marginLeft: -46, background: orange, clipPath: "polygon(12% 0,88% 0,100% 25%,86% 100%,14% 100%,0 25%)", boxShadow: "0 12px 28px rgba(255,93,50,.28)" }}><div style={{ position: "absolute", inset: 16, border: "5px solid white" }} /></div>}
  </div>;
}

export function FiveRoutesHook() {
  const frame = useCurrentFrame();
  return <AbsoluteFill style={{ background: paper, color: ink, overflow: "hidden", fontFamily: '"Yu Gothic", "Hiragino Sans", sans-serif' }}>
    <div style={{ position: "absolute", inset: 0, opacity: .11, backgroundImage: `radial-gradient(${blue} 0.8px, transparent 0.8px)`, backgroundSize: "10px 10px" }} />
    <div style={{ position: "absolute", top: 54, left: 54, right: 54, display: "flex", justifyContent: "space-between", fontFamily: "Arial", fontSize: 18, fontWeight: 900, letterSpacing: ".15em" }}><span>PENGIN / FIVE ROUTES</span><span>01</span></div>
    <div style={{ position: "absolute", left: 0, right: 0, top: 120, height: 980 }}>{routes.map((route) => <RouteBoard route={route} frame={frame} key={route.label} />)}</div>
    <div style={{ position: "absolute", left: 58, right: 58, bottom: 92 }}>
      <div style={{ display: "inline-block", padding: "10px 14px", background: acid, fontFamily: "Arial", fontSize: 18, fontWeight: 900, letterSpacing: ".12em", marginBottom: 28 }}>5 DIRECTIONS / 1 DECISION</div>
      <div style={{ fontSize: 112, fontWeight: 950, lineHeight: .93, letterSpacing: "-.075em" }}>5案。<br/><span style={{ color: blue }}>1つも、</span><br/>妥協しない。</div>
      <div style={{ width: 620, height: 14, marginTop: 38, background: orange }} />
    </div>
  </AbsoluteFill>;
}