demo效果:
通过js修改元素的keyframes样式的使用场景非常少, 一般用于元素的动画末节点不固定且动画复杂(比如分为多段不同效果的动画);
记录一下方法, 权当保存一种思路.
demo调用:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#block { width: 20px; height: 20px; background: lightblue; position: absolute; left: 20px; top: 20px; }
.ani { animation: myFrame .5s forwards; }
.btn { position: absolute; left: 20px; top: 220px; }
@keyframes myFrame {
from { width: 20px; height: 20px; left: 20px; top: 20px; }
to { width: 99px; height: 99px; left: 99px; top: 99px; }
}
</style>
</head>
<body>
<div id="block" class="ani"></div>
<button id="btn" class="btn">随机生成animation动效</button>
</body>
<script src="./editKeyframes.js"></script>
<script>
const frame_edit = _editKeyframes('myFrame')
// 记录最后操作的目标点
let from_str = 'width: 99px; height: 99px; left: 99px; top: 99px;'
const getRandom = () => { // 随机生成大于等于20且小于100的整数
return Math.floor(Math.random() * 80 + 20)
}
btn.onclick = () => {
/*
注* 如果在dom的className没有改变的情况下直接修改keyframes样式将无法实现动效
所以修改流程为: 去除dom的className -> 修改keyframes -> 重新给dom设置className(须异步修改)
*/
block.style = from_str // 在清除dom的className之前把dom的样式固定在动画的末端
block.className = '' // 修改目标元素的className
const customStr = `width: ${getRandom()}px; height: ${getRandom()}px; left: ${getRandom()}px; top: ${getRandom()}px;`
const keyframes_str = `
{
from { ${from_str}; }
to { ${customStr} }
}
`
from_str = customStr
frame_edit(keyframes_str)
setTimeout(() => { // 异步目标修改元素的className
block.className = 'ani'
})
}
</script>
</html>
功能代码实现(editKeyframes.js):
/**
* 修改名为keyframesName的keyframes样式
*
* @param {String} keyframesName keyframes名称
*
* @returns {Function} 柯里化后的最终处理方法函数
*
* @example
* const edit = _editKeyframes(KEYFRAMESNAME)
* edit('from {...} to {...}')
*
* 注* 如果在dom的className没有改变的情况下直接修改keyframes样式将无法实现动效
* 所以修改流程为: 去除dom的className -> 修改keyframes -> 重新给dom设置className
*/
export const _editKeyframes = (keyframesName) => {
let sheetObj = null
let ss = Array.from(document.styleSheets).filter(styleSheet => {
return !styleSheet.href || styleSheet.href.startsWith(window.location.origin)
})
if (!!ss.length) {
for (let i = 0; i < ss.length; ++i) {
for (let j = 0; j < ss[i].cssRules.length; ++j) {
if (ss[i].cssRules[j].type === window.CSSRule.KEYFRAMES_RULE && ss[i].cssRules[j].name === keyframesName) {
sheetObj = {
has: true,
value: ss[i],
}
break
}
}
if (sheetObj === null) {
sheetObj = {
has: false,
value: ss[0],
}
}
}
} else {
document.head.appendChild(document.createElement('style'))
ss = Array.from(document.styleSheets).filter(styleSheet => {
return !styleSheet.href || styleSheet.href.startsWith(window.location.origin)
})
sheetObj = {
has: false,
value: ss[0],
}
}
return function (cssStr) {
if (sheetObj.has) {
const index = [...sheetObj.value.cssRules].findIndex(val => {
return val.name === keyframesName
})
sheetObj.value.deleteRule(index)
sheetObj.value.insertRule(`@keyframes ${keyframesName} ${cssStr}`)
} else {
sheetObj.value.insertRule(`@keyframes ${keyframesName} ${cssStr}`)
sheetObj.has = true
}
}
}