In the previous section we discussed the render tree, but only used static values for our node properties. All
of those properties can also take time-varying values to create rich animations. These values can come from
user input or Transitionables.
Transitionables define a value or array
of values that change over time. They are interpolated either by easing curves
or physics transitions, like springs. Transitionables like all Samsara streams emit start, update and
end events. To animate a value, call the set method with a target value, transition definition, and (optional)
callback function. We'll discuss what we mean by a transition definition below, but first an example:
var Transitionable = require('samsara/core/Transitionable');
var t = new Transitionable(0); // define a transitionable with initial value 0
t.on('start', function(value){
console.log(value) // 0
});
t.on('update', function(value){
console.log(value) // values between 0 and 100
});
t.on('end', function(value){
console.log(value) // 100
});
t.set(
100, // new value
{curve : 'easeIn', duration : 500}, // transition definition
function(){ console.log("finished"); // callback
});
Layout data of a node can read from Transitionables. For example, we can have a Transitionable fade out a Surface
with the following code:
var opacity = new Transitionable(1);
var surface = new Surface({
size : [100,100],
properties : {background : 'red'},
opacity : opacity
});
context.add(surface);
opacity.set(0, {curve : 'linear', duration : 500});
Like all Samsara streams, Transitionables have a map method, which converts its values into other types.
Calling map will return a new stream, so the same transitionable can map to several different streams.
Here we use a Transitionable to rotate a Surface and fade it out at the same time.
var t = new Transitionable(0);
var rotation = t.map(function(angle){
return Transform.rotateZ(2 * Math.PI * angle);
});
var opacity = t.map(function(x){
return 1 - x / 2;
});
var surface = new Surface({
content : 'click me',
size : [100, 100],
origin : [.5,.5],
properties : {background : 'red'}
});
surface.on('click', function(){
t.set(1, {curve : 'easeInOut', duration : 700});
});
var context = new Context();
context
.add({
transform : rotation,
opacity : opacity,
align : [.5,.5]
})
.add(surface);
See the Pen transitionable-map by SamsaraJS (@samsaraJS) on CodePen.
By default, Transitionables come with 9 predefined easing curves:
linear (default)easeIneaseOuteaseInOuteaseOutBounceeaseInCubiceaseOutCubiceaseInOutCubiceaseOutWallThey are used with a transition definition, as in the examples above, which is a JSON object with the following properties:
| key | default | description |
|---|---|---|
| duration | 500 | The time in milliseconds to complete the transition |
| curve | 'linear' | The easing curve name |
Though Samsara only includes 9 easing curves by default, any easing curve can be added by providing your own easing function. This function should be defined on the domain [0, 1] and map to the range [0,1]. You can map to values beyond the range [0,1] which will correspond to an undershoot (if less than 0) or overshoot (if greater than 1).
var myCustomFunction = function(t){ return Math.pow(t, 5); }
var myTransitionable.set(100, {curve : myCustomFunction, duration : 500});
Or give your function a name and register it with the Transitionable constructor. You can then define it once
and use it from anywhere in your application.
Transitionable.registerCurve("myCustomCurve", myCustomFunction);
var myTransitionable.set(100, {curve : "myCustomCurve", duration : 500});
Physical curves can also be used to transition values. They lack the precise timing of easing curves, but whereas easing curves come in a discrete number, physics curves are continuously parametrized. They can also take into account the velocity of a previous motion, such as a gesture, to create more natural transitions. Currently, the physics curves that Samsara comes with are springs and inertia.
The API for a physics curves is exactly like an easing curve except the transition definition is different.
| key | default | Range | description |
|---|---|---|---|
| period | 100 | [0, ∞] | The time it takes for one oscillation without any damping applied |
| damping | 0.5 | [0, 1] | If 0 the spring will oscillate forever, if 1 the spring will never oscillate |
| velocity | 0 | [-∞, ∞] | The initial velocity of the transition in milliseconds/pixel |
For example:
myTransitionable.set(1, {curve : 'spring', damping : .5, period : 100, velocity : -.1});
Here's the same example as above, but with a physics transition instead of an easing curve:
See the Pen transitionable-map-physics by SamsaraJS (@samsaraJS) on CodePen.
| key | default | Range | description |
|---|---|---|---|
| drag | 0.1 | [0, 1] | The ratio the velocity will decrease by each tick |
| velocity | 0 | [-∞, ∞] | The initial velocity of the transition in milliseconds/pixel |
For example:
myTransitionable.set(1, {curve : 'inertia', drag : .5, velocity : -.1});