D3 Chart SVG响应条和轴

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了D3 Chart SVG响应条和轴相关的知识,希望对你有一定的参考价值。

我已经逐步创建了D3图表。我做了一些更改以使SVG响应。我现在的目标是使条形图更具响应性,以便在屏幕尺寸较小(宽度)时更容易阅读。我已经粘贴了下面和页面底部的片段,我专注于我认为解决方案被隐藏的部分。

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)理想情况下,使条形按比例缩放,以便用户可以轻松读取所有内容(小文本大小是个问题):enter image description here

我正在测试下面的函数,但我可能会收到与某些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());
;
答案

你的问题与this one基本相同。

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

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

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

另外,有关SVG坐标系的深入说明,请阅读this series of articles by Sara Soueidan

至于你对jQuery的怀疑和手机上的文字太小:

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

b)通过“反缩放”文本可以避免文本太小,即当整个条形图(即条形图,轴,标签)缩小时,文本会向上扩展。这有时被称为粘性文本。你可以看到一个example here

另一答案

我已经改变了我的脚本以使其工作。请参阅下面的代码:

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/

以上是关于D3 Chart SVG响应条和轴的主要内容,如果未能解决你的问题,请参考以下文章

D3 在调整大小窗口上更改 SVG 尺寸

响应式 D3 图表

响应式 D3 js 图表 [重复]

使 d3.js 可视化布局响应的最佳方法是啥?

条形高度占据 viewBox 响应式 SVG 的整个高度

如何使 D3.js 中的强制布局图响应屏幕/浏览器大小