Рисунок Javascript ImageData неправильно масштабируется
Идея программы состоит в том, чтобы иметь изображение карты и наложить черный холст на эту карту. Затем пользователь нажимает на некоторую часть холста, и, подобно инструменту аэрозольной краской, пиксели рядом с мышью становятся прозрачными на холсте. Таким образом, карта будет показана (как объект типа тумана войны). Когда я щелкаю рядом с верхним левым краем холста, аэрозольная краска работает вроде как и предполагалось, но когда я щелкаю дальше вправо и вниз по холсту, пиксели, которые становятся прозрачными, становятся намного правее и ниже... Любая идея, что здесь не так? Вот код:
// On document ready function.
$(document).ready(function() {
canvas = document.getElementById("myImageDisplayerCanvas");
drawingContext = canvas.getContext("2d");
drawingContext.fillStyle = "#000000";
drawingContext.fillRect(0, 0, 800, 554);
});
// Spray paint logic.
var _intervalId, // used to track the current interval ID
_center, // the current center to spray
density = 10,
radius = 10,
drawingCxt,
leftClickPressed,
drawingContext,
canvas;
function getRandomOffset() {
var randomAngle = Math.random() * 360;
var randomRadius = Math.random() * radius;
return {
x: Math.cos(randomAngle) * randomRadius,
y: Math.sin(randomAngle) * randomRadius
};
}
this.startDrawing = function(position) {
_center = position;
leftClickPressed = true;
// spray once every 10 milliseconds
_intervalId = setInterval(this.spray, 10);
};
this.moveDrawing = function(position) {
if (leftClickPressed) {
clearInterval(_intervalId);
_center = position;
// spray once every 10 milliseconds
_intervalId = setInterval(this.spray, 10);
}
}
this.finishDrawing = function(position) {
clearInterval(_intervalId);
leftClickPressed = false;
};
this.spray = function() {
var centerX = parseInt(_center.offsetX),
centerY =
parseInt(_center.offsetY),
i;
for (i = 0; i < density; i++) {
var offset = getRandomOffset();
var x = centerX + parseInt(offset.x) - 1,
y = centerY +
parseInt(offset.y) - 1;
var dy = y * 800 * 4;
var pos = dy + x * 4;
var imageData = drawingContext.getImageData(0, 0, 800, 554);
imageData.data[pos++] = 0;
imageData.data[pos++] = 0;
imageData.data[pos++] = 0;
imageData.data[pos++] = 0;
drawingContext.putImageData(imageData, 0, 0);
}
};
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<div id="myImageDisplayerDiv" style="position:relative; width:800px; height:554px">
<img src="~/images/RedLarch.jpg" style="width:800px; height:554px; top: 0; left: 0; position: absolute; z-index: 0" />
<canvas id="myImageDisplayerCanvas" onmousedown="startDrawing(event)" onmousemove="moveDrawing(event)" onmouseup="finishDrawing(event)" style="width:800px; height:554px; top: 0; left: 0; position: absolute; z-index: 1; opacity: 1; fill: black" />
</div>
1 ответ
Установите разрешение холста.
Причина, по которой брызги смещены, заключается в том, что вы не установили разрешение холста. Вы устанавливаете разрешение через свойства ширины и высоты элемента canvas.
<canvas id="canvas" width="800" height="554"></canvas>
Установка ширины и высоты стиля устанавливает размер отображения холста, а не разрешение. Если вы не установите разрешение холста, по умолчанию будет 300 на 150.
Было много других проблем с вашим кодом.
Получение целых данных изображения холста только для установки одного пикселя - это слишком много. Просто создайте однопиксельный объект imageData и разместите этот пиксель там, где это необходимо
использование requestAnimationFrame
для синхронизации с дисплеем, никогда не используйте setInterval
или же setTimeout
так как они не синхронизируются с оборудованием дисплея и вызовут у вас бесконечные проблемы.
Создайте одну функцию события мыши, чтобы обрабатывать все события мыши и просто записывать состояние мыши. Используйте основной цикл, вызываемый requestAnimationFrame, для обработки рисунка, а не рисования из события мыши.
Вам не нужно определять функции с this.functionName = function(){}
если функция не является частью объекта.
Функция триггера использует радианы, а не градусы. 360 градусов в радианах - это 2 * Math.PI
Ниже приведен быстрый переписать ваш код, используя приведенные выше примечания.
const ctx = canvas.getContext("2d");
requestAnimationFrame(mainLoop); // start main loop when code below has run
ctx.fillStyle = "#000000";
ctx.fillRect(0, 0, 800, 554);
// create a pixel buffer for one pixel
const imageData = ctx.getImageData(0, 0, 1, 1);
const pixel32 = new Uint32Array(imageData.data.buffer);
pixel32[0] = 0;
// Spray paint logic.
const density = 10;
const radius = 10;
// mouse handler
const mouse = {x : 0, y : 0, button : false};
function mouseEvents(e){
const bounds = canvas.getBoundingClientRect();
mouse.x = e.pageX - bounds.left - scrollX;
mouse.y = e.pageY - bounds.top - scrollY;
mouse.button = e.type === "mousedown" ? true : e.type === "mouseup" ? false : mouse.button;
}
["down","up","move"].forEach(name => document.addEventListener("mouse"+name,mouseEvents));
function getRandomOffset() {
const angle = Math.random() * Math.PI * 2;
const rad = Math.random() * radius;
return { x: Math.cos(angle) * rad, y: Math.sin(angle) * rad};
}
function spray(pos) {
var i;
for (i = 0; i < density; i++) {
const offset = getRandomOffset();
ctx.putImageData(imageData, pos.x + offset.x, pos.y + offset.y);
}
}
// main loop called 60 times a second
function mainLoop(){
if (mouse.button) { spray(mouse) }
requestAnimationFrame(mainLoop);
}
<canvas id="canvas" width="800" height="554"></canvas>