Skip to content

Node module for generating sprites (with coordinate maps) for creating beautiful, reversible, mobile-friendly animations with canvas

License

Notifications You must be signed in to change notification settings

jonnybrooks/flicker

Repository files navigation

flicker

Create beautiful, seekable, mobile-friendly-auto-playable HTML videos (faked with sprite animations and canvas).

Demo: flicker.jonathan-brooks.co.uk

Install

  npm install flicker

Why?

I basically didn't like HTML video limitations on mobile (specifically no autoplay for hero backgrounds), so I figured a good solution would be to use sprite animations - but packing the sprites and converting the frame coords to JSON with an external tool took forever, and I wanted to play/pause/reverse/seek my sprite animations easier. So I made a tool which does both. Essentially, my pipeline was as follows:

(create video)     --> video 
(photoshop export) --> image sequence(s)
(flicker package)  --> packed sprites & JSON frame coord map
(flicker.min.js)   --> canvas animation goodness

Useage - Step 0 - Generating image sequences for packing

This step, namely converting your video into an image sequence, is at your discretion. Here are a couple possible methods:

Useage - Step 1 - Packing the image sequences into sprites

After running npm install flicker, you should notice the following folders in the package directory:

.
+-- sequences/ (where to put your image sequences)
+-- flicker/ (where the sequences are exported to packaged sprites and frame coord map)

Before running the flicker app, you'll need to move your image sequences into the sequences/ folder. Each sequence should be stored within it's own folder, resembling the following:

.
+-- sequences/
|   +-- sequence1/
|   |	+-- image1.jpg 
|   |	+-- image2.jpg 
|   +-- sequence2/
|   |	+-- image1.jpg 
|   |	+-- image2.jpg 
+-- flicker/

Then run npm start from the package directory - this process can take a little while, depending on the amount of images being packed into sprites. After it's finished, your packed sprites and the JSON map representing the entire animation (both sequence1 and sequence2 combined) will be inside flicker/:

.
+-- sequences/
|   +-- sequence1/
|   |	+-- image1.jpg 
|   |	+-- image2.jpg 
|   +-- sequence2/
|   |	+-- image1.jpg 
|   |	+-- image2.jpg 
+-- flicker/
|   +-- sequence1_sprite.jpg/
|   +-- sequence2_sprite.jpg/
|   +-- flicker_map.json/

Useage - Step 2 - Using the sprites in your HTML

You can find an example by heading to flicker.jonathan-brooks.co.uk and viewing the page source, but I'll include a snippet on it's set up here:

Defining a flicker context

Quick note, the canvas width and height attributes have been set according to the native size of each image in the sprite (each image is the same size, as they're all derived from the same video). Make sure to do this for your canvas too - then scaling it is as easy changing the width/height in CSS.

Additionally, the following snippet has a .controls range slider which acts as a seek control for the flicker. At a later date I'll document how you can replace this with your own custom implementation using the Flicker.seek() function.

<div id="flicker1" class="flicker">
	<canvas class="canvas" width="560" height="420"></canvas>
	<div class="controls">
		<input type="range" min="0" max="1" value="0" step="1" oninput="this.setAttribute('value', this.value);"/>
	</div>
	<div class="sprites" style="display: none;">
		<img class="sprite" src="flicker/sequence001_sprite.jpg" alt=" "/>
		<img class="sprite" src="flicker/sequence002_sprite.jpg" alt=" "/>
	</div>		
</div>				
<script type="text/javascript" src="js/flicker.min.js"></script>

Creating and configuring an instance of the flicker object

The js setup in this snippet is (thus far) as complicated as the config gets. I'll be adding additional features in the future, like additional event handlers, and more config options like playThrough (pausing between sequences) and show/hide default seek controls

		
var map = // copy flicker_map.json into a variable here, like { frames: [SUPER LONG ARRAY] };

// register the Flicker object

var myFlicker = new Flicker({
	animation: map, // specify the frame coordinate map
	rootPath: 'flicker/', // specify the root path for the sprites (defaults to flicker/)
	container: document.getElementById('flicker1') // specify the context for the flicker
});

// flicker provides a utility called waitOnImages which waits for the source sprites to load
// this is necessary when using high res images, as playing the animation before the images
// have loaded results in the canvas drawing a blank image

myFlicker.utils.waitOnImages(function () {
	console.log('done loading');

	/*  
		BACKGROUND LOOP EXAMPLE
	    play from current frame (defaults to frame 0 (the beginning)), 
		then register an event handler on flickerEnd to play the flicker
		from the beginning whenever the flicker finishes
	*/

	this.play();
	this.on('flickerEnd', function(direction){
		console.log('flicker ended whilst playing %s', direction);
		this.play(0); // play from the beginning
	});

	/*  
		ILLUSTRATIVE EXAMPLE
	    play from current frame, wait for 2 seconds, pause, wait for 3 seconds,
		reverse from current frame, wait for one second, play from frame 0 (the beginning)
	*/

	// this.play().wait(2).pause().wait(3).reverse().wait(1).play(0);
				
});

// naturally, event handlers can also be registered outside the
// waitOnImages function, directly on the flicker object
// the sequenceChange event is emitted whenever the flicker transitions
// from one sequence to another

myFlicker.on('sequenceChange', function(seq){
	console.log('transitioned to sprite sequence: %s', seq);
	/*  
		STOP PLAYTHROUGH EXAMPLE
		pauses between each sequence, requiring manual replaying
		to continue the flicker (perhaps triggered by a scroll event?)
	*/
	
	// this.pause(); 
});	

Notes

It should be noted that this tool is very much in pre-production, and as such it's still pretty limited. Here are some of the issues I'll be working on henceforth:

  • At the moment, because the sprite packer tries to process all the sequences at once, it falls over if the sequences are too long (and they often are - 24 images = 1 second of video after all). This fix is a priority and will come soon.
  • In testing the images package (which flicker depends on for sprite packing), I found that somewhat arbitrarily it falls over when the sprite resolution roughly exceeds 10,000 x 10,000 pixels. Again, I will do some investigating into why this limit exists and see if I can work around it with some crafty image compression (or something similar).
  • At the moment you have to manually separate the huge monolithic image sequences you get from the video export manually - at some point in the future I'm hoping to add custom CLI parameters/JSON config file which let you specify when to cut the sequence into sub-sequences at given points in time (specified in frames or seconds from the beginning of the video).

If you have any questions, or wish to contribute, then don't hesitate to contact me!

Happy coding everyone ♥

About

Node module for generating sprites (with coordinate maps) for creating beautiful, reversible, mobile-friendly animations with canvas

Resources

License

Stars

Watchers

Forks

Packages

No packages published