Как создать линейный график с вертикальной линией и разными фонами? - PullRequest
0 голосов
/ 10 октября 2018

Мне нужно создать линейную диаграмму с вертикальной линией, которая показывает границу.Левая сторона должна быть окрашена в красный, правая в зеленый соответственно:

Linear chart which I want to build

Я начал искать примеры в библиотеках, но примеров нет... Я сделал сходство на D3.Но я смог нарисовать только график и вертикальную линию, и я не могу заполнить детали и отобразить легенду.Подскажите пожалуйста, как создать этот тип расписания?Существуют ли уже существующие решения для реализации?Мой код, в котором области не закрашены и нет легенды.

// https://plnkr.co/edit/9tnP6l15dqmCaOAK8Ojm?p=preview

function drawChart(data) {

var svgWidth = 600, 
    svgHeight = 400;

var margin = { top: 20, right: 20, bottom: 30, left: 50 };

var width = svgWidth - margin.left - margin.right;
var height = svgHeight - margin.top - margin.bottom;

var svg = d3.select('svg')
    .attr("width", svgWidth)
    .attr("height", svgHeight);

// Grouping element with margins
var g = svg.append("g")
    .attr("transform", 
    "translate(" + margin.left + "," + margin.top + ")"
);

var x = d3.scaleLinear().rangeRound([0, width]);
var y = d3.scaleLinear().rangeRound([height, 0]);

var line = d3.line()
    .x(function(d) { return x(d.date)})
    .y(function(d) { return y(d.value)})
    x.domain(d3.extent(data, function(d) { return d.date }));
    y.domain(d3.extent(data, function(d) { return d.value }));

g.append("g")
    .attr("transform", "translate(0," + height + ")")
    .call(d3.axisBottom(x))
    .append("text")
    .attr("fill", "#000")
    .attr("transform", "rotate(0)")
    .attr("y", 0)
    .attr("dx", "50em")
    .attr("dy", "-0.5em")
    .attr("text-anchor", "start")
    .text("Title ($)");

// Text for Y axes
g.append("g")
    .call(d3.axisLeft(y))
    .append("text")
    .attr("fill", "#000")
    .attr("transform", "rotate(-90)")
    .attr("y", 6)
    .attr("dy", "0.71em")
    .attr("text-anchor", "end")
    .text("Price ($)");

g.append("path")
    .datum(data)
    .attr("fill", "none")
    .attr("stroke", "steelblue")
    .attr("stroke-linejoin", "round")
    .attr("stroke-linecap", "round")
    .attr("stroke-width", 1)
    .attr("d", line);

g.append("line")
    .attr("x1", 300) 
    .attr("y1", 0)
    .attr("x2", 300)
    .attr("y2", 350)
    .style("stroke-width", 2)
    .style("stroke", "red")
    .style("fill", "black");
}

document.addEventListener("DOMContentLoaded", function(event) {
var parsedData = [{
    date: 0,
    value: 10
 }, {
    date: 0,
    value: 1000
 }, {
    date: 200,
    value: 2400
 }, {
    date: 300,
    value: 4600
 }, {
    date: 400,
    value: 5600
 }, {
    date: 500,
    value: 7777
 }];

drawChart(parsedData);
});

1 Ответ

0 голосов
/ 10 октября 2018

Есть разные способы создать это, например, нарисовать два пути с одинаковыми данными и, например, использовать <clipPath>.

Но я считаю, что самый простой способ сделать это - просто использовать разные данные длякаждый путь.Следуя этому подходу, нам просто нужно установить генератор области ...

var area = d3.area()
    .x(function(d) { return x(d.date)})
    .y1(function(d) { return y(d.value)})
    .y0(y(0));

... и добавить пути, фильтруя данные в соответствии с границей (здесь она называется border):

g.append("path")
    .datum(data.filter(function(d) {
        return d.date <= border;
    }))
    .attr("fill", "red")
    .attr("d", area);

g.append("path")
    .datum(data.filter(function(d) {
        return d.date >= border;
    }))
    .attr("fill", "green")
    .attr("d", area);

Вот демоверсия с вашим кодом:

var data = [{
  date: 0,
  value: 1000
}, {
  date: 200,
  value: 2400
}, {
  date: 300,
  value: 4600
}, {
  date: 400,
  value: 5600
}, {
  date: 500,
  value: 7777
}];

var svgWidth = 600,
  svgHeight = 400;

var margin = {
  top: 20,
  right: 20,
  bottom: 30,
  left: 50
};

var width = svgWidth - margin.left - margin.right;
var height = svgHeight - margin.top - margin.bottom;

var svg = d3.select('svg')
  .attr("width", svgWidth)
  .attr("height", svgHeight);

// Grouping element with margins
var g = svg.append("g")
  .attr("transform",
    "translate(" + margin.left + "," + margin.top + ")"
  );

var x = d3.scaleLinear().rangeRound([0, width]);
var y = d3.scaleLinear().rangeRound([height, 0]);

var border = 300;

var line = d3.line()
  .x(function(d) {
    return x(d.date)
  })
  .y(function(d) {
    return y(d.value)
  });

var area = d3.area()
  .x(function(d) {
    return x(d.date)
  })
  .y1(function(d) {
    return y(d.value)
  })
  .y0(y(0));

x.domain(d3.extent(data, function(d) {
  return d.date
}));
y.domain([0, d3.max(data, function(d) {
  return d.value
})]);

g.append("path")
  .datum(data.filter(function(d) {
    return d.date <= border;
  }))
  .attr("fill", "red")
  .attr("d", area);

g.append("path")
  .datum(data.filter(function(d) {
    return d.date >= border;
  }))
  .attr("fill", "green")
  .attr("d", area);

g.append("path")
  .datum(data)
  .attr("fill", "none")
  .attr("stroke", "steelblue")
  .attr("stroke-linejoin", "round")
  .attr("stroke-linecap", "round")
  .attr("stroke-width", 2)
  .attr("d", line);

g.append("line")
  .attr("x1", x(border))
  .attr("y1", 0)
  .attr("x2", x(border))
  .attr("y2", 350)
  .style("stroke-width", 2)
  .style("stroke", "blue");

g.append("g")
  .attr("transform", "translate(0," + height + ")")
  .call(d3.axisBottom(x))
  .append("text")
  .attr("fill", "#000")
  .attr("transform", "rotate(0)")
  .attr("y", 0)
  .attr("dx", "50em")
  .attr("dy", "-0.5em")
  .attr("text-anchor", "start")
  .text("Title ($)");

// Text for Y axes
g.append("g")
  .call(d3.axisLeft(y))
  .append("text")
  .attr("fill", "#000")
  .attr("transform", "rotate(-90)")
  .attr("y", 6)
  .attr("dy", "0.71em")
  .attr("text-anchor", "end")
  .text("Price ($)");
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg></svg>

Имейте в виду, что этот простой код работает, поскольку в вашем примере предел (равный 300) на самом деле является точкой данных.Если это не так (например, ограничение составляет 280, но две соседние точки данных - 250 и 300), вам потребуется более сложный алгоритм для создания двух отдельных наборов данных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...