style: format all block HTML files with oxfmt

This commit is contained in:
Miguel Ángel
2026-05-06 01:02:25 -07:00
parent 20c1341cc8
commit 73c4aaf08d
12 changed files with 6090 additions and 4496 deletions
+74 -65
View File
@@ -12,8 +12,13 @@
/>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body {
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html,
body {
margin: 0;
width: 1920px;
height: 1080px;
@@ -30,7 +35,7 @@
}
#captions-bounce::before {
content: '';
content: "";
position: absolute;
bottom: -80px;
left: 50%;
@@ -58,7 +63,7 @@
.caption-group .word-pill {
display: inline-block;
font-family: 'Inter', system-ui, sans-serif;
font-family: "Inter", system-ui, sans-serif;
font-weight: 800;
font-size: 64px;
line-height: 1;
@@ -71,9 +76,15 @@
}
/* Alternating pill colors */
.word-pill-0 { background: rgba(0, 212, 255, 0.85); }
.word-pill-1 { background: rgba(139, 92, 246, 0.85); }
.word-pill-2 { background: rgba(16, 185, 129, 0.85); }
.word-pill-0 {
background: rgba(0, 212, 255, 0.85);
}
.word-pill-1 {
background: rgba(139, 92, 246, 0.85);
}
.word-pill-2 {
background: rgba(16, 185, 129, 0.85);
}
.driver {
position: absolute;
@@ -99,76 +110,74 @@
</div>
<script>
(function () {
var GROUPS = [
{ text: "Write HTML", start: 0.3, end: 1.8 },
{ text: "render video", start: 2.0, end: 3.5 },
{ text: "No timeline editors", start: 3.8, end: 5.5 },
{ text: "no After Effects", start: 5.7, end: 7.2 },
{ text: "Ship 10x faster", start: 7.5, end: 9.5 },
{ text: "with HyperFrames", start: 9.8, end: 12.0 }
];
(function () {
var GROUPS = [
{ text: "Write HTML", start: 0.3, end: 1.8 },
{ text: "render video", start: 2.0, end: 3.5 },
{ text: "No timeline editors", start: 3.8, end: 5.5 },
{ text: "no After Effects", start: 5.7, end: 7.2 },
{ text: "Ship 10x faster", start: 7.5, end: 9.5 },
{ text: "with HyperFrames", start: 9.8, end: 12.0 },
];
var PILL_COLORS = ["word-pill-0", "word-pill-1", "word-pill-2"];
var PILL_COLORS = ["word-pill-0", "word-pill-1", "word-pill-2"];
var root = document.getElementById("captions-bounce");
var root = document.getElementById("captions-bounce");
// Build caption group elements with per-word pill spans
GROUPS.forEach(function (group, gi) {
var div = document.createElement("div");
div.className = "caption-group";
div.id = "cg-" + gi;
// Build caption group elements with per-word pill spans
GROUPS.forEach(function (group, gi) {
var div = document.createElement("div");
div.className = "caption-group";
div.id = "cg-" + gi;
var words = group.text.split(" ");
words.forEach(function (word, wi) {
var span = document.createElement("span");
span.className = "word-pill " + PILL_COLORS[wi % PILL_COLORS.length];
span.id = "cg-" + gi + "-w" + wi;
span.textContent = word;
div.appendChild(span);
var words = group.text.split(" ");
words.forEach(function (word, wi) {
var span = document.createElement("span");
span.className = "word-pill " + PILL_COLORS[wi % PILL_COLORS.length];
span.id = "cg-" + gi + "-w" + wi;
span.textContent = word;
div.appendChild(span);
});
root.appendChild(div);
});
root.appendChild(div);
});
// Build timeline
var tl = gsap.timeline({ paused: true });
// Build timeline
var tl = gsap.timeline({ paused: true });
GROUPS.forEach(function (group, gi) {
var el = "#cg-" + gi;
var words = group.text.split(" ");
var staggerDelay = 0.08;
GROUPS.forEach(function (group, gi) {
var el = "#cg-" + gi;
var words = group.text.split(" ");
var staggerDelay = 0.08;
// Show group container
tl.set(el, { opacity: 1, visibility: "visible" }, group.start);
// Show group container
tl.set(el, { opacity: 1, visibility: "visible" }, group.start);
// Bounce each word in with elastic ease, staggered
words.forEach(function (word, wi) {
var wordEl = "#cg-" + gi + "-w" + wi;
var wordStart = group.start + wi * staggerDelay;
tl.to(
wordEl,
{ opacity: 1, y: 0, scale: 1, duration: 0.6, ease: "elastic.out(1, 0.4)" },
wordStart,
);
});
// Bounce each word in with elastic ease, staggered
words.forEach(function (word, wi) {
var wordEl = "#cg-" + gi + "-w" + wi;
var wordStart = group.start + wi * staggerDelay;
tl.to(wordEl,
{ opacity: 1, y: 0, scale: 1, duration: 0.6, ease: "elastic.out(1, 0.4)" },
wordStart
);
// Fade out the whole group
tl.to(el, { opacity: 0, duration: 0.15, ease: "power2.in" }, group.end - 0.15);
// Hard kill: reset all word pills and hide group
words.forEach(function (word, wi) {
var wordEl = "#cg-" + gi + "-w" + wi;
tl.set(wordEl, { opacity: 0, y: 40, scale: 0.5 }, group.end);
});
tl.set(el, { opacity: 0, visibility: "hidden" }, group.end);
});
// Fade out the whole group
tl.to(el,
{ opacity: 0, duration: 0.15, ease: "power2.in" },
group.end - 0.15
);
// Hard kill: reset all word pills and hide group
words.forEach(function (word, wi) {
var wordEl = "#cg-" + gi + "-w" + wi;
tl.set(wordEl, { opacity: 0, y: 40, scale: 0.5 }, group.end);
});
tl.set(el, { opacity: 0, visibility: "hidden" }, group.end);
});
window.__timelines = window.__timelines || [];
window.__timelines.push(tl);
})();
window.__timelines = window.__timelines || [];
window.__timelines.push(tl);
})();
</script>
</body>
</html>
+69 -68
View File
@@ -12,8 +12,13 @@
/>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body {
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html,
body {
margin: 0;
width: 1920px;
height: 1080px;
@@ -31,7 +36,7 @@
/* Warm vignette */
#captions-cinematic::before {
content: '';
content: "";
position: absolute;
inset: 0;
background: radial-gradient(ellipse at center, transparent 50%, rgba(0, 0, 0, 0.4) 100%);
@@ -62,7 +67,7 @@
}
.caption-text {
font-family: 'Inter', system-ui, sans-serif;
font-family: "Inter", system-ui, sans-serif;
font-weight: 300;
font-style: italic;
font-size: 48px;
@@ -99,84 +104,80 @@
</div>
<script>
(function () {
var GROUPS = [
{ text: "Write HTML", start: 0.3, end: 1.8 },
{ text: "render video", start: 2.0, end: 3.5 },
{ text: "No timeline editors", start: 3.8, end: 5.5 },
{ text: "no After Effects", start: 5.7, end: 7.2 },
{ text: "Ship 10x faster", start: 7.5, end: 9.5 },
{ text: "with HyperFrames", start: 9.8, end: 12.0 }
];
(function () {
var GROUPS = [
{ text: "Write HTML", start: 0.3, end: 1.8 },
{ text: "render video", start: 2.0, end: 3.5 },
{ text: "No timeline editors", start: 3.8, end: 5.5 },
{ text: "no After Effects", start: 5.7, end: 7.2 },
{ text: "Ship 10x faster", start: 7.5, end: 9.5 },
{ text: "with HyperFrames", start: 9.8, end: 12.0 },
];
var root = document.getElementById("captions-cinematic");
var root = document.getElementById("captions-cinematic");
// Build caption group elements: line + text
GROUPS.forEach(function (group, gi) {
var wrapper = document.createElement("div");
wrapper.className = "caption-wrapper";
wrapper.id = "cg-wrap-" + gi;
// Build caption group elements: line + text
GROUPS.forEach(function (group, gi) {
var wrapper = document.createElement("div");
wrapper.className = "caption-wrapper";
wrapper.id = "cg-wrap-" + gi;
var line = document.createElement("div");
line.className = "caption-line";
line.id = "cg-line-" + gi;
var line = document.createElement("div");
line.className = "caption-line";
line.id = "cg-line-" + gi;
var text = document.createElement("div");
text.className = "caption-text";
text.id = "cg-" + gi;
text.textContent = group.text;
var text = document.createElement("div");
text.className = "caption-text";
text.id = "cg-" + gi;
text.textContent = group.text;
wrapper.appendChild(line);
wrapper.appendChild(text);
root.appendChild(wrapper);
});
wrapper.appendChild(line);
wrapper.appendChild(text);
root.appendChild(wrapper);
});
// Build timeline
var tl = gsap.timeline({ paused: true });
// Build timeline
var tl = gsap.timeline({ paused: true });
GROUPS.forEach(function (group, gi) {
var wrapEl = "#cg-wrap-" + gi;
var lineEl = "#cg-line-" + gi;
var textEl = "#cg-" + gi;
GROUPS.forEach(function (group, gi) {
var wrapEl = "#cg-wrap-" + gi;
var lineEl = "#cg-line-" + gi;
var textEl = "#cg-" + gi;
// Show wrapper
tl.set(wrapEl, { opacity: 1, visibility: "visible" }, group.start);
// Show wrapper
tl.set(wrapEl, { opacity: 1, visibility: "visible" }, group.start);
// Line extends from center
tl.fromTo(lineEl,
{ scaleX: 0 },
{ scaleX: 1, duration: 0.4, ease: "power2.out", transformOrigin: "center" },
group.start
);
// Line extends from center
tl.fromTo(
lineEl,
{ scaleX: 0 },
{ scaleX: 1, duration: 0.4, ease: "power2.out", transformOrigin: "center" },
group.start,
);
// Text fades in with letter-spacing settling
tl.fromTo(textEl,
{ opacity: 0, letterSpacing: "8px" },
{ opacity: 1, letterSpacing: "4px", duration: 0.6, ease: "power1.out" },
group.start + 0.1
);
// Text fades in with letter-spacing settling
tl.fromTo(
textEl,
{ opacity: 0, letterSpacing: "8px" },
{ opacity: 1, letterSpacing: "4px", duration: 0.6, ease: "power1.out" },
group.start + 0.1,
);
// Text fades out slowly
tl.to(textEl,
{ opacity: 0, duration: 0.4, ease: "power1.in" },
group.end - 0.4
);
// Text fades out slowly
tl.to(textEl, { opacity: 0, duration: 0.4, ease: "power1.in" }, group.end - 0.4);
// Line contracts
tl.to(lineEl,
{ scaleX: 0, duration: 0.3, ease: "power2.in" },
group.end - 0.3
);
// Line contracts
tl.to(lineEl, { scaleX: 0, duration: 0.3, ease: "power2.in" }, group.end - 0.3);
// Hard kill
tl.set(wrapEl, { opacity: 0, visibility: "hidden" }, group.end);
tl.set(textEl, { opacity: 0 }, group.end);
tl.set(lineEl, { scaleX: 0 }, group.end);
});
// Hard kill
tl.set(wrapEl, { opacity: 0, visibility: "hidden" }, group.end);
tl.set(textEl, { opacity: 0 }, group.end);
tl.set(lineEl, { scaleX: 0 }, group.end);
});
window.__timelines = window.__timelines || [];
window.__timelines.push(tl);
})();
window.__timelines = window.__timelines || [];
window.__timelines.push(tl);
})();
</script>
</body>
</html>
+65 -62
View File
@@ -12,8 +12,13 @@
/>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body {
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html,
body {
margin: 0;
width: 1920px;
height: 1080px;
@@ -30,7 +35,7 @@
}
#captions-karaoke::before {
content: '';
content: "";
position: absolute;
bottom: -60px;
left: 50%;
@@ -47,7 +52,7 @@
left: 50%;
transform: translateX(-50%);
text-align: center;
font-family: 'Inter', system-ui, sans-serif;
font-family: "Inter", system-ui, sans-serif;
font-weight: 700;
font-size: 72px;
line-height: 1.2;
@@ -97,75 +102,73 @@
</div>
<script>
(function () {
var GROUPS = [
{ text: "Write HTML", start: 0.3, end: 1.8 },
{ text: "render video", start: 2.0, end: 3.5 },
{ text: "No timeline editors", start: 3.8, end: 5.5 },
{ text: "no After Effects", start: 5.7, end: 7.2 },
{ text: "Ship 10x faster", start: 7.5, end: 9.5 },
{ text: "with HyperFrames", start: 9.8, end: 12.0 }
];
(function () {
var GROUPS = [
{ text: "Write HTML", start: 0.3, end: 1.8 },
{ text: "render video", start: 2.0, end: 3.5 },
{ text: "No timeline editors", start: 3.8, end: 5.5 },
{ text: "no After Effects", start: 5.7, end: 7.2 },
{ text: "Ship 10x faster", start: 7.5, end: 9.5 },
{ text: "with HyperFrames", start: 9.8, end: 12.0 },
];
var root = document.getElementById("captions-karaoke");
var root = document.getElementById("captions-karaoke");
// Build caption group elements with per-word spans
GROUPS.forEach(function (group, gi) {
var div = document.createElement("div");
div.className = "caption-group";
div.id = "cg-" + gi;
// Build caption group elements with per-word spans
GROUPS.forEach(function (group, gi) {
var div = document.createElement("div");
div.className = "caption-group";
div.id = "cg-" + gi;
var words = group.text.split(" ");
words.forEach(function (word, wi) {
var span = document.createElement("span");
span.className = "word";
span.id = "cg-" + gi + "-w" + wi;
span.textContent = word;
div.appendChild(span);
var words = group.text.split(" ");
words.forEach(function (word, wi) {
var span = document.createElement("span");
span.className = "word";
span.id = "cg-" + gi + "-w" + wi;
span.textContent = word;
div.appendChild(span);
});
root.appendChild(div);
});
root.appendChild(div);
});
// Build timeline
var tl = gsap.timeline({ paused: true });
// Build timeline
var tl = gsap.timeline({ paused: true });
GROUPS.forEach(function (group, gi) {
var el = "#cg-" + gi;
var words = group.text.split(" ");
var totalWords = words.length;
var groupDuration = group.end - group.start;
GROUPS.forEach(function (group, gi) {
var el = "#cg-" + gi;
var words = group.text.split(" ");
var totalWords = words.length;
var groupDuration = group.end - group.start;
// Show group (dimmed words visible)
tl.set(el, { opacity: 1, visibility: "visible" }, group.start);
// Show group (dimmed words visible)
tl.set(el, { opacity: 1, visibility: "visible" }, group.start);
// Highlight each word as its time arrives
words.forEach(function (word, wi) {
var wordStart = group.start + (wi / totalWords) * groupDuration;
var wordEl = "#cg-" + gi + "-w" + wi;
tl.to(
wordEl,
{ color: "#ffffff", scale: 1.05, duration: 0.15, ease: "power2.out" },
wordStart,
);
});
// Highlight each word as its time arrives
words.forEach(function (word, wi) {
var wordStart = group.start + (wi / totalWords) * groupDuration;
var wordEl = "#cg-" + gi + "-w" + wi;
tl.to(wordEl,
{ color: "#ffffff", scale: 1.05, duration: 0.15, ease: "power2.out" },
wordStart
);
// Fade out group
tl.to(el, { opacity: 0, duration: 0.15, ease: "power2.in" }, group.end - 0.15);
// Hard kill: reset all words and hide group
words.forEach(function (word, wi) {
var wordEl = "#cg-" + gi + "-w" + wi;
tl.set(wordEl, { color: "rgba(255,255,255,0.3)", scale: 1 }, group.end);
});
tl.set(el, { opacity: 0, visibility: "hidden" }, group.end);
});
// Fade out group
tl.to(el,
{ opacity: 0, duration: 0.15, ease: "power2.in" },
group.end - 0.15
);
// Hard kill: reset all words and hide group
words.forEach(function (word, wi) {
var wordEl = "#cg-" + gi + "-w" + wi;
tl.set(wordEl, { color: "rgba(255,255,255,0.3)", scale: 1 }, group.end);
});
tl.set(el, { opacity: 0, visibility: "hidden" }, group.end);
});
window.__timelines = window.__timelines || [];
window.__timelines.push(tl);
})();
window.__timelines = window.__timelines || [];
window.__timelines.push(tl);
})();
</script>
</body>
</html>
+41 -42
View File
@@ -12,8 +12,13 @@
/>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body {
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html,
body {
margin: 0;
width: 1920px;
height: 1080px;
@@ -30,7 +35,7 @@
}
#captions-minimal::before {
content: '';
content: "";
position: absolute;
bottom: 0;
left: 0;
@@ -46,7 +51,7 @@
left: 50%;
transform: translateX(-50%);
text-align: center;
font-family: 'Inter', system-ui, sans-serif;
font-family: "Inter", system-ui, sans-serif;
font-weight: 500;
font-size: 56px;
line-height: 1.3;
@@ -86,49 +91,43 @@
</div>
<script>
(function () {
var GROUPS = [
{ text: "Write HTML", start: 0.3, end: 1.8 },
{ text: "render video", start: 2.0, end: 3.5 },
{ text: "No timeline editors", start: 3.8, end: 5.5 },
{ text: "no After Effects", start: 5.7, end: 7.2 },
{ text: "Ship 10x faster", start: 7.5, end: 9.5 },
{ text: "with HyperFrames", start: 9.8, end: 12.0 }
];
(function () {
var GROUPS = [
{ text: "Write HTML", start: 0.3, end: 1.8 },
{ text: "render video", start: 2.0, end: 3.5 },
{ text: "No timeline editors", start: 3.8, end: 5.5 },
{ text: "no After Effects", start: 5.7, end: 7.2 },
{ text: "Ship 10x faster", start: 7.5, end: 9.5 },
{ text: "with HyperFrames", start: 9.8, end: 12.0 },
];
var root = document.getElementById("captions-minimal");
var root = document.getElementById("captions-minimal");
// Build caption group elements
GROUPS.forEach(function (group, gi) {
var div = document.createElement("div");
div.className = "caption-group";
div.id = "cg-" + gi;
div.textContent = group.text;
root.appendChild(div);
});
// Build caption group elements
GROUPS.forEach(function (group, gi) {
var div = document.createElement("div");
div.className = "caption-group";
div.id = "cg-" + gi;
div.textContent = group.text;
root.appendChild(div);
});
// Build timeline
var tl = gsap.timeline({ paused: true });
// Build timeline
var tl = gsap.timeline({ paused: true });
GROUPS.forEach(function (group, gi) {
var el = "#cg-" + gi;
// Fade in with slight upward motion
tl.set(el, { visibility: "visible" }, group.start);
tl.from(el,
{ y: 15, opacity: 0, duration: 0.3, ease: "power2.out" },
group.start
);
// Fade out with slight upward drift
tl.to(el,
{ y: -5, opacity: 0, duration: 0.2, ease: "power2.in" },
group.end - 0.2
);
tl.set(el, { opacity: 0, visibility: "hidden" }, group.end);
});
GROUPS.forEach(function (group, gi) {
var el = "#cg-" + gi;
// Fade in with slight upward motion
tl.set(el, { visibility: "visible" }, group.start);
tl.from(el, { y: 15, opacity: 0, duration: 0.3, ease: "power2.out" }, group.start);
// Fade out with slight upward drift
tl.to(el, { y: -5, opacity: 0, duration: 0.2, ease: "power2.in" }, group.end - 0.2);
tl.set(el, { opacity: 0, visibility: "hidden" }, group.end);
});
window.__timelines = window.__timelines || [];
window.__timelines.push(tl);
})();
window.__timelines = window.__timelines || [];
window.__timelines.push(tl);
})();
</script>
</body>
</html>
+66 -53
View File
@@ -12,8 +12,13 @@
/>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body {
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html,
body {
margin: 0;
width: 1920px;
height: 1080px;
@@ -31,7 +36,7 @@
/* Subtle radial glow in center-bottom */
#captions-slam::before {
content: '';
content: "";
position: absolute;
bottom: -100px;
left: 50%;
@@ -48,13 +53,15 @@
left: 0;
right: 0;
text-align: center;
font-family: 'Inter', system-ui, sans-serif;
font-family: "Inter", system-ui, sans-serif;
font-weight: 900;
font-size: 96px;
line-height: 1.1;
text-transform: uppercase;
color: #ffffff;
text-shadow: 0 0 40px rgba(255, 255, 255, 0.15), 0 0 80px rgba(255, 255, 255, 0.05);
text-shadow:
0 0 40px rgba(255, 255, 255, 0.15),
0 0 80px rgba(255, 255, 255, 0.05);
letter-spacing: 2px;
opacity: 0;
visibility: hidden;
@@ -66,7 +73,9 @@
color: #00d4ff;
display: inline-block;
transform: scale(1.3);
text-shadow: 0 0 30px rgba(0, 212, 255, 0.4), 0 0 60px rgba(0, 212, 255, 0.15);
text-shadow:
0 0 30px rgba(0, 212, 255, 0.4),
0 0 60px rgba(0, 212, 255, 0.15);
}
.caption-group .word-gradient {
@@ -102,60 +111,64 @@
</div>
<script>
(function () {
var GROUPS = [
{ text: "Write HTML", start: 0.3, end: 1.8 },
{ text: "render video", start: 2.0, end: 3.5 },
{ text: "No timeline editors", start: 3.8, end: 5.5 },
{ text: "no After Effects", start: 5.7, end: 7.2 },
{ text: "Ship 10x faster", start: 7.5, end: 9.5 },
{ text: "with HyperFrames", start: 9.8, end: 12.0 }
];
(function () {
var GROUPS = [
{ text: "Write HTML", start: 0.3, end: 1.8 },
{ text: "render video", start: 2.0, end: 3.5 },
{ text: "No timeline editors", start: 3.8, end: 5.5 },
{ text: "no After Effects", start: 5.7, end: 7.2 },
{ text: "Ship 10x faster", start: 7.5, end: 9.5 },
{ text: "with HyperFrames", start: 9.8, end: 12.0 },
];
var root = document.getElementById("captions-slam");
var root = document.getElementById("captions-slam");
// Build caption group elements
GROUPS.forEach(function (group, gi) {
var div = document.createElement("div");
div.className = "caption-group";
div.id = "cg-" + gi;
// Build caption group elements
GROUPS.forEach(function (group, gi) {
var div = document.createElement("div");
div.className = "caption-group";
div.id = "cg-" + gi;
// Apply per-word styling
var words = group.text.split(" ");
var html = words.map(function (word) {
if (word === "10x") {
return '<span class="word-accent">' + word + '</span>';
}
if (word === "HyperFrames") {
return '<span class="word-gradient">' + word + '</span>';
}
return word;
}).join(" ");
// Apply per-word styling
var words = group.text.split(" ");
var html = words
.map(function (word) {
if (word === "10x") {
return '<span class="word-accent">' + word + "</span>";
}
if (word === "HyperFrames") {
return '<span class="word-gradient">' + word + "</span>";
}
return word;
})
.join(" ");
div.innerHTML = html;
root.appendChild(div);
});
div.innerHTML = html;
root.appendChild(div);
});
// Build timeline
var tl = gsap.timeline({ paused: true });
// Build timeline
var tl = gsap.timeline({ paused: true });
GROUPS.forEach(function (group, gi) {
var el = "#cg-" + gi;
tl.fromTo(el,
{ scale: 0, opacity: 0, visibility: "visible" },
{ scale: 1, opacity: 1, duration: 0.15, ease: "back.out(1.7)" },
group.start
);
tl.to(el,
{ scale: 1.05, opacity: 0, duration: 0.12, ease: "power2.in" },
group.end - 0.12
);
tl.set(el, { opacity: 0, visibility: "hidden" }, group.end);
});
GROUPS.forEach(function (group, gi) {
var el = "#cg-" + gi;
tl.fromTo(
el,
{ scale: 0, opacity: 0, visibility: "visible" },
{ scale: 1, opacity: 1, duration: 0.15, ease: "back.out(1.7)" },
group.start,
);
tl.to(
el,
{ scale: 1.05, opacity: 0, duration: 0.12, ease: "power2.in" },
group.end - 0.12,
);
tl.set(el, { opacity: 0, visibility: "hidden" }, group.end);
});
window.__timelines = window.__timelines || [];
window.__timelines.push(tl);
})();
window.__timelines = window.__timelines || [];
window.__timelines.push(tl);
})();
</script>
</body>
</html>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+428 -306
View File
@@ -1,342 +1,464 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=1920, height=1080">
<title>Magnetic Cursor — HTML-in-Canvas</title>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<style>
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
body { background: #0c0c0c; overflow: hidden; font-family: system-ui, -apple-system, sans-serif; color: #e8e8e8; }
#root { position: relative; width: 1920px; height: 1080px; overflow: hidden; background: #0c0c0c; }
canvas { display: block; }
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>Magnetic Cursor — HTML-in-Canvas</title>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<style>
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #0c0c0c;
overflow: hidden;
font-family:
system-ui,
-apple-system,
sans-serif;
color: #e8e8e8;
}
#root {
position: relative;
width: 1920px;
height: 1080px;
overflow: hidden;
background: #0c0c0c;
}
canvas {
display: block;
}
.page-content {
width: 1920px; height: 1080px; overflow: hidden;
font-family: system-ui, -apple-system, sans-serif; color: #e8e8e8;
display: flex; align-items: center; justify-content: center;
}
.page-inner { max-width: 900px; padding: 0 48px; }
.heading {
font-size: 64px; font-weight: 700; line-height: 1.15;
letter-spacing: -0.03em; color: #f0f0f0; margin-bottom: 32px;
}
.heading em { font-style: normal; color: #70c7ed; }
.body-text {
font-size: 22px; line-height: 1.7;
color: rgba(255,255,255,0.6); margin-bottom: 24px;
}
.body-text:last-of-type { margin-bottom: 48px; }
.hint {
font-size: 15px; color: rgba(255,255,255,0.3);
font-family: ui-monospace, 'SF Mono', monospace;
margin-bottom: 48px; letter-spacing: 0.02em;
}
.card-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; }
.card {
background: rgba(255,255,255,0.04);
border: 1px solid rgba(255,255,255,0.08);
border-radius: 14px; padding: 24px;
}
.card-icon {
width: 40px; height: 40px; border-radius: 10px;
margin-bottom: 14px; display: flex; align-items: center;
justify-content: center; font-size: 20px;
background: rgba(112,199,237,0.12);
}
.card-title { font-size: 16px; font-weight: 600; color: rgba(255,255,255,0.85); margin-bottom: 8px; }
.card-desc { font-size: 14px; line-height: 1.5; color: rgba(255,255,255,0.4); }
.page-content {
width: 1920px;
height: 1080px;
overflow: hidden;
font-family:
system-ui,
-apple-system,
sans-serif;
color: #e8e8e8;
display: flex;
align-items: center;
justify-content: center;
}
.page-inner {
max-width: 900px;
padding: 0 48px;
}
.heading {
font-size: 64px;
font-weight: 700;
line-height: 1.15;
letter-spacing: -0.03em;
color: #f0f0f0;
margin-bottom: 32px;
}
.heading em {
font-style: normal;
color: #70c7ed;
}
.body-text {
font-size: 22px;
line-height: 1.7;
color: rgba(255, 255, 255, 0.6);
margin-bottom: 24px;
}
.body-text:last-of-type {
margin-bottom: 48px;
}
.hint {
font-size: 15px;
color: rgba(255, 255, 255, 0.3);
font-family: ui-monospace, "SF Mono", monospace;
margin-bottom: 48px;
letter-spacing: 0.02em;
}
.card-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
}
.card {
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 14px;
padding: 24px;
}
.card-icon {
width: 40px;
height: 40px;
border-radius: 10px;
margin-bottom: 14px;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
background: rgba(112, 199, 237, 0.12);
}
.card-title {
font-size: 16px;
font-weight: 600;
color: rgba(255, 255, 255, 0.85);
margin-bottom: 8px;
}
.card-desc {
font-size: 14px;
line-height: 1.5;
color: rgba(255, 255, 255, 0.4);
}
/* Subtitles */
.subtitle-bar {
position: absolute; bottom: 60px; left: 50%; transform: translateX(-50%);
z-index: 10; pointer-events: none;
}
.subtitle {
font-size: 28px; font-weight: 500; color: white; text-align: center;
background: rgba(0,0,0,0.7); padding: 12px 32px; border-radius: 8px;
backdrop-filter: blur(8px); white-space: nowrap; opacity: 0;
font-family: system-ui, -apple-system, sans-serif;
}
/* Subtitles */
.subtitle-bar {
position: absolute;
bottom: 60px;
left: 50%;
transform: translateX(-50%);
z-index: 10;
pointer-events: none;
}
.subtitle {
font-size: 28px;
font-weight: 500;
color: white;
text-align: center;
background: rgba(0, 0, 0, 0.7);
padding: 12px 32px;
border-radius: 8px;
backdrop-filter: blur(8px);
white-space: nowrap;
opacity: 0;
font-family:
system-ui,
-apple-system,
sans-serif;
}
/* Cursor dot */
.cursor-dot {
position: absolute; width: 20px; height: 20px;
border-radius: 50%; background: rgba(112,199,237,0.8);
box-shadow: 0 0 20px rgba(112,199,237,0.5), 0 0 40px rgba(112,199,237,0.3);
z-index: 10; pointer-events: none; transform: translate(-50%, -50%);
opacity: 0;
}
</style>
</head>
<body>
<div id="root"
data-composition-id="magnetic-cursor"
data-width="1920"
data-height="1080"
data-start="0"
data-duration="15"
data-root="true">
<canvas id="gl-canvas" layoutsubtree width="1920" height="1080"
style="position:absolute;top:0;left:0;width:1920px;height:1080px">
<div class="page-content" id="content" style="width:1920px;height:1080px">
<div class="page-inner">
<h1 class="heading">Pixels bend toward<br>your <em>cursor</em></h1>
<p class="body-text">
Move your mouse across this page. Every pixel is drawn through a WebGL shader
that warps space toward your cursor position, like a magnet pulling iron filings
beneath a thin rubber sheet.
</p>
<p class="body-text">
The distortion follows a Gaussian falloff — strong at close range, tapering
smoothly to nothing. The text remains readable even under deformation.
</p>
<p class="hint">HTML-in-Canvas API + WebGL2 shader distortion</p>
<div class="card-grid">
<div class="card">
<div class="card-icon">&#9889;</div>
<div class="card-title">Real-time</div>
<div class="card-desc">60fps distortion on live DOM content via texElementImage2D</div>
</div>
<div class="card">
<div class="card-icon">&#128268;</div>
<div class="card-title">Native API</div>
<div class="card-desc">No html2canvas. Browser's own rasterizer at full fidelity.</div>
</div>
<div class="card">
<div class="card-icon">&#127912;</div>
<div class="card-title">GPU Shaders</div>
<div class="card-desc">Gaussian warp + chromatic aberration in one fragment shader pass.</div>
/* Cursor dot */
.cursor-dot {
position: absolute;
width: 20px;
height: 20px;
border-radius: 50%;
background: rgba(112, 199, 237, 0.8);
box-shadow:
0 0 20px rgba(112, 199, 237, 0.5),
0 0 40px rgba(112, 199, 237, 0.3);
z-index: 10;
pointer-events: none;
transform: translate(-50%, -50%);
opacity: 0;
}
</style>
</head>
<body>
<div
id="root"
data-composition-id="magnetic-cursor"
data-width="1920"
data-height="1080"
data-start="0"
data-duration="15"
data-root="true"
>
<canvas
id="gl-canvas"
layoutsubtree
width="1920"
height="1080"
style="position: absolute; top: 0; left: 0; width: 1920px; height: 1080px"
>
<div class="page-content" id="content" style="width: 1920px; height: 1080px">
<div class="page-inner">
<h1 class="heading">Pixels bend toward<br />your <em>cursor</em></h1>
<p class="body-text">
Move your mouse across this page. Every pixel is drawn through a WebGL shader that
warps space toward your cursor position, like a magnet pulling iron filings beneath a
thin rubber sheet.
</p>
<p class="body-text">
The distortion follows a Gaussian falloff — strong at close range, tapering smoothly
to nothing. The text remains readable even under deformation.
</p>
<p class="hint">HTML-in-Canvas API + WebGL2 shader distortion</p>
<div class="card-grid">
<div class="card">
<div class="card-icon">&#9889;</div>
<div class="card-title">Real-time</div>
<div class="card-desc">
60fps distortion on live DOM content via texElementImage2D
</div>
</div>
<div class="card">
<div class="card-icon">&#128268;</div>
<div class="card-title">Native API</div>
<div class="card-desc">
No html2canvas. Browser's own rasterizer at full fidelity.
</div>
</div>
<div class="card">
<div class="card-icon">&#127912;</div>
<div class="card-title">GPU Shaders</div>
<div class="card-desc">
Gaussian warp + chromatic aberration in one fragment shader pass.
</div>
</div>
</div>
</div>
</div>
</canvas>
<!-- Visible cursor dot -->
<div class="cursor-dot" id="cursor-dot"></div>
<!-- Subtitles -->
<div class="subtitle-bar">
<div class="subtitle" id="sub1">
The HTML-in-Canvas API renders live DOM as a WebGL texture
</div>
<div class="subtitle" id="sub2">A magnetic shader warps every pixel toward the cursor</div>
<div class="subtitle" id="sub3">
Chromatic aberration splits RGB channels at the distortion site
</div>
<div class="subtitle" id="sub4">
Impossible without native drawElementImage — no polyfill exists
</div>
</div>
<div
id="driver"
class="clip"
data-start="0"
data-duration="15"
data-track-index="0"
style="position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none"
></div>
</div>
</canvas>
<!-- Visible cursor dot -->
<div class="cursor-dot" id="cursor-dot"></div>
<script>
var W = 1920,
H = 1080;
var canvas = document.getElementById("gl-canvas");
var content = document.getElementById("content");
var cursorDot = document.getElementById("cursor-dot");
<!-- Subtitles -->
<div class="subtitle-bar">
<div class="subtitle" id="sub1">The HTML-in-Canvas API renders live DOM as a WebGL texture</div>
<div class="subtitle" id="sub2">A magnetic shader warps every pixel toward the cursor</div>
<div class="subtitle" id="sub3">Chromatic aberration splits RGB channels at the distortion site</div>
<div class="subtitle" id="sub4">Impossible without native drawElementImage — no polyfill exists</div>
</div>
function isSupported() {
var tc = document.createElement("canvas");
if (!("layoutSubtree" in tc)) return false;
tc.setAttribute("layoutsubtree", "");
var ctx = tc.getContext("2d");
return ctx && typeof ctx.drawElementImage === "function";
}
<div id="driver" class="clip"
data-start="0" data-duration="15" data-track-index="0"
style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none">
</div>
</div>
// ── WebGL2 Setup ─────────────────────────────────────────────────
var gl = canvas.getContext("webgl2", { alpha: false, preserveDrawingBuffer: true });
<script>
var W = 1920, H = 1080;
var canvas = document.getElementById("gl-canvas");
var content = document.getElementById("content");
var cursorDot = document.getElementById("cursor-dot");
function compile(type, src) {
var s = gl.createShader(type);
gl.shaderSource(s, src);
gl.compileShader(s);
if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) console.error(gl.getShaderInfoLog(s));
return s;
}
function link(vsSrc, fsSrc) {
var p = gl.createProgram();
gl.attachShader(p, compile(gl.VERTEX_SHADER, vsSrc));
gl.attachShader(p, compile(gl.FRAGMENT_SHADER, fsSrc));
gl.linkProgram(p);
return p;
}
function isSupported() {
var tc = document.createElement("canvas");
if (!("layoutSubtree" in tc)) return false;
tc.setAttribute("layoutsubtree", "");
var ctx = tc.getContext("2d");
return ctx && typeof ctx.drawElementImage === "function";
}
var VS = [
"#version 300 es",
"in vec2 a_pos;",
"out vec2 v_uv;",
"void main() {",
" v_uv = vec2(a_pos.x * 0.5 + 0.5, 0.5 - a_pos.y * 0.5);",
" gl_Position = vec4(a_pos, 0.0, 1.0);",
"}",
].join("\n");
// ── WebGL2 Setup ─────────────────────────────────────────────────
var gl = canvas.getContext("webgl2", { alpha: false, preserveDrawingBuffer: true });
var FS = [
"#version 300 es",
"precision highp float;",
"in vec2 v_uv;",
"out vec4 fragColor;",
"uniform sampler2D u_content;",
"uniform vec2 u_mouse;",
"uniform vec2 u_resolution;",
"uniform float u_repel;",
"uniform float u_strength;",
"",
"void main() {",
" vec2 uv = v_uv;",
" float aspect = u_resolution.x / u_resolution.y;",
" vec2 delta = u_mouse - uv;",
" vec2 aspectDelta = delta * vec2(aspect, 1.0);",
" float dist = length(aspectDelta);",
" float strength = exp(-dist * dist * 20.0) * 0.04 * u_strength;",
" float direction = mix(1.0, -1.0, u_repel);",
" vec2 displaced = uv + delta * strength * direction;",
" displaced = clamp(displaced, vec2(0.0), vec2(1.0));",
" float aberration = strength * 0.6;",
" vec2 aberDir = normalize(delta + 0.0001) * aberration;",
" float r = texture(u_content, displaced + aberDir * 0.3).r;",
" float g = texture(u_content, displaced).g;",
" float b = texture(u_content, displaced - aberDir * 0.3).b;",
" fragColor = vec4(r, g, b, 1.0);",
"}",
].join("\n");
function compile(type, src) {
var s = gl.createShader(type);
gl.shaderSource(s, src);
gl.compileShader(s);
if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) console.error(gl.getShaderInfoLog(s));
return s;
}
function link(vsSrc, fsSrc) {
var p = gl.createProgram();
gl.attachShader(p, compile(gl.VERTEX_SHADER, vsSrc));
gl.attachShader(p, compile(gl.FRAGMENT_SHADER, fsSrc));
gl.linkProgram(p);
return p;
}
var prog = link(VS, FS);
var VS = [
"#version 300 es",
"in vec2 a_pos;",
"out vec2 v_uv;",
"void main() {",
" v_uv = vec2(a_pos.x * 0.5 + 0.5, 0.5 - a_pos.y * 0.5);",
" gl_Position = vec4(a_pos, 0.0, 1.0);",
"}"
].join("\n");
// Quad
var vao = gl.createVertexArray();
gl.bindVertexArray(vao);
var buf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(
gl.ARRAY_BUFFER,
new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]),
gl.STATIC_DRAW,
);
var aPos = gl.getAttribLocation(prog, "a_pos");
gl.enableVertexAttribArray(aPos);
gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 0, 0);
var FS = [
"#version 300 es",
"precision highp float;",
"in vec2 v_uv;",
"out vec4 fragColor;",
"uniform sampler2D u_content;",
"uniform vec2 u_mouse;",
"uniform vec2 u_resolution;",
"uniform float u_repel;",
"uniform float u_strength;",
"",
"void main() {",
" vec2 uv = v_uv;",
" float aspect = u_resolution.x / u_resolution.y;",
" vec2 delta = u_mouse - uv;",
" vec2 aspectDelta = delta * vec2(aspect, 1.0);",
" float dist = length(aspectDelta);",
" float strength = exp(-dist * dist * 20.0) * 0.04 * u_strength;",
" float direction = mix(1.0, -1.0, u_repel);",
" vec2 displaced = uv + delta * strength * direction;",
" displaced = clamp(displaced, vec2(0.0), vec2(1.0));",
" float aberration = strength * 0.6;",
" vec2 aberDir = normalize(delta + 0.0001) * aberration;",
" float r = texture(u_content, displaced + aberDir * 0.3).r;",
" float g = texture(u_content, displaced).g;",
" float b = texture(u_content, displaced - aberDir * 0.3).b;",
" fragColor = vec4(r, g, b, 1.0);",
"}"
].join("\n");
// Texture
var tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
var prog = link(VS, FS);
// Uniforms
gl.useProgram(prog);
var U = {};
["u_content", "u_mouse", "u_resolution", "u_repel", "u_strength"].forEach(function (n) {
U[n] = gl.getUniformLocation(prog, n);
});
// Quad
var vao = gl.createVertexArray();
gl.bindVertexArray(vao);
var buf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1,-1, 1,-1, -1,1, 1,1]), gl.STATIC_DRAW);
var aPos = gl.getAttribLocation(prog, "a_pos");
gl.enableVertexAttribArray(aPos);
gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 0, 0);
// ── Capture + Render ─────────────────────────────────────────────
var captured = false;
// Texture
var tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
function captureContent() {
if (!isSupported()) return;
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texElementImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, content);
captured = true;
}
// Uniforms
gl.useProgram(prog);
var U = {};
["u_content","u_mouse","u_resolution","u_repel","u_strength"].forEach(function(n) {
U[n] = gl.getUniformLocation(prog, n);
});
// Also support drawElementImage fallback via 2D context capture
function captureVia2D() {
var tc = document.createElement("canvas");
tc.setAttribute("layoutsubtree", "");
tc.width = W;
tc.height = H;
// Can't use drawElementImage from a different canvas — skip fallback
}
// ── Capture + Render ─────────────────────────────────────────────
var captured = false;
function render(mouseX, mouseY, repel, strength) {
gl.viewport(0, 0, W, H);
gl.useProgram(prog);
gl.bindVertexArray(vao);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.uniform1i(U.u_content, 0);
gl.uniform2f(U.u_mouse, mouseX, mouseY);
gl.uniform2f(U.u_resolution, W, H);
gl.uniform1f(U.u_repel, repel);
gl.uniform1f(U.u_strength, strength);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}
function captureContent() {
if (!isSupported()) return;
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texElementImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, content);
captured = true;
}
// ── Animation State ──────────────────────────────────────────────
var S = {
mouseX: 0.5,
mouseY: 0.5,
repel: 0,
strength: 0,
progress: 0,
};
// Also support drawElementImage fallback via 2D context capture
function captureVia2D() {
var tc = document.createElement("canvas");
tc.setAttribute("layoutsubtree", "");
tc.width = W; tc.height = H;
// Can't use drawElementImage from a different canvas — skip fallback
}
// ── GSAP Timeline ────────────────────────────────────────────────
window.__timelines = window.__timelines || {};
var tl = gsap.timeline({ paused: true });
function render(mouseX, mouseY, repel, strength) {
gl.viewport(0, 0, W, H);
gl.useProgram(prog);
gl.bindVertexArray(vao);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.uniform1i(U.u_content, 0);
gl.uniform2f(U.u_mouse, mouseX, mouseY);
gl.uniform2f(U.u_resolution, W, H);
gl.uniform1f(U.u_repel, repel);
gl.uniform1f(U.u_strength, strength);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}
// Driver tween
tl.to(
S,
{
progress: 1,
duration: 15,
ease: "none",
onUpdate: function () {
// Update cursor dot position
cursorDot.style.left = S.mouseX * W + "px";
cursorDot.style.top = S.mouseY * H + "px";
render(S.mouseX, S.mouseY, S.repel, S.strength);
},
},
0,
);
// ── Animation State ──────────────────────────────────────────────
var S = {
mouseX: 0.5, mouseY: 0.5,
repel: 0, strength: 0,
progress: 0
};
// Cursor appears
tl.to(cursorDot, { opacity: 1, duration: 0.3 }, 0.5);
tl.to(S, { strength: 1, duration: 0.5, ease: "power2.out" }, 0.5);
// ── GSAP Timeline ────────────────────────────────────────────────
window.__timelines = window.__timelines || {};
var tl = gsap.timeline({ paused: true });
// Cursor path: smooth figure-8 across the page
// Phase 1: sweep across heading
tl.to(S, { mouseX: 0.35, mouseY: 0.3, duration: 2, ease: "power1.inOut" }, 1);
tl.to(S, { mouseX: 0.6, mouseY: 0.35, duration: 2, ease: "power1.inOut" }, 3);
// Driver tween
tl.to(S, {
progress: 1, duration: 15, ease: "none",
onUpdate: function() {
// Update cursor dot position
cursorDot.style.left = (S.mouseX * W) + "px";
cursorDot.style.top = (S.mouseY * H) + "px";
render(S.mouseX, S.mouseY, S.repel, S.strength);
}
}, 0);
// Phase 2: circle around text
tl.to(S, { mouseX: 0.4, mouseY: 0.5, duration: 1.5, ease: "sine.inOut" }, 5);
tl.to(S, { mouseX: 0.55, mouseY: 0.55, duration: 1.5, ease: "sine.inOut" }, 6.5);
// Cursor appears
tl.to(cursorDot, { opacity: 1, duration: 0.3 }, 0.5);
tl.to(S, { strength: 1, duration: 0.5, ease: "power2.out" }, 0.5);
// Phase 3: repel pulse
tl.to(S, { repel: 1, duration: 0.2, ease: "power4.out" }, 8);
tl.to(S, { repel: 0, duration: 0.5, ease: "power2.out" }, 8.5);
// Cursor path: smooth figure-8 across the page
// Phase 1: sweep across heading
tl.to(S, { mouseX: 0.35, mouseY: 0.3, duration: 2, ease: "power1.inOut" }, 1);
tl.to(S, { mouseX: 0.6, mouseY: 0.35, duration: 2, ease: "power1.inOut" }, 3);
// Phase 4: sweep across cards
tl.to(S, { mouseX: 0.3, mouseY: 0.75, duration: 2, ease: "power1.inOut" }, 9);
tl.to(S, { mouseX: 0.65, mouseY: 0.72, duration: 2, ease: "power1.inOut" }, 11);
// Phase 2: circle around text
tl.to(S, { mouseX: 0.4, mouseY: 0.5, duration: 1.5, ease: "sine.inOut" }, 5);
tl.to(S, { mouseX: 0.55, mouseY: 0.55, duration: 1.5, ease: "sine.inOut" }, 6.5);
// Phase 5: exit
tl.to(S, { mouseX: 0.8, mouseY: 0.2, duration: 1.5, ease: "power2.inOut" }, 13);
tl.to(S, { strength: 0, duration: 0.5 }, 14);
tl.to(cursorDot, { opacity: 0, duration: 0.3 }, 14.2);
// Phase 3: repel pulse
tl.to(S, { repel: 1, duration: 0.2, ease: "power4.out" }, 8);
tl.to(S, { repel: 0, duration: 0.5, ease: "power2.out" }, 8.5);
// Subtitles
tl.to("#sub1", { opacity: 1, duration: 0.3 }, 1);
tl.to("#sub1", { opacity: 0, duration: 0.3 }, 3.5);
tl.to("#sub2", { opacity: 1, duration: 0.3 }, 4);
tl.to("#sub2", { opacity: 0, duration: 0.3 }, 6.5);
tl.to("#sub3", { opacity: 1, duration: 0.3 }, 7.5);
tl.to("#sub3", { opacity: 0, duration: 0.3 }, 9.5);
tl.to("#sub4", { opacity: 1, duration: 0.3 }, 10.5);
tl.to("#sub4", { opacity: 0, duration: 0.3 }, 13);
// Phase 4: sweep across cards
tl.to(S, { mouseX: 0.3, mouseY: 0.75, duration: 2, ease: "power1.inOut" }, 9);
tl.to(S, { mouseX: 0.65, mouseY: 0.72, duration: 2, ease: "power1.inOut" }, 11);
window.__timelines["magnetic-cursor"] = tl;
// Phase 5: exit
tl.to(S, { mouseX: 0.8, mouseY: 0.2, duration: 1.5, ease: "power2.inOut" }, 13);
tl.to(S, { strength: 0, duration: 0.5 }, 14);
tl.to(cursorDot, { opacity: 0, duration: 0.3 }, 14.2);
// Paint event for texture upload
canvas.onpaint = function () {
if (!captured) {
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texElementImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, content);
captured = true;
}
};
// Subtitles
tl.to("#sub1", { opacity: 1, duration: 0.3 }, 1);
tl.to("#sub1", { opacity: 0, duration: 0.3 }, 3.5);
tl.to("#sub2", { opacity: 1, duration: 0.3 }, 4);
tl.to("#sub2", { opacity: 0, duration: 0.3 }, 6.5);
tl.to("#sub3", { opacity: 1, duration: 0.3 }, 7.5);
tl.to("#sub3", { opacity: 0, duration: 0.3 }, 9.5);
tl.to("#sub4", { opacity: 1, duration: 0.3 }, 10.5);
tl.to("#sub4", { opacity: 0, duration: 0.3 }, 13);
window.__timelines["magnetic-cursor"] = tl;
// Paint event for texture upload
canvas.onpaint = function() {
if (!captured) {
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texElementImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, content);
captured = true;
}
};
setTimeout(function() {
if (isSupported()) {
canvas.requestPaint();
}
tl.seek(0);
}, 0);
</script>
</body>
setTimeout(function () {
if (isSupported()) {
canvas.requestPaint();
}
tl.seek(0);
}, 0);
</script>
</body>
</html>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff