如何在HTML画布元素之外绘制?

5
我有一个HTML画布元素,并实现了一个笔刷,它可以捕获画布元素的mousedown、mousemove和mouseup事件。这对于在画布上绘图非常有效。然而,如果您的鼠标在绘制过程中离开了画布,您将无法继续绘制。这种情况很不友好,也不太适合用户体验。
如果您打开Microsoft Paint并使用笔刷或椭圆形之类的工具进行绘制,只要您从画布内开始绘制,您就可以将鼠标拖到屏幕的任何位置并重新进入画布。这也使得在角落里绘制四分之一圆变得容易,因为您可以将椭圆形工具拖出屏幕。我希望这讲得清楚些。
无论如何,我想知道是否有一种方法可以在HTML5画布中实现这个功能,或者我该如何实现这种东西。用户永远不需要看到在那里绘制的任何东西;这主要是为了提高可用性。
编辑:许多解决方案都存在坐标问题。当前我的画布位于屏幕中央,画布的左上角是(0, 0),右下角是(500, 500)。还必须考虑坐标的转换工作。
编辑2:我发现实际上可以完全在画布边界之外绘制。例如,您可以提供负宽度、高度和坐标,画布元素将处理得很好。因此,解决方案可能只涉及捕获文档的mousemove和mouseup事件,并将x和y转换为从画布的左上角开始。

3
您能否在窗口本身上捕获鼠标事件,然后委托给画布? - zero298
也许尝试发布一下您当前的示例。如果我们不必首先重新创建自己的“损坏”示例,那么帮助您会更容易。 - duncanhall
1
也许您可以将画布元素设置为窗口的全宽和高,并使绘图区域在画布内略微缩小? - Aidas Bendoraitis
@AidasBendoraitis:这也是一个相当不错的想法。让我快速尝试一下。 - Lawrence Douglas
@zero298:你可以展示一下如何做吗? - Lawrence Douglas
3个回答

2
这里有一种方法可以让您在重新进入画布时保持绘图:
  • 创建一个全局变量,并在mousedown时将其设置为true
  • 添加一个全局事件用于mouseup,以便您可以捕获是否在画布外进行操作,如果是,则将全局变量设置为false,当然,canvas元素的mouseup也需要设置相同的变量
  • 在mousemove之前检查全局变量是否为true以继续绘制
为了在画布“外部”绘制,例如在角落中的四分之一圆弧中,我会将所有事件移动到文档级别作为全局处理程序,并在单击canvas元素时捕获并将其客户端坐标传递以与文档坐标一起计算。

1
这是一份非常初步的草稿,让你可以监听窗口上的鼠标事件而不是画布,以便能够连续绘制:

var logger = document.getElementById("logger"),
  mState = document.getElementById("mState"),
  mX = document.getElementById("mX"),
  mY = document.getElementById("mY"),
  cX = document.getElementById("cX"),
  cY = document.getElementById("cY"),
  c = document.getElementById("canvas"),
  ctx = c.getContext("2d");

var mouse = {
  x: 0,
  y: 0,
  state: ""
};

function printCanvasLocation() {
  var b = c.getBoundingClientRect();
  cX.innerHTML = b.top;
  cY.innerHTML = b.left;
}

function setState(mouseE, state) {
  mouse.x = mouseE.clientX;
  mouse.y = mouseE.clientY;

  mX.innerHTML = mouseE.clientX;
  mY.innerHTML = mouseE.clientY;
  if (state) {
    mState.innerHTML = state;
    mouse.state = state;
  }
}

window.addEventListener("mousedown", function(mouseE) {
  setState(mouseE, "down");
});

window.addEventListener("mouseup", function(mouseE) {
  setState(mouseE, "up");
});

window.addEventListener("mousemove", function(mouseE) {
  var offset = c.getBoundingClientRect();

  var fix = {
    x1: (mouse.x - offset.left),
    y1: (mouse.y - offset.top),
    x2: (mouseE.clientX - offset.left),
    y2: (mouseE.clientY - offset.top)
  };

  if (mouse.state === "down") {
    ctx.moveTo(fix.x1, fix.y1);
    ctx.lineTo(fix.x2, fix.y2);
    ctx.strokeStyle = "#000";
    ctx.stroke();
  }
  setState(mouseE);
});

window.addEventListener("resize", function() {
  printCanvasLocation();
});

printCanvasLocation();
            .center {

              text-align: center;

            }

            canvas {

              background-color: lightblue;

            }
<main>
  <div class="center">
    <canvas id="canvas" width="128" height="128">If you can see me, you should update your browser</canvas>
  </div>
  <div id="logger" role="log">
    <span>State: </span><span id="mState">Unknown</span>
    <span>X: </span><span id="mX">Unknown</span>
    <span>Y: </span><span id="mY">Unknown</span>
    <span>Canvas X: </span><span id="cX">Unknown</span>
    <span>Canvas Y: </span><span id="cY">Unknown</span>
  </div>
</main>


你的 Canvas XCanvas Y 是反过来的,但除此之外回答很好!我认为这就可以了。 - Lawrence Douglas
@AndreyK 我不明白你的意思。如果你的意思是使用CSS或其他方式旋转了画布,我会假设你需要将相同的矩阵乘法应用于从鼠标事件中获取的坐标。 - zero298

0
一个解决方案是将画布的大小直接设置为窗口的大小,并随着窗口大小的变化而缩放。画布可以大部分透明。
我相信还有一种方法可以使鼠标事件等先经过画布,然后再传递到后面的元素,如果需要的话。(参见:“js事件冒泡和捕获”)。
但是这样你就可以完全控制并能够在任何地方绘制任何东西。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接