29.06.2022, 21:36
|
Аспирант
|
|
Регистрация: 18.01.2011
Сообщений: 93
|
|
Нужна помощь с несложной рисовалкой в canvas
Привет! Нашел простенький скрипт, который рисует в canvas мышкой или на сенсорных устройствах пальцем. Но есть проблема - не могу управлять шириной поля canvas. Мне нужно, чтобы оно было адаптивным, занимало всю ширину родителя, бутстраповскую колонку col-12 col-lg-6, но если ему задать ширину 100%, то линии рисунка остают от курсора (или пальца), а если ширина указана 320px - то все четко, линия прямо под курсором, как если карандашом пишешь. Вот кодинг
var canvas = document.getElementById('canvas');
var startedPainting = false;
canvas.addEventListener("mousedown", handleStart, false);
canvas.addEventListener("mousemove", handleMove, false);
canvas.addEventListener("mouseup", handleEnd, false);
canvas.addEventListener("touchstart", handleStart, false);
canvas.addEventListener("touchmove", handleMove, false);
canvas.addEventListener("touchend", handleEnd, false);
function handleStart(evt) {
evt.preventDefault();
startedPainting = true;
var el = $("#canvas").get(0);
var ctx = el.getContext("2d");
ctx.lineWidth = 3;
var touches = evt.changedTouches;
if (touches) {
ctx.moveTo(touches[0].pageX - el.offsetLeft, touches[0].pageY - el.offsetTop);
for (var i = 0; i < touches.length; i++) {
ctx.lineTo(touches[i].pageX - el.offsetLeft, touches[i].pageY - el.offsetTop);
}
} else {
ctx.moveTo(evt.pageX - el.offsetLeft, evt.pageY - el.offsetTop);
ctx.lineTo(evt.pageX - el.offsetLeft, evt.pageY - el.offsetTop);
}
ctx.stroke();
}
function handleMove(evt) {
evt.preventDefault();
if (startedPainting) {
var el = $("#canvas").get(0);
var ctx = el.getContext("2d");
ctx.lineWidth = 3;
var touches = evt.changedTouches;
if (touches) {
for (var i = 0; i < touches.length; i++) {
ctx.lineTo(touches[i].pageX - el.offsetLeft, touches[i].pageY - el.offsetTop);
}
} else {
ctx.lineTo(evt.pageX - el.offsetLeft, evt.pageY - el.offsetTop);
}
ctx.stroke();
}
}
function handleEnd(evt) {
evt.preventDefault();
startedPainting = false;
var el = $("#canvas").get(0);
var ctx = el.getContext("2d");
ctx.lineWidth = 3;
var touches = evt.changedTouches;
if (touches) {
for (var i = 0; i < touches.length; i++) {
ctx.lineTo(touches[i].pageX - el.offsetLeft, touches[i].pageY - el.offsetTop);
}
} else {
ctx.lineTo(evt.pageX - el.offsetLeft, evt.pageY - el.offsetTop);
}
ctx.stroke();
}
подозреваю, что вся магия отступов кроется в строчках типа этой
el.offsetLeft, touches[0].pageY - el.offsetTop
но как ни старался там что-то менять, у меня ничего не получилось.
Подскажите что поправить?
Заранее благодарю!
|
|
29.06.2022, 23:04
|
Профессор
|
|
Регистрация: 04.12.2012
Сообщений: 3,800
|
|
После 15-й строки добавьте это:
el.width = el.offsetWidth;
el.height = el.offsetHeight;
|
|
30.06.2022, 08:47
|
Аспирант
|
|
Регистрация: 18.01.2011
Сообщений: 93
|
|
Nexus,
Спасибо! Работает как надо, но появилась вторая проблема - если что то нарисовать, а потом кликнуть в canvas, то все что нарисовал ранее стирается. Попробовал эти строчки добавить во все функции, но тогда вообще ничего не работает.
|
|
30.06.2022, 08:55
|
|
Профессор
|
|
Регистрация: 03.02.2020
Сообщений: 2,754
|
|
Сообщение от Volonter
|
Привет! Нашел простенький скрипт, который рисует в canvas мышкой или на сенсорных устройствах пальцем.
|
На моем сенсорном устройстве он пальцем рисовать не будет!
Нет у меня touchstart, touchmove и прочих таучей
Но есть pointerevents. Pointerevents есть везде. И на десктопах, и на смартфонах, и на планшетах, и даже на моем ноуте с сенсорным экраном.
Вот их и надо использовать. Вместо мышиных событий и таучей.
|
|
30.06.2022, 08:58
|
Аспирант
|
|
Регистрация: 18.01.2011
Сообщений: 93
|
|
Сообщение от voraa
|
Вот их и надо использовать. Вместо мышиных событий и таучей.
|
Понял, спасибо за наводку! Попробую!
|
|
30.06.2022, 10:49
|
Аспирант
|
|
Регистрация: 18.01.2011
Сообщений: 93
|
|
Сообщение от voraa
|
На моем сенсорном устройстве он пальцем рисовать не будет!
Нет у меня touchstart, touchmove и прочих таучей
Но есть pointerevents. Pointerevents есть везде. И на десктопах, и на смартфонах, и на планшетах, и даже на моем ноуте с сенсорным экраном.
Вот их и надо использовать. Вместо мышиных событий и таучей.
|
Попробовал заменить это
canvas.addEventListener("mousedown", handleStart, false);
canvas.addEventListener("mousemove", handleMove, false);
canvas.addEventListener("mouseup", handleEnd, false);
canvas.addEventListener("touchstart", handleStart, false);
canvas.addEventListener("touchmove", handleMove, false);
canvas.addEventListener("touchend", handleEnd, false);
на это
canvas.addEventListener("pointerdown", handleStart, false);
canvas.addEventListener("pointermove", handleMove, false);
canvas.addEventListener("pointerup", handleEnd, false);
может, что то делаю не так, но как я понял pointerdown заменяет сразу и mousedown, и touchstart, с другими событиями по аналогии. Но на десктопах работает, на сенсорных нет.
Да это и не суть, мне больше важнее разобраться с основным вопросом, с шириной canvas то есть))
|
|
30.06.2022, 11:34
|
Профессор
|
|
Регистрация: 04.12.2012
Сообщений: 3,800
|
|
Volonter,
https://codesandbox.io/s/musing-fog-...=/src/index.js
function resizeCanvas(ctx) {
var canvas = ctx.canvas;
var img = ctx.getImageData(0, 0, canvas.width, canvas.height);
canvas.width = canvas.offsetWidth;
canvas.height = canvas.offsetHeight;
ctx.putImageData(img, 0, 0);
}
function handleStart(evt) {
evt.preventDefault();
startedPainting = true;
var el = $("#canvas").get(0);
var needToResize =
el.width !== el.offsetWidth || el.height !== el.offsetHeight;
var ctx = el.getContext("2d");
if (needToResize) {
resizeCanvas(ctx);
}
ctx.lineWidth = 3;
var touches = evt.changedTouches;
if (touches) {
ctx.moveTo(
touches[0].pageX - el.offsetLeft,
touches[0].pageY - el.offsetTop
);
for (var i = 0; i < touches.length; i++) {
ctx.lineTo(
touches[i].pageX - el.offsetLeft,
touches[i].pageY - el.offsetTop
);
}
} else {
ctx.moveTo(evt.pageX - el.offsetLeft, evt.pageY - el.offsetTop);
ctx.lineTo(evt.pageX - el.offsetLeft, evt.pageY - el.offsetTop);
}
ctx.stroke();
}
При изменении свойств размеров холста он очищается. Чтобы избежать потери данных я попытался сохранить всё намалеванное с помощью метода getImagaData до изменений размера и отрисовать эти данные после.
Получилось, НО при изменении уменьшении ширины холста данные, которые были у правого края будут утеряны.
Как это пофиксить - я не знаю, возможно вам подскажет кто-нибудь другой, либо вы сами решите эту проблему.
touches я не трогал.
|
|
30.06.2022, 11:55
|
Профессор
|
|
Регистрация: 04.12.2012
Сообщений: 3,800
|
|
Изменил код по ссылке, привязал ресайз холста к событию resize окна, и добавил функцию throttle, чтобы ресайз происходил не чаще 10 раз в секунду.
Реализацию функции throttle взял отсюда: https://learn.javascript.ru/task/throttle
Последний раз редактировалось Nexus, 30.06.2022 в 17:41.
Причина: test
|
|
30.06.2022, 12:01
|
Профессор
|
|
Регистрация: 04.12.2012
Сообщений: 3,800
|
|
А вообще, реализацию нужного для вас скрипта можете поискать на этом форуме. Сверху справа есть ссылка "поиск", нажмите на нее и ищите слово "рисовалка".
Не смог отредактировать прошлое сообщение, поэтому создал новое.
|
|
30.06.2022, 13:26
|
|
Профессор
|
|
Регистрация: 27.05.2010
Сообщений: 33,134
|
|
Nexus,
возможно "потерялась" https: в ссылке на редактирование, добавить вручную.
|
|
|
|