Initial state of element to be animated in - javascript

I have an element that i would like off screen to begin with, but then on click of a link, that element gets animated in (using animate.css). But, i'm not sure what css method to use to hide that element off screen so it can be animated in.
The js i'm using is:
$('.services-wrapper').on('click','.services-panel__cta',function(e){
e.preventDefault();
$('.services-panel__secondary').addClass('animated bounceInright');
})
And i have tried doing:
position: absolute;
left: 100%
and
left: -9999px
But i'm not sure that even makes sense to try tbh.
Any help really gratefully received!

With animate.css, you don't need to specify the position beforehand. You can hide it with display: none; and then add an additional class that adds display: block;.
JS Fiddle
CSS
.services-panel__secondary {
display: none;
}
.show {
display: block;
}
JS
$('.services-wrapper').on('click', '.services-panel__cta', function() {
$('.services-panel__secondary').addClass('show animated bounceInRight');
})
Or just use show() instead of adding the class:
JS Fiddle
$('.services-wrapper').on('click', '.services-panel__cta', function() {
$('.services-panel__secondary').show().addClass('animated bounceInRight');
});
And Lastly
If you can edit the html directly, you can add the animate.css classes directly and just show() the element:
JS Fiddle
Add classes in html and hide with display: block;
<div class="services-panel__secondary animated bounceInRight">
Bounce this in
</div>
JQuery- Simply show it and it will bounce in.
$('.services-wrapper').on('click', '.services-panel__cta', function() {
$('.services-panel__secondary').show();
})
IMPORTANT:
With animate.css, notice that "right" should have an uppercase "R" like bounceInRight

animate.css actually takes care of this for you with it's animations. Check out the source of bounceInRight (which you are using). As you can see, it moves the x-value around using transform: trasnslate3d(...). As mentioned by
#dwreck08 (+1), you only need to worry about hide/show.
#keyframes bounceInRight {
from, 60%, 75%, 90%, to {
-webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
}
from {
opacity: 0;
-webkit-transform: translate3d(3000px, 0, 0);
transform: translate3d(3000px, 0, 0);
}
60% {
opacity: 1;
-webkit-transform: translate3d(-25px, 0, 0);
transform: translate3d(-25px, 0, 0);
}
75% {
-webkit-transform: translate3d(10px, 0, 0);
transform: translate3d(10px, 0, 0);
}
90% {
-webkit-transform: translate3d(-5px, 0, 0);
transform: translate3d(-5px, 0, 0);
}
to {
-webkit-transform: none;
transform: none;
}
}

A solution which allows animating in and out
This example code comes from Animate.css's own documentation. I have expanded on it to include adding and removing a show class, which will maintain state once the animation is complete.
const animateCSS = (element, animation, prefix = 'animate__') => {
// Create a Promise and return it
new Promise((resolve, reject) => {
const animationName = `${prefix}${animation}`;
const node = document.querySelector(element);
// Add class to display element when animating in
if (animation.indexOf('In') >= 0)
node.classList.add('show');
node.classList.add(`${prefix}animated`, animationName);
// When the animation ends, we clean the classes and resolve the Promise
function handleAnimationEnd(event) {
event.stopPropagation();
// Remove class to display element when animating out
if (animation.indexOf('Out') >= 0)
node.classList.remove('show');
node.classList.remove(`${prefix}animated`, animationName);
resolve('Animation ended');
}
node.addEventListener('animationend', handleAnimationEnd, { once: true });
});
}
Set initial styles to display: none and create a show class with display: block. Then call the method we created with the following:
animateCSS('.services-panel__secondary', 'bounceInright');

Related

animate.css does not trigger twice in a row [duplicate]

I'm trying to reproduce a specific animation each time a button is pressed.
Specifically, I'm using jQuery and the animate.css library for this effect: on button click, a class is added (two classes to be precise: fadeInDown animated) to the element I want to animate.
The animation does work correctly, but only once. Why is this?
Fiddle: http://jsfiddle.net/jqm4vjLj/2/
I want the animation to reset every time I click the button, even if it is halfway through completion from a previous click. It should also work whenever I click the button, not only once.
JS:
$("#button").click(function () {
$("#button").removeClass();
$("#button").addClass("fadeInDown animated");
});
Classes from animate.css:
#-webkit-keyframes fadeInDown {
0% {
opacity: 0;
-webkit-transform: translateY(-20px);
transform: translateY(-20px);
}
100% {
opacity: 1;
-webkit-transform: translateY(0);
transform: translateY(0);
}
}
#keyframes fadeInDown {
0% {
opacity: 0;
-webkit-transform: translateY(-20px);
-ms-transform: translateY(-20px);
transform: translateY(-20px);
}
100% {
opacity: 1;
-webkit-transform: translateY(0);
-ms-transform: translateY(0);
transform: translateY(0);
}
}
.fadeInDown {
-webkit-animation-name: fadeInDown;
animation-name: fadeInDown;
}
.animated {
-webkit-animation-duration: 1s;
animation-duration: 1s;
-webkit-animation-fill-mode: both;
animation-fill-mode: both;
}
A much safer approach will be use the animation end event like
$("#button").click(function() {
$("#button").addClass("fadeInDown animated").one('animationend webkitAnimationEnd oAnimationEnd', function() {
$("#button").removeClass();
});
});
#button {
height: 30px;
width: 120px;
border: 1px solid black;
}
.animated {
-webkit-animation-duration: 1s;
animation-duration: 1s;
}
#-webkit-keyframes fadeInDown {
0% {
opacity: 0;
-webkit-transform: translateY(-20px);
transform: translateY(-20px);
}
100% {
opacity: 1;
-webkit-transform: translateY(0);
transform: translateY(0);
}
}
#keyframes fadeInDown {
0% {
opacity: 0;
-webkit-transform: translateY(-20px);
-ms-transform: translateY(-20px);
transform: translateY(-20px);
}
100% {
opacity: 1;
-webkit-transform: translateY(0);
-ms-transform: translateY(0);
transform: translateY(0);
}
}
.fadeInDown {
-webkit-animation-name: fadeInDown;
animation-name: fadeInDown;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="button"></div>
See http://css-tricks.com/restart-css-animation/, which discusses this exact problem.
I think the cleanest solution is to wrap the functionality of removing the element and re-inserting it in the same position in the HTML tree in a global function. Then, when you want to restart an animation, you just remove the class, call the reset function (grabbing the newly-inserted element from the return value), and then add the animation class(es) back to start the animation again.
For example:
function reset($elem) {
$elem.before($elem.clone(true));
var $newElem = $elem.prev();
$elem.remove();
return $newElem;
} // end reset()
$("#button").click(function () {
var $this = $(this);
$this.removeClass();
$this = reset($this);
$this.addClass("fadeInDown animated");
});
http://jsfiddle.net/jqm4vjLj/6/
This solution provides maximum responsiveness, since the old in-progress animation is automatically ended when the animating element is destroyed, and the newly-inserted element's animation begins immediately.
The simple solution for me is to start reflow process before adding class.
You can do it for example by "change" width of element. I think reflow process is faster for browser then node reinserting. And its simpler in terms of coding.
In jQuery:
$(this)
.removeClass('myAnimation')
.width('auto') // the magic
.addClass('myAnimation')
You need to remove class after the animation is complete,
but directly using removeClass wont work, because it will not let animation complete, instead use setTimeout
see this http://jsfiddle.net/jqm4vjLj/4/
$("#button").click(function () {
$("#button").addClass("fadeInDown animated");
setTimeout(function(){ $("#button").removeClass("fadeInDown animated");},100)
});
Problem is it immediately remove class and then add class so I just added new div with button2 id and on its click it just remove class and then click again on button div it would animate.
so problem is you need to do it after completion of animation.
$("#button1").click(function () {
$("#button").removeClass();
});
fiddle is here: http://jsfiddle.net/jqm4vjLj/5/
maybe this is good solution for your problem:
https://jsfiddle.net/d2c16fk7/3/
In your javascript the classes .animation and .fadeInDown were added to the #button and after that you had these classes and you couldn't add it again.
This works for me, from the animate.css github:
animateCss: function (animationName) {
var animationEnd = 'webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend';
$(this).addClass('animated ' + animationName).one(animationEnd, function() {
$(this).removeClass('animated ' + animationName);
});
}
To reset the element we have to make css recalculate right ? So what I did was hide and then show the element. What I did was removeClass() and then hide() and then show() and addClass()
$("#button").click(function () {
$("#button").removeClass("fadeInDown animated").hide();
$("#button").show().addClass("fadeInDown animated");
});

How to apply an animation defined in CSS to a class on click?

I have a class which is just a simple green square. I have made a simple shaking animation but I have not managed to find a way to apply the animation on click. I have tried both jQuery and pure CSS solutions and nothing has worked so far.
The animation:
#keyframes hit {
40% {transform: scale(1,1);transform: rotateX(-20deg);transform: rotateY(20deg);transform:rotate(-5deg);}
60% {transform: scale(1.1,1.1);transform: rotateX(20deg);transform: rotateY(-20deg); }
And the class:
target-container {
animation-name: none;
animation-duration:0.3s;}
The closest I got to making it work was using this function:
function hitTarget() {
target.style.animationName="hit";
setTimeout(stopAnimation,300);
function stopAnimation() {
target.style.animationName="none";
}
}
target.addEventListener("click",function() {
hitTarget();},false);
A few issues with your code - not sure if they're a result of working them into the question, or part of your actual code. So let's go through them.
I think I had to fix some syntax errors in the CSS - missing closing } (bracket).
Also, to define multiple transforms just list all transforms in a single transform style. Like transform: rotate(2deg) scale(1.2).
Instead of passing an anonymous function which calls the hitTarget function, we'll pass the hitTarget function as the callback to the event listener.
Finally, instead of adding/removing the animation-name I'd recommend adding/removing a CSS class, which applies the animation.
Here it is all cleaned up and working:
function hitTarget(event) {
const animationClass = "withAnimation";
event.target.classList.add(animationClass);
setTimeout(stopAnimation, 300);
function stopAnimation() {
event.target.classList.remove(animationClass);
}
}
document.querySelector(".target-container").addEventListener("click", hitTarget, false);
#keyframes hit {
40% {
transform: scale(1, 1) rotateX(-20deg) rotateY(20deg) rotate(-5deg);
}
60% {
transform: scale(1.1, 1.1) rotateX(20deg) rotateY(-20deg);
}
}
.target-container {
width: 100px;
height: 100px;
background-color: green;
}
.withAnimation {
animation-name: hit;
animation-duration: 0.3s;
}
<div class="target-container"></div>

Sequentially add a class depending on data attribute

I have a couple of links that shows a number of divs, depending on data-attribute. When I click one of the links, and there is more than one div that has the corresponding attribute, I want the class to be added sequential to the div, making them load one at a time. Just like it does in ready function but I've messed it up in some way and can't figure it out.
$('.filter_link').on('click', function(e) {
e.preventDefault();
var linkAttr = $(this).attr('data-related');
$('.blurb-content').each(function() {
$(this).removeClass('flip');
if($(this).attr('data-dest') == linkAttr) {
// this does not work
setTimeout(function() {
$(this).addClass('flip'); //add class with delay
}, 500);
}
});
});
fiddle
Two Issues:
Inside of a setTimeout(), this will no longer refer to your element. For this reason, you'll need to declare it outside the setTimeout() as a variable, and use that variable inside the setTimeout() instead of $(this). More information on this can be found here.
When you iterate through items using .each() they are all happening essentially simultaneously. Your setTimeout() isn't attaching "sequentially", so they will all delay 500 and then fire at the same time. Change the 500 to be multiplied by the iteration of your .each() by giving the each function a parameter i, and doing 500 * i just as you did in your first each.
EDIT: As you can see, I've re-factored your code quite a bit. It now uses the function showBlurbs() to simply fade each one in (taking the parameter as a list of jQuery objects). It resolves the need for so many .each() loops, and the code is quite a bit cleaner.
A few other notes:
You can get a data attribute by using .data("xxx") instead of .attr("data-xxx")
Rather than looping through all items and checking their data attribute, you can simply select only ones where it matches by doing:
var linkAttr = $(this).data('related');
$blurbsToShow = $('.blurb-content[data-dest=' + linkAttr + ']');
$(document).ready(function() {
//Show ALL blurbs at the beginning
var timeouts = [];
var $allBlurbs= $(".blurb-content");
showBlurbs($allBlurbs);
//On click, show related blurbs
$('.filter_link').on('click', function(e) {
e.preventDefault();
var linkAttr = $(this).data('related');
var $relatedBlurbs = $('.blurb-content[data-dest=' + linkAttr + ']');
showBlurbs($relatedBlurbs);
});
function showBlurbs(blurbs) {
//Clear all pending animations
for (var i = 0; i < timeouts.length; i++) {
clearTimeout(timeouts[i]);
}
//Reset everything to hidden
timeouts = [];
$(".blurb-content").removeClass("flip");
//Sequentially trigger the "fade-in" for each blurb
blurbs.each(function(i) {
var $self = $(this);
timeouts.push(setTimeout(function() {
$self.addClass("flip");
}, 500 * i));
});
}
});
.blurb-content {
opacity: 0;
display: none;
}
.flip {
display: block;
-webkit-animation: fade-in-bottom 0.6s cubic-bezier(0.390, 0.575, 0.565, 1.000) both;
animation: fade-in-bottom 0.6s cubic-bezier(0.390, 0.575, 0.565, 1.000) both;
}
#-webkit-keyframes fade-in-bottom {
0% {
-webkit-transform: translateY(50px);
transform: translateY(50px);
op.blurb-content {
opacity: 0;
display: none;
}
.flip {
display: block;
-webkit-animation: fade-in-bottom 0.6s cubic-bezier(0.390, 0.575, 0.565, 1.000) both;
animation: fade-in-bottom 0.6s cubic-bezier(0.390, 0.575, 0.565, 1.000) both;
}
#-webkit-keyframes fade-in-bottom {
0% {
-webkit-transform: translateY(50px);
transform: translateY(50px);
opacity: 0;
}
100% {
-webkit-transform: translateY(0);
transform: translateY(0);
opacity: 1;
}
}
#keyframes fade-in-bottom {
0% {
-webkit-transform: translateY(50px);
transform: translateY(50px);
opacity: 0;
}
100% {
-webkit-transform: translateY(0);
transform: translateY(0);
opacity: 1;
}
}
acity: 0;
}
100% {
-webkit-transform: translateY(0);
transform: translateY(0);
opacity: 1;
}
}
#keyframes fade-in-bottom {
0% {
-webkit-transform: translateY(50px);
transform: translateY(50px);
opacity: 0;
}
100% {
-webkit-transform: translateY(0);
transform: translateY(0);
opacity: 1;
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
link1
link2
link3
link4
<div class="blurb-content" data-dest="1">content 1</div>
<div class="blurb-content" data-dest="1">content 1</div>
<div class="blurb-content" data-dest="2">content 2</div>
<div class="blurb-content" data-dest="3">content 3</div>
$('.filter_link').on('click', function(e) {
e.preventDefault();
var linkAttr = $(this).attr('data-related');
$('.blurb-content').each(function(i) {
var thisBlurb = $(this);
thisBlurb.removeClass('flip');
if(thisBlurb.attr('data-dest') == linkAttr) {
// this work
setTimeout(function() {
thisBlurb.addClass('flip'); //add class with delay
}, 500 * i );
}
});});
Copy paste your code and changed the $(this) of .blurb to the var thisBlurb assigned at the start of each loop.
The JavaScript keyword "this" is scoped to the function it's in. setTimeout uses a function so any "this" inside the setTimeout is no longer the element .blurb. "this" is now the function used by setTimeout :)
I also added parameter "i" in the function that the "each" loop is using. "i" is the index of each loop and will increase by 1 per iteration of the loop. Multiplying this index to the setTimeout millisecond parameter will make give you the effect you want.

Vue.js animations not working correctly

I am using Vue 2 and attempting to include CSS animations on elements that are created and destroyed frequently. Below is an example of my code:
export default {
name: 'MyElement',
methods: {
enterStart: function (el) {
console.log('about to enter');
el.classList.add('testing-enter');
},
enter: function (el) {
console.log('entered');
},
leaveStart: function (el) {
console.log('starting to leave!');
},
leave: function (el) {
console.log('leaving!');
},
}
};
.testing-enter {
animation: enter .2s;
}
.testing-leave {
animation: leave .2s;
}
#keyframes enter {
0% {
opacity: 0;
transform: scale(0);
}
100% {
opacity: 1;
transform: scale(1);
}
}
#keyframes leave {
0% {
opacity: 1;
transform: scale(1);
}
100% {
opacity: 0;
transform: scale(0);
}
}
<template>
<div>
<transition
#before-enter="enterStart"
#enter="enter"
#leave="leaveStart"
#leave-active="leave"
appear
>
<div>My element is here!</div>
</transition>
</div>
</template>
First off, none of this works unless I include appear in my <transition ...> element. I know that this makes the transition happen on initial rendering, but I want them to happen any time the element is created or destroyed.
Next, in my console. I can see enterStart and enter both run, but leaveStart and leave never run, even when the elements are destroyed. What am I doing wrong?
The element inside the transition needs a state (show or hide). Also your transition needs a name that must much the transition in the CSS and it should be named with
name="transitionName"
e.g:
new Vue({
el: "#app",
data: function() {
return {
showThisElement: false
}
},
methods: {
toggleShow: function() {
this.showThisElement = !this.showThisElement
}
}
});
.testing-enter-active {
animation: enter .2s;
}
.testing-leave-active {
animation: leave .2s;
}
#keyframes enter {
0% {
opacity: 0;
transform: scale(0);
}
100% {
opacity: 1;
transform: scale(1);
}
}
#keyframes leave {
0% {
opacity: 1;
transform: scale(1);
}
100% {
opacity: 0;
transform: scale(0);
}
}
<div id="app">
<div #click="toggleShow">Show/Hide</div>
<transition
name="testing">
<div v-if="showThisElement">My element is here!</div>
</transition>
</div>
In the codepen, click on 'show/hide' to toggle the transition.
http://codepen.io/anon/pen/WpZPJp
Problem solved!
So I took out the transition from the individual component and created a transition-group instead around the container component that rendered them.
Then, after a bit more reading I realized I wanted to add the mode="out-in" field to my transition-group so that the leaving components fully animate before the new ones are rendered.
I also looked at the HTML when the animations were supposed to be happening to see what classes Vue added. It looks like Vue added v-enter-active, v-enter-to, and v-leave-to. Instead of customizing any names I just stuck with those classes and added my animations to them in the styling.
Hopefully if anybody else wants a similar effect this helps them decrease their stress levels a bit...

jQuery animation of large image moving choppy

http://henrybuiltfurniture.com/new/furniture.php?p=wave-stool
I have a series of large images that I need to transition between smoothly (not necessarily with jquery - maybe I could use css3 somehow?) and I can't seem to do so with jQuery.
Here's the code that moves the document, effectively moving the image:
$('body').animate({scrollLeft: $("#limiter"+(viewing+1)).css("left")}, image_change_speed, 'easeOutCirc', function() {
//irrelevant code here
}
Any help is appreciated.
try adding this to your CSS style sheet:
.limiter * {
transition:all 1s ease-in-out;
-webkit-transform: translate3d(0, 0, 0);
-moz-transform: translate3d(0, 0, 0);
-ms-transform: translate3d(0, 0, 0);
-o-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
}
Basically, we're adding hardware acceleration and then smoothing effect with transition (you can adjust it at will, of course)
You should not use straight up jQuery for more demanding animations, as you mentioned. I would scrap jQuery alltogether or get a css3 animation plugin for it. Using vanilla javascript, this is a really simplified example of how to animate the images:
Say you have two images:
<img src="http://henrybuiltfurniture.com/new/images/6_5.jpg" class='bigimg img-1'>
<img src="http://henrybuiltfurniture.com/new/images/6_4.jpg" class='bigimg img-2'>
And some styles to put them at the right place:
.bigimg {
position: absolute;
width: 1920px;
height: 1080px;
transition: transform 2s;
}
.img-1 {
left: 0;
}
.img-2 {
left: 1920px;
}
Then you could easily animate by just changing the bigimg's transform:
var bigImages = document.querySelectorAll('.bigimg');
for(var i = 0; i < bigImages.length; i++) {
var image = bigImages[i];
image.style.transform = 'translateX(-1920px)';
}
Example on JSFiddle
Paul irish has a great article going through why it is better to animate transform rather than position: absolute with left and top attributes if you would like some further reading.

Categories