如何根据条件用不同颜色填充区域?
How can I fill an area with different colors based on conditions?
嗨,数据可视化爱好者,
Codepen 在这里:https://codepen.io/shanyulin/pen/ZEXNgOb
我正在尝试根据不同的条件填充D3区域的颜色。我有两组数据climate_data
(绿色)和obs_data
(红色),我相应地画了两条线。
我想在两条线之间添加区域,如下所示:
使用以下代码:
this.svg
.append("path")
.datum(this.data)
.attr("transform", this.x_translate)
.attr("fill", this.obs_data_color)
.attr("stroke", "none")
.attr("fill-opacity", opacity)
.attr("stroke-width", 0)
.attr(
"d",
d3
.area()
.curve(curve)
.x((d) => {
return this.x_scale(new Date(d.time));
})
.y0((d) => {
return this.y_scale(d.climate_data);
})
.y1((d) => {
return this.y_scale(d.obs_data);
})
但是我想设置不同的颜色,一种在绿线上方,一种在下方。
我提到了这个postD3 Area fill with different color based on conditions
但输出看起来很奇怪(如红色方块所示):
有谁知道如何解决这个问题?
任何提示将不胜感激。谢谢!
这里是一个使用 clipPaths, based on this difference chart by Mike Bostock.
的例子
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script src="https://d3js.org/d3.v7.js"></script>
</head>
<body>
<div id="chart"></div>
<script>
// set up
const margin = { top: 10, right: 10, bottom: 50, left: 50 };
const width = 500 - margin.left - margin.right;
const height = 300 - margin.top - margin.bottom;
const svg = d3.select('#chart')
.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`);
// data
const parseTime = d3.timeParse('%Y-%m-%d');
const data = [
{ time: "2021-12-16", obs_data: 22.2, climate_data: 18.21 },
{ time: "2021-12-17", obs_data: 18.5, climate_data: 17.59 },
{ time: "2021-12-18", obs_data: 15.4, climate_data: 17.84 },
{ time: "2021-12-19", obs_data: 17.3, climate_data: 17.67 },
{ time: "2021-12-20", obs_data: 19.7, climate_data: 18.31 },
{ time: "2021-12-21", obs_data: 18.6, climate_data: 17.59 },
{ time: "2021-12-22", obs_data: 17.7, climate_data: 17.56 },
{ time: "2021-12-23", obs_data: 20, climate_data: 17.71 },
{ time: "2021-12-24", obs_data: 19.4, climate_data: 17.82 },
{ time: "2021-12-25", obs_data: 16.4, climate_data: 17.7 },
{ time: "2021-12-26", obs_data: 13.9, climate_data: 17.58 },
{ time: "2021-12-27", obs_data: 13.1, climate_data: 17.34 },
{ time: "2021-12-28", obs_data: 16.7, climate_data: 17.13 },
{ time: "2021-12-29", obs_data: 17.8, climate_data: 17.14 },
{ time: "2021-12-30", obs_data: 16, climate_data: 16.81 },
{ time: "2021-12-31", obs_data: 16, climate_data: 15.86 },
{ time: "2022-01-01", obs_data: 16.9, climate_data: 16.37 },
{ time: "2022-01-02", obs_data: 16.9, climate_data: 17.09 },
{ time: "2022-01-03", obs_data: 18.6, climate_data: 17.68 },
{ time: "2022-01-04", obs_data: 18, climate_data: 17.56 },
{ time: "2022-01-05", obs_data: 19.3, climate_data: 17.13 },
{ time: "2022-01-06", obs_data: 16.8, climate_data: 17.3 },
{ time: "2022-01-07", obs_data: 16.1, climate_data: 17.19 },
{ time: "2022-01-08", obs_data: 16.5, climate_data: 16.54 },
{ time: "2022-01-09", obs_data: 17.6, climate_data: 16.3 },
{ time: "2022-01-10", obs_data: 17.4, climate_data: 16.95 },
{ time: "2022-01-11", obs_data: 13.8, climate_data: 17.26 },
{ time: "2022-01-12", obs_data: 13.3, climate_data: 16.63 },
{ time: "2022-01-13", obs_data: 14, climate_data: 16.15 },
{ time: "2022-01-14", obs_data: 15.3, climate_data: 16.15 },
{ time: "2022-01-15", obs_data: 16.9, climate_data: 16.16 }
].map(({time, obs_data, climate_data}) => ({ time: parseTime(time), obs_data, climate_data }));
// scales
const x = d3.scaleTime()
.domain(d3.extent(data, d => d.time))
.range([0, width]);
const y = d3.scaleLinear()
.domain(d3.extent(data.flatMap(d => [d.obs_data, d.climate_data]))).nice()
.range([height, 0]);
// area generators
// from the top of the chart to the line for climate
const topToClimate = d3.area()
.x(d => x(d.time))
.y0(0)
.y1(d => y(d.climate_data))
.curve(d3.curveMonotoneX);
// from the bottom of the chart to the line for climate
const bottomToClimate = d3.area()
.x(d => x(d.time))
.y0(height)
.y1(d => y(d.climate_data))
.curve(d3.curveMonotoneX);
// from the top of the chart to the line for obs
const topToObs = d3.area()
.x(d => x(d.time))
.y0(0)
.y1(d => y(d.obs_data))
.curve(d3.curveMonotoneX);
// from the bottom of the chart to the line for obs
const bottomToObs = d3.area()
.x(d => x(d.time))
.y0(height)
.y1(d => y(d.obs_data))
.curve(d3.curveMonotoneX);
// clip paths
svg.append('clipPath')
.attr('id', 'topToObs')
.append('path')
.attr('d', topToObs(data));
svg.append('clipPath')
.attr('id', 'bottomToObs')
.append('path')
.attr('d', bottomToObs(data));
// areas
// draw a blue area from the bottom of the chart to the blue line for climate.
// the clip path makes any part of this area outside of the clip path invisible.
// the clip path goes from the top of the chart to the red line for obs.
// the result is that you can only see the blue area when it is above the obs
// line and beneath the climate line.
svg.append('path')
.attr('fill', 'blue')
.attr('opacity', 0.6)
.attr('clip-path', 'url(#topToObs)')
.attr('d', bottomToClimate(data));
// draw a red area from the top of the chart to the blue line for climate.
// the clip path makes any part of this area outside of the clip path invisible.
// the clip path goes from the bottom of the chart to the red line for obs.
// the result is that you can only see the read area when it is above the climate
// line and beneath the obs line.
svg.append('path')
.attr('fill', 'red')
.attr('opacity', 0.6)
.attr('clip-path', 'url(#bottomToObs)')
.attr('d', topToClimate(data));
// lines
// draw a blue line for climate
svg.append('path')
.attr('stroke', 'blue')
.attr('fill', 'none')
.attr('d', bottomToClimate.lineY1()(data));
// draw a red line for obs
svg.append('path')
.attr('stroke', 'red')
.attr('fill', 'none')
.attr('d', bottomToObs.lineY1()(data));
// axes
svg.append('g')
.attr('transform', `translate(0,${height})`)
.call(d3.axisBottom(x).ticks(5, '%b %d'));
svg.append('g')
.call(d3.axisLeft(y));
</script>
</body>
</html>
嗨,数据可视化爱好者,
Codepen 在这里:https://codepen.io/shanyulin/pen/ZEXNgOb
我正在尝试根据不同的条件填充D3区域的颜色。我有两组数据climate_data
(绿色)和obs_data
(红色),我相应地画了两条线。
我想在两条线之间添加区域,如下所示:
使用以下代码:
this.svg
.append("path")
.datum(this.data)
.attr("transform", this.x_translate)
.attr("fill", this.obs_data_color)
.attr("stroke", "none")
.attr("fill-opacity", opacity)
.attr("stroke-width", 0)
.attr(
"d",
d3
.area()
.curve(curve)
.x((d) => {
return this.x_scale(new Date(d.time));
})
.y0((d) => {
return this.y_scale(d.climate_data);
})
.y1((d) => {
return this.y_scale(d.obs_data);
})
但是我想设置不同的颜色,一种在绿线上方,一种在下方。
我提到了这个postD3 Area fill with different color based on conditions
但输出看起来很奇怪(如红色方块所示):
有谁知道如何解决这个问题? 任何提示将不胜感激。谢谢!
这里是一个使用 clipPaths, based on this difference chart by Mike Bostock.
的例子<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script src="https://d3js.org/d3.v7.js"></script>
</head>
<body>
<div id="chart"></div>
<script>
// set up
const margin = { top: 10, right: 10, bottom: 50, left: 50 };
const width = 500 - margin.left - margin.right;
const height = 300 - margin.top - margin.bottom;
const svg = d3.select('#chart')
.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`);
// data
const parseTime = d3.timeParse('%Y-%m-%d');
const data = [
{ time: "2021-12-16", obs_data: 22.2, climate_data: 18.21 },
{ time: "2021-12-17", obs_data: 18.5, climate_data: 17.59 },
{ time: "2021-12-18", obs_data: 15.4, climate_data: 17.84 },
{ time: "2021-12-19", obs_data: 17.3, climate_data: 17.67 },
{ time: "2021-12-20", obs_data: 19.7, climate_data: 18.31 },
{ time: "2021-12-21", obs_data: 18.6, climate_data: 17.59 },
{ time: "2021-12-22", obs_data: 17.7, climate_data: 17.56 },
{ time: "2021-12-23", obs_data: 20, climate_data: 17.71 },
{ time: "2021-12-24", obs_data: 19.4, climate_data: 17.82 },
{ time: "2021-12-25", obs_data: 16.4, climate_data: 17.7 },
{ time: "2021-12-26", obs_data: 13.9, climate_data: 17.58 },
{ time: "2021-12-27", obs_data: 13.1, climate_data: 17.34 },
{ time: "2021-12-28", obs_data: 16.7, climate_data: 17.13 },
{ time: "2021-12-29", obs_data: 17.8, climate_data: 17.14 },
{ time: "2021-12-30", obs_data: 16, climate_data: 16.81 },
{ time: "2021-12-31", obs_data: 16, climate_data: 15.86 },
{ time: "2022-01-01", obs_data: 16.9, climate_data: 16.37 },
{ time: "2022-01-02", obs_data: 16.9, climate_data: 17.09 },
{ time: "2022-01-03", obs_data: 18.6, climate_data: 17.68 },
{ time: "2022-01-04", obs_data: 18, climate_data: 17.56 },
{ time: "2022-01-05", obs_data: 19.3, climate_data: 17.13 },
{ time: "2022-01-06", obs_data: 16.8, climate_data: 17.3 },
{ time: "2022-01-07", obs_data: 16.1, climate_data: 17.19 },
{ time: "2022-01-08", obs_data: 16.5, climate_data: 16.54 },
{ time: "2022-01-09", obs_data: 17.6, climate_data: 16.3 },
{ time: "2022-01-10", obs_data: 17.4, climate_data: 16.95 },
{ time: "2022-01-11", obs_data: 13.8, climate_data: 17.26 },
{ time: "2022-01-12", obs_data: 13.3, climate_data: 16.63 },
{ time: "2022-01-13", obs_data: 14, climate_data: 16.15 },
{ time: "2022-01-14", obs_data: 15.3, climate_data: 16.15 },
{ time: "2022-01-15", obs_data: 16.9, climate_data: 16.16 }
].map(({time, obs_data, climate_data}) => ({ time: parseTime(time), obs_data, climate_data }));
// scales
const x = d3.scaleTime()
.domain(d3.extent(data, d => d.time))
.range([0, width]);
const y = d3.scaleLinear()
.domain(d3.extent(data.flatMap(d => [d.obs_data, d.climate_data]))).nice()
.range([height, 0]);
// area generators
// from the top of the chart to the line for climate
const topToClimate = d3.area()
.x(d => x(d.time))
.y0(0)
.y1(d => y(d.climate_data))
.curve(d3.curveMonotoneX);
// from the bottom of the chart to the line for climate
const bottomToClimate = d3.area()
.x(d => x(d.time))
.y0(height)
.y1(d => y(d.climate_data))
.curve(d3.curveMonotoneX);
// from the top of the chart to the line for obs
const topToObs = d3.area()
.x(d => x(d.time))
.y0(0)
.y1(d => y(d.obs_data))
.curve(d3.curveMonotoneX);
// from the bottom of the chart to the line for obs
const bottomToObs = d3.area()
.x(d => x(d.time))
.y0(height)
.y1(d => y(d.obs_data))
.curve(d3.curveMonotoneX);
// clip paths
svg.append('clipPath')
.attr('id', 'topToObs')
.append('path')
.attr('d', topToObs(data));
svg.append('clipPath')
.attr('id', 'bottomToObs')
.append('path')
.attr('d', bottomToObs(data));
// areas
// draw a blue area from the bottom of the chart to the blue line for climate.
// the clip path makes any part of this area outside of the clip path invisible.
// the clip path goes from the top of the chart to the red line for obs.
// the result is that you can only see the blue area when it is above the obs
// line and beneath the climate line.
svg.append('path')
.attr('fill', 'blue')
.attr('opacity', 0.6)
.attr('clip-path', 'url(#topToObs)')
.attr('d', bottomToClimate(data));
// draw a red area from the top of the chart to the blue line for climate.
// the clip path makes any part of this area outside of the clip path invisible.
// the clip path goes from the bottom of the chart to the red line for obs.
// the result is that you can only see the read area when it is above the climate
// line and beneath the obs line.
svg.append('path')
.attr('fill', 'red')
.attr('opacity', 0.6)
.attr('clip-path', 'url(#bottomToObs)')
.attr('d', topToClimate(data));
// lines
// draw a blue line for climate
svg.append('path')
.attr('stroke', 'blue')
.attr('fill', 'none')
.attr('d', bottomToClimate.lineY1()(data));
// draw a red line for obs
svg.append('path')
.attr('stroke', 'red')
.attr('fill', 'none')
.attr('d', bottomToObs.lineY1()(data));
// axes
svg.append('g')
.attr('transform', `translate(0,${height})`)
.call(d3.axisBottom(x).ticks(5, '%b %d'));
svg.append('g')
.call(d3.axisLeft(y));
</script>
</body>
</html>