Adding an Animation to a RaphaelJS toggle function - javascript

I ran across this snippet of code and have "used" it as a reference for developing my own specific toggle function.
Raphael.js - if / else statement not toggling on click
http://jsfiddle.net/YLrzk/1/
I would like to apply an animation to the stroke-width per say when it is increased on click. I can't seem to figure out how to add this animation in alongside the toggle function.
I figured this would be applied around the variables StrON and StrOFF so I have tried things such as:
var strOff = function() {
this.animate({ 'stroke-width': '1' }, 100);
};
var strOn = function() {
this.animate({ 'stroke-width': '5' }, 100);
};
and even just:
var strOff =
this.animate({ 'stroke-width': '1' }, 100);
var strOn =
this.animate({ 'stroke-width': '5' }, 100);
Sorry about the lazy syntax If I missed anything on the two examples of what I've tried. Thanks for any help.

Neither of these will work because strOn and strOff are not the right data type -- they must be an object containing attributes for the selected and deselected states of a given rectangle. This represents a fundamental misunderstanding of what animate does: it is essentially an asynchronous version of attr.
You could solve your problem by simply restoring strOn and strOff to their original state and then calling this in the click handler for a given rectangle:
box1.animate( strOn, 100 );
box2.animate( strOff, 100 );
box3.animate( strOff, 100 );
This still leaves you with a complexity issue. If you want to add a fourth or fifth rectangle, you will quickly drown in conditional logic. This sort of state information should, in my opinion, almost never be implemented like this. Instead, I recommend doing this:
Use a single, generic click handler.
var genericClickHandler = function()
{
// First step: find and deselect the currently "active" rectangle
paper.forEach( function( el )
{
if ( el.data('box-sel' ) == 1 )
{
el.animate( strOff, 100 ).data('box-sel', 0 );
return false; // stops iteration
}
} );
this.animate( strOn, 100 ).data( 'box-sel', 1 );
}
This will iterate through all elements in the paper -- if one of them is marked as "active," it will be animated back into its inactive state.
Use data to keep track of the selected rectangle:
paper.rect( x1, y1, w1, h1 ).attr( {} ).data( 'box-sel', 0 ).click( genericClickHandler ); // note that we're setting data to indicate that this rectangle isn't "active"
paper.rect( x2, y2, w2, h2 ).attr( {} ).data( 'box-sel', 0 ).click( genericClickHandler );
// ... as many rectangles as you like
paper.rect( xN, yN, wN, hN ).attr( {} ).data( 'box-sel', 0 ).click( genericClickHandler );
Using this approach, there's no need to keep track of individual rectangles -- only whether or not a given rectangle is selected or not.
Here's an example supporting many rectangles.

Related

Click through path in Raphael

I have defined a path with opacity 0 that includes within several elements. The purpose of that is because when mouse is over that path, the element appears (or disappear if we left the path). My problem is since the path is on top of the canvas, I cannot click on those elements and expect to be able to handle the event.
What would be the best strategy to deal with that?
I tried to put the path on back(); but when I hover over an element, it's like if I left the path.
I tried to put the element on top, but same result as before.
Using the function ispointinsidepath() and then getting the coordinates of the point I clicked seems fastidious, since element size are not constant.
You can put handlers on each of the elements, and deal with it that way. Might even be slightly easier if using Snap rather than Raphael as you can use groups then (and not need multiple event handlers), but assuming Raphael is a requirement you could do this...
var r = paper.rect(50,50,200,200).attr({ fill: 'blue', opacity: '0'}).hover( hoverover, hoverout )
var c1 = paper.circle(100,100,30).attr({ fill: 'red' }).click( clickFunc ).hover( hoverover, hoverout)
var c2 = paper.circle(200,200,30).attr({ fill: 'blue' }).click( clickFunc ).hover( hoverover, hoverout )
function hoverover() { r.attr({ opacity: 1 } ) }
function hoverout() { r.attr({ opacity: 0 } ) }
function clickFunc() { alert('clicked')}
jsfiddle

Keeping seperate track of id attributes in a .animate's step function?

I have a series of blocks that fall, and when they get to a certain point a grey block (placeholder for dust) appears; it goes away if the block bounces up past a certain point, then reappears when the block falls down again, getting smaller each time impact occurs.
Currently, however, since a class for the grey block is what is being affected, any falling block affects the overall size of the "dust" block despite falling and hitting at different times. I would like to keep track of each dust block separately, so each one changes depending on when the color block it is associated with impacts on the ground.
I've tried using ids - id='smoke"+i+"', $("#smoke"+i).height(40+smokeGlobal); - instead of a single class, but it appears doing that only allows the first block to animate. (I assumed a "for" loop is necessary there, but adding that in seemed to cancel the dust block altogether.)
As always, help would be appreciated. Thanks.
http://jsfiddle.net/pjhoL899/1/
JS:
var links = ["#portfolio", "#hamumu", "#beep", "#jk"];
var linkGlobal;
smokeGlobal = 0;
//home page functions
$(document).ready(function homePage() {
//check collisions
//initial animation
for (var i=0; i<links.length; i++) {
//animate links
$(links[i]).animate({
top: '0'
}, {
duration: 2000*(Math.random()*(2)+1),
easing: 'easeOutBounce',
step: function(y) {
if (y >= -2) {
smokeGlobal -= 2;
linkGlobal = $(this);
linkGlobal.html("<div class='smoke' id='smoke"+i+"'></div>");
$(".smoke").height(40+smokeGlobal);
$(".smoke").fadeTo("fast", 0);
}
}, complete: setTimeout(function() {
linkGlobal.html("");
}, 2000)
});
}
})
Edit, updated
Substituted now for y and $(tween.elem) for $(this) at step function. step function argument tween is one of objects within jquery .animate() ; having several properties , including currently animated element (elem) ; easing ; now ; options - which also has queue property (see .queue()).
For each element within the set links , the currently animated property could be retrieved utilizing tween object , along with the elements' animated position between the start and end of the animated property.
At original post , y could actually be utilized as now argument ; and $(this) for linkGlobal . The tween object should give access to the animated element at the specific portion of the animation required ; for adjustment of the element , animation , callback.
To keep track of each dust block separately , can access that element through .fadeTo() callback , or utilize done callback to adjust effects ; done fires before complete . done also has promise which could be utilized to adjust next animated element . There may be several potential options available to adjust features utilizing .animate().
Try (this pattern)
var links = ["#portfolio", "#hamumu", "#beep", "#jk"];
var linkGlobal;
smokeGlobal = 0;
//home page functions
$(document).ready(function homePage() {
//check collisions
//initial animation
for (var i=0; i<links.length; i++) {
//animate links
$(links[i]).animate({
top: '0'
}, {
duration: 2000*(Math.random()*(2)+1),
easing: 'easeOutBounce',
step: function(now, tween) {
console.log(now, tween);
// substituted `now` for `y`
if (now >= -2) {
smokeGlobal -= 2;
// substituted `$(tween.elem)` for `$(this)`
linkGlobal = $(tween.elem);
linkGlobal.html("<div class='smoke' id='smoke"+i+"'></div>");
// adjust "dust" colors ,
// from checking `tween.elem` `backgroundColor` , `now`
var color = linkGlobal.css("background-color");
$(".smoke").eq(0)
.css("background-color"
, color === "yellow" && now < -1.05 ? "purple" : "orange");
$(".smoke").eq(1)
.css("background-color"
, color === "purple" && now < -0.00025 ? "orange" : "pink");
$(".smoke").eq(2)
.css("background-color"
, color === "pink" && now >= -0.0075 ? "purple" : "yellow");
$(".smoke").eq(3)
.css("background-color"
, color === "orange" && now < -0.003 ? "pink" : "purple");
// alert(now);
$(".smoke").height(40+smokeGlobal);
$(".smoke").fadeTo("fast", 0);
}
}, complete: setTimeout(function() {
linkGlobal.html("");
}, 2000)
});
}
});
jsfiddle http://jsfiddle.net/guest271314/pjhoL899/10/

How to prevent loss hover in Raphael?

I'm developing some page when I use Raphael liblary to draw some items.
my App
So my problem is in that when I'm moving to some rect it growing up but when my mouse is on text which is positioning on my rect, it loss his hover. You can see it on my app example.
var paper = new Raphael(document.getElementById('holder'), 500, object.length * 100);
drawLine(paper, aType.length, bType.length, cType.length, cellSize, padding);
process = function(i,label)
{
txt = paper.text(390,((i+1)* cellSize) - 10,label.devRepo)
.attr({ stroke: "none", opacity: 0, "font-size": 20});
var a = paper.rect(200, ((i+1)* cellSize) - 25, rectWidth, rectHeight)
.hover(function()
{
this.animate({ transform : "s2"}, 1000, "elastic");
this.prev.animate({opacity: 1}, 500, "elastic");
this.next.attr({"font-size" : 30});
},
function()
{
this.animate({ transform : "s1" }, 1000, "elastic");
this.prev.animate({opacity: 0}, 500);
this.next.attr({"font-size" : 15});
});
}
I have tried e.preventDefault(); on hover of this.next and some other solutions but it's doesn't work.
Any help would be appreciated.
Most people will suggest you place a transparent rectangle over the box and the labels and attach the hover functions to that instead. (If memory serves, you have to make the opacity 0.01 instead of 0 to prevent the object from losing its attached events.) This works fine, but I don't love this solution; it feels hacky and clutters the page with unnecessary objects.
Instead, I recommend this: Remove the second function from the hover, making it functionally a mouseover function only. Before you draw any of the rectangles and labels, make a rectangular "mat" the size of the paper. Then, attach the function that minimizes the label as a mouseover on the mat. In other words, you're changing the trigger from mousing out of the box to mousing over the area outside of it.
I left a tiny bit of opacity and color on the mat to be sure it's working. You can just change the color to your background color.
var mat = paper.rect(0, 0, paper.width, paper.height).attr({fill: "#F00", opacity: 0.1});
Now, you want to make a container for all the rectangles so you can loop through them to see which need to be minimized. I made an object called "rectangles" that contains the objects we're concerned with. Then:
mat.mouseover(function () {
for (var c = 0; c < rectangles.length; c += 1) {
//some measure to tell if rectangle is presently expanded
if (rectangles[c].next.attr("font-size")) {
rectangles[c].animate({
transform : "s1"
}, 1000, "elastic");
rectangles[c].prev.animate({opacity: 0}, 500);
rectangles[c].next.attr({"font-size" : 15});
}
}
});
Then I just removed the mouseout function from the individual rectangles.
jsBin
To be clear, this will have some downsides: If people run the mouse around really fast, they can expand several rectangles at the same time. This is remedied as soon as the mouse touches the mat. I think the functionality looks pretty nice. But the invisible mats is always an option.
I wrote a small extension to Raphael - called hoverInBounds - that resolves this limitation.
Demo: http://jsfiddle.net/amustill/Bh276/1
Raphael.el.hoverInBounds = function(inFunc, outFunc) {
var inBounds = false;
// Mouseover function. Only execute if `inBounds` is false.
this.mouseover(function() {
if (!inBounds) {
inBounds = true;
inFunc.call(this);
}
});
// Mouseout function
this.mouseout(function(e) {
var x = e.offsetX || e.clientX,
y = e.offsetY || e.clientY;
// Return `false` if we're still inside the element's bounds
if (this.isPointInside(x, y)) return false;
inBounds = false;
outFunc.call(this);
});
return this;
}

Jquery animate problem with potential variable scope issue?

I've written an example of my problem, for some reason (which I don't seem to fully understand I guess), my variables are losing scope. I'm trying to animate several things on the screen, once they've reached their destination the 'complete' function within animate() kicks in, this is meant to basically delete the dom element as well as a couple other functions that are meant to be delayed within it. A good way to think of it is a gun firing, do the damage when the bullet hits and not before, and the damage the bullet does is tied into the animation itself.
As I mentioned in my first sentence, I've written an example to demonstrate exactly what I mean. I'll also paste in the console.log output that came with it and give a summary of what it shows.
I've placed the example in jsbin, as it should be easier for you guys to see (I hope -- I've never used that stuff before). For my example I just did an animation of a square block filling, and some squares flying near it. When those squares reach its destination, it empties some out of the 'Jar' and resets its animation, then it removes itself from the dom.
http://jsbin.com/ihozil/2
Here's the console.log text from chrome:
Set Height to: 30px
Testing.Start( [Object] )
Setup #I63326848.animate()
Setup #I22596539.animate()
Setup #I14561405.animate()
Setup #I57372916.animate()
Setup #I31994195.animate()
OnComplete for :I63326848
Set Height to: 30.6px
OnComplete for :I14561405
Set Height to: 33px
OnComplete for :I57372916
Set Height to: 34.2px
OnComplete for :I31994195
Set Height to: 36px
OnComplete for :I63326848
Set Height to: 36.6px
Finished filling
As you can see from the log above, #I22596539 (2nd one) was set up, however when it came to the OnComplete methods, it did not fire, all the others did and #I63326848 fired twice (1st method setup). I've also noticed that when I remove the .stop() part of the chain on the squared box (#Jar), that these problems do not happen. However that is needed so I don't end up with several animations trying to fill the jar at the same time. I've also noticed that it's ALWAYS the second animation to be set up that does this.
So I'm not entirely sure if it's variables losing scope, else surely this would happen to the others, I've sent the last couple of days trying to suss this one out and I've hit my road block. I've ran out of things to try to fix it. You guys are my last hope!
function Jar() {
this._iAmount = 50;
this._iMaxAmount = 100;
this._iRefillRate = 5;
return this;
}
Jar.prototype = {
ReduceAmount: function( m_iAmount ) {
this._iAmount -= m_iAmount;
if( this._iAmount < 0 ) {
this._iAmount = 0;
}
return;
},
StartFill: function() {
var iHeight = ( 60 - ( 60 * this._iAmount / this._iMaxAmount ) );
console.log( "Set Height to: "+iHeight+"px" );
jQuery( '#Jar' ).css( 'height', iHeight+'px' );
if( iHeight < 60 ) {
var iMillisecondsTilMax = ( ( this._iMaxAmount - this._iAmount ) / this._iRefillRate ) * 1000;
jQuery('#Jar').stop().animate({height: '0px'}, iMillisecondsTilMax, function() { console.log( "Finished filling" ); } );
}
return;
}
};
var Testing = {
Start: function( m_oJar ) {
console.log( "Testing.Start( [Object] )" );
for( var iLoop = 0; iLoop < 5; iLoop++ ) {
var elNewDiv = document.createElement('div');
var iRandomValue = Math.round( 1 + ( Math.random() * ( 99999999 - 1 ) ) );
var iAmount = Math.round( 1 + ( Math.random() * ( 5 - 1 ) ) );
var strID = "I"+iRandomValue;
elNewDiv.setAttribute( 'id', strID );
elNewDiv.style.border = 'solid 1px red';
elNewDiv.style.position = "absolute";
elNewDiv.style.height = '200px';
elNewDiv.style.width = '200px';
elNewDiv.style.left = '0px';
elNewDiv.style.top = '0px';
document.body.appendChild( elNewDiv );
this.Animate( m_oJar, strID, iAmount );
}
return;
},
Animate: function( m_oJar, m_strID, m_iAmount ) {
console.log( "Setup #"+m_strID+".animate()" );
jQuery( '#'+m_strID ).animate({ width: '30px', height: '30px', top: '100px', left: '200px' }, {
duration: 1000,
queue: false,
easing: 'linear',
complete: function() {
console.log( "OnComplete for :"+m_strID );
m_oJar.ReduceAmount( m_iAmount );
m_oJar.StartFill();
jQuery( '#'+m_strID ).remove();
}
});
}
};
jQuery(document).ready( function() {
var oJar = new Jar();
oJar.StartFill();
Testing.Start( oJar );
});
I don't have a great answer, but I think this might be a jQuery bug. I have been able to work around the issue by modifying the StartFill method, or just not calling it the first time in the ready() function. I've also seen it go away when I use the debugger which leans towards some sort of timing issue...possibly in jQuery, possibly in the browser. I've not tested this on anything other than Chrome so it's hard to be sure. However I don't think there is anything wrong with the logic in your code provided.

Why i am unable to set z-index of div using jquery?

I have created a sample
http://jsbin.com/eqiti3
here we have three divs. Now, what i want to do is: on clicking of any div it should come on the top of other div then fade and then back to its original position. This is what i am using:
$(".box").click( function () {
var zindex = $(this).css('z-index');
/* this too is not working
$(this).css('z-index',14);
$(this).fadeTo ( 'slow', 0.5 ).fadeTo('slow', 1 )
$(this).css('z-index',zindex);
*/
$(this).css('z-index',14).fadeTo ( 'slow', 0.5 ).fadeTo('slow', 1 ).css('z-index',zindex);
} );
provided $(this).css('z-index',14) this alone is capable of making the div to come over other divs.
Use the callback
$(this).css('z-index',14)
.fadeTo('slow', 0.5 )
.fadeTo('slow', 1, function(){
$(this).css('z-index',zindex);
});
The 3rd parameter is a callback function, it will run when the animation ends.
Revision #3 on jsBin.
change your code to this:
$(".box").click( function () {
var zindex = $(this).css('z-index');
$(this).css('z-index',14).fadeTo ( 'slow', 0.5 ).fadeTo('slow', 1, function(){
$(this).css('z-index',zindex);
});
});
You can't chain the .css() method after fadeTo(), because those fx functions run asyncronously and therefore, .css() was executing immediately.
That's why all fx methods do offer callbacks which fire when finished.
See this in action: http://jsbin.com/eqiti3/2/edit
Set background:'white'; it is solved for me

Categories