2016-05-26 11 views
1

散布図をSVGではなくキャンバスで描画します。html5キャンバス散布図のD3.jsマウスオーバー

だから、のようなもの:

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イベントにデータを渡すのでしょうか?

私がツールチップについて見た例はすべて、あなたが要素moused-overにバインドされたデータを持つイベントを処理していることを当然としています。

しかし、キャンバスはどうですか?

d3.bisectorまたはマウスイベントのx座標に基づいて同様のものを使用する必要があると仮定します。

答えて

2

次のように私は、ノアVeltmanによって提案されたソリューションを使用して終了:

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に基づくゲッターセッター機能を有する

var factory = d3.geom.quadtree() .extent([ [0, 0], [width, height] ]);

、各点のy座標値のペアを使用します。

1

1つの方法は、すべての点を繰り返し、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

私は同様のソリューションを使用して終了しました。 –

+1

この場合、quadtreeを使って移動することが最良の解決策です。 – Cyril

+0

はい。検索関数を書かなくて済むネイティブの '.find'メソッドがあることに注意してください。 –