CSS rotation, final frame and inline-block - javascript

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.

Related

Why does my CSS text scaling animation create a "curving" motion and how can I eliminate it?

Please note: I am pretty new to CSS so I may use incorrect terminology or awkward descriptions. Also, my usage of the hover psuedo-class is simply to make it easy to demonstrate in my examples. I am NOT using any psuedo-class in my actual code.
The following snippet is a very simple CSS transition that I am applying to some text. It works exactly as I want it to except that in order to invoke the transition/animation I have to use JavaScript to manually add the effect class to the element.
.effect:hover {
transform-origin: left top;
transform: scale(.5, .5);
transition: transform 1s;
}
<div>
<span style="display: inline-block; font-size: x-large;" class="effect">HOVER OVER THIS</span>
</div>
I would much rather implement this transition/animation effect without having to write any JavaScript if possible. So I wrote the below snippet that is invoked immediately when the CSS interpreter evaluates the animation property:
.effect:hover {
animation: 1s shrink forwards;
}
#keyframes shrink {
to {
transform-origin: left top;
transform: scale(.5, .5);
}
}
<div>
<span style="display: inline-block; font-size: x-large;" class="effect">HOVER OVER THIS</span>
</div>
After running both of these snippets and comparing the result you should immediately see the problem I am having. The animation in the second snippet is adding something extra to the movement of the text as it shrinks... It's almost like a "curving" motion. Not sure how else to describe it.
Does anyone know what is going on here and how I can eliminate this "curving" behavior?
This is because the default transform-origin is 50%, 50% 0 (x-axis, y-axis, z-axis). So right now the transform-origin moves from 50%, 50% 0 to top left 0 causing the element to "curve inwards" while scaling down.
Try this:
.effect {
transform-origin: left top;
}
.effect:hover {
animation: 1s shrink;
}
#keyframes shrink {
from {
transform: scale(1, 1);
}
to {
transform: scale(.5, .5);
}
}
<div>
<span style="display: inline-block; font-size: x-large;" class="effect">HOVER OVER THIS</span>
</div>

SVG Animation - Animating SVG groups

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.

Controlling horizontal scrolling when using waypoints.js

I am building a custom wizard form with waypoints. Something interesting is happening and I can't figure it out for the life of me.
My sample CODEPEN is showing 2 pages of the wizard process to show you what I mean.
When you hit the forward action button (search in the first page of the wizard), the waypoints slide from the right and the next page or screen shows. That would repeat on-forward and backwards if I click on the backward action button. That is working.
The problem I see is with the initial horizontal scrollbar. It shows on page load, which it's a problem because the user could just scroll to the next screen by dragging the scrollbar. I thought of giving it an overflow-x but it didn't fix the issue. The interesting thing is, if I click on the search button and the waypoint slides, the scroll bar disappears and gives me the desired effect! What gives?
I built the CODEPEN as close as possible to the real environment so that you guys can catch any conflict with other elements instead of isolating the problem.
Here is the related code just in case:
HTML:
<div id="content" class="content">
<div class="row page">
<!-- First page content here -->
</div>
<div class="row page2">
<!-- Second page content here -->
</div>
</div>
CSS:
.page, .page2 {
position: absolute;
top: 20px;
left: 10px;
width: 100%;
-webkit-transition: -webkit-transform 0.8s;
transition: -webkit-transform 0.8s;
transition: transform 0.8s;
transition: transform 0.8s, -webkit-transform 0.8s
}
.page {
-webkit-transform: translateX(0%);
transform: translateX(0%)
}
.show-page2 .page {
-webkit-transform: translateX(-100%);
transform: translateX(-100%)
}
.page2 {
-webkit-transform: translateX(100%);
transform: translateX(100%)
}
.show-page2 .page2 {
-webkit-transform: translateX(0%);
transform: translateX(0%)
}
JS:
(function () {
var body = $('#content'),
nav = $('.btn-waypoint'),
panels = $('#content');
nav.on('click', function (e) {
e.preventDefault();
var dest = $(this).data('panel-link');
body
.removeClass(function (index, css) {
// remove only classes start with show-
// http://stackoverflow.com/questions/2644299/jquery-removeclass-wildcard
return (css.match(/\bshow-\S+/g) || []).join(' ');
})
.addClass('show-' + dest);
});
}());
The closest fix I've tried to solve this is making page2 display:none on page load to eliminate the scrollbar and then making it visible on button click. That almost did it except a funky look happens between the waypoint sliding effect and the css fade effect. Here is the code for that:
JS
$( document ).ready(function() {
$('.page2').css('display', 'none');
$('[data-panel-link]').on('click', function(){
$('.page2').css('display', 'block');
});
});
Here is the link to my CODEPEN
Thanks in advance!
As it plays out the root of the problem is the hard positioning. The waypoint divs are natively in vertical position which they would obviously not produce a horizontal scrollbar. They are being forced to be side by side by position:absolute and the transform: translateX(-100%) and this creates the horizontal scrollbar. If the mousewheel is disabled via jQuery the scrollbar goes away, but it goes away vertically as well. So instead of fighting that battle, a better alternative is to use a different transition that looks good but doesn't require a side by side animation. A fade will do just nice:
Simply replace the css effects from translateX to the following:
.page, .page2{
position: absolute;
width:100%;
transition: opacity 1s ease-in-out;
-moz-transition: opacity 1s ease-in-out;
-webkit-transition: opacity 1s ease-in-out;
}
.page {
opacity: 1;
}
.show-page2 .page {
opacity: 0;
}
.page2{
opacity: 0;
}
.show-page2 .page2{
opacity: 1;
}

How to change the execution time of an infinite animation(in CSS3) smoothly with Javascript

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

CSS Animation Forward then Backward Flickering

I'm trying to create a CSS animation that when a user clicks an element it animates to the right then when they click it again it animates to the left. The problem I have is that it is introducing flickering. I know the reason, but I'm not sure the best way to fix it. I want the most elegant solution for this problem.
I setup a jsFiddle (WebKit only) here: http://jsfiddle.net/4Ad5c/2/
The CSS:
.animateRight{
-webkit-animation: goRightLeft 1s;
-webkit-animation-fill-mode: forwards;
}
.animateLeft{
-webkit-animation: goRightLeft 1s;
-webkit-animation-fill-mode: backwards;
-webkit-animation-direction: reverse;
}
#-webkit-keyframes goRightLeft {
0%{margin-left: 0px;}
100%{margin-left: 100px;}
}
The JavaScript:
this.animateBox = function(className){
var box = document.getElementsByClassName('box')[0];
box.className = "box";
setTimeout(function(){
box.className = "box " + className;
},1);
};
When you click the Animate Right it works as expected, but when you click the Animate Left it will flicker to the left and then animate as expected. The reason is that you have to remove the class and add another in order to get the animate to run again, but I don't know the best way to get this working. I figure I could add a class when removing the previous animation that has it in its current state, but that seems wrong to me.
Thanks for the help!
Reason for flickering:
You are applying class box on click before setting the next animationClass which makes the box go to left abruptly. and then you are applying the animation to go reverse. So it causes flickering while it abrupty goes left (removal of class) and adding class in timeout causes revereses animation according to the fillmode and direction in animateLeft class and makes it even more worser as goRightLeft again adds margin pulls it to right due to the margin in the rule and webkit-animation-fill-mode: backwards; pushes to to the left. So one approach i mentioned here is to do the reverse (adding/reducing) the margin.
Here is one solution for this:
For real reverse animation you need to apply margin decresing from 100px to 0 as you do while forward animation. So just add keyframes for LeftToRight and apply that in animation.
Css
.animateRight{
-webkit-animation: goRightLeft 1s;
-webkit-animation-fill-mode: forwards;
}
.animateLeft{
-webkit-animation: goLeftRight 1s; /* Note the goLeftRight animation */
-webkit-animation-fill-mode: backwards;
}
#-webkit-keyframes goRightLeft {
0%{margin-left: 0px;}
100%{margin-left: 100px;}
}
#-webkit-keyframes goLeftRight { /* Note the new keyframes rule for reverse animation*/
0%{margin-left: 100px;}
100%{margin-left: 0px;}
}
Script
this.animateBox = function(className){
var box = document.getElementsByClassName('box')[0];
box.className = "box " + className;
};
Demo

Categories