🎭 Library for seamless transition animations of images.

Usage no npm install needed!

<script type="module">
  import eivor from 'https://cdn.skypack.dev/eivor';



🎭 Library for seamless transition animations of images.

View demo page or the sample app from the gif on the right.

What & How

  1. You give it two images
    <img> tag or element with background-image
    Could be different scales, cropped,
  2. It calculates how A and B overlap
    Handles physical image size, crop and different aspect ratio; sizing (object-fit, background-size: cover, contain and percentages), positioning background-position, clip-path, etc...
  3. Creates intermediary element for animation
    Or picks the suitable one of the A and B. (Either the higher quality one, or one that contains the other)
  4. Runs smooth animation
    Transitioning from the position and location of the first image, to the second one.
  5. ???
  6. Profit


npm install eivor
<script type="importmap">
{"imports": {"eivor": "./node_modules/eivor/src/ImageTransition.js"}}


ImageTransition class

source and target can be <img> tag or any html element with background-image css property. Third and optional argument is an options object.

import {ImageTransition} from 'eivor'
let transition = new ImageTransition(source, target, {duration: 1000})
await transition.play()

Optionally you can await transition.ready Promise if you need to wait for images to load.

let transition = new ImageTransition(source, target)
// Wait for source and target images to load. Calculating position delta hasn't yet begun.
await transition.ready
// Images are now loaded, here you can do something
await transition.play()
// animation is over, temporary files are removed from DOM, source and target have returned to their original positions, any additional CSS props are removed.

options object

  • options.easing: string
  • options.duration: number
  • options.fill: string
  • options.mode: 'crop'|'recreate'|'clone' automatically determined. Can be overriden with caution.
    Eivor figures out which image to animate (depending on crop, size and image quality) wheter a) source to target's position while the target is hidden or b) target from source's position while the source is hidden. If cropping one whithin the other is not possible, then a temporary node with the full image is created for the duration of the transition
    crop: Crop, scale and translation are applied to the larger image. Only available if one image fits into the other.
    recreate: The larger image is temporarily resized in order to display the whole image uncropped. Then the target image is animated like crop.
    clone: Like recreate, but animation is applied to a clone of the target image while the original is hidden.

TODOs & Ideas

The script is already mighty as is right now. But there are still some edge cases or nice to have things I'd like to implement.

  • Figure out a way to write unit tests
    testing animations is hard. There's an extensive example/debug file but it has to be tested manually.
  • Add option to specify which image to animate.
    Now you specify source and target nodes. Until recently the script always animated target node from source node's position and size. Now it can also animate source into target's position.
    It'd be nice if user could specify which image to use for transition (either which node to manipulate if *ContainedWithinTarget allows it, or which url to use for clone node)
  • Detect which image is higher quality and use that for animation
    Will work perfectly with 1:1 images. (maybe it should be default case in this scenario)
    May collide with variously shaped and clipped source/target images, thus ignoring sourceContainedWithinTarget / targetContainedWithinSource and forcing use of clone instead of manipulating the image that covers more imge space. (maybe it should be just an option defaulting to false)
  • Ahead of time animation
    Right now, both source and target images have to be in the DOM, and the script waits for both of them to load in order to calculate their position, size, clip, etc...
    If user could provide that information to script, we could launch the animation while the target image is still loading (e.g. navigating to new view)
  • Option to insert clone globally into body instead of source's/target's container
  • Option to force clone instead of manipulating source or target nodes
  • work with pixel values in background-position
  • Investigate use of transform-origin (maybe as an API for user)


MIT, Mike Kovařík, Mutiny.cz