多个画布层和鼠标移动 [英] Multiple canvas layers and mousemove

查看:129
本文介绍了多个画布层和鼠标移动的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在画布上使用mousemove事件,灵感来自Wayne的回答小提琴.

I'm trying to use a mousemove event on a canvas, inspired from Wayne's answer here, and the corresponding fiddle.

问题是,我正在使用代码通过以下方式创建分层"画布:

The problem is, I'm working with a code which creates "layered" canvas in the following way:

  // 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事件:

My goal is to get the color which is on the layer "clickable_colors", and so I adapted the fiddle's script to set the mousemove event on that layer:

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有关,但是给它最高的值以确保它在顶部并不能解决问题.

However, the mousemove event isn't triggered at all. I first assumed it had to do with z-index, but giving it the highest value to make sure it's on top didn't solve it.

有人知道什么可能阻止事件触发吗?还是考虑到画布的分层"配方,我应该注意什么?任何帮助表示赞赏!

Does anyone know what might be preventing the event from firing? Or what I should pay attention to, given the "layered" formulation of canvases? Any help appreciated!

我发现的另一种解释是,分层"画布属于父div元素,而工作的小提琴直接在主体中提供了画布.这会阻止鼠标事件起作用吗?如果是这样,如何应对div中的画布?

Another explanation I found is that the "layered" canvases belong to a parent div element, whereas the working fiddle provides a canvas directly in the body. Could this be preventing the mouse event from working? If so, how to cope with canvases in a div?

为了回应评论,此 bl.ocks 中提供了一个简化且可验证的示例. 原始代码可在此处中找到(参见示例文件brushing.html).该问题也在相应的问题委员会上进行了讨论.

In response to a comment, a simplified and verifiable example is provided in this bl.ocks. The original code is available here (cf. example file brushing.html). The question is also discussed on the corresponding issue board.

推荐答案

多层帆布鼠标事件

最好的方法是最简单的方法.仅创建一个侦听文档鼠标事件的鼠标处理程序.然后跟踪活动层并适当地对其进行处理.

Multy layered canvas mouse events

The best way to approch is the simplest. Create just one mouse handler that listens to document mouse events. Then keep track of the active layer and handle it appropreatly.

该摘录取自另一个答案,适用于在几层上创建线条.单击图层以将其选中,然后单击拖动以添加线.每个图层都有自己的颜色,需要对其进行选择以对其进行修改.

The snippet is taken from another answer and adapted to create lines on several layers. Click the layer to select it, then click drag to add lines. Each layer has its own colour and needs to be selected to modify it.

只有一个鼠标处理程序可以监听文档的鼠标事件,重置是在呈现代码中完成的.

There is only one mouse handler that listens to the document mouse events, the reset is done in the render code.

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>

这篇关于多个画布层和鼠标移动的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆