首页 > 解决方案 > 画布圈不可点击

问题描述

我正在使用 d3js 库创建地图,并在特定位置指向一些圆圈,并尝试在这些圆圈上创建点击事件,但这些圆圈不可点击。这是代码。

<!DOCTYPE html>
<head>
<title>A D3 Map, points plotted with canvas instead of SVG</title>
<style>

body {
  margin: 0;
}

#container {
  position: relative;
  overflow: hidden;
    z-index: 0;
}


#points{
    width:100%;
    height:100%;
    position:relative;
    z-index:100;
}

.layer{
    position:absolute;
    z-index:-10;
}

.tile {
    pointer-events: none;
    position: absolute;
    width: 256px;
    height: 256px;
}

.info {
  position: absolute;
  bottom: 0px;
  left: 0px;
    padding: 20px;
    background: #000;
    color: #fff;
    width: 100%;
    height: 18px;
    z-index: 1000;
    font-family:Helvetica, Arial, sans-serif;
    font-size:16px;
}   

.credit{
    position:absolute;
  bottom: 0px;
  right: 0px;
    padding: 9px 20px;
    color:#fff;
    font-family:Helvetica, Arial, sans-serif;
    font-size:13px;
    z-index: 1000;
}

.credit a{
    text-decoration:none;
    color:#ddd;
}


</style>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/d3.geo.tile.v0.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.2/d3.min.js"></script>
</head>
<body>
<div id="container">
    <div class="layer"></div>
    <div id="map">
        <canvas>
            <div id="points" ></div>
        </canvas>
    </div>
    <div class="credit"><p>Data &copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> Tiles &copy; <a href="http://cartodb.com/attributions">CartoDB</a></p></div>
</div>
  <script type='text/javascript'>

/* global variables and initial canvas space set up*/

var width = window.innerWidth;
var height = window.innerHeight;
var prefix = prefixMatch(["webkit", "ms", "Moz", "O"]);
console.log(width,height)
var dataSet = "";

d3.csv("/../map2/karriereTutor.csv", function(error, dataset) {
  dataSet = dataset;
  createMap(dataset) 
});

var tile = d3.geo.tile()
    .size([width, height]);

var projection = d3.geo.mercator()
.scale((1 << 24) / 2 / Math.PI)
.translate([width / 2, height / 2]); // just temporary

$('.arc').on('click',function(){
  console.log('asdfd')
})

var zoom = d3.behavior.zoom()
.scale(4.5 << 12)
    .scaleExtent([1 << 9, 12 << 23])
    .translate([width / 3  ,3000])
    .on("zoom", zoomed);

var container = d3.select("#container")
        .style("width", width + "px")
        .style("height", height + "px")
        .call(zoom)
        .on("mousemove", mousemoved)

var base = d3.select('#map');

var chart = d3.select('canvas')
            .attr("class", "layer")
      .attr('width', width)
      .attr('height', height);

var context = chart.node().getContext('2d');

var locations = d3.select('#points');


var layer = d3.select('.layer');

var info = base.append("div")
    .attr("class", "info");

zoomed();

function createMap(dataset) {

  var dataBinding = locations.selectAll("points.arc")
    .data(dataset)
    .enter()
    .append("div")
    .classed("arc", true)
    .attr('id',function(d){ return d.city })
    .attr("x", function(d) { return projection([d.y,d.x])[0]})
    .attr("y", function(d) {return projection([d.y,d.x])[1]})
    .attr("radius", 5)
    .attr("fillStyle", "#ff0000")
    drawCanvas();
}

function drawCanvas() {

    var elements = locations.selectAll("div.arc");
    elements.each(function(d) {
        var node = d3.select(this);
        context.beginPath();
        context.arc(node.attr("x"), node.attr("y"), node.attr("radius"), 0, 2 * Math.PI);
        context.fillStyle = node.attr("fillStyle");
        context.fill();        
        context.closePath();
    })
}


function reDraw() {
    context.clearRect(0, 0, width, height);
    drawCanvas();
}

function zoomed() {
  var tiles = tile
      .scale(zoom.scale())
      .translate(zoom.translate())
      ();

  projection
      .scale(zoom.scale() / 2 / Math.PI)
      .translate(zoom.translate());

    d3.selectAll("div.arc")
        .attr("x", function(d) {return projection([d.y,d.x])[0]})
        .attr("y", function(d) {return projection([d.y,d.x])[1]})
        reDraw();

  var image = layer
      .style(prefix + "transform", matrix3d(tiles.scale, tiles.translate))
      .selectAll(".tile")
      .data(tiles, function(d) { return d; })


  image.exit()
      .remove();

 image.enter().append("img")
 .attr("class", "tile")
      .attr("src", function(d) { return "http://" + ["a", "b", "c"][Math.random() * 3 | 0] + ".tile.openstreetmap.org/" + d[2] + "/" + d[0] + "/" + d[1] + ".png"; })
      .style("left", function(d) { return (d[0] << 8) + "px"; })
      .style("top", function(d) { return (d[1] << 8) + "px"; })
}



function mousemoved() {
  // for(var i = 0; i < dataSet.length; i++){
  //   var projectionPoints = projection.invert(d3.mouse(this));
  //   // console.log(dataSet[i].x, projectionPoints[1])
  //   if(Math.round(projectionPoints[1]) == Math.round(dataSet[i].x)){
  //     console.log(dataSet[i].city)
  //   }   
  // }
  info.text(formatLocation(projection.invert(d3.mouse(this)), zoom.scale()));
}

function matrix3d(scale, translate) {
  var k = scale / 256, r = scale % 1 ? Number : Math.round;
  return "matrix3d(" + [k, 0, 0, 0, 0, k, 0, 0, 0, 0, k, 0, r(translate[0] * scale), r(translate[1] * scale), 0, 1 ] + ")";
}

function prefixMatch(p) {
  var i = -1, n = p.length, s = document.body.style;
  while (++i < n) if (p[i] + "Transform" in s) return "-" + p[i].toLowerCase() + "-";
  return "";
}

function formatLocation(p, k) {
  var format = d3.format("." + Math.floor(Math.log(k) / 2 - 2) + "f");
  return (p[1] < 0 ? format(-p[1]) + " S" : format(p[1]) + " N") + " "
       + (p[0] < 0 ? format(-p[0]) + " W" : format(p[0]) + " E");
}

</script>
</body>
</html>

我正在使用 d3.js 来创建地图,这里是用于创建这些圈子位置的 csv 文件

x,y,name,city
48.0851267,11.198654,karriere tutor,Wörthsee
52.2815691,7.443409099999999,karriere tutor,Rheine
48.2629984,11.4339022,karriere tutor,Dachau
49.7019294,9.2559214,karriere tutor,Miltenberg
47.697371,9.5629717,karriere tutor,Meckenbeuren
48.5667364,13.4319466,karriere tutor,Passau
52.4357364,8.6159777,karriere tutor,Rahden
49.6743636,12.1489337,karriere tutor,Weiden in der Oberpfalz
47.7782704,9.612130299999999,karriere tutor,Ravensburg
48.3705449,10.89779,karriere tutor,Augsburg Bayern
51.9032375,8.385753500000002,karriere tutor,Gütersloh
53.6764136,10.2378854,karriere tutor,Ahrensburg
51.3876468,10.7465696,karriere tutor,Straussberg
49.749992,6.6371433,karriere tutor,Trier
52.52000659999999,13.404954,karriere tutor,Berlin
54.3232927,10.1227651,karriere tutor,Kiel
51.4969802,11.9688029,karriere tutor,Halle (Saale)
51.6659316,8.7276375,karriere tutor,Borchen
52.3472237,14.5505672,karriere tutor,Frankfurt (Oder)
54.0729431,9.9840158,karriere tutor,Neumünster Holstein
53.9317752,9.5051475,karriere tutor,Itzehoe
53.5395845,8.580942499999999,karriere tutor,Bremerhaven
54.19517639999999,9.1019015,karriere tutor,Heide Holstein
51.8368113,10.7844266,karriere tutor,Wernigerode
52.9154545,12.7990783,karriere tutor,Neuruppin
52.21697469999999,13.453792,karriere tutor,Zossen
53.2464214,10.4115179,karriere tutor,Lüneburg
50.8021728,8.7667933,karriere tutor,Marburg
53.8654673,10.6865593,karriere tutor,Lübeck
51.8205718,9.8683087,karriere tutor,Einbeck
50.0522076,8.6952638,karriere tutor,Neu-Isenburg
51.2964148,6.8401844,karriere tutor,Ratingen
51.2041968,6.6879511,karriere tutor,Neuss
50.0294877,8.6949231,karriere tutor,Dreieich
51.3387609,6.5853417,karriere tutor,Krefeld
49.4874592,8.466039499999999,karriere tutor,Mannheim
54.0924406,12.0991466,karriere tutor,Rostock
54.08654629999999,13.3923414,karriere tutor,Greifswald
47.84199820000001,12.9728226,karriere tutor,Freilassing
50.4358385,7.825795299999999,karriere tutor,Montabaur
53.2778837,9.7240138,karriere tutor,Tostedt
49.8028671,8.603536199999999,karriere tutor,Pfungstadt
48.8914741,9.034192899999999,karriere tutor,Hardthof bei Markgröningen
48.6471033,9.4519635,karriere tutor,Kirchheim unter Teck
48.6893963,10.1610948,karriere tutor,Heidenheim an der Brenz
47.6779496,9.173238399999999,karriere tutor,Konstanz
50.5558095,9.6808449,karriere tutor,Fulda
53.6355022,11.4012499,karriere tutor,Schwerin Mecklenburg
51.3127114,9.4797461,karriere tutor,Kassel Hessen
50.0782184,8.239760799999999,karriere tutor,Wiesbaden
48.4010822,9.987607599999999,karriere tutor,Ulm Donau
48.5950369,8.8671577,karriere tutor,Herrenberg
50.1109221,8.6821267,karriere tutor,Frankfurt am Main
52.3758916,9.732010400000002,karriere tutor,Hannover
48.1351253,11.5819805,karriere tutor,München
51.3396955,12.3730747,karriere tutor,Leipzig
50.937531,6.9602786,karriere tutor,Köln
51.5135872,7.465298100000001,karriere tutor,Dortmund
51.4556432,7.0115552,karriere tutor,Essen Ruhr
53.07929619999999,8.8016936,karriere tutor,Bremen
53.5510846,9.9936819,karriere tutor,Hamburg
50.14720579999999,8.824969099999999,karriere tutor,Maintal
48.8940624,9.195464,karriere tutor,Ludwigsburg Württemberg
49.9456399,11.5713346,karriere tutor,Bayreuth
52.2688736,10.5267696,karriere tutor,Braunschweig
50.2612094,10.962695,karriere tutor,Coburg
50.3569429,7.5889959,karriere tutor,Koblenz am Rhein
47.9837999,10.1801883,karriere tutor,Memmingen
51.3670777,7.4632841,karriere tutor,Hagen Westfalen
51.1652199,7.0671161,karriere tutor,Solingen
53.1434501,8.214552099999999,karriere tutor,Oldenburg (Oldb)
51.1804572,6.4428041,karriere tutor,Mönchengladbach
51.4185682,6.884522599999999,karriere tutor,Mülheim an der Ruhr
49.0134297,12.1016236,karriere tutor,Regensburg
49.47741,8.445179999999999,karriere tutor,Ludwigshafen am Rhein
49.4771169,10.988667,karriere tutor,Fürth Bayern
51.0504088,13.7372621,karriere tutor,Dresden
50.8216502,6.1320672,karriere tutor,Würselen
51.2562128,7.150763599999999,karriere tutor,Wuppertal
50.73743,7.0982068,karriere tutor,Bonn
48.7781001,8.0875217,karriere tutor,Karlsruhe Baden
48.7758459,9.1829321,karriere tutor,Stuttgart
51.2277411,6.7734556,karriere tutor,Düsseldorf
48.1916623,11.6460441,karriere tutor,Unterföhring
50.22683079999999,8.6181618,karriere tutor,Bad Homburg vor der Höhe
50.09563620000001,8.776084299999999,karriere tutor,Offenbach am Main
50.17874,8.47191,karriere tutor,Königstein im Taunus
49.3063689,8.6427693,karriere tutor,Walldorf Baden
50.98476789999999,11.02988,karriere tutor,Erfurt
49.24015720000001,6.996932699999999,karriere tutor,Saarbrücken
52.3905689,13.0644729,karriere tutor,Potsdam
49.4521018,11.0766654,karriere tutor,Nürnberg Mittelfranken
51.9606649,7.6261347,karriere tutor,Münster Westfalen
49.9928617,8.2472526,karriere tutor,Mainz am Rhein
52.1205333,11.6276237,karriere tutor,Magdeburg
47.9990077,7.842104299999999,karriere tutor,Freiburg im Breisgau
50.1467469,8.561455500000001,karriere tutor,Eschborn Taunus
49.8728253,8.6511929,karriere tutor,Darmstadt
48.6813312,9.0088299,karriere tutor,Böblingen
52.0302285,8.532470800000002,karriere tutor,Bielefeld
52.844198,8.053015799999999,karriere tutor,Cloppenburg
53.7513549,9.6632521,karriere tutor,Elmshorn
48.5788725,7.8160821,karriere tutor,Kehl Rhein

标签: javascripthtmld3.js

解决方案


您已经使用 DOM 节点(在本例中未渲染)使用画布实现了进入/更新/退出循环,但这些 DOM 节点根本不与画布交互 - 画布是像素的无状态集合。因此,任何与画布的鼠标交互都只能与整个画布交互——一个像素不能有自己的鼠标点击事件。

幸运的是,有许多方法可用于跟踪画布上的鼠标并将其与绘制的形状相关联。

直接开箱即用的最简单方法可能是在d3.forceSimulation没有任何实际模拟的情况下使用。它提供了一种方便的方法,该方法使用四叉树来查找最接近鼠标操作的节点(它还允许指定搜索半径)。这样做的好处是可以快速编码和快速执行,而无需尝试学习四叉树。如果您有圆形以外的形状,则可能不理想

force.find(x,y,r)` 仅在 d3v4 之后,但您似乎同时使用 d3v3 和 d3v4,因此 d3.forceSimulation 无需修改即可在此示例中工作

这是一个在画布上跟踪离鼠标最近的城市的示例:

首先我们设置节点:

var simulation = d3.forceSimulation().nodes(dataset);

然后我们找到鼠标位置并找到最近的节点:

 var xy = d3.mouse(this);               // get xy position of mouse
 var longlat = projection.invert(xy);   // convert it to a lat long
 var nearest = simulation.find(longlat[1],longlat[0]); // see what point is closest.

这里有几点注意事项:

  • 您已将经度标记为ycsv 并将纬度标记为x,这与正常惯例相反,但为了避免在代码的其他地方进行更改,我保留了它。这就是为什么最近.find() 在这里使用纬度、经度 (y,x)。
  • 强制布局使用xandy属性来跟踪节点位置,这就是为什么我没有指定任何访问器函数,因为您的数据集中已经有 x,y 属性
  • 当我在查找功能中比较纬度和经度时,我正在比较哪个圆更接近鼠标的角距离,而不是投影距离 - 这可以通过一些我不会在这里谈论的工作来避免这是一个单独的问题,在这种情况下,由于使用了几个坐标系而更加复杂:缩放、画布、地理和平铺

上面四五行代码,稍微修改一下鼠标悬停功能,显示离鼠标最近的城市可能是这样的

我没有指定搜索半径,但请记住,它会以度为单位,因为模拟使用地理坐标 ( d.x, d.y)。


推荐阅读