Как сделать круговой прогресс бар с помощью Canvas?
Как сделать круг в <canvas> проблем не возникает
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.arc(100, 100, 90, 0, 2 * Math.PI);
ctx.strokeStyle = "#1CB3FE"
ctx.lineWidth = 10;
ctx.stroke();
<canvas width="400" height="400" </canvas>
Проблемы - создать анимацию растущей линии прогресс-бара, то есть найти аналог в canvas атрибутам CSS и SVG stroke-dasharray и stroke-dashoffset
Вопрос:
Как повторить средствами canvas анимацию прогресс-бара с выводом процентов выполнения:
svg {
height: 90vh;
margin: auto;
display: block;
}
path {
stroke-linecap: round;
stroke-width: 6;
}
path.grey {
stroke: #e7e7e8;
}
path.purple {
stroke: url(#gradient);
stroke-dasharray: 198;
stroke-dashoffset: 198;
animation: dash 3s ease-out forwards;
}
path.white {
stroke: #ffffff;
stroke-dasharray: 0px, 198px;
stroke-dashoffset: 198;
stroke-width: 3.5px;
animation: dash 3s ease-out forwards;
}
@keyframes dash {
to {
stroke-dashoffset: 1;
}
}
<svg viewbox="0 0 100 100">
<linearGradient id="gradient" x1="0" y1="0" x2="0" y2="100%">
<stop offset="0%" stop-color="#56c4fb" />
<stop offset="100%" stop-color="#0baeff" />
</linearGradient>
<path class="grey" d="M30,90 A40,40 0 1,1 80,90" fill='none' />
<path id="purple" fill='none' class="purple" d="M30,90 A40,40 0 1,1 80,90" />
<path id="white" fill='none' class="white" d="M30,90 A40,40 0 1,1 80,90" />
</svg>
Ответы (3 шт):
var flag = false;
var time = 5000;
var step = 1;
document.addEventListener('DOMContentLoaded', function () {
var percentage = document.querySelectorAll('.survey-results__percentage');
var targetBox = document.querySelector('.survey-results__wrap');
var current = document.documentElement.scrollTop;
var target = targetBox.offsetTop - 200;
var circles = document.querySelectorAll('.survey-results__circle');
if (current >= target && !flag) {
for (var i = 0; i < circles.length; i++) {
circles[i].classList.add('active');
outNum(parseFloat(percentage[i].dataset.percent), percentage[i]);
flag = true;
}
}
});
function outNum(num, elem) {
var e = elem;
n = 0;
var t = time / (num / step);
var interval = setInterval(() => {
n = n + step;
if (n >= num) {
clearInterval(interval);
e.innerHTML = num.toString().replace('.', ',') + '%';
} else {
e.innerHTML = parseFloat(n).toFixed(2).toString().toString().replace('.', ',') + '%';
}
}, t);
}
.survey-results__wrap {
display: flex;
}
.survey-results__block {
max-width: 170px;
}
.survey-results__chart {
margin-bottom: 10px;
width: 170px;
height: 170px;
}
.survey-results__circular {
display: block;
width: 100%;
height: 100%;
}
.survey-results__circle {
fill: none;
stroke-width: 2.8;
stroke: #58bdfc;
animation: progress 2s ease-out forwards;
}
.survey-results__percentage {
font-family: inherit;
font-weight: 700;
font-size: 8px;
fill: #ec8d06;
text-anchor: middle;
}
@keyframes progress {
0% {
stroke-dasharray: 0 100;
}
}
<div class="survey-results__wrap">
<div class="survey-results__block">
<div class="survey-results__chart">
<svg viewBox="0 0 36 36"
class="survey-results__circular survey-results__circular--accent">
<path class="survey-results__circle" stroke-dasharray="98.7, 100"
d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831"/>
<text x="18" y="20.35" class="survey-results__percentage" data-percent="98.7">
98,7%
</text>
</svg>
</div>
</div>
</div>
Как вариант можно и это, в stroke-dasharray менять на нужное кол-во процентов
Где-то так:
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
const from = 0.75 * Math.PI;
const to = 2.25 * Math.PI;
var progress = 0.65;
ctx.beginPath();
ctx.arc(100, 100, 90, from, to);
ctx.strokeStyle = "#ccc"
ctx.lineWidth = 10;
ctx.stroke();
ctx.beginPath();
ctx.arc(100, 100, 90, from, from + (to-from)*progress);
ctx.strokeStyle = "#1CB3FE"
ctx.lineWidth = 10;
ctx.stroke();
<canvas width="400" height="400" </canvas>
Менять значение progress и перерисовывать.
При использовании canvas анимацию придется делать самостоятельно.
Например воспользоваться requestAnimationFrame, для вызова каждого шага отрисовки.
Для отрисовки круга, можно использовать .arc. Данный метод позволяет указать центр круга и какой сегмент в радианах нужно отрисовать
Для облегчения расчетов можно переместить систему координат в центр круга, с помощью .translate
Теперь достаточно задать начальный угол, конечный угол и шаг изменения и вызывать перерисовку каждый раз изменяя текущий угол.
Например:
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
const startAngle = Math.PI / 6;
const endAngle = 2 * Math.PI - startAngle;
const stepAngle = Math.PI / 108;
let curAngle = startAngle;
var grad = ctx.createLinearGradient(0, 0, 100, 0);
grad.addColorStop(0, "#56c4fb");
grad.addColorStop(1, "#0baeff");
function drawArc(start, end, strokeStyle) {
ctx.save()
ctx.translate(120, 120);
ctx.rotate(Math.PI / 2);
ctx.strokeStyle = strokeStyle;
ctx.beginPath();
ctx.lineWidth = 20;
ctx.lineCap = 'round';
ctx.arc(0, 0, 100, start, end);
ctx.stroke();
ctx.restore();
}
function drawCircle(angle) {
ctx.save();
ctx.translate(120, 120);
ctx.rotate(Math.PI / 2);
ctx.fillStyle = 'white';
ctx.translate(Math.cos(angle) * 100, Math.sin(angle) * 100)
ctx.beginPath();
ctx.arc(0, 0, 8, 0, 2 * Math.PI);
ctx.fill();
ctx.restore();
}
function drawText(length, cur) {
const percent = (cur / length * 100).toFixed(0);
ctx.save();
ctx.translate(120, 120);
ctx.font = 'bold 50px sans-serif';
ctx.textBaseline = 'middle';
ctx.textAlign = 'center';
ctx.beginPath();
ctx.fillText(percent + '%', 0, 0);
ctx.stroke();
ctx.restore();
}
function drawSpeed(start, end, cur) {
drawArc(start, end, 'lightgrey');
drawArc(start, cur, grad);
drawText(end - start, cur - start);
if (cur !== end) {
drawCircle(cur);
}
}
requestAnimationFrame(function draw(t) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
curAngle = Math.min(curAngle + stepAngle, endAngle);
drawSpeed(startAngle, endAngle, curAngle);
if (curAngle < endAngle) {
requestAnimationFrame(draw);
}
})
<canvas width="400" height="400"> </canvas>
