动态更改keyframes

背景

最近做了一个上线滚动文字的走马灯组件,通过关键帧@keyframes at-rule 来定义滚动;
由于我的滚动条目是动态的,个数不定,所以需要动态计算时间段显示的内容。

思路

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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

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

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

styleSheets 分内部和外部样式

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

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

通过属性设置animation-name属性

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

1
animation: myfirst 5s;

由于我是 react,使用了 webpack 打包,打包后,名字myfirst被重新命名了。由于 demo 中的样式比价少,可以通过document.styleSheets看到,样式中 myfirst 被打包后的名字;并且也可以看到它的位置。
但是在项目中,样式比较多,根据打包后的名字去设置比较困难的

1
2
3
4
5
6
7
8
9
10
11
12
13
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()方法

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

最终解决方案

  • 在样式中不指定 animation-name,通过 js 操作样式属性设置,这样生成 keyframes 时,就不用去获取打包生成的名字。
  • 样式直接插入最后的 styleSheets 中
  • 在 js 中指定animationName
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 在全局定义名字
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,
}}
>
...
文章作者: wenmu
文章链接: http://blog.wangpengpeng.site/2020/06/19/%E5%8A%A8%E6%80%81%E6%9B%B4%E6%94%B9keyframes/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 温木的博客
微信打赏