Popmotion

Get started

Last updated one year ago

Popmotion is used to drive the motion of a user interface. It has native support for CSS, DOM attributes, SVG and SVG paths, and can be used with any API that accepts numerical values.

By the end of this introductory guide you’ll have learned about the Actor class, Popmotion’s primary interface. You’ll use one with a DOM element to animate it, simulate physics on it, and have it respond to user input.

Open the sandbox

We’ve created a sandbox on CodePen for you to follow along with this tutorial.

You can alternatively download Popmotion and use it locally.

Create an Actor

An Actor is used to track and manipulate numerical values of a single element over time.

Create one using the new ui.Actor() constructor:

var myActor = new ui.Actor({
    onUpdate: function (output) {
        console.log(output);
    }
});

Here, we’ve assign a callback that will console.log any changed values to onUpdate, which fires any time an Actor’s value updates.

Tweenieboppers

Now we have an Actor, we can make a Tween. Tweens change a number, from one value to another, over a set duration of time. Create one with ui.Tween:

var foo = new ui.Tween({
    values: {
        x: 100
    }
});

When we set a Tween value, we can either set it as an object of properties or a direct value. Setting a Tween value directly sets its to property. We can now play that Tween on our Actor:

myActor.start(fooTween);

If you open your console, you’ll see Popmotion assumes a starting value of 0 for x and tweens it to 100 using the default settings of 300 milliseconds duration and an ease of "easeOut".

Next, we’ll plug that number into the red ball to create an animation.

Making animations

The Actor constructor takes an object of properties as its first argument, or a string to select a single DOM element. The primary subject of an Actor is set to the element property.

In this example, we want to pass it the ball DOM element.

var ballActor = new ui.Actor('#ball');

Popmotion will detect that this is a DOM element and assign the CSS role to handle its values on update.

So now we just have to pass foo to the ballActor instead to create this animation:

ballActor.start(foo);

Easy! Animation is explained in greater detail in the Introduction to animation guide.

Physics

Animations are traditionally the workhorse of UI motion design because they’re controllable and predictable. But they don’t take into account an object’s current velocity and always run for a set period of time.

More dynamic interactions can be built with physics simulations. There are a number of different simulations that ship with Popmotion, but for this introduction let’s use the default, velocity.

Replace Tween with Simulate, the physics class:

var foo = new ui.Simulate({
    values: {
        x: 100
    }
});

With a Tween action, directly assigning a value sets its to property. With Simulate, we are now settings its velocity property (in units per second).

The Introduction to physics guide explores Popmotion physics much further.

User input

With the ui.Track class, you can make values watch values from an Input. You can experiment with your own custom Input classes, but Popmotion provides native support for mouse and touch.

First, let’s define our Track action. Replace the Simulate code with the following:

var foo = new ui.Track({
    values: {
        x: {}
        // equivalent to
        // x: { watch: 'x' }
    }
});

We’ve set x to an empty object because unless told to watch a specific property, Popmotion will use the name of the value itself. And as we want to watch the x movement of our mouse/touch input, we don’t need to explicitly set this.

Now we need to listen for the mousedown and touchstart events. We’re using jQuery as our event handler, but you can use a different framework or roll your own. By passing our Track action as the first argument and our event as the second argument, Popmotion will automatically start tracking our input:

$('body').on('mousedown touchstart', '#ball', function (event) {
    event.stopPropagation();
    event.preventDefault();

    ballActor.start(foo, event);
});

Mouse and touch Inputs have x, y, angle and distance properties.

As you can see, the ball will stick to your mouse even if you let go, so now we add a second event handler that listens for mouseup and touchend events and calls the Actor method stop, which will stop all currently active Actions.

$('body').on('mousedown touchstart', '#ball', function (event) {
    event.stopPropagation();
    event.preventDefault();
    
    ballActor.start(foo, event);

    $('body').one('mouseup touchend', function () {
        ballActor.stop();
    });
});

You can learn more about input tracking in our Input tracking introduction guide.

Bring it all together

Now we know enough to make a very simple interaction sequence.

We’ll animate the ball into view on load, then add an event listener that will allow the user to drag the ball around the page. When they throw it, it will use physics to spring back to its original position.

First, open this sandbox (the ball is there, it’s just hidden with opacity: 0)

Create your ball Actor as before:

var ballActor = new ui.Actor({
    element: '#ball',
    values: {
        y: 100
    }
});

We’ve add a values property to initialise the Actor with some starting values. This is useful when starting values from a number other than 0.

Intro animation

var showBall = new ui.Tween({
    values: {
        y: 0,
        opacity: 1
    }
});

ballActor.start(showBall);

Extending actions

Action properties can be overridden/extended at any time with the extend method. This returns a new instance of an action, with all its old properties and all the new ones:

ballActor.start(showBall.extend({
    duration: 500,
    ease: 'backOut'
}));

Actions can be extended infinitely, so it’s a good way to create inheritance and keep your code DRY.

Bind events

We can use the actor’s then method to chain commands. You can provide it an action, the same way you’d provide one to start, but you can also provide a callback:

ballActor
    .start(showBall.extend({
        duration: 500,
        ease: 'backOut'
    }))
    .then(function () {
        $('body').on('mousedown touchstart', ballActor.element, function (event) {
            event.stopPropagation();
            event.preventDefault();
            
            ballActor.start(trackBall, event);
            
            $('body').on('mouseup touchend', function () {
                ballActor.start(springBack);
            });
        });
    });

Here we’re passing two new actions, trackBall and springBall. Let’s define those at the top of our script:

var trackBall = new ui.Track({
    values: {
        x: {},
        y: {}
    }
});

This is the same as our previous Track definition, except this time we’ve added a y property to make the ball’s movement two-dimensional.

var springBack = new ui.Simulate({
    simulate: 'spring',
    spring: 500,
    friction: 0.2,
    values: {
        x: {
            to: 0
        },
        y: {
            to: 0
        }
    }
});

Here, we’re using a new simulation, "spring". Adjusting the spring and friction properties will affect how the simulation feels. You can also give x and y different properties to affect how each axis feels separately.

Final product

You should end up with this:

Next steps

Ready to dive in? Go deeper with our introduction to animation, physics and input tracking guides.