Lawrence Douglas
Lawrence Douglas

Reputation: 727

How to draw outside of an HTML canvas element?

I have an HTML canvas element and have implemented a brush that captures the mousedown, mousemove, and mouseup events of the canvas element. This all works fine for drawing on the canvas. However, I don't think I like how you can't continue a drawing if your mouse leaves the canvas mid stroke. It kind of just cuts it off. It's very unforgiving to the person and not very user-friendly in my opinion.

If you open up Microsoft Paint and begin drawing with the brush or ellipse or something, as long as you start within the canvas, you can drag your mouse anywhere on your screen and re-enter the canvas wherever. It also makes it easy, for example, to draw quarter-circles in corners because you can drag the ellipse tool off-screen. I hope this makes sense.

Anyways, I was wondering if there was a way to implement this with the HTML5 canvas or how I would go about implementing this sort of thing. The user would never have to actually seen anything drawn over there; it's mostly just going to be a feature for usability.

Edit: A problem with a lot of these solutions is how to handle coordinates. Currently my canvas is in the middle of the screen and the top left of the canvas is (0, 0) and the bottom right is (500, 500). The translation work of the coordinates has to be considered as well.

Edit2: I found out that apparently you can draw off the canvas bounds just fine. For example, you can supply negative widths, heights, and coordinates and the canvas element will handle it just fine. So basically the solution will likely involve just capturing the document's mousemove and mouseup and just translating the x and y to start at the canvas's top left corner.

Upvotes: 5

Views: 5857

Answers (3)

Andrew
Andrew

Reputation: 1

One solution would be to literally make the canvas the size of the window, and scale its size with it. The canvas can be mostly transparent.

I'm sure there's also a way to make mouse events and such go through the canvas first but then pass through to the elements behind, if that's desired. (See: "js events bubbling and capturing".)

But then you would have absolute control and be able to draw anything anywhere.

Upvotes: 0

zero298
zero298

Reputation: 26909

Here is an extremely rough first draft of how you can listen for mouse events on the window rather than the canvas to be able to draw continuously:

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>

Upvotes: 1

Asons
Asons

Reputation: 87251

Here is one way you can keep drawing when reenter the canvas:

  • Create a global variable and set that one to true on mousedown
  • Add a global event for mouseup so you can catch if someone do that outside the canvas, and if so, set global variable to false, and the canvas element's mouseup need of course also to set the same variable
  • On mousemove, check for global variable to be true before draw

To draw "outside" the canvas, like quarter-circles in a corner, I would move all events to the document level as global handler and catch the canvas element on click and pass its client coordinates to be computed with the document coordinates.

Upvotes: 2

Related Questions