前言
思考一下这个倒计时可以如何实现呢?
基本知识
以下复制粘贴的张旭鑫大佬的文章。
SVG中有个比较重要的属性分支,名为stroke, 中文软件中称之为“描边”。
-
stroke 表示描边颜色。这很有意思,名字不是stroke-color, 而就是单纯的stroke. 其值,官方称之为”paint“,我就不上梁小丑般翻译了。一般有如下类型值:none, currentColor, <color>,none表示没有颜色,<color>就是我们常规的颜色值。RGBA, HSBA都支持。currentColor略高深,我看了下官方文档,个人理解为:共享父级但不越过SVG元素的XML中color(style中的)值;可以让路径绘制的文字直接继承父标签的color颜色值。
-
stroke-width 表示描边的粗细。
-
stroke-linecap 表示描边端点表现方式。可用值有:butt, round, square, inherit. 表现如下图:
-
stroke-linejoin 表示描边转角的表现方式。可用值有:miter, round, bevel, inherit. 表现如下图:
-
stroke-miterlimit 表示描边相交(锐角)的表现方式。默认大小是4. 什么斜角转斜面的角度损耗之类的意思,值越大,损耗越小。具体干嘛的,我自己也不确定。大家可查查其他资料。
-
stroke-dasharray 表示虚线描边。可选值为:none, <dasharray>, inherit. 其中,none表示不是虚线;<dasharray>为一个逗号或空格分隔的数值列表。表示各个虚线端的长度。可以是固定的长度值,也可以是百分比值;inherit表继承。
-
stroke-dashoffset 表示虚线的起始偏移。可选值为:<percentage>, <length>, inherit. 百分比值,长度值,继承。
-
stroke-opacity 表示描边透明度。默认是1.
基本原理
stroke-dasharray在SVG中表示描边是虚线,两个值,第一个是虚线的宽度,第二个是虚线之间的间距。只需要让间距大于等于圆的周长,然后,虚线的长度 = 百分比值 * 圆的周长就可以了。
// 假设周长是1068, percent是百分比值
circle.setAttribute('stroke-dasharray', 1068 * percent + " 1069");
stroke-dasharray默认的起始位置在右侧,而不是上方,因此需要使用transform逆时针旋转90°。
Vue实现
<template>
<div class="circle-timer__wrapper">
<svg
class="circle-timer__svg"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg">
<g class="circle-timer__circle">
<circle
class="circle-timer__path-elapsed"
cx="50"
cy="50"
r="45"></circle>
<path
:stroke-dasharray="strokeDashArray"
class="circle-timer__path-remaining"
:style="{
color: pathColor }"
d="
M 50, 50
m -45, 0
a 45,45 0 1,0 90,0
a 45,45 0 1,0 -90,0
"></path>
</g>
</svg>
<span class="circle-timer__label">
{
{time}}
</span>
</div>
</template>
<script>
const FULL_DASH_ARRAY = 283;
const DEFAULT_COLOR = '#FFE2AA';
export default {
name: 'CricleTimer',
props: {
endTime: {
type: Number,
default: 0
}, // 结束时间,单位s
startTime: Number, // 开始时间,单位s
step: {
type: Number,
default: -1
}, // 间隔时间,单位s,倒计时为负数,正计时为正数
onFinished: Function, // 计时结束事件
thresholds: Array // 阶段阈值,以及颜色变化。
},
data() {
return {
time: this.startTime,
timer: null,
timeLimit: Math.abs(this.startTime - this.endTime)
}
},
computed: {
// 路径颜色
pathColor() {
let result = DEFAULT_COLOR
if (Array.isArray(this.thresholds)) {
this.thresholds
.sort((a, b) => a.threshold - b.threshold)
.some(item => {
if (this.time <= this.timeLimit * item.threshold) {
// 根据当前时间获取距离最近的阈值的颜色
result = item.color;
return true;
}
return false;
});
}
return result
},
// stroke虚线数组
strokeDashArray() {
// 圆滑过渡
const rawTimeFraction = this.time / this.timeLimit;
const timeFraction = rawTimeFraction - (1 / this.timeLimit) * (1 - rawTimeFraction);
return `${
(timeFraction * FULL_DASH_ARRAY).toFixed(
0,
)} ${
FULL_DASH_ARRAY}`
}
},
mounted() {
this.timer = setInterval(() => {
// step>0正计时 和 step<0倒计时
if (
(this.step < 0 && this.time <= this.endTime) ||
(this.step > 0 && this.time >= this.endTime)
) {
this.onFinished?.();
clearInterval(this.timer);
} else {
const cur = this.time + this.step;
this.time = cur <= 0 ? 0 : cur;
}
}, Math.abs(this.step) * 1000);
},
beforeDestroye() {
clearInterval(this.timer)
}
}
</script>
<style lang="less">
.circle-timer {
&__wrapper {
position: relative;
width: 42px;
height: 42px;
margin: auto;
text-align: center;
}
&__svg {
// -1逆时针 1顺时针
transform: scaleX(1);
}
&__circle {
fill: none;
stroke: none;
}
&__path-elapsed {
stroke-width: 8px;
stroke: rgba(255, 255, 255, 0.2);
}
&__path-remaining {
stroke-width: 8px;
stroke-linecap: round;
transform: rotate(90deg);
transform-origin: center;
transition: 1s linear all;
fill-rule: nonzero;
stroke: currentColor;
}
&__label {
position: absolute;
width: 42px;
height: 42px;
top: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
}
}
</style>
使用
<template>
<CricleTimer
:start-time="10"
:step="-1"
:thresholds="[
{ color: '#FF9370', threshold: 0.5 },
{ color: '#FF1100', threshold: 0.25 }
]"
/>
</template>
<script>
import CricleTimer from '@/components/CircleTimer.vue'
export default {
components: {
CricleTimer
}
}
</script>
参考文档
寥寥数行SVG实现圆环loading或倒计时效果 « 张鑫旭-鑫空间-鑫生活
纯CSS实现帅气的SVG路径描边动画效果 « 张鑫旭-鑫空间-鑫生活
How to Create an Animated Countdown Timer With HTML, CSS and JavaScript | CSS-Tricks
转载:https://blog.csdn.net/sinat_36521655/article/details/115742968