Changing positions of things during animation - famo.us

If I have a transform translate thats animating something vertically between say 0 -> 1000, over 5 seconds, is there a way if I have click events to change the horizontal position without interrupting the vertical animation?
p.setTransform(Transform.translate(20,1000,0),
{
duration: 5000,
curve: 'linear'
});
then on some random click, I want to start changing the x position over 100ms or so, and it might have multiple of these before the 5000 duration has finished
I can play with the origin and animate that independently, but I really need pixel control. So is there a way to animate just the x?
If I add a setTransform, it chains after the first one rather than concurrently.

Yes, you most certainly can achieve this. You will want to use a ModifierChain. ModifierChain allows you to chain your modifiers together and control the transforms independently. Check out this example..
Hope this helps!
var Engine = require('famous/core/Engine');
var Surface = require('famous/core/Surface');
var StateModifier = require('famous/modifiers/StateModifier');
var Transform = require('famous/core/Transform');
var ModifierChain = require('famous/modifiers/ModifierChain');
var Easing = require('famous/transitions/Easing');
var context = Engine.createContext();
var surface = new Surface({
size:[200,200],
properties: { backgroundColor:'green' }
});
surface.yState = new StateModifier();
surface.xState = new StateModifier();
var x = 0;
var y = context.getSize()[1] - 200;
surface.on('click',function(){
x += 100;
surface.xState.halt();
surface.xState.setTransform(Transform.translate(x,0,0),{duration:200});
});
surface.chain = new ModifierChain();
surface.chain.addModifier(surface.yState);
surface.chain.addModifier(surface.xState);
context.add(surface.chain).add(surface);
surface.yState.setTransform(Transform.translate(0,y,0), {duration:5000});

Related

Scrollview Pull to Refresh Famo.us

I'm trying to implement a pull to reload/refresh type effect, that you see in lots of apps, with scrollview. I've got multiple pain points on this.
Just stopping a scrollview past where it wants to sit is a pain. Setting velocity or speed limit doesn't work, just setting the position just makes it bounce because it wants to continue back.
Then setting up an event call that doesn't just fire when they scroll too hard up while doing their normal browsing.
If anyone has thoughts on how to accomplish this I'd appreciate it. If I don't get any responses in a day I'll start posting my specific attempts and where they fell short. I can't believe I'm the only one trying to implement this pretty common feature.
You could use the FlexScrollView which supports this feature out of the box (it is nearly impossible to do it right with the stock famo.us scrollview..):
var FlexScrollView = require('famous-flex/FlexScrollView');
var RefreshLoader = require('famous-refresh-loader/RefreshLoader');
var scrollView = new FlexScrollView({
autoPipeEvents: true,
pullToRefreshHeader: new RefreshLoader({
size: [undefined, 60],
pullToRefresh: true,
color: 'green',
backgroundColor: 'white',
particleCount: 8,
particleSize: 7
})
});
Demo: https://rawgit.com/IjzerenHein/famous-flex-chat/master/dist/index.html
Repo: https://github.com/IjzerenHein/famous-flex
RefreshLoader: https://github.com/IjzerenHein/famous-refresh-loader
Tutorial: https://github.com/IjzerenHein/famous-flex/blob/master/tutorials/FlexScrollView.md
I finally have a solution for you. It relies only on start, and end events of the scrollview sync. To start you will see the scrollview.reset function. This function is taken for scrollviews internals to return _scroller to its default behavior.
We will use a transitionable and scrollview._scroller.positionFrom to control the behavior of the scrollview when it should not be default. Essentially everything is normalized on scrollview.sync.on 'start' and the update is carried out on scrollview.sync.on 'end' if the position of the scrollview has reached the refresh offset.
Much of the functionality is tied to the fact that I am using a timeout to simulate the load time of the refresh. It may be more appropriate to have a variable that is keeping track of whether a request is still being made.
Anyway, I hope this gives you some ideas.. Here is the full example.
var Engine = require('famous/core/Engine');
var Surface = require('famous/core/Surface');
var Transform = require('famous/core/Transform');
var Scrollview = require('famous/views/Scrollview');
var Transitionable = require('famous/transitions/Transitionable');
var SnapTransition = require('famous/transitions/SnapTransition');
Transitionable.registerMethod('snap',SnapTransition);
var snap = { method:'snap', period:200, dampingRatio:0.4 }
var context = Engine.createContext();
var scrollview = new Scrollview();
var surfaces = [];
scrollview.sequenceFrom(surfaces);
for (var i = 0; i < 20; i++) {
var surface = new Surface({
size: [undefined, 200],
properties: {
backgroundColor: "hsl(" + (i * 360 / 20) + ", 100%, 50%)",
}
});
surface.pipe(scrollview);
surfaces.push(surface);
}
scrollview.trans = new Transitionable(0);
// Timeout that will simulate loading time
scrollview.timer = null;
// Total simulated load time
scrollview.timeout = 500;
// Vertical offset scrollview will start load at
scrollview.refreshOffset = 100;
// Reset scroller to default behavior
scrollview.reset = function(){
scrollview._scroller.positionFrom(scrollview.getPosition.bind(scrollview));
}
scrollview.sync.on('start',function(){
clearTimeout(scrollview.timer);
scrollview.trans.halt();
var pos = scrollview.trans.get()
if (pos != 0) scrollview.setPosition(pos);
scrollview.reset()
});
scrollview.sync.on('end',function(){
var pos = scrollview.getPosition();
if (pos < (-scrollview.refreshOffset)) {
scrollview.trans.halt();
scrollview.trans.set(pos);
scrollview._scroller.positionFrom(function(){
return scrollview.trans.get();
});
scrollview.trans.set(-scrollview.refreshOffset,snap,function(){
scrollview.timer = setTimeout(function(){
scrollview.trans.halt();
scrollview.trans.set(0,snap,function(){
scrollview.reset()
});
}, scrollview.timeout );
});
} else {
scrollview.trans.halt();
scrollview.trans.set(0);
}
});
context.add(scrollview);

Animation in famo.us with changing opacity of surface

I am new to famo.us. I am interested in creating animation where surface/object is in continuous motion and also its opacity keeps changing( ranging from 0->1 and back). I have am able to continuously rotate a surface and change its opacity from 1 to 0 once. I am stuck here as I am not sure how to proceed forward. Can someone suggest me some ideas ?
There are a couple ways you can go about doing this. It really depends on the exact effect you are trying to achieve. Here is an example using Modifier and its opacityFrom method to achieve a never ending sine effect.
var Engine = require('famous/core/Engine');
var Surface = require('famous/core/Surface');
var RenderNode = require('famous/core/RenderNode');
var Modifier = require('famous/core/Modifier');
var context = Engine.createContext();
surface = new Surface({
size:[200,200],
properties:{
backgroundColor:'green'
}
})
surface.node = new RenderNode();
surface.mod = new Modifier({origin:[0.5,0.5]});
var startTime = Date.now();
var period = 1000;
surface.mod.opacityFrom(function(){
currentTime = Date.now();
return Math.abs(Math.sin((((currentTime - startTime) / period))));
});
surface.node.add(surface.mod).add(surface);
context.add(surface.node);
Or if you would like, you could achieve a never ending animation by just using StateModifier the callback function in setOpacity. Here I am recursively calling the same function, in a nested callback. This is what that looks like.
Hope some of this helps!
var Engine = require('famous/core/Engine');
var Surface = require('famous/core/Surface');
var RenderNode = require('famous/core/RenderNode');
var StateModifier = require('famous/modifiers/StateModifier');
var Easing = require('famous/transitions/Easing');
var context = Engine.createContext();
surface = new Surface({
size:[200,200],
properties:{
backgroundColor:'green'
}
})
surface.node = new RenderNode();
surface.mod = new StateModifier({origin:[0.5,0.5]});
surface.state = 0
surface.setState = function(state,transition){
surface.state = (state == 0) ? 1 : 0;
surface.mod.setOpacity(state,transition,function(){
surface.setState(surface.state,transition);
});
}
surface.node.add(surface.mod).add(surface);
context.add(surface.node);
surface.setState(surface.state,{duration:1000, curve:Easing.inOutQuad});

code like Lightbox example

I'm searching for an code example of the Lightbox demo from famo.us. Unfortunately, the interesting part of the app in the codepen is uglified in the pens version of the famous.lib.js.
It's not the whole gallery thing I'm interested in, its "just" a scollable view with multiple elements in one row, depending on their size and the screen size.
Has anyone experiences in developing a View/Layout like this?
Thanks in advance
I have been doing some work with Scrollview and GridLayout. You can calculate grid dimensions based on contextSize and target/min cell size.
Here is an example to get you started. I am essentially adding a gridLayout as the sole view in Scrollview. I know this may work against scrollviews efficient rendering in that scrollview will always be rendering the entire grid, but I am doing it for the examples sake. I use Modifier around the gridLayout to ensure the size of the view is always calculated and scrollview always scrolls the right amount. So anyway, here is what I did..
var Engine = require('famous/core/Engine');
var Surface = require('famous/core/Surface');
var RenderNode = require('famous/core/RenderNode');
var Modifier = require('famous/core/Modifier'); // edited
var Scrollview = require('famous/views/Scrollview');
var GridLayout = require('famous/views/GridLayout');
var context = Engine.createContext();
var scrollViews = [];
var scrollview = new Scrollview();
scrollview.sequenceFrom(scrollViews);
var gridCells = [];
var grid = new GridLayout();
grid.sequenceFrom(gridCells);
grid.mod = new Modifier();
var cellCount = 24;
var cellMinWidth = 200.0;
grid.mod.sizeFrom(function(){
var size = context.getSize();
var cellsX = Math.floor(size[0] / cellMinWidth);
var cellsY = Math.ceil(cellCount * 1.0 / cellsX);
var cellHeight = size[0] * 1.0 / cellsX;
grid.setOptions({dimensions:[cellsX,cellsY]});
return [undefined,cellsY*cellHeight];
});
grid.node = new RenderNode();
grid.node.add(grid.mod).add(grid);
for (var i = 0; i < cellCount; i++) {
var surface = new Surface({
size:[undefined,undefined],
properties: {
backgroundColor:'hsl('+(i*360/12)+',75%,50%)'
}
});
gridCells.push(surface);
surface.pipe(scrollview);
};
scrollViews.push(grid.node);
context.add(scrollview);
FWIW To really maximize the efficiency of scrollview, you need to have a list of views that are rendered sequentially. Since we are only rendering one view in scrollview, we are always rendering everything, all the time. I have thought of a couple ways to get around this though, that both extend beyond the scope is this example.
You could do the visibility check yourself and render nodes based on the position of the scrollview.
You could create a gridLayout for each row, then manage the cells within each grid using list manipulation techniques.
BONUS:
If you just want to use GridLayout just for the modifiers, I found using _modifiers property rather helpful (Note: only available after deploy!). I was able to create a rearrangeable layout using this technique. The surfaces are all floating outside the gridlayout and only being positioned via their draggable modifier based on the gridlayouts modifiers. Here is that working demo of that..
http://higherorderhuman.com/examples/rearrangeable.html
Hope this helps!

How to build an endless animation with famo.us?

I'm willing to build an endless animation using famous (for example an endless rolling gear or a randomly shaken surface). Should I write a custom Transitionable with an infinite duration or there is something smarter to achieve this ?
I would recommend using Modifiers transformFrom method to define a position or rotation based on time. This allows you to set a transform that will be updated on every tick of the engine, but will be controlled via actual time.
Here is an example of that..
Hope it helps!
var Engine = require('famous/core/Engine');
var Surface = require('famous/core/Surface');
var Transform = require('famous/core/Transform');
var Modifier = require('famous/core/Modifier');
var context = Engine.createContext();
var surface = new Surface({
size:[200,200],
content: "Hello",
properties: {
lineHeight:"200px",
color:"white",
textAlign:"center",
backgroundColor:'green'
}
})
surface.mod = new Modifier({
origin: [0.5,0.5],
align:[0.5,0.5]
});
var startTime = (new Date).getTime();
surface.mod.transformFrom(function(){
var currentTime = (new Date).getTime();
var rotation = (currentTime - startTime) / 360.0;
return Transform.rotate(0,0,rotation);
});
context.add(surface.mod).add(surface);
The answer is to use Transitionables
First you use use Transitionable.set(destination, {duration: VALUE})
Each Engine 'prerender' (every frame), you use Transitionable.get() to update a Modifier Transform
At the end of the Transitionable, it will run a callback to update the new destination
this.transit = new Transitionable(0);
// ------- when i say twerk, you twerk ----->
var _createWheelMod = function() {
var _setWheelModRotation = function() {
Engine.on("prerender", function() {
this.wheelMod.setTransform(Transform.rotate(0, 0, this.transit.get()));
}.bind(this));
};
// ------------ charge lasers ------->
var _setDestination = function() {
this.transit.set(100 + this.transit.get(), {duration: 2e5},
_setDestination.bind(this)); // callback when the transition has finished
};
_setWheelModRotation.call(this);
_setDestination.call(this);
};

Centered surface transform in famo.us

In order to center a scaling or a rotation you need to rely on a modifier using the origin property i.e. {"origin":[0.5,0.5]}
Unfortunately it seems that origin will not only set the reference center point of the surface but also translate it to some place relative to its parent.
Now imagine the following case: I would like to create a button surface and to place it in the upper left corner (default with origin:[0,0]). While triggering an action, the surface should scale (or rotate) from its center and not from this upper left corner (which would be the default behaviour).
Obviously I could make it work by creating a custom transform matrix multiplying a translation and a scaling matrix in order to center the surface.
However I cannot imagine there is no better famo.us way of doing this, I tried to make a container (read a view), add the surface to the view through a centred modifier then add this view to the context through another modifier with origin [0,0] but no way… any advise on the pattern to use ?
[EDIT] Famo.us version > 0.2.0 introduced the "align" property.
var rotation = new StateModifier({
origin:[0.5,0.5], // Set the origin to the center, this will center the rotation as well
align:[0,0], // Set the surface's origin to the upper left corner of the parent
transform: Transform.rotateZ(1), // a transform (here a rotation)
});
Here below is my previous solution for versions < 0.2.0:
As long as there is no modifier with the "size" property the context one is taken as a fallback. In the same way, a modifier cannot be positioned relatively to its parent if its size property is missing. Here is an example on how to do this
var Engine = require('famous/core/Engine');
var Surface = require('famous/core/Surface');
var Transform = require('famous/core/Transform');
var StateModifier = require('famous/modifiers/StateModifier');
var mainContext = Engine.createContext();
var surface = new Surface({
size: [100, 100],
properties: {
backgroundColor: '#FA5C4F' // a silly square surface
}
});
var rotation = new StateModifier({
origin:[0.5,0.5], //center for the rotation
transform: Transform.rotateZ(1),// a transform
});
var stateModifier = new StateModifier({
origin:[0.0,0.0],//The modifier position according to the parent
size:[100,100],// The size with which the modifier can be positioned (note that using [1,1] would put the center of the square stuck to the upper left corner)
});
mainContext.add(stateModifier).add(rotation).add(surface);
When you say 'no way', are you saying you do not want to do it like this, or that the View does not work?
I would suggest using the ContainerSurface every time you want to alter the origin. If there are two origins there has to two modifiers.
Here is how I approached the problem..
Hope it helps..
var Engine = require('famous/core/Engine');
var Surface = require('famous/core/Surface');
var StateModifier = require('famous/modifiers/StateModifier');
var ContainerSurface = require('famous/surfaces/ContainerSurface');
var EventHandler = require('famous/core/EventHandler')
var Transform = require('famous/core/Transform')
var Easing = require('famous/transitions/Easing')
var context = Engine.createContext();
var surface = new Surface({
size: [200,200],
properties: { backgroundColor: 'green' }
});
surface.rotateState = new StateModifier({origin:[0.5,0.5]});
surface.translateState = new StateModifier({origin:[0,0]});
surface.container = new ContainerSurface({size:surface.getSize()});
surface.container.add(surface.rotateState).add(surface);
var rotate = 0;
var transition = {duration:400,curve:Easing.inOutQuad};
surface.on('click',function(){
rotate += Math.PI;
surface.rotateState.setTransform(Transform.rotate(0,0,rotate),transition);
});
context.add(surface.translateState).add(surface.container);