【问题标题】:2d-transform - How to keep position?2d-transform - 如何保持位置?
【发布时间】:2018-10-04 19:29:52
【问题描述】:

我想在画布的右下角绘制几个透视变换的矩形(形状)。为此,我使用了 ctx.transform:ctx.transform(1, 0, -1, 1, 10, 10)

现在我想使用变量scale=n 缩放我的绘图大小,但仍将位置保持在该点(在中心)。

这是我到目前为止编写的代码。移动滑块会更改形状的位置。我怎样才能避免这种情况?

let canvas = document.getElementById("canvas")
canvas.width = canvas.height = 200;
$(canvas).appendTo(document.body)
let ctx = canvas.getContext("2d")

let update = function(input) {
  let scale = input.value;
  ctx.clearRect(0, 0, canvas.width, canvas.height)
  ctx.transform(1, 0, -1, 1, 10, 10)
  for (let i = 0; i < 4; i++) {
    ctx.fillStyle = (i === 2) ? "#3b2a19" : "#957e67"
    let layer = {
      x: canvas.width + scale * 7 - i * scale,
      y: canvas.height - scale * 5 - i * scale,
      width: scale * 3,
      height: scale * 1.5
    }
    ctx.fillRect(layer.x, layer.y, layer.width, layer.height)
    ctx.strokeRect(layer.x, layer.y, layer.width, layer.height)
  }
  ctx.resetTransform();
}

$("input").trigger("input")
#canvas {
  border: 2px solid red
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id="canvas"></canvas>

<input oninput="update(this)" type="range" min="1" max="20" />

【问题讨论】:

    标签: javascript html css canvas transform


    【解决方案1】:

    我建议您自己绘制形状,而不是使用变换。

    参见下面的示例代码:

    let canvas = document.getElementById("canvas")
    canvas.width = canvas.height = 200;
    $(canvas).appendTo(document.body)
    let ctx = canvas.getContext("2d")
    
    function shape(x,y,s) {
      var f = s/18
      ctx.moveTo(x-10*f, y-60*f);
      ctx.lineTo(x-110*f, y-60*f);
      ctx.lineTo(x-160*f, y-10*f);
      ctx.lineTo(x-60*f, y-10*f);
      ctx.lineTo(x-10*f, y-60*f);
      ctx.fill();
      ctx.stroke();
    }
    
    let update = function(input) {
      let scale = input.value;
      ctx.clearRect(0, 0, canvas.width, canvas.height)
      ctx.lineWidth = Math.min(scale/2, 2);
      for (let i = 0; i < 4; i++) {
        ctx.beginPath();
        ctx.fillStyle = (i === 2) ? "#3b2a19" : "#957e67"
        shape(canvas.width, canvas.height -i * scale * 1.5, scale)
      }
    }
    
    $("input").trigger("input")
    canvas { border: 1px solid red }
    input { position: absolute }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <input oninput="update(this)" type="range" min="1" max="20" >
    <canvas id="canvas"></canvas>

    【讨论】:

    • 笔画也需要缩放
    • @Kaiido 它是ctx.lineWidth = Math.min(scale/2, 2);
    【解决方案2】:

    避免在绘图中进行太多计算。

    所有形状实际上共享相同的恒定宽度和高度,以及相对于堆栈中前一个形状的恒定偏移量。
    唯一真正的变量是相机的位置。

    所以只修改这个相机,为此,只改变你的上下文的变换矩阵。

    你需要

    • 将上下文转换为第一个形状的原点
    • 按当前比例值缩放上下文
    • 在 Y 轴上将上下文倾斜 -1
    • 用常量值绘制堆栈

    前三个步骤可以在一次调用绝对 setTransform 方法中完成。

    参数将是,

    setTransform(
      scale,    // scale-x
      0,        // skew-x
      - scale,  // skew-y (we use '- scale' here because skew should be made
                // after scale so we need to multiply by scale)
      scale,    // scale-y
      origin_x, // these should be made before so normal scale is ok
      origin_y
    )
    

    之后,真正的绘图部分将永远是一样的。

    const canvas = document.getElementById("canvas")
    canvas.width = canvas.height = 200;
    const ctx = canvas.getContext("2d")
    const input = document.querySelector('input');
    function update() {
      // some constants about our shapes
      const origin_x = 160; // center of all the rects
      const origin_y = 160; // bottom of all the shapes
      const w = 33; // width of each rect
      const h = 16.5; // height of each rect
      const offset = 11; // axis offset (* i)
      const scale = input.value;
      
      // first reset the transformation matrix
      ctx.setTransform(1,0,0,1,0,0);
      // so we clear everything
      ctx.clearRect(0, 0, canvas.width, canvas.height)
      // now we move our context
      // so that our origins are in the top - left
      // and that we are already scaled and skewed
      ctx.setTransform(scale, 0, -scale, scale, origin_x, origin_y);
      
      // from now on, we don't care about the scale
      for (let i = 0; i < 4; i++) {
        ctx.fillStyle = (i === 2) ? "#3b2a19" : "#957e67"
        let layer = {
          x: -i * offset - w/2,
          y: -i * offset,
          width: w,
          height: h
        }
        ctx.fillRect(layer.x, layer.y, layer.width, layer.height)
        ctx.strokeRect(layer.x, layer.y, layer.width, layer.height)
      }
    }
    input.oninput = update;
    input.oninput();
    #canvas {
      border: 2px solid red
    }
    input {
      display: block;
      width: 75vw;
    }
    <input type="range" min="0.01" max="20" value="1" step="0.001" id="inp"/>
    <canvas id="canvas"></canvas>

    【讨论】:

    • “画布”。不是一堆形状。从 OP “但仍将位置准确地保持在这一点(在中心)。” 无论如何,这只是在恒定绘图部分要做的一点修改(只需更改 x 和 y 以及可能的原点)答案保持不变。
    • 函数内部const有什么好处?
    • 它们是常量,所以应该使用const。它们将更容易针对解释器进行优化。
    • 既然是常量,就应该在函数之外,所以每次函数运行时都不会分配内存?
    • 可能是的,但这也会污染主范围。反正不是很重要。
    猜你喜欢
    • 1970-01-01
    • 2011-11-28
    • 1970-01-01
    • 2011-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-14
    • 2010-10-03
    相关资源
    最近更新 更多