A lightweight, dependency-free library for ultra-smooth scroll-driven canvas animations.
- 🎞 Frame-by-frame scroll animations using Canvas
- 🖼 Image sequence support (PNG/JPG)
- 🧩 Subframe extraction for large spritesheets
- 🎬 MP4 decoding via the modern
VideoDecoderAPI - ⚡ High performance thanks to
ImageBitmapand caching - 📦 Zero dependencies
- 🔌 Easy integration via a custom HTML tag:
<scroll-animation>
npm install your-package-name
<script src="scroll-animation.js" type="module"></script>
-
Add the custom element:
<scroll-animation section-id="hero" animation-id="product-spin" host="https://your-host.com/animations"> </scroll-animation>
-
Create an
animation.jsonalongside the frame assets:
product-spin/
├─ 0001.png
├─ 0002.png
├─ ...
└─ animation.json
Example animation.json:
[
{
"imageSrcUrl": "",
"imgSize": [1920, 1080],
"numFrames": 120,
"files": [".png"],
"numSourceFiles": 120,
"reverse": false
}
]This configuration describes how the animation should be loaded and rendered.
my-app/
├─ animations/
│ └─ product-spin/
│ ├─ 0001.png
│ ├─ 0002.png
│ ├─ ...
│ └─ animation.json
├─ src/
│ └─ main.js
└─ index.html
The library:
- Loads all image frames or decodes video frames
- Splits render files into subframes if needed
- Sorts and caches frames globally
- Maps scroll position → frame index
- Renders the selected frame to a
<canvas>inside your<scroll-animation>element
getFrameIndex() {
const currentScrollPosition =
(-this.scrollSection.getBoundingClientRect().top) /
((this.height - this.canvasElement.clientHeight) - window.innerHeight);
return Math.round(currentScrollPosition * this.numFrames);
}If your animation is a single .mp4 file, the library uses the VideoDecoder API:
const videoDecoder = new VideoDecoder({
output: (frame) => {
createImageBitmap(frame).then((bitmap) => {
bitmap.index = images.length;
images.push(bitmap);
});
},
error: console.error
});This lets you deliver high-quality scroll animations without storing hundreds of images.
<section id="hero" style="height: 300vh;">
<scroll-animation
section-id="hero"
animation-id="product-spin"
host="/animations">
</scroll-animation>
</section>
<script type="module">
import './scroll-animation.js';
</script>| Key | Type | Description |
|---|---|---|
| imageSrcUrl | string |
Base path for frames |
| imgSize | [w,h] |
Frame width & height |
| numFrames | number |
Total frames |
| numSourceFiles | number |
Number of source files |
| files | string[] |
Filenames or extension |
| reverse | boolean |
Play animation backwards |
| range | [a,b] |
Scroll range control |
| id | string |
Cache identifier |
Your <scroll-animation> tag supports these attributes:
| Attribute | Purpose |
|---|---|
| section-id | Section tied to the scroll position |
| animation-id | Folder name / animation config identifier |
| host | Server location of your animation assets |
Example:
<scroll-animation section-id="intro" animation-id="car" host="/assets/animations"></scroll-animation>Build and test commands depend on your project setup; add more if needed.
npm run dev
npm run build
Contributions, issues, and feature requests are welcome — feel free to open a PR or start a discussion.
MIT License — free to use, modify, and distribute.
If you find this library useful, please consider:
- ⭐ Starring the repo
- 🐛 Reporting issues
- 💬 Suggesting new features