concurrent modifiers result in huge surface trembling in famo.us - famo.us

My goal is to mimic Z-translation in perspective mode by using multiple modifiers. I can not use just z-translation of a surface because a text of translated surface became blurred (at least at Chrome but also on another browsers). The idea of using concurrent modifiers is explained in my blog: https://ozinchenko.wordpress.com/2015/02/04/how-to-avoid-blurring-of-the-text-in-famo-us-during-transition-in-z-direction/
As a result I want to have smooth translation in Z direction surface with a smooth text scaling.
the codepen code is here:
http://codepen.io/Qvatra/pen/yyPMyK?editors=001
var Engine = famous.core.Engine;
var Surface = famous.core.Surface;
var ImageSurface = famous.surfaces.ImageSurface;
var ContainerSurface = famous.surfaces.ContainerSurface;
var View = famous.core.View;
var Entity = famous.core.Entity;
var Modifier = famous.core.Modifier;
var StateModifier = famous.modifiers.StateModifier;
var Transform = famous.core.Transform;
var Transitionable = famous.transitions.Transitionable;
var TransitionableTransform = famous.transitions.TransitionableTransform;
var Easing = famous.transitions.Easing;
var Scrollview = famous.views.Scrollview;
var perspective = 1000;
var fontValue = 100; //initially font-size is 100%
var surfSize = [100,100];
var mainContext = Engine.createContext();
mainContext.setPerspective(perspective);
var transitionable = new Transitionable(0);
var mySurface = new Surface({
size: surfSize,
properties: {
backgroundColor: 'red',
textAlign: 'center',
color: 'white',
fontSize: fontValue + '%',
lineHeight: surfSize[1] + 'px'
},
content: 'Click Me'
});
var transitionModifier = new StateModifier({
origin: [.5, .5],
align: [.5, .5],
transform: Transform.translate(0,0,0.01)
});
mainContext.add(transitionModifier).add(mySurface);
function translateZ(dist, transition) {
transitionable.reset(0);
transitionable.set(dist, transition);
function prerender() {
var currentDist = transitionable.get();
//perspective formula: dist = perspective(1 - 1/scaleFactor)
var currentScale = 1 / (1 - currentDist / perspective);
var currentSize = [surfSize[0] * currentScale, surfSize[1] * currentScale];
var currentFontValue = fontValue * currentScale;
//f.e: bring closer => make projection scaleFactor times bigger
var transitionTransform = Transform.translate(0,0, currentDist);
//scaling back to avoid text blurring
var scaleTransform = Transform.scale(1/currentScale, 1/currentScale, 1);
transitionModifier.setTransform(Transform.multiply(transitionTransform, scaleTransform));
mySurface.setSize(currentSize); //resize to get correct projection size
mySurface.setOptions({
properties:{
fontSize: currentFontValue + '%', //resizing font;
lineHeight: currentSize[1] + 'px' //align text;
}
})
if (currentDist === dist) {
Engine.removeListener('prerender', prerender);
}
}
Engine.on('prerender', prerender);
}
Engine.on('click', function() {
translateZ(750, {curve: 'easeOutBounce', duration: 2000});
});
Why do I have the shaking of the image? How to avoid that?

The StateModifier is changing the size of your surface while you are setting the size of the surface. Because you are handling the size of the surface, there is no need to change (set) the StateModifier to scale. I am not sure your method will hold up in all cases, but this answers your question.
Here is a new translateZ function:
function translateZ(dist, transition) {
transitionable.reset(0);
transitionable.set(dist, transition);
function prerender() {
var currentDist = transitionable.get();
//perspective formula: dist = perspective(1 - 1/scaleFactor)
var currentScale = 1 / (1 - currentDist / perspective);
var currentSize = [surfSize[0] * currentScale, surfSize[1] * currentScale];
var currentFontValue = fontValue * currentScale;
mySurface.setSize(currentSize); //resize to get correct projection size
mySurface.setOptions({
properties:{
fontSize: currentFontValue + '%', //resizing font;
lineHeight: currentSize[1] + 'px' //align text;
}
})
if (currentDist === dist) {
Engine.removeListener('prerender', prerender);
}
console.log('trans')
}
Engine.on('prerender', prerender);
}

Related

Resizing multiple surfaces in a Scrollview

Another famo.us beginner question...
My question relates to this question and johntraver's excellent answer (provided below). I've been playing around with this for a bit, and I can't figure out how to access the other surfaces in the Scrollview when one is clicked.
For example, I'd like to re-size both 'Surface 2' AND 'Surface 3' when 'Surface 2' is clicked.
I've looked at the guides, examples, etc., and my confusion is with the RenderNode. I'm not entirely clear on the purpose it serves.
Thanks in advance for the help.
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 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: 600, dampingRatio: 0.6 }
var mainContext = Engine.createContext();
var scrollview = new Scrollview();
var surfaces = [];
scrollview.sequenceFrom(surfaces);
for (var i = 0; i < 20; i++) {
var surface = new Surface({
content: "Surface: " + (i + 1),
size: [undefined, undefined],
properties: {
backgroundColor: "hsl(" + (i * 360 / 40) + ", 100%, 50%)",
lineHeight: "200px",
textAlign: "center"
}
});
surface.open = false;
surface.state = new Modifier();
surface.trans = new Transitionable(200);
surface.state.sizeFrom(function(){
return [undefined, this.trans.get()];
}.bind(surface));
surface.node = new RenderNode();
surface.node.add(surface.state).add(surface);
surface.pipe(scrollview);
surface.on('click',function(e){
if (this.open) {
this.trans.halt();
this.trans.set(200,snap);
} else {
this.trans.halt();
this.trans.set(400,snap);
}
this.open = !this.open;
}.bind(surface));
surfaces.push(surface.node);
}
mainContext.add(scrollview);
Think of the RenderNode as the view of the items being added to the scrollview. In the example, #johntraver is storing references to his node and surface modifiers onto the surface. This may be confusing you a bit. Although it is fine, we will need to use node (item) as our reference point. Also, the render node item purpose is to allow us to have a node branch where we have multiple items.
I changed the example to make it a little more accessible to be able to get to the surfaces stored in the items (views). I am now calling the surfaces collection views to help us understand what we are passing to the scrollview for rendering. Instead a RenderNode (node) is holding the reference to our surface as properties, so we can later access the surfaces.
Here is a running example on jsBin.
Building our view
Note: I moved the functions out of the loop, for easier reading.
for (var i = 0; i < 20; i++) {
var node = new RenderNode();
node.surface = new Surface({
content: "Surface: " + (i + 1),
size: [undefined, undefined],
properties: {
backgroundColor: "hsl(" + (i * 360 / 40) + ", 100%, 50%)",
lineHeight: "200px",
textAlign: "center"
}
});
node.surface.open = false;
node.surface.state = new Modifier();
node.surface.trans = new Transitionable(200);
node.surface.state.sizeFrom(_surfaceSize.bind(node.surface));
// Add the modifier and the surface to our view
node.add(node.surface.state).add(node.surface);
node.surface.pipe(scrollview);
node.surface.on('click', _resize.bind(node.surface, i, views));
views.push(node);
}
Function to access our multiple views and resize
function _resize(index, views, event){
console.log(index, views, event);
next = index+1 < views.length ? views[index+1].surface : views[0].surface;
if (this.open) {
this.trans.halt();
this.trans.set(200, snap);
next.trans.halt();
next.trans.set(200, snap);
} else {
this.trans.halt();
this.trans.set(400, snap);
next.trans.halt();
next.trans.set(400, snap);
}
this.open = !this.open;
next.open = this.open;
}
The full code
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 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: 600, dampingRatio: 0.6 };
var mainContext = Engine.createContext();
var scrollview = new Scrollview();
var views = [];
scrollview.sequenceFrom(views);
function _resize(index, views, event){
console.log(index, views, event);
next = index+1 < views.length ? views[index+1].surface : views[0].surface;
if (this.open) {
this.trans.halt();
this.trans.set(200, snap);
next.trans.halt();
next.trans.set(200, snap);
} else {
this.trans.halt();
this.trans.set(400, snap);
next.trans.halt();
next.trans.set(400, snap);
}
this.open = !this.open;
next.open = this.open;
}
function _surfaceSize(){
return [undefined, this.trans.get()];
}
for (var i = 0; i < 20; i++) {
var node = new RenderNode();
node.surface = new Surface({
content: "Surface: " + (i + 1),
size: [undefined, undefined],
properties: {
backgroundColor: "hsl(" + (i * 360 / 40) + ", 100%, 50%)",
lineHeight: "200px",
textAlign: "center"
}
});
node.surface.open = false;
node.surface.state = new Modifier();
node.surface.trans = new Transitionable(200);
node.surface.state.sizeFrom(_surfaceSize.bind(node.surface));
node.add(node.surface.state).add(node.surface);
node.surface.pipe(scrollview);
node.surface.on('click', _resize.bind(node.surface, i, views));
views.push(node);
}
mainContext.add(scrollview);

famo.us scale and rescale, save scale data from event to event

I have a simple problem I am stuck on. I want to implement a ScaleSync on an ImageSurface. Everytime I end the scale action the data value is reset to 1 and if I perform another scale the ImageSurfaces starts transforming from 1.
define(function(require, exports, module) {
var Engine = require('famous/core/Engine');
var Modifier = require('famous/core/Modifier');
var Transform = require('famous/core/Transform');
var ImageSurface = require('famous/surfaces/ImageSurface');
var Surface = require('famous/core/Surface');
var ScaleSync = require("famous/inputs/ScaleSync");
var mainContext = Engine.createContext();
var start = 1;
var update = 0;
var end = 0;
var growShrink = "";
var objectScale = 1;
var scaleSync = new ScaleSync();
var logo = new ImageSurface({
size: [200, 200],
content: '/content/images/famous_logo.png',
classes: ['backfaceVisibility']
});
var centerModifier = new Modifier({
align: [0.5, 0.5],
origin: [0.5, 0.5]
});
logo.pipe(scaleSync);
scaleSync.on("update", function(data) {
objectScale = data.scale
centerModifier.setTransform(
Transform.scale( objectScale, objectScale, 1)
);
});
scaleSync.on("start", function(){
start ++;
});
scaleSync.on("end", function() {
end++;
});
mainContext.add(centerModifier).add(logo);
});
I tried achieving this using an Accumulator, but I think that's for different purposes. I think I have to somehow alter the data in the on.update callback, but have no clue how to do that.
Your help is much appreciated!
I found the solution. Hope this helps anybody with similar issues. The modifier should have a scale variable that is referenced as follows:
var scale = 1;
var centerModifier = new Modifier({
align: [0.5, 0.5],
origin: [0.5, 0.5],
transform: function(){
return Transform.scale(scale, scale, 1);
}
});
And the state of the variable is changed on event update (I divide data.delta by 10 to normalize the value):
scaleSync.on("update", function(data) {
scale += data.delta / 10;
});

famo.us animate state of Surfaces in a GridLayout with a StateModifier

How can I attach a StateModifier to a Surface that resides in a GridLayout?
My Code looks something like this:
//...
var grid = new Gridlayout({dimensions: [2,1]});
var surfaces = [];
grid.sequenceFrom(surfaces);
var surface01 = new Surface({content: 'Surface 01'});
var surface02 = new Surface({content: 'Surface 02'});
surfaces.push(surface01,surface02);
this._node.add(grid);
//...
Since the surfaces are not added to the render tree explicitly like:
this._node.add(modifier).add(surface)
I don't know how I can attach Modifiers to the surfaces?! Am I missing something? Any help is much appreciated
You will need to add a view as your sequence from items. The example code below uses a RenderNode as the view item and adds the StateModifier and Surface
Example jsBin Code [v0.3.0 of Famo.us]
var mainContext = Engine.createContext();
var surfaces = [];
var grid = new GridLayout({
dimensions: [2, 1]
});
var counter = 0;
_getView = function(name) {
var rnode = new RenderNode();
var state = new StateModifier({
size: [undefined, 500]
});
var surface = new Surface({
content:name,
properties: {
backgroundColor: "hsl(" + (counter * 360 / 8) + ", 100%, 50%)",
lineHeight: '500px',
textAlign: 'center',
cursor: 'pointer'
}
});
rnode.add(state).add(surface);
counter += 1;
return rnode;
};
surfaces.push(_getView('Surface 1'));
surfaces.push(_getView('Surface 2'));
grid.sequenceFrom(surfaces);
mainContext.add(grid);
If I'm not missing anything you want to modify the state of your surface inside the gridLayout, by clicking on one of them??
The gridLayout has an array of state that you can access by typing gridlayout._states['index of your surface']
var mainContext = Engine.createContext();
var surfaces = [];
var GridLayout = new GridLayout({
dimensions: [4,10]
});
for (var i = 0; i<40; i++){
var surface = new Surface({
content: 'surface' + i,
size: [200, 200]
});
surface.index = i;
surfaces.push(surface);
});
GridLayout.sequenceFrom(surfaces);
//now if you want to modify the state of the surface with index 2
GridLayout._states[2].set(Transform.rotateX(Math.PI/2)); // your surface will rotate by 90° on X axis
mainContext.add(GridLayout);

Walls are in the incorrect place

When I set the balls origin to [0.5,0.5] the walls are placed correctly, when i set its transform or origin to any other location the walls move as well?
I have tried to explicitly set the engines origin but that doesn't work either
this code is copy paste able into main.js in the famous starter kit
define(function(require, exports, module) {
var Engine = require('famous/core/Engine');
var Surface = require('famous/core/Surface');
var EventHandler = require('famous/core/EventHandler');
var View = require('famous/core/View');
var Transform = require('famous/core/Transform');
var $ = require('jquery');
var StateModifier = require('famous/modifiers/StateModifier');
var Modifier = require('famous/core/Modifier');
var PhysicsEngine = require('famous/physics/PhysicsEngine');
var Body = require('famous/physics/bodies/Body');
var Circle = require('famous/physics/bodies/Circle');
var Wall = require('famous/physics/constraints/Wall');
var Vector = require('famous/math/Vector');
var context = Engine.createContext();
var handler = new EventHandler();
//{origin:[0.5,0.5]}
var physicsEngine = new PhysicsEngine();
$('#game').on('click', function(event) {
console.log('x '+event.clientX);
console.log('y '+event.clientY)
createBall(event.clientX, event.clientY);
})
var leftWall = new Wall({
normal: [1, 0, 0],
distance: window.innerWidth / 2.0,
restitution: 0.5
});
var rightWall = new Wall({
normal: [-1, 0, 0],
distance: window.innerWidth / 2.0,
restitution: 0.5
});
var topWall = new Wall({
normal: [0, 1, 0],
distance: window.innerHeight / 2.0,
restitution: 0.5
});
console.log(window.innerHeight )
console.log(window.innerWidth )
var bottomWall = new Wall({
normal: [0, -1, 0],
distance: window.innerHeight / 2.0,
restitution: 0.5
});
leftWall = physicsEngine.attach(leftWall,[]);
rightWall = physicsEngine.attach(rightWall,[]);
topWall = physicsEngine.attach(topWall,[]);
bottomWall = physicsEngine.attach(bottomWall,[]);
var balls = []
function createBall(x, y) {
var ball = new Surface({
size: [100, 100],
properties: {
backgroundColor: 'red',
borderRadius: '100px'
}
})
ball.state = new StateModifier({
// transform: Transform.translate(x, y, 0)
});
ball.particle = new Circle({
radius: 50,
position : new Vector(x, y, 0)
});
physicsEngine.addBody(ball.particle);
ball.on("click", function() {
console.log('clicked ball')
ball.particle.setVelocity([1, 1, 0]);
});
context.add(ball.state).add(ball)
Engine.on('prerender', function() {
ball.state.setTransform(ball.particle.getTransform())
});
// balls.push(ball.particle);
//bottomWall = physicsEngine.attach(ball.particle,balls);
physicsEngine.attachTo(leftWall,ball.particle);
physicsEngine.attachTo(rightWall,ball.particle);
physicsEngine.attachTo(topWall,ball.particle);
physicsEngine.attachTo(bottomWall,ball.particle);
}
});
The physics collision and adding of particles is driven by the origin of the physics engine in famo.us, so you would want to set the origin of the physics engine to your render node.
As you can see from the code below or the following link:
jsFiddle Link
The placement of the walls are driven by the origin of the physics engine when they are attached. The particles you attach to the walls are bound by the origin of the physics engine also, so changing their origin is going to have an effect on their collision to the walls.
The example code does not attach a modifier to the circles, but adds them to the particles. I am not sure if you were trying to do something different, but hopefully this answers your question.
define('main', function (require, exports, module) {
var Engine = require('famous/core/Engine');
var Surface = require('famous/core/Surface');
var RenderNode = require('famous/core/RenderNode');
var EventHandler = require('famous/core/EventHandler');
var View = require('famous/core/View');
var Transform = require('famous/core/Transform');
//var $ = require('jquery');
var StateModifier = require('famous/modifiers/StateModifier');
var Modifier = require('famous/core/Modifier');
var PhysicsEngine = require('famous/physics/PhysicsEngine');
var Body = require('famous/physics/bodies/Body');
var Circle = require('famous/physics/bodies/Circle');
var Wall = require('famous/physics/constraints/Wall');
var Vector = require('famous/math/Vector');
var context = Engine.createContext();
var node = new RenderNode();
var physicsOrigin = [0.5, 0.5];
var radius = 50;
context.add(new Modifier({
origin: physicsOrigin
})).add(node);
var handler = new EventHandler();
var physicsEngine = new PhysicsEngine();
node.add(physicsEngine);
console.log(window.innerHeight);
console.log(window.innerWidth);
var dimX = window.innerWidth;
var dimY = window.innerHeight;
Engine.on('click', function (event) {
console.log('x ' + event.clientX);
console.log('y ' + event.clientY)
var x = event.clientX - (dimX * physicsOrigin[0]);
var y = event.clientY - (dimY * physicsOrigin[1]);
createBall(x, y);
});
var leftWall = new Wall({
normal: [1, 0, 0],
distance: Math.round(dimX / 2.0),
restitution: 0.5
});
var rightWall = new Wall({
normal: [-1, 0, 0],
distance: Math.round(dimX / 2.0),
restitution: 0.5
});
var topWall = new Wall({
normal: [0, 1, 0],
distance: Math.round(dimY / 2.0),
restitution: 0.5
});
var bottomWall = new Wall({
normal: [0, -1, 0],
distance: Math.round(dimY / 2.0),
restitution: 0.5
});
var balls = [];
physicsEngine.attach([leftWall, rightWall, topWall, bottomWall], balls);
function createBall(x, y) {
var ball = new Surface({
size: [radius * 2, radius * 2],
properties: {
backgroundColor: 'blue',
borderRadius: (radius * 2) + 'px'
}
})
ball.particle = new Circle({
radius: radius,
position: [x, y, 0]
});
physicsEngine.addBody(ball.particle);
node.add(ball.particle).add(ball)
ball.on("click", function () {
console.log('clicked ball')
ball.setOptions({properties: {backgroundColor: 'red'}});
ball.particle.setVelocity([1, 1, 0]);
});
balls.push(ball.particle);
}
});

How can I use Rotational Spring of Famo.us?

I'm trying to make some examples using Famo.us Physics engine.
I had tried an example of spring. I thought rotational spring could be similar with it,so I'm trying to do but it's not that easy for me.
Here is an example using Rotational Spring, but it doesn't work
var Engine = require("famous/core/Engine");
var Surface = require("famous/core/Surface");
var ImageSurface = require("famous/surfaces/ImageSurface");
var Modifier = require("famous/core/Modifier");
var Transform = require("famous/core/Transform");
var Transitionable = require("famous/transitions/Transitionable");
var PhysicsEngine = require("famous/physics/PhysicsEngine");
var Spring = require("famous/physics/forces/Spring");
var RotationalSpring = require("famous/physics/forces/RotationalSpring");
var Particle = require("famous/physics/bodies/Particle");
var Body = require("famous/physics/bodies/Body");
var Constraint = require("famous/physics/constraints/Constraint");
var MouseSync = require("famous/inputs/MouseSync");
var mainContext = Engine.createContext();
var PE = new PhysicsEngine();
content.particle = new Particle({
mass: 1,
position: [0, 0, 0],
velocity: [0, 0, 0],
axis : 0x01 //0x01 : Particle.AXES.y
});
// Create a spring that will act on the particle
var rotationalSpring = new RotationalSpring({
anchor: [0, 0, 0],
period: 400, // <= Play with these values :-)
dampingRatio: 0.07, // <= if 0 then just once / else if 1 then infinite
length: 0
});
// Link the spring, particle and surface together
PE.attach(rotationalSpring, content.particle);
PE.addBody(content.particle);
var translateMod = new Modifier({ origin: [.5,.5] });
//Get Mouse input
var position = [0, 0];
var mouseSync = new MouseSync(function() {
return position;
});
Engine.pipe(mouseSync);
// Get start when there is a Mouse input
mouseSync.on("start", function() {
FaceSequence.particle.applyForce(new Vector(0, 1.0, 0));
});
translateMod.transformFrom(function() {
var transM = FaceSequence.particle.getTransform();
return transM;
});
How can I make it works?? :)
After poking at the above code a bit, I realized that the function calls of RotationalSpring matched up with Body, not Particle. After a quick rewrite I got a number of odd results which made me think of rotational balances. They are extremely sensitive and extremely delicate, so I guessed that this might work the same way. After dropping the torque a bit I started to get some almost reasonable results. I'll leave the fine tuning, but here is some almost reasonable rotational spring code. I have tilted a surface of angle a little bit to make the rotation a little more clear. You can also see it run live at codefamo.us
Here is the code...
/* globals define */
define(function(require, exports, module) {
var Engine = require("famous/core/Engine");
var Surface = require("famous/core/Surface");
var ImageSurface = require("famous/surfaces/ImageSurface");
var Modifier = require("famous/core/Modifier");
var StateModifier = require("famous/modifiers/StateModifier");
var Transform = require("famous/core/Transform");
var Transitionable = require("famous/transitions/Transitionable");
var PhysicsEngine = require("famous/physics/PhysicsEngine");
var Spring = require("famous/physics/forces/Spring");
var RotationalSpring = require("famous/physics/forces/RotationalSpring");
var Particle = require("famous/physics/bodies/Particle");
var Body = require("famous/physics/bodies/Body");
var Constraint = require("famous/physics/constraints/Constraint");
var Vector = require('famous/math/Vector');
var MouseSync = require("famous/inputs/MouseSync");
var mainContext = Engine.createContext();
var PE = new PhysicsEngine();
var surf = new Surface({
size: [200,200],
properties: {
backgroundColor:'red',
'-webkit-backface-visibility':'visible'
}
});
var tilt = new StateModifier({
transform: Transform.rotateX(Math.PI/8)
});
// Create a spring that will act on the particle
var rotationalSpring = new RotationalSpring({
anchor: new Vector(0, 1, 0),
period: 3000, // <= Play with these values :-)
dampingRatio: 0.1// <= if 0 then just once / else if 1 then infinite
});
var body = new Body({
mass: 10
});
// Link the spring, particle and surface together
PE.addBody(body);
PE.attach(rotationalSpring, body);
var translateMod = new Modifier({
origin: [.5,.5]
});
translateMod.transformFrom(function() {
return body.getTransform();
});
Engine.on('click', function() {
body.applyTorque(new Vector(0,.001,0));
});
mainContext.add(translateMod).add(tilt).add(surf);
});