多个画布层和mousemove

时间:2017-07-05 10:51:47

标签: javascript jquery d3.js canvas mousemove

我正在尝试在画布上使用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中的画布?

enter image description here

EDIT2: 在回复评论时,bl.ocks提供了一个简化且可验证的示例。 原始代码可用here(参见示例文件brushing.html)。问题也在相应的issue board上进行了讨论。

1 个答案:

答案 0 :(得分:0)

Multy分层画布鼠标事件

最好的方式是最简单的方法。只创建一个侦听文档鼠标事件的鼠标处理程序。然后跟踪活动层并适当地处理它。

该片段取自另一个答案,并适合在多个图层上创建线条。单击图层将其选中,然后单击拖动以添加线条。每个图层都有自己的颜色,需要选择它来修改它。

只有一个鼠标处理程序可以侦听文档鼠标事件,重置是在渲染代码中完成的。

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>