问题引出:这几天同事突然发消息问我说,UI给了140张序列图,分4个动画,每个动画有35张图片,问我如何实现。
我这脑子,灵光以下,下意识反应就是CSS3实现啊。结果后来同事说她用js的setInterval修改image标签的src属性实现的,而且上传到服务器的时候会发现肉眼根本看不到图片在变化。
因此,我想着要不我用JS去生成css的动画字符串,岂不是快哉!说干就干…
<div id="container">
CSS3序列图实现帧动画
</div>
#container {
width: 500px;
height: 500px;
margin: 100px auto;
text-align: center;
color: #ffffff;
}
/**
* 生成动画css字符串,生成的动画字符串只兼容Chrome浏览器
* @containerName{string} 动画所在的容器,必须值
* @frameNumber{number} 动画的帧数,必须值
* @animateKey{string} 应用动画的属性key,必须值
* @animateValueList{Array<string>} 应用动画的属性value,必须值
* @name{string} 动画名称,可选值,默认为run
* @times{string} 动画一个周期的时间,可选值,默认3s
* @animateFun{string} 动画执行函数,可选值,默认steps(20, start)
* @isInfinite{string} 是否无限循环动画,可选值,默认infinite
*/
var generateAnimationCss = function (containerName, frameNumber, animateKey, animateValueList, name, times, animateFun, isInfinite) {
if (!containerName || !frameNumber) {
console.log("动画容器或动画帧为空!");
return;
}
name = name || "run";
times = times || "3s";
animateFun = animateFun || "steps(20, start)";
isInfinite = isInfinite || "infinite";
//生成@keyframs时间轴
var timelines = [];
var base = 100 / frameNumber;
for (var i = 1; i <= frameNumber; i++) {
timelines.push(i * base);
}
//生成@keyframes内部字符串
var innerString = "";
timelines.forEach(function (item, index) {
innerString = innerString + item + "%" + "{" + animateKey + ":" + animateValueList[index] + ";}";
});
var string = "@keyframes " + name + "{" + innerString + "}";
string = string + "@-webkit-keyframes " + name + "{" + innerString + "}";
string = string + containerName + "{animation: " + name + " " + times + " " + animateFun + " " + isInfinite + ";";
string = string + "-webkit-animation: " + name + " " + times + " " + animateFun + " " + isInfinite + ";}"
return string;
}
/**
* 随机颜色生成器
*/
var getRandomColor = function () {
return "#" + (function (color) {
var rand = Math.floor(Math.random() * 0XFFFFFF).toString(16);
return rand.length == 6 ? rand : arguments.callee(color);
})('');
}
/**
* 将动画添加到head元素
*/
var appendAnimation = function (containerName, frameNumber, animateKey) {
var headTag = document.getElementsByTagName("head")[0];
var styleTag = document.getElementsByTagName("style")[0];
if (!styleTag) {
styleTag = document.createElement("style");
styleTag.type = "text/css";
}
var colorList = [];
for (var j = 1; j <= frameNumber; j++) {
colorList.push(getRandomColor());
}
var animation = generateAnimationCss(containerName, frameNumber, animateKey, colorList);
var textNode = document.createTextNode(animation);
styleTag.appendChild(textNode);
headTag.appendChild(styleTag);
}
/**
* dom加载完成后开始执行
*/
window.onload = function () {
//必须先给定动画容器的宽高
appendAnimation("#container", 35, "background-color");
};
整体代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSS3序列图实现帧动画</title>
<style>
#container {
width: 500px;
height: 500px;
margin: 100px auto;
text-align: center;
color: #ffffff;
}
</style>
</head>
<body>
<div id="container">
CSS3序列图实现帧动画
</div>
<script>
/**
* 生成动画css字符串,生成的动画字符串只兼容Chrome浏览器
* @containerName{string} 动画所在的容器,必须值
* @frameNumber{number} 动画的帧数,必须值
* @animateKey{string} 应用动画的属性key,必须值
* @animateValueList{Array<string>} 应用动画的属性value,必须值
* @name{string} 动画名称,可选值,默认为run
* @times{string} 动画一个周期的时间,可选值,默认3s
* @animateFun{string} 动画执行函数,可选值,默认steps(20, start)
* @isInfinite{string} 是否无限循环动画,可选值,默认infinite
*/
var generateAnimationCss = function (containerName, frameNumber, animateKey, animateValueList, name, times, animateFun, isInfinite) {
if (!containerName || !frameNumber) {
console.log("动画容器或动画帧为空!");
return;
}
name = name || "run";
times = times || "3s";
animateFun = animateFun || "steps(20, start)";
isInfinite = isInfinite || "infinite";
//生成@keyframs时间轴
var timelines = [];
var base = 100 / frameNumber;
for (var i = 1; i <= frameNumber; i++) {
timelines.push(i * base);
}
//生成@keyframes内部字符串
var innerString = "";
timelines.forEach(function (item, index) {
innerString = innerString + item + "%" + "{" + animateKey + ":" + animateValueList[index] + ";}";
});
var string = "@keyframes " + name + "{" + innerString + "}";
string = string + "@-webkit-keyframes " + name + "{" + innerString + "}";
string = string + containerName + "{animation: " + name + " " + times + " " + animateFun + " " + isInfinite + ";";
string = string + "-webkit-animation: " + name + " " + times + " " + animateFun + " " + isInfinite + ";}"
return string;
}
/**
* 随机颜色生成器
*/
var getRandomColor = function () {
return "#" + (function (color) {
var rand = Math.floor(Math.random() * 0XFFFFFF).toString(16);
return rand.length == 6 ? rand : arguments.callee(color);
})('');
}
/**
* 将动画添加到head元素
*/
var appendAnimation = function (containerName, frameNumber, animateKey) {
var headTag = document.getElementsByTagName("head")[0];
var styleTag = document.getElementsByTagName("style")[0];
if (!styleTag) {
styleTag = document.createElement("style");
styleTag.type = "text/css";
}
var colorList = [];
for (var j = 1; j <= frameNumber; j++) {
colorList.push(getRandomColor());
}
var animation = generateAnimationCss(containerName, frameNumber, animateKey, colorList);
var textNode = document.createTextNode(animation);
styleTag.appendChild(textNode);
headTag.appendChild(styleTag);
}
/**
* dom加载完成后开始执行
*/
window.onload = function () {
//必须先给定动画容器的宽高
appendAnimation("#container", 35, "background-color");
};
</script>
</body>
</html>
最后实现的效果:(尴尬!CSDN竟然不支持上传自定义视屏,那大家只能把代码搞到本地去跑咯~)