I have a view (tribeProduct) that generates a list of surfaces that are contained in a scroll view, what I want to do is a create an event that when the view is "dragged" down, an animation comes into play. The following is what I have so far that for some reason isn't working.
for (var t = 0; t < tribesLength; t++) {
var tribe = new TribesView({tribes: tribes, tribe: t});
if(tribes[t]['notifications'] > 0) {
// Create view with list of surfaces contained in a scroll view
var tribeProduct = new ProductView({tribes: tribes, tribe: t});
var productModifier = new Modifier({
opacity: 0,
transform: Transform.translate(window.innerWidth * 1, 0, 100)
});
productContext.add(productModifier).add(tribeProduct);
tribe.on('click', function(e) {
var tribeProduct = this[0];
var productModifier = this[1];
productModifier.setOpacity(1, { duration: 500 });
productModifier.setTransform(Transform.translate(0, 0, 100), { duration: 500, curve: Easing.inOutBack });
var handleSwipe = new GenericSync(
['mouse', 'touch'],
{ direction: GenericSync.DIRECTION_Y }
);
tribeProduct.pipe(handleSwipe);
handleSwipe.on('end', (function(data) {
console.log('test');
var position = data.position;
var startPos = 100;
if(position > startPos) {
this
.setOpacity(
0, { duration: 500, curve: 'easeInOut' }
)
.setTransform(
Transform.translate(0, startPos + 200, 0), { duration: 500, curve: 'easeInOut' }
);
}
console.log(position);
}).bind(productModifier));
}.bind([tribeProduct, productModifier]));
}
tribe.pipe(scrollView);
surfaces.push(tribe);
}
I had a similar question it was targeted at a pull to refresh but it answers your questions as well. Or at least I believe it does.
Scrollview Pull to Refresh Famo.us
Using John Traver's response I came up with the refreshScrollView:
https://github.com/vizidrix/famous
If you look at the code I think it will help.
Related
I have a very simple view class.
MyView(subView) {
this.node = subView;
}
MyView.render = function() {
this.node.render();
}
Using this impl with e.g. a y-rotation around 0.5,0.5 works as expected, the surface is rotated correctly.
var modifier = new famous.modifiers.StateModifier({
origin: [0.5, 0.5]
});
modifier.setTransform(famous.core.Transform.rotateY(Math.PI / 3 * 1), { duration: 10000, curve: 'easeInOut' });
context.add(modifier).add(new MyView( new Surface(...)))
To be able to use the parents size i subclassed the ContextualView and override the commit().
commit: function (context) {
var transform = context.transform;
var origin = context.origin;
var size = context.size;
var align = context.align;
if (size) {
// stolen from gridlayout
transform = famous.core.Transform.moveThen([-size[0]*origin[0], -size[1]*origin[1], 0], transform);
}
return {
origin : origin,
transform: transform,
align : align,
size: size,
target: [
{
target : this.node.render()
}
]
};
},
Unfortunaly this does not work, the rotation is different.
What is the minimal code a commit() method must contain to have the same effect like render?
Solution: Removing the origin from returned render-tree, solves the issue.
commit: function (context) {
var transform = context.transform;
var origin = context.origin;
var size = context.size;
var align = context.align;
if (size) {
// stolen from gridlayout
transform = famous.core.Transform.moveThen([-size[0]*origin[0], -size[1]*origin[1], 0], transform);
}
return {
transform: transform,
align : align,
size: size,
target : this.node.render()
};
}
If I have some bodies that are repulsed and also have a distance. Right now they bounce back and forth endlessly. Is there a way to add a resistance to the physics engine so that they may come to rest?
var context = Engine.createContext();
var contextSize = context.getSize();
var handler = new EventHandler();
var physicsEngine = new PhysicsEngine();
function addBall(color, i) {
var ball = new Surface ({
size: [50,50],
properties: {
backgroundColor: color,
borderRadius: '100px'
}
});
ball.state = new StateModifier({origin:[0.5,0.5]});
ball.particle = new Circle({radius:50, mass: 20 + i * 1});
return ball;
}
var leftWall = new Wall({normal : [1,0,0], distance : contextSize[0]/2.0, restitution : 0.7});
var rightWall = new Wall({normal : [-1,0,0], distance : contextSize[0]/2.0, restitution : 0.7});
var topWall = new Wall({normal : [0,1,0], distance : contextSize[1]/2.0, restitution : 0.7});
var bottomWall = new Wall({normal : [0,-1,0], distance : contextSize[1]/2.0, restitution : 0.7});
var walls = [leftWall,rightWall,bottomWall,topWall];
var bodies = [];
_.each(['yellow', 'blue','green', 'red', 'orange', 'purple','gray', 'black'],function(color, index) {
var body = addBall(color, index);
bodies.push(body);
});
_.each(bodies, function (body) {
physicsEngine.addBody(body.particle);
context.add(body.state).add(body);
});
var particles = _.map(bodies, function(body){
return body.particle;
});
_.each(particles, function(particle, index){
var r = new Repulsion({strength: 61});
var d = new Distance({length: 80, minLength: 0});
physicsEngine.attach(r, [particles[(index + 4) % 8]], particle);
physicsEngine.attach(r, [particle], particles[(index + 4) % 8]);
physicsEngine.attach(d, [particles[(index + 1) % 8]], particle);
if (index == 0) {
physicsEngine.attach(d, [particle], particles[7]);
} else {
physicsEngine.attach(d, [particle], particles[(index - 1) % 8]);
}
particle.setVelocity([0.004101*index,0.004101*index,0 ]);
});
_.each(walls, function(wall) {
physicsEngine.attach(wall);
});
Engine.on('prerender', function(){
_.each(bodies, function(body) {
body.state.setTransform(body.particle.getTransform());
});
});
I was looking for Drag!
var drag = new Drag({strength: 0.1})
physicsEngine.attach(drag, [particle]);
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);
i'd like to manipulate the sizes of surfaces in a famo.us ScrollView, ideally with a transition.
the RenderNode() trick for doing this in a SequentialLayout, (Surface->Modifier->RenderNode) doesn't apply because it can't handle the .pipe()..
and wrapping each Node in another containerSurface with size:[true,true] just collapses it.
so i can't figure out a configuration that allows manipulation of sizes inside a Scrollview.
thanks, in advance
I found an interesting bug with undefined width surfaces that was not allowing me to do achieve this with a StateModifier. I used a Modifier and everything worked as expected.
I set up a normal looking scrollview and loop through the creation of the surfaces. To animate size, I prefer to wrap an [undefined,undefined] sized surface in a sized Modifier. This allows me to animate the size with more control.
After the creation of each surface, I create a RenderNode and a Modifier and then add the Modifier and the Surface to the RenderNode. I am still only piping events from surface, and that is all that is needed.
To animate the size, I define a Transitionable and use it in the return statement of Modifiers sizeFrom method. Now animating the height of the surface is as easy as setting the transitionable. I do so on click.
Here is the example. Good Luck!
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);
Alternatively you can use the FlexScrollView, which supports a flow-mode for smooth inserting/removing/updating/swapping:
var scrollView = new FlexScrollView({
flow: true // flow-mode causes renderables to smoothly flow to their new position
});
scrollView.push(...);
scrollView.push(...);
scrollView.insert(1, ...); // insert item in between
https://github.com/IjzerenHein/famous-flex/blob/master/tutorials/FlexScrollView.md
https://github.com/IjzerenHein/famous-flex
Sorry if this is a newbie question but I have just started using cocos2d-html and I am having some issues creating a ccmenu with an image. Here is my code:
var ActionLayer = cc.Layer.extend({
getTexture: function (name) {
return cc.TextureCache.getInstance()
.addImage('./images/' + name + '.png');
},
addObject: function (desc) {
var sprite = cc.Sprite.createWithTexture(this.getTexture(desc.name));
sprite.setAnchorPoint(desc.anchor || cc.p(0.5, 0.5));
sprite.setScaleX(desc.scaleX || desc.scale || 1);
sprite.setScaleY(desc.scaleY || desc.scale || 1);
sprite.setRotation(desc.rotation || 0);
sprite.setPosition(cc.p(desc.x || 0, desc.y || 0));
this.addChild(sprite, desc.z || 0);
return sprite;
},
checkAnswer:function(){
alert('yay');
},
init: function () {
this._super();
this.removeAllChildrenWithCleanup(true);
this.setTouchEnabled(true);
var layer1 = cc.LayerColor.create(
new cc.Color4B(00, 185, 214, 255), 1024, 768);
layer1.setPosition(new cc.Point(0.0,0.0));
this.addChild(layer1,-2);
var director = cc.Director.getInstance(),
self = this,
winSize = director.getWinSize();
var bgSprite = this.addObject({
name: "GenericBG",
scaleY: 1,
anchor: cc.p(0, 0),
z: 0
});
var closeItem = cc.MenuItemImage.create('./images/sign.png','./images/sign.png',this,'checkAnswer');
closeItem.setAnchorPoint(cc.p(0.5, 0.5));
var menu = cc.Menu.create(closeItem);
menu.setPosition(500,300);
this.addChild(menu, 5);
}
}); //end ActionLayer
//--------------------- Scene ---------------------
var ActionLayerScene = cc.Scene.extend({
onEnter: function () {
this._super();
var layer = new ActionLayer();
layer.init();
this.addChild(layer);
}
});
The menu is displayed on the screen but when I click on the menuitem, nothing happens
There is no javascript error on the console so I really don't know how to debug this.
Thanks
Cyril
Your parameter for cc.MenuItemImage.create() is wrong. Instead of:
var closeItem = cc.MenuItemImage.create('./images/sign.png','./images/sign.png',this,'checkAnswer');
it should be:
var closeItem = cc.MenuItemImage.create('./images/sign.png','./images/sign.png','checkAnswer', this);
for(var i=0; i<5; i++){
var colorImg = cc.MenuItemImage.create(color_list[i], color_list[i], "colorAction", this);
colorImg.setPosition(new cc.Point(40+i*70, 38));
colorImg.tag = i+100;
this.colorImageArray.push(colorImg);
colorMenu.addChild(colorImg);
}
colorAction:function(event)
{
var colorTag = event.tag;
switch(colorTag)
{
case 1:
break;
case 2:
break;
case 3:
break;
}
}