如何在 Vue 组件内创建圆形进度
Posted
技术标签:
【中文标题】如何在 Vue 组件内创建圆形进度【英文标题】:How to create a circle progress inside a Vue component 【发布时间】:2019-02-03 15:32:39 【问题描述】:我正在使用 Vue 构建的应用程序中创建一个组件。这个组件是一个倒计时,范围从X
分钟到00:00
。
我知道可以为svg
制作动画以达到预期的效果,但我没有必要的知识。我从未使用过任何svg
库。
我需要在我的进度组件中创建以下动画:
动画需要顺着路径顺着天气走。路径节点应根据时间插入/更新。
这是我实际的倒计时组件:
var app = new Vue(
el: '#app',
data:
date: moment(2 * 60 * 1000)
,
computed:
time: function()
return this.date.format('mm:ss');
,
mounted: function()
var timer = setInterval(() =>
this.date = moment(this.date.subtract(1, 'seconds'));
if(this.date.diff(moment(0)) === 0)
clearInterval(timer);
alert('Done!');
, 1000);
);
<script src="https://momentjs.com/downloads/moment.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="app"> time </div>
这是进度圈的 svg:
<svg x="0px" y="0px" viewBox="0 0 90 90">
<style type="text/css">
.st0fill:#FFFFFF;
.st1fill:none;stroke:#B5B5B5;stroke-miterlimit:10;
.st2fill:none;stroke:#408EFF;stroke-linecap:round;stroke-miterlimit:10;
.st3fill:#408EFF;
</style>
<rect class="st0" />
<circle class="st1" cx="45" cy="45" r="40"/>
<path class="st2" d="M45,5c22.1,0,40,17.9,40,40S67.1,85,45,85S5,67.1,5,45S22.9,5,45,5"/>
<circle class="st3" cx="45" cy="5" r="3"/>
</svg>
我怎样才能达到预期的效果?
欢迎所有帮助。
【问题讨论】:
@SphinxsetInterval
是必需的,因为我的进度圈使用时间而不是百分比。此外,动画并不完美,除了路径末端没有小圆圈。
【参考方案1】:
按照您的模板,一种解决方案是将路径预定义为一个数组(每个路径节点是数组的一个元素)。然后将路径节点推送到每个间隔的当前进度路径。
如下演示:
var app = new Vue(
el: '#app',
data:
date: moment(2 * 60 * 1000),
pathRoute: ['M45 5', 'c22.1 0 40 17.9 40 40','S67.1 85 45 85','S5 67.1 5 45','S22.9 5 45 5'],
pathProgess: [],
stepIndex: 0
,
computed:
time: function()
return this.date.format('mm:ss');
,
computedProgress: function ()
return this.pathProgess.join(' ')
,
mounted: function()
var timer = setInterval(() =>
this.date = moment(this.date.subtract(1, 'seconds'));
this.$set(this.pathProgess, this.stepIndex, this.pathRoute[this.stepIndex])
this.stepIndex++
if(this.date.diff(moment(0)) === 0)
clearInterval(timer);
, 1000);
);
.st0fill:#FFFFFF;
.st1fill:none;stroke:#B5B5B5;stroke-miterlimit:10;
.st2fill:none;stroke:#408EFF;stroke-linecap:round;stroke-miterlimit:10;
.st3fill:#408EFF;
<script src="https://momentjs.com/downloads/moment.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="app">
<p>computedProgress</p>
<svg x="0px" y="0px" viewBox="0 0 90 90">
<rect class="st0" />
<circle class="st1" cx="45" cy="45" r="40"/>
<text class="circle-chart-percent" x="20.91549431" y="40.5" font-size="8">time</text>
<path class="st2" :d="computedProgress"/>
<circle class="st3" cx="45" cy="5" r="3"/>
</svg>
</div>
或者您可以使用Answered at another question的方法,实时计算路径。
var app = new Vue(
el: '#app',
data:
date: moment(2 * 60 * 1000),
pathProgess: ''
,
computed:
time: function()
return this.date.format('mm:ss');
,
mounted: function()
let maxValue = this.date.diff(moment(0), 'seconds') //total seconds
var timer = setInterval(() =>
this.date = moment(this.date.subtract(1, 'seconds'))
let curValue = this.date.diff(moment(0), 'seconds') // current seconds
this.pathProgess = this.describeArc(45, 45, 40, 0, (maxValue-curValue)*360/maxValue)
if(this.date.diff(moment(0)) === 0)
clearInterval(timer);
, 1000);
,
methods:
//copy from https://***.com/a/18473154/5665870
polarToCartesian: function (centerX, centerY, radius, angleInDegrees)
var angleInRadians = (angleInDegrees-90) * Math.PI / 180.0;
return
x: centerX + (radius * Math.cos(angleInRadians)),
y: centerY + (radius * Math.sin(angleInRadians))
;
,
//copy from https://***.com/a/18473154/5665870
describeArc: function (x, y, radius, startAngle, endAngle)
var start = this.polarToCartesian(x, y, radius, endAngle);
var end = this.polarToCartesian(x, y, radius, startAngle);
var largeArcFlag = endAngle - startAngle <= 180 ? "0" : "1";
var d = [
"M", start.x, start.y,
"A", radius, radius, 0, largeArcFlag, 0, end.x, end.y
].join(" ");
return d;
);
.st0fill:#FFFFFF;
.st1fill:none;stroke:#B5B5B5;stroke-miterlimit:10;
.st2fill:none;stroke:#408EFF;stroke-linecap:round;stroke-miterlimit:10;
.st3fill:#408EFF;
<script src="https://momentjs.com/downloads/moment.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="app">
<p>pathProgess</p>
<svg x="0px" y="0px" viewBox="0 0 90 90">
<rect class="st0" />
<circle class="st1" cx="45" cy="45" r="40"/>
<text class="circle-chart-percent" x="20.91549431" y="40.5" font-size="8">time</text>
<path class="st2" :d="pathProgess"/>
<circle class="st3" cx="45" cy="5" r="3"/>
</svg>
</div>
【讨论】:
酷,不过圈子要逐渐闭合,伴随着setInterval
返回的时间。
是否有可能让圆圈和蓝色笔划沿着平行于总时间的路径? 0% = 2:00,100% = 00:00。过渡应该是平稳的。
路径节点应该根据时间插入/更新
@CaioKawasaki 查看第二个演示,它将是您所需要的。【参考方案2】:
您需要熟悉 SVG 形状,尤其是 <path>
才能制作弧线。
这是一个例子:
Vue.component('progress-ring',
template: '#progress-ring',
props:
value:
type: Number,
default: 0,
,
min:
type: Number,
default: 0,
,
max:
type: Number,
default: 1,
,
text:
type: null,
default: '',
,
,
computed:
theta()
const frac = (this.value - this.min) / (this.max - this.min) || 0;
return frac * 2 * Math.PI;
,
path()
const large = this.theta > Math.PI;
return `M0,-46 A46,46,0,$large ? 1 : 0,1,$this.endX,$this.endY`;
,
endX()
return Math.cos(this.theta - Math.PI * 0.5) * 46;
,
endY()
return Math.sin(this.theta - Math.PI * 0.5) * 46;
,
,
);
new Vue(
el: '#app',
);
body
font-family: sans-serif;
.progress-ring
width: 100px;
height: 100px;
.progress-ring-circle
stroke: rgba(0, 0, 0, 0.1);
stroke-width: 1;
fill: none;
.progress-ring-ring
stroke: #007fff;
stroke-width: 2;
fill: none;
.progress-ring-end
fill: #007fff;
<script src="https://rawgit.com/vuejs/vue/dev/dist/vue.js"></script>
<div id="app">
<progress-ring :min="0" :max="100" :value="40" text="12:34"></progress-ring>
</div>
<template id="progress-ring">
<svg class="progress-ring" viewBox="-50,-50,100,100">
<circle class="progress-ring-circle" r="46"/>
<path class="progress-ring-ring" :d="path"/>
<circle class="progress-ring-end" :cx="endX" :cy="endY" r="4"/>
<text alignment-baseline="middle" text-anchor="middle"> text </text>
</svg>
</template>
至于动画,你只需要使用 javascript 来更改 value
属性,例如使用 setInterval
或其他方式。
【讨论】:
以上是关于如何在 Vue 组件内创建圆形进度的主要内容,如果未能解决你的问题,请参考以下文章