有一张图片...我正在尝试悬停...逻辑是这样的:
- 加载图像后,使用辅助画布,我得到图像的像素数组(左侧)
- 在画布上移动鼠标时,我确定它在图片上方的位置
- 之后,根据像素数组(第1项),我判断鼠标下的像素是否透明——如果不是,画一个悬停(图片右侧)
它似乎工作......,代码如下。
但是,当试图使画布适应屏幕大小(以及相应的图像)时,问题就出现了......我的悬停实现停止工作......变量scale_X和负责比例scale_Y,在代码中标度定义下方被注释掉。
谁能帮助解决问题?
const WIDTH = 800;
const HEIGHT = 374;
let scale_X = 1; // масштаб
let scale_Y = 1; // масштаб
let ctxStyles;
let hover;
const canvas = document.getElementById('canvas');
//const help = document.getElementById('helper');
const help = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const helper = help.getContext('2d');
const real_width = document.documentElement.clientWidth;
const my_width = (real_width / 100) * 70;
const my_height = my_width / 2.14;
//scale_X = my_width / WIDTH;
//scale_Y = my_height / HEIGHT;
canvas.width = my_width;
canvas.height = my_height;
help.width = my_width;
help.height = my_height;
ctxStyles = canvas.getBoundingClientRect();
canvas.addEventListener('mousemove', handlerMousemove);
const img = new Image();
//img.src = 'https://isstatic.askoverflow.dev/R5QJI.gif';
img.crossOrigin = "anonymous";
img.src = 'https://i.ibb.co/pfnXvqk/barraks-4-2.gif';
img.onload = function() {
img.coords = {
x: 40,
y: 30
};
getPixArr(img);
drawImgOnCanvas(img);
};
function drawImgOnCanvas(img) {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
const arr = [
img,
0,
0,
img.width / 2,
img.height,
img.coords.x * scale_X,
img.coords.y * scale_Y,
(img.width / 2) * scale_X,
img.height * scale_Y
];
ctx.drawImage(...arr);
// отрисовывает 2-ю часть картинки, на которой нарисован контур
if (hover) {
arr[1] = img.width / 2;
arr[3] = img.width;
arr[5] = img.coords.x * scale_X - 1;
arr[7] = img.width * scale_X;
ctx.drawImage(...arr);
}
}
function handlerMousemove(event) {
const mouseX = event.clientX - ctxStyles.left;
const mouseY = event.clientY - ctxStyles.top;
hover = false;
if (check_Mouse_On_Img(mouseX, mouseY, img)) {
const notTransparent = check_not_transparent_pixel(mouseX, mouseY, img);
if (notTransparent) {
hover = true;
drawImgOnCanvas(img);
} else {
drawImgOnCanvas(img);
}
}
}
function getPixArr(img) {
const ctx = helper;
const imgWidth = img.width;
const imgHeight = img.height;
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.drawImage(
img,
0,
0,
imgWidth / 2,
imgHeight,
0,
0,
(imgWidth / 2) * scale_X,
imgHeight * scale_Y
);
const pixArr = ctx.getImageData(
0,
0,
(imgWidth / 2) * scale_X,
imgHeight * scale_Y
);
img.pixArr = pixArr;
}
function check_Mouse_On_Img(mouseX, mouseY, img) {
// const ctx = helper
const topX = img.coords.x * scale_X;
const topY = img.coords.y * scale_Y;
const leftX = topX;
const leftY = topY + img.pixArr.height;
const rigthX = topX + img.pixArr.width;
const rigthY = topY;
const bottomX = rigthX;
const bottomY = leftY;
ctx.beginPath();
// ctx.strokeStyle = 'red';
ctx.strokeStyle = "transparent";
ctx.moveTo(leftX, leftY);
ctx.lineTo(topX, topY);
ctx.lineTo(rigthX, rigthY);
ctx.lineTo(bottomX, bottomY);
ctx.lineTo(leftX, leftY);
ctx.stroke();
ctx.closePath();
return ctx.isPointInPath(mouseX, mouseY);
}
//определяет прозрачный ли пиксель
function check_not_transparent_pixel(clientX, clientY, img) {
const imgX = img.coords.x;
const imgY = img.coords.y;
// const imgX = img.coords.x * scale_X;
// const imgY = img.coords.y * scale_Y;
const mouseX = clientX - imgX; //т.к. массив пикселей построен из начальных координат 0:0, делаю сдвиг
const mouseY = clientY - imgY;
const pixArr = img.pixArr;
const index = Math.floor(get_Pix_Index(mouseX, mouseY, pixArr));
let alpha = pixArr.data[index + 3]; //прозрачность
if (index > pixArr.data.length) {
return false;
}
if (alpha > 0) {
return true;
}
return false;
//определяет индекс пикселя в массиве пикселей
function get_Pix_Index(mouseX, mouseY, pixArr) {
let pixel;
if (mouseX == 0 && mouseY > 0) {
pixel = (pixArr.width * scale_X) * mouseY + 1;
} else if (mouseY == 0 && mouseX > 0) {
pixel = mouseX;
} else {
pixel = mouseY * (pixArr.width* scale_X) + mouseX - 1;
}
let index = pixel * 4;
return index;
}
}
// просто для тестов
canvas.addEventListener('click', event => {
const mouseX = event.clientX - ctxStyles.left;
const mouseY = event.clientY - ctxStyles.top;
console.log(mouseX, mouseY);
ctx.beginPath();
ctx.arc(mouseX, mouseY, 2, 0, 2 * Math.PI);
ctx.fill();
ctx.closePath();
ctx.putImageData(img.pixArr, mouseX, mouseY);
});
body{
height: 100vh;
width: 100vw;
overflow: hidden;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
canvas{
border: 1px solid;
}
<canvas id="canvas" ></canvas>
<canvas id="helper" style="position: fixed; left: -300%"></canvas>
PS...还有一个问题:如果使用属性隐藏辅助画布,则图像的透明像素被定义为不透明,hidden或者display: none- 为什么?

这是旧的方式,我称之为“峰值缓冲区”,本质是在一个附加的图片中,所有的对象都被绘制成独特的颜色。
在应用程序开始时,我制作了重复的精灵,用每个精灵唯一的颜色(标识符颜色)替换其中所有不完全透明的像素,并将它们与隐藏画布上的主画布平行绘制,当移动时,鼠标,我取颜色并确定鼠标下方的内容。
该示例适用于悬停和拖动
PS:使用有符号距离场在精灵的不透明区域添加程序描边,连线到纹理中
UPD:通过使用 3 个颜色通道进行拾取,大大降低了出错的可能性并扩大了可能的对象数量
UPD2:为清晰起见,将元素的形状复杂化
相关答案:https ://ru.stackoverflow.com/a/962780/188366
解决了问题...正如我在评论中建议的那样,问题在于确定像素索引。但是,事实证明,需要舍入的不是收到的索引,而是鼠标坐标