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);
Related
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);
}
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);
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;
});
In my context I have a scroll view, and I'm trying to position the child elements within the view using origin/align properties in a state modifier. However for some reason, when I scroll to the bottom, the last surface isn't displayed correctly.
I can see this is because I'm using origin/align but I'm not sure on the correct way to position child elements within a scroll view? If someone could point me in the right direction that would be great.
Thanks
Code:
main.js
// Create the main context
var mainContext = Engine.createContext();
// Create scroll view
var scrollView = new Scrollview();
var surfaces = [];
scrollView.sequenceFrom(surfaces);
// Create logo
var logoNode = new RenderNode();
var logo = new ImageSurface({
size: [150, 112],
content: 'img/logo.png',
classes: ['logo']
});
// Center logo within context, center and set opacity
var modifier = new StateModifier({
align: [0.5, 0.05],
origin: [0.5, 0.05],
});
logoNode.add(modifier).add(logo);
logo.pipe(scrollView);
surfaces.push(logoNode);
var tribesLength = Object.keys(tribes).length;
for (var t = 0; t < tribesLength; t++) {
var tribe = new TribesView({tribes: tribes, tribe: t});
tribe.pipe(scrollView);
surfaces.push(tribe);
}
mainContext.add(scrollView);
TribesView.js
function TribesView() {
View.apply(this, arguments);
_displayTribe.call(this);
}
TribesView.prototype = Object.create(View.prototype);
TribesView.prototype.constructor = TribesView;
TribesView.DEFAULT_OPTIONS = {
tribes: {},
tribe: 0,
};
function _displayTribe() {
var tribes = this.options.tribes;
var tribe = this.options.tribe;
var node = new RenderNode();
var surface = new Surface({
size: [, 100],
content: tribes[tribe]['name'],
properties: {
background: tribes[tribe]['bg'],
color: 'blue'
}
});
var modifier = new StateModifier({
origin: [0, 0.1],
align: [0, 0.1]
});
node.add(modifier).add(surface);
surface.pipe(this._eventOutput);
this.add(node);
}
module.exports = TribesView;
The problem comes as you suspected, from the use of..
var modifier = new StateModifier({
origin: [0, 0.1],
align: [0, 0.1]
});
in the _displayTribe function. You have to remember that TribeView although labeled a view is nothing representative of something visual on screen. That means when you add this modifier to a surface inside a view, view thinks it is one place, which will be laid out in scrollview, and the modifier will put it in another place (in your case too low on screen).
It is difficult to give you a clear example, because I do not have the data or images or anything to make this look halfway good. If you want to use modifiers within your TribeViews, take a look at chain modifier. I have found it helpful for creating a sort of container surface without using a containerSurface.
Here is what I did to _displayTribe to give the content text an offset relative to the view..
function _displayTribe() {
var tribes = this.options.tribes;
var tribe = this.options.tribe;
var surface = new Surface({
size: [undefined, 100],
properties: {
color: 'blue',
border:'1px solid black'
}
});
this.add(surface)
var text = new Surface({
size:[undefined,true],
content: "Helloo",
})
chain = new ModifierChain()
var containerModifier = new StateModifier({
size: [undefined, 100],
});
var modifier = new StateModifier({
origin: [0, 0.1],
align: [0, 0.1]
});
chain.addModifier(modifier)
chain.addModifier(containerModifier)
this.add(chain).add(text);
surface.pipe(this._eventOutput);
}
I removed anything 'asset' related since it was not available to me. The first surface I added to the view completely unmodified. This allows us to see where scrollview is placing our view. For the text surface, I am using true sizing, and creating a ModifierChain to simulate the container as I previously mentioned. You do this by defining two modifiers, one for the size of the container, and the other for positioning in the container, then chain them together.
Lots of information, Hope this helps!
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);
});