Skip to content

Commit cd2cc50

Browse files
committed
Add replay control to demo animation
1 parent abe0581 commit cd2cc50

1 file changed

Lines changed: 97 additions & 5 deletions

File tree

  • site/public/demos/deeppapernote-readme-demo

site/public/demos/deeppapernote-readme-demo/index.html

Lines changed: 97 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,59 @@
643643
text-transform: uppercase;
644644
}
645645

646+
.replay-button {
647+
position: absolute;
648+
left: 2.2%;
649+
bottom: 2.2%;
650+
z-index: 95;
651+
display: inline-flex;
652+
align-items: center;
653+
justify-content: center;
654+
gap: 8px;
655+
min-height: 36px;
656+
padding: 0 14px 0 12px;
657+
border: 1px solid rgba(250,249,245,0.14);
658+
border-radius: 999px;
659+
background: rgba(37,35,32,0.9);
660+
color: var(--cream);
661+
box-shadow: 0 18px 48px rgba(0,0,0,0.28), inset 0 1px 0 rgba(250,249,245,0.06);
662+
font-family: var(--sans);
663+
font-size: clamp(10px, 0.72vw, 14px);
664+
font-weight: 600;
665+
line-height: 1;
666+
cursor: pointer;
667+
opacity: 0;
668+
pointer-events: none;
669+
transform: translateY(8px);
670+
transition: opacity 180ms ease, transform 180ms ease, border-color 160ms ease, background 160ms ease;
671+
backdrop-filter: blur(14px);
672+
}
673+
674+
.replay-button svg {
675+
width: 16px;
676+
height: 16px;
677+
flex: 0 0 auto;
678+
color: var(--green);
679+
}
680+
681+
.replay-button.is-visible {
682+
opacity: 1;
683+
pointer-events: auto;
684+
transform: translateY(0);
685+
}
686+
687+
.replay-button:not(:disabled):hover,
688+
.replay-button:not(:disabled):focus-visible {
689+
border-color: rgba(204,120,92,0.55);
690+
background: rgba(47,44,40,0.96);
691+
outline: none;
692+
}
693+
694+
.replay-button:not(:disabled):hover svg,
695+
.replay-button:not(:disabled):focus-visible svg {
696+
color: var(--gold);
697+
}
698+
646699
.stage {
647700
background:
648701
radial-gradient(circle at 18% 16%, rgba(204,120,92,0.16), transparent 28%),
@@ -1004,12 +1057,22 @@ <h1>DeepPaperNote</h1>
10041057
<div class="final-rule"></div>
10051058
</section>
10061059

1060+
<button class="replay-button" id="replayButton" type="button" aria-label="Replay demo" disabled>
1061+
<svg viewBox="0 0 16 16" aria-hidden="true" focusable="false">
1062+
<path
1063+
d="M8.8 2.2a5.8 5.8 0 1 1-5.55 7.45.78.78 0 0 1 1.5-.43 4.25 4.25 0 1 0 .98-4.21h1.8a.75.75 0 0 1 0 1.5H3.75A.75.75 0 0 1 3 5.76V2a.75.75 0 0 1 1.5 0v1.74A5.77 5.77 0 0 1 8.8 2.2Z"
1064+
fill="currentColor"
1065+
/>
1066+
</svg>
1067+
<span>Replay</span>
1068+
</button>
10071069
<div class="watermark">Created by Huashu-Design</div>
10081070
</main>
10091071

10101072
<script>
10111073
const DURATION = 24;
1012-
const startTime = performance.now();
1074+
let playbackStartTime = performance.now();
1075+
let animationFrameId = 0;
10131076
const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
10141077
const $ = (id) => document.getElementById(id);
10151078

@@ -1027,6 +1090,7 @@ <h1>DeepPaperNote</h1>
10271090
phraseTwo: $("phraseTwo"),
10281091
phraseThree: $("phraseThree"),
10291092
final: $("final"),
1093+
replayButton: $("replayButton"),
10301094
nodes: [$("node0"), $("node1"), $("node2"), $("node3"), $("node4")],
10311095
labels: [$("label0"), $("label1"), $("label2"), $("label3"), $("label4")]
10321096
};
@@ -1106,32 +1170,60 @@ <h1>DeepPaperNote</h1>
11061170
}
11071171
}
11081172

1173+
function setReplayVisible(isVisible) {
1174+
els.replayButton.classList.toggle("is-visible", isVisible);
1175+
els.replayButton.disabled = !isVisible;
1176+
}
1177+
1178+
function startPlayback() {
1179+
window.__manualSeek = false;
1180+
playbackStartTime = performance.now();
1181+
setReplayVisible(false);
1182+
cancelAnimationFrame(animationFrameId);
1183+
1184+
if (reducedMotion) {
1185+
render(DURATION);
1186+
setReplayVisible(true);
1187+
return;
1188+
}
1189+
1190+
render(0);
1191+
animationFrameId = requestAnimationFrame(tick);
1192+
}
1193+
11091194
function tick(now) {
11101195
if (window.__manualSeek) return;
1111-
const t = reducedMotion ? 18 : (now - startTime) / 1000;
1196+
const t = (now - playbackStartTime) / 1000;
11121197
render(t);
11131198
if (t < DURATION) {
1114-
requestAnimationFrame(tick);
1199+
animationFrameId = requestAnimationFrame(tick);
11151200
} else {
11161201
render(DURATION);
1202+
animationFrameId = 0;
1203+
setReplayVisible(true);
11171204
}
11181205
}
11191206

11201207
window.__seek = (seconds) => {
11211208
window.__manualSeek = true;
1209+
cancelAnimationFrame(animationFrameId);
1210+
animationFrameId = 0;
1211+
setReplayVisible(Number(seconds) >= DURATION);
11221212
render(Number(seconds) || 0);
11231213
};
11241214

1215+
els.replayButton.addEventListener("click", startPlayback);
1216+
11251217
if (document.fonts && document.fonts.ready) {
11261218
document.fonts.ready.then(() => requestAnimationFrame(() => {
11271219
render(0);
11281220
window.__ready = true;
1129-
requestAnimationFrame(tick);
1221+
startPlayback();
11301222
}));
11311223
} else {
11321224
render(0);
11331225
window.__ready = true;
1134-
requestAnimationFrame(tick);
1226+
startPlayback();
11351227
}
11361228
</script>
11371229
</body>

0 commit comments

Comments
 (0)