← back to Only CSS: Joint Animationcompare with ground truth →
model output
Claude Sonnet 4.6
Only CSS: Joint Animation
A 0.83T 0.30
1<!DOCTYPE html>
2<html lang="en">
3<head>
4<meta charset="UTF-8">
5<meta name="viewport" content="width=device-width, initial-scale=1.0">
6<title>Morphing Bar Animation</title>
7<style>
8 * { margin: 0; padding: 0; box-sizing: border-box; }
9 body {
10 width: 1024px;
11 height: 768px;
12 background: white;
13 display: flex;
14 align-items: center;
15 justify-content: center;
16 overflow: hidden;
17 }
18 canvas {
19 position: absolute;
20 top: 0; left: 0;
21 }
22</style>
23</head>
24<body>
25<canvas id="c" width="1024" height="768"></canvas>
26<script>
27const canvas = document.getElementById('c');
28const ctx = canvas.getContext('2d');
29
30const COLOR = '#4BADD3';
31const LINE_WIDTH = 52;
32
33// Animation: horizontal bar → V shape → L shape → V shape → horizontal bar (loop)
34// Total duration: ~3 seconds per cycle
35
36// Key points for the shape:
37// Frame 1 (t=0): Horizontal bar from ~(365,385) to ~(880,385)
38// Frame 2 (t=0.3): V shape - left segment goes up-left, right segment goes right
39// left end: ~(370,390), bend: ~(615,490), right end: ~(870,460)
40// Frame 3 (t=0.6): L shape - vertical segment up, horizontal segment right
41// top: ~(415,340), bend: ~(415,580), right end: ~(720,580)
42// Frame 4-5: hold L shape
43// Frame 6: back to V
44// Then back to horizontal
45
46// I'll animate using parametric interpolation between keyframes
47
48function lerp(a, b, t) { return a + (b - a) * t; }
49
50function lerpPt(p1, p2, t) {
51 return { x: lerp(p1.x, p2.x, t), y: lerp(p1.y, p2.y, t) };
52}
53
54// Keyframe shapes: each shape is [p1, p2, p3] where p2 is the bend point
55const shapes = {
56 horizontal: [
57 { x: 365, y: 385 },
58 { x: 622, y: 385 }, // midpoint as bend (straight)
59 { x: 880, y: 385 }
60 ],
61 vShape: [
62 { x: 370, y: 370 },
63 { x: 610, y: 490 },
64 { x: 870, y: 460 }
65 ],
66 lShape: [
67 { x: 415, y: 330 },
68 { x: 415, y: 575 },
69 { x: 720, y: 575 }
70 ]
71};
72
73function easeInOut(t) {
74 return t < 0.5 ? 2*t*t : -1+(4-2*t)*t;
75}
76
77function drawShape(p1, bend, p3) {
78 ctx.clearRect(0, 0, 1024, 768);
79 ctx.strokeStyle = COLOR;
80 ctx.lineWidth = LINE_WIDTH;
81 ctx.lineCap = 'round';
82 ctx.lineJoin = 'round';
83
84 ctx.beginPath();
85 ctx.moveTo(p1.x, p1.y);
86 ctx.lineTo(bend.x, bend.y);
87 ctx.lineTo(p3.x, p3.y);
88 ctx.stroke();
89}
90
91function interpShape(s1, s2, t) {
92 return [
93 lerpPt(s1[0], s2[0], t),
94 lerpPt(s1[1], s2[1], t),
95 lerpPt(s1[2], s2[2], t)
96 ];
97}
98
99// Timeline (seconds):
100// 0.0 - 0.5: horizontal → vShape
101// 0.5 - 1.2: vShape → lShape
102// 1.2 - 2.0: hold lShape
103// 2.0 - 2.7: lShape → vShape
104// 2.7 - 3.2: vShape → horizontal
105// 3.2 - 3.7: hold horizontal
106// 3.7: loop
107
108const CYCLE = 3700; // ms
109
110function animate(timestamp) {
111 const t = (timestamp % CYCLE) / CYCLE;
112 const ms = t * CYCLE;
113
114 let shape;
115
116 if (ms < 500) {
117 // horizontal → vShape
118 const p = easeInOut(ms / 500);
119 shape = interpShape(shapes.horizontal, shapes.vShape, p);
120 } else if (ms < 1200) {
121 // vShape → lShape
122 const p = easeInOut((ms - 500) / 700);
123 shape = interpShape(shapes.vShape, shapes.lShape, p);
124 } else if (ms < 2000) {
125 // hold lShape
126 shape = shapes.lShape;
127 } else if (ms < 2700) {
128 // lShape → vShape
129 const p = easeInOut((ms - 2000) / 700);
130 shape = interpShape(shapes.lShape, shapes.vShape, p);
131 } else if (ms < 3200) {
132 // vShape → horizontal
133 const p = easeInOut((ms - 2700) / 500);
134 shape = interpShape(shapes.vShape, shapes.horizontal, p);
135 } else {
136 // hold horizontal
137 shape = shapes.horizontal;
138 }
139
140 drawShape(shape[0], shape[1], shape[2]);
141 requestAnimationFrame(animate);
142}
143
144requestAnimationFrame(animate);
145</script>
146</body>
147</html>