Axes shifted 0.5 px to the right and downwards

334 views
Skip to first unread message

Bernhard Freiberger

unread,
Nov 18, 2016, 11:44:46 AM11/18/16
to d3-js

Hello dear community!

I noticed the following behavior of D3:
Apparently, the components of an axis are offset 0.5px to the right and downwards. In my example, the components have the following stroke-widths:
Red lines - 2
Black axis components - 1

The origin of the axes doesn't match the intersection of the red lines which I drew from (0,0) to (10,0) and from (0,0) to (0,10). You can verify the half-pixel-shift using the DOM-Explorer of your Browser and check the SVG-path and the lines which form the axis and the ticks.

I also found a discussion involving Mike Bostock with this subject:
https://github.com/d3/d3-axis/issues/8

My Question:
Is this shifting behaviour absolutely necessary and if not, can it be avoided?

Best Regards,

Bernhard



<!DOCTYPE html>
<meta charset="utf-8">
<html>
    <head>
        <title>Axes</title>
        <script src="https://d3js.org/d3.v4.min.js"></script>
        <style>
            .axis{
                stroke-width:2;
            }
            .line{
                stroke:red;
                stroke-width:1;
            }
        </style>
    </head>
    <body>
        <script>
            var margin = {top:20, right:20, bottom:20, left:20};
            var width = 440 - margin.left - margin.right;
            var height = 440 - 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 scaleX = d3.scaleLinear()
                .domain([0, 16])
                .range([0, width]);
            var axisX = d3.axisBottom(scaleX)
                .tickSize(0)
            var x = svg.append("g")
                .attr("class", "axis")
                .attr("transform", "translate( 0, " + height + ")")
                .call(axisX);
           
            var scaleY = d3.scaleLinear()
                .domain([0, 16])
                .range([height, 0]);       
            var axisY = d3.axisLeft(scaleY)
                .tickSize(0)
            var y = svg.append("g")
                .attr("class", "axis")
                .call(axisY);

            var line1 = svg.append("line")
                .attr("class", "line")
                .attr("x1", scaleX(0))
                .attr("y1", scaleY(0))
                .attr("x2", scaleX(10))
                .attr("y2", scaleY(0));

            var line2 = svg.append("line")
                .attr("class", "line")
                .attr("x1", scaleX(0))
                .attr("y1", scaleY(0))
                .attr("x2", scaleX(0))
                .attr("y2", scaleY(5));
        </script>
    </body>
</html>

Mike Bostock

unread,
Nov 18, 2016, 11:55:53 AM11/18/16
to d3-js
The ticks and domain path are shifted by a half-pixel so that the subsequent one-pixel wide stroke exactly fills one row (or column) of pixels, rather than filling half of both adjacent rows. In SVG (and most other graphics systems, including Canvas) a stroke is centered around the path, and thus to align the exterior of the stroked path with the exact pixel boundaries, this offset is necessary.

There is no option to disable this behavior (and it is not clear why you would want to, since it would result in blurry axes). But you can of course translate your axis element, or modify the generated contents however you like.

Mike
--
You received this message because you are subscribed to the Google Groups "d3-js" group.
To unsubscribe from this group and stop receiving emails from it, send an email to d3-js+un...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Reply all
Reply to author
Forward
0 new messages