#svg #d3.js
Вопрос:
Попытался нарисовать синусоиду с положительными и отрицательными частями разного цвета, используя path
элемент и fill
штрих, но получил странный/сбивающий с толку результат.
Вот код D3:
window.onload = function(){ let xy_values =[]; let xy_values2 =[]; let len = 99; //Number of points let dt = 8*Math.PI/len; //X-Distance between points for (var i =0; ilt;len; i ){ let valx = i*dt; let valy = Math.sin(valx); if(valy gt; 0){ // Positive sine xy_values.push( {key: valx, value: valy} ); xy_values2.push( {key: valx, value: 0.0} ); }else{ //Negative sine xy_values.push( {key: valx, value: 0.0} ); xy_values2.push( {key: valx, value: valy} ); } } draw(xy_values, xy_values2); } function draw(cdata, ddata){ var height=200; var width=800; let svg=d3.select("#container").append("svg").attr("width", width).attr("height", height); walkX = d3.scaleLinear() .domain([0, 30]) .range([40, width ]) walkY = d3.scaleLinear() .domain([-1, 1]) .range([height-10, 10]) // Add the green regions svg.append("path") .datum(cdata) .attr("fill", "#BFB") .attr("stroke", "none") .attr("stroke-width", 1.5) .attr("d", d3.line() .x(function(d) { return walkX(d.key) }) .y(function(d) { return walkY(d.value) }) ); // Add the red regions svg.append("path") .datum(ddata) .attr("fill", "#FBB") .attr("stroke", "none") .attr("stroke-width", 1.5) .attr("d", d3.line() .x(function(d) { return walkX(d.key) }) .y(function(d) { return walkY(d.value) }) ); // Add scatter points svg.append('g') .selectAll("dot") .data(ddata) .enter() .append("circle") .attr("cx", function (d) { return walkX(d.key); } ) .attr("cy", function (d) { return walkY(d.value); } ) .attr("r", 1.5) .style("fill", "#69b3a2") svg.append("g") .attr("transform", "translate(" 0 "," 10 ")") .call(d3.axisBottom(walkX)); svg.append("g") .attr("transform", "translate(" 0 "," (height-10) ")") .call(d3.axisBottom(walkX)); svg.append("g") .attr("transform", "translate(" 0 "," (height/2) ")") .call(d3.axisBottom(walkX)); svg.append("g") .attr("transform", "translate(" 35 "," 0 ")") .call(d3.axisLeft(walkY)); }
Вопросы
Что вызывает это неправильное заполнение/ наклонное заполнение ?
Это из-за какой-то проблемы с точностью ? или это сбой в том, как fill
реализовано в D3 ?
Ответ №1:
Это из-за какой-то проблемы с точностью ? или это сбой в том, как заполнение реализовано в D3 ?
Нет, D3 используется только для создания ваших SVG-элементов в DOM, а не для их визуализации, проблема в том, как применяются заливки SVG. При применении заливки к элементу пути заливка соединяет последнюю точку пути с первой, поэтому вы видите этот эффект. Вы увидите, что эффект, который вы пытаетесь удалить, образует линию между этими двумя точками. Браузер при рендеринге SVG не имеет никаких инструкций для придания заливке другой формы.
Один из вариантов исправить это-использовать генератор областей, а не генератор линий, который позволяет устанавливать базовую линию.
Генератор областей D3 создает путь с двумя значениями y для каждого значения x (указывая область, которая должна быть заполнена, а не путь), в этом случае один в базовой линии (y0) и один в значении данных (y1):
d3.area() .x(function(d) { return walkX(d.key) }) .y1(function(d) { return walkY(d.value) }) .y0(function() { return walkY(0) })
window.onload = function(){ let xy_values =[]; let xy_values2 =[]; let len = 99; //Number of points let dt = 8*Math.PI/len; //X-Distance between points for (var i =0; ilt;len; i ){ let valx = i*dt; let valy = Math.sin(valx); if(valy gt; 0){ // Positive sine xy_values.push( {key: valx, value: valy} ); xy_values2.push( {key: valx, value: 0.0} ); }else{ //Negative sine xy_values.push( {key: valx, value: 0.0} ); xy_values2.push( {key: valx, value: valy} ); } } draw(xy_values, xy_values2); } function draw(cdata, ddata){ var height=200; var width=800; let svg=d3.select("#container").append("svg").attr("width", width).attr("height", height); walkX = d3.scaleLinear() .domain([0, 30]) .range([40, width ]) walkY = d3.scaleLinear() .domain([-1, 1]) .range([height-10, 10]) // Add the green regions svg.append("path") .datum(cdata) .attr("fill", "#BFB") .attr("stroke", "none") .attr("stroke-width", 1.5) .attr("d", d3.area() .x(function(d) { return walkX(d.key) }) .y1(function(d) { return walkY(d.value) }) .y0(function() { return walkY(0) }) ); // Add the red regions svg.append("path") .datum(ddata) .attr("fill", "#FBB") .attr("stroke", "none") .attr("stroke-width", 1.5) .attr("d", d3.area() .x(function(d) { return walkX(d.key) }) .y1(function(d) { return walkY(d.value) }) .y0(function() { return walkY(0) }) ); // Add scatter points svg.append('g') .selectAll("dot") .data(ddata) .enter() .append("circle") .attr("cx", function (d) { return walkX(d.key); } ) .attr("cy", function (d) { return walkY(d.value); } ) .attr("r", 1.5) .style("fill", "#69b3a2") svg.append("g") .attr("transform", "translate(" 0 "," 10 ")") .call(d3.axisBottom(walkX)); svg.append("g") .attr("transform", "translate(" 0 "," (height-10) ")") .call(d3.axisBottom(walkX)); svg.append("g") .attr("transform", "translate(" 0 "," (height/2) ")") .call(d3.axisBottom(walkX)); svg.append("g") .attr("transform", "translate(" 35 "," 0 ")") .call(d3.axisLeft(walkY)); }
lt;script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.1.0/d3.min.js"gt;lt;/scriptgt; lt;div id="container"gt;lt;/divgt;
Что дает нам:
Комментарии:
1. Очень Интересно. Один вопрос : почему наклон не происходит для верхней области (зеленым цветом) ?
2. Поскольку последняя точка достаточно близка к нулю (или равна нулю), что первая и последняя точки на верхнем пути не имеют изменений или заметного изменения значения y: ребро, соединяющее первую и последнюю точки, почти перекрывает ось при y=0.
3. В этом случае, если я добавлю точку (x_last, 0), то
fill
она будет отображаться правильно. Так вот какfill
это работает, спасибо.