I have an element with two classes, one called "rotate" that will rotate the element 360 degrees and another called "doublesize" that will scale the element 2x its normal size:
.rotate {
transform: rotate(0deg);
}
.rotate:hover {
transform: rotate(360deg);
}
.doublesize {
transform: scale(1);
}
.doublesize:hover {
transform: scale(2);
}
http://jsfiddle.net/Sbw8W/
I'm guessing this does not work because the classes override each other's transform property?
I know that I could easily do this in one CSS rule like:
.doublerotatesize {
transform: scale(1) rotate(0deg);
}
.doublerotatesize:hover {
transform: scale(2) rotate(360deg);
}
But I would like to be able to apply each class separately from the other if it is possible.
I'm guessing this does not work because the classes override each other's transform property?
Correct. This is an unfortunate limitation as a side-effect of how the cascade works.
You will have to specify both functions in a single transform declaration. You could simply chain both class selectors together instead of creating a new class for a combined transform:
.doublesize.rotate {
-webkit-transform: scale(1) rotate(0deg);
}
.doublesize.rotate:hover {
-webkit-transform: scale(2) rotate(360deg);
}
... but as you can see, the issue lies in the transform property rather than in the selector.
This is expected to be rectified in Transforms level 2, where each transform has been promoted to its own property, which would allow you to combine transforms simply by declaring them separately as you would any other combination of CSS properties. This means you would be able to simply do this:
/* Note that rotate: 0deg and scale: 1 are omitted
as they're the initial values */
.rotate:hover {
rotate: 360deg;
}
.doublesize:hover {
scale: 2;
}
... and take advantage of the cascade rather than be hindered by it. No need for specialized class names or combined CSS rules.
Using CSS variables you can have this separation. The idea is to chain as many transformation as you want inside the element using CSS variables then later you update each variable individually:
div {
width: 100px;
height: 100px;
display: inline-block;
background-color: red;
transform:
/* I prepared 3 placeholder so we can chain 3 transformation later */
var(--t1,) /* we need "nothing" as fallbak*/
var(--t2,)
var(--t3,);
}
.transitionease {
transition: all 1s ease;
}
.transitionease:hover {
transition: all 3s ease;
}
.rotate {
--t1: rotate(0deg);
}
.rotate:hover {
--t1: rotate(360deg);
}
.doublesize {
--t2: scale(1);
}
.doublesize:hover {
--t2: scale(2);
}
<div class="transitionease"></div>
<div class="transitionease doublesize rotate "></div>
<div class="transitionease doublesize"></div>
You can surely combine multiple animation, but not by combining CSS classes. See the first answer here : combining multiple css animations into one overall animation
The first part tells you how to combine animation with CSS with some parameters (delay, duration) :
.outside.animate {
-webkit-animation-delay: 0s, .5s, .5s;
-webkit-animation-duration: 500ms, 1000ms, 1000ms;
-webkit-animation-name: button-bounce, rotate, skyblue;
}
For sure, you firstly need to define your animations.
You can't do this using just CSS, but if you don't mind using some jQuery you can attach this effect:
var $elem = $('.cssDropPinImage');
$({deg: 0}).animate({deg: 360}, {
duration: 600,
step: function(now) {
var scale = (2 * now / 360);
$elem.css({
transform: 'rotate(' + now + 'deg) scale(' + scale + ')'
});
}
});
body {
padding: 50px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<img src="https://via.placeholder.com/40" class="cssDropPinImage">
The question hints at a scale and rotate animation on hover. Another pragmatic way of solving this, is by rearranging the elements in the HTML and applying the individual transforms to the parent and the child, only responding to the hover on the parent element.
That looks something like this:
<div class="doublesize">
<div class="rotate"></div>
</div>
.doublesize {
transition: transform 1s ease;
}
.rotate {
width: 100px;
height: 100px;
background-color: red;
transition: transform 1s ease;
}
.doublesize:hover {
transform: scale(2);
}
.doublesize:hover .rotate {
transform: rotate(360deg);
}
http://jsfiddle.net/n38csxy1/4/
I know this is quite a leap from the original setup, but I hope this perspective helps someone nevertheless.
Related
I want to use https://animate.style/. But it's more than 90 KBs in size.
I just want a very simple bouncing animation. That's all.
Apart from going into the source code and trying to recreate the bouncing keyframes and animations, is there another way to do so?
For example, in Material UI, or in TailwindCSS only what you have used would be included in the final bundle.
Is there something similar for Animate.css too?
If you only need a simple bouncing animation, why not using your own keyframes?
Exemple falling down :
#keyframes myAnim {
0% {
animation-timing-function: ease-in;
opacity: 1;
transform: translateY(-45px);
}
75% {
transform: translateY(10px);
}
100% {
transform: translateY(0px);
}
}
#my_little_square {
width:50px;
height:50px;
background-color:#f00;
animation: myAnim 1s ease 0s 1 normal forwards;
}
<div id="my_little_square">
</div>
Here is a little tool to help you start : https://webcode.tools/generators/css/keyframe-animation
I have an element with two classes, one called "rotate" that will rotate the element 360 degrees and another called "doublesize" that will scale the element 2x its normal size:
.rotate {
transform: rotate(0deg);
}
.rotate:hover {
transform: rotate(360deg);
}
.doublesize {
transform: scale(1);
}
.doublesize:hover {
transform: scale(2);
}
http://jsfiddle.net/Sbw8W/
I'm guessing this does not work because the classes override each other's transform property?
I know that I could easily do this in one CSS rule like:
.doublerotatesize {
transform: scale(1) rotate(0deg);
}
.doublerotatesize:hover {
transform: scale(2) rotate(360deg);
}
But I would like to be able to apply each class separately from the other if it is possible.
I'm guessing this does not work because the classes override each other's transform property?
Correct. This is an unfortunate limitation as a side-effect of how the cascade works.
You will have to specify both functions in a single transform declaration. You could simply chain both class selectors together instead of creating a new class for a combined transform:
.doublesize.rotate {
-webkit-transform: scale(1) rotate(0deg);
}
.doublesize.rotate:hover {
-webkit-transform: scale(2) rotate(360deg);
}
... but as you can see, the issue lies in the transform property rather than in the selector.
This is expected to be rectified in Transforms level 2, where each transform has been promoted to its own property, which would allow you to combine transforms simply by declaring them separately as you would any other combination of CSS properties. This means you would be able to simply do this:
/* Note that rotate: 0deg and scale: 1 are omitted
as they're the initial values */
.rotate:hover {
rotate: 360deg;
}
.doublesize:hover {
scale: 2;
}
... and take advantage of the cascade rather than be hindered by it. No need for specialized class names or combined CSS rules.
Using CSS variables you can have this separation. The idea is to chain as many transformation as you want inside the element using CSS variables then later you update each variable individually:
div {
width: 100px;
height: 100px;
display: inline-block;
background-color: red;
transform:
/* I prepared 3 placeholder so we can chain 3 transformation later */
var(--t1,) /* we need "nothing" as fallbak*/
var(--t2,)
var(--t3,);
}
.transitionease {
transition: all 1s ease;
}
.transitionease:hover {
transition: all 3s ease;
}
.rotate {
--t1: rotate(0deg);
}
.rotate:hover {
--t1: rotate(360deg);
}
.doublesize {
--t2: scale(1);
}
.doublesize:hover {
--t2: scale(2);
}
<div class="transitionease"></div>
<div class="transitionease doublesize rotate "></div>
<div class="transitionease doublesize"></div>
You can surely combine multiple animation, but not by combining CSS classes. See the first answer here : combining multiple css animations into one overall animation
The first part tells you how to combine animation with CSS with some parameters (delay, duration) :
.outside.animate {
-webkit-animation-delay: 0s, .5s, .5s;
-webkit-animation-duration: 500ms, 1000ms, 1000ms;
-webkit-animation-name: button-bounce, rotate, skyblue;
}
For sure, you firstly need to define your animations.
You can't do this using just CSS, but if you don't mind using some jQuery you can attach this effect:
var $elem = $('.cssDropPinImage');
$({deg: 0}).animate({deg: 360}, {
duration: 600,
step: function(now) {
var scale = (2 * now / 360);
$elem.css({
transform: 'rotate(' + now + 'deg) scale(' + scale + ')'
});
}
});
body {
padding: 50px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<img src="https://via.placeholder.com/40" class="cssDropPinImage">
The question hints at a scale and rotate animation on hover. Another pragmatic way of solving this, is by rearranging the elements in the HTML and applying the individual transforms to the parent and the child, only responding to the hover on the parent element.
That looks something like this:
<div class="doublesize">
<div class="rotate"></div>
</div>
.doublesize {
transition: transform 1s ease;
}
.rotate {
width: 100px;
height: 100px;
background-color: red;
transition: transform 1s ease;
}
.doublesize:hover {
transform: scale(2);
}
.doublesize:hover .rotate {
transform: rotate(360deg);
}
http://jsfiddle.net/n38csxy1/4/
I know this is quite a leap from the original setup, but I hope this perspective helps someone nevertheless.
I have write a less package for my work.
Some code like this:
.goTop(#time:1s,#position:-100%){
#random_suffix:`Math.round(Math.random() * 1.0e8)`;
#keyframes #random_suffix{
from{transform:translateY(0);}
to{transform:translateY(#position);}
}
animation-name:#random_suffix;
animation-duration:#time;
animation-timing-function:ease-out;
animation-fill-mode:forwards;
}
Then I can use it on somewhere I need.
Like this:
.testbox{
.goTop(1s);
}
Why I should use a random number?
Because,if all of the keyframes are the same name,and the last part will overwrite the old part.(the arguments are not the same)
Now,the keyframe name is a random number.
But when the keyframe name is a number,the animation will not run.
So I hope the name like goTop_12345678.
I try to contact the string for the variable name.
#temp1:goTop_;
#temp2:`Math.round(Math.random() * 1.0e8)`;
#test:#temp1 + #temp2;
// Error
//{"type":"Syntax","message":"Cannot read property 'numerator' of undefined","filename":"input","index":94,"line":4,"callLine":null,"column":0,"extract":["#temp2:goTop_;","#test:#temp1 + #temp2;",""]}
#temp1:'goTop_';
#temp2:`Math.round(Math.random() * 1.0e8)`;
#test:`#{temp1} + #{temp2}`;
//"goTop_12345678"
//It contains the symbol quote "",and the animation will not run.
#temp1:goTop_;
#temp2:#temp1+`Math.round(Math.random() * 1.0e8)`;
#test:#temp2;
//"goTop_ 12345678"
//It contains the space ,and the animation will not run too.
//For the second and third way
//I have try to use replace way to solve it,but I failed.
so,...
How to solve this problem...
Thanks a lot.
#cloudhead
Thanks for #seven-phases-max.
the right way to write the var is like this:
#random: `Math.round(Math.random() * 1.0e8)`;
#name: ~"go-top-#{random}"; // <- here we go
//or
#name:~"go-top-`Math.round(Math.random() * 1.0e8)`";
But the new problem is that the random number are not the same...
.test {
background-color: #f39;
width: 200px;
height: 200px;
margin-top: 400px;
-webkit-animation-name: goTop_70024767;
animation-name: goTop_70024767;
-webkit-animation-duration: 1s;
animation-duration: 1s;
-webkit-animation-timing-function: ease-out;
animation-timing-function: ease-out;
-webkit-animation-fill-mode: forwards;
animation-fill-mode: forwards;
}
#-webkit-keyframes goTop_50030730 {
from {
-webkit-transform: translateY(0);
transform: translateY(0);
}
to {
-webkit-transform: translateY(-100%);
transform: translateY(-100%);
}
}
#keyframes goTop_50030730 {
from {
-webkit-transform: translateY(0);
transform: translateY(0);
}
to {
-webkit-transform: translateY(-100%);
transform: translateY(-100%);
}
}
The last solution,some code like this:
.goTop(#time:1s,#position:-100%,#ease:ease-out,#fillmode:forwards){
.goTopKeyframe(~`'goTop_'+Math.round(Math.random() * 1.0e8)`);
}
.goTopKeyframe(#name){
#keyframes #name{
from{transform:translateY(0);}
to{transform:translateY(#position);}
}
animation-name:#name;
animation-duration:#time;
animation-timing-function:#ease;
animation-fill-mode:#fillmode;
}
We are implementing a wishlist functionality on a site we are developing, and when the user clicks on an icon to add the current item to a wishlist, it fires an ajax request.
Now while that ajax request is doing it's business, we add a loading class to the icon, so it scales bigger and smaller slightly. My issue, is once the ajax request has finished loading, we then remove that class, but the animation abruptly stops rather than scaling back down to it's initial size.
How can we make the animation finish, rather than just suddenly stopping?
Below is my CSS:
/**
* Keyframes
*/
#keyframes breathe {
50% {
transform: scale(1.2);
}
100% {
transform: scale(1);
}
}
/**
* Icon
*/
.wishlist-icn {
transition: all .3s ease;
}
.wishlist-icn--isAdded {
fill: #4B3814;
}
.wishlist-icn--isLoading {
animation: breathe 2s ease-in-out 0s infinite normal both;
}
tl;dr: Try applying animation-fill-mode: forwards
The normal behavior after CSS animation is done is that it resets the styles to the initial state. The way I see the process here is that when you remove the --isLoading class the animation stops and reverts the styles to the original state. Only after then the transitions starts to work and has nothing to do since the styles already are reset. animation-fill-mode: forwards in .wishlist-icn would prevent the animation from resetting, thus the transition would be able to operate gradually. To be sure you can add transform: scale(1) to .wishlist-icn or to :not(.wishlist-icn--isLoading) so that the transition knew what to head for. Not that I have tested it in this particular case, but it's worth trying ;P
$('.start-animation').on('click', function() {
$('.wishlist-icn').addClass('wishlist-icn--isLoading');
});
$('.stop-animation').on('click', function() {
$(".wishlist-icn").bind("mozAnimationIteration animationiteration webkitAnimationIteration", function() {
$(this).removeClass("wishlist-icn--isLoading");
});
});
/**
* Keyframes
*/
#keyframes breathe {
50% {
transform: scale(1.2);
}
100% {
transform: scale(1);
}
}
/**
* Icon
*/
.wishlist-icn {
position: relative;
display: inline;
transition: all 2s ease;
font-size: 5em;
}
.wishlist-icn--isAdded {
fill: #4B3814;
}
.wishlist-icn--isLoading {
animation: breathe 2s ease-in-out 0s infinite normal both;
animation-fill-mode: forward;
}
.wishlist-icn--isLoaded {
transform: scale(1);
}
}
<link href="http://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="wishlist-icn"> <i class="ion-heart"></i>
</div>
<button class="start-animation">start animation</button>
<button class="stop-animation">stop animation</button>
you would probably have to use javascript to listen for the animation iteration to end then remove the class.
$(".wishlist-icn").bind( "mozAnimationIteration animationiteration webkitAnimationIteration" , function() {
$(this).removeClass("wishlist-icn--isLoading");
});
So, it is possible to have reverse animation on mouse out such as:
.class{
transform: rotate(0deg);
}
.class:hover{
transform: rotate(360deg);
}
but, when using #keyframes animation, I couldn't get it to work, e.g:
.class{
animation-name: out;
animation-duration:2s;
}
.class:hover{
animation-name: in;
animation-duration:5s;
animation-iteration-count:infinite;
}
#keyframe in{
to {transform: rotate(360deg);}
}
#keyframe out{
to {transform: rotate(0deg);}
}
What is the optimal solution, knowing that I'd need iterations and animation itself?
http://jsfiddle.net/khalednabil/eWzBm/
I think that if you have a to, you must use a from.
I would think of something like :
#keyframe in {
from: transform: rotate(0deg);
to: transform: rotate(360deg);
}
#keyframe out {
from: transform: rotate(360deg);
to: transform: rotate(0deg);
}
Of course must have checked it already, but I found strange that you only use the transform property since CSS3 is not fully implemented everywhere. Maybe it would work better with the following considerations :
Chrome uses #-webkit-keyframes, no particuliar version needed
Safari uses #-webkit-keyframes since version 5+
Firefox uses #keyframes since version 16 (v5-15 used #-moz-keyframes)
Opera uses #-webkit-keyframes version 15-22 (only v12 used #-o-keyframes)
Internet Explorer uses #keyframes since version 10+
EDIT :
I came up with that fiddle :
http://jsfiddle.net/JjHNG/35/
Using minimal code. Is it approaching what you were expecting ?
Its much easier than all this: Simply transition the same property on your element
.earth { width: 0.92%; transition: width 1s; }
.earth:hover { width: 50%; transition: width 1s; }
https://codepen.io/lafland/pen/MoEaoG
I don't think this is achievable using only CSS animations. I am assuming that CSS transitions do not fulfil your use case, because (for example) you want to chain two animations together, use multiple stops, iterations, or in some other way exploit the additional power animations grant you.
I've not found any way to trigger a CSS animation specifically on mouse-out without using JavaScript to attach "over" and "out" classes. Although you can use the base CSS declaration trigger an animation when the :hover ends, that same animation will then run on page load. Using "over" and "out" classes you can split the definition into the base (load) declaration and the two animation-trigger declarations.
The CSS for this solution would be:
.class {
/* base element declaration */
}
.class.out {
animation-name: out;
animation-duration:2s;
}
.class.over {
animation-name: in;
animation-duration:5s;
animation-iteration-count:infinite;
}
#keyframes in {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
#keyframes out {
from {
transform: rotate(360deg);
}
to {
transform: rotate(0deg);
}
}
And using JavaScript (jQuery syntax) to bind the classes to the events:
$(".class").hover(
function () {
$(this).removeClass('out').addClass('over');
},
function () {
$(this).removeClass('over').addClass('out');
}
);
Creating a reversed animation is kind of overkill to a simple problem. What you need is:
animation-direction: reverse
However, this won't work on its own because animation spec forgot to add a way to restart the animation, so here is how you do it with the help of JS
let item = document.querySelector('.item')
// play normal
item.addEventListener('mouseover', () => {
item.classList.add('active')
})
// play in reverse
item.addEventListener('mouseout', () => {
item.style.opacity = 0 // avoid showing the init style while switching the 'active' class
item.classList.add('in-active')
item.classList.remove('active')
// force dom update
setTimeout(() => {
item.classList.add('active')
item.style.opacity = ''
}, 5)
item.addEventListener('animationend', onanimationend)
})
function onanimationend() {
item.classList.remove('active', 'in-active')
item.removeEventListener('animationend', onanimationend)
}
#keyframes spin {
0% {
transform: rotateY(0deg);
}
100% {
transform: rotateY(180deg);
}
}
div {
background: black;
padding: 1rem;
display: inline-block;
}
.item {
/* because span cant be animated */
display: block;
color: yellow;
font-size: 2rem;
}
.item.active {
animation: spin 1s forwards;
animation-timing-function: ease-in-out;
}
.item.in-active {
animation-direction: reverse;
}
<div>
<span class="item">ABC</span>
</div>
we can use requestAnimationFrame to reset animation and reverse it when browser paints in next frame.
Also use onmouseenter and onmouseout event handlers to reverse animation direction
As per
Any rAFs queued in your event handlers will be executed in the same
frame. Any rAFs queued in a rAF will be executed in the next frame.
function fn(el, isEnter) {
el.className = "";
requestAnimationFrame(() => {
requestAnimationFrame(() => {
el.className = isEnter? "in": "out";
});
});
}
.in{
animation: k 1s forwards;
}
.out{
animation: k 1s forwards;
animation-direction: reverse;
}
#keyframes k
{
from {transform: rotate(0deg);}
to {transform: rotate(360deg);}
}
<div style="width:100px; height:100px; background-color:red"
onmouseenter="fn(this, true)"
onmouseleave="fn(this, false)"
></div>
Would you be better off having just the one animation, but having it reverse?
animation-direction: reverse
Using transform in combination with transition works flawlessly for me:
.ani-grow {
-webkit-transition: all 0.5s ease;
-moz-transition: all 0.5s ease;
-o-transition: all 0.5s ease;
-ms-transition: all 0.5s ease;
transition: all 0.5s ease;
}
.ani-grow:hover {
transform: scale(1.01);
}
I've put together a CodePen with a CSS-only fix and one with 2 lines of jQuery to fix the on-page load issue. Continue reading to understand the 2 solutions in a simpler version.
https://codepen.io/MateoStabio/pen/jOVvwrM
If you are searching how to do this with CSS only, Xaltar's answer is simple, straightforward, and is the correct solution. The only downside is that the animation for the mouse out will play when the page loads. This happens because to make this work, you style your element with the OUT animation and the :hover with the IN animation.
svg path{
animation: animateLogoOut 1s;
}
svg:hover path{
animation: animateLogoIn 1s;
}
#keyframes animateLogoIn {
from {stroke-dashoffset: -510px;}
to {stroke-dashoffset: 0px;}
}
#keyframes animateLogoOut {
from {stroke-dashoffset: 0px;}
to {stroke-dashoffset: -510px;}
}
Some people found this solution to be useless as it played on page load. For me, this was the perfect solution. But I made a Codepen with both solutions as I will probably need them in the near future.
If you do not want the CSS animation on page load, you will need to use a tiny little script of JS that styles the element with the OUT animation only after the element has been hovered for the first time. We will do this by adding a class of .wasHovered to the element and style the added class with the OUT Animation.
jQuery:
$("svg").mouseout(function() {
$(this).addClass("wasHovered");
});
CSS:
svg path{
}
svg.wasHovered path{
animation: animateLogoOut 1s;
}
svg:hover path{
animation: animateLogoIn 1s;
}
#keyframes animateLogoIn {
from {stroke-dashoffset: -510px;}
to {stroke-dashoffset: 0px;}
}
#keyframes animateLogoOut {
from {stroke-dashoffset: 0px;}
to {stroke-dashoffset: -510px;}
}
And voila! You can find all of this and more on my codepen showing in detail the 2 options with an SVG logo hover animation.
https://codepen.io/MateoStabio/pen/jOVvwrM
Have tried several solutions here, nothing worked flawlessly; then Searched the web a bit more, to find GSAP at https://greensock.com/ (subject to license, but it's pretty permissive); once you reference the lib ...
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.2.4/gsap.min.js"></script>
... you can go:
var el = document.getElementById('divID');
// create a timeline for this element in paused state
var tl = new TimelineMax({paused: true});
// create your tween of the timeline in a variable
tl
.set(el,{willChange:"transform"})
.to(el, 1, {transform:"rotate(60deg)", ease:Power1.easeInOut});
// store the tween timeline in the javascript DOM node
el.animation = tl;
//create the event handler
$(el).on("mouseenter",function(){
//this.style.willChange = 'transform';
this.animation.play();
}).on("mouseleave",function(){
//this.style.willChange = 'auto';
this.animation.reverse();
});
And it will work flawlessly.
Try this:
#keyframe in {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
#keyframe out {
from {
transform: rotate(360deg);
}
to {
transform: rotate(0deg);
}
}
supported in Firefox 5+, IE 10+, Chrome, Safari 4+, Opera 12+