D3.js多个y轴具有相同的刻度位置?

问题描述 投票:7回答:1

我有3个y轴和一个x轴的图表,必须放大所有3轴。

DEMO


what I have

是否可以设置所有3 y轴,中心为0?如果我缩放图表滴答必须保持在同一个地方?

或者,也许,说“在0值之间始终只显示10个滴答”。例如: 范围1 - > [10..0 ..- 10] 范围2 - > [350..0 ..- 350] 范围3 - > [0,1..0..0,9]

但是来自所有3个范围的所有蜱必须保持在相同的位置。

need same view


UPDATE

//zooming
var zoom0 = d3.behavior.zoom().x(x).y(_y[0]).on("zoom", zoomed);
var zoom1 = d3.behavior.zoom().y(_y[1]);
var zoom2 = d3.behavior.zoom().y(_y[2]);
***
function zoomed() {
  zoom1.scale(zoom0.scale());
  zoom1.translate(zoom0.translate());
  zoom2.scale(zoom0.scale());
  zoom2.translate(zoom0.translate());
  graph.select(".x.axis").call(xAxis);
  for (var i = 0; i < _y.length; i++) {
    graph.select(".y.ax" + i).call(_yAxis[i]);
    graph.select('.line.ax' + i).attr('d', _lineGenerator(i));
  }
}

更新v3:

enter image description here


更新:

enter image description here

是否可以在y轴上的刻度之间设置填充?或者更好地说“禁用刻度线之间的距离,并将其设置为固定值”

.tickValues() - doesn't help in this case...
function create_Y_axes() {
  var _key, _currentKeyIndex, yDomain_before, yDomain_after,  yD_min,  yD_max,  _index;

  for (var i = 0; i < series.length; i++) {

    _key = series[i]["unit"];
    _currentKeyIndex = yCategories.indexOf(_key);

    if (_currentKeyIndex === -1) {
      //If key not found in array
      yCategories.push(_key);

      series[i]["yAxis"] = yCategories.length - 1;

      _y.push(
        d3.scale.linear()
          .domain(d3.extent(series[i]["UNIT_DATA"], function(d,i){return d.wert;}))
          .range([chartHeight, 0])
          .nice()
      );

    } else {
      series[i]["yAxis"] = _currentKeyIndex;
    }
  } //for

  var yDomain,min, max, ticks = 10, r1,r2,range,stepL,stepR, t, tArray, h;

  for (var i = 0; i < _y.length; i++) {

    yDomain = _y[i].domain();// [-60, 60]

    min = yDomain[0];// -60
    max = yDomain[1];//  60

    h = Math.floor((ticks / 2));

    stepL = (Math.abs(min / h));
    stepR = (Math.abs(max / h));

    r1 = d3.range(min, 0 - stepL, stepL);
    r2 = d3.range(0, max + stepR, stepR);

    tArray = d3.merge([r1, r2]);

    t = d3.scale.ordinal().domain(tArray).rangePoints([chartHeight, 0]).domain();
      _yAxis.push(
        d3.svg.axis()
              .scale(_y[i])
              .orient("left")
              .tickPadding(5)
              .tickValues(t)
              .tickFormat(function(d){return d.toFixed(2);})
      );

  } 

}// createYaxes()

更新v5:

这就是我需要的:

enter image description here

我创建了这个3轴没有d3.axis。***,但是这个解决方案非常糟糕:)我将使用d3-API作为我的图表!

请帮我!!!!


javascript d3.js charts nvd3.js
1个回答
5
投票

以下是您的代码修改,以使得刻度线排在0处。注意的一些更改在代码中注释为:

// ***

主要变化是,如果您希望刻度线在零处排列并且在所有三个轴上具有相同的刻度数高于和低于零,则您的域应该以零为中心。

我在每个缩放/平移上重新生成刻度值,显示最小值,最大值和等间距值。您可能想要进行一些舍入,或者如果它在可见域中,则强制零为刻度值。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <link rel="stylesheet prefetch" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
  <style>
body {
  background: #fff;
  text-align: center;
  position: relative;
}

.row {
  margin-top: 25px;
  margin-bottom: 25px;
}

.tooltipX {
  position: absolute;
  text-align: center;
  padding: 10px;
  border-radius: 15px;
  border: 1px solid #000;
  margin-top: -20px;
  font: 10px sans-serif;
  background: rgba(0, 0, 0, 0.1);
  pointer-events: none;
  display: block;
}

svg {
  background: #e8e8e8;
}

svg .title {
  font-size: 16px;
  fill: #000;
  font-weight: 700;
}

svg .subtitle {
  font-size: 12px;
  font-weight: 500;
  fill: #000;
  stroke-width: 1px;
  stroke-linecap: butt;
  stroke-linejoin: miter;
}

svg .axis {
  fill: #000;
  pointer-events: all;
}

svg .axis text {
  font-family: sans-serif;
  font-size: 11px;
}

svg .axis path {
  fill: none;
  stroke-width: 1;
  stroke: #000;
  shape-rendering: crispEdges;
}

svg .axis line {
  fill: none;
  stroke-width: 1;
  stroke: #000;
  shape-rendering: crispEdges;
}

svg .line {
  stroke-width: 1;
  fill: none;
}

svg .grid .tick {
  stroke-width: 1;
  stroke: rgba(0, 0, 0, 0.1);
}

svg .legend {
  cursor: pointer;
}

svg .overlay {
  fill: none;
  pointer-events: all;
}

svg .x.axis .tick line {
  stroke: rgba(0, 0, 0, 0.1);
}

svg .y.axis .tick line {
  stroke: #f00;
}
  </style>
</head>
<body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script src="https://a4a42cb81401f26d4437b778613cb8ba7c3b1b73.googledrive.com/host/0B0gcn9nZbbFgM3MtZFRGeTJVWFU"></script>
<div class="container">
  <div class="row">
<div class="col-xs-12">
  <div id="chart"></div>
</div>
  </div>
</div>
<script>

  (function (window) {
'use strict';

var TICKS = 10;

//--------------------------------------------------
//Generate D3 random color
var colorscale = d3.scale.category20();

var chart = {
  "options": {
    "chart": {
      "title": "",
      "description": ""
    }
  },
  "legend": {
    "enable": true,
    "useName": true
  },
  "show": {
    "title": true,
    "description": true,
    "yGrid": true
  },
  "zoom": {
    "enable": true,
    "resetBtnId": "#btnResetZoom"
  },
  "cursor": {
    "enable": true
  },
  "series": [],
  "yGridBtnId": "#btnYGrid"
};

//prepare options
var options = {
  "chart": {
    "title": "my title",
    "description": "my description"
  }
};

//prepare Series
var series = [
  {
    "channel": {
      "name": "my name",
      "code": "codeXY"
    },
    "stat": {
      "min": -6.99796,
      "minTime": 0.01225,
      "max": 0.110055,
      "maxTime": 0.01755
    },
    "unit": "Bar",
    "UNIT_DATA": data001
  },

  {
    "channel": {
      "name": "my Name 2",
      "code": "codeOP"
    },
    "stat": {
      "min": -310.51,
      "minTime": 0.0472,
      "max": 191.675,
      "maxTime": 0.1282
    },
    "unit": "N",
    "UNIT_DATA": data036
  },

  {
    "channel": {
      "name": "my name 3",
      "code": "coedXP"
    },
    "stat": {
      "min": -30.2723,
      "minTime": 0.05935,
      "max": 0.753309,
      "maxTime": -0.0972
    },
    "unit": "Nm",
    "UNIT_DATA": data080
  }
];

//--------------------------------------------------
//Settings & variables
var _x;
var xAxis;
var _yScale = [];
var _yAxis = [];
var zooming;
var yCategories = [];
var margins = {
    top: 50,
    left: 50,
    bottom: 100,
    right: 50,
    height: 480
  },
  _factor = 50,
  chartWidth = "100%",
  chartHeight = margins.height - margins.top - margins.bottom, graph;

//--------------------------------------------------
//add y-Axis
function create_Y_axes() {
  var _key, _currentKeyIndex, yDomain, yD_max;
  for (var i = 0; i < series.length; i++) {
    _key = series[i]["unit"];
    _currentKeyIndex = yCategories.indexOf(_key);
    if (_currentKeyIndex === -1) {
      //If key not found in array
      yCategories.push(_key);
      series[i]["yAxis"] = yCategories.length - 1;
      yDomain = d3.extent(series[i]["UNIT_DATA"], function (d, i) {
        return d.wert;
      });
      // *** Center the yDomain around 0
      yD_max = d3.max([Math.abs(yDomain[0]), Math.abs(yDomain[1])]);
      yDomain = [-yD_max, +yD_max];
      console.log(i, yCategories[i], yDomain);
      _yScale.push(
        d3.scale.linear()
          .domain(yDomain)
          .range([chartHeight, 0])
          .nice()
      );
    } else {
      series[i]["yAxis"] = _currentKeyIndex;
    }
  } //for

  var tArray, tickValues, ticks = 10;

  for (var i = 0; i < _yScale.length; i++) {

    tArray = generateTicksForYaxis(_yScale[i].domain());// [-60, 60]
    // *** Don't need this
    //tickValues = d3.scale.ordinal().domain(tArray).rangePoints([chartHeight, 0]).domain();
    //console.error(tickValues, tickValues.length);
    //_yScale[i].domain(d3.extent(tickValues));
    console.log(" LINEAR\t", d3.scale.linear().domain(tArray).domain());
    console.log("ORDINAL\t", d3.scale.ordinal().domain(tArray).rangePoints([chartHeight, 0]).domain());


    _yAxis.push(
      d3.svg.axis()
        .scale(_yScale[i])
        .orient("left")
        // *** No need for .ticks if specifying values
        //.ticks(ticks)
        .tickPadding(5)
        .tickValues(tArray)
        .tickFormat(function (d) {
          return d.toFixed(2);
        })
    );

  }

}// createYaxes()

function generateTicksForYaxis(yDomain) {
  // **** If the domain is centered around 0, we can just divide it into TICKS steps.
  var min = yDomain[0];
  var max = yDomain[1];
  var step = (max - min)/ TICKS;
  console.log("yDomain:", yDomain, "min", min, "max", max, "step:", step);
  var tickArray = d3.range(min, max+step, step);
  console.log("Range[", tickArray.length, "]:", tickArray);
  return tickArray;
}

//--------------------------------------------------
//add yAxes
create_Y_axes();

//update chart width!!!
chartWidth = d3.select("#chart").node().getBoundingClientRect().width - (_factor * _yScale.length) - margins.right;
create_X_axis();

//--------------------------------------------------
//SVG container
graph = d3
  .select("#chart")
  .append("svg:svg")
  .attr("width", "100%")
  .attr("height", chartHeight + margins.top + margins.bottom)
  .append("g") //append new container element and move it with margin top/left
  .attr("transform", "translate(" + (_factor * _yScale.length) + "," + margins.top + ")");

//--------------------------------------------------

//Redraw the axes
graph.selectAll('g.axis').remove();

//draw x-axis
graph
  .append("g")
  .attr("class", "x axis")
  .attr("transform", "translate(0, " + chartHeight + ")")
  .call(xAxis)
  .append("text")
  .attr("class", "x-text")
  .attr("x", chartWidth / 2)
  .attr("y", 25)
  .text("Sec.");

//draw y-axis
console.log("draw y-axes", _yScale.length, _yAxis.length, yCategories);
for (var i = 0; i < _yScale.length; i++) {
  graph
    .append("g")
    .attr("class", "y axis ax" + i)
    .attr("transform", "translate(-" + (i * 50) + ",0)")
    .call(_yAxis[i])
    .append("text")
    .attr("y", -10)
    .attr("x", -10)
    .style('text-anchor', 'end')
    .text(yCategories[i]);
}

// Add clip-zone
graph.append("clipPath").attr("id", "clip").append("rect").attr("class", "plot").attr("width", chartWidth).attr("height", chartHeight);

function activateZoom() {
  if (chart.zoom.enable) {
    var yID = null;
    zooming = [];
    for (var i = 0; i < series.length; i++) {
      yID = series[i]["yAxis"];
      zooming.push(d3.behavior.zoom().x(_x).y(_yScale[yID]));
    }
    zooming[0].on("zoom", zoomed);
    graph.call(zooming[0]);
    d3.select(chart.zoom.resetBtnId).on("click", resetZoom);
  }
} //activateZoom()

// *** On zoom display min, max and equally spaced values.
// You may want to force zero to display if it is present.
function reCalculateTickValues(yAxis, yScale) {
  var min = yScale.invert(chartHeight);
  var max = yScale.invert(0);
  var step = (Math.abs(max) + Math.abs(min))/ TICKS;
  var tickValues = d3.range(min, max, step);
  tickValues.push(max);
  yAxis.tickValues(tickValues);
}

function zoomed() {
  graph.select(".x.axis").call(xAxis);
  for (var i = 1; i < series.length; i++) {
    zooming[i].scale(zooming[0].scale());
    zooming[i].translate(zooming[0].translate());
  }

  for (var i = 0; i < series.length; i++) {
    graph.select('.line.ax' + i).attr('d', _lineGenerator(i));
  }

  for (var i = 0; i < _yScale.length; i++) {
    // ** Recalculate the tickValues so they don't go outside the axis
    reCalculateTickValues(_yAxis[i], _yScale[i]);
    graph.select(".y.ax" + i).transition().duration(1000).call(_yAxis[i]);
  }

}//zoomed()

function resetZoom() {
  //console.log("reset()");

  graph.call(
    zooming[0]
      .x(
      _x.domain(d3.extent(series[0]["UNIT_DATA"], function (d) {
        return d.zeit;
      }))
      //.nice()
    )
      .y(
      _yScale[0]
        .domain(d3.extent(series[0]["UNIT_DATA"], function (d) {
          return d.wert;
        }))
      //.nice()
    )
      .event
  );
}//reset


function getChannelName(channelObj) {
  return (chart.legend.useName) ? channelObj.name : channelObj.code;
}


function drawLines() {
  //console.log("drawLines()");

  series.forEach(function (d, i) {

    graph
      .append("path")
      .attr("d", _lineGenerator(i))
      .attr("stroke", colorscale(i))
      .attr("class", "line ax" + i)
      .attr("id", "channel_" + d.channel.code)
      .attr("clip-path", "url(#clip)")
      .on("click", function (d) {
        console.log("click on line:", d3.select(this).attr("id"));
      });

  });


}// drawLines()

// Draw lines
/**
 * @param index -
 *          index in array of series
 */
function _lineGenerator(index) {
  var yId = series[index]["yAxis"];
  //console.log("lineGenerator() ->", "index:", index, "yId:", yId);

  return d3.svg
    .line()
    .x(function (d) {
      return _x(d.zeit);
    })
    .y(function (d) {
      return _yScale[yId](d.wert);
    })(series[index]["UNIT_DATA"]);
}

//--------------------------------------------------

function create_X_axis() {
  _x = d3.scale
    .linear()
    .range([0, chartWidth])
    .domain(d3.extent(series[0]["UNIT_DATA"], function (d) {
      return d.zeit;
    }))
    .nice()
    //.clamp(true)
  ;

  xAxis = d3.svg.axis().scale(_x).orient("bottom").ticks(10).tickSize(-chartHeight);
}

activateZoom();

drawLines();

  })(window);

</script>
</body>
</html>
© www.soinside.com 2019 - 2024. All rights reserved.