Chapter 13. Mo.js

mo.js is a JavaScript library devoted to motion for the web. It offers a declarative syntax for motion and the creation of elements for animation. Even though mo.js is still in beta, there are already a host of amazing features to play with. Its author, Oleg Solomka (otherwise known as LegoMushroom), creates incredibly impressive demos and tutorials for the library’s offerings that you should check out, but in this article we’ll run through a really quick overview of features and tutorials to get you started.

Base Premises

mo.js basically offers two ways to make something that moves. You can do what other libraries do and reach inside the DOM or SVG DOM and move it, or you can create a special mo.js object, which has some unique offerings. There are fundamental things available to both ways of working, such as custom path easing and timelines. The path easing and timelines also have pretty impressive working tools to make them easier to adjust while you’re working.

Shapes

Depending on what you’re animating, the shapes and other objects that mo.js allows you to make might simplify your workflow. mo.js offers a declarative syntax that makes it very easy to create something on the fly.

Figure 13-1 and its corresponding example are basic demos of the syntax.

Figure 13-1. A simple shape with a lot of nice details, including absolute centering

The code for this example is as follows:

var shape = new mojs.Shape({
  shape:        'circle',  // shape "circle" is default
  radius:       50,        
  fill:         '#A8CABA',  // default is pink
  stroke:       '#5D4157', 
  strokeWidth:  3,         
  isShowStart:  true,      // show before any animation starts
});

Here are some of the base things you need to know:

  • The shapes that are available to you include circlerectcrossequalzigzag, and polygon. (Default is  circle).
  • You define a fill, a stroke, and a strokeWidth. (Default is fill with no stroke or strokeWidthequal and cross don’t have space to fill, so they will not appear unless a stroke is specified.)
  • You define a radius for the shape, and adjust it on an axis with an additional radiusX or radiusY. (Default is 50.)
  • You let it know if you want to show the shape with isShowStart. This is a Boolean—true or false. true allows you to see it even if you’re not going to animate the shape. (Default is false.)
  • polygonzigzag., and equal allow you to pick a number of points so that you can create different types of shapes. (Default is 3.)
  • All shapes will be placed relative to the middle of the screen using absolute positioning, unless you specify top, left, etc.

Figure 13-2 and its corresponding example demonstrate some of the shapes you can create.

Figure 13-2. Some of the shapes that are available to you out of the box in mo.js

The code that creates the first shape (top left) is as follows:

const zigzag = new mojs.Shape({
  shape:        'zigzag',
  points:       7,
  radius:       25,
  radiusY:      50,
  top:          pos.row1,
  left:         pos.col1,
  fill:         'none',
  stroke:       color1,
  isShowStart:   true,
});

You might notice if you look into the DOM that these are SVG shapes placed inside of a div for positioning. You can also pass a parent, like parent: '#id-to-be-placed-under', if you’d like to put the shape somewhere within the DOM. You can pass any DOM node as a parent, so parent: someEl would work as well. At some point, you’ll also be able to choose between using a div or SVG, which will be awesome, because it makes it much easier to create a scaling animation for mobile if you can place it with an SVG viewBox.

You can create custom shapes to animate as well, and add them in as the shape object:

// custom shape
class OneNote extends mojs.CustomShape {
  getShape () { return '<path d="M18.709
	...
"/>';
 }
}
mojs.addShape( 'oneNote', OneNote ); 

const note1 = new mojs.ShapeSwirl({
  shape:    'oneNote',
  ...
});

Shape Motion

To create an animation with a shape in mo.js, you pass in an object, with the key and value expressing what you’d like to tween from and to: { fromvalue : tovalue }. Also worth mentioning is that in ES5 environments, this means you can’t easily use a value stored in a variable as your “from” value.  This would look like options = {scale: {} }; options.scale[from]=to;. In ES6, it’s easier because you can use variables for key values when you declare object literals.

We can use transform properties like scale, angle (known in CSS as rotate), and opacity, and we can interpolate colors as well, as shown here with fill and in Figure 13-3:

scale:        { 0 : 1.5 },
angle:        { 0 : 180 },
fill:         { '#721e5f' : '#a5efce' },
Figure 13-3. Shape tweens pass in an object to interpolate between two states

We can also specify a few other parameters:

  • duration
  • delay
  • repeat
  • speed1 is the default speed, so 0.5 would be half speed and 1.5 would be 1.5× faster
  • isYoyo—whether or not it tweens back and forth
  • easing—written as an object, like ease.in, ease.out, or ease.inout
  • backwardEasing—when using isYoyo: true, if you want the way that it eases back (on the backswing of the yoyo) to be different from the way that it eases forward, you would specify that with this method (defaults to easing if not specified)
  • isSoftHide—whether it hides the shape with transforms rather than display (Boolean, defaults to true)

Easy Random Generation

In other JavaScript libraries, we’d either write a helper function to use random values, or use a bit of code that allows us to pick the ceiling and floor values to choose between. (See the note in the opening section of Chapter 11 for more details on Math.random().)

In mo.js, we can abstract this away very nicely. We can pass in random values by writing the string property : 'rand(min, max)'; for instance, angle: 'rand(0, 360)'.

Chaining

If we’d like to chain two animations on a Shape, we can call .then() on the initial tween, like so:

const polygon = new mojs.Shape({
  shape:        'polygon',
  points:       5,
  stroke:       '#A8CABA',
  scale:        { 0 : 1.5 },
  angle:        { 0 : 180 },
  fill:         { '#721e5f' : '#a5efce' },
  radius:       25,
  duration:     1200,
  easing:       'sin.out'
}).then ({
  stroke:       '#000',
  angle:        [-360],
  scale:        0,
  easing:       'sin.in'
});

Swirls

Features like ShapeSwirl and Burst are interesting parts of mo.js; they’re pretty beautiful out of the box. A ShapeSwirl is similar to a regular Shape object, but the movement is pretty much how it sounds—the shape swirls around. You have a few parameters to work with for a swirl, and they are all based on the sine that the swirl works with:

  • swirlSize—the amount it swirls horizontally (the deviation or amplitude of the sine)
  • swirlFrequency—the frequency of the sine
  • pathScale—the scale (length) of the sine
  • degreeShift—the angle of the sine, used if you’d like to make it move toward a different direction on a 360 degree plane (especially useful for using ShapeSwirl with a Burst)
  • direction—the direction of the sine, either -1 or 1 (good for setting something in the other direction if you want it to look a little random)
  • isSwirl—whether the shape should follow a sinusoidal path (Boolean—true or false)

These can be a little confusing to read and grok, so I’ve made a demo so you can play with the values to understand them a little better. Figure 13-4 is a screenshot of the demo.

Figure 13-4. Playing with the controls in this demo shows how all of the built-in options for a mo.js ShapeSwirl work

Also, you can set up a few base configurations—like a custom shape, or an object, with some configuration—to reuse for different ShapeSwirls (or any other shape) with an ES6 spread operator like this (Figure 13-5). This is really nice if you have a few similar objects:

const note_opts_two = {
  shape:    'twoNote',
  scale:    { 5 : 20 },
  y:        { 20: -10 },
  duration:  3000,
  easing: 'sin.out'
};

const note1 = new mojs.ShapeSwirl({
  ...note_opts_two,
  fill:     { 'cyan' : color2 },
  swirlSize:      15,
  swirlFrequency: 20
}).then({
  opacity:  0,
  duration: 200,
  easing: 'sin.in'
});
Figure 13-5. This demo shows ShapeSwirl used for the motion of the notes

Burst

A Burst is also really quite lovely out of the box. If you use the default configuration, you would say this:

const burst = new mojs.Burst().play();

To configure a Burst, you have a few options, as shown in Figure 13-6:

  • count—the number of children in the Burst (default is 5)
  • degree—the number of degrees around the center that the children come from
  • radius—the radius that the children spread out to (radiusX and radiusY apply here as well)
  • isSoftHide—whether it hides the children with transforms rather than display (Boolean, defaults to true); this applies to all shapes, but I bring it up again because it’s particularly useful for a Burst with several children
Figure 13-6. You can customize quite a lot in a Burst: size, color, even custom shapes

All of the same rules for Shape also apply to Burst, and we can apply them to the nodes themselves as a separate object using children, like this:

const burst = new mojs.Burst({ 
  radius: { 0: 100 },
  count: 12,
  children: {
    shape: 'polygon',
    ...
  }
});

Timeline

In a Timeline, you can either .add a bunch of objects or tweens that you have previously declared as a variable, or you can .append them, and have them fall in order:

const timeline = new mojs.Timeline({
  .add( tween )
  .append( tween ) 
});

.add: allows you to add any objects or shapes to the timeline. They’ll all fire at once, but you can still use delays or staggering to adjust their timing. .append: adds objects, but staggers them in the order they are added.

There are a few things you can do in a Timeline that are worth noting. You can add repeatdelay, and speed, just like to the objects themselves, as object parameters, like this:

new mojs.Timeline({
  repeat: 3,
  isYoyo: true
});

You can also nest a Timeline inside another Timeline; you can even nest them infinitely:

const subTimeline = new mojs.Timeline();

const master = new mojs.Timeline()
.add( subTimeline );

Tween

With all of these constructors, we haven’t really spoken too much about tweening (animating) what already exists. All of the same parameters for shape motion (duration, repeat, easing, etc.) are also available for Tweens. To use a Tween, we update styles or attributes (whatever we’re trying to change) along a path. Here’s a simple example:

var thingtoselect = document.querySelector('#thingtoselect');
new mojs.Tween({
  duration:    2000,
  onUpdate: function (progress) {
    square.style.transform = 'translateY(' + 200*progress + 'px)';
  }
}).play();

I used this kind of tween to create the effect on the far left side of the example in Figure 13-7, of the little zigzags drawing themselves on repeatedly using the SVG line drawing trick with stroke-dashoffset. I’m also using the path easing available in mo.js, which is discussed in the next section. I made the water in the tanks appear to move by updating SVG path attributes as well.

Figure 13-7. Raygun with mo.js swirls and path easing

Here’s the code for the laser beam:

new mojs.Tween({
  repeat:   999,
  duration: 2000,
  isYoyo: true,
  isShowEnd: false,
  onUpdate: function (progress) {
    var laser1EProgress = laser1E(progress);
    for (var i = 0; i < allSideL.length; i++) {
      allSideL[i].style.strokeDashoffset = 20*laser1EProgress + '%';
      allSideL[i].style.opacity = Math.abs(0.8*laser1EProgress);
    }
  }
}).play();

Tweens have rich callbacks available that take into account fine-tuning that can sometimes make all the difference. Some examples of this are onStart versus onRepeatStart, onComplete versus onRepeatComplete, and onPlaybackStart versus onPlaybackPause. A full list is available in the docs.

Path Easing

A very cool feature of mo.js is that aside from the other built-in easing values, you can also pass in an SVG path as an easing value. I use this feature in several demos in this chapter, but to be honest, I could never do path easing justice like the gorgeous tutorial LegoMushroom has prepared. I’ll simply explain the base premises to get you started and show you how it works, but I highly recommend going through his post.

Before we go through all of path easing, it’s important to establish that if you’d like to work with something out of the box, the base functions will get you very far. The syntax for built-in easing is written like so:

easing: 'cubic.in'

Mastering easing can be the key ingredient to bringing your animations to life, so being able to fine-tune your motion with custom paths is helpful. If you’re comfortable animating in CSS, you might like the mo.js bezier easing, which accepts the same curve values (without some of the same restrictions), as CSS’s cubic-bezier. Here’s an example of this kind of easing:

easing: 'bezier( 0.910, 0.000, 0.110, 1.005 )'

If you’d like more refined control than what bezier easing allows, path easing is really nice. You pass in an SVG path, and your shape is updated to work with it. Let’s look back again at the example I pulled out earlier from the raygun. I used path easing to interpolate the values as it updated:

const laser1E = mojs.easing.path('M0,400S58,111.1,80.5,175.1s43,286.4,
                63,110.4,46.3-214.8,70.8-71.8S264.5,369,285,225.5s16.6-209.7,
                35.1-118.2S349.5,258.5,357,210,400,0,400,0');

new mojs.Tween({
  repeat:   999,
  duration: 2000,
  isYoyo: true,
  isShowEnd: false,
  onUpdate: function (progress) {
    var laser1EProgress = laser1E(progress);
    for (var i = 0; i < allSideL.length; i++) {
      allSideL[i].style.strokeDashoffset = 20*laser1EProgress + '%';
      allSideL[i].style.opacity = Math.abs(0.8*laser1EProgress);
    }
  }
}).play();

To really get a sense of how a path ease can affect the movement and behavior of an animation, check out the CodePen demo in the next section. The curve editor tool that mo.js offers will help you to visualize and immediately get a sense of how an ease can refine what you create.

Mo.js Tools

One of the most impressive things about mo.js is the tooling. To whet your palate, there is a demonstration on Vimeo; see Figure 13-8.

Figure 13-8. Mo.js workflow demo on Vimeo

I made a quick pen to showcase both the player tool and the curve editor so that you can play around with them (Figure 13-9). Feel free to either fork it or just adjust it live in CodePen; it’s fun to try out. The curve tool is on the left side and the timeline is tucked at the bottom with a little arrow.

Figure 13-9. This is a starter pen you can easily fork to play around with mo.js tooling

Here’s the coolest part: LegoMushroom isn’t done. He’s working on new tooling for the timeline now. Check out the awesome design for this tool on GitHub. If you’re interested in contributing to open source, here’s an opportunity to dig in and help make a really useful tool—click on the “help wanted” label on the right!

The other finished tools are available in their own repos:

There is also a Slack channel you can join if you’re interested in contributing or learning.

If you’re the kind of person who likes playing with things more than reading, all of the CodePen demos from this chapter are available in a collection.

There are, of course, things in the library that I didn’t cover in this chapter. I went over some of the most useful features of mo.js in my opinion, but there are more things to discover. Check out the docs for more information.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.19.75.133