I try to set up this LESS mixin for CSS animation keyframes:
.keyframes(#name, #from, #to) {;
#-webkit-keyframes "#name" {
from {
#from;
}
to {
#to;
}
}
}
but there is some problem with name pharse, is there any option to do this corectly?
As of LESS >= 1.7 you can use variables for keyframe keywords (names).
Some changes have been made in LESS 1.7 to how directives work, which allows to use variables for the name/keyword of #keyframes (so the example from the question should work now).
Unfortunately keyframes names can not be dynamically generated in LESS <= 1.6
Hence, the normal way of going about keyframes would use hardcoded names and you would only call for specific "for" and "to" mixins, like this:
.colors-mixin-frame(#from,#to){
from {color: #from;}
to {color: #to;}
}
.width-mixin-frame(#from,#to){
from {width: #from;}
to {width: #to;}
}
// keyframes with hardcoded names calling for specific mixin frames
#keyframes red-blue { .colors-mixin-frame(red, blue); }
#keyframes change-width { .width-mixin-frame(254px, 512px); }
But you can use a workaround to dynamically generate the names
where you inject the name into the rule name, this however requires an declaration of the next rule that supplies the closing bracket } at the end of the keyframes declaration. The most convenient is if you just build the animation calling that keyframe
.animation-keyframes(#name, #from, #to, #anim-selector) {
#keyf: ~"#keyframes #{name} { `'\n'`from ";
#anim: ~"} `'\n'`.#{anim-selector}";
#{keyf} {
.from(#name,#from);
}
to {
.to(#name,#to);
}
#{anim} {
animation-name:#name;
}
}
Note that you also need to define .from(){} and .to(){} mixins, and not just use #from and #to like you did in your example (because LESS also does not allow for dynamically generated properties) ... this mixins can now construct the desired properties and values ... to use specific property you can use guards or name-specific mixins like these:
// name-specific from and to mixins that are used if first argument equals "colors"
.from (colors, #color) {
color: #color;
}
.to (colors, #color) {
color: #color;
}
Now we can call our mixin in LESS:
// test
.animation-keyframes (colors, red, blue, my-colanim);
and get CSS:
#keyframes colors {
from {
color: #ff0000;
}
to {
color: #0000ff;
}
}
.my-colanim {
animation-name: colors;
}
this will work also in LESS 1.4, but note that we used javascript interpolation for line breaks, which requires a javascript implementation of LESS.
Edit: to your additional question about prefixes
Mixin with vendor prefixes
Here I made two mixins ... one without vendor prefixes and one with them both calling a general .keyframes mixin:
.keyframes (#name, #from, #to, #vendor:"", #bind:"") {
#keyf: ~"#{bind}##{vendor}keyframes #{name} { `'\n'`from ";
#{keyf} {
.from(#name,#from);
}
to {
.to(#name,#to);
}
}
.animation-keyframes-novendor (#name, #from, #to, #anim-selector) {
.keyframes (#name, #from, #to);
#anim: ~"} `'\n'`.#{anim-selector}";
#{anim} {
animation-name:#name;
}
}
.animation-keyframes (#name, #from, #to, #anim-selector) {
#bind: "} `'\n'`";
.keyframes (#name, #from, #to, "-moz-");
.keyframes (#name, #from, #to, "-webkit-", #bind);
.keyframes (#name, #from, #to, "-o-", #bind);
.keyframes (#name, #from, #to, "-ms-", #bind);
.keyframes (#name, #from, #to, "", #bind);
#anim: ~"} `'\n'`.#{anim-selector}";
#{anim} {
-moz-animation: #name;
-webkit-animation: #name;
-o-animation: #name;
-ms-animation: #name;
animation: #name;
}
}
.from (colors, #color) {
color: #color;
}
.to (colors, #color) {
color: #color;
}
/* keyframes with all vendor prefixes */
.animation-keyframes (colors, red, blue, my-colanim);
/* keyframes with no vendor prefix */
.animation-keyframes-novendor (colors, red, blue, my-colanim);
The .animation-keyframes will now produce keyframes for all vendor prefixes and an animation selector with vendor prefixed properties. And as expected the .animation-keyframes-novendor gives the same output as the above simple solution (without vendor prefixes).
Some notes:
For your animation to actually work you need to set other animation parameters like timing-function, duration, direction, iteration-count (requires at least a duration time in addition to the name that we already set).
For example:
animation: #name ease-in-out 2s infinite alternate;
If you wrap above mixins in namespaces make sure you change the mixin references inside other mixins to their whole path (including the namespaces).
For example:
#namespace > .keyframes () // see .less source in the demo for details
I am currently working on a mixin library
The source can be found here https://github.com/pixelass/more-or-less
My keyframe mixin looks like this:
WORKS FOR LESS 1.7.x
MIXIN
.keyframes(#name) {
#-webkit-keyframes #name {
.-frames(-webkit-);
}
#-moz-keyframes #name {
.-frames(-moz-);
}
#keyframes #name {
.-frames();
}
}
INPUT
& {
.keyframes(testanimation);.-frames(#-...){
0% {
left: 0;
#{-}transform: translate(10px, 20px);
}
100% {
left: 100%;
#{-}transform: translate(100px, 200px);
}
}
}
OUTPUT
#-webkit-keyframes testanimation {
0% {
left: 0;
-webkit-transform: translate(10px, 20px);
}
100% {
left: 100%;
-webkit-transform: translate(100px, 200px);
}
}
#-moz-keyframes testanimation {
0% {
left: 0;
-moz-transform: translate(10px, 20px);
}
100% {
left: 100%;
-moz-transform: translate(100px, 200px);
}
}
#keyframes testanimation {
0% {
left: 0;
transform: translate(10px, 20px);
}
100% {
left: 100%;
transform: translate(100px, 200px);
}
}
How about this:
#-webkit-keyframes some-animation {.mixi-frames;}
#-moz-keyframes some-animation {.mixi-frames;}
#-ms-keyframes some-animation {.mixi-frames;}
#-o-keyframes some-animation {.mixi-frames;}
#keyframes some-animation {.mixi-frames;}
.mixi-frames () {
from {width: 254px;}
to {width: 512px;}
}
You need to do it for each animation.
Taken from: http://radiatingstar.com/css-keyframes-animations-with-less
Also thanks to the great answer by Martin Turjak, (thank you for that) I just put on github a less mixin which generate keyframes and animation's css code without hacks and in a flexible way, you can find it here github.com/kuus/animate-me.less.
With this mixin you can write this code to obtain valid and cross browser css (see the github repo for a complete explanation):
.animate-me(ComplexAnimation; 0.4s ease; '.complex-animation';
'50%, 100%'; '%stransform: translateZ(-250px) rotateY(30deg)';
70%; '%stransform: translateZ(-250px) rotateY(30deg); opacity: .5; background: green';
30%; '%stransform: translateZ(-250px) rotateY(30deg); opacity: .2; background: yellow';
80%; '%stransform: translateZ(-250px) rotateY(30deg); opacity: 1; background: red'
);
Before-mentioned https://github.com/kuus/animate-me.less does things!
You can also check out this one written by me (seems to be neater):
https://github.com/thybzi/keyframes
I think you should do this
#-webkit-keyframes #name
{
code...
}
change "#name" to #name
and you should delete ; after
.keyframes(#name, #from, #to) {
Related
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.
how to write Less mixin for keyframes.
I have tried in the following way but it is giving error,
ParseError: Directive options not recognized.
.keyFrameAlert(#-webkit-keyframes);
Mixin
.keyFrameAlert(#keyFrame){
#keyFrame alert {
0% {
opacity: 0;
}
50% {
opacity: 1;
}
100% {
top: 0;
}
}
}
can anybody help on this issue.
I think it might be throwing an error because of the # prefix for your keyframes, so where your passing #-webkit-keyframes it thinks your trying to pass it a variable with that same name.
There is a slightly different approach to this, where you can declare your keyframes, and add a class inside it which contains your keyframe set.
#-webkit-keyframes alert {.keyframes;}
#keyframes alert {.keyframes;}
.keyframes () {
0% {
opacity: 0;
}
50% {
opacity: 1;
}
100% {
top: 0;
}
}
This is slightly different to what you were trying previously, as you would still need to type out all of your vendor prefixes, but you only need to change your keyframes in one place.
I've pieced together this approach for generating vendor-prefixed properties and animations using LESS. First some factory functions:
.vendorprefix (#property, #value) {
-webkit-#{property}: #value;
-moz-#{property}: #value;
-ms-#{property}: #value;
-o-#{property}: #value;
#{property}: #value;
}
.keyframes(#name; #animation) {
#animation();
#-webkit-keyframes #name { .frames(-webkit-) }
#-moz-keyframes #name { .frames(-moz-) }
#-o-keyframes #name { .frames(-o-) }
#keyframes #name { .frames(~'') }
}
The '.vendorprefix' function can be used for general purpose properties including setting animations, e.g:
.element {
.vendorprefix(animation; slideout 1s);
}
The '.keyframes' function has a '.frames' mixin as one of its arguments which it uses to generate vendor prefixed keyframes. It also passes a '#vendor' argument to the '.frames' mixin so you can add vendor specific properties. e.g:
.keyframes (slideout; {
.frames(#vendor) {
0% {
#{vendor}transform: translate(0px, 0px);
}
100% {
#{vendor}transform: translate(100px, 0px);
}
}
});
This does work, but does anyone have a better method?
You can implement vendor-prefixing in LESS, but far better tools already exist.
Try to use css-postprocessor like Myth, it has auto-prefixing feature.
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 realized that I can't simple accomplish the same code below by separating by coma #keyframes mymove, #-moz-keyframes mymove, etc... In order for them to work I need to declare it each one separately as below.
Is there any way to group them and make this code shorter?
#keyframes mymove
{
from {top:0px;}
to {top:200px;}
}
#-moz-keyframes mymove /* Firefox */
{
from {top:0px;}
to {top:200px;}
}
#-webkit-keyframes mymove /* Safari and Chrome */
{
from {top:0px;}
to {top:200px;}
}
no, I don't think so, but you could use a CSS language (aka CSS preprocessor) like SASS/SCSS/LESS/... - the output (CSS) would still be the same, but changing something would be much easier!
Check out
http://sass-lang.com/
http://coding.smashingmagazine.com/2011/09/09/an-introduction-to-less-and-comparison-to-sass/
if you're interested - the effort of installing them and setting them up is totally worth it!
EDIT: Using SCSS I did the following:
#mixin keyframes($name) {
#-webkit-keyframes #{$name} { #content; }
#-moz-keyframes #{$name} { #content; }
#keyframes #{$name} { #content; }
}
example of usage:
#include keyframes(pulse) {
0%,100% {
opacity: 0;
}
50% {
opacity: 1;
}
}
Although it should be added that you need the latest pre-release of SASS to be able to nest rules (we have got a "{" inside another "{" rule...) so you should update run "gem install sass --pre" which should get you "sass-3.2.0.alpha.104"