[Archived] Archived WIP of vielle.dev
at master 304 lines 5.9 kB view raw
1--- 2import { blog, utils } from "@/config"; 3 4interface Props { 5 id: number; 6 of: number; 7} 8 9const { id, of } = Astro.props; 10 11const length = utils.getRandom(blog.balloons.length); 12const offset = utils.getRandom(blog.balloons.offset); 13const rotation = new Array(5) 14 .fill(0) 15 .map((_) => utils.getRandom(blog.balloons.rotation)); 16--- 17 18<div 19 class="cable" 20 style={`--length: ${length}px; 21 --id: ${id}; 22 --of: ${of}; 23 --offset: ${offset}px; 24 ${rotation.map((x, i) => `--rot-${i}: ${x}deg;`).join(" ")} 25 --timing: ${utils.getRandom(blog.balloons.timing)}s; 26 `} 27> 28 <div 29 class="balloon" 30 style={`--width: ${utils.getRandom(blog.balloons.size[0])}px; 31 --height: ${utils.getRandom(blog.balloons.size[1])}px;`} 32 tabindex="-1" 33 data-min-time={blog.balloons.time[0]} 34 data-max-time={blog.balloons.time[1]} 35 role="none" 36 > 37 <div class="cable-tie"></div> 38 <div class="tie"></div> 39 </div> 40</div> 41 42<script> 43 const balloons = document.querySelectorAll(".balloon"); 44 balloons.forEach((el) => { 45 if (!(el instanceof HTMLElement)) return; 46 47 const mintime = Number(el.dataset["minTime"] ?? "0"); 48 const maxtime = Number(el.dataset["maxTime"] ?? "0"); 49 50 el.addEventListener("click", () => { 51 const cableParent = el.parentElement; 52 const postParent = el.parentElement?.parentElement; 53 if (!cableParent) throw new Error("No parent 1 level up!!!"); 54 if (!postParent) throw new Error("No parent 2 levels up!!!"); 55 56 el.blur(); 57 58 Promise.all([ 59 el.animate( 60 { opacity: [1, 0] }, 61 { 62 duration: 100, 63 fill: "forwards", 64 }, 65 ).finished, 66 67 cableParent.animate( 68 [ 69 {}, 70 { 71 height: 0, 72 top: 0, 73 }, 74 ], 75 { 76 duration: 500, 77 fill: "forwards", 78 }, 79 ), 80 81 postParent.animate( 82 [ 83 {}, 84 { 85 top: "calc(var(--x-offset-0) + 500px)", 86 }, 87 ], 88 { 89 duration: 1000, 90 easing: "ease-in-out", 91 }, 92 ).finished, 93 ]).then(() => { 94 const duration = (mintime + Math.random() * (maxtime - mintime)) * 1000; 95 96 el.animate( 97 { 98 opacity: [0, 1], 99 scale: [0, 1], 100 offset: [0, 1], 101 }, 102 { 103 duration, 104 fill: "forwards", 105 // easing: "ease-in", 106 }, 107 ); 108 109 cableParent.animate( 110 [ 111 { 112 height: 0, 113 top: 0, 114 }, 115 { 116 height: "var(--length)", 117 top: "calc(-1 * var(--length))", 118 }, 119 ], 120 { 121 duration, 122 fill: "forwards", 123 }, 124 ); 125 126 postParent.animate( 127 [ 128 { 129 top: "calc(var(--x-offset-0) + 500px)", 130 }, 131 {}, 132 ], 133 { 134 duration, 135 fill: "forwards", 136 easing: "ease-in", 137 }, 138 ); 139 }); 140 }); 141 }); 142</script> 143 144<style> 145 @property --rot-0 { 146 syntax: "<angle>"; 147 inherits: false; 148 initial-value: 0px; 149 } 150 151 @property --rot-1 { 152 syntax: "<angle>"; 153 inherits: false; 154 initial-value: 0px; 155 } 156 157 @property --rot-2 { 158 syntax: "<angle>"; 159 inherits: false; 160 initial-value: 0px; 161 } 162 163 @property --rot-3 { 164 syntax: "<angle>"; 165 inherits: false; 166 initial-value: 0px; 167 } 168 169 @property --rot-4 { 170 syntax: "<angle>"; 171 inherits: false; 172 initial-value: 0px; 173 } 174 175 @keyframes tilt { 176 from, 177 to { 178 rotate: var(--rot-0); 179 } 180 181 20% { 182 rotate: var(--rot-1); 183 } 184 185 40% { 186 rotate: var(--rot-2); 187 } 188 189 60% { 190 rotate: var(--rot-3); 191 } 192 193 80% { 194 rotate: var(--rot-4); 195 } 196 } 197 198 @keyframes inv-tilt { 199 from, 200 to { 201 rotate: calc(-1 * var(--rot-0)); 202 } 203 204 20% { 205 rotate: calc(-1 * var(--rot-1)); 206 } 207 208 40% { 209 rotate: calc(-1 * var(--rot-2)); 210 } 211 212 60% { 213 rotate: calc(-1 * var(--rot-3)); 214 } 215 216 80% { 217 rotate: calc(-1 * var(--rot-4)); 218 } 219 } 220 221 @keyframes bouncing { 222 from, 223 to { 224 scale: 1 1; 225 } 226 50% { 227 scale: 1.05 1.1; 228 } 229 } 230 231 .cable { 232 position: absolute; 233 234 width: 5px; 235 height: var(--length); 236 border-radius: 2.5px; 237 background: black; 238 239 [data-time="night"] + * & { 240 background: #404040; 241 } 242 243 z-index: -99; 244 top: calc(-1 * var(--length)); 245 left: calc( 246 100% / var(--of) * var(--id) + 100% / 2 / var(--of) + var(--offset) 247 ); 248 249 transform-origin: bottom; 250 rotate: var(--rot-0); 251 252 @media (prefers-reduced-motion: no-preference) { 253 animation: infinite var(--timing) linear tilt; 254 } 255 } 256 257 .balloon { 258 border: none; 259 260 width: var(--width); 261 height: var(--height); 262 border-radius: calc(var(--width) / 2); 263 264 /* inherits from parent <Post /> */ 265 background: var(--colour); 266 /* 95% instead of 100% to prevent gaps between cable */ 267 translate: -50% -95%; 268 transform-origin: bottom; 269 rotate: calc(-1 * var(--rot-0)); 270 271 @media (prefers-reduced-motion: no-preference) { 272 animation: infinite var(--timing) linear inv-tilt; 273 } 274 } 275 276 .cable-tie { 277 width: 17.5px; 278 height: 5px; 279 280 position: absolute; 281 bottom: -2.5px; 282 left: 50%; 283 translate: -50%; 284 z-index: 1; 285 286 border-radius: 2.5px; 287 background-color: black; 288 289 [data-time="night"] + * & { 290 background: #404040; 291 } 292 } 293 294 .tie { 295 width: 20px; 296 height: 20px; 297 background-color: var(--colour); 298 clip-path: polygon(50% 0, 0 100%, 100% 100%); 299 position: absolute; 300 bottom: -10px; 301 left: 50%; 302 translate: -50%; 303 } 304</style>