I'm in need of 8 different CSS3 animations which are way too similar, so I used LESS for it. Below is the code, that works perfectly, with one little glitch - the @name variable.
.animation_top (@name, @pxFrom, @pxTo) {
@-moz-keyframes @name {
0% {
top: @pxFrom;
opacity: 0;
}
100% {
top: @pxTo;
opacity: 1;
}
}
@-webkit-keyframes @name {
0% {
top: @pxFrom;
opacity: 0;
}
100% {
top: @pxTo;
opacity: 1;
}
}
@-ms-keyframes @name {
0% {
top: @pxFrom;
opacity: 0;
}
100% {
top: @pxTo;
opacity: 1;
}
}
}
Because css keyframes are started by @ sign, LESS simply ignores the variable of @name. Is there any way how to escape the keyframe @ sign OR to force LESS to somehow render @name properly?
EDIT
Support for the(~"@{varname}")
selector will be removed in LESS 1.4.0.
To get the original solution to work, just introduce a temporary variable and make use of selector interpolation (new in LESS 1.3.1).
For the previous example, this would be:@tmp: ~"@{varname}" @{tmp} { ... }
The explanation below still uses the old selector, because it's conciser. And as shown before, replacing the old method with the new method is trivial.
I did update the code example though, because lots of us blindly copy-paste code.
The expected syntax is (vendorprefixed) (~"@keyframes @{name}") { ... }
. However, the output is incorrect (selectors are merged to @keyframes name 0% { ... } @keyframes name 100% {}
), because the tree syntax of @keyframes
is defined as an exception in less Source code.
The idea behind my crafty mixin is to add curly braces through selectors.
(~"@keyframes @{name}{") { ... }
.@keyframes name {{ ... }
{{
does not look well, I add a newline. I was not able to escape the newline directly, so I decided to create a variable @newline: `"\n"`;
. Less parses anything between backticks as JavaScript, so the resulting value is a newline character. Since { ... }
requires a "selector" to be valid, we pick the first step of the animation, 0%
.(~"} dummy") { .. }
. This is ugly, because a useless selector is added.(~"@{pre}@@{vendor}keyframes @{name} {@{newline}0%")
.@{pre}
has to be "}@{newline}"
for every keyframes block after the first one.animation-name
is the property to do so. I'm using a guarded mixin to implement this.The solution may look somewhat awkward at first, but it's quite concise.
@newline: `"\n"`; // Newline
.animation_top(@selector, @name, @pxFrom, @pxTo) {
.Keyframe(@pre, @post, @vendor) {
@keyframe: ~"@{pre}@@{vendor}keyframes @{name} {@{newline}0%";
@{keyframe} {
top: @pxFrom;
opacity: 0;
}
100% {
top: @pxTo;
opacity: 1;
}
.Local(){}
.Local() when (@post=1) {
@local: ~"}@{newline}@{selector}";
@{local} {
-moz-animation: @name;
-webkit-animation: @name;
-o-animation: @name;
-ms-animation: @name;
animation: @name;
}
}
.Local;
}
.Keyframe("" , 0, "-moz-");
.Keyframe(~"}@{newline}", 0, "-webkit-");
.Keyframe(~"}@{newline}", 0, "-o-");
.Keyframe(~"}@{newline}", 0, "-ms-");
.Keyframe(~"}@{newline}", 1, ""); // <-- Vendorless w3
}
.animation_top("#test", hey, 10px, 100px);
Is rendered as (notice that the indention inside the keyframes are off by one. This is expected, because Less does not know that we're inside another block, due to the manually added braces).
The following result is confirmed using LESS version 1.3.3 and 1.4.0-b1.
$ lessc --version
lessc 1.3.3 (LESS Compiler) [JavaScript]
$ lessc so
@-moz-keyframes hey {
0% {
top: 10px;
opacity: 0;
}
100% {
top: 100px;
opacity: 1;
}
}
@-webkit-keyframes hey {
0% {
top: 10px;
opacity: 0;
}
100% {
top: 100px;
opacity: 1;
}
}
@-o-keyframes hey {
0% {
top: 10px;
opacity: 0;
}
100% {
top: 100px;
opacity: 1;
}
}
@-ms-keyframes hey {
0% {
top: 10px;
opacity: 0;
}
100% {
top: 100px;
opacity: 1;
}
}
@keyframes hey {
0% {
top: 10px;
opacity: 0;
}
100% {
top: 100px;
opacity: 1;
}
}
#test {
-moz-animation: hey;
-webkit-animation: hey;
-o-animation: hey;
-ms-animation: hey;
animation: hey;
}
Final notes:
/**/
. Example: (~"..") {/**/}
-> .. {/**/}
.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With