Animating Mark Entry and Exit

Counterpoint’s stage concept allows you to choreograph the animated entry and exit of marks from a render group.

For example, in the demo below, click around the canvas to create points, then click some existing points to remove them. You should see that the number of points in the render group changes instantly when you remove a point. However, the number of points in the stage (i.e., points that are being rendered) changes only after the fade-out animation completes. That’s because the stage keeps track of marks that are being animated in and out separately from those that are part of the render group. This allows you to reuse marks that have are exiting, and to separately manage the specified and momentary states of the visualization.

Points in render group: 0. Points in stage: 0.

Configuring Staging Behavior

You define how the stage should animate the entry and exit of marks using a simple configuration method. For example, to animate the alpha and radius of a point when it is added to the render group, we might configure our render group as follows:

renderGroup.configureStaging({
    initialize: (mark) => mark.setAttr('alpha', 0.0).setAttr('radius', 0.0),
    enter: async (mark) => await (mark
        .animate('alpha', 1.0)
        .animate('radius', 20.0)
        .wait(['alpha', 'radius'])),
    exit: async (mark) => await (mark
        .animate('alpha', 0.0)
        .animate('radius', 0.0)
        .wait(['alpha', 'radius'])),
});

Here, we used three callback functions to specify the animation behavior:

  • initialize: Static changes to the mark before it enters, i.e. setting it up for an entrance animation.
  • enter: Animations to the mark to enter it onscreen. Note that this function should return a Promise that resolves when the animation completes, which we can easily provide using the wait function.
  • exit: Animations to the mark to remove it from the screen. Similar to enter, this function should return a Promise that resolves when the animation completes.

WARNING

If your enter and exit functions don’t return Promises, the stage can exhibit unintended behavior. Be sure to call wait() after any animation calls.

Adding and Removing Marks

Once your staging behavior is configured, you can simply add and remove marks from the render group. The entry and exit animations will automatically be applied.

There are two ways to add and delete marks. The first way requires that you have a reference to the Mark you wish to add or remove:

let mark = ...
// Add the mark
renderGroup.addMark(mark);
// Delete the mark
renderGroup.deleteMark(mark);

This method makes no assumptions about mark IDs and is purely imperative, so you can freely add or remove marks with the same ID.

In cases where you know that the mark IDs will be unique, you can use convenience methods to add and remove marks just based on their IDs. To delete a mark by its ID, simply call:

renderGroup.delete(id);

To add a mark by its ID, the render group needs to know how to construct a new mark based on the ID you give it. To do so, we can initialize the render group using a factory function:

renderGroup = new MarkRenderGroup((id) => new Mark(id, {
    x: ...,
    y: ...,
    color: 'green',
    alpha: 0
}));

Now, when we want to add a mark later, we can simply use the add method:

renderGroup.add(id);

What if we want to customize the mark after adding it? We can just get the mark and update it:

(renderGroup.add(id).get(id)
 .setAttr('x', 200)
 .setAttr('y', 100));

results matching ""

    No results matching ""