Как рисовать вне элемента холста HTML?

У меня есть элемент холста HTML и реализована кисть, которая фиксирует события mousedown, mousemove и mouseup элемента холста. Все это отлично подходит для рисования на холсте. Однако я не думаю, что мне нравится, что вы не можете продолжить рисование, если ваша мышь покидает холст на середине хода. Это как бы просто отсекает его. На мой взгляд, это очень неумолимо для человека и не очень удобно для пользователя.

Если вы откроете Microsoft Paint и начнете рисовать кистью, эллипсом или чем-то еще, пока вы начинаете с холста, вы можете перетащить указатель мыши в любое место на экране и повторно войти в холст в любом месте. Это также упрощает, например, рисование четверти круга в углах, потому что вы можете перетащить инструмент эллипса за пределы экрана. Я надеюсь в этом есть смысл.

В любом случае, мне было интересно, есть ли способ реализовать это с помощью холста HTML5 или как я буду реализовывать такие вещи. Пользователь никогда бы не увидел ничего нарисованного там; в основном это просто функция для удобства использования.

Изменить: проблема со многими из этих решений заключается в том, как обрабатывать координаты. В настоящее время мой холст находится в середине экрана, верхний левый угол - (0, 0), а нижний правый - (500, 500). Также следует учитывать перевод координат.

Edit2: Я обнаружил, что, по-видимому, вы можете просто выйти за пределы холста. Например, вы можете указать отрицательные значения ширины, высоты и координат, и элемент холста справится с этим без проблем. Таким образом, в основном решение, вероятно, будет включать в себя просто захват документов mousemove и mouseup и простой перевод x и y для начала в верхнем левом углу холста.


person Lawrence Douglas    schedule 22.09.2015    source источник
comment
Не могли бы вы захватить события мыши в самом окне, а затем передать их на холст?   -  person zero298    schedule 22.09.2015
comment
Возможно, попробуйте опубликовать пример того, что у вас есть на данный момент. Будет легче помочь, если нам не придется сначала воссоздавать наш собственный «сломанный» пример.   -  person duncanhall    schedule 22.09.2015
comment
Может быть, вы могли бы также сделать элемент холста во всю ширину и высоту окна, а область рисования несколько меньше внутри холста?   -  person Aidas Bendoraitis    schedule 22.09.2015
comment
@AidasBendoraitis: Это тоже неплохая идея. Позвольте мне попробовать это по-быстрому.   -  person Lawrence Douglas    schedule 22.09.2015
comment
@ zero298: Не могли бы вы показать, как это сделать?   -  person Lawrence Douglas    schedule 22.09.2015


Ответы (3)


Вот очень грубый первый набросок того, как вы можете отслеживать события мыши в окне, а не на холсте, чтобы иметь возможность рисовать непрерывно:

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>

person zero298    schedule 22.09.2015
comment
Ваши Canvas X и Canvas Y перевернуты, но в остальном отличный ответ! Я думаю, это сработает. - person Lawrence Douglas; 22.09.2015
comment
@AndreyK Я ​​не понимаю, о чем вы. Если вы имеете в виду, что вы повернули холст с помощью CSS или чего-то еще, я бы предположил, что вам нужно будет применить такое же матричное умножение к координатам, которые вы получаете из события мыши. - person zero298; 06.01.2020

Вот один из способов продолжить рисование при повторном входе на холст:

  • Создайте глобальную переменную и установите для нее значение true в mousedown
  • Добавьте глобальное событие для mouseup, чтобы вы могли поймать, если кто-то делает это за пределами холста, и если это так, установите глобальную переменную в false, и mouseup элемента холста, конечно, также необходимо установить ту же переменную
  • При перемещении мыши перед отрисовкой проверьте, чтобы глобальная переменная была истинной.

Чтобы рисовать «за пределами» холста, как четверть круга в углу, я бы переместил все события на уровень документа в качестве глобального обработчика, поймал бы элемент холста при щелчке и передал бы его клиентские координаты для вычисления с координатами документа.

person Ason    schedule 22.09.2015

Одним из решений было бы буквально сделать холст размером с окно и масштабировать его размер вместе с ним. Холст может быть в основном прозрачным.

Я уверен, что есть способ сделать так, чтобы события мыши и тому подобное сначала проходили через холст, а затем при желании передавались элементам позади. (См. «Всплытие и захват событий js».)

Но тогда у вас будет полный контроль и вы сможете рисовать что угодно где угодно.

person Andrew    schedule 21.11.2019