D3将条件格式应用于线元素

发布于 2025-02-12 05:23:56 字数 2901 浏览 1 评论 0原文

我的数据集看起来像这样:

var data = [{
    "Date": "31/12/2019",
    "Value": 23,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "31/01/2020",
    "Value": 49,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "29/02/2020",
    "Value": 23,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "31/03/2020",
    "Value": 12,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "30/04/2020",
    "Value": 33,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "31/05/2020",
    "Value": 62,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "30/06/2020",
    "Value": 65,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "31/07/2020",
    "Value": 77,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "31/08/2020",
    "Value": 65,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "30/09/2020",
    "Value": 58,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "31/10/2020",
    "Value": 87,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "30/11/2020",
    "Value": 120,
    "Type": "Actual",
    "Plan": 100,
    "Over": "Y"
  },
  {
    "Date": "31/12/2020",
    "Value": 103,
    "Type": "Fcst",
    "Plan": 100,
    "Over": "Y"
  },
  {
    "Date": "31/01/2021",
    "Value": 110,
    "Type": "Fcst",
    "Plan": 100,
    "Over": "Y"
  },
  {
    "Date": "28/02/2021",
    "Value": 117,
    "Type": "Fcst",
    "Plan": 100,
    "Over": "Y"
  }
]

我正在尝试绘制具有以下格式的线图:

d.type ===“实际”然后将行显示为 solid and 蓝色

d.type ===“ fcst”将行显示为 dashed stroke> stroke-dasharray ) 和绿色

至关重要的是我要实现的目标:

”在此处输入图像描述”

...这就是我所拥有的far:

“

这是我尝试添加条件燃料的方法:

//add Trend Line
svg.append('path')
  .datum(data)
  .attr('class', 'data-line glowed')
  .style('stroke', function(d, i) {
    if (d.Type == "Fcst")
      return "#00D4B2"
    else {
      return "#3087CD"
    }
  })
  .style('stroke-width', 2)
  .style("stroke-dasharray",function(d, i) {
    if (d.Type == "Fcst")
      return ("3, 1"); else {
      return ("0, 0")
    }
  })
  .style('fill', 'none')
  .attr('d', line)

这是我的 jsfiddle

My dataset looks like this:

var data = [{
    "Date": "31/12/2019",
    "Value": 23,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "31/01/2020",
    "Value": 49,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "29/02/2020",
    "Value": 23,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "31/03/2020",
    "Value": 12,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "30/04/2020",
    "Value": 33,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "31/05/2020",
    "Value": 62,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "30/06/2020",
    "Value": 65,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "31/07/2020",
    "Value": 77,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "31/08/2020",
    "Value": 65,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "30/09/2020",
    "Value": 58,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "31/10/2020",
    "Value": 87,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "30/11/2020",
    "Value": 120,
    "Type": "Actual",
    "Plan": 100,
    "Over": "Y"
  },
  {
    "Date": "31/12/2020",
    "Value": 103,
    "Type": "Fcst",
    "Plan": 100,
    "Over": "Y"
  },
  {
    "Date": "31/01/2021",
    "Value": 110,
    "Type": "Fcst",
    "Plan": 100,
    "Over": "Y"
  },
  {
    "Date": "28/02/2021",
    "Value": 117,
    "Type": "Fcst",
    "Plan": 100,
    "Over": "Y"
  }
]

I'm trying to plot a line chart that has the following formatting:

When d.Type === "Actual" then show the line as SOLID and BLUE

When d.Type === "Fcst" show the line as dashed (stroke-dasharray) and GREEN

Essential this is what I'm trying to achieve:

enter image description here

...and this is what I have so far:

enter image description here

This is what I tried doing to add the conditional fomatting:

//add Trend Line
svg.append('path')
  .datum(data)
  .attr('class', 'data-line glowed')
  .style('stroke', function(d, i) {
    if (d.Type == "Fcst")
      return "#00D4B2"
    else {
      return "#3087CD"
    }
  })
  .style('stroke-width', 2)
  .style("stroke-dasharray",function(d, i) {
    if (d.Type == "Fcst")
      return ("3, 1"); else {
      return ("0, 0")
    }
  })
  .style('fill', 'none')
  .attr('d', line)

Here is my JSFIDDLE

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

Smile简单爱 2025-02-19 05:23:56

因为您使用datum您不在行的每个点上迭代 - 相反,整个路径是由line生成的,并且是一个元素,只能接受一组样式。

下面的方法绘制了3行(一条透明)相互覆盖。较短的“实际”线位于“预测”线上。您需要这样做才能获得良好的曲率(我使用d3.curvemonotonex)。

步骤:

  1. 绘制“实际”数据的行(type),但
  2. 通过 getTotallength()
  3. 绘制全线长度(例如虚线绿色)的'预测'行
  4. 再次绘制“实际”行,这次是蓝色的,也设置 stroke-dasharray ,使其具有一个破折号(从步骤(2)中计算的长度开始),没有重复的破折号,因此原始'''''预测“线路”通过虚线的单个“差距”显示。

请参阅下面的工作示例并检查评论:

// get actual data points only
const actualData = data.filter(d => d.Type === "Actual");

// draw actual line but transparent 
const actual = svg.append("g")
  .append("path")
  .datum(actualData)
  .attr("d", line)
  .style("fill", "none")
  .style("opacity", 0)
  .style("stroke-width", lineWidth);

// get length of actual curve
const actualLength = actual.node().getTotalLength();

// draw all data points in dashed green - this will be forecast
const allPoints = svg.append("g")
  .append("path")
  .datum(data)
  .attr("d", line)
  .style("fill", "none")
  .style("stroke", "#00ee22")
  .style("stroke-width", lineWidth)
  .style("stroke-dasharray", "4, 2");

// draw full line in blue but only to actual end point with stroke-dasharray
const forecast = svg.append("g")
  .append("path")
  .datum(data)
  .attr("d", line)
  .style("fill", "none")
  .style("stroke", "#2222ee")
  .style("stroke-width", lineWidth)
  .style("stroke-dasharray", `${actualLength}`);
  
// add points on line etc
const points = svg.selectAll(".label")
  .data(data)
  .enter()
  .append("circle")
  .attr("cx", d => xScale(parseTime(d.Date)))
  .attr("cy", d => yScale(d.Value))
  .attr("r", 4)
  .attr("fill", "#2288ee")
  .attr("stroke", "#22ccee")  
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<script>
var data = [{
    "Date": "31/12/2019",
    "Value": 23,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "31/01/2020",
    "Value": 49,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "29/02/2020",
    "Value": 23,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "31/03/2020",
    "Value": 12,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "30/04/2020",
    "Value": 33,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "31/05/2020",
    "Value": 62,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "30/06/2020",
    "Value": 65,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "31/07/2020",
    "Value": 77,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "31/08/2020",
    "Value": 65,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "30/09/2020",
    "Value": 58,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "31/10/2020",
    "Value": 87,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "30/11/2020",
    "Value": 120,
    "Type": "Actual",
    "Plan": 100,
    "Over": "Y"
  },
  {
    "Date": "31/12/2020",
    "Value": 103,
    "Type": "Fcst",
    "Plan": 100,
    "Over": "Y"
  },
  {
    "Date": "31/01/2021",
    "Value": 110,
    "Type": "Fcst",
    "Plan": 100,
    "Over": "Y"
  },
  {
    "Date": "28/02/2021",
    "Value": 117,
    "Type": "Fcst",
    "Plan": 100,
    "Over": "Y"
  }
];

// basic line chart setup
const innerWidth = 520;
const innerHeight = 160;
const margin = {top: 5, bottom: 25, left: 15, right: 5}
const lineWidth = 3;
const svg = d3.select("body")
  .append("svg")
  .attr("width", innerWidth + margin.left + margin.right)
  .attr("height", innerHeight + margin.top + margin.bottom)
  .append("g")
  .attr("transform", `translate(${margin.left}, ${margin.top})`);
  
const yScale = d3.scaleLinear()
  .range([innerHeight, 0])
  .domain([0, d3.max(data, d => d.Value) + 20]);

const yAxis = svg.append("g")
  .attr("transform", `translate(${margin.left},0)`)
  .call(d3.axisLeft(yScale).ticks(5));
  
const parseTime = d3.timeParse("%d/%m/%Y")
const dates = data.map(d => parseTime(d.Date));
const xScale = d3.scaleTime()
  .range([0, innerWidth])
  .domain(d3.extent(dates))
  .nice();
  
const xAxis = svg.append("g")
  .attr("transform", `translate(${margin.left}, ${innerHeight})`)
  .call(d3.axisBottom(xScale)
    .tickFormat(d3.timeFormat('%b'))
  );
  
const line = d3.line()
  .x(d => xScale(parseTime(d.Date)))
  .y(d => yScale(d.Value))
  .curve(d3.curveMonotoneX);

</script>

Because you use datum you don't iterate over each point of the line - instead the entire path is generated by line and is a single element and can only accept one set of styles.

The approach below draws 3 lines (one transparent) overlaid to one another. The shorter 'actual' line is on top of the 'forecast' line. You need to do it this way to get the nice curvature (I use d3.curveMonotoneX).

The steps:

  1. Draw the line for 'actual' data (per filter on Type) but make it transparent
  2. Calculate the length of this 'actual' line from step (1) with getTotalLength()
  3. Draw the 'forecast' line for the full length of line (e.g. dashed green)
  4. Draw the 'actual' line again, this time in blue, and also set stroke-dasharray such that it has one dash (from start for length calculated in step (2)) and no repeating dash so the end of the original 'forecast' line shows through this single 'gap' of the dashed line.

See working example below and check the comments:

// get actual data points only
const actualData = data.filter(d => d.Type === "Actual");

// draw actual line but transparent 
const actual = svg.append("g")
  .append("path")
  .datum(actualData)
  .attr("d", line)
  .style("fill", "none")
  .style("opacity", 0)
  .style("stroke-width", lineWidth);

// get length of actual curve
const actualLength = actual.node().getTotalLength();

// draw all data points in dashed green - this will be forecast
const allPoints = svg.append("g")
  .append("path")
  .datum(data)
  .attr("d", line)
  .style("fill", "none")
  .style("stroke", "#00ee22")
  .style("stroke-width", lineWidth)
  .style("stroke-dasharray", "4, 2");

// draw full line in blue but only to actual end point with stroke-dasharray
const forecast = svg.append("g")
  .append("path")
  .datum(data)
  .attr("d", line)
  .style("fill", "none")
  .style("stroke", "#2222ee")
  .style("stroke-width", lineWidth)
  .style("stroke-dasharray", `${actualLength}`);
  
// add points on line etc
const points = svg.selectAll(".label")
  .data(data)
  .enter()
  .append("circle")
  .attr("cx", d => xScale(parseTime(d.Date)))
  .attr("cy", d => yScale(d.Value))
  .attr("r", 4)
  .attr("fill", "#2288ee")
  .attr("stroke", "#22ccee")  
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<script>
var data = [{
    "Date": "31/12/2019",
    "Value": 23,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "31/01/2020",
    "Value": 49,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "29/02/2020",
    "Value": 23,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "31/03/2020",
    "Value": 12,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "30/04/2020",
    "Value": 33,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "31/05/2020",
    "Value": 62,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "30/06/2020",
    "Value": 65,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "31/07/2020",
    "Value": 77,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "31/08/2020",
    "Value": 65,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "30/09/2020",
    "Value": 58,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "31/10/2020",
    "Value": 87,
    "Type": "Actual",
    "Plan": 100,
    "Over": ""
  },
  {
    "Date": "30/11/2020",
    "Value": 120,
    "Type": "Actual",
    "Plan": 100,
    "Over": "Y"
  },
  {
    "Date": "31/12/2020",
    "Value": 103,
    "Type": "Fcst",
    "Plan": 100,
    "Over": "Y"
  },
  {
    "Date": "31/01/2021",
    "Value": 110,
    "Type": "Fcst",
    "Plan": 100,
    "Over": "Y"
  },
  {
    "Date": "28/02/2021",
    "Value": 117,
    "Type": "Fcst",
    "Plan": 100,
    "Over": "Y"
  }
];

// basic line chart setup
const innerWidth = 520;
const innerHeight = 160;
const margin = {top: 5, bottom: 25, left: 15, right: 5}
const lineWidth = 3;
const svg = d3.select("body")
  .append("svg")
  .attr("width", innerWidth + margin.left + margin.right)
  .attr("height", innerHeight + margin.top + margin.bottom)
  .append("g")
  .attr("transform", `translate(${margin.left}, ${margin.top})`);
  
const yScale = d3.scaleLinear()
  .range([innerHeight, 0])
  .domain([0, d3.max(data, d => d.Value) + 20]);

const yAxis = svg.append("g")
  .attr("transform", `translate(${margin.left},0)`)
  .call(d3.axisLeft(yScale).ticks(5));
  
const parseTime = d3.timeParse("%d/%m/%Y")
const dates = data.map(d => parseTime(d.Date));
const xScale = d3.scaleTime()
  .range([0, innerWidth])
  .domain(d3.extent(dates))
  .nice();
  
const xAxis = svg.append("g")
  .attr("transform", `translate(${margin.left}, ${innerHeight})`)
  .call(d3.axisBottom(xScale)
    .tickFormat(d3.timeFormat('%b'))
  );
  
const line = d3.line()
  .x(d => xScale(parseTime(d.Date)))
  .y(d => yScale(d.Value))
  .curve(d3.curveMonotoneX);

</script>

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文