Using Sass to create a dynamic animation list - css

So, I've been messing around with creating a bit of code in CSS that is a list of animations set for one element. It's basically just a loop, but I need to be able to change the timing of each time it goes off. Basically, I'd like something as simple as this:
$intro-duration:1s;
$intro-delay:6.5s;
$intro-separation:0.7s;
$intro-style:ease;
#mixin intro($multiplier) {
-webkit-animation:intro $intro-duration $intro-separation*$multiplier+$intro-delay $intro-style;
}
.intro {
#include intro(-1);
#include intro(0);
#include intro(1);
#include intro(2);
#include intro(3);
}
But having that, it comes out like:
.intro {
-webkit-animation: intro 1s 5.8s ease;
-webkit-animation: intro 1s 6.5s ease;
-webkit-animation: intro 1s 7.2s ease;
-webkit-animation: intro 1s 7.9s ease;
-webkit-animation: intro 1s 8.6s ease;
}
Which would be all fine and dandy, but I obviously need to be able to leave off the "-webkit-animation" as well as the semi-colon in place of a commas, in order to properly combine the string of animation. So it needs to be this:
.intro {
-webkit-animation:
intro 1s 5.8s ease,
intro 1s 6.5s ease,
intro 1s 7.2s ease,
intro 1s 7.9s ease,
intro 1s 8.6s ease; }
I totally feel like I'm really just over complicating it and feel that if I were to simply step away from it for a few hours, I'd come up with the solution, but I've tried a few different things and am coming up short. Any help to simplify this code would be much appreciated!
EDIT: Removed a few lines of the repeating code to try and keep it simple.

You need to create a list:
#mixin intro($multiplier...) {
$collector: ();
#each $i in $multiplier {
$collector: append($collector, intro $intro-duration $intro-separation* $i+ $intro-delay $intro-style, comma);
}
animation: $collector;
}
.intro {
#include intro(-1, 0, 1, 2, 3);
}
Alternately:
#mixin intro($start, $end) {
$collector: ();
#for $i from $start through $end {
$collector: append($collector, intro $intro-duration $intro-separation* $i+ $intro-delay $intro-style, comma);
}
animation: $collector;
}
.intro {
#include intro(-1, 3);
}
And make sure you're providing all prefixes (as appropriate) + non-prefixed properties, not just webkit prefixes.

Related

Can I sum up some time values in CSS?

:) First of all - sory for my English. Second of all - I have an issue or puzzle.
I want to make animation spans with relation time between them. There is the simple code:
span.one { transition: transform 1s ease-out; } /*duration 1s delay 0s*/
span.two { transition: transform 1s ease-out 1s; } /*duration 1s delay 1s*/
span.three { transition: transform 1s ease-out 2s; } /*duration 1s delay 2s*/
So each span appear one by one and it's look ok. Every next element is animated in delay of value duration (let's get name 'time-op'). So if I change values from second to my class 'time-op' it will be like:
span.one { transition: transform time-op ease-out time-op*0; }
span.two { transition: transform time-op ease-out time-op*1; }
span.three { transition: transform time-op ease-out time-op*2; }
If I root my 'time-op' by :root at the beginning of my file, can I multiply this value as I described it above? For example:
span.three { transition: transform var(--time-op) ease-out var(--time-op)*2; }
I know this is available in JavaScript, but maybe there is a way in ONLY CSS :)
Use calc() function in CSS:
span.one { transition: transform time-op ease-out calc(time-op * 0) }

How to stagger multiple animation delays in Sass?

I was wondering how I can calculate two animation delays (fade in and fade out) by using a for loop in Sass.
Came up with the following, but doesn't know how to calc the second animation delay.
#for $i from 1 through 2 {
.text:nth-child(#{$i}) {
animation-delay: ($i * 0.5s), 4s;
}
}
I would like to do the same for the fade in, so each text element will be slightly stagger.
Already tried something like this, but with no result.
#for $i from 1 through 2 {
.text:nth-child(#{$i, $a}) {
animation-delay: ($i * 0.5s), ($a * 0.5s);
}
}
And how can I use the last delay to start another delay of another animation?
I'm not sure exactly what you're trying to accomplish.
The easy version
That said, the basic idea is to do the "in" and "out" animations as percentages of the same animation function. In this case the build in = 25%, static = 50%, and build out = 25%.
Then your duration controls how long each part takes to complete. In this case 2 seconds.
Next, the delay (calculated as part of the loop) offsets the animation. You could also multiply the duration in the loop to completely stagger the animation.
$timeOffset: 0.5s;
$aniLength: 2s;
// 0.5s in, 1s solid, 0.5s out = 2sec
.item {
opacity:0;
animation-name: animation;
animation-duration: $aniLength;
animation-timing-function: ease-in-out;
}
#for $i from 1 through 20 {
.item:nth-child(#{$i}){
// units are in the variable so SCSS just does math
animation-delay: $i * $timeOffset;
}
}
#keyframes animation {
0% {
opacity: 0;
transform: translatex(100%);
}
25% {
opacity: 1;
transform: translatex(0);
}
75% {
opacity: 1;
transform: translatex(0);
}
100% {
opacity: 0;
transform: translatex(-100%);
}
}
Here's a Codepen example since SO doesn't parse SCSS in the code sandbox available here.
More complex version with multiple delays
Your second example didn't work because you were building an odd selector in your nth-child code while using an undefined variable.
Also, you can do pretty complex math for each iteration. Just remember concepts like Order of Operations.
The correct way to specify two different delay values is like this:
#for $i from 1 through 20 {
// the hash forces SCSS to create a string
.item:nth-child(#{$i}){
// you need to use $i for BOTH since its the only defined increment
animation-delay: $i * $timeOffset, $i * $timeOffset + $aniLength;
// delay #1 = iteration * 0.5s, delay #2 = iteration * 0.5s + 2s (run at end of animation plus the offset)
}
}
You can set #if, #for, #each and #while in SCSS file. Refer my jsfiddle for fade out animation base on for loops.
#for $i from 1 through 10 {
.div-wrapper {
div:nth-child(#{$i}) {
transition: all $i/3 +s;
transition-delay: $i/3 +s;
}
}
}
Example: https://jsfiddle.net/hardyrajput/nt6p9hs2/29/
Refernce: http://thesassway.com/intermediate/if-for-each-while

Multiple transitions with scss

I have a multiple transition problem with scss #mixin.
Im trying to create dynamic transition #mixin with 1-5 different properties. When I'm processing the code below this error shows up:
Error: Mixin transition takes 1 argument but 3 were passed.
on line 758 of style.scss, in `transition'
from line 758 of style.scss Use --trace for backtrace.
This is my code:
#mixin:
#mixin transition($x){
transition: $x;
-webkit-transition: $x;
-moz-transition: $x;
-ms-transition: $x;
-o-transition: $x;
}
#include:
#include transition(visibility 0s ease 0.2s, opacity 0.2s ease, transform 0.3s ease);
I figured it out with this hack but it looks like a very unclean solution to me:
#include transition(visibility 0s ease 0.2s + "," + opacity 0.2s ease + "," + transform 0.3s ease);
Is there a better way to do it?
In your mixin, you have declared a single variable $x as a parameter which means that sass expects the mixin to be called with one argument.
#include transition(visibility 0s ease 0.2s)
When you pass the mixin comma separated values, it causes an error because sass sees these as multiple values instead of a single value which it expects.
#include transition(visibility 0s ease 0.2s, opacity 0.2s ease) //Sees two args instead of one arg
In Sass, comma separated values can be interpreted as a single value if declared as varargs. Varargs are mixin or function parameters declared with 3 dots appended to their name.
Replacing your $x parameter with $x... will ensure that sass interprets the comma separated arguments passed to your mixin as one value.
#mixin transition($x...){
-webkit-transition: $x;
-moz-transition: $x;
-ms-transition: $x;
-o-transition: $x;
transition: $x;
}
It can then be used like this
div {
#include transition(color 1s, background-color 1s, border-color 1s);
}
which compiles to
div {
-webkit-transition: color 1s, background-color 1s, border-color 1s;
-moz-transition: color 1s, background-color 1s, border-color 1s;
-ms-transition: color 1s, background-color 1s, border-color 1s;
-o-transition: color 1s, background-color 1s, border-color 1s;
transition: color 1s, background-color 1s, border-color 1s;
}
By doing this you can pass the values as you normally would in CSS without the hack you are currently using making it much cleaner.
Hope this helps
Since this is the first result on Google, I want to say that this does not solve my problem. I wanted to transition multiple properties, with only one mixin. I came up with this solution: (see link for helper functions)
/*
usage: #include transition(prop1, prop2, ..., 0.5s cubic-bezier(0.16, 0.85, 0.45, 1));
*/
#mixin transition($args...) {
$type: nth($args, length($args));
$props: remove-nth($args, length($args));
$result: ();
#for $i from 1 through length($props) {
$prop: nth($props, $i);
$result: append($result, $prop);
$result: append($result, $type);
#if $i != length($props) {
$result: append($result, unquote($string: ","));
}
}
#include simple_transition($result);
}
I created a short mixin that allows adding multiple transition properties in one declaration. In case number of arguments provided for the timing, easing or delay, is less than number of transition properties, the arguments are repeated.
#mixin transition($prop, $time, $easing: $ease1, $delay: 0s) {
$transition: ();
#for $i from 1 through length($prop) {
#for $j from 0 to (length($prop)) - (length($time)) {
$time: join($time, nth($time, -1));
}
#for $j from 0 to (length($prop)) - (length($easing)) {
$easing: join($easing, nth($easing, -1));
}
#for $j from 0 to (length($prop)) - (length($delay)) {
$delay: join($delay, nth($delay, -1));
}
$transition: append(
$transition,
(nth($prop, $i) nth($time, $i) nth($easing, $i) nth($delay, $i)),
$separator: comma
);
}
transition: $transition;
}
//scss input:
#include transition(height width transform, 0.2s 0.3s, linear, 0s);
//css output:
transition: height 0.2s linear 0s, width 0.3s linear 0s, transform 0.3s linear 0s;
Where time and easing are the same, but with multiple properties:
#mixin transitionPrefixMultiple($time, $properties...) {
$transition: ();
#each $property in $properties {
$transition: append(
$transition, ($property $time cubic-bezier(.42, 0, .58, 1)), $separator: comma
);
}
-webkit-transition: $transition;
-moz-transition: $transition;
-ms-transition: $transition;
-o-transition: $transition;
transition: $transition;
}
Usage:
#include transitionPrefixMultiple(150ms, width, background-color, etc);
Thank you #nidhishs06 as this is a cleaner version of your answer

Sass mixin with multiple optional parameters, but only one property

So I'm trying to do something functionally equivalent to this:
Can a sass #mixin accept an undefined number of arguments?
...with a transitionFast mixin, but I want to be strict about the easing functions and speeds, so I've defined them within the mixin, like so:
#mixin transitionFast($property) {
-webkit-transition:$property ease 0.2s;
-moz-transition:$property ease 0.2s;
-o-transition:$property ease 0.2s;
transition:$property ease 0.2s;
}
How can I pass multiple $properties to this, without passing through the ease 0.2s part? I've tried this:
#mixin transitionFast($properties...) {
#each $property in $properties {
transition:$property ease 0.5s;
}
}
which I'm trying to call like this:
#include transitionFast(background-color, color);
but it doesn't work and only applies the transition to the last passed argument. Any ideas?
In your case there are several ways to go with sass splat-like args:
1) If you define mixin with splat args like so:
#mixin transitionFast($properties...) {
# with iteration on $properties
}
And you include it normal like this:
#include transitionFast(firstArg, secondArg, thirdArg);
Then you get
# compiled
transition: firstArg;
transition: secondArg;
transition: thirdArg;
2) You will also get the same thing when you don't specify splat args
#mixin transitionFast($properties) {
# with iteration on $properties
}
And include them with double parents
#include transitionFast((firstArg, secondArg, thirdArg));
3) If you declare mixin this way:
#mixin transitionFast($properties...) {
# with iteration on $properties
}
and call it whis way:
#include transitionFast(( background-color, color, red ));
you will get behavior similar to default behavior (ie without iteration over splat):
transition: background-color, color, red;
Now, back to your question, you can do this to get what You want:
$ease: ease 0.5s;
#mixin transitionFast($properties...) {
#each $prop in $properties {
transition: $prop $ease;
}
}
html {
#include transitionFast( background-color, color, margin );
}
Edit: instead transition: $prop $ease; go with compass #include transition( $prop $ease ); as Pik_at suggested, its more dry and sexy.
Just for fun:
If you leave (above) mixin as it is and include it like this:
#include transitionFast(( background-color, color ), padding);
it compiles to
transition: background-color, color ease 0.5s;
transition: padding ease 0.5s;
combining iteration and regular 'splatting', very cool stuff :)
Array parameters should be on bracket like:
#include transitionFast((background-color, color));
I made a simple code test to show you:
http://codepen.io/pik_at/pen/wBjgpY
Likewise, using Compass would help you to add transition mixin with browsers prefix:
#mixin transitionFast($properties) {
#each $property in $properties {
#include transition($property 2s ease);
}
}

SCSS transition property overwrite

I use a #mixin to add different transition properties to elements. Let's say I have this in my .scss:
#mixin transition($prop, $sec){
-webkit-transition: $prop $sec;
-moz-transition: $prop $sec;
-ms-transition: $prop $sec;
-o-transition: $prop $sec;
transition: $prop $sec;
}
then I call:
.sample{
#include transition(background-color, 0.2s);
#include transition(margin, 0.3s)
}
.sample has only got margin transition, but I also want background-color transition: is there any simple way to get it working?
It's important that I have different calls
There is no way in SASS to concatenate properties and I don't know if exists a CSS external tool to do this task. Sass was created to improve CSS capabilities and not to allow programmers to develop bad programming practices. I don't really know the purpose of create multiple CSS declaration statements when you can keep them all in a single statement. Keeping all your transitions in one statement improves considerably your structure, your workflow and the performance of your Sass code.
Ok, that said, and as you have mentioned before "let the horrible kludge be".
Here are two different mixins for you one for shorthand transition declaration and another for the long form, differences between them in processing and even load time are negligible, the only noticeable difference is in the style of your code.
Long form mixin
#mixin transition($properties, $durations, $timing-function: null, $delay: null) {
$declarations: (property, $properties),
(duration, $durations),
(timing-function, $timing-function),
(delay, $delay);
#each $declaration in $declarations {
#if nth($declaration, 2) {
$output: ();
#each $val in nth($declaration, 2) {
$output: append($output, $val, comma);
}
#each $prefix in '-webkit-', '-moz-', '-ms-', '-o-', '' {
#{$prefix}transition-#{nth($declaration, 1)}: $output;
}
}
}
}
It's similar to #LeBen mixin but you can use the include with comma separated arguments without quotes:
#include transition(background-color margin, 0.2s 0.3s);
Shorthand form
#mixin transition($declarations...) {
#each $prefix in '-webkit-', '-moz-', '-ms-', '-o-', '' {
#{$prefix}transition: $declarations;
}
}
Here's the way to implement it in your code
#include transition(background-color 0.2s, margin 0.3s);
And now, to solve your problem with "different calls" the only way to deal with them, in my opinion, is using append() list function.
Let's go with the example. I'm going to use shorthand mixin form because it's easier to implement.
Imagine that you have four pages, three partials (_variables.scss, _page1.scss, _page2.scss, _page3.scss) and a style.scss file which imports the other:
_VARIABLES.SCSS
// Here comes the variable declaration
$transition-list: color 1s;
_PAGE1.SCSS
// Using append($list, $val, $separator:auto) list function
// we can add some items to $transition-list
$transition-list: append($transition-list, margin 2s, comma);
_PAGE2.SCSS
// You can add also the output of a function
#function example(){
#return unquote("background-color 1s")
}
$transition-list: append($transition-list, example(), comma);
STYLE.SCSS
// You can add new values into the same page
$transition-list: append($transition-list, padding 4s, comma);
$transition-list: append($transition-list, border 10s, comma);
// And finally you only need to use the include to generate the final transition
example {
#include transition($transition-list);
}
As I've said before, I don't recommend to use this method, it's not a good practice.
Your mixin is too rigid and don't feel like natural CSS. Personally, I recommend using the mixins provided by Compass, especially since it should handle prefixed values as well. If you just want a simple mixin, you want to use variable arguments like so:
#mixin transition($values...) {
-webkit-transition: $values;
// other prefixes
transition: $values;
}
#mixin transition-property($values...) {
-webkit-transition-property: $values;
// other prefixes
transition-property: $values;
}
#mixin transition-delay($values...) {
-webkit-transition-delay: $values;
// other prefixes
transition-delay: $values;
}
// etc
.foo {
#include transition(background-color, margin); // or transition-property
#include transition-delay(0.2s);
}
Output:
.foo {
-webkit-transition: background-color, margin;
transition: background-color, margin;
-webkit-transition-delay: 0.2s;
transition-delay: 0.2s;
}
Alternate Usage:
.foo {
#include transition(background-color 0.2s, margin 0.3s);
}
Output:
.foo {
-webkit-transition: background-color 0.2s, margin 0.3s;
transition: background-color 0.2s, margin 0.3s;
}
Here is the mixin you could use:
#mixin transition($properties, $durations, $timing-function: null, $delay: null) {
$props: unquote($properties);
$durs: unquote($durations);
-webkit-transition-property: $props;
-moz-transition-property: $props;
-ms-transition-property: $props;
-o-transition-property: $props;
transition-property: $props;
-webkit-transition-duration: $durs;
-moz-transition-duration: $durs;
-ms-transition-duration: $durs;
-o-transition-duration: $durs;
transition-duration: $durs;
#if ($timing-function) {
-webkit-transition-timing-function: $timing-function;
-moz-transition-timing-function: $timing-function;
-ms-transition-timing-function: $timing-function;
-o-transition-timing-function: $timing-function;
transition-timing-function: $timing-function;
}
#if ($delay) {
-webkit-transition-delay: $delay;
-moz-transition-delay: $delay;
-ms-transition-delay: $delay;
-o-transition-delay: $delay;
transition-delay: $delay;
}
}
With the following call
#include transition("background-color, margin", 0.2s);

Resources