Why do we need transitionableTransform class in famo.us? - 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);

Related

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 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;
});

Built-in Popup Modal

Is there any built-in support for modals in Famous?
I completed the Famous University and looked through the documentation but didn't see anything.
I am about to roll my own, but I figured I should ask first.
You are going to want to use the Lightbox object. Lightbox is like a RenderController that has state. This means you can define, how views come in an out of view both by transition and transform. Here is a very basic example of the modal type you see often in iOS..
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 Lightbox = require('famous/views/Lightbox');
var Easing = require('famous/transitions/Easing');
var context = Engine.createContext();
var surface = new Surface({
size:[200,200],
properties:{
backgroundColor:'green',
color:'white',
lineHeight:'200px',
textAlign:'center'
}
});
surface.on('click',function(){ openModal() });
context.add(new Modifier({origin:[0.5,0.5]})).add(surface);
var modal = new Surface({
size:[500,500],
properties:{
backgroundColor:'red'
}
})
modal.on('click',function(){ hideModal() });
modal.lightbox = new Lightbox({
inTransform: Transform.translate(0,500,0),
outTransform: Transform.translate(0,500,0),
inTransition: {duration:1000, curve:Easing.outElastic},
outTransition: {duration:200, curve:Easing.inOutQuad},
});
context.add(new Modifier({origin:[0.5,0.5]})).add(modal.lightbox);
function openModal(){
modal.lightbox.show(modal);
}
function hideModal(){
modal.lightbox.hide();
}

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!

famo.us: add an item to main context after an animation completes

I have an animation that takes 1 sec to complete, and then I want to show some text boxes for username/password and a login button. Is there some sort of "oncomplete" event for an animation?
Yes, there most certainly is..
It will be the third argument in your setTransform or equivalent animation function..
Check it out..
EDIT:
I did misunderstand your question. There is no event that is emitted, only the ability to set the completion handler.
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 Easing = require("famous/transitions/Easing");
var context = Engine.createContext();
var surface = new Surface({
size:[200,200],
properties: { backgroundColor: 'green' }
})
surface.state = new StateModifier({ origin:[0,0.5] });
var completionFunction = function(){ console.log("Complete!"); };
var xOffset = 0;
context.add(surface.state).add(surface);
surface.on('click', function(){
transition = { duration:1000, curve:Easing.inOutQuad };
xOffset += 100;
surface.state.setTransform(Transform.translate(xOffset,0,0),transition, completionFunction )
});
There is no built in 'oncomplete' event, but assuming that you're using some sort of Transitionable, you can do whatever you want via the the callback argument of Transitionable#set:
https://famo.us/docs/0.1.1/transitions/Transitionable/#set
If you prefer to work with events, you can use an EventEmitter to emit one via the callback yourself.