D3.js鼠标悬停在html5画布散点图

时间:2016-05-26 18:15:41

标签: html5 canvas d3.js scatter

让我们说我们绘制散点图不是用SVG而是用画布。

类似于:

    var scale = 1 + Math.floor(Math.random() * 10);

    // Redraw axes
    x.domain([0, scale]);
    y.domain([0, scale]);
    xg.call(xAxis);
    yg.call(yAxis);

    var points = randomPoints(scale),
        colors = {};

    // Update canvas
    context.clearRect(0, 0, width, height);
    picker.clearRect(0, 0, width, height);

    points.forEach(function(p,i){

      // Space out the colors a bit
      var color = getColor(i * 1000 + 1);
      colors[color] = p;
      picker.fillStyle = "rgb(" + color + ")";

      context.beginPath();
      picker.beginPath();

      context.arc(x(p[0]), y(p[1]), 10, 0, 2 * Math.PI);
      picker.arc(x(p[0]), y(p[1]), 5, 0, 2 * Math.PI);

      context.fill();
      picker.fill();

    });

现在我们如何将数据传递到mouseover事件,比如绘制工具提示?

我在工具提示中看到的示例都理所当然地认为您正在处理一个事件,该事件的数据绑定到一个被剔除的元素。

但是画布怎么样?

我假设您需要根据鼠标事件的x y坐标使用d3.bisector或类似内容。

2 个答案:

答案 0 :(得分:3)

一种方法是迭代所有点并检查x和y是否与点击的附近匹配。当散点图点太多时,这肯定会很慢。{我认为在你的情况下,你在画布上制作散点图只是为了解决这个问题}

其他方式是使用四叉树。 首先,我随机取10000点。

  sampleData = d3.range(1000).map(function(d) {
    var datapoint = {};
    datapoint.id = "Sample Node " + d;
    datapoint.x = Math.random() * 500;
    datapoint.y = Math.random() * 500;

    return datapoint;
  })

将散点图中的所有点存储在四叉树中,如下所示。

  quadtree = d3.geom.quadtree()
    .extent([[0,0], [500,500]]) //here 500 is the width and height of the canvas or the max x/y range of the points in scatter chart.
    .x(function(d) {return d.x})
    .y(function(d) {return d.y});

将所有点传递到四叉树:

  quadData = quadtree(sampleData);

现在点击查找关联的节点数据:

  quadData = quadtree(sampleData);
   d3.select("canvas").on("click", function(){
    found = []; 
    //find in the vicinity of 10 pixel around the click.
    search(quadData, d3.event.pageX -10, d3.event.pageY-10, d3.event.pageX +10, d3.event.pageY+10);
    var message = "";
    //iterate the found and make the message
    found.forEach(function(d){
      message += d.id + " ";
    });
    alert("selected Node" + message);
    var data
  })

最后我的搜索功能检查四叉树矩形中的节点:

function search(quadtree, x0, y0, x3, y3) {
  quadtree.visit(function(node, x1, y1, x2, y2) {
    var p = node.point;
    if (p) {
      p.selected = (p.x >= x0) && (p.x < x3) && (p.y >= y0) && (p.y < y3);
      if(p.selected){
        found.push(p);
      }
    }
    return x1 >= x3 || y1 >= y3 || x2 < x0 || y2 < y0;
  });
}

点击任意一个圆圈,您将收到有关其所持数据的警报 工作代码here

答案 1 :(得分:3)

我最终使用Noah Veltman建议的解决方案,如下:

let button = NSButton()
button.target = self
button.action = #selector(someAction)

关键是使用

var margin = {top: 20, right: 10, bottom: 30, left: 40}, width = 960 - margin.left - margin.right, height = 500 - margin.top - margin.bottom; var svg = d3.select("body").append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + " " + margin.top + ")"); var factory = d3.geom.quadtree() .extent([ [0, 0], [width, height] ]); var x = d3.scale.linear() .range([0, width]); var y = d3.scale.linear() .range([height, 0]); var xAxis = d3.svg.axis() .scale(x) .orient("bottom"); var yAxis = d3.svg.axis() .scale(y) .orient("left"); var xg = svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + height + ")"); var yg = svg.append("g") .attr("class", "y axis"); var chartArea = d3.select("body").append("div") .style("left", margin.left + "px") .style("top", margin.top + "px"); var canvas = chartArea.append("canvas") .attr("width", width) .attr("height", height); var context = canvas.node().getContext("2d"); context.fillStyle = "#f0f"; // Layer on top of canvas, example of selection details var highlight = chartArea.append("svg") .attr("width", width) .attr("height", height) .append("circle") .attr("r", 7) .classed("hidden", true); redraw(); function redraw() { // Randomize the scale var scale = 1 + Math.floor(Math.random() * 10); // Redraw axes x.domain([0, scale]); y.domain([0, scale]); xg.call(xAxis); yg.call(yAxis); var points = randomPoints(scale); var tree = factory(points); // Update canvas context.clearRect(0, 0, width, height); points.forEach(function(p,i){ context.beginPath(); context.arc(x(p[0]), y(p[1]), 5, 0, 2 * Math.PI); context.fill(); }); canvas.on("mousemove",function(){ var mouse = d3.mouse(this), closest = tree.find([x.invert(mouse[0]), y.invert(mouse[1])]); highlight.attr("cx", x(closest[0])) .attr("cy", y(closest[1])); }); canvas.on("mouseover",function(){ highlight.classed("hidden", false); }); canvas.on("mouseout",function(){ highlight.classed("hidden", true); }); } function randomPoints(scale) { // Get points return d3.range(1000).map(function(d){ return [ Math.random() * scale, Math.random() * scale ]; }); }

具有基于每个点的x,y值对的getter setter函数。