【问题标题】:D3 Chart SVG Responsive Bars and AxisD3 图表 SVG 响应条和轴
【发布时间】:2018-09-13 16:26:16
【问题描述】:

我已经逐步创建了一个 D3 图表。我进行了一些更改以使 SVG 具有响应性。我现在的目标是使条形图更具响应性,以便在屏幕尺寸较小(宽度)时更易于阅读。我在页面底部和下方粘贴了 sn-p,我将重点放在我认为解决方案被隐藏的部分。

var data = [
    {"area": "one ", "value": 18000},
    {"area": "Two ", "value": 17000},
    {"area": "three ", "value": 80000},
    {"area": "four ", "value": 55000},
    {"area": "five ", "value": 100000},
    {"area": "six", "value": 50000},
    {"area": "seven", "value": 50000}
];
 
  
var margin = {top: 10, right: 10, bottom: 70, left: 30};
var width = 1900 - margin.left - margin.right;
var height = 400 - margin.top - margin.bottom;
          

//A fully-responsive chart area
var svg = d3.select("#chart-div")
                .append("svg")
                .attr("width","100%")
                .attr("height","500px")
                .attr("viewBox","0 0 "+
                    (width+margin.left+margin.right)+
                    " "+
                    (height+margin.top+margin.bottom) )
                .append("g")
                .attr("transform","translate("+
                    margin.left+","+margin.top+")");

var tooltip = d3.select("body").append("div").attr("class", "toolTip");
  
var x = d3.scaleLinear().range([0, width]);
var y = d3.scaleBand().range([height, 0]);

var g = svg.append("g")
		.attr("transform", "translate(" + margin.left + "," + margin.top + ")");


  	data.sort(function(a, b) { return a.value - b.value; });
  
  	x.domain([0, d3.max(data, function(d) { return d.value; })]);
    y.domain(data.map(function(d) { return d.area; })).padding(0.1);

    g.append("g")
        .attr("class", "x axis")
       	.attr("transform", "translate(0," + height + ")")
      	.call(d3.axisBottom(x).ticks(5).tickFormat(function(d) { return parseInt(d / 1000); }).tickSizeInner([-height]));

    g.append("g")
        .attr("class", "y axis")
        .call(d3.axisLeft(y));

    g.selectAll(".bar")
        .data(data)
      .enter().append("rect")
        .attr("class", "bar")
        .attr("x", 0)
        .attr("height", y.bandwidth())
        .attr("y", function(d) { return y(d.area); })
        .attr("width", function(d) { return x(d.value); })
        .on("mousemove", function(d){
            tooltip
              .style("left", d3.event.pageX - 50 + "px")
              .style("top", d3.event.pageY - 70 + "px")
              .style("display", "inline-block")
              .html((d.area) + "<br>" + "£" + (d.value));
        })
    		.on("mouseout", function(d){ tooltip.style("display", "none");});
@import url('https://fonts.googleapis.com/css?family=Roboto');

body {
  margin: 15px;
  background-color: #F1F3F3;
  font-family: 'Roboto'!important;
}
.bar {
	fill: #6F257F;
}
.axis path,
.axis line {
  fill: none;
  stroke: #D4D8DA;
  stroke-width: 1px;
  shape-rendering: crispEdges;
}
.x path {
	display: none;
}
.toolTip {
	position: absolute;
  display: none;
  min-width: 80px;
  height: auto;
  background: none repeat scroll 0 0 #ffffff;
  border: 1px solid #6F257F;
  padding: 14px;
  text-align: center;
}



.svg-container {
    display: inline-block;
    position: relative;
    width: 100%;
    padding-bottom: 100%; /* aspect ratio */
    vertical-align: top;
    overflow: hidden;
}
.svg-content-responsive {
    display: inline-block;
    position: absolute;
    top: 10px;
    left: 0;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<div id="chart-div" style="width:100%;height:100%;"></div>

我用以下代码更改了部分代码:

var parentwidth = $("#chart-div").parent().width(); 
var margin = {top: 10, right: 10, bottom: 70, left: 30};
var width = parentwidth - margin.left - margin.right;
var height = 400 - margin.top - margin.bottom;

我实际上是使用 jQuery 获得 parentwidth 的地方。

我实际上在考虑是否:

a) 在这种情况下可以避免使用 jQuery。

b) 理想情况下,让条形图以不同的方式缩放,以便用户轻松阅读所有内容(小文本大小是个问题):

我正在测试下面的函数,但我可能会遇到与某些 chrome 插件相关的错误,以避免跨域错误。如果以下是最佳解决方案,我可以更新问题:

function resize() {
  var width = parseInt(d3.select("#chart").style("width")) - margin.left - margin.right,
  height = parseInt(d3.select("#chart").style("height")) - margin.top - margin.bottom;

  // Update the range of the scale with new width/height
  xScale.range([0, width]);
  yScale.rangeRoundBands([height, 0], 0.1);

  // Update the axis and text with the new scale
  svg.select(".x.axis")
    .call(xAxis)
    .attr("transform", "translate(0," + height + ")")
    .select(".label")
      .attr("transform", "translate(" + width / 2 + "," + margin.bottom / 1.5 + ")");

  svg.select(".y.axis")
    .call(yAxis);

  // Update the tick marks
  xAxis.ticks(Math.max(width/75, 2), " $");

  // Force D3 to recalculate and update the line
  svg.selectAll(".bar")
    .attr("width", function(d) { return xScale(d["total"]); })
    .attr("y", function(d) { return yScale(d["Name"]); })
    .attr("height", yScale.rangeBand());
};

【问题讨论】:

    标签: javascript jquery html css d3.js


    【解决方案1】:

    你的问题和this one基本一样。

    TL;DR 是:给你的&lt;svg&gt; 一个viewBox 属性和一个preserveAspectRatio(例如xMinYMin meet)属性。然后将&lt;svg&gt; 包裹在具有position: relative&lt;div&gt; 中。

    这不是唯一的解决方案,但它可能是最容易实现并且(我认为)使用最多的解决方案。

    有关其他几种解决方案的概述和讨论,请参阅this article by Amelia Bellamy-Royds

    另外,关于 SVG 坐标系的深入解释,请阅读this series of articles by Sara Soueidan

    关于你对jQuery和手机上文字太小的疑问:

    a) 你可以完全避免使用 jQuery

    b) 您可以通过“反缩放”文本来避免文本太小,即当您的整个条形图(即条形图、轴、标签)缩小时,文本 放大。这有时称为粘性文本。你可以看到example here

    【讨论】:

      【解决方案2】:

      我已更改我的脚本以使其正常工作。请看下面的sn-p:

      var margin = {top: 20, right: 20, bottom: 50, left: 100},
          width = parseInt(d3.select("#chart").style("width")) - margin.left - margin.right,
          height = parseInt(d3.select("#chart").style("height")) - margin.top - margin.bottom;
      
      var yScale = d3.scale.ordinal()
          .rangeRoundBands([height, 0], 0.1);
      
      var xScale = d3.scale.linear()
          .range([0, width]);
      
      var dollarFormatter = d3.format(",.0f")
      
      var yAxis = d3.svg.axis()
          .scale(yScale)
          .orient("left");
      
      var xAxis = d3.svg.axis()
          .scale(xScale)
          .orient("bottom")
          .tickFormat(function(d) { return "$" + dollarFormatter(d);});
      
      var svg = d3.select("#chart")
          .attr("width", width + margin.left + margin.right)
          .attr("height", height + margin.top + margin.bottom)
        .append("g")
          .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
      
      var tip = d3.tip()
          .attr('class', 'd3-tip')
          .offset([-10, 0])
          .html(function(d) {
            return "<div><span>Name:</span> <span style='color:white'>" + d.Name + "</span></div>" +
                    "<div><span>Sub-Category:</span> <span style='color:white'>" + d["Sub-Category"] + "</span></div>" +
                   "<div><span>Total Sales:</span> <span style='color:white'>" + "$"+ dollarFormatter(d.total) + "</span></div>";
          })
      
      svg.call(tip);
      //Get CSV, JSON from URL
      //var url = "http://bl.ocks.org/josiahdavis/raw/7d84b2f1837eab9c24d9/top.csv";
      
      
      var data = [
        {
          "metric": "Sales",
          "Category": "Furniture",
          "Sub-Category": "Bookcases",
          "Name": "Tom Stivers",
          "total": 1889.8,
          "Type": "Customer"
        },
        {
          "metric": "Sales",
          "Category": "Furniture",
          "Sub-Category": "Bookcases",
          "Name": "Keith Herrera",
          "total": 2020.161,
          "Type": "Customer"
        },
        {
          "metric": "Sales",
          "Category": "Furniture",
          "Sub-Category": "Bookcases",
          "Name": "Jack O'Briant",
          "total": 2122.545,
          "Type": "Customer"
        },
        {
          "metric": "Sales",
          "Category": "Furniture",
          "Sub-Category": "Bookcases",
          "Name": "Nora Paige",
          "total": 2154.9,
          "Type": "Customer"
        },
        {
          "metric": "Sales",
          "Category": "Furniture",
          "Sub-Category": "Bookcases",
          "Name": "Anna Gayman",
          "total": 2396.2656,
          "Type": "Customer"
        },
        {
          "metric": "Sales",
          "Category": "Furniture",
          "Sub-Category": "Bookcases",
          "Name": "Tracy Blumstein",
          "total": 3083.43,
          "Type": "Customer"
        },
        {
          "metric": "Sales",
          "Category": "Furniture",
          "Sub-Category": "Bookcases",
          "Name": "Maribeth Schnelling",
          "total": 3406.664,
          "Type": "Customer"
        },
        {
          "metric": "Sales",
          "Category": "Furniture",
          "Sub-Category": "Bookcases",
          "Name": "Greg Tran",
          "total": 4007.84,
          "Type": "Customer"
        },
        {
          "metric": "Sales",
          "Category": "Furniture",
          "Sub-Category": "Bookcases",
          "Name": "Quincy Jones",
          "total": 4404.9,
          "Type": "Customer"
        },
        {
          "metric": "Sales",
          "Category": "Furniture",
          "Sub-Category": "Bookcases",
          "Name": "Peter Fuller",
          "total": 6232.624,
          "Type": "Customer"
        }];
      
      
      //d3.csv(url, format, function(error, data){
        //if (error) throw error;
      
        // Filter to select a subset
        var subset = data.filter(function(el){
          return  (el["metric"] === "Sales")
                  && (el["Sub-Category"] === "Bookcases")
                  && (el["Type"] === "Customer");
        });
      
        // Sort the data so bar chart is sorted in decreasing order
        subset = subset.sort(function(a, b) { return a["total"] - b["total"]; });
        console.log(JSON.stringify(subset, null, 2));
      
        yScale.domain(subset.map(function(d) { return d["Name"]; }));
        xScale.domain([0, d3.max(subset, function(d) { return d["total"]; })]);
      
        svg.append("g")
            .attr("class", "y axis")
            .call(yAxis);
      
        svg.append("g")
            .attr("class", "x axis")
            .call(xAxis)
            .attr("transform", "translate(0," + height + ")")
          .append("text")
            .attr("class", "label")
            .attr("transform", "translate(" + width / 2 + "," + margin.bottom / 1.5 + ")")
            .style("text-anchor", "middle")
            .text("Sales");
      
        svg.selectAll(".bar")
            .data(subset)
          .enter().append("rect")
            .attr("class", "bar")
            .attr("width", function(d) { return xScale(d["total"]); })
            .attr("y", function(d) { return yScale(d["Name"]); })
            .attr("height", yScale.rangeBand())
            .on('mouseover', tip.show)
            .on('mouseout', tip.hide);;
      
      //});
      
      // Define responsive behavior
      function resize() {
        var width = parseInt(d3.select("#chart").style("width")) - margin.left - margin.right,
        height = parseInt(d3.select("#chart").style("height")) - margin.top - margin.bottom;
      
        // Update the range of the scale with new width/height
        xScale.range([0, width]);
        yScale.rangeRoundBands([height, 0], 0.1);
      
        // Update the axis and text with the new scale
        svg.select(".x.axis")
          .call(xAxis)
          .attr("transform", "translate(0," + height + ")")
          .select(".label")
            .attr("transform", "translate(" + width / 2 + "," + margin.bottom / 1.5 + ")");
      
        svg.select(".y.axis")
          .call(yAxis);
      
        // Update the tick marks
        xAxis.ticks(Math.max(width/75, 2), " $");
      
        // Force D3 to recalculate and update the line
        svg.selectAll(".bar")
          .attr("width", function(d) { return xScale(d["total"]); })
          .attr("y", function(d) { return yScale(d["Name"]); })
          .attr("height", yScale.rangeBand());
      };
      
      // Call the resize function whenever a resize event occurs
      d3.select(window).on('resize', resize);
      
      // Call the resize function
      resize();
      
      // Define the format function
      function format(d) {
        d.total = +d.total;
        return d;
      }
      @import url('https://fonts.googleapis.com/css?family=Roboto');
      
      body {
        margin: 5px;
        background-color: #F1F3F3;
        font-family: 'Roboto'!important;
      }
      
      .bar {
        fill: #14405F;
      }
      
      .bar:hover {
        fill: #33A1EE;
      }
      
      .axis {
        font: 10px sans-serif;
      }
      
      .axis path,
      .axis line {
        fill: none;
        stroke: #D4D8DA;
        stroke-width: 1px;
        shape-rendering: crispEdges;
      }
      .x path {
      	display: none;
      }
      
      #chart {
        width: 100%;
        height: 100%;
        position: absolute;
      }
      
      .d3-tip {
        line-height: 1;
        font: 14px sans-serif;
        padding: 12px;
        background: rgba(0, 0, 0, 0.8);
        color: rgb(185, 185, 185);
        border-radius: 2px;
      }
      
      /*
      .toolTip {
      	position: absolute;
        display: none;
        min-width: 80px;
        height: auto;
        background: none repeat scroll 0 0 #ffffff;
        border: 1px solid #6F257F;
        padding: 14px;
        text-align: center;
      }
      */
      <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
      <script src="https://labratrevenge.com/d3-tip/javascripts/d3.tip.v0.6.3.js"></script>
      
      
      <svg id="chart"></svg>

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-04-24
        • 1970-01-01
        • 1970-01-01
        • 2013-07-11
        • 1970-01-01
        • 2014-07-07
        • 1970-01-01
        相关资源
        最近更新 更多