GithubHelp home page GithubHelp logo

d3-axis's Introduction

d3-axis

The axis component renders human-readable reference marks for scales. This alleviates one of the more tedious tasks in visualizing data.

Installing

If you use npm, npm install d3-axis. You can also download the latest release on GitHub. For vanilla HTML in modern browsers, import d3-axis from Skypack:

<script type="module">

import {axisLeft} from "https://cdn.skypack.dev/[email protected]";

const axis = axisLeft(scale);

</script>

For legacy environments, you can load d3-axis’s UMD bundle from an npm-based CDN such as jsDelivr; a d3 global is exported:

<script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>
<script>

const axis = d3.axisLeft(scale);

</script>

Try d3-axis in your browser.

API Reference

Regardless of orientation, axes are always rendered at the origin. To change the position of the axis with respect to the chart, specify a transform attribute on the containing element. For example:

d3.select("body").append("svg")
    .attr("width", 1440)
    .attr("height", 30)
  .append("g")
    .attr("transform", "translate(0,30)")
    .call(axis);

The elements created by the axis are considered part of its public API. You can apply external stylesheets or modify the generated axis elements to customize the axis appearance.

Custom Axis

An axis consists of a path element of class “domain” representing the extent of the scale’s domain, followed by transformed g elements of class “tick” representing each of the scale’s ticks. Each tick has a line element to draw the tick line, and a text element for the tick label. For example, here is a typical bottom-oriented axis:

<g fill="none" font-size="10" font-family="sans-serif" text-anchor="middle">
  <path class="domain" stroke="currentColor" d="M0.5,6V0.5H880.5V6"></path>
  <g class="tick" opacity="1" transform="translate(0.5,0)">
    <line stroke="currentColor" y2="6"></line>
    <text fill="currentColor" y="9" dy="0.71em">0.0</text>
  </g>
  <g class="tick" opacity="1" transform="translate(176.5,0)">
    <line stroke="currentColor" y2="6"></line>
    <text fill="currentColor" y="9" dy="0.71em">0.2</text>
  </g>
  <g class="tick" opacity="1" transform="translate(352.5,0)">
    <line stroke="currentColor" y2="6"></line>
    <text fill="currentColor" y="9" dy="0.71em">0.4</text>
  </g>
  <g class="tick" opacity="1" transform="translate(528.5,0)">
    <line stroke="currentColor" y2="6"></line>
    <text fill="currentColor" y="9" dy="0.71em">0.6</text>
  </g>
  <g class="tick" opacity="1" transform="translate(704.5,0)">
    <line stroke="currentColor" y2="6"></line>
    <text fill="currentColor" y="9" dy="0.71em">0.8</text>
  </g>
  <g class="tick" opacity="1" transform="translate(880.5,0)">
    <line stroke="currentColor" y2="6"></line>
    <text fill="currentColor" y="9" dy="0.71em">1.0</text>
  </g>
</g>

The orientation of an axis is fixed; to change the orientation, remove the old axis and create a new axis.

# d3.axisTop(scale) · Source

Constructs a new top-oriented axis generator for the given scale, with empty tick arguments, a tick size of 6 and padding of 3. In this orientation, ticks are drawn above the horizontal domain path.

# d3.axisRight(scale) · Source

Constructs a new right-oriented axis generator for the given scale, with empty tick arguments, a tick size of 6 and padding of 3. In this orientation, ticks are drawn to the right of the vertical domain path.

# d3.axisBottom(scale) · Source

Constructs a new bottom-oriented axis generator for the given scale, with empty tick arguments, a tick size of 6 and padding of 3. In this orientation, ticks are drawn below the horizontal domain path.

# d3.axisLeft(scale) · Source

Constructs a new left-oriented axis generator for the given scale, with empty tick arguments, a tick size of 6 and padding of 3. In this orientation, ticks are drawn to the left of the vertical domain path.

# axis(context) · Source

Render the axis to the given context, which may be either a selection of SVG containers (either SVG or G elements) or a corresponding transition.

# axis.scale([scale]) · Source

If scale is specified, sets the scale and returns the axis. If scale is not specified, returns the current scale.

# axis.ticks(arguments…) · Source
# axis.ticks([count[, specifier]])
# axis.ticks([interval[, specifier]])

Sets the arguments that will be passed to scale.ticks and scale.tickFormat when the axis is rendered, and returns the axis generator. The meaning of the arguments depends on the axis’ scale type: most commonly, the arguments are a suggested count for the number of ticks (or a time interval for time scales), and an optional format specifier to customize how the tick values are formatted.

This method has no effect if the scale does not implement scale.ticks, as with band and point scales. To set the tick values explicitly, use axis.tickValues. To set the tick format explicitly, use axis.tickFormat.

For example, to generate twenty ticks with SI-prefix formatting on a linear scale, say:

axis.ticks(20, "s");

To generate ticks every fifteen minutes with a time scale, say:

axis.ticks(d3.timeMinute.every(15));

This method is also a convenience function for axis.tickArguments. For example, this:

axis.ticks(10);

Is equivalent to:

axis.tickArguments([10]);

To generate tick values directly, use scale.ticks.

# axis.tickArguments([arguments]) · Source

If arguments is specified, sets the arguments that will be passed to scale.ticks and scale.tickFormat when the axis is rendered, and returns the axis generator. The meaning of the arguments depends on the axis’ scale type: most commonly, the arguments are a suggested count for the number of ticks (or a time interval for time scales), and an optional format specifier to customize how the tick values are formatted.

If arguments is specified, this method has no effect if the scale does not implement scale.ticks, as with band and point scales. To set the tick values explicitly, use axis.tickValues. To set the tick format explicitly, use axis.tickFormat.

If arguments is not specified, returns the current tick arguments, which defaults to the empty array.

For example, to generate twenty ticks with SI-prefix formatting on a linear scale, say:

axis.tickArguments([20, "s"]);

To generate ticks every fifteen minutes with a time scale, say:

axis.tickArguments([d3.timeMinute.every(15)]);

See also axis.ticks.

# axis.tickValues([values]) · Source

If a values iterable is specified, the specified values are used for ticks rather than using the scale’s automatic tick generator. If values is null, clears any previously-set explicit tick values and reverts back to the scale’s tick generator. If values is not specified, returns the current tick values, which defaults to null. For example, to generate ticks at specific values:

var xAxis = d3.axisBottom(x)
    .tickValues([1, 2, 3, 5, 8, 13, 21]);

The explicit tick values take precedent over the tick arguments set by axis.tickArguments. However, any tick arguments will still be passed to the scale’s tickFormat function if a tick format is not also set.

# axis.tickFormat([format]) · Source

If format is specified, sets the tick format function and returns the axis. If format is not specified, returns the current format function, which defaults to null. A null format indicates that the scale’s default formatter should be used, which is generated by calling scale.tickFormat. In this case, the arguments specified by axis.tickArguments are likewise passed to scale.tickFormat.

See d3-format and d3-time-format for help creating formatters. For example, to display integers with comma-grouping for thousands:

axis.tickFormat(d3.format(",.0f"));

More commonly, a format specifier is passed to axis.ticks:

axis.ticks(10, ",f");

This has the advantage of setting the format precision automatically based on the tick interval.

# axis.tickSize([size]) · Source

If size is specified, sets the inner and outer tick size to the specified value and returns the axis. If size is not specified, returns the current inner tick size, which defaults to 6.

# axis.tickSizeInner([size]) · Source

If size is specified, sets the inner tick size to the specified value and returns the axis. If size is not specified, returns the current inner tick size, which defaults to 6. The inner tick size controls the length of the tick lines, offset from the native position of the axis.

# axis.tickSizeOuter([size]) · Source

If size is specified, sets the outer tick size to the specified value and returns the axis. If size is not specified, returns the current outer tick size, which defaults to 6. The outer tick size controls the length of the square ends of the domain path, offset from the native position of the axis. Thus, the “outer ticks” are not actually ticks but part of the domain path, and their position is determined by the associated scale’s domain extent. Thus, outer ticks may overlap with the first or last inner tick. An outer tick size of 0 suppresses the square ends of the domain path, instead producing a straight line.

# axis.tickPadding([padding]) · Source

If padding is specified, sets the padding to the specified value in pixels and returns the axis. If padding is not specified, returns the current padding which defaults to 3 pixels.

# axis.offset([offset]) · Source

If offset is specified, sets the offset to the specified value in pixels and returns the axis. If offset is not specified, returns the current offset which defaults to 0 on devices with a devicePixelRatio greater than 1, and 0.5px otherwise. This default offset ensures crisp edges on low-resolution devices.

d3-axis's People

Contributors

curran avatar fil avatar llorenspujol avatar mbostock avatar stof avatar yurivish avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

d3-axis's Issues

Updating scale domain does not remove old tick marks

First time using an axis results in correct ticks, but upon modifying the underlying scale, selecting the same axis, and calling it again, what happens is new ticks are created, but the old ones are not removed.

EX:

// Create svg, etc... 
var svg = d3.select("body").append("svg")/* margin conventions, etc */ .append("g")



var y = d3.scaleLinear().rangeRound([height, 0]).domain([0,1]);
var yAxis = d3.axisRight(y).tickFormat(d3.format(".2s"))

// Works
svg.append("g")
  .attr("class", "y axis")
  .attr("transform", "translate(" + width + ",0)")
  .call(yAxis)

// updating the scales domain
var max = 16000;
y.domain([0, max]);

// issues occur
 svg.select(".y.axis").call(yAxis);

Uncaught TypeError: setting getter-only property "top" when calling axis function

Using the latest release (1.0.12), I was unable to produce axes, always getting the error Uncaught TypeError: setting getter-only property "top" when calling an axis function, e.g.

const years = [2019,2018,2017,2016,2015]
const margin = {top: 0, right: 60, bottom: 40, left: 0}
const Y = scaleLinear() // time axis
	.domain([Math.min(...years),Math.max(...years)])
	.range([0,height-margin.bottom])
svg.append('g')
	.attr('transform',`translate(${width-margin.right},0)`)
	.call(axisRight(Y)) // <-- it always failed right here

Reverting the package to 1.0.0 solved the problem with no change to my code.

Suggestion: Add axisTop example

A recent version of chrome appears to have delivered a change to their default user agent specification that implements the following rule;

https://www.w3.org/TR/SVG/styling.html#UAStyleSheet

Specifically the following appears to be new (not sure exactly when but last couple of chrome versions at least);

svg:not(:root), image, marker, pattern, symbol { overflow: hidden; }

Specifically the part that is interesting to this library is that svgs that are not at the root will now have their overflow hidden by default.

What this means is that the axisTop function will now no longer display unless you override that style because it works by using negative values for y which takes causes it to overflow (and therefore not display because of this new rule).

The following hopefully illustrates this better;

var scale = d3.scaleLinear().domain([0, 100]).range([0, 400]);

var axis = d3.axisBottom(scale);

d3.select("#axis-demo")
  .append("svg")
    .style('overflow', 'inherit') // previously you didn't need to do this, but now you do
    .attr("width", 400)
    .attr("height", 30)
  .append("g")
    .attr("transform", "translate(0,30)")
    .call(axis);

I don't think this is necessarily something to "fix" but I was wondering if you thought it was worth putting a quick note in the docs about this to save anyone else running into this? Happy to provide the PR if you agree.

`center` offset

I think the center func offset in axis.js is slightly off.

function center(scale) {
  var offset = scale.bandwidth() / 2;
  if (scale.round()) offset = Math.round(offset);
  return function(d) {
    return scale(d) + offset;
  };
}

Suppose bandwidth is 3. Then we expect the offset to be 1, to place it on the middle pixel. With the current implementation, you would get 1.5 or 2, depending on the rounding flag. I think the correct offset should be:

var offset = (scale.bandwidth() - 1) / 2;

tickSizeOuter(0) not yielding straight line

The README states

An outer tick size of 0 suppresses the square ends of the domain path, instead producing a straight line.

However, practically this is not exactly true. When setting a stroke on the .domain path, one can observe that the endpoints are not quite right, as in this example:

image

In this case, after setting tickSizeOuter(0), I would expect that the domain path ending would end up as a square end.

The path for the X axis domain is M0.5,0V0.5H867.5V0. When changing it manually to M0.5,0V0.5H867.5, the resulting rendered path is a straight line:

image

The solution would be to exclude the V0 part of the path when tickSizeOuter === 0 somewhere around axis.js#L92. The following bit of implementation would need to change:

    path
        .attr("d", orient === left || orient == right
            ? "M" + k * tickSizeOuter + "," + range0 + "H0.5V" + range1 + "H" + k * tickSizeOuter
            : "M" + range0 + "," + k * tickSizeOuter + "V0.5H" + range1 + "V" + k * tickSizeOuter);

The change might look something like this:

    path
        .attr("d", orient === left || orient == right
            ? "M" + k * tickSizeOuter + "," + range0 + "H0.5V" + range1 + (tickSizeOuter ? "H" + k * tickSizeOuter : "")
            : "M" + range0 + "," + k * tickSizeOuter + "V0.5H" + range1 + (tickSizeOuter? "V" + k * tickSizeOuter : ""));

Is this change something that would be desirable?

Option to hide domain path (line)

Hi, it would be nice to have an api for hiding domain line. Sometimes axis on a side doesn't need to show domain line, just values. Currently it is possible to hide it with css, but it would be nicer to not render it in a first place.

Thank you

Right-align tick text in axisRight() ?

Is there a way to right-align the tick text when creating a right axis via axisRight()?

I'm currently rendering a right axis on my graph like so:

  root
    .attr("class", "y gap-axis")
    .attr("margin-top", margin.top)
    .attr("transform", `translate(${width})`)
    .call(
      d3.axisRight(barYScale)
        .tickPadding(-1),
    );
}

and I get results like so:
image
but I want the numbers to be right-aligned.

Support external rendering

Hi,

It's quite common now to use a JS framework such as React or Vue to render the elements instead of D3.js DOM manipulation. While it's possible with most of the packages, d3-axis doesn't support this kind of operation which is useful not only for consistency and reactivity but also for rendering of labels outside of svg to keep font rendering and size consistent with the remaining page elements even when chart is being scaled.

Is it possible to make d3-axis more friendly for select-less operation? E.g. provide methods to get ticks coordinates, line path etc. I would file a PR if I was sure how to do it right but I'm not, so could you either implement it or guide me on how this can be done?

`axis.tickValues` could also return `values`?

axis.tickValues([values])

... If values is not specified, returns the current tick values, which defaults to null. ...

I would have expected, that once an axis is drawn (and has called scale.ticks()) calling axis.tickValues() would return me the cached continuous.ticks. However, since I did not previously set axis.tickValues(a_value), axis.tickValues() returns null. (I'm assuming that calling continuous.ticks() requires computation.)

I think this would involve changing this line to set tickValues as well as values

var values = tickValues == null ? (scale.ticks ? scale.ticks.apply(scale, tickArguments) : scale.domain()) : tickValues,

Or expose a new method to get the current values. In the meantime I'll just call continuous.ticks.


By the way, why doesn't continuous.ticks have a <> which links to some line of code in the source?

More configuration options?

It might be nice to have more configuration options built-in. All of these are possible using post-selection, but that approach is likely more tedious than having supported configuration, especially if you want it to work with transitions.

This options are demonstrated in this example scatterplot:

https://observablehq.com/@d3/scatterplot

Render in vanilla HTML?

I want to propose the idea of rendering axis in pure HTML (rather than SVG).

SVG is great for it's power, but I've found generally text manipulation/styling is generally much more difficult. With regular HTML I believe we could leverage things such as auto-truncation of long labels in CSS, simpler sizing of fonts etc.

Be interested to know thoughts on this (obviously would be a re-implementation of the renderer). I was taking a look at a Parallel Co-ordinates chart at the time and thinking there's quite a bit of work to be done with scaling and contrast to make the chart more accessible.

d3 shape like API

I am trying to integrate d3 axis into a react-native application.
d3 shape API is quite good in the sense that it gives back the raw path data which could be directly used with react-native ART api.
However d3-axis doesn't have similar isolation. it relies on knowledge of DOM to setup the path correctly. It would be nice to have an API to get those axis, ticks and labels path directly so that it can be used with any compatible rendering methods.

Default Fonts Being Applied to Axis Container

D3: 4.7.4
Chrome: 57.0.2987.133 (64-bit)
OS: Win 10x64, Ubuntu 16.10

We are transitioning to D3 v4 and have noticed that the axis containers are receiving a default font and size, font-size="10" font-family="sans-serif", which is clobbering our desired inherited styles.

Is this expected behaviour?

.ticks behavior on axis created from scaleBands scale

I noticed that the suggested count argument for the number of ticks for the .ticks() method does not change the tick behavior when called on an axis created from a scaleBands scale. I think this makes sense, but it may be helpful to mention this in the documentation.

I found this issue when graphing a bar chart with many dates along the X axis. The dates would overlap, and .ticks() was not changing the result. I see that the tickValues are assigned to the scale's domain here when the scale has no ticks function, which makes sense why I was not seeing any changes when providing an argument to .ticks().

I was able to find this post where a solution is directly assigning tickValues based off a filtered scale domain, which helped me.

The docs are currently as follows:

. . . most commonly, the arguments are a suggested count for the number of ticks (or a time interval for time scales) . . .

This makes perfect sense, but it may be helpful to include the behavior for other scales as well. Would it be worthwhile to mention the behavior of the ticks method for scaleBands scales and any other outliers?

Color ramps?

d3.rampHorizontal = function(x, color) {
  var size = 16;

  function ramp(g) {
    var image = g.selectAll("image").data([null]),
        xz = x.range(),
        x0 = xz[0],
        x1 = xz[xz.length - 1],
        canvas = document.createElement("canvas"),
        context = (canvas.width = x1 - x0 + 1, canvas.height = 1, canvas).getContext("2d");

    for (var i = x0; i <= x1; ++i) {
      context.fillStyle = color(x.invert(i));
      context.fillRect(i - x0, 0, 1, 1);
    }

    image = image.enter().append("image").merge(image)
        .attr("x", x0)
        .attr("y", -size)
        .attr("width", x1 - x0 + 1)
        .attr("height", size)
        .attr("preserveAspectRatio", "none")
        .attr("xlink:href", canvas.toDataURL());
  }

  ramp.position = function(_) {
    return arguments.length ? (x = _, ramp) : x;
  };

  ramp.color = function(_) {
    return arguments.length ? (color = _, ramp) : color;
  };

  ramp.size = function(_) {
    return arguments.length ? (size = +_, ramp) : size;
  };

  return ramp;
};

Slowdown due to superficious DOM modifications

Some attributes are re-set to the same values on pre-existing elements instead of setting once only on freshly added elements:

https://github.com/d3/d3-axis/blob/master/src/axis.js#L85-110

Profiler of MS Edge browser shows that some DOM operations in existing axis code take as much as 0.5 ms, which is large given the 16.7 ms total time bugdet. Firefox is slower, Chrome is faster, and the story on mobile devices may be totally different, but still.

A dirty hack shows significant increase in frame rate, it seems that setting attributes on text nodes is especially expensive:

https://github.com/streamcode9/svg-time-series/blob/626ddf9bed104bb457217f2183084b91a4b4261f/benchmarks/d3-myaxis/index.js#L118-142

While I added separate axisUp method for incremental updates, it's a proof of concept code to see if framerate can be improved. A cleaner approach of setting attributes only on what "enters" is certainly possible.

Also, grouping and stylesheet manipulation using CSS DOM can be used to avoid repeatedly setting the same attributes on added nodes. They can be inherited from parent element or defined once in declarations instead.

What do you think? Can we for example avoid setting .transform on many nodes, and set it once on a CSS group element or something like that?

for d3 version 4

I included changes for this script to work with d3 version 4:

d3.slider = function module() {
  "use strict";

  var div, min = 0, max = 100, svg, svgGroup, value, classPrefix, axis, 
  height=40, rect,
  rectHeight = 12,
  tickSize = 6,
  margin = {top: 25, right: 25, bottom: 15, left: 25}, 
  ticks = 0, tickValues, scale, tickFormat, dragger, width, 
  range = false,
  callbackFn, stepValues, focus;

  function slider(selection) {
    selection.each(function() {
      div = d3.select(this).classed('d3slider', true);
      width = parseInt(div.style("width"), 10)-(margin.left 
                                                + margin.right);

      value = value || min; 
      scale = d3.scaleLinear().domain([min, max]).range([0, width])
      .clamp(true);
      
      // SVG 
      svg = div.append("svg")
      .attr("class", "d3slider-axis")
      .attr("width", width + margin.left + margin.right)
      .attr("height", height + margin.top + margin.bottom)
      .append("g")
      .attr("transform", "translate(" + margin.left + 
            "," + margin.top + ")");

      // Range rect
      svg.append("rect")
      .attr("class", "d3slider-rect-range")
      .attr("width", width)
      .attr("height", rectHeight);
     
      // Range rect 
      if (range) {
        svg.append("rect")
        .attr("class", "d3slider-rect-value")
        .attr("width", scale(value))
        .attr("height", rectHeight);
      }
      
      // Axis      
      var axis = d3.axisBottom()
      .scale(scale);
      
      if (ticks != 0) {
        axis.ticks(ticks);
        axis.tickSize(tickSize);
      } else if (tickValues) {
        axis.tickValues(tickValues);
        axis.tickSize(tickSize);
      } else {
        axis.ticks(0);
        axis.tickSize(0);
      }
      if (tickFormat) {
        axis.tickFormat(tickFormat);
      }
      
      svg.append("g")
      .attr("transform", "translate(0," + rectHeight + ")")
      .call(axis)
      //.selectAll(".tick")
      //.data(tickValues, function(d) { return d; })
      //.exit()
      //.classed("minor", true);
   
      var values = [value];
      dragger = svg.selectAll(".dragger")
      .data(values)
      .enter()
      .append("g")
      .attr("class", "dragger")
      .attr("transform", function(d) {
        return "translate(" + scale(d) + ")";
      }) 
      
      var displayValue = null;
      if (tickFormat) { 
        displayValue = tickFormat(value);
      } else {
        displayValue = d3.format(",.0f")(value);
      }
      
      dragger.append("text")
      .attr("x", 0)
      .attr("y", -15)
      .attr("text-anchor", "middle")
      .attr("class", "draggertext")
      .text(displayValue);

      dragger.append("circle")
      .attr("class", "dragger-outer")
      .attr("r", 10)
      .attr("transform", function(d) {
        return "translate(0,6)";
      });
      
      dragger.append("circle")
      .attr("class", "dragger-inner")
      .attr("r", 4)
      .attr("transform", function(d) {
        return "translate(0,6)";
      });


      // Enable dragger drag 
      var dragBehaviour = d3.drag();
      dragBehaviour.on("drag", slider.drag);
      dragger.call(dragBehaviour);
      
      // Move dragger on click 
      svg.on("click", slider.click);

    });
  }

  slider.draggerTranslateFn = function() {
    return function(d) {
      return "translate(" + scale(d) + ")";
    }
  }

  slider.click = function() {
    var pos = d3.event.offsetX || d3.event.layerX;
    slider.move(pos);
  }

  slider.drag = function() {
    var pos = d3.event.x;
    slider.move(pos+margin.left);
  }

  slider.move = function(pos) {
    var l,u;
    var newValue = scale.invert(pos - margin.left);
    // find tick values that are closest to newValue
    // lower bound
    if (stepValues != undefined) {
      l = stepValues.reduce(function(p, c, i, arr){
        if (c < newValue) {
          return c;
        } else {
          return p;
        }
      });

      // upper bound
      if (stepValues.indexOf(l) < stepValues.length-1) {
        u = stepValues[stepValues.indexOf(l) + 1];
      } else {
        u = l;
      }
      // set values
      var oldValue = value;
      value = ((newValue-l) <= (u-newValue)) ? l : u;
    } else {
      var oldValue = value;
      value = newValue;
    }
    var values = [value];

    // Move dragger
    svg.selectAll(".dragger").data(values)
    .attr("transform", function(d) {
      return "translate(" + scale(d) + ")";
    });
    
    var displayValue = null;
    if (tickFormat) { 
      displayValue = tickFormat(value);
    } else {
      displayValue = d3.format(",.0f")(value);
    }
    svg.selectAll(".dragger").select("text")
    .text(displayValue);
   
    if (range) { 
      svg.selectAll(".d3slider-rect-value")
      .attr("width", scale(value));
    }

    if (callbackFn) {
      callbackFn(slider);
    }
  }

  // Getter/setter functions
  slider.min = function(_) {
    if (!arguments.length) return min;
    min = _;
    return slider;
  };

  slider.max = function(_) {
    if (!arguments.length) return max;
    max = _;
    return slider;
  };

  slider.classPrefix = function(_) {
    if (!arguments.length) return classPrefix;
    classPrefix = _;
    return slider;
  }

  slider.tickValues = function(_) {
    if (!arguments.length) return tickValues;
    tickValues = _;
    return slider;
  }
 
  slider.ticks = function(_) {
    if (!arguments.length) return ticks;
    ticks = _;
    return slider;
  }

  slider.stepValues = function(_) {
    if (!arguments.length) return stepValues;
    stepValues = _;
    return slider;
  }
  
  slider.tickFormat = function(_) {
    if (!arguments.length) return tickFormat;
    tickFormat = _;
    return slider;
  } 

  slider.value = function(_) {
    if (!arguments.length) return value;
    value = _;
    return slider;
  } 
  
  slider.showRange = function(_) {
    if (!arguments.length) return range;
    range = _;
    return slider;
  } 

  slider.callback = function(_) {
    if (!arguments.length) return callbackFn;
    callbackFn = _;
    return slider;
  }

  slider.setValue = function(newValue) {
    var pos = scale(newValue) + margin.left;
    slider.move(pos);
  }

  slider.mousemove = function() {
    var pos = d3.mouse(this)[0];
    var val = slider.getNearest(scale.invert(pos), stepValues);
    focus.attr("transform", "translate(" + scale(val) + ",0)");
    focus.selectAll("text").text(val);
  }
  
  slider.getNearest = function(val, arr) {
    var l = arr.reduce(function(p, c, i, a){
      if (c < val) {
        return c;
      } else {
        return p;
      }
    });
    var u = arr[arr.indexOf(l)+1];
    var nearest = ((value-l) <= (u-value)) ? l : u;
    return nearest;
  }

  slider.destroy = function() {
    div.selectAll('svg').remove();
    return slider;
  }

  return slider;

};

Error when trying to transition axis

When I try to call the axis on a transition, I get an infinitely repeating error:

329d3-transition.v0.2.js:308 Uncaught TypeError: interpolate is not a function
(anonymous function) @ d3-transition.v0.2.js:308
tween @ d3-transition.v0.2.js:347
start @ d3-transition.v0.2.js:120
schedule @ d3-transition.v0.2.js:69
r @ d3-timer.v0.4.min.js:1
u @ d3-timer.v0.4.min.js:1

The minimal example code:

var scale = d3_scale.scaleLinear().range([0, 400])
var axis = d3_axis.axisBottom().scale(scale)

d3_selection.select('#update').on('click', update)

update()

function update() {
  var t = d3_transition.transition().duration(2000)

  scale.domain([0, Math.random() * 100])

  d3_selection.select('#example')
     .transition(t)
    .call(axis)
}

Here's an example showing the issue (press update to cause the error) https://jsfiddle.net/obsrwzdc/

Offset ticks and domain path by a half-pixel?

Safari often botches shape-rendering: crispEdges, drawing a two-pixel-wide line even though the stroke-width is one pixel. Also, in the common case where the coordinates are rounded, using a half-pixel offset should produce crisp edges even without shape-rendering.

I think we should use zero-based indexing for coordinates, such that y = 0 means the topmost pixel and x = 0 means the leftmost pixel, and therefore offsetting by +0.5 (not -0.5) in both x and y.

Canvas rendering?

It wouldn’t be as configurable as SVG rendering, since you wouldn’t be able to apply a stylesheet or perform post-selection; and I definitely wouldn’t want to open the door to a million configuration options. (You can always overlay SVG on top of Canvas if you want to customize the appearance.)

Not easy to have integer scale

I have a chart with linear scale axis.
Now if I want to have integers only on axis, I have to override scale.ticks() method which isn't an elegant way to do that:

    const _ticks = y.ticks;
    y.ticks = (...args) => {
        const ticks = _ticks.apply(y, args);
        return ticks.filter(a => Math.floor(a) === a);
    };

Any ideas how to improve tick generation customization?
Maybe ticks generator function as an option of axis, then having built-in multiple ticks generator functions?

It's not working, you overjump the creating basic axis

How can you get write the manual so is not use in examples? I follow your instruction in VS code, but it's still erroring scale is not defined.

<script src="https://d3js.org/d3-axis.v1.min.js"></script> <script src="https://d3js.org/d3.v5.min.js"></script>
<script>
    var axis = d3.axisLeft(scale);
    d3.select("body").append("svg")
      .attr("width", 1440)
      .attr("height", 300)
      .append("g")
      .attr("transform", "translate(0,30)")
      .call(axis);
  </script>

Date boundary issue for months with 31 days

image

For months with 31 days, it is affecting the scale of the axis. This doesn't happen with 30 days or 28.

React and typescript, versions:

"d3": "^5.7.0"

"@types/d3": "^5.5.0",
"@types/d3-array": "^1.2.4",
"@types/d3-shape": "^1.2.2",

Show scale extrema values in ticks

Is there an option to show extrema ticks (in my case, the origin tick would suffice).

As you can see below, my y axis ticks are fine, except that I'd like a tick going through my x-axis. The domain goes from 388 to 617, and I'd like to keep the same ticks as shown on the graph + a new tick at position 388.

image

I will use tickValues for the time being and manually calculate all the ticks needed. Hopefully you can tell me about a simpler way :)

Use attr instead of style to provide defaults and facilitate overrides.

This will allow us to define a default fill and stroke for ticks and the domain path, rather than requiring users to include these style rules:

.axis line {
  stroke: #000;
}

.axis path {
  fill: none;
  stroke: #000;
}

(I’m not 100% certain crispEdges should be included by default; it’s probably best to use make the defaults as minimal as possible, both for performance and to facilitate overrides.)

Related d3/d3#2102; this would also apply to the text-anchor used to position axis ticks.

Updating tickValues after axis scaling

I've done an Y axis scaling slider. It worked fine with ticks(50) and my custom implementation minor tick hiding... But even though Y axis range had equal amounts of positive and negative, sometimes zero was not drawn as a major tick.

After doing custom calculation of tick values, I noticed that when 'tickValues' is called again it does not replace the previous ticks but rather draws an another set of ticks.

How about an improvement that when tickValues() is called with new data, it would replace the previously drawn ticks?

If I'd just remove those ticks like so:

d3.selectAll('.focus .axis--y1 .tick').remove();
yAxis.tickValues(yTickValues);
d3.selectAll('.focus .axis--y1 .tick')
    .filter(function (d, i) { return !isMajorTick(i); })
    .classed("minor", true);

It doesn't work as the removal operation takes time...and implementing an event listener callback for chaining this these further operations would seem a too cumbersome solution.

The upper range value should be rounded down.

Currently I say:

range0 = range[0] + 0.5,
range1 = range[range.length - 1] + 0.5,

But wouldn’t this be better?

range0 = range[0] + 0.5,
range1 = range[range.length - 1] - 0.5,

Use currentColor instead of black?

This would make d3-axis work automatically against a dark background in cases where the CSS color is contrasting, and would provide an easier mechanism to restyle the axis.

Recalling a different axis, axisLeft to axisRight, is not changing existing text-anchor?

I am trying to do some user action which allows to change the axis direction, and getting unexpected behaviour.

When empty the g axis element, then g_elm.call(d3.axisRight(y0)), the text-anchor should have changed to "start", but is not, text-anchor stays as "end". Result is tick text appears wrong.

jsfiddle here https://jsfiddle.net/ba9nohc4/1/

first button, keep g element. Keep g element, but clear children.
second button, remove the g element, and create new g element.

axisLeft = text-anchor = "end"
axisRight = text-anchor = "start"

the axis code does look like text-anchor is being run, but not being set.

Is something stopping the text-anchor being changed, or some other attribute on the g element that needs clearing before calling axisRight?

Same issue going from axisRight to axisLeft, expect text-anchor should change from 'start' to 'end'

Expose orient of an axis

Without having the orient set, if I'm passed an anonymous axis object, I cannot inspect to see what orientation it is at.

If I wanted to transform containing elements based on orientation, there's no way to do that.

Added a PR for this #17.

Feasibility of returning html string

Would it be feasible to optionally just return the html string instead of rendering directly to the DOM (or otherwise depending on the DOM at all)?

Default formatter shows month name instead of Sunday

What's the rational behind this? I know I can set my own tickFormat, but not when dynamically changing the time range (i.e zoom). It's a minor detail, I guess, but I find it very odd. To illustrate, the time axis may show [..., Sat 02, 12 PM, Feb 03, 12PM, Mon 04, ...]

How to apply pan/zoom to rotated X Axis labels?

I am an absolute newbie to D3

I am able to follow and use the recipe of Rotated axis labels in v4 ( https://bl.ocks.org/d3noob/3c040800ff6457717cca586ae9547dbf )
However; if I apply pan/zoom to such plot, all labels vanish and even ticks do not move with the zoom or pan.

However; if the axis labels are kept without any special formatting of text-anchor and rotation etc, the zoom behavior for the axis works just fine.

Please let me know ( or point me to any example about ) how to use pan / zoom with rotated or formatted x axis labels.

(I have created same issue in d3/zoom (d3/d3-zoom#155))

Thanks in advance
-Mahesh

Axis labels are not removed from the DOM

I'm implementing a chart that has a sliding x axis.

I noticed that the axis labels that slide out of the chart are not removed from the DOM.

Why? How could I fix that to prevent the DOM growing indefinitely?

Demo

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.