Modifiers are used to change the way renderables get drawn to the screen. They are one of the two primary types of render nodes.
Modifiers affect all nodes that occur below them in the render tree — including other modifiers. Modifiers can be chained to produce cumulative affects. Used in conjunction with transforms, they can produce complex animations. Famo.us also comes packaged with several specialized modifiers that you can use out-of-the-box.
To use a modifier, we instantiate a new Modifier
object, give it values for all the properties we wish to modify, and then add the modifier to the render tree above all of the elements we want it to affect.
// Just a simple example
var modifier = new Modifier();
context.add(modifier).add(renderable);
In the snippet above, the modifier
object would affect the way the renderable
object is drawn.
Modifiers can be used to control the following properties:
There are three general ways to use modifiers:
Transitionable
and/or TransitionableTransform
That is, we can use modifiers to set the property values directly, give the modifier a set of functions which will return values for those properties, or use Transitionable
and/or TransitionableTransform
to do some of the work for us.
We can can give fixed values to the modifier via its constructor options. For example:
var modifier = new Modifier({
size: [100, 200],
proportions: [.5, .75],
opacity: 0.89,
align: [0.5, 0.5],
origin: [0.5, 0.5],
transform: Transform.translate(100, 200)
});
In this way, we modify the applicable renderables once. The fixed-values approach works best when we don’t need to change the way renderables are drawn over time.
// @famous-block
// @famous-block-option preset famous-0.3.0-globals
// @famous-block-group modifier1
var Engine = famous.core.Engine;
var Modifier = famous.core.Modifier;
var Transform = famous.core.Transform;
var Surface = famous.core.Surface;
var context = Engine.createContext();
var surface = new Surface();
var modifier = new Modifier({
size: [100, 100],
opacity: 0.8,
align: [0, 0],
origin: [0, 0],
transform: Transform.translate(100, 150)
});
context.add(modifier).add(surface);
// @famous-block
// @famous-block-option preset famous-0.3.0-globals
// @famous-block-group modifier1
// @famous-block-filename main.css
.famous-container {
outline: 1px dashed white;
margin: 1px;
padding: 1px;
background-color: #111;
}
.famous-container::after {
font-family: monospace;
content: "context (.famous-container)";
}
.famous-surface {
outline: 1px solid white;
margin: 1px;
padding: 1px;
background-color: #222;
}
.famous-surface::after {
font-family: monospace;
content: "surface (.famous-surface)";
}
We can also pass functions to the modifier instance. The function will return values for each property on every single engine tick. For example:
modifier.sizeFrom(function() { return [100, 200]; });
modifier.proportionsFrom(function() { return [.5, .75]; });
modifier.opacityFrom(function() { return 0.89; });
modifier.alignFrom(function() { return [0.5, 0.5]; });
modifier.originFrom(function() { return [0.5, 0.5]; });
modifier.transformFrom(function() { return Transform.identity; });
In this way, we modify the applicable renderables many times over time.
Functions work best when we want to change the way a renderable gets drawn over time. In this example, we set the modifier’s transform
property using a function instead of a fixed value. We use the function to adjust the values of the transform dynamically over time, giving us an animation!
// @famous-block
// @famous-block-option preset famous-0.3.0-globals
// @famous-block-group modifier2
var Engine = famous.core.Engine;
var Modifier = famous.core.Modifier;
var Transform = famous.core.Transform;
var Surface = famous.core.Surface;
var context = Engine.createContext();
var surface = new Surface();
var modifier = new Modifier({
size: [200, 200],
opacity: 0.8,
align: [0.5, 0.5],
origin: [0.5, 0.5]
});
// Adjust the transform using the engine tick #
var counter = 0;
modifier.transformFrom(function() {
var scaleX = Math.sin(counter++ / 20);
var scaleY = Math.cos(counter / 40);
return Transform.scale(scaleX, scaleY);
});
context.add(modifier).add(surface);
// @famous-block
// @famous-block-option preset famous-0.3.0-globals
// @famous-block-group modifier2
// @famous-block-filename main.css
.famous-container {
outline: 1px dashed white;
margin: 1px;
padding: 1px;
background-color: #111;
}
.famous-container::after {
font-family: monospace;
content: "context (.famous-container)";
}
.famous-surface {
outline: 1px solid white;
margin: 1px;
padding: 1px;
background-color: #222;
}
.famous-surface::after {
font-family: monospace;
content: "surface (.famous-surface)";
}
Another approach, similar to the dynamic-values technique, but a bit more lightweight, involves modules called Transitionable
and TransitionableTransform
. These tweening objects can be supplied directly as property values to the modifier’s constructor.
size
, align
, origin
, and opacity
.transform
property.// @famous-block
// @famous-block-option preset famous-0.3.0-globals
// @famous-block-group modifier3
var Engine = famous.core.Engine;
var Modifier = famous.core.Modifier;
var Transitionable = famous.transitions.Transitionable;
var TransitionableTransform = famous.transitions.TransitionableTransform;
var Surface = famous.core.Surface;
var context = Engine.createContext();
var surface = new Surface();
var sizeTransitionable = new Transitionable([100, 100]);
var transitionableTransform = new TransitionableTransform();
var modifier = new Modifier({
size: sizeTransitionable,
transform: transitionableTransform
});
sizeTransitionable.set([200, 200], {duration: 1000});
transitionableTransform.setTranslate([100, 100, 0]);
context.add(modifier).add(surface);
// @famous-block
// @famous-block-option preset famous-0.3.0-globals
// @famous-block-group modifier3
// @famous-block-filename main.css
.famous-container {
outline: 1px dashed white;
margin: 1px;
padding: 1px;
background-color: #111;
}
.famous-container::after {
font-family: monospace;
content: "context (.famous-container)";
}
.famous-surface {
outline: 1px solid white;
margin: 1px;
padding: 1px;
background-color: #222;
}
.famous-surface::after {
font-family: monospace;
content: "surface (.famous-surface)";
}
Famo.us comes packaged with several specialized modifiers you can use to produce effects out of the box:
Let’s take a look at each of these in more depth:
The StateModifier
class is a convenient variant of the standard Modifier
. It’s most useful when creating animations using the dynamic values strategy outlined above. Rather than manually creating functions to handle transitions from state to state, you can use a StateModifier
to encapsulate this setup. This makes it easy to produce animations via tweening. Here’s a small snippet to illustrate the difference from a normal modifier:
// @famous-block
// @famous-block-option preset famous-0.3.0-globals
// @famous-block-group statemodifier
// @famous-block-option textPanelActive true
var Engine = famous.core.Engine;
var Surface = famous.core.Surface;
var StateModifier = famous.modifiers.StateModifier;
var context = Engine.createContext();
var surface = new Surface({
properties: {
backgroundColor: '#fa5c4f'
}
});
var stateModifier = new StateModifier({
size: [100, 100] // Initial size
});
function expand() {
stateModifier.setSize(
[300, 300],
{ duration: 1000 },
contract
);
}
function contract() {
stateModifier.setSize(
[100, 100],
{ duration: 1000 },
expand
);
}
expand();
context.add(stateModifier).add(surface);
// @famous-block
// @famous-block-option preset famous-0.3.0-globals
// @famous-block-group statemodifier
// @famous-block-filename en.md
# From State to State
Thanks to their convenience, StateModifiers are one of the most commonly used type of modifiers.
Here, we illustrate how a single StateModifier encapsulates the change from one state to another with a transition.
When we call the `.setSize()` method, we supply three arguments: the value to set, the transition to use when changing the value, and a callback function to run when the state change is complete.
The StateModifier provides the following methods to transition from one state to another.
stateModifier.setSize(value, transition, callback);
stateModifier.setOrigin(value, transition, callback);
stateModifier.setAlign(value, transition, callback);
stateModifier.setOpacity(value, transition, callback);
stateModifier.setProportions(value, transition, callback);
stateModifier.setTransform(value, transition, callback);
As you know, modifiers can be chained — but setting up large chains of modifiers manually can be verbose. The ModifierChain
module provides a convenient way to set up chains of modifiers, keeping your code clean and concise:
// @famous-block
// @famous-block-option preset famous-0.3.0-globals
// @famous-block-group modifierchain
var Engine = famous.core.Engine;
var Surface = famous.core.Surface;
var Modifier = famous.core.Modifier;
var ModifierChain = famous.modifiers.ModifierChain;
var Transform = famous.core.Transform;
var context = Engine.createContext();
var surface = new Surface({
size: [200, 200],
content: 'I get modified multiple times',
properties: {
backgroundColor: '#fa5c4f'
}
});
var chain = new ModifierChain(
new Modifier({ transform: Transform.translate(100, 100) })
);
chain.addModifier(
new Modifier({ opacity: 0.8 })
);
context.add(chain).add(surface);
The Draggable
module, as you might guess, is a special modifier that allows renderables to be dragged around the screen using mouse and touch input. Here’s how we would set up a single surface to be draggable:
// @famous-block
// @famous-block-option preset famous-0.3.0-globals
var Engine = famous.core.Engine;
var Surface = famous.core.Surface;
var Draggable = famous.modifiers.Draggable;
var context = Engine.createContext();
var surface = new Surface({
size: [200, 200],
content: 'Drag me around',
properties: {
backgroundColor: '#fa5c4f'
}
});
var draggable = new Draggable();
// Important! We have to pipe events from
// the surface to the draggable modifier
// so it has a source of input.
surface.pipe(draggable);
context.add(draggable).add(surface);
Copyright © 2013-2015 Famous Industries, Inc.