Is it possible to chain align and origin modifiers in famo.us? - famo.us

I'm modifying align and origin values to animate views in and out of the screen. However, I also need to use align and origin to position a surface in one of the views. When this surface sets the align and origin values it seems to not take into account the setting of align and origin set on the parent view. Anyone know how to structure the render tree to have align and origin values take parent view settings into account?
Lets say view has align [1, 0] and origin [0,0] (pushed out of screen to the right) in this view there's a surface with align [0.5, 0.5] and origin [0.5, 0.5] (centered in view). This shows the surface in the center of the screen whilst I would like to have it in the center of the parent view.

You CAN chain modifiers, but remember Modifiers are a one to one relationship with their Render Nodes. You cannot accomplish your objective with using the same Modifier for two different renderables.
In your case you would be building a render tree and chaining your Render Nodes to each other. A root RenderNode (View) can be used to transition your chained Render Nodes off the screen.
Click Here for a working Example
Let's Explain
The root node will be your entry node for the main view.
var rootView = new View();
var rootMod = new Modifier({
size: [undefined, undefined],
origin: [0, 0],
align: [1, 0]
});
Create an off screen View Node and a splash View Node to be chained.
var offView = new View();
var offMod = new Modifier({
size: [undefined, undefined],
origin: [0, 0],
align: [0, 0]
});
var off = new Surface({
size: [undefined, undefined],
properties: {
backgroundColor: 'rgba(0,0,0,0.25)'
}
});
var splashView = new View();
var splash = new Surface({
size: [200, 200],
properties: {
backgroundColor: 'rgba(255,0,0,0.25)'
}
});
var splashMod = new Modifier({
origin: [0.5, 0.5],
align: [0.5, 0.5]
});
Now add them to their respective Render Nodes.
Then add the off screen view node to the root node:
rootView.add(offView);
Add the splash View to the off screen View
offView.add(splashView);
Now you are ready to add Renderables with their Modifiers to each Render Node respectively.
offView.add(offMod).add(off);
splashView.add(splashMod).add(splash);
mainContext.add(rootMod).add(rootView);
mainContext.add(root);

Related

Why do we need transitionableTransform class in famo.us?

Why do we need transitionableTransform class in famo.us if we simply can set transforms and transitions in state modifier:
var mainContext = Engine.createContext();
var surface = new Surface({
size:[100,100],
content: 'Click Me'
});
var modifier = new StateModifier({
align: [.5, .5],
origin: [.5, .5],
transform: Transform.translate(0,-240,0)
});
surface.on("click", function(){
modifier.setTransform(Transform.translate(0,0,0), {curve: 'easeIn', duration: 700});
});
mainContext.add(modifier).add(surface);
The same question is about transitionable state maintainer. When and why I should use it instead of direct setting transform and transition of state modifier as shown in the code?
According to the Famo.us Docs, modifier.setTransform (as used in talves example) is Deprecated.
From https://famo.us/docs/api/latest/core/Modifier
"Deprecated: Prefer transformFrom with static Transform, or use a TransitionableTransform."
--
However the recommended way of using TransitionableTransform is different from what you have. you are supposed to store the reference to a transitionableTransform instance in the Modifier's instantiation.
An advantage you get is that you don't have to reference the modifier when working on it's transform. You just call methods directly on the transform.
Refer here for an example: https://gist.github.com/LearnFamous/ae8d1c8f7af7a702d544
Passing it as an option is just a way to initialize the transform state of your modifier. As pointed out by Antonio setTransform is now deprecated.
The following is the same as your code: Here is the example using TansitionableTransform
var mainContext = Engine.createContext();
var surface = new Surface({
size:[100,100],
content: 'Click Me',
properties: {
backgroundColor: 'lightgrey'
}
});
var transTransform = new TransitionableTransform();
transTransform.set(Transform.translate(0,-240,0));
var modifier = new Modifier({
align: [0.5, 0.5],
origin: [0.5, 0.5],
transform: transTransform
});
surface.on("click", function(){
transTransform.setTranslate(Transform.translate(0,0,0), {curve: 'easeIn', duration: 700});
});
mainContext.add(modifier).add(surface);

bad quality of a surface content after scaling or as a result of Z-translate with perspective option

var mainContext = Engine.createContext();
mainContext.setPerspective(500);
var surface = new Surface({
content: 'im a content',
size: [200, 200]
});
var modifier = new StateModifier();
mainContext.add(modifier).add(surface);
in onClick event the following code results in very bad quality of displayed content 'im a content':
1) modifier.setTransform(Transform.translate(0, 0, 300));
or
2) modifier.setTransform(Transform.scale(3, 3, 1));
What Im doing wrong here?
How to force famo.us to rerender content after transformations for better quality?
This is not so much an issue of Famo.us as it is the browser. See the issue here.
Windows Chrome browser?
I tested it using this code. Example jsBin
Chrome (39.0.2171.95) Not Working
Firefox (31) Working
IE (11.0.15) Working
var mainContext = Engine.createContext();
mainContext.setPerspective(1000);
var surface = new Surface({
content: 'Famous Application',
size: [200, 200],
transform: Transform.scale(1, 1, 1),
properties: {
fontSize: '2em',
backgroundColor: 'rgba(0, 0, 0, 0.4)'
}
});
var modifier = new Modifier({
origin: [0, 0],
align: [0, 0]
});
mainContext.add(modifier).add(surface);
surface.on('click', function() {
console.log('clicked');
if (!surface.clicked) {
//modifier.setTransform(Transform.translate(0, 0, 300));
modifier.setTransform(Transform.scale(3, 3, 1), {duration: 300}, function(){surface.setContent('');surface.setContent('<div>Scaled Now!</div>');});
}
else {
modifier.setTransform(Transform.scale(1, 1, 1), {duration: 300}, function(){surface.setContent('');surface.setContent('Famous Application');});
}
surface.clicked = !surface.clicked;
});

famo.us - RenderController and draggable surfaces

I have two surfaces. One is draggable and one is 'full screen' (size: [undefined, undefined]). I'd like to drag the first surface (yellow in my example) right and have the second (green)surface appear. When I click on the green surface, I'd like show the first surface again back in the original starting point (center of screen).
I'd also like the green surface to be non-draggable.
I'm a famo.us novice, and any help is GREATLY appreciated!
fiddle here: http://jsfiddle.net/cjs123456/vfzy4j51/
// the position state
var position = [0, 0];
// create a Sync to listen to mouse events
var sync = new MouseSync();
var renderController = new RenderController();
var mySurface = new Surface({
size: [350, 200],
content: 'drag me right more than 100px to see other surface',
properties: {
backgroundColor: "hsl(" + (5 * 360 / 40) + ", 100%, 50%)",
lineHeight: '200px',
textAlign: 'center',
cursor: 'pointer'
}
});
// Surface provides events that the sync listens to
mySurface.pipe(sync);
// Syncs have `start`, `update` and `end` events. On `update` we increment the position state of the surface based
// on the change in x- and y- displacements
sync.on('update', function(data){
position[0] += data.delta[0];
position[1] += data.delta[1];
console.log(data.position[0]);
if (data.position[0] > 100) {
console.log("FULL");
renderController.show(fullSurface);
}
});
// this modifier reads from the position state to create a translation Transform that is applied to the surface
var positionModifier = new Modifier({
transform : function(){
return Transform.translate(position[0], position[1], 0);
}
});
// a modifier that centers the surface
var centerModifier = new Modifier({
origin : [0.5, 0.5],
align: [0.5, 0.5]
});
var fullSurface = new Surface({
size: [undefined, undefined],
content: 'Click me to show other surface',
properties: {
backgroundColor: "hsl(" + (9 * 360 / 40) + ", 100%, 50%)",
lineHeight: '400px',
textAlign: 'center'
}
});
fullSurface.on("click", function() {
renderController.show(mySurface);
});
renderController.show(mySurface);
var mainContext = Engine.createContext();
var node = mainContext.add(centerModifier).add(positionModifier);
node.add(renderController);
});
There are more than one way to solve this issue.
Control the dragging of the render controller
Create separate render controller and control views
Because your use case here is simple enough, I will show the creating of a separate render controller
Here is the jsFiddle Example of the Code
Create a background render controller
var renderController = new RenderController();
var backRenderController = new RenderController();
Change the node to be the center modifier. Add our draggable to the node. Add the background controller to the node.
var node = mainContext.add(centerModifier)
node.add(positionModifier).add(renderController);
node.add(backRenderController);
Control the draggable render contoller view (Line 42)
renderController.hide();
backRenderController.show(fullSurface);
Clicking on the background will reset the position of the draggable back to the origin, hide the background and show the draggable again.
// Set draggable position back to the origin
position = [0, 0];
// Hide the back render element
backRenderController.hide();
// Show the draggable
renderController.show(mySurface);
Full code:
// the position state
var position = [0, 0];
// create a Sync to listen to mouse events
var sync = new MouseSync();
var renderController = new RenderController();
var backRenderController = new RenderController();
var mySurface = new Surface({
size: [350, 200],
content: 'drag me right more than 100px to see other surface',
properties: {
backgroundColor: "hsl(" + (5 * 360 / 40) + ", 100%, 50%)",
lineHeight: '200px',
textAlign: 'center',
cursor: 'pointer'
}
});
// Surface provides events that the sync listens to
mySurface.pipe(sync);
// Syncs have `start`, `update` and `end` events. On `update` we increment the position state of the surface based
// on the change in x- and y- displacements
sync.on('update', function (data) {
position[0] += data.delta[0];
position[1] += data.delta[1];
console.log(data.position[0]);
if (data.position[0] > 100) {
console.log("FULL");
renderController.hide();
backRenderController.show(fullSurface);
}
//else {
// mySurface.setPosition([0,0,0], {
// curve: Easing.outBack,
// duration: 300
// });
//}
});
// this modifier reads from the position state to create a translation Transform that is applied to the surface
var positionModifier = new Modifier({
transform: function () {
return Transform.translate(position[0], position[1], 0);
}
});
// a modifier that centers the surface
var centerModifier = new Modifier({
origin: [0.5, 0.5],
align: [0.5, 0.5]
});
var fullSurface = new Surface({
size: [undefined, undefined],
content: 'Click me to show other surface',
properties: {
backgroundColor: "hsl(" + (9 * 360 / 40) + ", 100%, 50%)",
lineHeight: '400px',
textAlign: 'center'
}
});
fullSurface.on("click", function () {
// Set draggable position back to the origin
position = [0, 0];
// Hide the back render element
backRenderController.hide();
// Show the draggable
renderController.show(mySurface);
});
//var node = mainContext.add(myModifier);
//node.add(draggable).add(surface);
renderController.show(mySurface);
var mainContext = Engine.createContext();
//mainContext.add(myModifier).add(draggable).add(renderController);
var node = mainContext.add(centerModifier)
node.add(positionModifier).add(renderController);
node.add(backRenderController);

Do famo.us layouts like SequentialLayout participate in the render tree?

When using a SequentialLayout in trying to apply StateModifiers to Surface objects that had been added to a layout, it looks like some unexpected behavior happens:
When applying transformations via setTransform on a StateModifier, I expect to see the transformation applied from the origin of the Surface in question.
Instead, the transform is applied from an origin of 0,0 in relation to the parent SequentialLayout
Given the code below, the above behavior seems to make no logical sense (for context, I am working on a sorting algorithms demo, using famo.us):
/* globals define */
define(function(require, exports, module) {
'use strict';
// import dependencies
var Engine = require('famous/core/Engine');
var Modifier = require('famous/core/Modifier');
var Transform = require('famous/core/Transform');
var Surface = require('famous/core/Surface');
var StateModifier = require('famous/modifiers/StateModifier');
var SequentialLayout = require('famous/views/SequentialLayout');
// create the main context
var mainContext = Engine.createContext();
// your app here
var surfaces = [];
// Sorter
var Sort = require('sort');
var arr = [100,25,20,15,30,-20,-10,10,0];
var min = Math.min.apply(null, arr);
var base_dims = [ 50, 50 ];
arr.forEach(function(el) {
surfaces.push(new Surface({
content: el,
size: base_dims.map(function(d) { return d + (el - min); }),
properties: {
backgroundColor: 'rgb(240, 238, 233)',
textAlign: 'center',
padding: '5px',
border: '2px solid rgb(210, 208, 203)',
marginTop: '50px',
marginLeft: '50px'
}
}));
});
var sequentialLayout = new SequentialLayout({
direction: 0,
itemSpacing:20
});
sequentialLayout.sequenceFrom(surfaces);
mainContext.add(sequentialLayout);
var swap_modifiers = [
new StateModifier({}), new StateModifier({})
];
Sort.bubble_sort_iterative(arr, function(first_swap_index, second_swap_index) {
swap_modifiers[0].setTransform(
Transform.translate(300, 0, 0),
{ duration : 750, curve: 'linear' }
);
swap_modifiers[1].setTransform(
Transform.translate(300, 0, 0),
{ duration : 750, curve: 'linear' }
);
mainContext.add(swap_modifiers[0]).add(surfaces[first_swap_index]);
mainContext.add(swap_modifiers[1]).add(surfaces[second_swap_index]);
});
});
A surface has no origin, a (state-)modifier has an origin. Since you don't provide any origin vaue, the default value is set up, which is [0, 0]. See more:
http://famo.us/university/lessons/#/famous-101/positioning/8
Think of your SequentialLayout as a Render Node in your tree. Adding surfaces to SequentialLayout is in essence adding individual nodes to that tree branch. SequentialLayout happens to be adding each item at the same level in the tree.
Sort.bubble_sort_iterative(... changes the location of the surfaces by adding them to the mainContext of your application. This is the same level as the sequentialLayout and makes their origin the same origin as the sequentialLayout. Not what you wanted!
Remember: Adding a modifier to a context will make that context the parent node.
Without knowing the specifics of the above code, we know that we can add a View rather than surfaces to the sequentialLayout and could transition the View's modifiers within each of those items without changing their location in the render tree.
A simple code example of views in the sequential layout:
arr.forEach(function(el) {
var surfSize = base_dims.map(function(d) { return d + (el - min); });
console.log(size);
var view = new View();
view.mod = new StateModifier({ size: surfSize });
view.surface = new Surface({
content: el,
size: [undefined, undefined],
properties: {
backgroundColor: 'rgb(240, 238, 233)',
textAlign: 'center',
padding: '5px',
border: '2px solid rgb(210, 208, 203)',
marginTop: '50px',
marginLeft: '50px'
}
});
view.add(view.mod).add(view.surface);
surfaces.push(view);
});
Trying to swap out the views from one to the other will give you some unexpected results. It would be better to just swap out the options and content values.

Famo.us scrollview positioning

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!