Заполнение пути D3 дает странный результат

#svg #d3.js

Вопрос:

Попытался нарисовать синусоиду с положительными и отрицательными частями разного цвета, используя path элемент и fill штрих, но получил странный/сбивающий с толку результат.

  • С 99 очками введите описание изображения здесь
  • С 399 очками введите описание изображения здесь

Вот код 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 это работает, спасибо.