My purpose is to make an image blink 3 times on a scroll (like lights on, then off, 3 times in a row with a 1 sec delay), then stay on until user scrolls down more that 3600px.
I've added event listener:
created() {
window.addEventListener('scroll', this.scrollAnimation)
}
On scroll i fire method scrollAnimation:
methods: {
scrollAnimation() {
let currentPos = window.pageYOffset
if (currentPos > 3000 && currentPos < 3600) {
this.$refs.supportOff.style.display = 'none'
this.$refs.supportOn.style.display = 'block'
} else {
this.$refs.supportOff.style.display = 'block'
this.$refs.supportOn.style.display = 'none'
}
}
}
And here's the template for the images:
<div class="support__image-wrapper">
<img ref="supportOff" class="support__image support__image_on" src="../../assets/images/247-off.png">
<img ref="supportOn" class="support__image support__image_off" src="../../assets/images/247-on.png">
</div>
Now this code works, when i scroll 3000 pixels down, but not lower than 3600 pixels, it shows 247-on image and hides 247-off image. But, there's an issues with blinking it, if i will use setInterval it's going to be fired every time a user scrolls between 3000 and 3600 px. What's the best way to achieve that blinking?
A few things to try...
Don't start manipulating the dom with $ref
Instead, create a variable which will trigger changes in the dom
methods: {
scrollAnimation() {
this.showSupport = window.pageYOffset > 3000 && window.pageYOffset < 3600
}
}
<div>
<img v-if="showSupport" class="blink" src="../../assets/images/247-on.png">
<img v-else src="../../assets/images/247-off.png">
</div>
Blinking I would advise using css animations (#keyframes). This way you can control the timing of the blink without anything in the script. It would just start blinking as soon as it's visible on the page.
.blink {
animation: blink 1s infinite;
}
#keyframes blink {
0% {opacity: 0}
49%{opacity: 0}
50% {opacity: 1}
}
Hope this helps.
Just wanted to add a quick demo for future readers, based on t3__rry's comment on how scroll based events which are not optimized/debounced can lead to serious performance issue; as well as Mulhoon's nice advice on utilizing CSS #keyframes for blinking animation:
new Vue({
el: '#app',
data() {
return {
blinkRate: 1000,
blinkCount: 3,
blinking: false,
blinkTimeoutId: -1,
state: false,
currentPos: window.pageYOffset
}
},
mounted() {
window.addEventListener('scroll', _.debounce(() => {
this.currentPos = window.pageYOffset;
if (this.currentPos > 3000 && this.currentPos < 3600) {
this.state = true;
}
else {
this.state = false;
}
}), 100);
},
methods: {
blink() {
if (this.blinkTimeoutId > -1) {
clearTimeout(this.blinkTimeoutId);
}
this.blinking = true;
this.blinkTimeoutId = setTimeout(() => {
this.blinking = false;
}, 1000 * this.blinkCount);
}
},
watch: {
state() {
this.blink();
}
}
});
#app {
background-color: gainsboro;
height: 2000vh;
padding: 10px;
}
.page-offset {
position: fixed;
top: 20px;
right: 20px;
}
.blinker > div {
border-radius: 50%;
border: 2px solid white;
color: white;
font-weight: bold;
height: 35px;
left: 20px;
line-height: 35px;
padding: 5px;
position: fixed;
text-align: center;
top: 20px;
vertical-align: middle;
width: 35px;
}
.blinker.animate > div {
animation: blink 1s infinite;
}
.blinker .on {
background-color: green;
}
.blinker .off {
background-color: crimson;
}
#keyframes blink {
0% {
opacity: 0
}
49% {
opacity: 0
}
50% {
opacity: 1
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
<div id="app">
<div :class="['blinker', { 'animate': blinking } ]">
<div class="on" v-if="state">ON</div>
<div class="off" v-else>OFF</div>
</div>
<code class="page-offset">pageYOffset: {{Math.floor(currentPos)}}</code>
</div>
Related
I learning coding HTML and CSS, I'm practicing this project on this website:https://backstagetalks.com/#issue5.
I'm wondering how can the background color of this website change so smoothly like that.
Long story short
You need to change CSS transition property:
body{
transition:background-color .2s ease-in-out;
}
This will animate/transition changing colors switched by "background-color" property.
If you are unsure of how the background color is changed you can do this:
body{
transition:all .2s ease-in-out;
}
This will animate/transition everything
You can use css transition property
Check the scrollTop and change the backgroundColor when scrollTop reach some number
const back = document.querySelector('.back')
back.addEventListener('scroll', (event) => {
if (back.scrollTop >= 1000 && back.scrollTop < 2000) {
back.style.backgroundColor = '#ffff00'
} else if (back.scrollTop >= 2000 && back.scrollTop < 3000) {
back.style.backgroundColor = '#00ff00'
} else if (back.scrollTop >= 3000 && back.scrollTop < 4000) {
back.style.backgroundColor = '#00ffff'
} else if (back.scrollTop >= 4000 && back.scrollTop < 5000) {
back.style.backgroundColor = '#0000ff'
} else {
back.style.backgroundColor = '#ff0000'
}
})
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
.back {
background-color: #ff0000;
height: 296px;
overflow-y: scroll;
transition: background-color 2s ease-in-out; /* Change 2s with delay */
}
.content {
height: 5000px;
overflow: hidden;
}
<div class="back">
<div class="content"></div>
</div>
I have created a marquee slider. After a few iterations the marquee lacks.
I want to have the marquee to have full view-width. As I can see the marquee is lacking less on smaller devices. I have created other similar marquees before with different elements and never had that problem. The empty list elements I have implemented to have a spacing between the text elements, but the icons should be next to the text as they are currently.
(function marquee() {
var marqueeWrapper = $('.js-marquee');
var FPS = (60/100); // 60fps
var SLIDESPEED = 1000; // default | lower is faster
marqueeWrapper.each(function (index, element) {
var _marqueeWrapper = $(element);
var _marqueeTracks = $('>ul', _marqueeWrapper);
var _marqueeSlides = $('>ul>li', _marqueeWrapper);
var _marqueeWidth = parseFloat(_marqueeSlides.last().position().left + _marqueeSlides.last().outerWidth(true));
var shifted = _marqueeWrapper.attr('data-marquee-shift') || false;
var SPEED = (_marqueeWrapper.attr('data-marquee-speed') * _marqueeSlides.length) || (SLIDESPEED * _marqueeSlides.length);
var frames = SPEED * FPS;
var steps = _marqueeWidth / frames; // distance elems will move each frames
var posX = 0;
var tempSteps;
function _clone() {
var times = Math.ceil(_marqueeWrapper.outerWidth(true) / _marqueeWidth) + 1;
_marqueeTracks.each(function () {
$('>ul', _marqueeWrapper).empty();
var sliders = _marqueeSlides;
for (i = 1; i <= times; i++) {
sliders.clone().appendTo(($(this)));
}
})
}
function _animated() {
posX += -steps;
_marqueeTracks.css({
transform: 'translate3d(' + posX + 'px, 0, 0)'
});
if (Math.abs(posX) >= _marqueeWidth) {
posX = 0;
}
window.requestAnimationFrame(_animated);
}
function _pause() {
tempSteps = steps;
return steps = 0;
}
function _resume() {
return steps = tempSteps;
}
function _shiftPosition() {
if(shifted) return posX = -(_marqueeSlides.first().outerWidth(true)) / 2 ;
}
/*
function _registerEvents() {
_marqueeTracks.on('mouseenter', _pause);
_marqueeTracks.on('mouseleave', _resume);
$(window).on('resize', debounce(_clone, 300))
}*/
function init() {
_shiftPosition()
_clone();
_animated();
/*_registerEvents();*/
}
function debounce(func, wait, immediate) {
var timeout;
return function () {
var context = this,
args = arguments;
var later = function () {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};// debounce
init();
})
})();
.marquee {
background: black;
width: 100%;
overflow: hidden;
}
.marquee__track {
display: flex;
padding: 0;
margin: 0;
list-style: none;
}
.marquee__item {
display: flex;
justify-content: center;
align-items: center;
color: white;
height: 100px;
margin-right: 10px;
padding-bottom: 10px;
}
.marquee__item {
height: 80px;
}
.marquee__item_vegan {
flex: 0 0 120px;
font-size: 50px;
font-family: Gothic821;
}
.marquee__item_gluten {
flex: 0 0 160px;
font-size: 50px;
font-family: Gothic821;
}
.marquee__item_natural {
flex: 0 0 130px;
font-size: 50px;
font-family: Gothic821;
}
.marquee__item_empty {
flex: 0 0 180px;
}
.marquee__item_small {
flex: 0 0 80px;
}
.marquee__item_icon {
width: 50px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div data-marquee-speed="100" data-marquee-shift="false" class="marquee js-marquee">
<ul class="marquee__track">
<li class="marquee__item marquee__item_small"><img class="marquee__item_icon" src="{{ 'Haken_weiss.svg' | asset_url }}"/></li>
<li class="marquee__item marquee__item_vegan">VEGAN</li>
<li class="marquee__item marquee__item_empty"></li>
<li class="marquee__item marquee__item_small"><img class="marquee__item_icon" src="{{ 'Haken_weiss.svg' | asset_url }}"/></li>
<li class="marquee__item marquee__item_gluten">GLUTENFREI</li>
<li class="marquee__item marquee__item_empty"></li>
<li class="marquee__item marquee__item_small"><img class="marquee__item_icon" src="{{ 'Haken_weiss.svg' | asset_url }}"/></li>
<li class="marquee__item marquee__item_natural">NATURAL</li>
<li class="marquee__item marquee__item_empty"></li>
</ul>
</div>
The given code seems rather complex for a simple marquee.
While you will need to invoke a little JS at the start (on a load or a resize) to make sure the timing is right for the speed required (it varies depending on viewport width) there seems little to be gained from using JS for the actual animation.
And there may be some loss in using JS which does not guarantee real-time timing (your system may be busy doing other things) and that can introduce lag. Using a CSS method can help to smooth things as there is less need to move back and forth between CPU and GPU.
The given code also performs the animation in steps, moving position, which can be jerkier than translating.
This snippet is stripped down to demonstrate the basic ideas in using HTML and CSS without JS.
As in the code in the question, a second copy of the items is made. This allows the marquee to appear continuous, as the first item disappears to the left it can be seen coming in from the right.
The units used are related to the viewport width so the whole thing is responsive. A CSS animation is used to get the movement, moving the whole 50% of its width, then when the copy comes in it is overwritten by the first part so the movement looks continuous (this is the same technique as used in the given code but with pure CSS rather than JS/CSS).
.marquee,
.marquee * {
margin: 0;
padding: 0;
border-width: 0;
}
.marquee {
background: black;
overflow: hidden;
}
.marquee>ul {
display: flex;
width: 200vw;
justify-content: space-around;
list-style: none;
animation: move 5s linear infinite;
white-space: nowrap;
}
.marquee>ul li {
font-size: 4vw;
color: white;
margin: 5vw;
position: relative;
}
.marquee__item_vegan,
.marquee__item_gluten,
.marquee__item_natural {
--bg: linear-gradient(white, white);
/* just for demo */
}
.marquee>ul li::before {
content: '';
width: 4vw;
height: 4vw;
left: -4vw;
display: inline-block;
position: absolute;
background-image: var(--bg);
background-repeat: no-repeat;
background-size: 80% 80%;
background-position: bottom left;
}
#keyframes move {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-50%);
}
}
<div class="marquee">
<ul>
<li class="marquee__item_vegan">VEGAN</li>
<li class="marquee__item_gluten">GLUTENFREI</li>
<li class="marquee__item_natural">NATURAL</li>
<li class="marquee__item_vegan">VEGAN</li>
<li class="marquee__item_gluten">GLUTENFREI</li>
<li class="marquee__item_natural">NATURAL</li>
</ul>
</div>
Notes: you may want to reinstate some JS to calculate the timing - depends on your use case.
The spurious list items have been removed. The imgs which are really just visual clues are added as before pseudo elements. The empty items are not needed. (It's best to keep formatting and content separate, especially for those using assistive technology such as screen readers).
I have a header component which gets the fixed class after user scrolls down a bit like this :
const [show, setShow] = React.useState(false);
const handleShow = () => {
if (typeof window !== "undefined") {
if (window.pageYOffset > 120) {
if (!show) {
console.log("set to true");
setShow(true);
}
}
if (window.pageYOffset < 120) {
console.log("set to false");
setShow(false);
}
}
};
React.useEffect(() => {
if (typeof window !== "undefined") {
window.addEventListener("scroll", handleShow);
}
return () => {
if (typeof window !== "undefined") {
window.removeEventListener("scroll", handleShow);
}
};
}, []);
return (
<div
className={show ? classes.fixed : ""}
style={{
background: bgColor,
boxShadow: "0 0 10px #ccc",
}}
>
<div className="d-flex justify-content-center">
<div>Header</div>
</div>
</div>
And the css code for fixed class is :
.fixed {
position: fixed;
transition: all 0.3s;
z-index: 1000;
width: 100%;
}
It gets fixed to the top but the transition doesn't work and I want the header to get fixed at the top with some kind of animation .
How can I animate with position fixed for a div ?
You can add lazy moving animation. Instead of using position: fixed; use position: absolute and on every scroll end of window capture the scroll delta and set it on div. Pseudocode is as follow:
Append scroll event
On every scroll end capture the Y scroll position.
Now set that captured Y scroll position on that div (header) who have position: absolute;
If this sounds confusing to you then let me know then I can share the code as well.
Hopes this helps :)
I solved the issue with applying to classes to the div :
<div
className={
show ? `${classes.navbar} ${classes.navbarSticky}` : classes.navbar
}
style={{
background: bgColor,
boxShadow: "0 0 10px #ccc",
}}
>
<div className="d-flex justify-content-between p-4">
<div>{left}</div>
<div>{right}</div>
</div>
{bottom}
</div>
and the styles are :
.navbar {
position: absolute;
z-index: 1;
width: 100%;
background: red;
}
.navbarSticky {
background: #333;
position: fixed;
top: 0;
left: 0;
animation: moveDown 0.5s ease-in-out;
}
#keyframes moveDown {
from {
transform: translateY(-5rem);
}
to {
transform: translateY(0rem);
}
}
#keyframes rotate {
0% {
transform: rotateY(360deg);
}
100% {
transform: rotateY(0rem);
}
}
I'm being tormented in the past 4 hours to find out how to do this, I don't know what I'm doing wrong, I have a page with multiple layers, I wish to trigger some transition when the needed page has opacity 1, it should be simple when u think of it, here is my code, please help ;)
slide1 = document.querySelector('.slide1');
function videoPlay() {
var videoOne = document.getElementById('myVideo');
if ((slide1.style.opacity) > 0 ) {
videoOne.play();
}
}
videoPlay();
.slide {
width: 100%;
background-size: cover;
background-position: center;
position: absolute;
}
.slide1 {
width: 100%;
background: none;
opacity: 0;
}
<div class="slide slide1">
<div class="slide-content">
<div class="secondColumn">
<video muted id="myVideo">
<source src="Media/Acqua.mp4" type="video/mp4">
</video>
<div class="lowerTab"></div>
</div>
</div>
here is the code which i use to change the opacity using the wheel :
//wheel event
document.addEventListener('wheel',
function scrollWheel(event) {
var fig =event.deltaY;
if (fig > 0) {
slideMove();
}
else if (fig<0) {
slideMovReverse();
}
})
//basic movement
function slideMove() {
if (current === sliderImages.length-1 ) {
current = -1
}
reset();
sliderImages[current+1].style.transition = "opacity 1s ease-in 0s";
sliderImages[current+1].style.opacity= "1.0";
current++;
}
You can use the transitionend event, but you'd have to set up the transition first. As it sits now, there's not much information in your question about the different slides, how the transitions are set up, etc. Here's a baseline to give you an idea:
const slide1 = document.querySelector('.slide1');
const videoEl = document.querySelector('.slide1__video');
const button = document.querySelector('button');
let inView = false;
slide1.addEventListener('transitionend', () => {
let content = 'Playing';
if (inView) {
content = ''
}
videoEl.textContent = content;
inView = !inView;
})
button.addEventListener('click', () => {
slide1.classList.toggle('active')
})
.slide1 {
transition: opacity 500ms linear;
opacity: 0;
border: 1px solid green;
padding: 10px;
margin-bottom: 24px
}
.slide1.active {
opacity: 1
}
<div class="slide1">
Slide 1
<div class="slide1__video"></div>
</div>
<button>Next</button>
Edit
It'll need some love but I think it's in the right direction to what you're after.
const slides = Array.from(document.querySelectorAll('.slide'));
document.addEventListener('wheel', onScroll);
const SCROLL_TOLERANCE = 100;
let currentIndex = 0;
let currentScroll = 0;
function onScroll(e) {
if (e.deltaY > 0) {
currentScroll += 1;
} else {
currentScroll -= 1;
}
if (currentScroll >= (currentIndex * SCROLL_TOLERANCE) + 15) {
showNext();
} else if (currentScroll <= (currentIndex * SCROLL_TOLERANCE) - 15) {
showPrevious();
}
}
function showNext() {
if (currentIndex === slides.length - 1) {
return console.warn('At the end.');
}
currentIndex += 1;
setSlide();
}
function showPrevious() {
if (currentIndex === 0) {
return console.warn('At the beginning.');
}
currentIndex -= 1;
setSlide();
}
function setSlide() {
let newOpacity = 0;
slides.forEach(slide => {
if (+slide.dataset.index === currentIndex) {
newOpacity = 1
} else {
newOpacity = 0;
}
slide.style.opacity = newOpacity;
slide.addEventListener('transitionend', () => {
console.log('Done transitioning!');
// Do things here when the transition is over.
})
});
}
html,
body {
padding: 0;
margin: 0;
font-family: sans-serif;
font-size: 18px
}
.slide {
border: 3px solid #efefef;
position: absolute;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
transition: all 500ms linear;
opacity: 0;
transition-delay: 250ms;
}
.slide.active {
opacity: 1;
}
<div class="slide active" data-index="0">
Slide 1
</div>
<div class="slide" data-index="1">
Slide 2
</div>
<div class="slide" data-index="2">
Slide 3
</div>
<div class="slide" data-index="3">
Slide 4
</div>
So, I've got this -webkit-animation rule:
#-webkit-keyframes shake {
0% {
left: 0;
}
25% {
left: 12px;
}
50% {
left: 0;
}
75% {
left: -12px;
}
100% {
left:0;
}
}
And some CSS defining some of the animation rules on my box:
#box{
-webkit-animation-duration: .02s;
-webkit-animation-iteration-count: 10;
-webkit-animation-timing-function: linear;
}
I can shake the #box like this:
document.getElementById("box").style.webkitAnimationName = "shake";
But I can't shake it again later.
This only shakes the box once:
someElem.onclick = function(){
document.getElementById("box").style.webkitAnimationName = "shake";
}
How can I re-trigger a CSS animation via JavaScript without using timeouts or multiple animations?
I found the answer based on the source code and examples at the CSS3 transition tests github page.
Basically, CSS animations have an animationEnd event that is fired when the animation completes.
For webkit browsers this event is named “webkitAnimationEnd”. So, in order to reset an animation after it has been called you need to add an event-listener to the element for the animationEnd event.
In plain vanilla javascript:
var element = document.getElementById('box');
element.addEventListener('webkitAnimationEnd', function(){
this.style.webkitAnimationName = '';
}, false);
document.getElementById('button').onclick = function(){
element.style.webkitAnimationName = 'shake';
// you'll probably want to preventDefault here.
};
and with jQuery:
var $element = $('#box').bind('webkitAnimationEnd', function(){
this.style.webkitAnimationName = '';
});
$('#button').click(function(){
$element.css('webkitAnimationName', 'shake');
// you'll probably want to preventDefault here.
});
The source code for CSS3 transition tests (mentioned above) has the following support object which may be helpful for cross-browser CSS transitions, transforms, and animations.
Here is the support code (re-formatted):
var css3AnimationSupport = (function(){
var div = document.createElement('div'),
divStyle = div.style,
// you'll probably be better off using a `switch` instead of theses ternary ops
support = {
transition:
divStyle.MozTransition === ''? {name: 'MozTransition' , end: 'transitionend'} :
// Will ms add a prefix to the transitionend event?
(divStyle.MsTransition === ''? {name: 'MsTransition' , end: 'msTransitionend'} :
(divStyle.WebkitTransition === ''? {name: 'WebkitTransition', end: 'webkitTransitionEnd'} :
(divStyle.OTransition === ''? {name: 'OTransition' , end: 'oTransitionEnd'} :
(divStyle.transition === ''? {name: 'transition' , end: 'transitionend'} :
false)))),
transform:
divStyle.MozTransform === '' ? 'MozTransform' :
(divStyle.MsTransform === '' ? 'MsTransform' :
(divStyle.WebkitTransform === '' ? 'WebkitTransform' :
(divStyle.OTransform === '' ? 'OTransform' :
(divStyle.transform === '' ? 'transform' :
false))))
//, animation: ...
};
support.transformProp = support.transform.name.replace(/([A-Z])/g, '-$1').toLowerCase();
return support;
}());
I have not added the code to detect “animation” properties for each browser. I’ve made this answer “community wiki” and leave that to you. :-)
You have to first remove the animation, then add it again. Eg:
document.getElementById("box").style.webkitAnimationName = "";
setTimeout(function ()
{
document.getElementById("box").style.webkitAnimationName = "shake";
}, 0);
To do this without setTimeout remove the animation during onmousedown, and add it during onclick:
someElem.onmousedown = function()
{
document.getElementById("box").style.webkitAnimationName = "";
}
someElem.onclick = function()
{
document.getElementById("box").style.webkitAnimationName = "shake";
}
Following the suggestion from https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations/Tips, remove and then add the animation class, using requestAnimationFrame to ensure that the rendering engine processes both changes. I think this is cleaner than using setTimeout, and handles replaying an animation before the previous play has completed.
$('#shake-the-box').click(function(){
$('#box').removeClass("trigger");
window.requestAnimationFrame(function(time) {
window.requestAnimationFrame(function(time) {
$('#box').addClass("trigger");
});
});
});
http://jsfiddle.net/gcmwyr14/5/
A simple but effective alternative:
HTML:
<div id="box"></div>
<button id="shake-the-box">Shake it!</button>
css:
#box{
background: blue;
margin:30px;
height:50px;
width:50px;
position:relative;
-moz-animation:shake .2s 0 linear 1;
-webkit-animation:shake .2s 0 linear 1;
}
#box.trigger{
display:table;
}
#-webkit-keyframes shake {
0% {
left: 0;
}
25% {
left: 12px;
}
50% {
left: 0;
}
75% {
left: -12px;
}
100% {
left:0;
}
}
#-moz-keyframes shake {
0% {
left: 0;
}
25% {
left: 12px;
}
50% {
left: 0;
}
75% {
left: -12px;
}
100% {
left:0;
}
}
jQuery:
$('#shake-the-box').click(function(){
$('#box').toggleClass('trigger');
});
Demo:
http://jsfiddle.net/5832R/2/
Issues:
I don't know if it works on Firefox, because the animation doesn't seem to work there...
Clone works pretty good on paused Karaoke:
On IE11 had to force a reflow (R. Krupiński's shorter version).
$('#lyrics').text("Why does it hurt when I pee?");
changeLyrics('3s');
function changeLyrics(sec) {
str = 'lyrics '+ sec + ' linear 1';
$('#lyrics').css( 'animation', str);
$('#lyrics').css( 'animation-play-state', 'running' );
$('#lyrics').replaceWith($('#lyrics').clone(true));
}
or you can use the following:
function resetAnimation(elm) {
$('#'+elm).replaceWith($('#'+elm).clone(true));
}
Reset the value first. Use reflow to apply the change without using timeout:
function shake() {
var box = document.getElementById("box");
box.style.animationName = null;
box.offsetHeight; /* trigger reflow */
box.style.animationName = "shake";
}
#keyframes shake {
0% { left: 0; }
25% { left: 12px; }
50% { left: 0; }
75% { left: -12px; }
100% { left: 0; }
}
#box {
position: absolute;
width: 75px; height: 75px;
background-color: black;
animation-duration: .02s;
animation-iteration-count: 10;
animation-timing-function: linear;
}
button {
position: absolute;
top: 100px;
}
<div id="box"></div>
<button onclick="shake()">Shake</button>
In contrast to the accepted answer that recommends animationEnd, this method resets the animation even when it's still in progress. This might be or might be not what you want.
An alternative would be to create a duplicate #keyframes animation and switch between the two:
function shake() {
var box = document.getElementById("box");
if (box.style.animationName === "shake")
box.style.animationName = "shake2";
else
box.style.animationName = "shake";
}
#keyframes shake {
0% { left: 0; }
25% { left: 12px; }
50% { left: 0; }
75% { left: -12px; }
100% { left: 0; }
}
#keyframes shake2 {
0% { left: 0; }
25% { left: 12px; }
50% { left: 0; }
75% { left: -12px; }
100% { left: 0; }
}
#box {
position: absolute;
width: 75px; height: 75px;
background-color: black;
animation-duration: .02s;
animation-iteration-count: 10;
animation-timing-function: linear;
}
button {
position: absolute;
top: 100px;
}
<div id="box"></div>
<button onclick="shake()">Shake</button>
Is there an issue with using setTimeout() to remove the class and then read it 5ms later?
svg.classList.remove('animate');
setTimeout(function() {
svg.classList.add('animate');
}, 10);
With your javascript, you could also add (and then remove) a CSS class in which the animation is declared. See what I mean ?
#cart p.anim {
animation: demo 1s 1; // Fire once the "demo" animation which last 1s
}
1) Add animation name to the #box.trigger in css
#box.trigger{
display:table;
animation:shake .2s 0 linear 1;
-moz-animation:shake .2s 0 linear 1;
-webkit-animation:shake .2s 0 linear 1;
}
2) In java-script you cannot remove the class trigger.
3) Remove the the class name by using setTimeOut method.
$(document).ready(function(){
$('#shake-the-box').click(function(){
$('#box').addClass('trigger');
setTimeout(function(){
$("#box").removeClass("trigger")},500)
});
});
4) Here is the DEMO.