Диаграмма d3 адаптивные полосы и оси 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;

где я на самом деле получаю parentwidth с помощью jQuery.

Я действительно думаю:

а) В этом случае можно избежать использования jQuery.

б) В идеале сделайте шкалу шкалы по-другому, чтобы пользователь мог все легко прочитать (небольшой размер текста является проблемой): 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());
};
0
0
1 089
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Ваш вопрос в основном такой же, как Вот этот.

TL; DR: присвойте вашему <svg> атрибут viewBox и атрибут preserveAspectRatio (например, xMinYMin meet). Затем оберните <svg> в <div> с position: relative.

Это не единственное решение, но, вероятно, оно является самым простым в реализации и (я думаю) наиболее часто используемым.

Для обзора и обсуждения некоторых других решений см. эта статья Амелии Беллами-Ройдс.

Кроме того, для более подробного объяснения системы координат SVG прочтите эта серия статей Сары Суейдан.

Что касается ваших сомнений в том, что jQuery и текст слишком мал на мобильных телефонах:

а) вы можете полностью избежать jQuery

б) вы можете избежать слишком маленького размера текста, "противодействуя" масштабированию текста, а именно, когда ваш вся гистограмма (т.е. столбцы, ось, метки) масштабируется вниз, ваш текст масштабируется вверх. Иногда это называется липкий текст. Вы можете увидеть пример здесь.

Я изменил свой сценарий, чтобы он работал. См. Фрагмент ниже:

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>

Другие вопросы по теме