html5 canvas工具提示仅对最后绘制的对象可见,对先前的对象不可见

时间:2018-09-19 12:11:03

标签: javascript jquery html5 canvas html5-canvas

我真正想要实现的是

  1. 在画布上绘制对象和
  2. 将鼠标悬停在工具提示上显示相关数据。

here,您可以查看代码。

$categories = Category::all();
$count = count($categories);
$categoriesWithProducts = array();

for($i = 0; $i < $count; $i++) {
    if($categories[$i]->products->count() > 0) {
        array_push($categoriesWithProducts, $categories[$i]);
    }
    return  response()->json($categoriesWithProducts);
}
var canvasBack;
var canvasLabel;
var canvasDraw;
var ctxBack;
var ctxLabel;
var ctxDraw;
var last_mousex = 0;
var last_mousey = 0;
var mousex = 0;
var mousey = 0;
var canWidth;
var canHeight;
var scaleParameter;
var radius;
var xVertex;
var yVertex;
var hotspots = [];

// initialization on loading of canvas
$('canvas').ready(function() {
  init();
});

// initialization function used for binding events, and inital logic implemented.
function init() {
  scaleParameter = 1;
  canvasBack = document.getElementById('backSpace');
  canvasLabel = document.getElementById('layerCanvas');
  canvasDraw = document.getElementById('drawSpace');
  ctxBack = canvasBack.getContext('2d');
  ctxLabel = canvasLabel.getContext('2d');
  ctxDraw = canvasDraw.getContext('2d');

  canWidth = parseInt($(canvasBack).attr('width'));
  canHeight = parseInt($(canvasBack).attr('height'));

  var canvasx = $(canvasBack).offset().left;
  var canvasy = $(canvasBack).offset().top
  var mousedown = false;

  //Mousedown
  $('canvas').on('mousedown', function(e) {
    $('#drawSpace').css('display', 'block');
    last_mousex = mousex = parseInt(e.clientX - canvasx);
    last_mousey = mousey = parseInt(e.clientY - canvasy);
    mousedown = true;
  });

  //Mouseup
  $('canvas').on('mouseup', function(e) {
    hotspots.push({
      x: xVertex,
      y: yVertex,
      radius: radius,
      tip: 'You are over ' + mousex + ',' + mousey
    });
    let cw = canvasBack.width;
    let ch = canvasBack.height;
    ctxBack.drawImage(canvasDraw, 0, 0, cw, ch);
    $('#drawSpace').css('display', 'none');
    mousedown = false;
  });

  //Mousemove
  $('canvas').on('mousemove', function(e) {
    mousex = parseInt(e.clientX - canvasx);
    mousey = parseInt(e.clientY - canvasy);
    if (mousedown) {
      // draw(mousedown);
      drawEllipse(last_mousex, last_mousey, mousex, mousey);
    } else {
      hoverTooltip();
    }
  });
}


function drawEllipse(x1, y1, x2, y2) {
  var leftScroll = $("#scrollParent").scrollLeft();
  var topScroll = $("#scrollParent").scrollTop();
  let cw = canvasBack.width;
  let ch = canvasBack.height;
  ctxDraw.clearRect(0, 0, cw, ch);
  var radiusX = x2 - x1,
    radiusY = y2 - y1,
    centerX = x1 + radiusX,
    centerY = y1 + radiusY,
    step = 0.01,
    a = step,
    pi2 = Math.PI * 2 - step;

  radius = Math.sqrt(radiusX * radiusX + radiusY * radiusY) / 2;

  ctxDraw.beginPath();
  ctxDraw.arc(centerX, centerY, radius, 0, 2 * Math.PI, true);
  ctxDraw.closePath();
  ctxDraw.fillStyle = 'green';
  ctxDraw.fill();
  ctxDraw.strokeStyle = '#000';
  ctxDraw.stroke();

  xVertex = centerX;
  yVertex = centerY;
}

// tooltip show on hover over objects
function hoverTooltip() {
  var leftScroll = $("#scrollParent").scrollLeft();
  var topScroll = $("#scrollParent").scrollTop();
  let cw = canvasBack.width;
  let ch = canvasBack.height;

  for (var i = 0; i < hotspots.length; i++) {
    var h = hotspots[i];
    var dx = mousex - h.x;
    var dy = mousey - h.y;
    if (dx * dx + dy * dy < h.radius * h.radius) {
      $('#console').text(h.tip);
      ctxLabel.clearRect(0, 0, cw, ch);
      ctxLabel.fillText(h.tip, mousex + leftScroll, mousey + topScroll);
    } else {
      ctxLabel.clearRect(0, 0, cw, ch);
    }
  }
}
#scrollParent {
  width: 644px;
  height: 364px;
  overflow: auto;
  position: relative;
}

#scrollParent>canvas {
  position: absolute;
  left: 0;
  top: 0;
  border: 1px solid #ababab;
}

#backSpace {
  z-index: 0;
}

#drawSpace {
  display: none;
  z-index: 1;
}

#layerCanvas {
  z-index: 2;
}

波纹管图像中的实际问题是,绘制第一个对象时工具提示可以正常工作,但是绘制第二个对象时,工具提示仅适用于第二个对象,而不适用于先前绘制的对象。

什么原因导致此问题,以及如何解决? enter image description here

2 个答案:

答案 0 :(得分:2)

删除其他内容将不会在离开椭圆形时删除标签。

一旦使用break从数组中找到正确的椭圆,就需要退出循环。

function hoverTooltip() {
  var leftScroll = $("#scrollParent").scrollLeft();
  var topScroll = $("#scrollParent").scrollTop();
  let cw = canvasBack.width;
  let ch = canvasBack.height;

  for (var i = 0; i < hotspots.length; i++) {
    var h = hotspots[i];
    var dx = mousex - h.x;
    var dy = mousey - h.y;
    if (dx * dx + dy * dy < h.radius * h.radius) {
      $('#console').text(h.tip);
      ctxLabel.clearRect(0, 0, cw, ch);
      ctxLabel.fillText(h.tip, mousex + leftScroll, mousey + topScroll);
      break; // exit the loop
    } else {
       ctxLabel.clearRect(0, 0, cw, ch);
    }
  }
}

更新

我认为,如果您在两个对象之间绘制两个对象,则会表现不佳。试试这个吧。它将显示最新绘制点的信息。

function hoverTooltip() {
  var leftScroll = $("#scrollParent").scrollLeft();
  var topScroll = $("#scrollParent").scrollTop();
  let cw = canvasBack.width;
  let ch = canvasBack.height;

  var spots = hotspots.filter((h) => {
    var dx = mousex - h.x;
    var dy = mousey - h.y; 
    return (dx * dx + dy * dy < h.radius * h.radius);
  })

  if (spots.length > 0) {
    var h = spots[spots.length - 1]; // latest drawn spot
    $('#console').text(h.tip);
    ctxLabel.clearRect(0, 0, cw, ch);
    ctxLabel.fillText(h.tip, mousex + leftScroll, mousey + topScroll);
  } 
  else
  {
    ctxLabel.clearRect(0, 0, cw, ch);
  }
}

答案 1 :(得分:2)

我看到已经有一些答案了。这是我的: 为了能够在悬停时显示每个圆的标签,您需要将所有圆保存在am数组中:circles数组。我正在使用ctx.isPointInPath()方法来知道鼠标是否在圆圈上,如果是,我会绘制标签。

const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
let cw = canvas.width = 640;
let ch = canvas.height = 360;

let found = false;//is a circle found?

const cText = document.querySelector("#text");
const ctxText = cText.getContext("2d");
cText.width = 640;
cText.height = 360;
ctxText.font="1em Verdana";


let drawing = false;

let circles = []


class Circle{
  constructor(x,y){
    this.x = x; 
    this.y = y;
    this.r = 0;
  }
  
  updateR(m) {
    this.r = dist(this,m);
  }
  draw(){ 
     ctx.beginPath();
     ctx.arc(this.x,this.y,this.r,0,2*Math.PI);
   }
  paint(){
    ctx.fillStyle = "green";
    ctx.strokeStyle = "black";
    this.draw();
    ctx.stroke();
    ctx.fill();
  }
  
  label(m){
    this.draw();
    if (ctx.isPointInPath(m.x, m.y)) {
    ctx.beginPath();
    ctx.arc(this.x, this.y, 4, 0, 2 * Math.PI);
     
    ctxText.fillStyle = "black";
    ctxText.fillText(`you are over ${this.x},${this.y}`,m.x,m.y)
    found = true;
    }
}
}
let m = {}// mouse

cText.addEventListener("mousedown",(e)=>{
  drawing = true;
  m = oMousePos(canvas, e);
  let circle = new Circle(m.x,m.y)
  circles.push(circle);
})

cText.addEventListener("mouseup",(e)=>{
  drawing = false; 
  
})


cText.addEventListener("mousemove",(e)=>{
  m = oMousePos(canvas, e);
  found = false;
  if(drawing){
    let circle = circles[circles.length-1];//the last circle in the circles arrey
    circle.updateR(m); 
  }
  ctx.clearRect(0,0, cw,ch);
  ctxText.clearRect(0,0,cw,ch) 
  circles.map((c) => {c.paint();}); 
  for(let i = circles.length-1; i >=0 ; i--){
    circles[i].label(m);
    if(found){break;}
  }
  
  
})


function oMousePos(canvas, evt) {
  var ClientRect = canvas.getBoundingClientRect();
  return { //objeto
    x: Math.round(evt.clientX - ClientRect.left),
    y: Math.round(evt.clientY - ClientRect.top)
  }
}

function dist(p1, p2) {
  let dx = p2.x - p1.x;
  let dy = p2.y - p1.y;
  return Math.sqrt(dx * dx + dy * dy);
}
canvas{border:1px solid;position:absolute; top:0; left:0;}
#scrollParent{position:relative;}
<div id="scrollParent">
  <!-- actual canvas that is visible -->
  <canvas width="640" height="360"></canvas>
  <canvas width="640" height="360" id="text"></canvas>

</div>

我已经在@HelderSepu的注释的基础上更新了代码

来自@HelderSepu的第二条消息的第二次更新。他希望看到“多条消息,但要避免重叠消息”

const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
let cw = canvas.width = 640;
let ch = canvas.height = 360;

let text = "";

const cText = document.querySelector("#text");
const ctxText = cText.getContext("2d");
cText.width = 640;
cText.height = 360;
ctxText.font="1em Verdana";


let drawing = false;

let circles = []


class Circle{
  constructor(x,y){
    this.x = x; 
    this.y = y;
    this.r = 0;
  }
  
  updateR(m) {
    this.r = dist(this,m);
  }
  draw(){ 
     ctx.beginPath();
     ctx.arc(this.x,this.y,this.r,0,2*Math.PI);
   }
  paint(){
    ctx.fillStyle = "green";
    ctx.strokeStyle = "black";
    this.draw();
    ctx.stroke();
    ctx.fill();
  }
  
  label(m){
    this.draw();
    if (ctx.isPointInPath(m.x, m.y)) {
    this.text = `[${this.x},${this.y}]`
  }else{
    this.text = "";
  }
}
}
let m = {}// mouse

cText.addEventListener("mousedown",(e)=>{
  drawing = true;
  m = oMousePos(canvas, e);
  let circle = new Circle(m.x,m.y)
  circles.push(circle);
})

cText.addEventListener("mouseup",(e)=>{
  drawing = false; 
  
})


cText.addEventListener("mousemove",(e)=>{
  m = oMousePos(canvas, e);
  
  if(drawing){
    let circle = circles[circles.length-1];//the last circle in the circles arrey
    circle.updateR(m); 
  }
  ctx.clearRect(0,0, cw,ch);
  ctxText.clearRect(0,0,cw,ch);
  text="";
  circles.map((c) => {c.paint();c.label(m);}); 
  circles.map((c) => {text += c.text;});
  ctxText.fillStyle = "black";
  ctxText.fillText(text,m.x,m.y)
  
})


function oMousePos(canvas, evt) {
  var ClientRect = canvas.getBoundingClientRect();
  return { //objeto
    x: Math.round(evt.clientX - ClientRect.left),
    y: Math.round(evt.clientY - ClientRect.top)
  }
}

function dist(p1, p2) {
  let dx = p2.x - p1.x;
  let dy = p2.y - p1.y;
  return Math.sqrt(dx * dx + dy * dy);
}
canvas{border:1px solid;position:absolute; top:0; left:0;}
#scrollParent{position:relati
<div id="scrollParent">
  <!-- actual canvas that is visible -->
  <canvas width="640" height="360"></canvas>
  <canvas width="640" height="360" id="text"></canvas>

<div id="console"></div>
</div>