snap svg toggle animation - javascript

I'm using Snap.svg to create an animated icon that toggles between search and X on click. I've got the animation working in one direction. I know it probably uses a callback function to toggle back but I don't have enough experience to get it working properly.
JSFiddle
//Creates Snap object
var paper = Snap("#search");
//Creates lines
var top = paper.path("M42.5,104.7L98.3,44c0,0,8.9-11.2,21.2-2.5c17.5,12.3,19.2,56-7.7,66.4c-6.8,2.7-12.7-3.2-12.7-3.2L80.8,85.7l0,0c-4,4.3-9.7,6.9-16.1,6.9c-12.2,0-22.1-9.9-22.1-22.1c0-1.3,0.1-2.6,0.3-3.9").attr({fill:"none", strokeWidth:"10px", stroke:"#b7b7b7",strokeLinecap:"round", strokeLinejoin:"round", strokeMiterlimit:"10", strokeDashoffset:"0",strokeDasharray:"80, 220"});
var bottom = paper.path("M43,66.7c1.8-10.3,10.9-18.2,21.7-18.2c12.2,0,22.1,9.9,22.1,22.1c0,5.8-2.3,11.1-5.9,15.1c0,0-16.9,15-49.8,4.3C9.8,83.2,5.8,57.3,17.1,46.4C30.8,32.9,42,45.6,42,45.6l57.2,59.3").attr({fill:"none", strokeWidth:"10px", stroke:"#b7b7b7",strokeLinecap:"round", strokeLinejoin:"round", strokeMiterlimit:"10",strokeDashoffset:"81",strokeDasharray:"80, 220"});
//Animates on click
paper.click(function(){Snap.animate(0,-200, function(value){top.attr({'strokeDashoffset': value })},300, mina.easein );});
paper.click(function(){Snap.animate(0,-300, function(value){bottom.attr({'strokeDashoffset': value })},300, mina.easein);});

The simplest solution would probably just involve toggling between two different states. My implementation looked like:
var toggle = true;
paper.click(function() {
if (toggle) {
toggleTo();
toggle = false;
} else {
toggleFrom();
toggle = true;
}
});
function toggleTo() {
Snap.animate(0,-200, function(value){top.attr({'strokeDashoffset': value })},300, mina.easein );
Snap.animate(0,-300, function(value){bottom.attr({'strokeDashoffset': value })},300, mina.easein);
}
function toggleFrom() {
Snap.animate(-200, 0, function(value){top.attr({'strokeDashoffset': value })},300, mina.easein );
Snap.animate(-300, 81, function(value){bottom.attr({'strokeDashoffset': value })},300, mina.easein);
}
To start, I set a boolean variable toggle to true. Then on the click event for the SVG you created, I check which state the element is in (based on the boolean value) and then call the appropriate function to animate the SVG.
Edit: Here's the JSFiddle.
Here's a similar Stack Overflow question.

Related

YUI3 - Update CSS class based on XY position

I am using the drag and drop to be able to move one of my nodes within a page. What I want to be able to do is once the drag and drop is completed, get the XY position and if it is outside a certain position (XY), then the class applied to the node should be updated.
http://jsfiddle.net/gabrielesandoval/ab1cjrcj/
CSS:
.dd-demo-inside {
background-color: #8DD5E7;
color: #000;
}
.dd-demo-outside {
background-color: #004C6D;
}
JS:
YUI().use('dd-constrain', function(Y) {
var dd1 = new Y.DD.Drag({
node: '#dd-demo-1'
}).plug(Y.Plugin.DDConstrained, {
constrain2node: '#dd-demo-canvas1'
});
});
So in the JS fiddle example above, if the box moves to the outer color, then the CSS applied to "dd-demo-1" should change from .dd-demo-inside to .dd-demo-outside.
I know YUI has a getXY() function but I wasnt sure how the best way to use it or what event it can be used on to make sure it is called once the dragging of the node is completed.
Any help you can provide would be much appreciated.
You can use the Node.inRegion method to test if the node is inside another node, passing true as the second parameter will ensure that it is fully inside the target region.
http://jsfiddle.net/ab1cjrcj/16/
YUI().use('dd-constrain', function(Y) {
var dragNode = Y.one('#dd-demo-1'),
innerCanvasNode = Y.one('#dd-demo-canvas3'),
dd1;
dd1 = new Y.DD.Drag({
node: dragNode
}).plug(Y.Plugin.DDConstrained, {
constrain2node: '#dd-demo-canvas1'
});
dd1.on('drag:end', function(e){
if (dragNode.inRegion(innerCanvasNode, true)){
dragNode.replaceClass('dd-demo-outside', 'dd-demo-inside');
} else {
dragNode.replaceClass('dd-demo-inside', 'dd-demo-outside');
}
//console.log(dragNode.inRegion(innerCanvasNode, true));
});
});
So i updated my own code. I subscirbed to the drag:end event--
dd1.on('drag:end', getOffsetTop);
Then I make a pure JS function that just checks for the offsetTop and offsetLeft. I think I should be able to create my own condition based on these values to change the class names.
Once you release you can call a function that uses javascript to retrieve the X and Y offset and update the class accordingly. Assuming you're using jQuery it could look something like this.
var offset = $(element).offset();
if(offset.left > x && offset.top > y) {
element.addClass(newClass);
}
I built onto your JSFiddle to show how it would work http://jsfiddle.net/ab1cjrcj/12/

jQuery Accordion | Open first element on pageload & active state confusion

I am using the Javascript below to animate an accordion (it's a slightly modified variant of the one explained here: http://tympanus.net/codrops/2010/04/26/elegant-accordion-with-jquery-and-css3/.
Now I wanted to have the first element to be open on pageload, so I figured I just give it some sort of extra-class via Javascript (and define that .active state via CSS) to have it open up.
This worked, however if I hover over any but the first-element with said .active class, the first element keeps its state, and stays open until I hover over it at least once.
So, what I want is: the first element of my accordion is open and collapses if the user hovers over any of the elements that are not the first. I think I need to add a line in the hover function to either take the class away of the first element or to give the new element the active state, but I don't know how to do it and keep breaking the thing.
<script type="text/javascript">
jQuery(function() {
activeItem = jQuery("#accordion li:first");
jQuery(activeItem).addClass('active');
jQuery('#accordion > li, #accordion > li.heading').hover(
function () {
var jQuerythis = jQuery(this);
jQuerythis.stop().animate({'height':'280px'},500);
jQuery('.heading',jQuerythis).stop(true,true).fadeOut();
jQuery('.bgDescription',jQuerythis).stop(true,true).slideDown(500);
jQuery('.description',jQuerythis).stop(true,true).fadeIn();
},
function () {
var jQuerythis = jQuery(this);
jQuerythis.stop().animate({'height':'40px'},1000);
jQuery('.heading',jQuerythis).stop(true,true).fadeIn();
jQuery('.description',jQuerythis).stop(true,true).fadeOut(500);
jQuery('.bgDescription',jQuerythis).stop(true,true).slideUp(700);
}
);
});
</script>
Looks like this is happening because each accordion item has its own hover event that takes care of its own animation. You can refactor the code slightly to make this easier to understand and reuse:
var activeItem = jQuery("#accordion li:first");
jQuery('#accordion > li, #accordion > li.heading').hover(
function () { hoverMe(jQuery(this)); },
function () { unhoverMe(jQuery(this)); }
);
//This gets called when cursor hovers over any accordion item
var hoverMe = function(jQuerythis) {
//If the first item is still active
if (activeItem) {
contract(activeItem); //...Shrink it!
activeItem = false;
}
//Expand the accordion item
expand(jQuerythis);
};
//This gets called when cursor moves out of accordion item
var unhoverMe = function(jQuerythis) {
contract(jQuerythis);
};
//I have moved the hover animation out into a separate function, so we can call it on page load
var expand = function(jQuerythis) {
jQuerythis.stop().animate({'height':'280px'},500);
jQuery('.heading',jQuerythis).stop(true,true).fadeOut();
jQuery('.bgDescription',jQuerythis).stop(true,true).slideDown(500);
jQuery('.description',jQuerythis).stop(true,true).fadeIn();
};
//I have moved the unhover animation out into a separate function, so we can contract the first active item from hoverMe()
var contract = function() {
jQuerythis.stop().animate({'height':'40px'},1000);
jQuery('.heading',jQuerythis).stop(true,true).fadeIn();
jQuery('.description',jQuerythis).stop(true,true).fadeOut(500);
jQuery('.bgDescription',jQuerythis).stop(true,true).slideUp(700);
};
//Now expand the first item
expand(activeItem);
I have put together a simplified version demonstrating the logic. Please let me know how you get on.

combining a method to hide or show content

I was wondering how I can combine a hide function and show function into 1 toggle Function that either fades in content or fades it out, im guessing this argument would update the fade method:
This is my current effort of JS using jQuery from my object but is totally wrong:
toggleAlertOverlay: function (state) {
var instance = this;
if (state === hide) {
instance.selector.fadeOut();
}
elseif(state === show) {
instance.selector.fadeIn();
}
},
toggleAlertOverlay(hide);
Try using .fadeToggle()
The .fadeToggle() method animates the opacity of the matched elements. When called on a visible element, the element's display style property is set to none once the opacity reaches 0, so the element no longer affects the layout of the page.
$(<element>).fadeToggle();
Where <element> is a valid selector ....
var toggleState = 'none';
toggleAllertOverlay: function()
{
if(toggleState == 'none')
{
$('#element').fadeIn();
toggleState == 'showing';
}
else
{
$('#element').fadeOut();
toggleState == 'none';
}
}
is just a concept based on what you have. Note the variable outside of the function. Its outside so it doesn't get destroyed upon function complete. But aside from that, theres various other methods you can try if this doesn't suit you. Up to and including jQuery toggle()

jQuery animate issue

My function looks like that
$.fn.animateHighlight = function(highlightColor, originalColor, type, duration) {
var highlightBg = highlightColor || "#FFFF9C";
var animateMs = 500;
var animateVal1 = {};
animateVal1[type] = highlightColor;
var animateVal2 = {};
animateVal2[type] = originalColor;
this.stop().animate(animateVal1, animateMs).delay(duration).animate(animateVal2, animateMs);
};
Calling this like that
$("#variants").animateHighlight("red", "#9c9c9c", "borderColor", 3000);
The problem is,
This is default border color of fieldset
And this is after animation color
I know that animate adds extra style attribute to element. What I wanna do is, return back original fieldset border color (removing style attribute will return back original border color).
Tried to change last line of function to this
this.stop().animate(animateVal1, animateMs).delay(duration).animate(animateVal2, animateMs).removeAttribute('style');
Animate didn't even start.
How can I animate and reset back to original version after flashing?
this.stop().animate(animateVal1, animateMs).delay(duration).animate(animateVal2, animateMs, function() {
this.removeAttribute('style');
});
I think the two method for this.
1.If you used border color in stye attribute for VariantDiv.Default Styles will be lost in style when you remove style attribute. So you should hold first border color.
For Example :
http://jsfiddle.net/tEwa9/
2.if when you don't use style you can do this way.
You can call this code when animate completed.
$(this).attr('style','');
For example:
http://jsfiddle.net/xSYWS/

Webkit transitionEnd event grouping

I have a HTML element to which I have attached a webkitTransitionEnd event.
function transEnd(event) {
alert( "Finished transition!" );
}
var node = document.getElementById('node');
node.addEventListener( 'webkitTransitionEnd', transEnd, false );
Then I proceed to change its CSS left and top properties like:
node.style.left = '400px';
node.style.top = '400px';
This causes the DIV to move smoothly to the new position. But, when it finishes, 2 alert boxes show up, while I was expecting just one at the end of the animation. When I changed just the CSS left property, I get one alert box - so this means that the two changes to the style are being registered as two separate events. I want to specify them as one event, how do I do that?
I can't use a CSS class to apply both the styles at the same time because the left and top CSS properties are variables which I will only know at run time.
Check the propertyName event:
function transEnd(event) {
if (event.propertyName === "left") {
alert( "Finished transition!" );
}
}
var node = document.getElementById('node');
node.addEventListener( 'webkitTransitionEnd', transEnd, false );
That way, it will only fire when the "left" property is finished. This would probably work best if both properties are set to the same duration and delay. Also, this will work if you change only "left", or both, but not if you change only "top".
Alternatively, you could use some timer trickery:
var transEnd = function anon(event) {
if (!anon.delay) {
anon.delay = true;
clearTimeout(anon.timer);
anon.timer = setTimeout(function () {
anon.delay = false;
}, 100);
alert( "Finished transition!" );
}
};
var node = document.getElementById('node');
node.addEventListener( 'webkitTransitionEnd', transEnd, false );
This should ensure that your code will run at most 1 time every 100ms. You can change the setTimeout delay to suit your needs.
just remove the event:
var transEnd = function(event) {
event.target.removeEventListener("webkitTransitionEnd",transEnd);
};
it will fire for the first property and not for the others.
If you prefer it in JQuery, try this out.
Note there is an event param to store the event object and use within the corresponding function.
$("#divId").bind('oTransitionEnd transitionEnd webkitTransitionEnd', event, function() {
alert(event.propertyName)
});
from my point of view the expected behaviour of the code would be to
trigger an alert only when the last transition has completed
support transitions on any property
support 1, 2, many transitions seamlessly
Lately I've been working on something similar for a page transition manager driven by CSS timings.
This is the idea
// Returs the computed value of a CSS property on a DOM element
// el: DOM element
// styleName: CSS property name
function getStyleValue(el, styleName) {
// Not cross browser!
return window.getComputedStyle(el, null).getPropertyValue(styleName);
}
// The DOM element
var el = document.getElementById('el');
// Applies the transition
el.className = 'transition';
// Retrieves the number of transitions applied to the element
var transitionProperties = getStyleValue(el, '-webkit-transition-property');
var transitionCount = transitionProperties.split(',').length;
// Listener for the transitionEnd event
function eventListener(e) {
if (--transitionCount === 0) {
alert('Transition ended!');
el.removeEventListener('webkitTransitionEnd', eventListener);
}
}
el.addEventListener('webkitTransitionEnd', eventListener, false);
You can test here this implementation or the (easier) jQuery version, both working on Webkit only
If you are using webkit I assume you are mobilizing a web-application for cross platform access.
If so have you considered abstracting the cross platform access at the web-app presentation layer ?
Webkit does not provide native look-and-feel on mobile devices but this is where a new technology can help.

Categories

Resources