I want to animate the width of a svg rect.
Initially, rect width should be 0, on button click the width should grow in 5 seconds to 100% of the width.
I create this codesandbox.
I basically create this class:
.grow {
animation: growAnimation 5s linear 1;
}
#keyframes growAnimation {
from {
transform: scale(0%, 100%);
}
to {
transform: scale(100%, 100%);
}
}
I assign this class only when user clicks on button but it doesn't work: rect width is 0 and on click is 100%, there is no growing during 5 seconds.
Why?
Is there a better way to to this? In the future I have to do also a press and hold animation:
on button click the animation start
on button hold the animation continue
if user releases the button before the growing animation ends, then there is the inverse animation (from 100% to 0%).
you should pass parameters to scale property as number not as percentage:
#keyframes growAnimation {
from {
transform: scale(0, 1);
}
to {
transform: scale(1, 1);
}
}
See this Working Example
Related
This hypothetical button would be yellow when it isn't hovered, but would be a different colour depending on what time you do hover over it. So the animation would be persistent whether or not it is being hovered and if you never hovered it, you would never know the colour on hover is changing because it would always be yellow otherwise.
I hope this explanation makes sense. I haven't got any code to offer, because if it is doable I have no idea where I would start.
EDIT:
HTML
<div class = "box"></div>
<button class = "btn">Colour change animation</button>
CSS
.box {
margin : 50px;
height :200px;
width : 200px;
background-color : pink;
}
.colour-changer:hover {
animation : colour-change 5s linear 0s;
}
#keyframes colour-change {
0% {
}
100% {
background-color : purple;
}
}
JS
document.addEventListener("DOMContentLoaded", function(){
document.getElementsByClassName("btn")[0].addEventListener("click", function(){
document.getElementsByClassName("box")[0].classList.add("colour-changer");
});
});
I did try to make a JSFiddle, but I couldn't get it working for some reason.
Anyway, this makes the box change colour after the class is added and you also hover the box and the animation restarts every time you hover the box. What I'm after is making the animation happen regardless of the hover, but only be visible when hovered so the box would otherwise be pink.
An idea is to use multiple background to have two layers. The top one always visible and with the same color and the bottom one will be animated continously and changing its color (we cannot see it of course). The on hover you make the first layer invisible and you pause the animation and we will see the new color.
Here is an example:
.box {
height: 200px;
width: 200px;
background-image: linear-gradient(yellow, yellow), linear-gradient(red, red);
background-size: 100%, 100%;
animation: change 5s linear infinite alternate;
}
.box:hover {
background-size: 0%, 100%;
animation-play-state: paused
}
#keyframes change {
0% {
background-image: linear-gradient(yellow, yellow), linear-gradient(red, red);
}
30% {
background-image: linear-gradient(yellow, yellow), linear-gradient(orange, orange);
}
60% {
background-image: linear-gradient(yellow, yellow), linear-gradient(blue, blue);
}
100% {
background-image: linear-gradient(yellow, yellow), linear-gradient(green, green);
}
}
<div class="box"></div>
I've got a jquery solution, and with a bit of work it could be migrated to vanilla javascript. Also, there's a 1/6 chance of no color change, but it should give you a sense of one way to go about doing this:
HTML:
<div class = "box"></div><button class = "btn">Colour change when hovered</button>
CSS:
button{
background-color:yellow;
}
.color-0{
background-color:purple !important;
}
.color-1{
background-color:blue !important;
}
.color-2{
background-color:green !important;
}
.color-3{
background-color:yellow !important;
}
.color-4{
background-color:orange !important;
}
.color-5{
background-color:red !important;
}
Javascript (+ JQuery):
$("button").on("mouseenter",function(){
$(this).removeClass("color-0");
$(this).removeClass("color-1");
$(this).removeClass("color-2");
$(this).removeClass("color-3");
$(this).removeClass("color-4");
$(this).removeClass("color-5");
var d = new Date();
var seconds = d.getSeconds();
var color = parseInt(seconds % 6);
$(this).addClass("color-"+color.toString());
})
example JSFiddle:
https://jsfiddle.net/needsmorejson/yL31cjtd/19/
I have an introductory portion of a site where 4 letters (SVGS) pop onto the screen. Most of the time, it plays just fine. On occasion however, sometimes one or two or all the images won't animate in at all. I'm not sure what could be causing this (it's not the cache), and a page refresh usually fixes it. Am I missing something? Should I wait for the images to load AND for the entire DOM to be ready?
Here is the relevant CSS (Sass).
Animation:
#keyframes bobble {
0% {
transform: translateY(124px) scale(0.8, 1.6);
}
25% {
transform: translateY(-64px) scale(1.6, 0.8);
}
55% {
transform: translateY(16px) scale(0.9, 1.1);
}
100% {
transform: translateY(0) scale(1);
opacity: 1;
}
}
Styling
.hello-header-img {
height: 100%;
width: 100%;
opacity: 0;
animation: bobble $animation-duration cubic-bezier(0.64, 0.57, 0.67, 1.53);
animation-fill-mode: forwards;
animation-play-state: paused;
will-change: transform, opacity;
// Plays animations when images have loaded (JS)
.is-ready & {
animation-play-state: running;
}
}
Each letter also has an animation delay which starts at 0.4s and increases by 0.3s for each letter (so letter 1 = 0.4s delay, letter 2 = 0.7s delay, letter 3 = 1s delay, etc).
Javascript
const imgs = document.querySelectorAll('.hello-header img');
let counter = 0;
let hasScrolled = false;
// Animate after hello section images have loaded
[].forEach.call( imgs, function( img ) {
img.addEventListener( 'load', ()=> {
counter++;
if ( counter === imgs.length ) {
document.querySelectorAll('.js-hello-header')[0].classList.add('is-ready');
}
}, false );
});
Try replacing translate and scale with translate3d and scale3d (with the appropriate parameters).
translate and scale in general are very CPU expensive while their 3d counterparts utilize hardware acceleration from the graphics card and therefore play animations much smoother.
I want to achieve a conveyor belt animation on an SVG element i.e. the blocks on the conveyor belt need to move along with the belt in a particular direction.
A better view of what i want to achieve:
GIF
The SVG image has separate groups for separate blocks that need to be moved along a line (belt). The svg can be found here
I tried using jquery animate to move them at an angle like so:
$(function() {
var dist = 130;
var angle = -48;
var x = Math.cos(angle*Math.PI/180) * dist;
var y = Math.sin(angle*Math.PI/180) * dist;
// $(".st93").animate({'left': '+='+x+'px', 'top': '+='+y+'px'}, 1000);
$(".st93").animate({"transform": "translateX(+=" +x+ "px)"}, {"transform": "translateY(+=" +y+ "px)"}, 1000);
})
which didn't work.
and then i tried using CSS only transforms like so:
#Square_1 {
position: relative;
animation-name: move;
animation-duration: 2s;
animation-timing-function: linear;
animation-iteration-count: infinite;
}
#keyframes move {
0% {
opacity:0;
//transform: translateX( -400px, 10px);
}
10% {
opacity:1;
}
90% {
opacity:1;
transform: translate( 100px, -60px);
}
100% {
opacity:0;
}
}
which took me closer to what I wanted but still isn't showing the desired effect. the blocks start abruptly forming at their respective starting position instead of a continuously flowing from the start of the belt which seems far from natural.
Any help will be appreciated. Thanks.
I've recently been experimenting with CSS animations and have come across some behaviour I can't explain with regards to final frame state.
Given this very small piece of HTML:
<span id="rotateme">This is text</span>
Some CSS:
#rotateme { display: inline-block; }
.clockwise {
animation: clockwise 1s;
animation-fill-mode: forwards;
}
.anticlockwise {
animation: anticlockwise 1s;
animation-fill-mode: forwards;
}
#keyframes anticlockwise {
from { transform: rotate(0deg); }
to { transform: rotate(-90deg); }
}
#keyframes clockwise {
from { transform: rotate(-90deg); }
to { transform: rotate(0deg); }
}
And a little bit of Javascript to tie it together:
document.addEventListener("DOMContentLoaded", function(event) {
d3.select('#rotateme')
.on('click', rotateAnticlockwise)
function rotateClockwise() {
d3
.select(this)
.classed('clockwise', true)
.classed('anticlockwise', false)
.on('click', rotateAnticlockwise)
}
function rotateAnticlockwise() {
d3
.select(this)
.classed('clockwise', false)
.classed('anticlockwise', true)
.on('click', rotateClockwise)
}
});
(For a live example, this is also in a codepen)
If you click on the text it'll rotate, click on it again and it'll rotate back. However, if you remove the display style from rotateme element then the final frame of the animation isn't preserved. For the clockwise motion this means it snaps back to the original, horizontal position, and the anticlockwise motion starts from the wrong place.
My question is, what is that inline-block is doing in this situation that makes the animation work as I expect it to. i.e Stay in what I understand to be the forward fill mode.
I should add that I'm doing this in Chrome 43 just in case it's a browser quirk.
Span elements are inline by default, and therefore have limitations on dimension, position, etc. By removing inline-block from the style display, you're allowing it to revert to inline, whereby dimension and position are stripped.
I have searched through a lot of questions related to my question in stackoverflow but i haven't found one yet that answers my question with plain JavaScript (not using libraries of any kind).
My problem is that I have an infinite animation with CSS3 i.e.:
.clockwiseAnimation {
top: 270px;
left: 200px;
position: absolute;
-webkit-animation: clockwise 4s linear infinite; /* Chrome, Safari 5 */
-moz-animation: clockwise 4s linear infinite; /* Firefox 5-15 */
-o-animation: clockwise 4s linear infinite; /* Opera 12+ */
animation: clockwise 4s linear infinite; /* Chrome, Firefox 16+, IE 10+, Safari 5 */
}
#-webkit-keyframes clockwise {
from { -webkit-transform: rotate(0deg) translateX(150px) rotate(0deg); }
to { -webkit-transform: rotate(360deg) translateX(150px) rotate(-360deg); }
}
#-moz-keyframes clockwise {
from { -moz-transform: rotate(0deg) translateX(150px) rotate(0deg); }
to { -moz-transform: rotate(360deg) translateX(150px) rotate(-360deg); }
}
#-o-keyframes clockwise {
from { -o-transform: rotate(0deg) translateX(150px) rotate(0deg); }
to { -o-transform: rotate(360deg) translateX(150px) rotate(-360deg); }
}
#keyframes clockwise {
from { transform: rotate(0deg) translateX(150px) rotate(0deg); }
to { transform: rotate(360deg) translateX(150px) rotate(-360deg); }
}
This animation allows me to spin (clockwise) whatever tag that has the class "clockwiseAnimation".
What I want to do is to change the time of execution (I'll call it speed) of the animation with javascript like:
HTML:
<span id="someID" class="clockwiseAnimation">sometext</span>
JavaScript:
var style = document.getElementById("someID").style,
speed = 6;
//obviously the speed is dynamic within my site (through an `<input type="range">`)
//for the purposes of this example I set the speed to a different value(6seconds) than the original value(4seconds).
style.webkitAnimationDuration = style.mozAnimationDuration = style.oAnimationDuration = style.animationDuration = speed + "s";
It works when I pause and then play(by play I mean UNPAUSE not restart) the animation, i.e.:
var style = document.getElementById("someID").style;
some = 6; //it is dynamic (as I pointed out before)
//pause
style.webkitAnimationPlayState = style.mozAnimationPlayState = style.oAnimationPlayState = style.animationPlayState = "paused";
//change speed
style.webkitAnimationDuration = style.mozAnimationDuration = style.oAnimationDuration = style.animationDuration = speed + "s";
//play (== UNPAUSE) //UPDATE: Added the timeout because I can't get it to work any other way.
setTimeout(function(){
style.webkitAnimationPlayState = style.mozAnimationPlayState = style.oAnimationPlayState = style.animationPlayState = "running";
},1);
UPDATED:
And it works! BUT, it has a big RANDOM jump in the animation, meaning that when I change the "speed" with the "<input type="range"> slider" the element jumps to a random location (not the beginning nor the end of the animation just a random location).
NOTE: Pause and play works very smooth without changing the "speed" of the animation.
My question(s): Can I change the "speed" of the animation smoothly WITH JavaScript? (WITHOUT the jumping)
If the answer is: "There is not a way to do it smoothly throughout the animation execution", then:
Is there a way to change it in the next iteration of the infinite animation?
If so:
Then how can I tell it to start in the next iteration and how to know which is the next iteration if I set the animation to infinite (animation-iteration-count property of the element that is doing the animation always returns "infinite").
Here is an example. I hope it helps.
What may be occurring is that the animation-duration "change" could be "jumping" to the point in the animation corresponding to the "changed" #keyframes - based on the total "changed" animation duration..
If the animation-duration began from (or 0%) proceeded to to (or 100%), the corresponding #keyframes "position" may be changed as well.
For example if the original animation-duration was 4s (or, 4000ms) at approximately 2s (or, 2000ms), the corresponding keyframes may be at approximately 50%, or
at 2 seconds into 4 second animation
50% { -webkit-transform: rotate(180deg) translateX(150px) rotate(-180deg); }
when the animation-duration is dynamically changed, the corresponding keyframes may be "changed" to the matching % point, or, a larger duration span for the same effect. The animated element may appear to go forwards or backwards, or hve a "jumping" due to it re-positioning itself within the "changed" corresponding keyframes and animations.
There is also 1s setTimeout function, that may or may not actually have a 1s duration.
It may be possible to "smoothly" "jump" to the newly "changed" position within the lengthier animation-duration the suggested transition effect or requestAnimationFrame (http://www.w3.org/TR/animation-timing/).
..
Try this:
html
<input type="range" id="speedSlider" min="2000" max="6000" value="4000">
css
input {
-webkit-transform: rotate(180deg);
top : 50px;
position : absolute;
}
.clockwiseAnimation {
/* top: 270px;
left: 200px; */
left : 50%;
top : 50%;
position:absolute;
/* css animation (no change) */
}
js
var speedSlider = document.getElementById("speedSlider");
speedSlider.addEventListener("change", changeSpeed, false);
function changeSpeed(e){
var speed = Math.floor(speedSlider.value);
var element = document.getElementById("span");
var style = element.style;
style.webkitAnimationPlayState = style.mozAnimationPlayState = style.oAnimationPlayState = style.animationPlayState = "paused";
style.webkitAnimationDuration = style.mozAnimationDuration = style.oAnimationDuration = style.animationDuration = String(speed) + "ms";
style.webkitAnimationPlayState = style.mozAnimationPlayState = style.oAnimationPlayState = style.animationPlayState = "running";
}
With CSS: http://jsbin.com/UsunIMa/1/
CSS properties transition and animation allow you to pick the easing function.
div {
transition: all 600ms cubic-bezier(0.77, 0, 0.175, 1);
/* or */
animation: clockwise 4s cubic-bezier(0.77, 0, 0.175, 1) infinite;
}
use setTimeout to add animation class to your required element with the removal of animation class as a callback.
Maybe jQuery queue could help you.
Here it is