How to dynamically create '@-Keyframe' CSS animations?

前端 未结 9 1209
误落风尘
误落风尘 2020-11-27 03:42

I have a requirement to rotate a div and stop at a particular position ( The value will be received from the server).

I tried native JS to rotate and stop but it is

相关标签:
9条回答
  • 2020-11-27 04:11

    Alex Grande's answer works GREAT for a few keyframes. But, say you want to dynamically keep adding in keyframes over and over again, then your webpage get really laggy really quick. To solve this problem, just STOP creating new DOM elements. Rather, create 1 new DOM stylesheet, and just reuse it with the inertRule. If you want even more keyframes (like if you're generating a new keyframe every animationframe), then you need to set up a system which deletes old keyframes after they're no longer used. This is a good start to how something like this can be achieved.

    var myReuseableStylesheet = document.createElement('style'),
        addKeyFrames = null;
    document.head.appendChild( myReuseableStylesheet );
    if (CSS && CSS.supports && CSS.supports('animation: name')){
        // we can safely assume that the browser supports unprefixed version.
        addKeyFrames = function(name, frames){
            var pos = myReuseableStylesheet.length;
            myReuseableStylesheet.insertRule(
                "@keyframes " + name + "{" + frames + "}", pos);
        }
    } else {
        addKeyFrames = function(name, frames){
            // Ugly and terrible, but users with this terrible of a browser
            // *cough* IE *cough* don't deserve a fast site
            var str = name + "{" + frames + "}",
                pos = myReuseableStylesheet.length;
            myReuseableStylesheet.insertRule("@-webkit-keyframes " + str, pos);
            myReuseableStylesheet.insertRule("@keyframes " + str, pos+1);
        }
    }
    

    Example usage:

    addKeyFrames(
        'fadeAnimation',
        '0%{opacity:0}' + 
        '100%{opacity:1}'
    );
    

    Also, Alex Grande, I am pretty sure that document.getElementsByTagName('head')[0] and type = 'text/css' hasn't been needed since IE8, and @keyframes aren't supported till IE10. Just saying...

    0 讨论(0)
  • 2020-11-27 04:12

    You can create a <style> element, set its content to the CSS you want, in this case, the declaration of your animation and add it to the <head> of the page.

    Also, as others have suggested, if you need to create many different animations, then it would be better to reuse a single <style> tag rather than creating multiple of them and add the new styles using CSSStyleSheet.insertRule().

    Lastly, if you can use ES6's template literals/strings, your code will look much cleaner:

    let dynamicStyles = null;
    
    function addAnimation(body) {
      if (!dynamicStyles) {
        dynamicStyles = document.createElement('style');
        dynamicStyles.type = 'text/css';
        document.head.appendChild(dynamicStyles);
      }
      
      dynamicStyles.sheet.insertRule(body, dynamicStyles.length);
    }
    
    addAnimation(`
      @keyframes myAnimation { 
        0% { transform: rotate(0); }
        20% { transform: rotate(${ 360 * Math.random() }deg); }
        60% { transform: rotate(${ -360 * Math.random() }deg); }
        90% { transform: rotate(${ 360 * Math.random() }deg); }
        100% { transform: rotate(${ 0 }deg); }
      }
    `);
    
    document.getElementById("circle").style.animation = 'myAnimation 3s infinite';
    html,
    body {
      height: 100vh;
    }
    
    body {
      display: flex;
      justify-content: center;
      align-items: center;
      margin: 0;
    }
    
    #circle {
      width: 100px;
      height: 100px;
      box-shadow:
        0 0 48px -4px rgba(0, 0, 0, .25),
        0 0 0 4px rgba(0, 0, 0, .02);
      border-radius: 100%;
      position: relative;
      overflow: hidden;
    }
    
    #circle::before {
      content: '';
      position: absolute;
      top: 0;
      left: 50%;
      transform: translate(-2px);
      border-left: 4px solid #FFF;
      height: 24px;
      box-shadow: 0 -4px 12px rgba(0, 0, 0, .25);
    }
    <div id="circle"></div>

    Or even better:

    let dynamicStyles = null;
    
    function addAnimation(name, body) {
      if (!dynamicStyles) {
        dynamicStyles = document.createElement('style');
        dynamicStyles.type = 'text/css';
        document.head.appendChild(dynamicStyles);
      }
      
      dynamicStyles.sheet.insertRule(`@keyframes ${ name } {
        ${ body }
      }`, dynamicStyles.length);
    }
    
    addAnimation('myAnimation', `
      0% { transform: rotate(0); }
      20% { transform: rotate(${ 360 * Math.random() }deg); }
      60% { transform: rotate(${ -360 * Math.random() }deg); }
      90% { transform: rotate(${ 360 * Math.random() }deg); }
      100% { transform: rotate(${ 0 }deg); }
    `);
    
    document.getElementById("circle").style.animation = 'myAnimation 3s infinite';
    html,
    body {
      height: 100vh;
    }
    
    body {
      display: flex;
      justify-content: center;
      align-items: center;
      margin: 0;
    }
    
    #circle {
      width: 100px;
      height: 100px;
      box-shadow:
        0 0 48px -4px rgba(0, 0, 0, .25),
        0 0 0 4px rgba(0, 0, 0, .02);
      border-radius: 100%;
      position: relative;
      overflow: hidden;
    }
    
    #circle::before {
      content: '';
      position: absolute;
      top: 0;
      left: 50%;
      transform: translate(-2px);
      border-left: 4px solid #FFF;
      height: 24px;
      box-shadow: 0 -4px 12px rgba(0, 0, 0, .25);
    }
    <div id="circle"></div>

    0 讨论(0)
  • the user7892745 wont work for me, need some little adjustement

    1° "pos" not understand wot should be, but the console log say "undefined" so I've remove " , pos"

    2° " myReuseableStylesheet.insertRule" give me error " is not a function" so I used "innerHTML" insted of "insertRule"

    3° finally I've moved " document.head.appendChild( myReuseableStylesheet );" at the end

    but after this it work fine and it's exact what I looking for. thanks a lot user7892745 :D

    maybe the problem I had, come form the way I use it

    this is the script i used with it

    var getclass = document.getElementsByClassName("cls");
    var countclass = getclass.length;
    for (var i=0; i <countclass; i++ ){
        getclass[i].addEventListener('mouseover', function(){
            // get the data-name value to show element whose id are the same
            var x=  this.getAttribute("data-name"); 
            var y =document.getElementById(x);
                y.style.display="block";
                // because the element to show have fixed width, but different text length, they have different height
                // so I need to get the highness, then use the value of height to define the 100% value of animation
                // or the longer ones will be cutted an the shorten have a lot of empty space a the end
            var yHeig= Math.round(parseInt(getComputedStyle(y).getPropertyValue('height')));
                yHeig_ = yHeig - 10; // to shorten a bit the time from end and new passage
            console.log(yHeig+" - "+ yHeig_);
            addKeyFrames(
                    'showMe',
                    '0%{top:35px;}' + 
                    '100%{top:-'+ yHeig_ +'px;}'
                );
            y.style.animation="showMe 7s linear infinite";
    
        },false);
    
        getclass[i].addEventListener('mouseout', function(){
            var x=  this.getAttribute("data-name");
            document.getElementById(x).style.display="none";
        },false);
    }
    

    i know thath a html marquee cuold seem symple to do the same thing, but dont work well,

    0 讨论(0)
  • 2020-11-27 04:15

    You could create a new stylesheet with the animation you want in it. For Example:

    function addAnimation(keyframe){
         var ss=document.createElement('style');
         ss.innerText=keyframe;
         document.head.appendChild(ss);
    }
    

    This would create a new stylesheet with your animation.
    This method has only been tested in Chrome.

    0 讨论(0)
  • 2020-11-27 04:16

    well i don't think it is easy to create dynamic @keyframes they are inflexible because they must be hard-coded.

    Transitions are a little easier to work with, as they can gracefully respond to any CSS changes performed by JavaScript.

    However, the complexity that CSS transitions can give you is pretty limited — an animation with multiple steps is difficult to achieve.

    This is a problem that CSS @keyframe animations are meant to solve, but they don’t offer the level of dynamic responsiveness that transitions do.

    but these links might help you

    Link1 : a tool that generates a @-webkit-keyframe animation with many tiny steps. This opens the door to an unlimited selection of easing formula.

    Link2 it will be a great help for you to take it as a base as it provides a UI to create animations and exports it to CSS code.

    I guess this solution will definitely work for you. Its is used for dynamic keyframes

    0 讨论(0)
  • 2020-11-27 04:20

    In JavaScript is it possible to access to the style sheet with document.styleSheets. Every sheet has a rule and/or cssRule list (browser depending) and a CSSStyleSheet.insertRule() method.

    This method allows you to add a new keyframe raw as a string:

    JavaScript

    function insertStyleSheetRule(ruleText)
    {
        let sheets = document.styleSheets;
    
        if(sheets.length == 0)
        {
            let style = document.createElement('style');
            style.appendChild(document.createTextNode(""));
            document.head.appendChild(style);
        }
    
        let sheet = sheets[sheets.length - 1];
        sheet.insertRule(ruleText, sheet.rules ? sheet.rules.length : sheet.cssRules.length);
    }
    
    document.addEventListener("DOMContentLoaded", event =>
    {
        insertStyleSheetRule("@keyframes spinIt { 0% { transform: rotate(-20deg); } 100% { transform: rotate(20deg); } }");
    
        insertStyleSheetRule("#box { " + 
            "animation: spinIt 1s infinite alternate cubic-bezier(0.5,0,0.5,1); " + 
            "width: 64px; height: 64px; background-color: red; border: 4px solid black; " + 
        "}");
    });
    

    html

    <div id="box"></div>
    

    demo: https://jsfiddle.net/axd7nteu/

    0 讨论(0)
提交回复
热议问题