npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

stimulate

v0.0.30

Published

animation utility

Downloads

12

Readme

stimulate

javascript animation utility

Build Status

Coverage Status

Sauce Test Status

demo

npm install stimulate --save

Then:

import stimulate from 'stimulate';
stimulate({from:10, to:20});

or:

var stimulate = require('stimulate').stimulate;
stimulate({from:10, to:20});

or:

<script type="text/javascript" src="./dist/stimulate.min.js"></script>
<script>
    stimulate({from:10, to:20});
</script>

Overview

Stimulate helps you create animations with the following features:

  • stopping
  • resuming
  • resetting
  • looping
  • delaying
  • easing
  • reporting progress
  • handling onComplete callbacks
  • achieving high frame rates
  • integrating with various frameworks
  • syncing and managing multiple instances
  • reversing (even mid animation)
  • changing speed (even mid animation)
  • changing from and to (even mid animation)

Basics

Stimulate increments over time for animations. Use it in conjunction with any performant DOM manipulation system (vanilla javascript, jQuery, React, Angular).

Animation is achieved by showing a rapid sucession of frames. Stimulate's frame triggering is controlled through javascript: with window.requestAnimationFrame when it's available in the browser, and a recursive setTimeout method when it's not. Stimulate offers performance optimizations, such as real-time accurate progress reporting regardless of browser-performance-affected framerate conistency, frame-call batching, and progress report syncing over simultaneous Stimulate instances, and initial frame customizability. More depth on those later.

Stimulate does not use CSS3 transitions or @keyframes. CSS3 methods have their place, but they shortcoming like browser rendering complications, awkward javascript API, limited easing capabilities, and inefficient DOM-sniffing progress lookup. CSS3 animations have some advanatages, like easy assignability in css and better framerates in browsers lacking requestAnimationFrame.

You might prefer javascript-frame-based animation over CSS3 methods when interactive performance is critical, like for swiping/throwing elements. For simpler applications, like animating button hover/touch state, you might prefer CSS3 methods.

Here are some basic examples of using Stimulate in various popular contexts. These will animate an element from left to right a total of 95px over one second.

vanilla js

var elFoo = getElementById('foo');
stimulate({
    from: 5,
    to: 100,
    duration: 1000,
    frame: function(progress){
        elFoo.style.transform = 'translate3d(' + progress.tweened + 'px 0 0)';
    }
});

React

var Component = React.createClass({
    getInitialState: function () {
        return {
            xPosition: 5
        };
    },
    componentDidMount: function() {
        stimulate({
            from: 5,
            to: 100,
            duration: 1000,
            frame: function(progress){
                this.setState({
                    xPosition: progress.tweened
               });
            }
        });
    },
    render: function () {
        var style = {
            transform: 'translate3d(' + this.state.xPosition + 'px 0 0)'
        };
        return <div style={style}>Hello</div>;
    }
});

jQuery

var $elFoo = $('#foo');
stimulate({
    from: 5,
    to: 100,
    duration: 1000,
    frame: function(progress){
        $elFoo.css({
            transform: 'translate3d(' + progress.tweened + 'px 0 0)'
        });
    }
});

For the sake of simplicity, the remaining examples in the documentation will use jQuery.

In depth

In the examples above, see the basic structure of the stimulate call

//call to the library
stimulate(
    //settings
    {
        from:5,
        to: 100,
        duration: 1000,
        easing: function(ratioCompleted){
            // sine-out easing function
            return Math.sin(ratioCompleted * Math.PI/2)
        },
        frame: function(progress) {
            //affect the DOM
            $elFoo.css({
                transform: 'translate3d(' + progress.tweened + 'px 0 0)'
            });
        }
    }
);

This will increment $elFoo from 5 to 100 over 1 second (1000 milliseconds). One additional setting is included here, easing. Stimulate does not come with an easing library baked in. You must write your own or include functions from compatible libraries. One such compatible library can be imported with npm install eases --save. To see an example in this repo, go to ./src/demo/demo.js.

When you call stimulate(settings);, requestAnimationFrame/setTimeout-fallback, will update a progress object and call the frame callback over and over.

Look at the progress argument in the frame callback setting...

The progress object

progress is an object that is passed into the frame callback. This is its structure:

{  
   "ratioCompleted":0.8089999999999999,
   "easedRatioCompleted":0.9553299583633394,
   "tweened":77.80999999999999,
   "easedTweened":90.97969625270055,
   "aspects":{  
      "someChild_Y":{  
         "ratioCompleted":0.8089999999999999,
         "easedRatioCompleted":0.9912251264838419,
         "tweened":307.41999999999996,
         "easedTweened":376.6655480638599,
         "aspects":{  
            "asdf":{  
               "ratioCompleted":0.7623452345,
               "easedRatioCompleted":0.7623452345,
               "tweened":0.7623452345,
               "easedTweened":0.7623452345,
               "aspects":{}
            }
         }
      }
   }
}

The properties in the progress object

progress.ratioCompleted

This is an incrementation from 0 to 1. It ignores the from, to, and easing properties. With a duration of 1000ms, exactly half way through the animation, progress.ratioCompleted will be 0.5.

progress.easedRatioCompleted

If an easing function setting was specified, progress.easedRatioCompleted is the product of applying progress.ratioCompleted value to that function. If there is no easing setting, it's value is the same as progress.ratioCompleted.

progress.tweened

This is the value between the settings.from and settings.to according to this point in time relative to the duration, without easing. So if from: 10, to: 20 and duration: 1000, after 300 milliseconds, progress.tweened is 13. After 500 milliseconds, it's 15. It's a linear progression. If no from setting is specified, this value is the same as progress.ratioCompleted.

progress.easedTweened

This is like progress.tweened, but instead of progressing linearly from from to to, it uses the easing setting to determine its position between from and to. If no easing setting exists, its value matches progress.tweened. This is the the most useful property in the progress object for typical animation applications. This is how you get bouncy movements.

progress.aspects

Stimulate lets you nest instances in an aspects setting object (more on this later). progress.aspects lets you access the progress of nested descendants. In the example below, notice how the to level frame callback accesses the progress of someChild_Y in order to calculate the position for $foo.


var $foo = $('.foo'); // make sure you cache your selectors
stimulate({
    from:10,
    to:20,
    duration: 1000,
    easing: circOut,
    frame: function(progress){
        var x = progress.easedTweened;
        var y = progress.aspects.someChild_Y.easedTweened;
        };
        $foo.css({
            transform: 'translate3d(' + x + 'px ' + y + 'px 0)'
        });
    },
    aspects: {
        someChild_Y: {
            from: -142,
            to: 345,
            duration: 642,
            easing: sineOut,
            frame: function(progress){
                // This CB is here if you need it
                // but this instance's progress
                // is accessible in its parent progress.aspects
                // see below.
            },
            aspects: {
                asdf:{
                    delay: 13
                }
            }
        }
    },
    
});

Accessibility and persistence of the progress object

As we've seen, the progress object is accessible as an argument of the frame setting handler. But it's accessible in other ways. There are other callbacks for onComplete and onStop, all of which have progress as their first argument.

Progress is also a property on the object that calling stimulate returns. Stimulate() returns an object -- an instance of the StimulateAspect class. More on this later.

Also, you should be aware that the progress object is never destroyed and recreated for updates. The progress identity persists -- only its properties get updated. To illustrate this, follow truth in the example below to see how various progress references match eachother:

var truth = true;
var mainProgress;
var aspectProgress;
var myStimulation = stimulate({
    from: 5,
    to: 10,
    duration: 1000,
    aspects: {
        foo:{
            from: 12,
            to: 423,
            duration:243,
            frame: function(progress){
                truth = truth && progress === this.progress;
                aspectProgress = progress;
            }
        }
    },
    frame: function(progress){
        truth = truth && progress === this.progress;
        truth = truth && myStimulation.progress === progress;
        truth = truth && progress.aspects.foo === aspectProgress;
        mainProgress = progress;
        
    }
});
setTimeout(function(){
    truth = truth && mainProgress === myStimulation.progress;
    console.log(truth === true); //true
},100);

StimulationAspect instances

Calling stimulate({/*settings*/}) creates an instance of a class called StimulationAspect, and returns it. That returned object can be assigned to a variable and it has methods ready to be called and properties ready to be accessed.

See how this is used in the example below, where StimulationAspect is controlled by a button, that when clicked, stops the animation, reverses it, display the current ratioCompleted in an element, waits a half a second and resumes:

var myStimulation = stimulate({
    from: 5,
    to: 100,
    duration:1000,
    easing: spring,
    frame: function(){
        $foo.css('top',this.progress.easedTweened+'px')
    };
});
var reversed = false;
$button.on('click', function(){
    myStimulation.stop().updateSettings({
        reverse: !!reversed
    });;
    $meter.text(myStimulation.progress.ratioCompleted);
    setTimeout(function(){
        myStimulation.resume();
    }, 500)
});

Notice that some methods like stop are chainable. There will be more detail on various StimulationAspect methods later.

Nesting StimulationAspects

StimulationAspect instances can have StimulationAspect child instances. In this example, this section will descript the relationship between parent and child aspects.

var myStimulation = stimulate({
    from: 5,
    to: 100,
    duration: 600,
    aspects: {
        someChild: {
            from: 43,
            to: 29,
            aspects: {
                someGrandChild:{
                    from: 3,
                    to: 99999
                }
            }
        },
        someOtherChild: {
            duration: 300,
            from: 12,
            to: 77
        }
    },
    frame: function(progress){
        console.log(progress.aspects.someChild.aspects.someGrandChild);
    }
});
the parent can access the progress objects of descendants

This can be done in several ways.

var val = myStimulation.aspects.someChild.aspects.someGrandChild.progress;

or

var val = myStimulation.progress.aspects.someChild.aspects.someGrandChild.progress;

or

var val = myStimulation.progressAt('someChild.someGrandChild');

the parent can access its StimulationAspect descendants

This can be done in a couple of ways.

var val = myStimulation.aspects.someChild.aspects.someGrandChild

or

var val = mystimulation.aspectAt('someChild.someGrandChild');

certain settings are inherited by the child

For a settings like this, if a child does not have it specified, it will assume the value of its parent. Grandchildren will have this relationship with children, and so on. So a parent's setting can potentially impact a great-great-grandchild's assumed setting. Stimulate has a .updateSettings() method. Calling this on a parent will update any children/grandchildren that are dependant on inheritence. Even if you update settings in the middle of an animation, the inheritance will take effect.

Here are the inheritable settings.

  • duration -- (default 1000)
  • skipZeroFrame -- (default true)
  • delay -- (default 0)
  • delayEveryLoop -- (default false)
  • loop -- (default false)
  • endless -- (default false)
  • reverse -- (default false)
  • usePersistedSettings -- (default false)

Stimulate has additional settings. A comprehensive list of settings will be described later.

In the example above, myStimulation.progress.ratioCompleted and myStimulation.aspects.someChild.progress.ratioCompleted will reach their respective to values at the same time, but myStimulation.aspects.someOtherChild.progress.ratioCompleted will reach its to value 300 milliseconds before that. So, myStimulation.aspects.someChild inherited duration setting from its parent myStimulation.

certain methods called on the parent get called on the child automatically

myStimulation.stop() will effectively call myStimulation.aspects.someChild.stop(), which would call myStimulation.aspects.someChild.aspects.someGrandChild.stop().

resetAll() has this behavior as well.

children can be spawned from parents

There are two ways to do this. The first is nesting child aspect settings within the parent's setting of the original call. Like this:

stimulate({
    duration:100,
    aspects: {
        someChild: {
            duration: 200
        }
    }
});

The second way to spawn a child is by calling .birthChild on a StimulationAspect. Like this:

var myStimulation = stimulate({duration: 100});
var someChildOfMyStimulation = myStimulation.birthAspect('someChild',{duration:200}');