Я создаю компонент в своем приложении, созданном с помощью 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">
.st0{fill:#FFFFFF;}
.st1{fill:none;stroke:#B5B5B5;stroke-miterlimit:10;}
.st2{fill:none;stroke:#408EFF;stroke-linecap:round;stroke-miterlimit:10;}
.st3{fill:#408EFF;}
</style>
<rect class = "st0" width = "90" height = "90"/>
<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>
Как добиться желаемого результата?
Любая помощь будет приветствоваться.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Следуйте своему шаблону, одно решение - это предварительно определить путь в один массив (каждый узел пути является одним элементом массива). Затем нажимайте узел пути на текущий путь выполнения для каждого интервала.
Как показано ниже, демо:
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);
}
});.st0{fill:#FFFFFF;}
.st1{fill:none;stroke:#B5B5B5;stroke-miterlimit:10;}
.st2{fill:none;stroke:#408EFF;stroke-linecap:round;stroke-miterlimit:10;}
.st3{fill:#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" width = "90" height = "90"/>
<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>Или вы можете использовать подход Ответил на другой вопрос для вычисления пути в реальном времени.
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://stackoverflow.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://stackoverflow.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;
}
}
});.st0{fill:#FFFFFF;}
.st1{fill:none;stroke:#B5B5B5;stroke-miterlimit:10;}
.st2{fill:none;stroke:#408EFF;stroke-linecap:round;stroke-miterlimit:10;}
.st3{fill:#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" width = "90" height = "90"/>
<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 проверьте вторую демонстрацию, это будет то, что вам нужно.
Вам нужно будет ознакомиться с фигурами 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 или другие средства.
@Sphinx
setIntervalтребуется, потому что мой круг прогресса работает со временем, а не с процентом. Кроме того, анимация не идеальна, если не считать маленького кружка в конце пути.