画布仅缩放大小而不缩放坐标

wha*_*les 1 javascript html5-canvas

我正在尝试按比例对画布元素进行动画处理。是否可以仅缩放元素的大小(宽度和高度)而不缩放坐标系?标准 ctx.scale(x,y) 似乎对两者都有影响。

如果运行此代码片段,您将看到该元素变得更大。然而,它也在从一个位置转移到另一个位置。

var c = document.getElementById("c");
var ctx = c.getContext("2d");
var cH; 
var cW;
cW = window.innerWidth;
cH = window.innerHeight;
c.width = cW * devicePixelRatio;
c.height = cH * devicePixelRatio;
ctx.scale(devicePixelRatio, devicePixelRatio);
var startX = 200;
var startY = 100;
var currentScale = 0.1

function animateCloud () {
  // draw cloud shape
  ctx.scale(currentScale, currentScale)
  ctx.beginPath();
  ctx.moveTo(startX, startY);
  ctx.bezierCurveTo(startX - 40, startY + 10, startX - 20, startY + 180, startX + 60, startY + 70);
  ctx.bezierCurveTo(startX + 100, startY + 100, startX + 150, startY + 100, startX + 150, startY + 70);
  ctx.bezierCurveTo(startX + 280, startY + 70, startX + 230, startY + 40, startX + 210, startY + 20);
  ctx.bezierCurveTo(startX + 360, startY - 40, startX + 210, startY - 50, startX + 160, startY - 30);
  ctx.bezierCurveTo(startX + 150, startY - 75, startX + 80, startY - 60, startX + 70, startY - 30);
  ctx.bezierCurveTo(startX + 30, startY - 75, startX - 10, startY - 60, startX, startY);
  ctx.closePath();

  ctx.lineWidth = 5;
  ctx.strokeStyle = "#333";
  ctx.stroke();
  
  ctx.setTransform(1,0,0,1,0,0)

  if (currentScale >= 1 ){
    
  }else{
    currentScale += 0.1
    console.log(currentScale)
    console.log('animations going')
    requestAnimationFrame(animateCloud)
  }
}

requestAnimationFrame(animateCloud)
Run Code Online (Sandbox Code Playgroud)
canvas {
  display: block;
  width: 100vw;
  height: 100vh;
  cursor: pointer; 
}
Run Code Online (Sandbox Code Playgroud)
<canvas id="c"></canvas>
Run Code Online (Sandbox Code Playgroud)

我希望它保持在同一位置但变大。缩放也会影响坐标,这就是它在位置方面移动的原因。

我能想到的一种解决方案是遍历每个路径的坐标并将其除以比例变量。然而直觉上这似乎是一个糟糕的解决方案。

gma*_*man 6

translate您需要使用(androtate和)设置事物缩放的位置scale。事物总是围绕原点扩展。原点从左上角 0,0 开始。如果你想从其他地方进行缩放,你需要移动该原点,然后围绕该原点绘制内容。

例子:

const ctx = document.querySelector("canvas").getContext("2d");

function render(time) {
  time *= 0.001; // seconds
  
  const width = ctx.canvas.width;
  const height = ctx.canvas.height;
  ctx.clearRect(0, 0, width, height);
  
  // save all the canvas settings
  ctx.save();
  
  // move origin to center of canvas
  ctx.translate(width / 2, height / 2);
  
  const xScale = lerp(.5, 2, sinLerp(time * 1.1));
  const yScale = lerp(.5, 2, sinLerp(time * 1.7));
  
  ctx.scale(xScale, yScale);
  
  // draw a rectangle around the origin
  ctx.fillStyle = "red";
  ctx.fillRect(-20, -10, 40, 20);
  
  // restore all the canvas settings
  ctx.restore();
  
  requestAnimationFrame(render);
}
requestAnimationFrame(render);

// returns a number between 0 and 1
function sinLerp(t) {
  return Math.sin(t) * .5 + .5;
}

// returns a number bewteen a and b
// assuming t is between 0 and 1 inclusive
function lerp(a, b, t) {
  return a + (b - a) * t;
}
Run Code Online (Sandbox Code Playgroud)
canvas {
  border: 1px solid black;
}
Run Code Online (Sandbox Code Playgroud)
<canvas></canvas>
Run Code Online (Sandbox Code Playgroud)

在您的情况下,首先将原点移动到您想要形状中心的位置,然后缩放,然后将原点从当前位置移动,以便在绘制形状时其中心位于原点。在您的情况下,您的云看起来有 600x200 大,因此为了使云的中心绘制在原点 (0,0),我们需要翻译 -300,-100

var c = document.getElementById("c");
var ctx = c.getContext("2d");
var cH; 
var cW;
cW = window.innerWidth;
cH = window.innerHeight;
c.width = cW * devicePixelRatio;
c.height = cH * devicePixelRatio;
ctx.scale(devicePixelRatio, devicePixelRatio);
var startX = 200;
var startY = 100;
var currentScale = 0.1

function animateCloud () {
  // draw cloud shape

  // move the origin to the center (or anywhere)
  ctx.translate(c.width / 2, c.height / 2);
  
  ctx.scale(currentScale, currentScale);
  
  // move the origin from the center
  // so that the center is in the
  // center of your cloud
  
  // PS: just guessed the center is at 300,100
  // so you need to start drawing at -300,-100
  // from the origin so your shape's center
  // ends up at 0,0
  ctx.translate(-300, -100);
  
  ctx.beginPath();
  ctx.moveTo(startX, startY);
  ctx.bezierCurveTo(startX - 40, startY + 10, startX - 20, startY + 180, startX + 60, startY + 70);
  ctx.bezierCurveTo(startX + 100, startY + 100, startX + 150, startY + 100, startX + 150, startY + 70);
  ctx.bezierCurveTo(startX + 280, startY + 70, startX + 230, startY + 40, startX + 210, startY + 20);
  ctx.bezierCurveTo(startX + 360, startY - 40, startX + 210, startY - 50, startX + 160, startY - 30);
  ctx.bezierCurveTo(startX + 150, startY - 75, startX + 80, startY - 60, startX + 70, startY - 30);
  ctx.bezierCurveTo(startX + 30, startY - 75, startX - 10, startY - 60, startX, startY);
  ctx.closePath();

  ctx.lineWidth = 5;
  ctx.strokeStyle = "#333";
  ctx.stroke();
  
  ctx.setTransform(1,0,0,1,0,0)

  if (currentScale >= 1 ){
    
  }else{
    currentScale += 0.1
    console.log(currentScale)
    console.log('animations going')
    requestAnimationFrame(animateCloud)
  }
}

requestAnimationFrame(animateCloud)
Run Code Online (Sandbox Code Playgroud)
body { margin: 0; }
canvas {
  display: block;
  width: 100vw;
  height: 100vh;
  cursor: pointer; 
}
Run Code Online (Sandbox Code Playgroud)
<canvas id="c"></canvas>
Run Code Online (Sandbox Code Playgroud)