code like Lightbox example - famo.us

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!

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

Increase scrollview's element height on clicking it

I am new to famo.us like everybody else.
I cannot think of a way to increase the height of each item (probably surface) inside my scrollview on clicking that item.
Eg. like below list view below
item 1
item 2
item 3
Now Clicked item 2
item 1
item 2
details of item 2
item 3
Here is a way I was able to achieve clicking to adjust height. It gets a bit more complicated is you want to animate the size.
Here is the simple click example..
Hope it helps!
var Engine = require('famous/core/Engine');
var Surface = require('famous/core/Surface');
var Scrollview = require('famous/views/Scrollview');
var surfaces = [];
var scrollview = new Scrollview();
scrollview.sequenceFrom(surfaces);
for (var i = 0; i < 5; i++) {
var surface = new Surface({
size:[undefined,200],
properties: {
backgroundColor:'hsl('+(i*360/8)+',100%,50%)'
}
});
surface.open = false;
surface.on('click',function(){
var size = this.open ? [undefined,200] : [undefined,400] ;
this.setSize(size);
this.open = !this.open;
}.bind(surface));
surfaces.push(surface);
surface.pipe(scrollview);
};
context.add(scrollview);

Famo.us - How to add content to a CanvasSurface

I've just moved to Famo.us and think it has some amazing potential. I am trying to build a new App using Famo.us and will be having 'layered' Views, one of which has a CanvasSurface inside.
My question is about how I would populate the CanvasSurface? I have checked the Docs and while they talk about some of the parameter options they do not tell you how.
I have a View within which I add a Layout and then a Surface to that Layout. Other Views that have ImageSurfaces work fine - but I do not know if I am on the right track with CanvasSurface.
So far I have: (part of inside a BackgroundView.js file)
function BackgroundView() {
View.apply(this, arguments);
_createLayout.call(this);
_createBody.call(this);
_setListeners.call(this);
}
function _createBody() {
this.bodySurface = new CanvasSurface({
canvasSize : [undefined, undefined]
});
var bodyContext= this.bodySurface.getContext('2d');
bodyContext.fillText("Text on Canvas", 100, 100);
this.layout.content.add(this.bodySurface);
}
It runs with no errors, but shows nothing. The CanvasSurface is rendered...
Are there any examples using CanvasSurface or does anyone have any thoughts?
Thanks again for your help in advance.
:)
There are a couple of things I added to your code.. Defining the prototype and prototype.constructor, as well as adding the CanvasSurface to the BackgroundView. I found that canvasSize does not currently support the undefined size attribute like Surface does. You need to be explicit and use pixel size.
Check out what I did to your code.. Hope this helps..
var Engine = require('famous/core/Engine');
var Surface = require('famous/core/Surface');
var View = require('famous/core/View');
var CanvasSurface = require('famous/surfaces/CanvasSurface');
var context = Engine.createContext();
function BackgroundView() {
View.apply(this, arguments);
// _createLayout.call(this);
_createBody.call(this);
// _setListeners.call(this);
}
BackgroundView.prototype = Object.create(View.prototype);
BackgroundView.prototype.constructor = BackgroundView;
function _createBody() {
this.bodySurface = new CanvasSurface({
size:[400,200]
});
var bodyContext= this.bodySurface.getContext('2d');
bodyContext.font="20px Georgia";
bodyContext.fillText("Hello World!",50,50);
this.add(this.bodySurface);
}
var bg = new BackgroundView();
context.add(bg);

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

Changing positions of things during animation

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