Retour au catalogue

Video Cinematic

Section video cinematique avec barres letterbox 21:9, bouton play avec cercle SVG progress, titre qui fade au play.

videocomplex Dark Responsive a11y
darkelegantelegantuniversalagencyportfoliofullscreen
Theme
"use client";

import { useState } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { Play } from "lucide-react";

interface VideoCinematicProps {
  title?: string;
  subtitle?: string;
  ctaLabel?: string;
  duration?: string;
}

const EASE = [0.16, 1, 0.3, 1] as const;
const LETTERBOX = "12%";

export default function VideoCinematic({
  title = "Decouvrez notre film",
  subtitle = "Une experience visuelle unique",
  ctaLabel = "Regarder",
  duration = "2:34",
}: VideoCinematicProps) {
  const [isPlaying, setIsPlaying] = useState(false);
  const [progress, setProgress] = useState(0);

  const circumference = 2 * Math.PI * 34;

  const handlePlay = () => {
    setIsPlaying(true);
    setProgress(0);
    const interval = setInterval(() => {
      setProgress((prev) => {
        if (prev >= 100) {
          clearInterval(interval);
          return 100;
        }
        return prev + 0.5;
      });
    }, 50);
  };

  return (
    <section
      style={{
        position: "relative",
        width: "100%",
        minHeight: "100vh",
        display: "flex",
        alignItems: "center",
        justifyContent: "center",
        overflow: "hidden",
        background: "#000",
      }}
    >
      {/* Video background placeholder */}
      <div
        style={{
          position: "absolute",
          inset: 0,
          background: `
            radial-gradient(ellipse at center,
              color-mix(in srgb, var(--color-accent) 6%, transparent),
              transparent 60%
            ),
            linear-gradient(180deg, #0a0a0a 0%, #111 50%, #0a0a0a 100%)
          `,
        }}
      />

      {/* Letterbox bars — cinematic 21:9 */}
      <div
        aria-hidden
        style={{
          position: "absolute",
          top: 0,
          left: 0,
          right: 0,
          height: LETTERBOX,
          background: "#000",
          zIndex: 3,
        }}
      />
      <div
        aria-hidden
        style={{
          position: "absolute",
          bottom: 0,
          left: 0,
          right: 0,
          height: LETTERBOX,
          background: "#000",
          zIndex: 3,
        }}
      />

      {/* Content */}
      <div
        style={{
          position: "relative",
          zIndex: 2,
          textAlign: "center",
          padding: "0 var(--container-padding-x)",
        }}
      >
        {/* Title — fades out when playing */}
        <AnimatePresence>
          {!isPlaying && (
            <motion.div
              initial={{ opacity: 0, y: 20 }}
              animate={{ opacity: 1, y: 0 }}
              exit={{ opacity: 0, y: -20 }}
              transition={{ duration: 0.6, ease: EASE }}
            >
              <h2
                style={{
                  fontFamily: "var(--font-sans)",
                  fontSize: "clamp(2rem, 5vw, 4rem)",
                  fontWeight: 700,
                  lineHeight: 1.1,
                  letterSpacing: "-0.03em",
                  color: "#f5f5f5",
                  marginBottom: "0.75rem",
                }}
              >
                {title}
              </h2>
              <p
                style={{
                  fontSize: "1.125rem",
                  color: "rgba(255,255,255,0.5)",
                  marginBottom: "3rem",
                }}
              >
                {subtitle}
              </p>
            </motion.div>
          )}
        </AnimatePresence>

        {/* Play button with SVG progress ring */}
        <motion.button
          whileHover={{ scale: 1.06 }}
          whileTap={{ scale: 0.95 }}
          onClick={handlePlay}
          style={{
            position: "relative",
            display: "inline-flex",
            alignItems: "center",
            justifyContent: "center",
            width: "88px",
            height: "88px",
            borderRadius: "50%",
            background: "none",
            border: "none",
            cursor: "pointer",
          }}
        >
          {/* Progress ring SVG */}
          <svg
            width="88"
            height="88"
            style={{
              position: "absolute",
              top: 0,
              left: 0,
              transform: "rotate(-90deg)",
            }}
          >
            {/* Track */}
            <circle
              cx="44"
              cy="44"
              r="34"
              fill="none"
              stroke="rgba(255,255,255,0.15)"
              strokeWidth="2"
            />
            {/* Progress */}
            <circle
              cx="44"
              cy="44"
              r="34"
              fill="none"
              stroke="var(--color-accent)"
              strokeWidth="2.5"
              strokeLinecap="round"
              strokeDasharray={circumference}
              strokeDashoffset={circumference - (progress / 100) * circumference}
              style={{ transition: "stroke-dashoffset 0.1s linear" }}
            />
          </svg>

          {/* Play icon */}
          <div
            style={{
              width: "56px",
              height: "56px",
              borderRadius: "50%",
              background: "rgba(255,255,255,0.1)",
              backdropFilter: "blur(8px)",
              display: "flex",
              alignItems: "center",
              justifyContent: "center",
              border: "1px solid rgba(255,255,255,0.2)",
            }}
          >
            <Play
              style={{
                width: 22,
                height: 22,
                color: "#fff",
                marginLeft: 2,
              }}
            />
          </div>
        </motion.button>

        {/* Label & duration */}
        <motion.div
          initial={{ opacity: 0 }}
          animate={{ opacity: 1 }}
          transition={{ delay: 0.4, duration: 0.5 }}
          style={{
            marginTop: "1.5rem",
            display: "flex",
            alignItems: "center",
            justifyContent: "center",
            gap: "0.75rem",
          }}
        >
          <span
            style={{
              fontSize: "0.875rem",
              fontWeight: 500,
              color: "rgba(255,255,255,0.6)",
            }}
          >
            {ctaLabel}
          </span>
          <span
            style={{
              fontSize: "0.75rem",
              color: "rgba(255,255,255,0.3)",
              padding: "0.2rem 0.5rem",
              borderRadius: "var(--radius-sm)",
              background: "rgba(255,255,255,0.06)",
            }}
          >
            {duration}
          </span>
        </motion.div>
      </div>
    </section>
  );
}

Avis

Video Cinematic — React Video Section — Incubator