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" />
6<title>Rotating Segmented Pinwheel</title>
7<style>
8 :root{
9 --bg:#f7f7f7;
10 --shadow:rgba(120,80,180,.08);
11 --trail:rgba(170,120,220,.16);
12 }
13 html,body{
14 margin:0;
15 height:100%;
16 background:radial-gradient(circle at 50% 45%, #fbfbfb 0%, #f6f6f6 70%, #f1f1f1 100%);
17 overflow:hidden;
18 font-family:sans-serif;
19 }
20 svg{
21 width:100vw;
22 height:100vh;
23 display:block;
24 }
25 .arm{
26 transform-box: fill-box;
27 transform-origin: 0 0;
28 animation: armFold 3.2s cubic-bezier(.55,.02,.35,1) infinite;
29 }
30 .arm .seg{
31 filter: drop-shadow(0 1px 1px rgba(120,80,180,.08));
32 }
33 .arm .tip{
34 opacity:.9;
35 }
36 .trail{
37 fill:none;
38 stroke-linecap:round;
39 stroke-width:1.2;
40 opacity:0;
41 animation: trailFlash 3.2s linear infinite;
42 }
43
44 @keyframes armFold{
45 0%, 18%, 52%, 100% { transform: rotate(0deg); }
46 32%, 38% { transform: rotate(-34deg); }
47 70%, 84% { transform: rotate(0deg); }
48 }
49
50 @keyframes trailFlash{
51 0%, 22%, 58%, 100% { opacity:0; }
52 28%, 42% { opacity:.9; }
53 50% { opacity:.15; }
54 }
55
56 .whole{
57 animation: wholeSpin 3.2s cubic-bezier(.55,.02,.35,1) infinite;
58 transform-origin: 512px 384px;
59 }
60 @keyframes wholeSpin{
61 0%, 18% { transform: rotate(0deg) scale(1); }
62 32%, 52% { transform: rotate(-36deg) scale(.82); }
63 70%, 100% { transform: rotate(0deg) scale(1); }
64 }
65
66 .pulse{
67 animation: pulse 3.2s ease-in-out infinite;
68 transform-origin: 512px 384px;
69 }
70 @keyframes pulse{
71 0%,18%,100%{ transform:scale(1); }
72 36%,52%{ transform:scale(.92); }
73 76%{ transform:scale(1); }
74 }
75</style>
76</head>
77<body>
78<svg viewBox="0 0 1024 768" aria-label="Animated segmented pinwheel">
79 <defs>
80 <linearGradient id="g0" x1="0" y1="0" x2="1" y2="0">
81 <stop offset="0%" stop-color="#d84ab8"/>
82 <stop offset="100%" stop-color="#b43fc8"/>
83 </linearGradient>
84 <linearGradient id="g1" x1="0" y1="0" x2="1" y2="0">
85 <stop offset="0%" stop-color="#c946c8"/>
86 <stop offset="100%" stop-color="#8f3fd0"/>
87 </linearGradient>
88 <linearGradient id="g2" x1="0" y1="0" x2="1" y2="0">
89 <stop offset="0%" stop-color="#7a3fd8"/>
90 <stop offset="100%" stop-color="#4a43e0"/>
91 </linearGradient>
92 <linearGradient id="g3" x1="0" y1="0" x2="1" y2="0">
93 <stop offset="0%" stop-color="#4a43e0"/>
94 <stop offset="100%" stop-color="#6f4be6"/>
95 </linearGradient>
96 <linearGradient id="g4" x1="0" y1="0" x2="1" y2="0">
97 <stop offset="0%" stop-color="#8a3fd8"/>
98 <stop offset="100%" stop-color="#b04be0"/>
99 </linearGradient>
100 <linearGradient id="g5" x1="0" y1="0" x2="1" y2="0">
101 <stop offset="0%" stop-color="#c946d0"/>
102 <stop offset="100%" stop-color="#e05ad8"/>
103 </linearGradient>
104
105 <g id="segment">
106 <rect x="0" y="-10" width="110" height="20" rx="10" ry="10"></rect>
107 </g>
108 <g id="tip">
109 <path d="M0 0 L26 -8 L22 0 L26 8 Z"></path>
110 </g>
111 </defs>
112
113 <g class="pulse whole" id="pinwheel"></g>
114</svg>
115
116<script>
117const svgNS = "http://www.w3.org/2000/svg";
118const root = document.getElementById("pinwheel");
119
120const cx = 512, cy = 384;
121const arms = 6;
122const baseAngles = [-162, -92, -18, 58, 126, 198];
123const gradients = ["url(#g0)","url(#g1)","url(#g2)","url(#g3)","url(#g4)","url(#g5)"];
124
125function make(tag, attrs = {}) {
126 const el = document.createElementNS(svgNS, tag);
127 for (const k in attrs) el.setAttribute(k, attrs[k]);
128 return el;
129}
130
131function addUse(parent, href, attrs = {}) {
132 const u = make("use", attrs);
133 u.setAttributeNS("http://www.w3.org/1999/xlink", "href", href);
134 parent.appendChild(u);
135 return u;
136}
137
138baseAngles.forEach((a, i) => {
139 const arm = make("g", {
140 class: "arm",
141 transform: `translate(${cx} ${cy}) rotate(${a})`
142 });
143 arm.style.animationDelay = `${i * -0.08}s`;
144
145 const color = gradients[i];
146
147 const seg1 = addUse(arm, "#segment", {
148 class: "seg",
149 transform: "translate(8 0) scale(1.05 1.35)"
150 });
151 seg1.setAttribute("fill", color);
152
153 const seg2 = addUse(arm, "#segment", {
154 class: "seg",
155 transform: "translate(126 0) rotate(0) scale(.78 1.05)"
156 });
157 seg2.setAttribute("fill", color);
158
159 const seg3 = addUse(arm, "#tip", {
160 class: "tip",
161 transform: "translate(214 0) rotate(8) scale(.9 .9)"
162 });
163 seg3.setAttribute("fill", color);
164
165 root.appendChild(arm);
166
167 const trail = make("path", {
168 class: "trail",
169 stroke: i % 2 ? "rgba(190,120,220,.18)" : "rgba(120,120,230,.18)"
170 });
171
172 const r1 = 92, r2 = 210;
173 const a1 = (a - 34) * Math.PI / 180;
174 const a2 = (a + 18) * Math.PI / 180;
175 const mx = cx + Math.cos((a - 10) * Math.PI / 180) * 150;
176 const my = cy + Math.sin((a - 10) * Math.PI / 180) * 150;
177 const x1 = cx + Math.cos(a1) * r1;
178 const y1 = cy + Math.sin(a1) * r1;
179 const x2 = cx + Math.cos(a2) * r2;
180 const y2 = cy + Math.sin(a2) * r2;
181 trail.setAttribute("d", `M ${x1} ${y1} Q ${mx} ${my} ${x2} ${y2}`);
182 trail.style.animationDelay = `${i * -0.08}s`;
183 root.insertBefore(trail, root.firstChild);
184});
185
186// subtle center
187root.appendChild(make("circle", {
188 cx, cy, r: 4.5, fill: "#8f4ad8", opacity: ".9"
189}));
190</script>
191</body>
192</html>