我正在尝试在画布上使用mousemove事件,灵感来自Wayne的答案here以及相应的fiddle。
问题是,我正在使用code以下列方式创建“分层”画布:
// canvas data layers
["marks", "foreground", "brushed", "highlight", "clickable_colors"].forEach(function(layer, i) {
canvas[layer] = selection
.append("canvas")
.attr({
id: layer, //added an id for easier selecting for mouse event
class: layer,
style: "z-index: " + i +10000000
})[0][0];
ctx[layer] = canvas[layer].getContext("2d");
});
我的目标是获取图层“clickable_colors”上的颜色,因此我调整了小提琴的脚本以在该图层上设置mousemove事件:
var my_clickable_canvas = document.getElementById('clickable_colors');
var context = my_clickable_canvas.getContext('2d');
context.fillStyle = "rgb(255,0,0)";
context.fillRect(0, 0, 50, 50);
context.fillStyle = "rgb(0,0,255)";
context.fillRect(55, 0, 50, 50);
$("#clickable_colors").mousemove(function(e) {
debugger;
var pos = findPos(this);
var x = e.pageX - pos.x;
console.log(x)
var y = e.pageY - pos.y;
var coord = "x=" + x + ", y=" + y;
var c = this.getContext('2d');
var p = c.getImageData(x, y, 1, 1).data;
var hex = "#" + ("000000" + rgbToHex(p[0], p[1], p[2])).slice(-6);
$('#examplecanvas').html(coord + "<br>" + hex);
console.log("hi")
});
但是,根本不会触发mousemove事件。我首先假设它与z-index有关,但给它最高值以确保它在顶部没有解决它。
有谁知道可能阻止事件发生的原因?或者我应该注意什么,鉴于画布的“分层”配方?任何帮助表示赞赏!
编辑: 我发现的另一个解释是“分层”画布属于父div元素,而工作小提琴直接在体内提供画布。这可能会阻止鼠标事件的发生吗?如果是这样,如何应对div中的画布?
EDIT2:
在回复评论时,bl.ocks提供了一个简化且可验证的示例。
原始代码可用here(参见示例文件brushing.html
)。问题也在相应的issue board上进行了讨论。
答案 0 :(得分:0)
最好的方式是最简单的方法。只创建一个侦听文档鼠标事件的鼠标处理程序。然后跟踪活动层并适当地处理它。
该片段取自另一个答案,并适合在多个图层上创建线条。单击图层将其选中,然后单击拖动以添加线条。每个图层都有自己的颜色,需要选择它来修改它。
只有一个鼠标处理程序可以侦听文档鼠标事件,重置是在渲染代码中完成的。
const ctx1 = canvas1.getContext("2d");
const ctx2 = canvas2.getContext("2d");
const ctx3 = canvas3.getContext("2d");
const ctx4 = canvas4.getContext("2d");
const Point2 = (x,y) => ({x,y}); // creates a point
const Line = (p1,p2) => ({p1,p2});
const setStyle = (style,ctx) => eachOf(Object.keys(style), key => { ctx[key] = style[key] } );
const eachOf = (array, callback) => {var i = 0; while (i < array.length && callback(array[i],i ++) !== true ); };
const list = {
items : null,
add(item) { this.items.push(item); return item },
eachItem(callback) {
var i = 0;
while(i < this.items.length){
callback(this.items[i],i++);
}
}
}
function createList(extend){
return Object.assign({},list,{items : []},extend);
}
// this will extend the points list
function getClosestPoint(from ,minDist) {
var closestPoint;
this.eachItem(point => {
const dist = Math.hypot(from.x - point.x, from.y - point.y);
if(dist < minDist){
closestPoint = point;
minDist = dist;
}
});
return closestPoint;
}
function distanceLineFromPoint(line,point,points){
const lx = points.items[line.p1].x;
const ly = points.items[line.p1].y;
const v1x = points.items[line.p2].x - lx;
const v1y = points.items[line.p2].y - ly;
const v2x = point.x - lx;
const v2y = point.y - ly;
// get unit dist of closest point
const u = (v2x * v1x + v2y * v1y)/(v1y * v1y + v1x * v1x);
if(u >= 0 && u <= 1){ // is the point on the line
return Math.hypot(lx + v1x * u - point.x, ly + v1y * u - point.y);
} else if ( u < 0 ) { // point is before start
return Math.hypot(lx - point.x, ly - point.y);
}
// point is after end of line
return Math.hypot(points.items[line.p2].x - point.x, points.items[line.p2].y - point.y);
}
// this will extend the lines list
function getClosestline(from ,minDist) {
var closestLine;
this.eachItem(line => {
const dist = distanceLineFromPoint(line,from,this.points);
if(dist < minDist){
closestLine = line;
minDist = dist;
}
});
return closestLine;
}
function drawPoint(point,ctx){
ctx.moveTo(point.x,point.y);
ctx.rect(point.x - 2,point.y - 2, 4,4);
}
function drawLine(line,ctx,points){
ctx.moveTo(points.items[line.p1].x,points.items[line.p1].y);
ctx.lineTo(points.items[line.p2].x,points.items[line.p2].y);
}
function drawLines(ctx){ this.eachItem(line => drawLine(line,ctx,this.points)) }
function drawPoints(ctx){this.eachItem(point => drawPoint(point,ctx)) }
const mouse = {x : 0, y : 0, button : false, drag : false, dragStart : false, dragEnd : false, dragStartX : 0, dragStartY : 0}
function mouseEvents(e){
mouse.x = e.pageX;
mouse.y = e.pageY;
const lb = mouse.button;
mouse.button = e.type === "mousedown" ? true : e.type === "mouseup" ? false : mouse.button;
if(lb !== mouse.button){
if(mouse.button){
mouse.drag = true;
mouse.dragStart = true;
mouse.dragStartX = mouse.x;
mouse.dragStartY = mouse.y;
}else{
mouse.drag = false;
mouse.dragEnd = true;
}
}
}
["down","up","move"].forEach(name => document.addEventListener("mouse" + name, mouseEvents));
// short cut vars
var w;
var h;
var cw; // center
var ch;
var globalTime;
var closestLine;
var closestPoint;
var pointDrag; // true is dragging a point else dragging a line
var dragOffsetX;
var dragOffsetY;
var cursor;
var toolTip;
var helpCount = 0;
const minDist = 20;
var points ;
var lines;
const layers = [{
lineStyle : { lineWidth : 2, strokeStyle : "green", fillStyle : "Green" },
pointStyle : { lineWidth : 1, strokeStyle : "green", fillStyle : "Green"},
font : { fillStyle : "green", font : "18px arial", textAlign : "left"},
context : ctx1,
canvas : canvas1,
points :(points = createList({ getClosest : getClosestPoint,draw : drawPoints})),
lines : createList({getClosest : getClosestline, draw : drawLines, points : points }),
ready : false,
},{
lineStyle : { lineWidth : 2, strokeStyle : "blue", fillStyle : "blue" },
pointStyle : { lineWidth : 1, strokeStyle : "blue", fillStyle : "blue"},
font : { fillStyle : "blue", font : "18px arial", textAlign : "left"},
context : ctx2,
canvas : canvas2,
points :(points = createList({ getClosest : getClosestPoint,draw : drawPoints})),
lines : createList({getClosest : getClosestline, draw : drawLines, points : points}),
ready : false,
},{
lineStyle : { lineWidth : 2, strokeStyle : "gold", fillStyle : "gold" },
pointStyle : { lineWidth : 1, strokeStyle : "gold", fillStyle : "gold"},
font : { fillStyle : "gold", font : "18px arial", textAlign : "left"},
context : ctx3,
canvas : canvas3,
points :(points = createList({ getClosest : getClosestPoint,draw : drawPoints})),
lines : createList({getClosest : getClosestline, draw : drawLines, points : points}),
ready : false,
},{
lineStyle : { lineWidth : 2, strokeStyle : "aqua", fillStyle : "aqua" },
pointStyle : { lineWidth : 1, strokeStyle : "aqua", fillStyle : "aqua"},
font : { fillStyle : "aqua", font : "18px arial", textAlign : "left"},
context : ctx4,
canvas : canvas4,
points : (points = createList({ getClosest : getClosestPoint,draw : drawPoints})),
lines : createList({getClosest : getClosestline, draw : drawLines, points : points}),
ready : false,
}
];
var currentLayer = 0;
const highlightStyle = {
lineWidth : 3,
strokeStyle : "red",
}
const font = {
font : "18px arial",
fillStyle : "black",
textAlign : "center",
}
// main update function
function update(timer){
if(mouse.button){
if(mouse.x < 50 && mouse.y < 28 *5){
mouse.drag=mouse.button=mouse.dragStart = false;
currentLayer = (mouse.y / 28)|0;
eachOf(layers,layer=>layer.ready = false);
}
}
const layer = layers[currentLayer];
const ctx = layer.context;
const canvas = layer.canvas;
const lines = layer.lines;
const points = layer.points;
const lineStyle = layer.lineStyle;
const pointStyle = layer.pointStyle;
cursor = "crosshair";
toolTip = helpCount < 2 ? "Click drag to create a line" : "";
globalTime = timer;
ctx.setTransform(1,0,0,1,0,0); // reset transform
ctx.globalAlpha = 1; // reset alpha
if(w !== innerWidth || h !== innerHeight){
cw = (w = canvas1.width = innerWidth) / 2;
ch = (h = canvas1.height = innerHeight) / 2;
canvas2.height = canvas3.height = canvas4.height = h;
canvas2.width = canvas3.width = canvas4.width = w;
eachOf(layers,layer=>layer.ready = false);
}else{
ctx.clearRect(0,0,w,h);
}
if(mouse.drag=== false){
if(mouse.x < 50 && mouse.y < 28 *5){
cursor = "pointer";
toolTip = "Click to select layer";
closestPoint = closestLine = undefined;
}else{
closestLine = undefined;
closestPoint = points.getClosest(mouse,minDist);
if(closestPoint === undefined){
closestLine = lines.getClosest(mouse,minDist);
}
if(closestPoint || closestLine){
toolTip = "Click drag to move " + (closestPoint ? "point" : "line");
cursor = "move";
}
}
}
if(mouse.dragStart){
if(closestPoint){
dragOffsetX = closestPoint.x - mouse.x;
dragOffsetY = closestPoint.y - mouse.y;
pointDrag = true;
}else if( closestLine){
dragOffsetX = points.items[closestLine.p1].x - mouse.x;
dragOffsetY = points.items[closestLine.p1].y - mouse.y;
pointDrag = false;
} else {
points.add(Point2(mouse.x,mouse.y));
closestPoint = points.add(Point2(mouse.x,mouse.y));
closestLine = lines.add(Line(points.items.length-2,points.items.length-1));
dragOffsetX = 0;
dragOffsetY = 0;
pointDrag = true;
helpCount += 1;
}
mouse.dragStart = false;
}else if(mouse.drag){
cursor = 'none';
if(pointDrag){
closestPoint.x = mouse.x + dragOffsetX;
closestPoint.y = mouse.y + dragOffsetY;
}else{
const dx = mouse.x- mouse.dragStartX;
const dy = mouse.y -mouse.dragStartY;
mouse.dragStartX = mouse.x;
mouse.dragStartY = mouse.y;
points.items[closestLine.p1].x += dx;
points.items[closestLine.p1].y += dy;
points.items[closestLine.p2].x += dx;
points.items[closestLine.p2].y += dy;
}
}else{
}
// draw all points and lines
setStyle(lineStyle,ctx);
ctx.beginPath();
lines.draw(ctx);
ctx.stroke();
setStyle(pointStyle,ctx);
ctx.beginPath();
points.draw(ctx);
ctx.stroke();
// draw highlighted point or line
setStyle(highlightStyle,ctx);
ctx.beginPath();
if(closestLine){ drawLine(closestLine,ctx,points) }
if(closestPoint){ drawPoint(closestPoint, ctx) }
ctx.stroke();
eachOf(layers,(layer,i)=>{
if(!layer.ready){
const ctx = layer.context;
ctx.globalAlpha = 0.75;
ctx.clearRect(0,0,w,h);
setStyle(layer.lineStyle,ctx);
ctx.beginPath();
layer.lines.draw(ctx);
ctx.stroke();
setStyle(layer.pointStyle,ctx);
ctx.beginPath();
layer.points.draw(ctx);
ctx.stroke();
setStyle(layer.font,ctx);
ctx.fillText("Layer " + (i + 1), 10, i * 28+28);
layer.ready = true;
ctx.globalAlpha = 1;
}
});
setStyle(layer.font,ctx);
ctx.fillText("Layer On" , 10,currentLayer * 28+28);
if(helpCount < 2){
setStyle(font,ctx);
ctx.fillText(toolTip,cw,30);
}
canvas4.style.cursor = cursor;
if(helpCount < 5){
canvas4.title = toolTip;
}else{
canvas4.title = "Click layer to select it";
}
requestAnimationFrame(update);
}
requestAnimationFrame(update);
canvas {
position : absolute;
top : 0px;
left : 0px;
}
<canvas id="canvas1"></canvas>
<canvas id="canvas2"></canvas>
<canvas id="canvas3"></canvas>
<canvas id="canvas4"></canvas>