Frame Fusion
Animate sequences of images.
Just like animated GIFs or animated PNGs, but you can use higher quality images and have more control over the animation.
Find it on GitHub.
Motivation
Recently, I had a client that relies on the heavy use of animated GIFs in their website. They had used them for a most of their campaigns, but at one point, the quality of the GIFs became an issue because of the design and the style they were trying to achieve. The GIFs had a lot of soft shadows and gradients, which looked terrible when were converted to an animated GIF.
For this reason, I had to implement a solution that would allow me to animate the images in a sequence, and have the best of both worlds — the ability to use high quality images and the ability to have more control over the animation.
The solution I came up with worked pretty well, albeit it was complicated further with the need to work inside the carousel, which required the animation to be reinitialized on the slides where it appears, as the carousel plugin was copying the images and was losing the original references.
That’s why I decided to create a new library that would allow me to animate the images in a sequence, and expand from there to support other use cases, such as stepping through the images one by one, stopping and pausing the animation, and more.
Installation
Install the library.
npm install @chameleonmind/frame-fusion
Frame Fusion comes with a small CSS file that you can include in your project, you can import it with the following code.
import '@chameleonmind/frame-fusion/styles/main.css'
Styles
You can totally skip the default styles if you would like to use the library with your own styles.
The original CSS file is using the data attributes to identify the elements and apply the styles, but you can opt to write your own classes and styles.
You can provide your own active and poster hidden classes in options, check the options section below.
Here's the complete CSS that's included in the library, so you can use it as a reference.
/*
As the images are all absolutely positioned,
you need to set the container to relative positioning
*/
[data-ff-wrapper] {
position: relative;
overflow: hidden;
}
/*
You could replace this to target only the images,
could be more performant if you have a lot of frames
*/
[data-ff-wrapper] > * {
display: block;
width: 100%;
height: auto;
position: absolute;
top: 0;
left: 0;
visibility: hidden;
}
/*
This is the poster image that is shown before the animation starts
*/
[data-ff-wrapper] > [data-ff-poster] {
visibility: visible;
}
/*
This is the active image that is shown during the animation.
Note that we are using the visibility property to show the image.
Avoid using display none, as it will not load the image,
and also this way you can use the transitions if you want.
*/
[data-ff-wrapper] > [data-ff-active] {
visibility: visible;
}
/*
This data-ff-hidden is used to hide the images that are not active, for example, the poster image
*/
[data-ff-wrapper] > [data-ff-hidden] {
visibility: hidden;
}
Usage
If you have a sequence of images, place them inside a container with the data-ff-wrapper
attribute.
In this example, we have 6 images in a container with the data-ff-wrapper
attribute, an id which we will use to
initialize the animation, and a height of 400px, just to avoid the additional CSS.
<div id="animatedFrames" data-ff-wrapper style="height: 400px">
<img src="https://placehold.co/600x400?text=1" alt="Frame 1" />
<img src="https://placehold.co/600x400?text=2" alt="Frame 2" />
<img src="https://placehold.co/600x400?text=3" alt="Frame 3" />
<img src="https://placehold.co/600x400?text=4" alt="Frame 4" />
<img src="https://placehold.co/600x400?text=5" alt="Frame 5" />
<img src="https://placehold.co/600x400?text=6" alt="Frame 6" />
</div>
Then initialize the animation with the sequenceAnimation
function.
import { sequenceAnimation } from '@chameleonmind/frame-fusion'
const animation = sequenceAnimation('#animatedFrames', {
autoplay: true,
delay: 50,
})
The autoplay
option is set to true
so the animation will start automatically when the page and images are loaded.
If you're using the provided CSS, you'll notice that all the images are absolutely positioned and hidden by default. They are hidden using the visibility property, so they are not visible on the page, but they will be loaded and taken into account when the page renders.
Be careful not to change the CSS for the image, and add z-index or transitions to it, because it can cause unexpected behavior.
Actually, you can play with the transitions if you're using this lib to just cycle through images, it could create some interesting effects.
Options
You can pass an options object to the sequenceAnimation
function, for example:
import { sequenceAnimation } from '@chameleonmind/frame-fusion'
const animation = sequenceAnimation('#animatedFrames', {
// options here
})
Option | Type | Default | Description |
---|---|---|---|
autoplay | boolean | false | Start the animation automatically when the page and images are loaded. |
delay | number | undefined | Delay in milliseconds between each frame. |
framerate | number | 24 | Number of frames per second. |
fillMode | string | forwards | The fill mode of the animation, on which frame it should stop. Can be forwards (stops on the last frame), backwards (stops on the beginning frame) or poster (stops on poster image). |
direction | string | normal | The direction of the animation. Can be normal , reverse , alternate or alternate-reverse . |
repeat | number | undefined | The number of times the animation should repeat. |
visibleClass | string | undefined | Class name to add to the visible frames. Use this if you want to provide your own CSS. |
posterHiddenClass | string | undefined | Class name to add to the poster image when it is hidden. Use this if you want to provide your own CSS. |
keepFramesVisible | boolean | false | Keep the frames visible for the previous frames. Use this if you want your animation to stack up all your frames. Works only with direction: normal for now. |
selector | string | undefined | CSS selector to use to find the images. Useful if you have images that shouldn't be included in the animation, but are in the container element. Potentialy, it could be used for frames that are not necessarily images. |
frames | string[] | undefined | Array of image paths. If provided, the image elements will be created from the paths. You cannot apply any other attributes or classes to the images. The images will be appended to the main element. |
poster | string | undefined | Path to the poster image. If provided, the image will be created from the path. You cannot apply any other attributes or classes to the image. The image will be appended to the main element. |
Callbacks
You can pass a callback function to the sequenceAnimation
function, for example:
import { sequenceAnimation } from '@chameleonmind/frame-fusion'
const animation = sequenceAnimation('#animatedFrames', {
// options here
onChangeState: (state) => {
console.log('state', state)
},
})
Option | Type | Args | Description |
---|---|---|---|
onLoad | function | void | Callback function that is called when the animation is loaded. |
onEnd | function | void | Callback function that is called when the animation ends. |
onRepeat | function | void | Callback function that is called when the animation repeats. |
onStop | function | { currentIndex: number, nextFrameNumber: number, animationDirection: 'forward' | 'reverse' } | Callback function that is called when the animation is stopped. |
onPlay | function | { currentIndex: number, nextFrameNumber: number, animationDirection: 'forward' | 'reverse' } | Callback function that is called when the animation is played. |
onPause | function | { currentIndex: number, nextFrameNumber: number, animationDirection: 'forward' | 'reverse' } | Callback function that is called when the animation is paused. |
onChangeState | function | state: 'play' | 'pause' | 'stop' | Callback function that is called when the animation state changes. |
onReverse | function | void | Callback function that is called when the animation is reversed. |
Methods
You can call the following methods on the animation object returned by the sequenceAnimation
function. Other
than goToFrame
method, the methods don't take any arguments.
For example:
import { sequenceAnimation } from '@chameleonmind/frame-fusion'
const animation = sequenceAnimation('#animatedFrames', {
// options here
})
animation.play()
// then you could have a button that calls
animation.stop()
// or a button that calls
animation.pause()
// or a button that calls
animation.nextFrame()
// or a button that calls
animation.goToFrame(3)
Method | Description |
---|---|
play | Starts the animation. |
pause | Pauses the animation. |
stop | Stops the animation. |
nextFrame | Moves to the next frame. |
previousFrame | Moves to the previous frame. |
restart | Restarts the animation. |
goToFrame | Go to a specific frame. |