动画绘制arcTo到画布上的线

时间:2020-02-09 15:00:45

标签: html canvas html5-canvas

我正在尝试实现在Canvas上绘制arcTo线的动画。例如,对于一条直线,动画将如下所示

c = canvas.getContext("2d");

width = window.innerWidth;
height = window.innerHeight;
complete = false
var percent = 1

function drawEdge(x1, y1, x2, y2, color){
 c.beginPath();
 c.lineWidth = 10;
 c.strokeStyle = color;
 c.moveTo(x1, y1);
 c.lineTo(x2, y2);
 c.stroke();
 c.closePath();
}

function getPosition(x1, y1, x2, y2, percentageBetweenPoints){
 let xPosition = x1 + (x2 - x1) * (percentageBetweenPoints / 100);
 let yPosition = y1 + (y2 - y1) * (percentageBetweenPoints / 100);

 const position = {
     x: xPosition,
     y: yPosition,
 }
 return position
}

function drawLine(){
 if (!complete){
     requestAnimationFrame(drawLine);
 }

 if (percent >= 100){
     complete = true;
     percent = 100;
 } else{
     percent = percent + 1;
 }

 position = getPosition(300,300,1000,300,percent);
 c.clearRect(0, 0 , width, height);
 drawEdge(300,300,position.x,position.y, "black");

}

drawLine()

这将创建在屏幕上绘制线条的动画。但是,我无法对arcTo线执行相同的操作。有什么办法可以实现?

2 个答案:

答案 0 :(得分:1)

您正在寻找类似的东西吗?

let ctx = canvas.getContext('2d');
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.font = 'bold 18px Arial';

requestAnimationFrame(draw);

function draw(t) {
  t = t % 5e3 / 5e3;
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.beginPath();
  ctx.arc(canvas.width/2, canvas.height/2, 50, 0, t * 2 * Math.PI);
  ctx.stroke();
  ctx.fillText((t*100).toFixed(0), canvas.width/2, canvas.height/2);
  requestAnimationFrame(draw);
}
<canvas id=canvas></canvas>

答案 1 :(得分:0)

要砍还是不砍?

有两种方法可以做到这一点

  1. 计算每个线段的起点,终点和长度,起点,终点角度,方向(CW或CCW)以及每个弧段的中心。基本上重复所有使None成为有用的渲染函数的数学和逻辑(大约50行代码)。

    您可以从html5 canvas triangle with rounded corners

  2. 获取有关如何采用完整解决方案的详细信息
  3. 使用arcTo并使用长划线和长空格。随着时间的推移,使用ctx.lineDash来移动破折号,使线条的长度逐渐增加(请参见演示)。破折号偏移值是相反的,从最大长度开始,到零为止。

    注意,此方法存在一个问题。您不知道行的长度,因此您不知道完成行将花费多长时间。您可以进行估算。要知道行的长度,您必须进行所有计算(大约)

黑客

由于第二种方法最容易实现并且满足大多数需求,因此我将演示该方法。

不用多说了,它为ctx.lineDashOffset创建的路径设置了动画 附带的好处是它将使使用ctx.arcTo

渲染的任何路径动画化

ctx.stroke
requestAnimationFrame(mainLoop);
// Line is defined in unit space. 
// Origin is at center of canvas, -1,-1 top left, 1, 1 bottom right
// Unit box is square and will be scaled to fit the canvas size.
// Note I did not use ctx.setTransform to better highlight what is scaled and what is not.
const ctx = canvas.getContext("2d");
var w, h, w2, h2;          // canvas size and half size
var linePos;               // current dash offset
var scale;                 // canvas scale
const LINE_WIDTH = 0.05;      // in units
const LINE_STYLE = "#000"; // black
const LINE_SPEED = 1;      // in units per second
const MAX_LINE_LENGTH = 9; // in units approx
const RADIUS = 0.08;       //Arc radius in units
const SHAPE = [[0.4, 0.2], [0.8, 0.2], [0.5, 0.5], [0.95, 0.95], [0.0, 0.5], [-0.95, 0.95], [-0.5, 0.5], [-0.8, 0.2], [-0.2, 0.2],  [-0.2, -0.2], [-0.8, -0.2], [-0.5, -0.5], [-0.95, -0.95], [0.0, -0.5], [0.95,-0.95], [0.5, -0.5], [0.8, -0.2], [0.2, -0.2], [0.2, 0.2], [0.6, 0.2], [0.8, 0.2]];

function sizeCanvas() {
    w2 = (w = canvas.width = innerWidth) / 2;
    h2 = (h = canvas.height = innerHeight) / 2;
    scale = Math.min(w2, h2);
    resetLine();
}

function addToPath(shape) {
   var p1, p2;
   for (p2 of shape) {
       !p2.length ? 
           ctx.closePath() :
           (p1 ? ctx.arcTo(p1[0] * scale + w2, p1[1] * scale + h2, p2[0] * scale + w2, p2[1] * scale + h2, RADIUS * scale) : 
                ctx.lineTo(p2[0] * scale + w2, p2[1] * scale + h2)
            );
       p1 = p2;
   }
}

function resetLine() {
    ctx.setLineDash([MAX_LINE_LENGTH * scale, MAX_LINE_LENGTH * scale]);
    linePos = MAX_LINE_LENGTH * scale;
    ctx.lineWidth = LINE_WIDTH  * scale;
    ctx.lineJoin = ctx.lineCap = "round";
}

function mainLoop() {
    if (w !== innerWidth || h !== innerHeight) { sizeCanvas() }
    else { ctx.clearRect(0, 0, w, h) }
    ctx.beginPath();
    addToPath(SHAPE);  
    ctx.lineDashOffset = (linePos -= LINE_SPEED * scale * (1 / 60));
    ctx.stroke();
    if (linePos <= 0) { resetLine() }
    requestAnimationFrame(mainLoop);
}
    
    
body {
   padding: 0px,
   margin: 0px;
}
canvas {
   position: absolute;
   top: 0px;
   left: 0px;
}