Sass Mixin for animation keyframe which includes multiple stages and transform property

34,737

Solution 1

To deal with vendor-prefixers I recommend to use Autoprefixer instead of sass mixins.

Autoprefixer interface is simple: just forget about vendor prefixes and write normal CSS according to latest W3C specs. You don’t need a special language (like Sass) or special mixins.

Because Autoprefixer is a postprocessor for CSS, you can also use it with preprocessors, such as Sass, Stylus or LESS.

So, in your case, you just need to write this:

@keyframes crank-up {
  20%,
  40% { -webkit-transform: translateY(34px); }
  80% { opacity: .8; }
  100% { -webkit-transform: rotate(360deg);}
}

And autoprefixer converts it automatically to:

@-webkit-keyframes crank-up {
  20%, 40% {
    -webkit-transform: translateY(34px);
  }

  80% {
    opacity: .8;
  }

  100% {
    -webkit-transform: rotate(360deg);
  }
}

@keyframes crank-up {
  20%, 40% {
    -webkit-transform: translateY(34px);
  }

  80% {
    opacity: .8;
  }

  100% {
    -webkit-transform: rotate(360deg);
  }
}

Autoprefixer is widely supported, you can process your scss or css styles with this tool through Compass, Grunt, Sublime Text, node.js, Ruby, Ruby on Rails, PHP...

Here is more info about the project

Solution 2

If you are already using Compass and don't want to load whole bounce of extra library and just want to write some mixing then THIS is the best option.

/* animation mixing
keyframe animation
@include animation('animation-name .4s 1')*/

@mixin animation($animate...) {
$max: length($animate);
$animations: '';

@for $i from 1 through $max {
    $animations: #{$animations + nth($animate, $i)};

    @if $i < $max {
        $animations: #{$animations + ", "};
    }
}
-webkit-animation: $animations;
-moz-animation:    $animations;
-o-animation:      $animations;
animation:         $animations;
}

And here is the keyframe Mixing to include CSS3 properties inside particular browser vender prefix, instead of @include translate, we use the full css (Note: if you're using Sass 3.3 or older, you'll need to remove the !global flag):

@mixin keyframes($animationName) {
    @-webkit-keyframes #{$animationName} {
        $browser: '-webkit-' !global;
        @content;
    }
    @-moz-keyframes #{$animationName} {
        $browser: '-moz-' !global;
        @content;
    }
    @-o-keyframes #{$animationName} {
        $browser: '-o-' !global;
        @content;
    }
    @keyframes #{$animationName} {
        $browser: '' !global;
        @content;
    }
} $browser: null;

For your reference here is the SASS example:

@include keyframes(animation-name) {
   0% {
       #{$browser}transform: translate3d(100%, 0, 0);
   }
   100% {
      #{$browser}transform: translate3d(0%, 0, 0);
   }
}

And here how you include your animation to the particular class or ids

.new-class {
@include animation('animation-name 5s linear');
}

That's all.

Share:
34,737
fidev
Author by

fidev

.

Updated on September 25, 2020

Comments

  • fidev
    fidev over 3 years

    Here is the standard CSS I am trying to produce but want to use a SASS Mixin to do the work.

    STANDARD CSS

    @-webkit-keyframes crank-up {
      100% { -webkit-transform: rotate(360deg);}
    }
    @-moz-keyframes crank-up {
      100% { -moz-transform: rotate(360deg);}
    }
    @-o-keyframes crank-up {
      100% { -o-transform: rotate(360deg);}
    }
    keyframes crank-up {
      100% { transform: rotate(360deg);}
    }
    

    I'm using the same mixin as in the following post SASS keyframes not compiling as wanted which is shown below.

    MIXIN

    @mixin keyframes($name) {
      @-webkit-keyframes #{$name} {
          @content;
      }
      @-moz-keyframes #{$name} {
          @content;
      }
      @-ms-keyframes #{$name} {
          @content;
      }
      @keyframes #{$name} {
          @content;
      }
    }
    

    The above is OK, as long as none of the keyframes include a property that requires a vendor prefix. Like the transform property as all the vendor prefixed keyframes get applied with (in this case) the -webkit- prefix. For example:

    SCSS

    @include keyframes(crank-up) {
      100% { -webkit-transform: rotate(360deg);}
    }
    

    CSS

    @-webkit-keyframes crank-up { 100% { -webkit-transform: rotate(360deg); } }
    @-moz-keyframes crank-up { 100% { -webkit-transform: rotate(360deg); } }
    @-ms-keyframes crank-up { 100% { -webkit-transform: rotate(360deg); } }
    @keyframes crank-up { 100% { -webkit-transform: rotate(360deg); } }
    

    Notice the above, -webkit- with a -moz-keyframe. Should be -moz-

    So, my first thought was to alter the above mixin to:

    ALTERED MIXIN

    @mixin keyframes($first-name, $last-name, $argument) {
      @-webkit-keyframes #{$first-name} {
        -webkit-#{$last-name}: #{$argument};
      }
      @-moz-keyframes #{$first-name} {
        -moz-#{$last-name}: #{$argument};
      }
      @-o-keyframes #{$first-name} {
        -o-#{$last-name}: #{$argument};
      }
      @keyframes #{$first-name} {
        #{$last-name}: #{$argument};
      } 
    }
    

    With a call to the mixin looking like

    SCSS

    @include keyframes(crank-up, transform, rotate(360deg)) { }
    

    CSS

    @-webkit-keyframes crank-up { -webkit-transform: rotate(360deg); }
    @-moz-keyframes crank-up { -moz-transform: rotate(360deg); }
    @-o-keyframes crank-up { -o-transform: rotate(360deg); }
    @keyframes crank-up { transform: rotate(360deg); }
    

    This works all ok if there is only ONE Keyframe 'stage' (see in original code - top of page, there's only the 100% mark), excuse if my terminology is slightly off in reference to keyframe 'stage'.

    PROBLEM

    I want a mixin like the above to work with something like.

    @-webkit-keyframes crank-up {
      20%,
      40% { -webikit-transform: translateY(34px); }
      80% { opacity: .8; }
      100% { -webkit-transform: rotate(360deg);}
    }
    

    I have also looked into the two Compass Animate plugins; compass-animation and the newer compass-animate but not really sure if these can help. I need some way of adding in a variable and testing for this with a mixin but don't know if it's possible to pass variable into mixins.

    Any help much appreciated. Thanks

    I've been playing around with the following but neither work, just thought I'd add them up to see if anyone knows where I'm going wrong.

    EXPERIMENTAL MIXINS:

    @mixin vendor-prefix($name, $argument, $webkit: "-webkit-", $moz: "-moz-",$o: "-o-", $stale: ""){
      #{$webkit}: #{$name}: #{$argument};
      #{$moz}: #{$name}: #{$argument};
      #{$o}: #{$name}: #{$argument};
      #{$stale}: #{$name}: #{$argument};
    }
    
    @mixin vendor-prefix($last-name, $argument){
      @if $name == webkit { 
        -webkit-#{$name}: #{$argument};
      } @else if $name == moz { 
        -moz-#{$name}: #{$argument};
      } @else if $name == o { 
        -o-#{$name}: #{$argument};
      } @else { 
        #{$name}: #{$argument};
      } 
    }
    
  • fidev
    fidev over 10 years
    Thanks, but I'm slightly confused. Where are the -moz- and -o- keyframe prefixes WITH their own transform prefixes within each keyframe stage.
  • fidev
    fidev over 10 years
    @-moz-keyframes crank-up { 20%, 40% { -moz-transform: translateY(34px); } 80% { opacity: .8; } 100% { -o-transform: rotate(360deg);} } and @-o-keyframes crank-up { 20%, 40% { -o-transform: translateY(34px); } 80% { opacity: .8; } 100% { -o-transform: rotate(360deg);} }
  • Alex Guerrero
    Alex Guerrero over 10 years
    With autoprefixer you can specify the browsers you want to target in your project and the versions of each browser. I have the default settings which supports last two versions of each web browser (last two versionsfirefox doesn't need prefixers for keyframes and Opera now uses Webkit). If you want to edit your preferences, check out the guide
  • fidev
    fidev over 10 years
    Thanks @Alex, thats why there's no -o- prefix in my answer. Good to know!
  • jansesun
    jansesun over 8 years
    The snippet of sass code doesn't work in sass 3.4+, because of importing of scope; We should make it work fine by "!global" keyword.