提到这种动态绘制矢量图形的需求,一般会想到使用canvas;由于笔者不太熟悉canvas动画也可以考虑用CSS来实现,这里先记录使用CSS实现此效果的尝试过程:
①实现一条带有静态“流光”效果的边,参考CSS渐变背景;
②实现静态线条的“流光”动画效果,参考纯CSS实现背景颜色渐变动画;
③将以上步骤得到的流光边进行旋转(参考CSS-rotate)、镜像翻转(参考CSS水平翻转和垂直翻转),即可实现与此边同向和逆向的其他流光边效果,同时设置旋转和镜像翻转参考CSS同时设置多个变换效果
body {
background: #2A2A2A;
}
.light-line {
position: absolute;
top: 20px;
left: 20px;
width: 320px;
height: 320px;
}
.light-line-r {
position: absolute;
top: 20px;
left: 360px;
width: 320px;
height: 320px;
}
/* 渐变流光效果线条,要将横向宽度设置为超过100%的值,否则无动画效果 */
.line-block {
position: relative;
width: 100%;
height: 6px;
background: linear-gradient(
-90deg,
#FFEFCA 1%, #FFBB1F 4%, transparent 12%, transparent 16%,
#FFEFCA 16%, #FFBB1F 19%, transparent 27%, transparent 33%,
#FFEFCA 33%, #FFBB1F 36%, transparent 44%, transparent 50%,
#FFEFCA 50%, #FFBB1F 53%, transparent 61%, transparent 66%,
#FFEFCA 66%, #FFBB1F 69%, transparent 77%, transparent 84%,
#FFEFCA 84%, #FFBB1F 87%, transparent 95%, transparent 100%
);
background-size: 200% 100%;
}
/* 指定使用Gradient动画,5s完成一次动画,匀速,无限循环 */
.gradient {
animation: Gradient 5s linear infinite;
-webkit-animation: Gradient 5s linear infinite;
-moz-animation: Gradient 5s linear infinite;
}
/* 定义Gradient动画效果:初始时显示最右端,结束时显示最左端(向右滚动) */
@keyframes Gradient {
0% {
background-position: 100% 100%;
}
100% {
background-position: 0% 100%;
}
}
/* 兼容写法.. */
@-webkit-keyframes Gradient {
0% {
background-position: 100% 100%;
}
100% {
background-position: 0% 100%;
}
}
/* 兼容写法.. */
@-moz-keyframes Gradient {
0% {
background-position: 100% 100%;
}
100% {
background-position: 0% 100%;
}
}
.right-top {
transform: rotate(0deg);
}
.right-bottom {
transform: rotate(90deg);
}
.left-bottom {
transform: rotate(180deg);
}
.left-top {
transform: rotate(270deg);
}
.right-top-r {
transform: rotate(0deg) rotateY(180deg);
}
.right-bottom-r {
transform: rotate(90deg) rotateY(180deg);
}
.left-bottom-r {
transform: rotate(180deg) rotateY(180deg);
}
.left-top-r {
transform: rotate(270deg) rotateY(180deg);
}
可以拷贝到这里在线调试:
菜鸟工具–HTML/CSS/JS在线工具
- 首先记录一个大坑:由于不熟悉animation参数,最开始还因为animation-timing-function设置成了ease(慢->快->慢不匀速动画)导致流光动画不流畅
而试图用两段动画方向相反的线实现“流畅”的动画效果,具体思路:第一段线流动方向左->右->左,第二段线流动方向右->左->右,当线左->右运动时显示出来,而右->左运动时设置透明度为0隐藏(其实用这样的方式调试后实现的动画效果依然不完全流畅),将这一属性设置为linear可使动画匀速;- 可以通过将两段渐变色的百分比设为相同值来实现对比鲜明的渐变效果;
今天闲得无聊,突发奇想想要实现在之前基础上,流光颜色渐变效果,先贴上代码:
body {
background: #2A2A2A;
}
.light-line {
position: absolute;
top: 20px;
left: 20px;
width: 320px;
height: 320px;
}
/* 渐变流光效果线条,要将横向宽度设置为超过100%的值,否则无动画效果 */
.line-block {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 6px;
}
// 流光色彩配置
const gradientColors = [
{ name: "red", startColor: "#ffc1cb", endColor: "#ffaaaa"},
{ name: "yellow", startColor: "#ffffb3", endColor: "#ffff00"},
{ name: "green", startColor: "#b3ffb3", endColor: "#00ff00"},
{ name: "cyan", startColor: "#b3ffff", endColor: "#00ffff"},
{ name: "blue", startColor: "#ccccff", endColor: "#0000ff"},
{ name: "purple", startColor: "#f7b3ff", endColor: "#ce00e6"},
{ name: "pink", startColor: "#ffe1e7", endColor: "#ffcfd9"},
{ name: "peach", startColor: "#ffc1cb", endColor: "#ff3053"}
];
// 由于流光渐变样式结构相似,提取其结构并使用代码动态生成更为便捷
const linearGradientStyle = `linear-gradient(
-90deg,
$startColor 0%, $endColor 4%, transparent 12%, transparent 17%,
$startColor 17%, $endColor 21%, transparent 29%, transparent 34%,
$startColor 34%, $endColor 37%, transparent 45%, transparent 50%,
$startColor 50%, $endColor 54%, transparent 62%, transparent 67%,
$startColor 67%, $endColor 71%, transparent 79%, transparent 84%,
$startColor 84%, $endColor 87%, transparent 95%, transparent 100%
)`;
const animationDuration = 3; // 动画时长(秒)
const animationNames = ["animation", "-webkit-animation", "-moz-animation"];
// todo 在Chrome使用insertRule插入@-moz-keyframes动画规则报错
// const animationDefs = ["@keyframes", "@-webkit-keyframes", "@-moz-keyframes"];
const animationDefs = ["@keyframes", "@-webkit-keyframes"];
// 页面加载完成后动态添加流光样式
window.onload = function() {
showGradients();
}
// 动态添加流光样式
function showGradients() {
// 每条流光色彩显示时长占动画总时长的百分比
let gradPercent = parseInt(100 / parseInt(gradientColors.length));
// 动态生成流光条结构
for(let idx in gradientColors) {
let item = gradientColors[idx], clsName = item.name;
$(".light-line, .light-line-r").append('');
// 插入流光样式
insertCssRule("." + clsName, [
{ name: "z-index", value: idx },
{ name: "background", value: linearGradientStyle
.replaceAll("$startColor", item.startColor)
.replaceAll("$endColor", item.endColor) },
{ name: "background-size", value: "200% 100%" }
]);
// 插入流光动画
let animStyles = [];
for(let gName of animationNames)
animStyles.push(
{ name: gName, value: "Gradient-" + clsName +
" " + animationDuration + "s linear infinite" }
);
insertCssRule('.gradient-' + clsName, animStyles);
// 除了下标为0(第一条)渐变色外,所有渐变色的透明度动画过程:
// 透明->淡入->显示->淡出->隐藏
// 下标为idx的渐变色开始显示时,已完成的动画时长占比值
let percentNow = idx * gradPercent;
// 下标为idx的渐变色开始淡出时,已完成的动画时长占比值
let percentNext = (parseInt(idx) + 1) * gradPercent;
// 下标为idx的渐变色开始淡入时,已完成的动画时长占比值
let firstPosi = Math.max(0, percentNow / 3);
// 下标为idx的渐变色开始隐藏时,已完成的动画时长占比值
let lastPosi = Math.min(100, parseInt(percentNext) + firstPosi);
for(let dName of animationDefs)
// 第一条渐变色设置为始终不透明,使动画循环时衔接效果更自然
if(parseInt(idx) === 0)
insertCssRule(dName + " Gradient-" + clsName, [
{ name: "0%", value: [
{ name: "background-position", value: "100% 100%" },
{ name: "opacity", value: "1"}
]},
{ name: "100%", value: [
{ name: "background-position", value: "0% 100%" },
{ name: "opacity", value: "1"}
]}
]);
else
insertCssRule(dName + " Gradient-" + clsName, [
{ name: "0%", value: [
{ name: "background-position", value: "100% 100%" },
{ name: "opacity", value: "0"}
]},
{ name: firstPosi + "%", value: [
{ name: "background-position", value: (100 - firstPosi) + "% 100%" },
{ name: "opacity", value: "0"}
]},
{ name: percentNow + "%", value: [
{ name: "background-position", value: (100 - percentNow) + "% 100%" },
{ name: "opacity", value: "1"}
]},
{ name: percentNext + "%", value: [
{ name: "background-position", value: (100 - percentNext) + "% 100%" },
{ name: "opacity", value: "1"}
]},
{ name: lastPosi + "%", value: [
{ name: "background-position", value: (100 - lastPosi) + "% 100%" },
{ name: "opacity", value: "0"}
]},
{ name: "100%", value: [
{ name: "background-position", value: "0% 100%" },
{ name: "opacity", value: "0"}
]}
]);
}
}
// 添加样式(className:样式名称,style:样式内容)
function insertCssRule(className, style) {
let styleSheets = document.styleSheets[0];
let index = styleSheets.rules.length; //获取样式表中包含样式的个数
let insertStr = "";
for(let sItem of style) {
if(typeof sItem.value === 'string') // 添加普通样式
insertStr += sItem.name + ":" + sItem.value + ";";
else { // 添加动画规则
insertStr += sItem.name + " {";
for(let animStyle of sItem.value)
insertStr += animStyle.name + ":" + animStyle.value + ";";
insertStr += "}";
}
}
if(styleSheets.insertRule){ //判断浏览器是否支持insertRule()方法
// 使用insertRule在文档内部样式表中增加样式,插入位置在样式表的末尾
styleSheets.insertRule(className + " {" + insertStr + " }", index);
}else{ // 如果浏览器不支持insertRule(IE)
styleSheets.addRule(className, insertStr, index);
}
}
感兴趣的看官可自行拷贝到菜鸟工具–HTML/CSS/JS在线工具中运行调试效果哟~
(等有时间再增加注释以及学习使用canvas实现这样的效果吧=。=)
[1] 菜鸟工具–HTML/CSS/JS在线工具
[2] CSS渐变背景
[3] 纯CSS实现背景颜色渐变动画
[4] CSS-rotate
[5] CSS水平翻转和垂直翻转
[6] CSS同时设置多个变换效果
[7] animation-timing-function
[8] JS–addRule/insertRule
[9] JS动态操作CSS