The most basic conceptual pieces involved in using Concert.js are the Sequence
and the Transformation
.
A Transformation
is a single change applied over time to a single feature of a single object.
For instance, it could be a change in the height of a DOM element from 0px to 24px over 1000ms (1 second).
A Sequence
is a whole series of changes applied to a collection of objects over time. It contains one or more transformations.
Anything we do with Concert.js will involve creating and working with a Sequence
object.
let sequence = new Concert.Sequence();
sequence.addTransformations(
{
target: document.getElementById("HelloDiv"),
feature: "height",
unit: "px",
applicator: Concert.Applicators.Style,
keyframes: { times: [0, 2000], values: [0, 24] }
});
document.getElementById("GoButton").onclick =
function () { sequence.begin(); };
The above code does the following:
Concert.Sequence
object.addTransformations()
method.begin()
method to run the animated sequence.
We'll see later that there are several ways to run an animation or synchronize it to something else happening on the page,
but begin()
is the simplest-- it just runs the sequence from beginning to end, synchronized to the system clock.
Let's look at each of the properties of the object that defines a single animated feature (a Transformation
).
Later we'll see that it is not always necessary to specify every one of these properties, because we can set default values for them.
div
with id
"HelloDiv" which we want to slowly reveal.height
property (of the above-mentioned div
object).null
for animating things which don't require an appended unit string.times
, which is a list of moments in a timeline (here measured in milliseconds),
and values
, a corresponding list of values to apply at those times. Intermediate values will be calculated
automatically between those keyframes.
div
with id
"HelloDiv", and modify its height style,
setting it to 0px at time 0, and smoothly animating it until it reaches 24px at time 2000 (that is, at 2 seconds).
If you click the "Go" button, you'll see it work.
let sequence = new Concert.Sequence(),
box1 = document.getElementById("Box1");
let box1Transformations =
[
{
target: box1,
feature: "left",
unit: "px",
applicator: Concert.Applicators.Style,
keyframes: { times: [0, 1000], values: [0, 265] }
},
{
target: box1,
feature: "top",
unit: "px",
applicator: Concert.Applicators.Style,
keyframes: { times: [0, 1000], values: [0, 65] }
}
];
sequence.addTransformations(box1Transformations);
document.getElementById("GoButton").onclick =
function () { sequence.begin(); };
As can be seen above, addTransformations()
can take an entire array of transformation objects.
Here we transform two different features (the top and left style properties of the target object),
each one having its own independent movement.
Note that the same could have been accomplished by calling addTransformations()
once with each transformation object.
Shortcut: Multiple target features can be specified together in an array. This requires passing arrays of values as well. For instance, the below code would work exactly the same as the above, but is much more concise. Note that the value at time zero (as well as at time 1000) is an array. The first value is paired with the first feature ("left"), and the second value is paired with the second feature ("top").
let sequence = new Concert.Sequence(),
box1 = document.getElementById("Box1");
let box1Transformations =
{
target: box1,
feature: ["left", "top"],
unit: "px",
applicator: Concert.Applicators.Style,
keyframes: { times: [0, 1000], values: [[0, 0], [265, 65]] }
};
sequence.addTransformations(box1Transformations);
document.getElementById("GoButton").onclick =
function () { sequence.begin(); };
Normally, values are interpolated in between keyframes. If the goal instead is to have one animation segment, then a period of time with no animation, then another animation segment, that is possible as well. Watch how the two boxes behave differently:
let sequence = new Concert.Sequence(),
box1 = document.getElementById("Box1"),
box2 = document.getElementById("Box2");
let boxTransformations =
[
{
target: box1,
feature: "left",
unit: "px",
applicator: Concert.Applicators.Style,
keyframes:
{
times: [0, 750, 1500, 2250],
values: [0, 90, 180, 270]
}
},
{
target: box2,
feature: "left",
unit: "px",
applicator: Concert.Applicators.Style,
keyframes:
{
times: [0, 750, null, 1500, 2250],
values: [0, 90, null, 180, 270]
}
}
];
sequence.addTransformations(boxTransformations);
document.getElementById("GoButton").onclick =
function () { sequence.begin(); };
Inserting a null in between keyframes has the effect of breaking the animation into separate segments.
In the example above, the first box has continuous value interpolation between every keyframe and the one after it.
The second box is animated continuously from time 0 to time 750, but then no new values are calculated and applied
until hitting time 1500, at which point another animation segment begins.
Do note that the times
and values
arrays do need to be the same length,
and any nulls in one need to match nulls in the other.
If you want a lot of discontinous segments, however, you may wish to look at an alternate method of describing transformations:
For some purposes, it is more useful to think of animations as a series of segments, rather than as a continuous series of keyframes and corresponding values.
let sequence = new Concert.Sequence(),
box1 = document.getElementById("Box1");
let boxTransformations =
[
{
target: box1,
feature: "left",
unit: "px",
applicator: Concert.Applicators.Style,
segments:
[
{ t0: 0, t1: 1000, v0: 0, v1: 270 },
{ t0: 3000, t1: 4000, v0: 270, v1: 0 }
]
},
{
target: box1,
feature: "top",
unit: "px",
applicator: Concert.Applicators.Style,
keyframes: { times: [1000, 2000, 3000], values: [0, 70, 0] }
}
];
sequence.addTransformations(boxTransformations);
document.getElementById("GoButton").onclick =
function () { sequence.begin(); };
The first object in the boxTransformations
array above uses a new notation.
Instead of using keyframes
, it specifies segments
.
Each segment object in the array represents one single start and end point of animation.
It contains two properties corresponding to start and end times, t0
and t1
, respectively;
and it contains two properties corresponding to start and end values, v0
and v1
, respectively.
As demonstrated above, it is perfectly fine to mix segment notation and keyframe notation in the same sequence.
One handy feature of segment notation is that various additional properties can be specified at the segment level, allowing each segment to have different characteristics, such as using different easing functions. (Easing functions are covered in the next step of this tutorial.)
Whether you use keyframe or segment notation makes no difference in how the animation runs. Choosing one or the other is mainly a function of which one is easier for the task at hand. This choice can be especially useful when programmatically generating animations, where objects and arrays are being assembled by code, and one of these notations may fit better than the other for any given application.
Note: Whether using keyframe or segment notation, do not try to add overlapping segments applying to the same object feature. For instance, consider an animation in which you've added a segment causing an element's "left" property to increase from 0 to 100 between time 0 and time 1000. Adding a second segment (whether by keyframe or segment notation) that differently adjusts the same element's "left" property between, say, time 500 and time 1500 does not make sense and results in undefined behavior.
Worth knowing: When adding transformations to a sequence, you can also add several modifiers to the way the values are calculated. For instance, the value applied in any given frame can be modified by a multipier, a modulo factor, rounded, or adjusted by a constant offset amount. For details, see the full Concert.js documentation pages.
Reference documentation links for items covered in this step of the tutorial: