Issues with glow while dragging an Object - raphael

I have a rectangle which glows on mousedown event & glow disappears at mouseup event. The issue is when I drag the rectangle, the glow persists on the canvas!
Here's my code for clarity:
window.onload = function(){
var paper = new Raphael("holder",500,500);
var myRect = paper.rect(200,200,200,100,10);
myRect.attr({
fill: "#999",
stroke: "#555",
'stroke-width': 5
});
myRect.mousedown(function(){
this.g = myRect.glow();
}
);
myRect.mouseup(function(){
this.g.remove();
});
var start = function(){
this.ox = this.attr('x');
this.oy = this.attr('y');
},
move = function(dx,dy){
var att = {x:this.ox+dx,y:this.ox+dy};
this.attr(att);
},
up = function(){
//
};
myRect.drag(move,start,up);
}

You can remove the glow of the object on start and apply it again on up. In this way the shadow wont be visible while you're draggin the object but will be there when you'll drop the object.
Here is the fiddle with your example.

First of all, you shouldn't use this inside an anonymous function because the scope of this is just available in there.
Second, you don't need myRect.mousedown and myRect.mouseup because you handle those we the callbacks inside your myRect.drag (start and up)
So i made you a fiddle where you can see it working.
P.S. There seems to be another bug with the positioning: when you drag a few times the rectangle gets shifted away from the cursor.

Related

Famo.us - How to delete view with surfaces on it

Issue:
my famo.us app allows a user to add many "card" views on a work area. These cards are draggable, added directly to a ContainerSurface, and not in any particular layout.
The cards have a delete button, which when pressed deletes the card.
Currently I "delete" the view using
theCardView.render = function(){ return null; }
This works in removing the view and the surfaces on it from the work area;however, it has a strange side-effect. When I add another card the delete button surface is under the header surface that it normally sits on.
If I add a 2nd card, then the delete button on this 2nd card is displayed properly.
When I look at the DOM after the delete I see that divs for the surfaces on my view are still there, but empty
Is there a better/cleaner way to "delete" a view? Or is there something else I need to do to ensure the DOM is cleaned up?
I can get around this issue by setting the z-index of the button to 1; however this causes a problem when you drag the cards around over each other - The button is always on top of all the other cards.
Code of how I add the CardViews
this._eventInput.on('add-cards', function() {
for (var i=0; i < that.cardPicker.selected.length ;i++){
var x = new CardView({data:that.cardPicker.selected[i]});
that.subscribe(x);
that.cards.push(x);
that.contentContainer.add(x);
}
});
Code of how I delete the view
this._eventInput.on('delete-card',function(e){
// e is the CardView object that has the button that was clicked
removeFromArray(that.cards,e);
e.render = function(){ return null; }
});
Code for the view constructor
function CardView(data) {
View.apply(this, arguments);
that = this;
this.dbdata = data.data;
this.layout = new HeaderFooterLayout({
headerSize: 43,
footerSize: 0
});
// Header
var oname = this.dbdata.dbname+"."+this.dbdata.table.name;
this.headerSurface = new Surface({size: [275, 43],
content: oname
,properties: {
backgroundColor:'rgb(51, 51, 51)'
,color:'white'
,paddingTop:'10px'
,paddingLeft:'10px'
}
});
this.draggable = new Draggable();
this.headerSurface.pipe(this.draggable);
this.draggable.activate();
this.layout.header.add(this.headerSurface);
// Header delete button
this.deletebtn = new Surface({size: [55, 40],
content: '<button type="button" class="btn btn-default">del</button>'
,properties:{
}
});
this.mod = new Modifier({
transform: Transform.translate(215, 0, 0)
});
this.layout.header.add(this.mod).add(this.deletebtn);
this.deletebtn.pp = this;
this.deletebtn.on('click', function() {
that._eventOutput.emit('delete-card',this.pp);
});
this.layout.content.add(new Surface({size: [275, 300],
properties: {
backgroundColor:'rgb(236, 236, 236)'
}
}));
// a modifier that centers the surface
this.centerModifier = new Modifier({
origin : [0.5, 0.5],
align: [0.5, 0.5]
});
this.add(this.draggable).add(this.centerModifier).add(this.layout);
}
So a couple of things:
Deleting surfaces from the context
When calling context.add() a RenderNode is created and returned, all subsquent views/modifiers/etc added to this RenderNode create additional nodes. The render node is responsible for reporting to the context what elements it contains (each surface/modifier/etc is responsible for reporting what it represents).
To clear all of the elements from a render node do something like the following
var myView = new View();
var context = Engine.createContext();
myView.node = context.add(myView); // Now the context knows about your view
myView.node.set({}); // Now the context doesn't
Now the problem remains that the parent node/context will maintain a reference to the original node even though it doesn't contain anything. This is something that is known and are working towards a solution.
Dealing with z-index
If you wanted to set the z-index of the button so that it is above the surface itself, you can also modify the z-index of the focused view. This can be accomplished by maintaining a reference to the modifier of the specific view and increasing the z-index. You will have to ensure the the change in z-index flows down into each element.
I would go for the first solution and delete the elements from the render node.

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

Famo.us - Using events on surfaces in a loop

I have a loop that creates surfaces from "TribesView" and pipes them into a scroll view, the loop also creates a "ProductView" with a modifier and adds this to a context. I want to bind an event to the "ProductView" surface where if the surface is clicked then set the opacity to '1'. What I have so far only sets the opacity to the last "ProductView" in the array regardless of which "TribeView" is clicked. Here is the code:
for (var t = 0; t < tribesLength; t++) {
var tribe = new TribesView({tribes: tribes, tribe: t});
var tribeProduct = new ProductView({tribes: tribes, tribe: t});
var productModifier = new StateModifier({
opacity: '0'
});
tribe.on('click', function() {
productModifier.setOpacity(1)
});
productContext.add(productModifier).add(tribeProduct);
tribe.pipe(scrollView);
surfaces.push(tribe);
}
Yes, you want to bind the productModifier to the function. When you bind an object, it becomes represented as 'this' within the function.
Here is how it should be done.. Good luck!
tribe.on('click', function() {
this.setOpacity(1);
}.bind(productModifier));

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

Raphael how to add onclick action that changes colour for the last clicked object

Thanks to this post, here is what I have done :
var current = null;
for (var state in fr) {
(function (st, state) {
st[0].state = 0;
st[0].onmouseover = function() {
st[0].style.cursor = "pointer";
current && fr[current].animate({fill: "#EAEAEA", stroke: "#d5d5d5"}, 300);
st.animate({fill: "#EC5505", stroke: "#EC5505"}, 300);
st.toFront();
paper.safari();
document.getElementById(state).style.display = "block";
current = state;
};
st[0].onmouseout = function () {
if (this.state == 0)
st.animate({ fill: "#EAEAEA", stroke: "#D5D5D5" }, 500);
else
st.animate({ fill: "#EC5505", stroke: "#EC5505" }, 500);
st.toFront();
paper.safari();
};
st[0].onclick = function () {
st.animate({ fill: "#EC5505", stroke: "#EC5505" }, 500);
st.toFront();
if (this.state == 0) {
this.state = 1;
}
else
this.state = 0;
paper.safari();
};
})(fr[state], state);
}
I have a few problems... When I click on an area then hover out (fill in #EC5505 on hover) this area, the area go back to #EAEAEA for a half second and back to #EC5505... Normally, this should keep the hover color when it's clicked... The second problem is that I want to have only one area selected. So when I click on a second area, the first one has to go back to the initial state.
Could you help me ? Thanks a lot
Its got something to do with your events ... because the mouseover event fires when you go to click the element ... when you leave the mouseout event fires and hence the animation in click and mouseout both get fired ...
For the second problem you will have to keep a reference of the element that got clicked last ... so that when you click an element you check
if(element has already been clicked){
// go back to original state
}
try attatching a boolean to the set[0] ... or
when set[0] is clicked ....
`var clickedElement = set[0]`
inside the onclick function
if(clickedElement){
// you already have a clicked element so restore its state
}