анимация растущей линии на Canvas

123
04 апреля 2021, 08:40

Подскажите, как реализовать эффект постепенно растущей линии на canvas. Вот простой пример на canvas

var canvas = document.getElementById('cnvs'); 
var ctx = canvas.getContext('2d'); 
ctx.lineWidth = 4; 
ctx.moveTo(50, 50); 
ctx.lineTo(70, 80); 
ctx.lineTo(150, 10); 
ctx.lineTo(200, 100); 
ctx.lineTo(10, 150); 
ctx.closePath(); 
ctx.stroke();
#cnvs { 
  border: 1px solid #f00; 
}
<canvas id="cnvs" width="300" height="210">

Как сделать, что б в процессе анимации от точки к точке прорисовывался бы путь ?

Answer 1

Все посчитано при помощи обычной линейной интерполяции, хотя для этого можно было бы использовать svg.getPointAtLength().

Сперва нужно посчитать расстояния между точками и общую длину пути.

Затем, внутри цикла отрисовки необходимо считать сколько времени прошло и в какой точке пути должен находиться растущий конец линии.

Для дополнительной информации читайте комментарии в коде.

let points = [[50,50],[70,80],[150,10],[200,100],[10,150]]; 
let goal = 10000; // 10 сек 
 
// считаем расстояния между точками 
points.forEach((pt, i, arr) => { 
    let index = i ? i-1 : arr.length-1; 
    let x = arr[index][0] - pt[0]; 
    let y = arr[index][1] - pt[1]; 
    pt[2] = Math.sqrt(x*x + y*y); 
}) 
 
// общая длина пути 
let total = points.reduce((acc, el) => el[2] + acc, 0) 
 
// функция линейной интерполяции между точками p1 и p2 
let lerp = (t, p1, p2) => [p1[0] + (p2[0] - p1[0])*t, p1[1] + (p2[1] - p1[1])*t] 
 
let ctx = cnvs.getContext('2d'); 
ctx.lineWidth = 4; 
requestAnimationFrame(draw); 
 
function draw(t) { 
  ctx.clearRect(0, 0, cnvs.width, cnvs.height); 
  ctx.beginPath(); 
  ctx.moveTo(points[0][0], points[0][1]) 
 
  // если время конца анимации еще не настало 
  if (t < goal) {  
    requestAnimationFrame(draw) // запрашиваем следующий кадр 
    growPath(t/goal*total); // рисуем ползущую линию 
  }  
  // если анимация закончилась 
  else { 
    // рисуем замкнутую линию через все точки 
    points.forEach((pt, i) => i && ctx.lineTo(pt[0], pt[1])) 
    ctx.closePath(); 
  } 
  ctx.stroke(); 
  ctx.fillText((t/goal).toFixed(2), 5, 10) 
} 
 
function growPath(t) { 
  // длина пути между уже пройденными точками 
  let pathLen = 0;  
  for (var i=1; i<=points.length; i++) { 
    let pt = points[i] || points[0]; 
    // если точка уже пройдена 
    if (t - pathLen > pt[2]) {  
      pathLen += pt[2]; // считаем пройденный путь 
      ctx.lineTo(pt[0], pt[1]); // рисуем полную линию 
    }  
    // если точка еще не достигнута  
    else { 
      // считаем конечную точку и выходим из цикла 
      return ctx.lineTo(...lerp((t-pathLen)/pt[2], points[i-1], pt)); 
    } 
  } 
}
#cnvs { 
  border: 1px solid #f00; 
}
<canvas id="cnvs" width="300" height="170"></canvas>

READ ALSO
Приоритет выполнения в коде

Приоритет выполнения в коде

При создании объекта одному из свойств можно приписать вызов функцииНапример:

106
Отмена click на 5 секунд

Отмена click на 5 секунд

Ломаю себе голову уже второй часМожет кто поможет:

105
Отправка куки на сервер

Отправка куки на сервер

Есть Vue приложениеС него идёт запрос к API для получения данных о пользователе

114
Повторение анимации вылетающего блока

Повторение анимации вылетающего блока

Я не понимаю, почему не выходит повторить анимациюТо есть при нажатии на пробел я ожидаю, что вылетит столько чёрных блочков, сколько нажатий...

111