背景
最近做了一个上线滚动文字的走马灯组件,通过关键帧@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 属性
由于我是 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}`;
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}`;
styleSheet.insertRule(keyframes, styleSheet.rules.length); styleSheet.insertRule(keyframes_webdit, styleSheet.rules.length);
<section className={styles.itemContainer} style={{ animationDuration: animationDuration, animationName: keyframesName, ...contentStyle, }} > ...
|