БлогNot. Строим контуры по опорным точкам на canvas

Строим контуры по опорным точкам на canvas

Построив полиномы по упорядоченным значениям [x, y], добавим ещё случай, когда опорные точки для кривых Безье (а другого готового механизма в JS особо и нет) не обязаны быть упорядоченными по возрастанию x, то есть, задача интерполяции не ставится.

Вместо этого, задав массив points, состоящий из подмассивов, описывающих отдельные контуры как списки пар значений [x, y] координат опорных точек на canvas, мы можем соединить эти точки гладкой кривой в том порядке, в каком они перечислены в списке. Не вдаваясь в теорию, уточним, что итоговая кривая будет проходить через первую и последнюю точки контура, но не все промежуточные.

Линиям легко задать любую толщину, цвет, стиль и даже градиент, но нам хотелось сделать картинку ещё и "переливающейся". Для этого основной код вынесен в метод animateGradient, самовызывающийся по таймеру и с помощью пары переменных offset и delta управляющий промежуточным цветом градиента контуров (addColorStop).

Замкнутые контуры (у нас только "0") и тонкие линии (у нас толстая, 30 пикселей) анимируются таким подходом лучше. У нас цвет "доходит не до конца" именно из-за толщины линии. Далее показан скрипт в работе и код (без стандартных обрамляющих тегов HTML).

 <div style="width: 600px; margin: 0 auto;">
  <canvas id="myCanvas24" width="570" height="200" style="border: 1px dotted black;"></canvas>
 </div>
 <script>
  const canvas = document.getElementById("myCanvas24");
  const ctx = canvas.getContext("2d");
  const points = [ //опорные точки на канве
   [[20, 30], [95, 20], [95, 95], [20, 95], [20, 175], [105, 175]],
   [[220, 25], [245, 25], [245, 175], [170, 175], [170, 25], [220, 25]],
   [[320, 25], [395, 15], [395, 95], [320, 90], [320, 175], [450, 175]],
   [[470, 20], [470, 95], [545, 95], [545, -10], [545, 180]] 
  ];
  const colors =  ["#ff0000", "#0000ff", "#008000", "#800080"]; //основные цвета для градиента
  const colors2 = ["#800080", "#004080", "#404040", "#c00040"]; //промежуточные цвета для анимации
  ctx.lineWidth = 30; //толщина линии
  ctx.lineCap = 'round'; 
  let offset = 0, delta = 0.01;
   
  function animateGradient() { 
   points.forEach((line, index) => {
    let len = line.length - 1;
    if (line[0][0] == line[len][0] && line[0][1] == line[len][1])
     len = Math.floor(len / 2); //конечная точка градиента зависит от того, замкнут ли контур
    const gradient = ctx.createLinearGradient(line[0][0], line[0][1], line[len][0], line[len][1]);
    gradient.addColorStop(0, colors[index]);
    gradient.addColorStop(offset, colors2[index]);
    gradient.addColorStop(1, colors[(index + 1)%colors.length]);
    ctx.strokeStyle = gradient;
    ctx.beginPath();
    ctx.moveTo(line[0][0], line[0][1]);
    for (let i = 1; i < line.length - 1; i++) {
     const cpX = (line[i][0] + line[i + 1][0]) / 2;
     const cpY = (line[i][1] + line[i + 1][1]) / 2;
     ctx.quadraticCurveTo(line[i][0], line[i][1], cpX, cpY);
    }
    ctx.lineTo(line[line.length - 1][0], line[line.length - 1][1]);
    ctx.stroke();
   });
   offset += delta;
   if (offset >= 1) { offset = 1; delta = -0.01; }
   else if (offset <= 0) { offset = 0; delta = 0.01; }   
   setTimeout(() => { requestAnimationFrame(animateGradient); }, 1000/60);
  }
  
  animateGradient();
 </script>
 <noscript>
  <p>Включите Javascript в браузере для работы приложения.</p>
 </noscript>

13.06.2024, 12:47 [276 просмотров]


теги: javascript графика

К этой статье пока нет комментариев, Ваш будет первым