├── .editorconfig ├── .gitignore ├── .npmignore ├── .prettierrc ├── LICENSE ├── README.md ├── demo ├── run-once.css ├── run-once.html ├── track-progress.css └── track-progress.html ├── package-lock.json ├── package.json └── src ├── index.js ├── run-once.js └── track-progress.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | indent_size = 4 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | end_of_line = lf 10 | max_line_length = 200 11 | quote_type = single 12 | 13 | [*.md] 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/* 2 | node_modules 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | demo 2 | src 3 | .editorconfig 4 | .gitignore 5 | .prettierrc 6 | package-lock.json 7 | .github 8 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023 Bramus Van Damme - https://www.bram.us/ 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Scroll-Driven Animations Utilities 2 | 3 | Collection of utility functions for use with Scroll-Driven Animations 4 | 5 | ## Installation 6 | 7 | ```bash 8 | npm i @bramus/sda-utilities 9 | ``` 10 | 11 | ## The Utilities 12 | 13 | ### `runOnce` – Run Scroll-Driven Animation only once 14 | 15 | Example: run the `fade-in` animation on the `.hero` element only once. 16 | 17 | ```js 18 | import { runOnce } from '@bramus/sda-utilities'; 19 | 20 | window.addEventListener('load', (e) => { 21 | const $hero = document.querySelector('#hero'); 22 | runOnce($hero, 'fade-in'); 23 | }); 24 | ``` 25 | 26 | ### `trackProgress` – Sync the progress of an effect to something external 27 | 28 | Example: output the animation progress as text on the page. 29 | 30 | ```js 31 | import { trackProgress } from '@bramus/sda-utilities'; 32 | 33 | window.addEventListener('load', (e) => { 34 | trackProgress(document.querySelector('.animation-subject').getAnimations()[0], (progress) => { 35 | document.querySelector('.animation-subject').innerText = `${(progress * 100).toFixed(5)}%`; 36 | }); 37 | }); 38 | ``` 39 | 40 | ## Demos 41 | 42 | Demos are included in this repository. Run `npm run dev` and visit `http://127.0.0.1:8000/` to see the demos. 43 | 44 | ```bash 45 | npm run dev 46 | ``` 47 | 48 | ## License 49 | 50 | `@bramus/sda-utilities` is released under the MIT public license. See the enclosed `LICENSE` for details. 51 | -------------------------------------------------------------------------------- /demo/run-once.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | header, 7 | footer { 8 | height: 100dvh; 9 | display: grid; 10 | place-content: center; 11 | text-align: center; 12 | } 13 | 14 | main { 15 | width: 90vw; 16 | max-width: 800px; 17 | margin: 0 auto; 18 | } 19 | 20 | ul.photos { 21 | display: grid; 22 | grid-auto-flow: dense; 23 | grid-template-columns: repeat(4, 1fr); 24 | padding: 1vw 0; 25 | gap: 1vw; 26 | } 27 | 28 | li { 29 | list-style: none; 30 | } 31 | 32 | .photo { 33 | animation: animate-in ease-in both; 34 | animation-timeline: view(); 35 | animation-range: entry 25% entry calc(50% + 300px); 36 | transform-origin: 50% 100%; 37 | border-radius: 1vw; 38 | overflow: hidden; 39 | } 40 | 41 | @keyframes animate-in { 42 | from { 43 | opacity: 0.2; 44 | scale: 0.8; 45 | translate: 0 10%; 46 | } 47 | 99% { 48 | outline: none; 49 | } 50 | 100% { 51 | outline: 2px solid lime; 52 | outline-offset: -2px; 53 | } 54 | } 55 | 56 | img { 57 | display: block; 58 | width: 100%; 59 | height: auto; 60 | } 61 | 62 | li:nth-child(5n), 63 | li:nth-child(7n + 3), 64 | li:nth-child(3n) { 65 | grid-row: span 2; 66 | grid-column: span 2; 67 | } 68 | -------------------------------------------------------------------------------- /demo/run-once.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Scroll-Driven Animations Utilities: <code>runOnce</code> 8 | 9 | 20 | 21 | 22 | 23 |
24 |

Run Scroll-Driven Animations only once

25 |

Demo for runOnce

26 |

Scroll ↕

27 |
28 |
29 | 228 |
229 | 232 | 233 | 234 | 235 | -------------------------------------------------------------------------------- /demo/track-progress.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | margin: 0; 4 | padding: 0; 5 | } 6 | 7 | body { 8 | padding: 150vh 0; 9 | display: grid; 10 | place-content: center; 11 | } 12 | 13 | video { 14 | display: block; 15 | position: fixed; 16 | top: 25vh; 17 | left: 1em; 18 | height: 50vh; 19 | width: auto; 20 | z-index: 1; 21 | border: 1px solid #ccc; 22 | } 23 | 24 | @media (orientation: portrait) { 25 | video { 26 | height: auto; 27 | width: 80vw; 28 | } 29 | } 30 | 31 | @keyframes foo { 32 | to { 33 | scale: 1; 34 | } 35 | } 36 | 37 | .animation-subject { 38 | animation: foo linear both; 39 | animation-timeline: view(); 40 | animation-range: cover; 41 | 42 | transform-origin: 0% 50%; 43 | height: 24px; 44 | width: 100vw; 45 | background: hotpink; 46 | display: grid; 47 | place-content: center; 48 | } 49 | 50 | header, 51 | footer { 52 | position: fixed; 53 | left: 0; 54 | right: 0; 55 | text-align: center; 56 | font-style: italic; 57 | } 58 | header { 59 | top: 1em; 60 | } 61 | footer { 62 | bottom: 0; 63 | } 64 | 65 | @layer warning { 66 | .warning { 67 | box-sizing: border-box; 68 | padding: 1em; 69 | margin: 1em 0; 70 | border: 1px solid #ccc; 71 | background: rgba(255 255 205 / 0.8); 72 | 73 | position: fixed; 74 | bottom: 1em; 75 | font-size: 2em; 76 | left: 1em; 77 | right: 1em; 78 | max-width: 80ch; 79 | margin: 0 auto; 80 | z-index: 1000; 81 | } 82 | 83 | .warning > :first-child { 84 | margin-top: 0; 85 | } 86 | 87 | .warning > :last-child { 88 | margin-bottom: 0; 89 | } 90 | 91 | .warning a { 92 | color: blue; 93 | } 94 | .warning--info { 95 | border: 1px solid #123456; 96 | background: rgb(205 230 255 / 0.8); 97 | } 98 | .warning--alarm { 99 | border: 1px solid red; 100 | background: #ff000010; 101 | } 102 | 103 | @supports (animation-timeline: view()) { 104 | .warning:not([data-bug]) { 105 | display: none; 106 | } 107 | } 108 | 109 | @supports (animation-range: 0vh 90vh) { 110 | .warning[data-bug="1427062"] { 111 | display: none; 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /demo/track-progress.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Scroll-Driven Animations Utilities: <code>trackProgress</code> 7 | 8 | 15 | 16 | 17 |
18 | Demo showing trackProgress 19 |
20 | 21 |
0%
22 | 23 |
24 |

⚠️ Your browser does not support Scroll-driven Animations. Please use Chrome 115 or newer.

25 |
26 | 27 | 28 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bramus/sda-utilities", 3 | "version": "1.1.1", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@bramus/sda-utilities", 9 | "version": "1.1.1", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "esbuild": "^0.24.0", 13 | "prettier": "^3.4.1" 14 | } 15 | }, 16 | "node_modules/@esbuild/aix-ppc64": { 17 | "version": "0.24.0", 18 | "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz", 19 | "integrity": "sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==", 20 | "cpu": [ 21 | "ppc64" 22 | ], 23 | "dev": true, 24 | "license": "MIT", 25 | "optional": true, 26 | "os": [ 27 | "aix" 28 | ], 29 | "engines": { 30 | "node": ">=18" 31 | } 32 | }, 33 | "node_modules/@esbuild/android-arm": { 34 | "version": "0.24.0", 35 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.0.tgz", 36 | "integrity": "sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==", 37 | "cpu": [ 38 | "arm" 39 | ], 40 | "dev": true, 41 | "license": "MIT", 42 | "optional": true, 43 | "os": [ 44 | "android" 45 | ], 46 | "engines": { 47 | "node": ">=18" 48 | } 49 | }, 50 | "node_modules/@esbuild/android-arm64": { 51 | "version": "0.24.0", 52 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.0.tgz", 53 | "integrity": "sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==", 54 | "cpu": [ 55 | "arm64" 56 | ], 57 | "dev": true, 58 | "license": "MIT", 59 | "optional": true, 60 | "os": [ 61 | "android" 62 | ], 63 | "engines": { 64 | "node": ">=18" 65 | } 66 | }, 67 | "node_modules/@esbuild/android-x64": { 68 | "version": "0.24.0", 69 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.0.tgz", 70 | "integrity": "sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==", 71 | "cpu": [ 72 | "x64" 73 | ], 74 | "dev": true, 75 | "license": "MIT", 76 | "optional": true, 77 | "os": [ 78 | "android" 79 | ], 80 | "engines": { 81 | "node": ">=18" 82 | } 83 | }, 84 | "node_modules/@esbuild/darwin-arm64": { 85 | "version": "0.24.0", 86 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz", 87 | "integrity": "sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==", 88 | "cpu": [ 89 | "arm64" 90 | ], 91 | "dev": true, 92 | "license": "MIT", 93 | "optional": true, 94 | "os": [ 95 | "darwin" 96 | ], 97 | "engines": { 98 | "node": ">=18" 99 | } 100 | }, 101 | "node_modules/@esbuild/darwin-x64": { 102 | "version": "0.24.0", 103 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.0.tgz", 104 | "integrity": "sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==", 105 | "cpu": [ 106 | "x64" 107 | ], 108 | "dev": true, 109 | "license": "MIT", 110 | "optional": true, 111 | "os": [ 112 | "darwin" 113 | ], 114 | "engines": { 115 | "node": ">=18" 116 | } 117 | }, 118 | "node_modules/@esbuild/freebsd-arm64": { 119 | "version": "0.24.0", 120 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.0.tgz", 121 | "integrity": "sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==", 122 | "cpu": [ 123 | "arm64" 124 | ], 125 | "dev": true, 126 | "license": "MIT", 127 | "optional": true, 128 | "os": [ 129 | "freebsd" 130 | ], 131 | "engines": { 132 | "node": ">=18" 133 | } 134 | }, 135 | "node_modules/@esbuild/freebsd-x64": { 136 | "version": "0.24.0", 137 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.0.tgz", 138 | "integrity": "sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==", 139 | "cpu": [ 140 | "x64" 141 | ], 142 | "dev": true, 143 | "license": "MIT", 144 | "optional": true, 145 | "os": [ 146 | "freebsd" 147 | ], 148 | "engines": { 149 | "node": ">=18" 150 | } 151 | }, 152 | "node_modules/@esbuild/linux-arm": { 153 | "version": "0.24.0", 154 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.0.tgz", 155 | "integrity": "sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==", 156 | "cpu": [ 157 | "arm" 158 | ], 159 | "dev": true, 160 | "license": "MIT", 161 | "optional": true, 162 | "os": [ 163 | "linux" 164 | ], 165 | "engines": { 166 | "node": ">=18" 167 | } 168 | }, 169 | "node_modules/@esbuild/linux-arm64": { 170 | "version": "0.24.0", 171 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.0.tgz", 172 | "integrity": "sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==", 173 | "cpu": [ 174 | "arm64" 175 | ], 176 | "dev": true, 177 | "license": "MIT", 178 | "optional": true, 179 | "os": [ 180 | "linux" 181 | ], 182 | "engines": { 183 | "node": ">=18" 184 | } 185 | }, 186 | "node_modules/@esbuild/linux-ia32": { 187 | "version": "0.24.0", 188 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.0.tgz", 189 | "integrity": "sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==", 190 | "cpu": [ 191 | "ia32" 192 | ], 193 | "dev": true, 194 | "license": "MIT", 195 | "optional": true, 196 | "os": [ 197 | "linux" 198 | ], 199 | "engines": { 200 | "node": ">=18" 201 | } 202 | }, 203 | "node_modules/@esbuild/linux-loong64": { 204 | "version": "0.24.0", 205 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.0.tgz", 206 | "integrity": "sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==", 207 | "cpu": [ 208 | "loong64" 209 | ], 210 | "dev": true, 211 | "license": "MIT", 212 | "optional": true, 213 | "os": [ 214 | "linux" 215 | ], 216 | "engines": { 217 | "node": ">=18" 218 | } 219 | }, 220 | "node_modules/@esbuild/linux-mips64el": { 221 | "version": "0.24.0", 222 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.0.tgz", 223 | "integrity": "sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==", 224 | "cpu": [ 225 | "mips64el" 226 | ], 227 | "dev": true, 228 | "license": "MIT", 229 | "optional": true, 230 | "os": [ 231 | "linux" 232 | ], 233 | "engines": { 234 | "node": ">=18" 235 | } 236 | }, 237 | "node_modules/@esbuild/linux-ppc64": { 238 | "version": "0.24.0", 239 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.0.tgz", 240 | "integrity": "sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==", 241 | "cpu": [ 242 | "ppc64" 243 | ], 244 | "dev": true, 245 | "license": "MIT", 246 | "optional": true, 247 | "os": [ 248 | "linux" 249 | ], 250 | "engines": { 251 | "node": ">=18" 252 | } 253 | }, 254 | "node_modules/@esbuild/linux-riscv64": { 255 | "version": "0.24.0", 256 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.0.tgz", 257 | "integrity": "sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==", 258 | "cpu": [ 259 | "riscv64" 260 | ], 261 | "dev": true, 262 | "license": "MIT", 263 | "optional": true, 264 | "os": [ 265 | "linux" 266 | ], 267 | "engines": { 268 | "node": ">=18" 269 | } 270 | }, 271 | "node_modules/@esbuild/linux-s390x": { 272 | "version": "0.24.0", 273 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.0.tgz", 274 | "integrity": "sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==", 275 | "cpu": [ 276 | "s390x" 277 | ], 278 | "dev": true, 279 | "license": "MIT", 280 | "optional": true, 281 | "os": [ 282 | "linux" 283 | ], 284 | "engines": { 285 | "node": ">=18" 286 | } 287 | }, 288 | "node_modules/@esbuild/linux-x64": { 289 | "version": "0.24.0", 290 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.0.tgz", 291 | "integrity": "sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==", 292 | "cpu": [ 293 | "x64" 294 | ], 295 | "dev": true, 296 | "license": "MIT", 297 | "optional": true, 298 | "os": [ 299 | "linux" 300 | ], 301 | "engines": { 302 | "node": ">=18" 303 | } 304 | }, 305 | "node_modules/@esbuild/netbsd-x64": { 306 | "version": "0.24.0", 307 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.0.tgz", 308 | "integrity": "sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==", 309 | "cpu": [ 310 | "x64" 311 | ], 312 | "dev": true, 313 | "license": "MIT", 314 | "optional": true, 315 | "os": [ 316 | "netbsd" 317 | ], 318 | "engines": { 319 | "node": ">=18" 320 | } 321 | }, 322 | "node_modules/@esbuild/openbsd-arm64": { 323 | "version": "0.24.0", 324 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.0.tgz", 325 | "integrity": "sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==", 326 | "cpu": [ 327 | "arm64" 328 | ], 329 | "dev": true, 330 | "license": "MIT", 331 | "optional": true, 332 | "os": [ 333 | "openbsd" 334 | ], 335 | "engines": { 336 | "node": ">=18" 337 | } 338 | }, 339 | "node_modules/@esbuild/openbsd-x64": { 340 | "version": "0.24.0", 341 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.0.tgz", 342 | "integrity": "sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==", 343 | "cpu": [ 344 | "x64" 345 | ], 346 | "dev": true, 347 | "license": "MIT", 348 | "optional": true, 349 | "os": [ 350 | "openbsd" 351 | ], 352 | "engines": { 353 | "node": ">=18" 354 | } 355 | }, 356 | "node_modules/@esbuild/sunos-x64": { 357 | "version": "0.24.0", 358 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.0.tgz", 359 | "integrity": "sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==", 360 | "cpu": [ 361 | "x64" 362 | ], 363 | "dev": true, 364 | "license": "MIT", 365 | "optional": true, 366 | "os": [ 367 | "sunos" 368 | ], 369 | "engines": { 370 | "node": ">=18" 371 | } 372 | }, 373 | "node_modules/@esbuild/win32-arm64": { 374 | "version": "0.24.0", 375 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.0.tgz", 376 | "integrity": "sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==", 377 | "cpu": [ 378 | "arm64" 379 | ], 380 | "dev": true, 381 | "license": "MIT", 382 | "optional": true, 383 | "os": [ 384 | "win32" 385 | ], 386 | "engines": { 387 | "node": ">=18" 388 | } 389 | }, 390 | "node_modules/@esbuild/win32-ia32": { 391 | "version": "0.24.0", 392 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.0.tgz", 393 | "integrity": "sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==", 394 | "cpu": [ 395 | "ia32" 396 | ], 397 | "dev": true, 398 | "license": "MIT", 399 | "optional": true, 400 | "os": [ 401 | "win32" 402 | ], 403 | "engines": { 404 | "node": ">=18" 405 | } 406 | }, 407 | "node_modules/@esbuild/win32-x64": { 408 | "version": "0.24.0", 409 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.0.tgz", 410 | "integrity": "sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==", 411 | "cpu": [ 412 | "x64" 413 | ], 414 | "dev": true, 415 | "license": "MIT", 416 | "optional": true, 417 | "os": [ 418 | "win32" 419 | ], 420 | "engines": { 421 | "node": ">=18" 422 | } 423 | }, 424 | "node_modules/esbuild": { 425 | "version": "0.24.0", 426 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz", 427 | "integrity": "sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==", 428 | "dev": true, 429 | "hasInstallScript": true, 430 | "license": "MIT", 431 | "bin": { 432 | "esbuild": "bin/esbuild" 433 | }, 434 | "engines": { 435 | "node": ">=18" 436 | }, 437 | "optionalDependencies": { 438 | "@esbuild/aix-ppc64": "0.24.0", 439 | "@esbuild/android-arm": "0.24.0", 440 | "@esbuild/android-arm64": "0.24.0", 441 | "@esbuild/android-x64": "0.24.0", 442 | "@esbuild/darwin-arm64": "0.24.0", 443 | "@esbuild/darwin-x64": "0.24.0", 444 | "@esbuild/freebsd-arm64": "0.24.0", 445 | "@esbuild/freebsd-x64": "0.24.0", 446 | "@esbuild/linux-arm": "0.24.0", 447 | "@esbuild/linux-arm64": "0.24.0", 448 | "@esbuild/linux-ia32": "0.24.0", 449 | "@esbuild/linux-loong64": "0.24.0", 450 | "@esbuild/linux-mips64el": "0.24.0", 451 | "@esbuild/linux-ppc64": "0.24.0", 452 | "@esbuild/linux-riscv64": "0.24.0", 453 | "@esbuild/linux-s390x": "0.24.0", 454 | "@esbuild/linux-x64": "0.24.0", 455 | "@esbuild/netbsd-x64": "0.24.0", 456 | "@esbuild/openbsd-arm64": "0.24.0", 457 | "@esbuild/openbsd-x64": "0.24.0", 458 | "@esbuild/sunos-x64": "0.24.0", 459 | "@esbuild/win32-arm64": "0.24.0", 460 | "@esbuild/win32-ia32": "0.24.0", 461 | "@esbuild/win32-x64": "0.24.0" 462 | } 463 | }, 464 | "node_modules/prettier": { 465 | "version": "3.4.1", 466 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.1.tgz", 467 | "integrity": "sha512-G+YdqtITVZmOJje6QkXQWzl3fSfMxFwm1tjTyo9exhkmWSqC4Yhd1+lug++IlR2mvRVAxEDDWYkQdeSztajqgg==", 468 | "dev": true, 469 | "license": "MIT", 470 | "bin": { 471 | "prettier": "bin/prettier.cjs" 472 | }, 473 | "engines": { 474 | "node": ">=14" 475 | }, 476 | "funding": { 477 | "url": "https://github.com/prettier/prettier?sponsor=1" 478 | } 479 | } 480 | }, 481 | "dependencies": { 482 | "@esbuild/aix-ppc64": { 483 | "version": "0.24.0", 484 | "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz", 485 | "integrity": "sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==", 486 | "dev": true, 487 | "optional": true 488 | }, 489 | "@esbuild/android-arm": { 490 | "version": "0.24.0", 491 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.0.tgz", 492 | "integrity": "sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==", 493 | "dev": true, 494 | "optional": true 495 | }, 496 | "@esbuild/android-arm64": { 497 | "version": "0.24.0", 498 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.0.tgz", 499 | "integrity": "sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==", 500 | "dev": true, 501 | "optional": true 502 | }, 503 | "@esbuild/android-x64": { 504 | "version": "0.24.0", 505 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.0.tgz", 506 | "integrity": "sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==", 507 | "dev": true, 508 | "optional": true 509 | }, 510 | "@esbuild/darwin-arm64": { 511 | "version": "0.24.0", 512 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz", 513 | "integrity": "sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==", 514 | "dev": true, 515 | "optional": true 516 | }, 517 | "@esbuild/darwin-x64": { 518 | "version": "0.24.0", 519 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.0.tgz", 520 | "integrity": "sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==", 521 | "dev": true, 522 | "optional": true 523 | }, 524 | "@esbuild/freebsd-arm64": { 525 | "version": "0.24.0", 526 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.0.tgz", 527 | "integrity": "sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==", 528 | "dev": true, 529 | "optional": true 530 | }, 531 | "@esbuild/freebsd-x64": { 532 | "version": "0.24.0", 533 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.0.tgz", 534 | "integrity": "sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==", 535 | "dev": true, 536 | "optional": true 537 | }, 538 | "@esbuild/linux-arm": { 539 | "version": "0.24.0", 540 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.0.tgz", 541 | "integrity": "sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==", 542 | "dev": true, 543 | "optional": true 544 | }, 545 | "@esbuild/linux-arm64": { 546 | "version": "0.24.0", 547 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.0.tgz", 548 | "integrity": "sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==", 549 | "dev": true, 550 | "optional": true 551 | }, 552 | "@esbuild/linux-ia32": { 553 | "version": "0.24.0", 554 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.0.tgz", 555 | "integrity": "sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==", 556 | "dev": true, 557 | "optional": true 558 | }, 559 | "@esbuild/linux-loong64": { 560 | "version": "0.24.0", 561 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.0.tgz", 562 | "integrity": "sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==", 563 | "dev": true, 564 | "optional": true 565 | }, 566 | "@esbuild/linux-mips64el": { 567 | "version": "0.24.0", 568 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.0.tgz", 569 | "integrity": "sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==", 570 | "dev": true, 571 | "optional": true 572 | }, 573 | "@esbuild/linux-ppc64": { 574 | "version": "0.24.0", 575 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.0.tgz", 576 | "integrity": "sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==", 577 | "dev": true, 578 | "optional": true 579 | }, 580 | "@esbuild/linux-riscv64": { 581 | "version": "0.24.0", 582 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.0.tgz", 583 | "integrity": "sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==", 584 | "dev": true, 585 | "optional": true 586 | }, 587 | "@esbuild/linux-s390x": { 588 | "version": "0.24.0", 589 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.0.tgz", 590 | "integrity": "sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==", 591 | "dev": true, 592 | "optional": true 593 | }, 594 | "@esbuild/linux-x64": { 595 | "version": "0.24.0", 596 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.0.tgz", 597 | "integrity": "sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==", 598 | "dev": true, 599 | "optional": true 600 | }, 601 | "@esbuild/netbsd-x64": { 602 | "version": "0.24.0", 603 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.0.tgz", 604 | "integrity": "sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==", 605 | "dev": true, 606 | "optional": true 607 | }, 608 | "@esbuild/openbsd-arm64": { 609 | "version": "0.24.0", 610 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.0.tgz", 611 | "integrity": "sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==", 612 | "dev": true, 613 | "optional": true 614 | }, 615 | "@esbuild/openbsd-x64": { 616 | "version": "0.24.0", 617 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.0.tgz", 618 | "integrity": "sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==", 619 | "dev": true, 620 | "optional": true 621 | }, 622 | "@esbuild/sunos-x64": { 623 | "version": "0.24.0", 624 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.0.tgz", 625 | "integrity": "sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==", 626 | "dev": true, 627 | "optional": true 628 | }, 629 | "@esbuild/win32-arm64": { 630 | "version": "0.24.0", 631 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.0.tgz", 632 | "integrity": "sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==", 633 | "dev": true, 634 | "optional": true 635 | }, 636 | "@esbuild/win32-ia32": { 637 | "version": "0.24.0", 638 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.0.tgz", 639 | "integrity": "sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==", 640 | "dev": true, 641 | "optional": true 642 | }, 643 | "@esbuild/win32-x64": { 644 | "version": "0.24.0", 645 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.0.tgz", 646 | "integrity": "sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==", 647 | "dev": true, 648 | "optional": true 649 | }, 650 | "esbuild": { 651 | "version": "0.24.0", 652 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz", 653 | "integrity": "sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==", 654 | "dev": true, 655 | "requires": { 656 | "@esbuild/aix-ppc64": "0.24.0", 657 | "@esbuild/android-arm": "0.24.0", 658 | "@esbuild/android-arm64": "0.24.0", 659 | "@esbuild/android-x64": "0.24.0", 660 | "@esbuild/darwin-arm64": "0.24.0", 661 | "@esbuild/darwin-x64": "0.24.0", 662 | "@esbuild/freebsd-arm64": "0.24.0", 663 | "@esbuild/freebsd-x64": "0.24.0", 664 | "@esbuild/linux-arm": "0.24.0", 665 | "@esbuild/linux-arm64": "0.24.0", 666 | "@esbuild/linux-ia32": "0.24.0", 667 | "@esbuild/linux-loong64": "0.24.0", 668 | "@esbuild/linux-mips64el": "0.24.0", 669 | "@esbuild/linux-ppc64": "0.24.0", 670 | "@esbuild/linux-riscv64": "0.24.0", 671 | "@esbuild/linux-s390x": "0.24.0", 672 | "@esbuild/linux-x64": "0.24.0", 673 | "@esbuild/netbsd-x64": "0.24.0", 674 | "@esbuild/openbsd-arm64": "0.24.0", 675 | "@esbuild/openbsd-x64": "0.24.0", 676 | "@esbuild/sunos-x64": "0.24.0", 677 | "@esbuild/win32-arm64": "0.24.0", 678 | "@esbuild/win32-ia32": "0.24.0", 679 | "@esbuild/win32-x64": "0.24.0" 680 | } 681 | }, 682 | "prettier": { 683 | "version": "3.4.1", 684 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.1.tgz", 685 | "integrity": "sha512-G+YdqtITVZmOJje6QkXQWzl3fSfMxFwm1tjTyo9exhkmWSqC4Yhd1+lug++IlR2mvRVAxEDDWYkQdeSztajqgg==", 686 | "dev": true 687 | } 688 | } 689 | } 690 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bramus/sda-utilities", 3 | "version": "1.1.1", 4 | "description": "Collection of utility functions for use with Scroll-Driven Animations", 5 | "type": "module", 6 | "main": "./dist/index.cjs", 7 | "module": "./dist/index.js", 8 | "exports": { 9 | ".": { 10 | "browser": "./dist/index.js", 11 | "import": "./dist/index.js", 12 | "require": "./dist/index.cjs" 13 | } 14 | }, 15 | "unpkg": "./dist/index.js", 16 | "jsdelivr": "./dist/index.js", 17 | "files": [ 18 | "src", 19 | "dist" 20 | ], 21 | "scripts": { 22 | "build-esm": "esbuild --bundle ./src/index.js --outfile=./dist/index.js --format=esm --sourcemap --minify", 23 | "build-cjs": "esbuild --bundle ./src/index.js --outfile=./dist/index.cjs --format=cjs --sourcemap --minify", 24 | "lint": "prettier --check '{src,test,demo}/**/*.{ts,tsx,js,jsx}'", 25 | "prettier": "prettier --write '{src,test,demo}/**/*.{ts,tsx,js,jsx}'", 26 | "build": "npm run build-esm && npm run build-cjs", 27 | "dev": "esbuild --bundle ./src/index.js --outfile=./demo/sda-utilities.js --format=esm --sourcemap --minify --watch --servedir=demo", 28 | "prepack": "npm run prevent-dirty-tree && npm run test", 29 | "prepublish": "npm run build", 30 | "pretest": "npm run build", 31 | "test": "echo \"No tests specified\"", 32 | "preversion": "npm run prevent-dirty-tree && npm run test", 33 | "prevent-dirty-tree": "exit $(git status --porcelain | wc -l)" 34 | }, 35 | "repository": { 36 | "type": "git", 37 | "url": "git+https://github.com/bramus/sda-utilities.git" 38 | }, 39 | "keywords": [ 40 | "css", 41 | "scroll-driven animations" 42 | ], 43 | "author": { 44 | "name": "Bramus Van Damme", 45 | "email": "bramus@bram.us", 46 | "twitter": "@bramus", 47 | "web": "https://www.bram.us/" 48 | }, 49 | "license": "MIT", 50 | "bugs": { 51 | "url": "https://github.com/bramus/sda-utilities/issues" 52 | }, 53 | "homepage": "https://github.com/bramus/sda-utilities#readme", 54 | "devDependencies": { 55 | "esbuild": "^0.24.0", 56 | "prettier": "^3.4.1" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import trackProgress from './track-progress.js'; 2 | import runOnce from './run-once.js'; 3 | 4 | export { runOnce, trackProgress }; 5 | -------------------------------------------------------------------------------- /src/run-once.js: -------------------------------------------------------------------------------- 1 | // Run a Scroll-Driven Animation only once 2 | // This relies on: 3 | // - An animation of a given name to be applied only once onto an element. 4 | // - The `animation-fill-mode` being set to `forwards` or `both` in case the end frame is not the default styling. 5 | const runOnce = ($el, animationName = null) => { 6 | const animations = $el.getAnimations(); 7 | 8 | const shouldAnimationBeStopped = (animation, animationName = null) => { 9 | if (!(animation.timeline instanceof ScrollTimeline)) return false; 10 | 11 | if (animationName === null) { 12 | return true; 13 | } 14 | 15 | return animation.animationName === animationName; 16 | }; 17 | 18 | $el.addEventListener('animationend', (e) => { 19 | // animationend is also triggered when back at the start. Ignore those. 20 | if (e.elapsedTime == 0) return; 21 | 22 | // Extract animation 23 | // Could get the wrong one though, see https://github.com/w3c/csswg-drafts/issues/9010 for details 24 | const animation = animations.find((a) => a.animationName == e.animationName); 25 | 26 | // Only process if the animation name matches 27 | if (shouldAnimationBeStopped(animation, animationName)) { 28 | // Give a warning when the fill mode is not the correct one 29 | if (!['forwards', 'both'].includes(animation.effect.getComputedTiming().fill)) { 30 | console.warn(`The fillMode for the animation “${animationName}” is not set to \`forwards\`. This can cause a glitch when removing the animation.`); 31 | } 32 | 33 | // Commit the styles and remove the animation 34 | animation.commitStyles(); 35 | animation.cancel(); 36 | } 37 | }); 38 | }; 39 | 40 | export default runOnce; 41 | -------------------------------------------------------------------------------- /src/track-progress.js: -------------------------------------------------------------------------------- 1 | const trackProgressThroughEffect = (animation, cb, precision) => { 2 | let progress = null; 3 | 4 | const updateValue = () => { 5 | if (animation && animation.currentTime) { 6 | let newProgress = animation.effect.getComputedTiming().progress * 1; 7 | if (animation.playState === 'finished') newProgress = 1; 8 | newProgress = Math.max(0.0, Math.min(1.0, newProgress)).toFixed(precision); 9 | 10 | if (newProgress != progress) { 11 | progress = newProgress; 12 | cb(progress); 13 | } 14 | } 15 | requestAnimationFrame(updateValue); 16 | }; 17 | 18 | requestAnimationFrame(updateValue); 19 | }; 20 | 21 | // Note: once we have a `progress` event on an Animation, we can drop the rAF 22 | const trackProgressThroughAnimation = (animation, cb, precision) => { 23 | let progress = null; 24 | 25 | const updateValue = () => { 26 | if (animation && animation.currentTime) { 27 | let newProgress = animation.overallProgress.toFixed(precision); 28 | 29 | if (newProgress != progress) { 30 | progress = newProgress; 31 | cb(progress); 32 | } 33 | } 34 | requestAnimationFrame(updateValue); 35 | }; 36 | 37 | requestAnimationFrame(updateValue); 38 | }; 39 | 40 | const trackProgress = (animation, cb, precision = 5) => { 41 | if (("Animation" in globalThis && "overallProgress" in Animation.prototype)) { 42 | trackProgressThroughAnimation(animation, cb, precision); 43 | } else { 44 | trackProgressThroughEffect(animation, cb, precision); 45 | } 46 | }; 47 | 48 | export default trackProgress; 49 | --------------------------------------------------------------------------------