Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to reduce lagging of a guideline that follows the mouse pointer?

Tags:

svg

d3.js

I have a vertical guideline across my svg that follows the mouse pointer, but right now it's kind of slow to update its position, something that's especially noticeable with rapid mouse movements. Is there a way to reduce this lag?

Current code:

svg.on("mousemove", function(d) {
    svg.select(".guideline")
        .attr("x1", d3.mouse(this)[0]-1)
        .attr("x2", d3.mouse(this)[0]-1);

});

svg.on("mouseover", function(d) {
    svg.append("line")
        .attr("class", "guideline")
        .attr("y1", margin[0])
        .attr("y2", height+margin[0])
        .attr("opacity", originOpacity)
        .attr("stroke", "#333")
        .attr("pointer-events", "none");

});

svg.on("mouseout", function(d) {
    svg.select(".guideline").remove();
});
like image 989
Jimmy C Avatar asked Oct 10 '12 20:10

Jimmy C


3 Answers

You are selecting the line on every mousemove, keep the line in a variable instead:

var line = svg.append("line")
        .attr("class", "guideline")
        .attr("y1", margin[0])
        .attr("y2", height+margin[0])
        .attr("opacity", 0)
        .attr("stroke", "#333")
        .attr("pointer-events", "none");

svg.on("mousemove", function(d) {

    line
        .attr("x1", d3.event.pageX-1)
        .attr("x2", d3.event.pageY-1);

});

svg.on("mouseover", function(d) {
  line.attr("opacity", originOpacity);
});

svg.on("mouseout", function(d) {
    line.attr("opacity", 0);
});
like image 53
methodofaction Avatar answered Nov 20 '22 10:11

methodofaction


I am having the same problem, but I noticed two things:

  1. If you look at High Charts, they have implemented (in their JS library) a similar vertical guideline, which doesn't lag behind so much. So it is possible to do it. See for example: here

  2. I use a container element, to which I added an SVG element, to which I added a group (g) element with a coordinate translation/transformation, like so:

HTML:

<div id="d3-container"></div>

JS:

var svg = d3.select("#d3-container")
    .append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .attr("id","d3-svg")
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
    .attr("id","d3-canvas");

The interesting thing is, when I bind mouse events (using the code above in the first answer) to the various elements, I get very different performance: when I bind it to the "d3-canvas" group my guided line is very slow and lags behind, when I bind it to its parent svg element ("d3-svg") it is already faster, and if I bind it to the div ("d3-container") I get the fastest performance (although it is still not as quick as High Stock though). So I am thinking the coordinate transformation adds a lot of overhead to mouse events, but somehow either D3 or SVG is also not optimized for mouse events.

like image 3
genie Avatar answered Nov 20 '22 12:11

genie


Rather than updating the attributes on every mousemove, you could add a couple millisecond delay:

var lastMove, elapsed;
svg.on("mousemove", function(d) {

    elapsed = Date.now() - lastMove;
    if ( elapsed < 40 ) return;

    svg.select(".guideline")
        .attr("x1", d3.mouse(this)[0]-1)
        .attr("x2", d3.mouse(this)[0]-1);

    lastMove = Date.now();
});

This will certainly improve performance, but at the cost of making the movements more choppy. Play around with the number of milliseconds you check for. 40 may be too long.

like image 3
Wex Avatar answered Nov 20 '22 10:11

Wex