天天看点

动态更改动画keyframes修改样式

作者:沐土之夏

背景

最近做了一个上线滚动文字的走马灯组件,通过关键帧@keyframes at-rule 来定义滚动;

由于我的滚动条目是动态的,个数不定,所以需要动态计算时间段显示的内容。

思路

就是@keyframe 设置在不同时间显示不同的元素,通过绝对定位来实现,动态控制元素的位置来控制显示哪个元素。

div {
  animation: myfirst 5s;
  animation-iteration-count: infinite; // 无限循环
}
@keyframes myfirst {
  0% {
    left: 0;
    top: 0;
  }
  ,
  25% {
    left: 0;
    top: -24px;
  }
  50% {
    left: 0;
    top: -48px;
  }
  100% {
    left: 0;
    top: -72px;
  }
}

// 兼容性的代码省略           

上面是基本的代码,但是我的条目是动态的,所以时间刻度需要动态计算,但是@keyframe 没有像元素样式的动态的属性,不能计算后赋值给元素;在网上的查询后,最终使用动态给样式对象插入新的样式的方式来实现。

动态更改@keyframes

const styleSheet = document.styleSheets[0];
styleSheet.insertRule("keyframe的定义", 3);           

网上的大致思路就是上面的代码,但是有几个坑。

styleSheets 分内部和外部样式

我在写 demo 测试的时候,由于我没有内嵌样式,所以看到的都是外部,所以我就把样式插入到了第一个里面,但是放到项目里面发现,竟然报错提示没有rules属性。因为如果有内嵌样式,第一个是内嵌样式,内嵌的 rules 属性是没有值的。所以为了保险不要直接在第一个对象中插入。最好插入到最后一个

const styleSheet = document.styleSheets[document.styleSheets.length - 1];           

通过属性设置animation-name属性

开始我是按照网上的写法,直接设置 animation 属性

animation: myfirst 5s;           

由于我是 react,使用了 webpack 打包,打包后,名字myfirst被重新命名了。由于 demo 中的样式比较少,可以通过document.styleSheets看到,样式中 myfirst 被打包后的名字;并且也可以看到它的位置。

但是在项目中,样式比较多,根据打包后的名字去设置比较困难的

const styleSheet = document.styleSheets[document.styleSheets.length - 1];
const keyframesName = styleSheet.rules[0].name;
const keyframesStr = `${keyframesName} {0% {
          left: 0px;
          top: 0px;
        }
        ${keyframesItem.join("")}
      }`.replace(/\s+/g, "");
const keyframes = `@keyframes ${keyframesStr}`;
const keyframes_webdit = `@-webkit-keyframes ${keyframesStr}`; /* Safari 和 Chrome */

styleSheet.insertRule(keyframes, styleSheet.rules.length);
styleSheet.insertRule(keyframes_webdit, styleSheet.rules.length);
           

动态设置 keyframes

这个需要用到insertRule()方法

styleSheet.insertRule(keyframes_webdit, styleSheet.rules.length);           

最终解决方案

  • 在样式中不指定 animation-name,通过 js 操作样式属性设置,这样生成 keyframes 时,就不用去获取打包生成的名字。
  • 样式直接插入最后的 styleSheets 中
  • 在 js 中指定animationName
// 在全局定义名字
const keyframesName = "text-carousel-keyframesName-key-asdf8324-df324s";

// 关键代码
const styleSheet = document.styleSheets[document.styleSheets.length - 1];

const keyframesStr = `${keyframesName} {0% {
          left: 0px;
          top: 0px;
        }
        ${keyframesItem.join("")}
      }`.replace(/\s+/g, "");
const keyframes = `@keyframes ${keyframesStr}`;
const keyframes_webdit = `@-webkit-keyframes ${keyframesStr}`; /* Safari 和 Chrome */

styleSheet.insertRule(keyframes, styleSheet.rules.length);
styleSheet.insertRule(keyframes_webdit, styleSheet.rules.length);

// jsx

 <section
          className={styles.itemContainer}
          style={{
            animationDuration: animationDuration,
            animationName: keyframesName,   // 设置名字
            ...contentStyle,
          }}
        >
...           

继续阅读