Clean way to programmatically use CSS transitions from JS? - javascript

As the title implies, is there a proper way to set some initial CSS properties (or class) and tell the browser to transition these to another value?
For example (fiddle):
var el = document.querySelector('div'),
st = el.style;
st.opacity = 0;
st.transition = 'opacity 2s';
st.opacity = 1;
This will not animate the opacity of the element in Chrome 29/Firefox 23. This is because (source):
[...] you’ll find that if you apply both sets of properties, one immediately
after the other, then the browser tries to optimize the property
changes, ignoring your initial properties and preventing a transition.
Behind the scenes, browsers batch up property changes before painting
which, while usually speeding up rendering, can sometimes have adverse
affects.
The solution is to force a redraw between applying the two sets of
properties. A simple method of doing this is just by accessing a DOM
element’s offsetHeight property [...]
In fact, the hack does work in the current Chrome/Firefox versions. Updated code (fiddle - click Run after opening the fiddle to run animation again):
var el = document.querySelector('div'),
st = el.style;
st.opacity = 0;
el.offsetHeight; //force a redraw
st.transition = 'opacity 2s';
st.opacity = 1;
However, this is rather hackish and is reported to not work on some android devices.
Another answer suggests using setTimeout so the browser has time to perform a redraw, but it also fails in that we don't know how long it will take for a redraw to take place. Guessing a decent number of milliseconds (30-100?) to ensure that a redraw occurred means sacrificing performance, unnecessarily idling in the hopes that the browser performs some magic in that little while.
Through testing, I've found yet another solution which has been working great on latest Chrome, using requestAnimationFrame (fiddle):
var el = document.querySelector('div'),
st = el.style;
st.opacity = 0;
requestAnimationFrame(function() {
st.transition = 'opacity 2s';
st.opacity = 1;
});
I assume that requestAnimationFrame waits until right before the beginning of the next repaint before executing the callback, hence the browser does not batch up the property changes. Not entirely sure here, but works nicely on Chrome 29.
Update: after further testing, the requestAnimationFrame method does not work very well on Firefox 23 - it seems to fail most of the time. (fiddle)
Is there a proper or recommended (cross-browser) way of achieving this?

There isn't a clean way at this moment (without using CSS Animations -- see the nearby answer by James Dinsdale for an example using CSS Animations). There is a spec bug 14617, which unfortunately wasn't acted upon since it was filed in 2011.
setTimeout does not work reliably in Firefox (this is by design).
I'm not sure about requestAnimationFrame -- an edit to the original question says it doesn't work reliably either, but I did not investigate. (Update: it looks like requestAnimationFrame is considered at least by one Firefox core developer to be the place where you can make more changes, not necessarily see the effect of the previous changes.)
Forcing reflow (e.g. by accessing offsetHeight) is a possible solution, but for transitions to work it should be enough to force restyle (i.e. getComputedStyle): https://timtaubert.de/blog/2012/09/css-transitions-for-dynamically-created-dom-elements/
window.getComputedStyle(elem).opacity;
Note that just running getComputedStyle(elem) is not enough, since it's computed lazily. I believe it doesn't matter which property you ask from the getComputedStyle, the restyle will still happen. Note that asking for geometry-related properties might cause a more expensive reflow.
More information on reflow/restyle/repaint: http://www.phpied.com/rendering-repaint-reflowrelayout-restyle/

The situation has changed since 2013, so here's a new answer:
You could use Web Animations. They are implemented natively in Chrome 36 and Firefox 40 and there is a polyfill for all the other browsers.
Example code:
var player = snowFlake.animate([
{transform: 'translate(' + snowLeft + 'px, -100%)'},
{transform: 'translate(' + snowLeft + 'px, ' + window.innerHeight + 'px)'}
], 1500);
// less than 1500ms later...changed my mind
player.cancel();

You shouldn't need too much JavaScript to achieve what you want, just use CSS keyframes and animations for the same effect.
div {
opacity: 0;
}
div.fadeIn {
-webkit-animation: fadeIn 2s forwards;
animation: fadeIn 2s forwards;
}
#keyframes fadeIn {
0% {opacity: 0;}
100% {opacity: 1;}
}
#-webkit-keyframes fadeIn {
0% {opacity: 0;}
100% {opacity: 1;}
}
As shown in this JsFiddle it works either from page load (with class already added) or when dynamically adding the class to trigger the animation.

<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script>
<script>
$(document).ready(function(){
$('a.hiw*').click(function(){
id = this.id;
dval = $('#'+id).attr('data-value');
if (dval == 0) {
$('a.hiw*').attr('data-value','0');
$( ".hiw-popup" ).remove();
$('#'+id).attr('data-value','1');
$('<div class="hiw-popup white-well run-animation hidden-xs"><div class="row text-center"><div class="col-sm-2 animation-hiw col-xs-2"><span class="glyphicon glyphicon-pencil hiw-icon1" style="background:#ffffff;">1</span><br/>block1</div><div class="col-sm-2 animation-hiw col-xs-2"><span class="glyphicon glyphicon-shopping-cart hiw-icon2">2</span><br/>BLOCK3</div><div class="col-sm-3 animation-hiw col-xs-3"><span class="glyphicon glyphicon-folder-open hiw-icon3">3</span><br/>BLOCK2</div><div class="col-sm-3 animation-hiw col-xs-3"><span class="glyphicon glyphicon-ok hiw-icon4">4</span><br/>BLOCK</div><div class="col-sm-2 animation-hiw col-xs-2"><span class="glyphicon glyphicon-arrow-down hiw-icon5">5</span><br/>BLOCK</div></div></div>').insertAfter('#'+id);
}else{
$('#'+id).attr('data-value','0');
$( ".hiw-popup" ).remove();
}
});
});
var ahiw = function(id){
dval = $('#'+id).attr('data-value');
if (dval == 0) {
$('a.hiw*').attr('data-value','0');
$( ".hiw-popup" ).remove();
$('#'+id).attr('data-value','1');
$('<div class="hiw-popup white-well run-animation hidden-xs"><div class="row text-center"><div class="col-sm-2 animation-hiw col-xs-2"><span class="glyphicon glyphicon-pencil hiw-icon1" style="background:#ffffff;">1</span><br/>block1</div><div class="col-sm-2 animation-hiw col-xs-2"><span class="glyphicon glyphicon-shopping-cart hiw-icon2">2</span><br/>BLOCK3</div><div class="col-sm-3 animation-hiw col-xs-3"><span class="glyphicon glyphicon-folder-open hiw-icon3">3</span><br/>BLOCK2</div><div class="col-sm-3 animation-hiw col-xs-3"><span class="glyphicon glyphicon-ok hiw-icon4">4</span><br/>BLOCK</div><div class="col-sm-2 animation-hiw col-xs-2"><span class="glyphicon glyphicon-arrow-down hiw-icon5">5</span><br/>BLOCK</div></div></div>').insertAfter('#'+id);
}else{
$('#'+id).attr('data-value','0');
$( ".hiw-popup" ).remove();
}
}
</script>
/* Chrome, Safari, Opera */
#-webkit-keyframes animation-hiw-icon {
from {
background-color: #d9d9d9;
}
to {
background-color: #4ad18f;
}
}
/* Standard syntax */
#keyframes animation-hiw-icon {
from {
background-color: #d9d9d9;
}
to {
background-color: #4ad18f;
}
}
/* Chrome, Safari, Opera */
#-webkit-keyframes animation-hiw-prog {
from {
background-color: #d9d9d9;
width: 0%
}
to {
width: 100%;
background-color: #4ad18f;
}
}
/* Standard syntax */
#keyframes animation-hiw-prog {
from {
width: 0%
}
to {
width: 100%;
}
}
/* Chrome, Safari, Opera */
#-webkit-keyframes animation-hiw-pop {
from {
opacity: 0.5;
background-color: #d9d9d9;
-ms-transform: scale(0.8); /* IE 9 */
-webkit-transform: scale(0.8); /* Chrome, Safari, Opera */
transform: scale(0.8);
}
to {
background-color: #4ad18f;
opacity: 1;
font-weight: normal;
-ms-transform: scale(.8); /* IE 9 */
-webkit-transform: scale(.8); /* Chrome, Safari, Opera */
transform: scale(.8);
}
}
/* Standard syntax */
#keyframes animation-hiw-pop {
from {
background-color: #d9d9d9;
opacity: 0.5;
-ms-transform: scale(0.8); /* IE 9 */
-webkit-transform: scale(0.8); /* Chrome, Safari, Opera */
transform: scale(0.8);
}
to {
background-color: #4ad18f;
opacity: 1;
font-weight: normal;
-ms-transform: scale(.8); /* IE 9 */
-webkit-transform: scale(.8); /* Chrome, Safari, Opera */
transform: scale(.8);
}
}
/*Animation Trigger*/
.run-animation .hiw-progress:after, .run-animation .animation-hiw, .run-animation .hiw-icon1, .run-animation .hiw-icon2, .run-animation .hiw-icon3, .run-animation .hiw-icon4, .run-animation .hiw-icon5 {
-webkit-animation-play-state: running; /* Safari and Chrome */
animation-play-state: running;
}
.run-animation .hiw-progress:after, .run-animation .animation-hiw, .run-animation .hiw-icon1, .run-animation .hiw-icon2, .run-animation .hiw-icon3, .run-animation .hiw-icon4, .run-animation .hiw-icon5 {
-webkit-animation-play-state: running;
animation-play-state: running;
}
.hiw-progress:after {
content: "";
width: 0%;
height: 5px;
background: #4ad18f;
display: inline-block;
position: absolute;
top: 0;
left: 0;
-webkit-animation: animation-hiw-prog 5s linear forwards;
animation: animation-hiw-prog 5s linear forwards;
-webkit-animation-play-state: paused;
animation-play-state: paused;
}
.white-well {
background-color: #fff;
padding: 10px 15px; border-radius: 5px;
border: 1px solid #f1f1f1;
}
.hiw-popup {
position: absolute;
width: 100%;
z-index: 9;
margin: 30px 0 0 -15px;
padding: 0px 15px !important;
border-color: rgba(0, 0, 0, 0.25) !important;
box-shadow: 2px 4px 8px rgba(0, 0, 0, 0.60);
-webkit-box-shadow: 2px 4px 8px rgba(0, 0, 0, 0.60);
-mz-box-shadow: 2px 4px 8px rgba(0, 0, 0, 0.60);
}
.hiw-popup .arrow {
position: absolute;
display: block;
width: 0;
height: 0; border-color: transparent;
border-style: solid;
border-width: 11px;
left:90%;
margin-left: -11px;
border-top-width: 0;
border-bottom-color: rgba(0, 0, 0, 0.25);
top: -11px;
}
.hiw-popup .glyphicon {
margin-bottom: 10px;
margin-right: 0px !important;font-weight:bold;
background-color: #ffffff;color:#222222 !important;
}
.white-well .glyphicon {
background-color: #ffffff!important;
border-radius: 76px;margin-top: -3px;color:#d9d9d9 !important;
padding: 5px 9px 8px;
color: #fff;
box-shadow: 0px 0px 3px #222222;
border: 3px solid #ffffff;
}
.glyphicon {
position: relative;
top: 1px;
display: inline-block;
font-family: 'Glyphicons Halflings';
font-style: normal;
font-weight: normal;
line-height: 1;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.clearfix:before, .clearfix:after, .container:before, .container:after, .container-fluid:before, .container-fluid:after, .row:before, .row:after, .form-horizontal .form-group:before, .form-horizontal .form-group:after, .btn-toolbar:before, .btn-toolbar:after, .btn-group-vertical > .btn-group:before, .btn-group-vertical > .btn-group:after, .nav:before, .nav:after, .navbar:before, .navbar:after, .navbar-header:before, .navbar-header:after, .navbar-collapse:before, .navbar-collapse:after, .modal-footer:before, .modal-footer:after, .review:before, .review:after, .panel-body:before, .panel-body:after {
content: " ";
display: table;
}
.animation-hiw:nth-child(1) {
-webkit-animation-delay: .2s;
animation-delay: .2s;
}
.hiw-icon5 {
-webkit-animation: animation-hiw-icon 0.2s forwards;
animation: animation-hiw-icon 0.2s forwards;
-webkit-animation-delay: 5s;
animation-delay: 5s;
-webkit-animation-play-state: paused;
animation-play-state: paused;
}
.hiw-icon4 {
-webkit-animation: animation-hiw-icon 0.2s forwards;
animation: animation-hiw-icon 0.2s forwards;
-webkit-animation-delay: 3.75s;
animation-delay: 3.75s;
-webkit-animation-play-state: paused;
animation-play-state: paused;
}
.hiw-icon3 {
-webkit-animation: animation-hiw-icon 0.2s forwards;
animation: animation-hiw-icon 0.2s forwards;
-webkit-animation-delay: 2.25s;
animation-delay: 2.25s;
-webkit-animation-play-state: paused;
animation-play-state: paused;
}
.hiw-icon2 {
-webkit-animation: animation-hiw-icon 0.2s forwards;
animation: animation-hiw-icon 0.2s forwards;
-webkit-animation-delay: 1s;
animation-delay: 1s;
-webkit-animation-play-state: paused;
animation-play-state: paused;
}
.hiw-icon1 {
-webkit-animation: animation-hiw-icon 0.2s forwards;
animation: animation-hiw-icon 0.2s forwards;
-webkit-animation-delay: .2s;
animation-delay: .2s;
-webkit-animation-play-state: paused;
animation-play-state: paused;
}
.animation-hiw {
-webkit-animation: animation-hiw-pop 0.2s forwards; /* Chrome, Safari, Opera */
animation: animation-hiw-pop 0.2s forwards;
-webkit-animation-play-state: paused; /* Safari and Chrome */
animation-play-state: paused;
opacity: 0.5;
-ms-transform: scale(0.8); /* IE 9 */
-webkit-transform: scale(0.8); /* Chrome, Safari, Opera */
transform: scale(0.8);
background: #d9d9d9;
width: 15%;
padding: 2% 1%;
height: 140px;
color: #ffffff; float: left;
}
.animation-hiw:nth-child(1){ -webkit-animation-delay: .2s; animation-delay: .2s; }
.animation-hiw:nth-child(2){ -webkit-animation-delay: 1s; animation-delay: 1s; }
.animation-hiw:nth-child(3){ -webkit-animation-delay: 2.25s; animation-delay: 2.25s; }
.animation-hiw:nth-child(4){ -webkit-animation-delay: 3.75s; animation-delay: 3.75s; }
.animation-hiw:nth-child(5){ -webkit-animation-delay: 5s; animation-delay: 5s; }
hiw {
visibility: hidden;
font-size: 12px;
font-style: italic;
text-align: right;
float: right;
}
<body>
How it works
</body>
#

Here's a working version. See it yourself.
Tested on Chrome, Firefox, Opera.
On my version of firefox, it does not support style.transition so I made it to fallback to vendor specific name if standard name is not available.
http://jsfiddle.net/eNCBz/5/
var el = document.querySelector('div');
var VENDORS = ['Moz', 'Webkit', 'Ms', 'O'];
function getVendorSpecificName(el, prop) {
var style = el.style;
if (prop in style) {
return prop;
}
prop = ucfirst(prop);
for (var i = 0, l = VENDORS.length, name; i < l; i++) {
name = VENDORS[i] + prop;
if (name in style) {
return name;
}
}
return null;
}
function ucfirst(str) {
return str && str.charAt(0).toUpperCase() + str.substring(1);
}
function toCamelCase(str) {
return str.split('-').map(function (str, i) {
return i > 0 ? ucfirst(str) : str;
}).join('');
}
function animateCss(el, prop, from, to, duration) {
var style = el.style,
camel = toCamelCase(prop),
vendorSpecific = getVendorSpecificName(el, camel);
if (!vendorSpecific) {
console.log(prop + ' is not supported by this browser');
return false;
}
var transitionPropName = getVendorSpecificName(el, 'transition');
if (!(transitionPropName in style)) {
console.log('transition is not supported by this browser');
return false;
}
style[vendorSpecific] = from;
setTimeout(function () {
style[transitionPropName] = prop + ' ' + duration + 's ease';
setTimeout(function () {
style[vendorSpecific] = to;
}, 1);
}, 1);
return true;
}
animateCss(el, 'opacity', 0, 1, 2);
Let me explain what is going on:
made some helper functions like ucfirst, toCamelCase
style property uses camel case names
try to find vendor specific style property name if standard name is not available
utilize setTimeout function to make sure browser to redraw
as far as I know, all browsers always redraw on timeout
I tried to make it more generic function so other properties can be applied as well, such as color or background.
Hope this helps!

Related

How to apply an animation delay to only one animation with CSS and javascript?

I am new to css and javascript. I am trying to animate the spans within an h2 to fall from the top of the page on load and then on hover to have a bouncy effect. I was able to make them happen both BUT now the problem is that the animation delay I intended for the fall animation are applying to the bounce animation as well. When I add the animation name to the animation delay all the letter fall at the same time. What am I missing?
I tried specifying the animation name for the animation delay but it didn't work. When I add the animation name to the animation delay all the letter fall at the same time. What am I missing?. And I also tried to change the animation delay in JS after the first animation happens but wasn't able to figure out.
This is my html
<h2 class="test">
<span class="q">T</span><span class="q">a</span><span class="q">d</span><span
class="q">a</span><span class="q">a</span><span class="q">k</span><span class="q">i</span>
</h2>
<h2 class="test2">
<span class="q2">K</span><span class="q2">u</span><span class="q2">w</span><span
class="q2">a</span><span class="q2">y</span><span class="q2">a</span><span
class="q2">m</span><span class="q2">a</span>
</h2>
This is the CSS
span {
color: black;
font-size: 1em;
display: flex;
margin-bottom: 0;
padding-bottom: 0;
}
.onLoadAnimation {
position: relative;
transform: translateY(-100vh);
animation: fall 1s forwards;
animation-iteration-count: 1;
}
#keyframes fall {
100% {
transform: translateY(0);
}
}
.test span:nth-child(2) {
animation-delay: 0.1s;
}
.test span:nth-child(3) {
animation-delay: 0.2s;
}
.test span:nth-child(4) {
animation-delay: 0.3s;
}
.test span:nth-child(5) {
animation-delay: 0.4s;
}
.test span:nth-child(6) {
animation-delay: 0.5s;
}
.test span:nth-child(7) {
animation-delay: 0.6s;
}
.test2 span:nth-child(1) {
animation-delay: 0.1s;
}
.test2 span:nth-child(2) {
animation-delay: 0.12s;
}
.test2 span:nth-child(3) {
animation-delay: 0.3s;
}
.test2 span:nth-child(4) {
animation-delay: 0.4s;
}
.test2 span:nth-child(5) {
animation-delay: 0.5s;
}
.test2 span:nth-child(6) {
animation-delay: 0.6s;
}
.test2 span:nth-child(7) {
animation-delay: 0.7s;
}
.test2 span:nth-child(8) {
animation-delay: 0.8s;
}
.spanHoverEffect {
color: #0f4c5c;
animation: animate 0.6s;
}
#keyframes animate {
25% {
transform: scale(0.8, 1.3);
}
50% {
transform: scale(1.1, 0.8);
}
75% {
transform: scale(1.1, 0.8);
}
}
This is the JS
let letters = document.getElementsByClassName("q");
let lettersTwo = document.getElementsByClassName("q2");
window.onload = () => {
for (l of letters) {
l.classList.toggle('onLoadAnimation');
l.addEventListener('mouseover', function () {
this.classList
.remove('onLoadAnimation')
.add('spanHoverEffect')
});
l.addEventListener('mouseout', function () {
this.classList
.remove('onLoadAnimation')
.remove('spanHoverEffect')
});
}
for (l of lettersTwo) {
l.classList.toggle('onLoadAnimation');
l.addEventListener('mouseover', function () {
this.classList
.remove('onLoadAnimation')
.add('spanHoverEffect')
});
l.addEventListener('mouseout', function () {
this.classList
.remove('onLoadAnimation')
.remove('spanHoverEffect')
});
}
};
In this fiddle, I made two changes. The letters fall in sequence in Chrome, Firefox and Edge and the animation delay is not present when moused over in any of those browsers.
I added the onLoadAnimation class to the CSS for each letter.
.test .onLoadAnimation:nth-child(2) {
animation-delay: 0.1s;
}
And I removed the chained changes to classList, which is not something that classList supports in any of the browsers mentioned above.
l.classList.remove('onLoadAnimation')
l.classList.add('spanHoverEffect')
The reason I mention web browsers is that "this.classList.remove('onLoadAnimation').add('spanHoverEffect')" causes an error in all of those browsers, because remove returns undefined, so I am wondering if you are using a less-used browser that may work differently.

How to sync two animations using css keyframes?

I am working on solution
I have created a basic html banner where I want to keep image and text animations in sync.
Basically image animation is like scale logo for about 3 seconds, meanwhile logo is animated I want text for same in typing effect
I have created basic solution using css and javascript but it is not in sync
var typewriter = function(txt) {
var container = document.getElementById('typewriter'),
speed = 28,
i = 0,
wordsObj = txt.split(" ")
container.textContent = "";
runAllWords();
function runAllWords() {
if (i < wordsObj.length) {
var a = (i == 0) ? i : i - 1;
setTimeout(function() {
showWord(wordsObj[i], 0)
}, wordsObj[a].length * speed);
}
}
function showWord(word, countWord) {
if (countWord < word.length) {
setTimeout(function() {
showLetter(word, countWord)
}, speed);
} else {
container.textContent = container.textContent + " ";
i += 1;
runAllWords();
}
if (i === wordsObj.length) {
console.log('complete')
}
}
function showLetter(word, countWord) {
container.textContent = container.textContent + word[countWord];
showWord(word, countWord + 1);
}
}
var i = 0;
function myLoop() {
// create a loop function
var dataType = document.getElementById('typewriter').dataset.typewriter,
w = dataType.split(',')
setTimeout(function() { // call a 3s setTimeout when the loop is called
typewriter(w[i]); // your code here
i++; // increment the counter
if (i < w.length) { // if the counter < 10, call the loop function
myLoop(); // .. again which will trigger another
} // .. setTimeout()
}, 3000)
}
myLoop();
.addsp_320x50 {
width: 100%;
height: 50px;
position: relative;
}
.addsp_320x50_img {
position: absolute;
top: 1px;
left: 10px;
width: 48px;
height: 48px;
border: 0px solid #ccc;
border-radius: 50%;
}
.addsp_title_text {
position: absolute;
top: 5px;
left: 70px;
font-family: Open Sans;
font-weight: bold;
}
.addsp_title_desc {
position: absolute;
top: 20px;
left: 70px;
font-family: Open Sans;
color: #999;
}
.addsp_320x50_action button {
height: 27px;
background: #058562;
border-radius: 4px;
color: #fff;
border-color: #058562;
font-size: 12px;
font-weight: bold;
font-family: Open Sans;
border-style: solid;
position: absolute;
right: 10px;
top: 10px;
display: flex;
}
.adz_text_1 {}
.adz_text_2 {
animation: text2;
}
.adz_text_1,
.adz_text_2 {}
#keyframes text2 {
0%,
50%,
100% {
width: 0px;
}
60%,
90% {
width: 200px;
}
}
#keyframes text1 {
0%,
50%,
100% {
width: 0px;
}
10%,
40% {
width: 200px;
}
}
#media only screen and (min-width: 320px) {
.addsp_320x50_img {
width: 42px;
height: 42px;
top: 4px;
left: 5px;
}
.addsp_title_text {
top: 14px;
left: 56px;
font-size: 0.85rem;
}
.addsp_title_desc {
top: 25px;
left: 55px;
font-size: 0.8rem;
}
}
#media only screen and (min-width: 480px) {
.addsp_title_text {
top: 3px;
left: 55px;
font-size: 1.1rem;
}
.addsp_title_desc {
top: 28px;
left: 55px;
font-size: 0.8rem;
}
}
#media only screen and (min-width: 600px) {
.addsp_title_text {
top: 3px;
left: 70px;
font-size: 1.1rem;
}
.addsp_title_desc {
top: 28px;
left: 70px;
font-size: 0.8rem;
}
}
#media only screen and (min-width: 800px) {
.addsp_title_text {
top: 3px;
left: 70px;
font-size: 1.1rem;
}
.addsp_title_desc {
top: 28px;
left: 70px;
font-size: 0.8rem;
}
}
.addsp_320x50_img:nth-child(1) {
animation-name: scale;
animation-duration: 3s;
animation-timing-function: linear;
animation-delay: 1s;
animation-iteration-count: infinite;
animation-fill-mode: forwards;
opacity: 0;
}
.addsp_320x50_img:nth-child(2) {
animation-name: scale;
animation-duration: 3s;
animation-timing-function: linear;
animation-delay: 4s;
animation-iteration-count: infinite;
animation-fill-mode: forwards;
opacity: 0;
}
.addsp_320x50_img:nth-child(3) {
animation-name: scale;
animation-duration: 3s;
animation-timing-function: linear;
animation-delay: 7s;
animation-iteration-count: infinite;
animation-fill-mode: forwards;
opacity: 0;
}
#keyframes scale {
0% {
transform: scale(1);
opacity: 1
}
20% {
transform: scale(1.2);
opacity: 1
}
40% {
transform: scale(1);
opacity: 1
}
60% {
transform: scale(1.2);
opacity: 1
}
80% {
transform: scale(1);
opacity: 1
}
90% {
transform: translateY(-100px);
opacity: 0;
}
100% {
opacity: 0;
}
}
.blinking-cursor {
color: #2E3D48;
-webkit-animation: 1s blink step-end infinite;
-moz-animation: 1s blink step-end infinite;
-ms-animation: 1s blink step-end infinite;
-o-animation: 1s blink step-end infinite;
animation: 1s blink step-end infinite;
}
#keyframes "blink" {
from,
to {
color: transparent;
}
50% {
color: black;
}
}
#-moz-keyframes blink {
from,
to {
color: transparent;
}
50% {
color: black;
}
}
#-webkit-keyframes "blink" {
from,
to {
color: transparent;
}
50% {
color: black;
}
}
#-ms-keyframes "blink" {
from,
to {
color: transparent;
}
50% {
color: black;
}
}
#-o-keyframes "blink" {
from,
to {
color: transparent;
}
50% {
color: black;
}
}
<div class="addsp_320x50">
<img src="https://de7yjjf51n4cm.cloudfront.net/banners/amazonprime_newicon.jpg" class="addsp_320x50_img">
<img src="https://de7yjjf51n4cm.cloudfront.net/banners/amazonprime_newicon.jpg" class="addsp_320x50_img">
<img src="https://de7yjjf51n4cm.cloudfront.net/banners/amazonprime_newicon.jpg" class="addsp_320x50_img">
<div class="addsp_title_text">
<span class="adz_text_1 typewriter" id="typewriter" data-typewriter="Web Strategy,
UX Testing,
Content Management System,
Web Design,
Research and Analytics,
Information Architecture,
Strategic Consulting,Maintenance and Support"></span><span class="blinking-cursor">|</span>
</div>
<div class="addsp_320x50_action">
<button>DOWNLOAD</button></div>
</div>
Mathematically speaking, sinking means adjusting frequency and phase. I'll demonstrate each separately. Note that what I'm gonna explain is the concept and you can implement it in your codes using Javascript, css, etc
Frequency
You can't sink two animations unless the longer duration is a factor
of shorter duration.
For example in your codes, blinking has a duration of 1s. So your image scaling duration and Also the whole duration must be a selection of either 1s, 2s, 3s, ... or 1/2s, 1/3s, ...
For better understanding let me make a simple example. Assume two images want to be animated.
<img src="1.png" id="img1">
<img src="1.png" style="margin-left: 50px;" id="img2">
Consider two different animations for each one
#keyframes k1
{
25%
{
transform: rotate(-4deg);
}
50%
{
transform: rotate(0deg);
}
75%
{
transform: rotate(3deg);
}
100%
{
transform: rotate(0deg);
}
}
#keyframes k2
{
50%
{
transform: scale(1.2);
}
100%
{
transform: scale(1);
}
}
So since k2 is simpler, I'll first assign it to img2 with duration of 0.7s
#img2
{
animation: k2 0.7s linear infinite;
}
And based on what was explained, I will assign animation k1 to img1 with a duration of 1.4s. (NOT 1.3s NOT 1.5s VERY IMPORTANT!)
#img1
{
animation: k1 1.4s linear infinite;
}
If you run this code you'll see they are sink! To feel the concept better, change the duration of k1 to 0.9s. Now it feels like they are doing their thing separately!
Note
I set k1 to 1.4s (0.7s × 2) because k1 seems to be a combination of one go forward and come back and using 2x feels they are dancing together with the same harmony!
Phase
In css, phase is showed by animation-delay. Modifying frequencies (duration) is enough to sink two animations but if two animation begin at the same time it will feel better! So to illustrate set a delay for img1 of 0.2s. They are still sink but it doesn't feel nice! Now change the delay to 0.7s. Now it's beautiful again! (Maybe even more beautiful)
Back to your code
Your images scale with duration of 1.2s (40% of 3s) and your text blinking duration is 1s and as you can see they are not factor of each other so you can't sink!
I think you might be looking for the animation iteration event and the animation start event.
Instead of just using the myLoop function to call itself, try using these listeners to call it instead.
The end of your js file would look like:
var i = 0;
function myLoop() {
var dataType = document.getElementById("typewriter").dataset.typewriter,
w = dataType.split(",");
if (i < w.length -1 ) {
typewriter(w[i]);
}
i++;
}
var imageElems = Array.from(document.querySelectorAll('.addsp_320x50_img'));
imageElems.forEach(elem=>{
elem.addEventListener('animationstart',myLoop);
});
Where ".addsp_320x50_img" is just whatever common selector you give to all the images.
If you control the animation with the same JavaScript loop as the typewriter script, it won't lose sync. I rewrote the typewriter script to do this in the snippet below.
startTypewriter() Exaplaination
First, all the messages from the are collected converted into an array.
typewriter.getAttribute('data-typewriter').split(',');
Then the CSS icon animation is started. Because JavaScript intervals wait for their duration before executing their code, so the first message is typed by calling type() before the interval is created.
icon.classList.add('icon-animation');
type(typewriter, messages[0].trim(), animationDuration - pauseDuration);
The interval is now started, running every 3 seconds by default. The first thing that happens is the animation is reset in case it got out of sync somehow.
icon.classList.remove('icon-animation');
window.setTimeout(function() {
icon.classList.add('icon-animation');
}, 25);
Next, the message is typed by calling type(). Before it ends, a check is run so see if it's on the last array element. If so, it will start over.
if (i == messages.length) i = 0;
type() Exaplaination
At the start, the timePerCharacter value is calculated. The message is split to an array and the typewriter output is cleared
var timePerCharacter = duration / message.length;
var message = message.split('');
typewriter.innerHTML = '';
A loop is created, running every timePerCharacter. The character is outputted to the typewriter output.
typewriter.innerHTML += message[i];
Once all the characters are outputted, the loop is cleared
if (i == message.length) clearInterval(typeLoop);
Snippent
var animationDuration = 3000;
var pauseDuration = 2000;
startTypewriter();
function startTypewriter() {
var typewriter = document.getElementById('typewriter');
var icon = document.getElementById('icon');
var messages = typewriter.getAttribute('data-typewriter').split(',');
icon.classList.add('icon-animation');
type(typewriter, messages[0].trim(), animationDuration - pauseDuration);
var i = 1;
window.setInterval(function() {
icon.classList.remove('icon-animation');
window.setTimeout(function() {
icon.classList.add('icon-animation');
}, 25);
type(typewriter, messages[i].trim(), animationDuration - pauseDuration);
i++;
if (i == messages.length) i = 0;
}, animationDuration);
}
function type(typewriter, message, duration) {
var timePerCharacter = duration / message.length;
var message = message.split('');
typewriter.innerHTML = '';
var i = 0;
var typeLoop = window.setInterval(function() {
typewriter.innerHTML += message[i];
i++;
if (i == message.length) clearInterval(typeLoop);
}, timePerCharacter);
}
#keyframes icon {
20% {
transform: scale(0.9);
}
40% {
transform: scale(1);
}
60% {
transform: scale(0.9);
}
80% {
transform: scale(1);
}
100% {
transform: translateY(-200%);
}
}
.icon {
border-radius: 100%;
}
.icon-animation {
animation: icon 3s;
}
#keyframes cursor {
50% {
color: transparent;
}
}
.blinking-cursor {
animation: cursor 1s steps(1) infinite;
}
<img id="icon" src="https://de7yjjf51n4cm.cloudfront.net/banners/amazonprime_newicon.jpg" class="icon">
<span id="typewriter" data-typewriter="
Web Strategy,
UX Testing,
Content Management System,
Web Design,
Research and Analytics,
Information Architecture,
Strategic Consulting,
Maintenance and Support
">
</span>
<span class="blinking-cursor">|</span>

CSS animations to toggle when in viewport with vanilla JS

So I have got different animations made in CSS, though the problem is that they start right away when the page loads (ofcourse). I do not want this though. Is there a way in Vanilla JavaScript to get the animation to fire up only when it is in the viewport?
I have searched in a lot of places, but I either find a plugin I need to use or jQuery.
HTML:
<div class="introduction">
<h1>I can do the following for you:</h1>
<ul>
<li>Create a custommade, new website.</li>
<li>Code a PSD template into a working website.</li>
<li>Rework an outdated website.</li>
<li>Clean up messy code of a website.</li>
</ul>
</div>
CSS:
#keyframes showOnLoad {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
.introduction li {
list-style-type: none;
margin-bottom: 5px;
text-align: center;
opacity: 0;
-webkit-animation: showOnLoad;
animation: showOnLoad;
-webkit-animation-duration: 2s;
animation-duration: 2s;
-webkit-animation-fill-mode: forwards;
animation-fill-mode: forwards;
}
.introduction li:nth-child(2) {
-webkit-animation-delay: 1s;
animation-delay: 1s;
}
.introduction li:nth-child(3) {
-webkit-animation-delay: 2s;
animation-delay: 2s;
}
.introduction li:nth-child(4) {
-webkit-animation-delay: 3s;
animation-delay: 3s;
}
This is the code you need.
window.addEventListener("scroll", onScroll);
function onScroll() {
for (var item of document.querySelectorAll(".introduction li")) {
elementVisible(item);
}
}
function elementVisible(el) {
let top = el.offsetTop;
let height = el.offsetHeight;
let bottom = top + height;
let IsOverBottom = top > (window.pageYOffset + window.innerHeight);
let IsBeforeTop = bottom < window.pageYOffset;
if (!IsOverBottom && !IsBeforeTop) {
el.classList.add("show");
}
}
And a bit of CSS
#keyframes slideIn {
0% {
opacity: 0;
transform: translateX(100%);
}
100% {
opacity: 1;
transform: translateX(0%);
}
}
.show {
animation: slideIn 5s ease-in-out;
}
This is a basic implementation but it gets you closer.
http://jsbin.com/hetapaj/1/edit?css,js,output

CSS3 Animation: Forwards fill not working with position and display on Safari

So, I have created a CSS3 animation that is supposed to fade out an element by setting its opacity from 1 to 0 and at the last frames change the position to absolute and display to none. But on Safari it will only maintain the opacity, position and display are not set to the final values.
#-webkit-keyframes impressum-fade-out {
0% {
opacity: 1;
display: block;
position: relative;
}
99% {
opacity: 0;
position: relative;
}
100% {
opacity: 0;
display: none;
position: absolute;
}
}
It seems to work on Chrome but not on Safari (I tried version 8). Apparently, position and display do not work properly with animation-fill-mode: forwards...
JSFiddle: http://jsfiddle.net/uhtL12gv/
EDIT For Bounty: I am aware of workarounds with Javascript and transitionend events. But I am wondering why Browsers lack support for this? Does the specification state that fillmode forwards doesnt apply to some attributes like position or is this a bug in the browsers? Because I couldnt find anything in the bug trackers.. If anybody has some insight, I would really appreciate it
As Suggested in the comments, you can adjust the height.
EDIT: Animation Reference Links Added.
Display property is not animatable.
Position property is not
animatable.
List of all CSS properties and if and how they are
animatable.
$('.block').click(function() { $(this).toggleClass('active') });
#-webkit-keyframes impressum-fade-out {
0% {
opacity: 1;
}
99% {
opacity: 0;
}
100% {
opacity: 0;
height:0;
}
}
.block {
width: 100px;
height: 100px;
background: blue;
}
.block2 {
width: 100px;
height: 100px;
background: red;
}
.block.active {
-webkit-animation-name: impressum-fade-out;
animation-name: impressum-fade-out;
-webkit-animation-duration: 500ms;
animation-duration: 500ms;
-webkit-animation-fill-mode: forwards;
animation-fill-mode: forwards;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div class="block"></div>
<div class="block2"></div>
I would suggest you the cross-browser solution based on CSS3 Transitions and transitionend event:
JSFiddle
$('.block').one('click', function() {
var $this = $(this);
$this.one('webkitTransitionEnd transitionend', function() {
$this.addClass('block_hidden');
$this.removeClass('block_transition');
});
$this.addClass('block_transition');
});
.block {
width: 100px;
height: 100px;
background: blue;
-webkit-transition: opacity 0.5s;
transition: opacity 0.5s;
}
.block_2 {
background: red;
}
.block_transition {
opacity: 0;
}
.block_hidden {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="block"></div>
<div class="block block_2"></div>

CSS Animation onClick

How can I get a CSS Animation to play with a JavaScript onClick? I currently have:
.classname {
-webkit-animation-name: cssAnimation;
-webkit-animation-duration:3s;
-webkit-animation-iteration-count: 1;
-webkit-animation-timing-function: ease;
-webkit-animation-fill-mode: forwards;
}
#-webkit-keyframes cssAnimation {
from {
-webkit-transform: rotate(0deg) scale(1) skew(0deg) translate(100px);
}
to {
-webkit-transform: rotate(0deg) scale(2) skew(0deg) translate(100px);
}
}
How can I apply an onClick?
Are you sure you only display your page on webkit? Here is the code, passed on safari.
The image (id='img') will rotate after button click.
function ani() {
document.getElementById('img').className = 'classname';
}
.classname {
-webkit-animation-name: cssAnimation;
-webkit-animation-duration: 3s;
-webkit-animation-iteration-count: 1;
-webkit-animation-timing-function: ease;
-webkit-animation-fill-mode: forwards;
}
#-webkit-keyframes cssAnimation {
from {
-webkit-transform: rotate(0deg) scale(1) skew(0deg) translate(100px);
}
to {
-webkit-transform: rotate(0deg) scale(2) skew(0deg) translate(100px);
}
}
<input name="" type="button" onclick="ani()" value="Click">
<img id="img" src="https://i.stack.imgur.com/vghKS.png" width="328" height="328" />
You just use the :active pseudo-class. This is set when you click on any element.
.classname:active {
/* animation css */
}
Found solution on css-tricks
const element = document.getElementById('img')
element.classList.remove('classname'); // reset animation
void element.offsetWidth; // trigger reflow
element.classList.add('classname'); // start animation
You can achieve this by binding an onclick listener and then adding the animate class like this:
$('#button').onClick(function(){
$('#target_element').addClass('animate_class_name');
});
CSS ONLY solution that works on every click and plays the animation to the end:
All you have to do is to add the animation to the :focus pseudo class and set it to none in :active pseudo class.
If your element isn't focusable add tabindex="0" attribute to the html element:
#keyframes beat {
0% {
-webkit-transform: scale(1, 1);
transform: scale(1, 1);
}
100% {
-webkit-transform: scale(0.8,0.8);
transform: scale(0.8, 0.8);
}
}
.className {
background-color:#07d;
color: #fff;
font-family: sans-serif;
font-size: 20px;
padding: 20px;
margin:5px;
-webkit-transform: scale(1, 1);
transform: scale(1, 1);
}
.className:focus {
-webkit-animation: beat 1s ease-in-out backwards;
animation: beat 1s ease-in-out backwards;
}
.className:active {
-webkit-animation: none;
animation: none;
}
body {
text-align: center;
}
<h3>Any element with tabindex="0", like a div:</h3>
<div tabindex="0" class="className"> Click me many times!</div>
<h3>Any focusable element like a button:</h3>
<button class="className"> Click me many times!</button>
var abox = document.getElementsByClassName("box")[0];
function allmove(){
abox.classList.remove("move-ltr");
abox.classList.remove("move-ttb");
abox.classList.toggle("move");
}
function ltr(){
abox.classList.remove("move");
abox.classList.remove("move-ttb");
abox.classList.toggle("move-ltr");
}
function ttb(){
abox.classList.remove("move-ltr");
abox.classList.remove("move");
abox.classList.toggle("move-ttb");
}
.box {
width: 100px;
height: 100px;
background: red;
position: relative;
}
.move{
-webkit-animation: moveall 5s;
animation: moveall 5s;
}
.move-ltr{
-webkit-animation: moveltr 5s;
animation: moveltr 5s;
}
.move-ttb{
-webkit-animation: movettb 5s;
animation: movettb 5s;
}
#keyframes moveall {
0% {left: 0px; top: 0px;}
25% {left: 200px; top: 0px;}
50% {left: 200px; top: 200px;}
75% {left: 0px; top: 200px;}
100% {left: 0px; top: 0px;}
}
#keyframes moveltr {
0% { left: 0px; top: 0px;}
50% {left: 200px; top: 0px;}
100% {left: 0px; top: 0px;}
}
#keyframes movettb {
0% {left: 0px; top: 0px;}
50% {top: 200px;left: 0px;}
100% {left: 0px; top: 0px;}
}
<div class="box"></div>
<button onclick="allmove()">click</button>
<button onclick="ltr()">click</button>
<button onclick="ttb()">click</button>
Add a
-webkit-animation-play-state: paused;
to your CSS file, then you can control whether the animation is running or not by using this JS line:
document.getElementById("myDIV").style.WebkitAnimationPlayState = "running";
if you want the animation to run once, every time you click. Remember to set
-webkit-animation-iteration-count: 1;
You can do that by using following code
$('#button_id').on('click', function(){
$('#element_want_to_target').addClass('.animation_class');});
Try this:
<div>
<p onclick="startAnimation()">Start</p><!--O botão para iniciar (start)-->
<div id="animation">Hello!</div> <!--O elemento que você quer animar-->
</div>
<style>
#keyframes animationName {
from {margin-left:-30%;}
}
</style>
<script>
function startAnimation() {
document.getElementById("animation").style.animation = "animationName 2s linear 1";
}
</script>
Add the animation and remove it after the animation-duration ends using setTimeout()
const elem = document.querySelector(".element");
elem.onclick = () => {
elem.style.animation="YOUR_ANIMATION";
setTimeout(()=>{
elem.style.animation="none";
},YOUR_ANIMATION_DURATION);
}

Categories

Resources