Mountain: pause on click + figure 3x bigger
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
db7d7530f4
commit
badba0a9f7
1 changed files with 30 additions and 21 deletions
|
|
@ -59,6 +59,7 @@ const CARD_H = 128;
|
||||||
|
|
||||||
export default function MountainClimb() {
|
export default function MountainClimb() {
|
||||||
const [step, setStep] = useState(-1);
|
const [step, setStep] = useState(-1);
|
||||||
|
const [paused, setPaused] = useState(false);
|
||||||
const sectionRef = useRef<HTMLDivElement>(null);
|
const sectionRef = useRef<HTMLDivElement>(null);
|
||||||
const started = useRef(false);
|
const started = useRef(false);
|
||||||
|
|
||||||
|
|
@ -79,12 +80,12 @@ export default function MountainClimb() {
|
||||||
return () => obs.disconnect();
|
return () => obs.disconnect();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Advance one step every 1.6 s
|
// Advance one step every 1.6 s — paused when user clicked
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (step < 0 || step >= STEPS.length - 1) return;
|
if (step < 0 || step >= STEPS.length - 1 || paused) return;
|
||||||
const t = setTimeout(() => setStep((s) => s + 1), 1600);
|
const t = setTimeout(() => setStep((s) => s + 1), 1600);
|
||||||
return () => clearTimeout(t);
|
return () => clearTimeout(t);
|
||||||
}, [step]);
|
}, [step, paused]);
|
||||||
|
|
||||||
const atSummit = step === STEPS.length - 1;
|
const atSummit = step === STEPS.length - 1;
|
||||||
const wp = step >= 0 ? WP[step] : WP[0];
|
const wp = step >= 0 ? WP[step] : WP[0];
|
||||||
|
|
@ -92,8 +93,8 @@ export default function MountainClimb() {
|
||||||
// Card x: centred on person, clamped inside viewBox
|
// Card x: centred on person, clamped inside viewBox
|
||||||
const rawCX = wp.x - CARD_W / 2;
|
const rawCX = wp.x - CARD_W / 2;
|
||||||
const cardX = Math.min(Math.max(rawCX, 8), VW - CARD_W - 8);
|
const cardX = Math.min(Math.max(rawCX, 8), VW - CARD_W - 8);
|
||||||
// Card y: above figure head, clamped to top of viewBox
|
// Card y: above figure head (figure is 3× scaled → head at ~87 SVG units above foot)
|
||||||
const cardY = Math.max(wp.y - CARD_H - 58, 4);
|
const cardY = Math.max(wp.y - CARD_H - 110, 4);
|
||||||
|
|
||||||
const replay = () => {
|
const replay = () => {
|
||||||
started.current = true;
|
started.current = true;
|
||||||
|
|
@ -115,7 +116,12 @@ export default function MountainClimb() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* ── Mountain animation (md+) ── */}
|
{/* ── Mountain animation (md+) ── */}
|
||||||
<div className="hidden md:block">
|
<div
|
||||||
|
className="hidden md:block"
|
||||||
|
onClick={() => !atSummit && setPaused((p) => !p)}
|
||||||
|
style={{ cursor: atSummit ? "default" : "pointer" }}
|
||||||
|
title={paused ? "Klicken um fortzusetzen" : "Klicken zum Pausieren"}
|
||||||
|
>
|
||||||
<svg
|
<svg
|
||||||
viewBox={`0 0 ${VW} ${VH}`}
|
viewBox={`0 0 ${VW} ${VH}`}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
|
|
@ -152,27 +158,23 @@ export default function MountainClimb() {
|
||||||
transform={`translate(${wp.x}, ${wp.y}) rotate(${wp.lean})`}
|
transform={`translate(${wp.x}, ${wp.y}) rotate(${wp.lean})`}
|
||||||
style={{ transition: "transform 0.75s cubic-bezier(0.4,0.2,0.2,1)" }}
|
style={{ transition: "transform 0.75s cubic-bezier(0.4,0.2,0.2,1)" }}
|
||||||
>
|
>
|
||||||
{/* Inner group — receives bounce animation at summit */}
|
{/* Inner group — 3× scale + bounce at summit */}
|
||||||
<g
|
<g
|
||||||
|
transform="scale(3)"
|
||||||
className={atSummit ? "mc-bounce" : ""}
|
className={atSummit ? "mc-bounce" : ""}
|
||||||
style={{ transformBox: "fill-box", transformOrigin: "50% 100%" }}
|
style={{ transformBox: "fill-box", transformOrigin: "50% 100%" }}
|
||||||
>
|
>
|
||||||
{/* Head */}
|
{/* strokeWidth 0.5 = visually 1.5 after scale(3) */}
|
||||||
<circle cx="0" cy="-29" r="7.5" fill="none" stroke="#4f46e5" strokeWidth="1.5" />
|
<circle cx="0" cy="-29" r="7.5" fill="none" stroke="#4f46e5" strokeWidth="0.5" />
|
||||||
{/* Body */}
|
<line x1="0" y1="-21" x2="0" y2="-3" stroke="#4f46e5" strokeWidth="0.5" strokeLinecap="round" />
|
||||||
<line x1="0" y1="-21" x2="0" y2="-3" stroke="#4f46e5" strokeWidth="1.5" strokeLinecap="round" />
|
<line x1="0" y1="-16" x2="-10" y2="-8" stroke="#4f46e5" strokeWidth="0.5" strokeLinecap="round" />
|
||||||
{/* Left arm (down, holding mountain) */}
|
<line x1="0" y1="-16" x2="10" y2="-24" stroke="#4f46e5" strokeWidth="0.5" strokeLinecap="round" />
|
||||||
<line x1="0" y1="-16" x2="-10" y2="-8" stroke="#4f46e5" strokeWidth="1.5" strokeLinecap="round" />
|
<line x1="0" y1="-3" x2="-7" y2="10" stroke="#4f46e5" strokeWidth="0.5" strokeLinecap="round" />
|
||||||
{/* Right arm (raised) */}
|
<line x1="0" y1="-3" x2="7" y2="10" stroke="#4f46e5" strokeWidth="0.5" strokeLinecap="round" />
|
||||||
<line x1="0" y1="-16" x2="10" y2="-24" stroke="#4f46e5" strokeWidth="1.5" strokeLinecap="round" />
|
|
||||||
{/* Legs */}
|
|
||||||
<line x1="0" y1="-3" x2="-7" y2="10" stroke="#4f46e5" strokeWidth="1.5" strokeLinecap="round" />
|
|
||||||
<line x1="0" y1="-3" x2="7" y2="10" stroke="#4f46e5" strokeWidth="1.5" strokeLinecap="round" />
|
|
||||||
|
|
||||||
{/* Flag — appears at summit, attached to raised right arm */}
|
|
||||||
{atSummit && (
|
{atSummit && (
|
||||||
<g className="mc-fade-in" transform="translate(9, -25)">
|
<g className="mc-fade-in" transform="translate(9, -25)">
|
||||||
<line x1="0" y1="0" x2="0" y2="-22" stroke="#6366f1" strokeWidth="1.5" strokeLinecap="round" />
|
<line x1="0" y1="0" x2="0" y2="-22" stroke="#6366f1" strokeWidth="0.5" strokeLinecap="round" />
|
||||||
<polygon points="0,-22 18,-15 0,-8" fill="#6366f1" />
|
<polygon points="0,-22 18,-15 0,-8" fill="#6366f1" />
|
||||||
</g>
|
</g>
|
||||||
)}
|
)}
|
||||||
|
|
@ -188,7 +190,7 @@ export default function MountainClimb() {
|
||||||
x1={cardX + CARD_W / 2}
|
x1={cardX + CARD_W / 2}
|
||||||
y1={cardY + CARD_H}
|
y1={cardY + CARD_H}
|
||||||
x2={wp.x}
|
x2={wp.x}
|
||||||
y2={wp.y - 37}
|
y2={wp.y - 90}
|
||||||
stroke="#c7d2fe"
|
stroke="#c7d2fe"
|
||||||
strokeWidth="1"
|
strokeWidth="1"
|
||||||
strokeDasharray="4 3"
|
strokeDasharray="4 3"
|
||||||
|
|
@ -236,6 +238,13 @@ export default function MountainClimb() {
|
||||||
)}
|
)}
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
|
{/* Pause hint */}
|
||||||
|
{step >= 0 && !atSummit && (
|
||||||
|
<p className="text-center text-xs text-slate-400 mt-2 select-none">
|
||||||
|
{paused ? "▶ Klicken zum Fortsetzen" : "⏸ Klicken zum Pausieren"}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Replay button */}
|
{/* Replay button */}
|
||||||
{atSummit && (
|
{atSummit && (
|
||||||
<div className="text-center mt-4">
|
<div className="text-center mt-4">
|
||||||
|
|
|
||||||
Reference in a new issue