问题
I am trying to achieve this:
with the line path & shadow's transition in sync. And I am able to do that with the help of this answer,
but I am trying to figure out a way to append the red color gradient area after a certain (x, y) point (in this case (48, 0)).
I filtered the graph data and have two arrays (x-axis points -> from 0 to 48 and from 48 to 60), but then don't know how to apply the data in attrTween
function.
Here's the stackblitz link.
Here's the shadow function:
// Blue
const colorArray1 = [
["rgb(8, 141, 218)", "0.8"],
["rgb(8, 141, 218)", "0.5"],
["rgb(23, 38, 65)", "0.3"]
];
// Red
const colorArray2 = [
["rgb(253, 17, 57)", "0.8"],
["rgb(253, 17, 57)", "0.4"],
["rgb(23, 38, 65)", "0.3"]
];
const id: string = isShadowBefore48Hr ? "grad1" : "grad2";
const data: Array<{}> = isShadowBefore48Hr
? this.graphData.filter(d => d.hrCount <= 48)
: this.graphData.filter(d => d.hrCount >= 48);
const angle: number = isShadowBefore48Hr ? -15 : 0;
const defs = this.g.append("defs");
const grad = defs
.append("linearGradient")
.attr("id", id)
.attr("x1", "0%")
.attr("x2", "0%")
.attr("y1", "0%")
.attr("y2", "100%")
.attr("gradientTransform", "rotate(" + angle + ")");
grad
.selectAll("stop")
.data(isShadowBefore48Hr ? colorArray1 : colorArray2)
.enter()
.append("stop")
.style("stop-color", (d: any) => {
return d[0];
})
.style("stop-opacity", (d: any) => {
return d[1];
})
.attr("offset", (d: any, i: any) => {
return 100 * (i / 2) + "%";
});
const area = d3
.area()
.y0(this.height)
.y1((d: any) => d.y)
.x((d: any) => d.x);
this.g
.append("path")
.attr("fill", "url(#" + id + ")")
.transition()
.duration(5000)
.attrTween("d", (d: any) => {
const line = d3.select(".line").node(); // Find the line graph
const length = line.getTotalLength(); // Get length
const i = d3.interpolate(0, length); // Interpolator function over length
const dataPoints = []; // Array to hold accumulated data points
return (t: any) => {
dataPoints.push(line.getPointAtLength(i(t))); // On every iteration, get a point on the line
return area(dataPoints);
};
});
}
回答1:
As suggested in my comment you should have clear two paths (as you can't have only one gradient for both) and maybe reveal the chart by adding a clip path (of the entire chart area) that changes its width from 0 to chart width, essentially showing a chart in transition. This is old code for D3.js version4, slightly adapted with your gradients (you'll figure the rest out):
<html>
<head>
<meta charset="utf-8" />
<style>
.line {
fill: none;
stroke: steelblue;
stroke-width: 2px;
}
pre#data {
display: none;
}
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<pre id="data">
date,close
1-May-12,58.13
30-Apr-12,53.98
27-Apr-12,67.00
26-Apr-12,89.70
25-Apr-12,99.00
24-Apr-12,130.28
23-Apr-12,166.70
20-Apr-12,234.98
19-Apr-12,345.44
18-Apr-12,443.34
17-Apr-12,543.70
16-Apr-12,580.13
13-Apr-12,605.23
12-Apr-12,622.77
11-Apr-12,626.20
10-Apr-12,628.44
9-Apr-12,636.23
5-Apr-12,633.68
4-Apr-12,624.31
3-Apr-12,629.32
2-Apr-12,618.63
30-Mar-12,599.55
29-Mar-12,609.86
28-Mar-12,617.62
27-Mar-12,614.48
26-Mar-12,606.98
</pre>
<script>
let animationDuration = 5000;
// set the dimensions and margins of the graph
var margin = {
top: 20,
right: 20,
bottom: 30,
left: 50
},
width = 720 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
// parse the date / time
var parseTime = d3.timeParse("%d-%b-%y");
// set the ranges
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
// define the area
var area = function(datum, boolean) {
return d3.area()
.y0(height)
.y1(function(d) {
return boolean ? y(d.close) : y(d.close);
})
.x(function(d) {
return boolean ? x(d.date) : 0;
})
(datum);
}
// define the line
var valueline = d3.line()
.x(function(d) {
return x(d.date);
})
.y(function(d) {
return y(d.close);
});
// append the svg obgect to the body of the page
// appends a 'group' element to 'svg'
// moves the 'group' element to the top left margin
var svg = d3.select("body").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 + ")");
var defs = svg.append("defs");
var clip = defs.append("clipPath")
.attr("id", "clip");
var clipRect = clip.append("rect")
.attr("width", 0)
.attr("height", height);
// Blue
const colorArray1 = [
["rgb(8, 141, 218)", "0.8"],
["rgb(8, 141, 218)", "0.5"],
["rgb(23, 38, 65)", "0.3"]
];
// Red
const colorArray2 = [
["rgb(253, 17, 57)", "0.8"],
["rgb(253, 17, 57)", "0.4"],
["rgb(23, 38, 65)", "0.3"]
];
function addGradient(isShadowBefore48Hr) {
const id = isShadowBefore48Hr ? "grad1" : "grad2";
const angle = isShadowBefore48Hr ? -15 : 0;
const grad = defs
.append("linearGradient")
.attr("id", id)
.attr("x1", "0%")
.attr("x2", "0%")
.attr("y1", "0%")
.attr("y2", "100%")
.attr("gradientTransform", "rotate(" + angle + ")");
grad
.selectAll("stop")
.data(isShadowBefore48Hr ? colorArray1 : colorArray2)
.enter()
.append("stop")
.style("stop-color", (d) => {
return d[0];
})
.style("stop-opacity", (d) => {
return d[1];
})
.attr("offset", (d, i) => {
return 100 * (i / 2) + "%";
});
}
addGradient(true);
addGradient(false);
var data = d3.csvParse(d3.select("pre#data").text());
data.reverse();
// format the data
data.forEach(function(d) {
d.date = parseTime(d.date);
d.close = +d.close;
});
// scale the range of the data
x.domain(d3.extent(data, function(d) {
return d.date;
}));
y.domain([0, d3.max(data, function(d) {
return d.close;
})]);
// add first area
svg.append("path")
.data([data.filter(function(item) { return item.date <= parseTime("23-Apr-12") } ) ])
.attr("class", "area")
.attr("d", d => area(d, true))
//.attr("fill", "lightsteelblue")
.attr("fill", "url(#" + "grad1" + ")")
.attr("clip-path", "url(#clip)");
// add second area
svg.append("path")
.data([data.filter(function(item) { return item.date >= parseTime("23-Apr-12") } ) ])
.attr("class", "area")
.attr("d", d => area(d, true))
//.attr("fill", "red")
.attr("fill", "url(#" + "grad2" + ")")
.attr("clip-path", "url(#clip)");
// add the valueline path.
svg.append("path")
.data([data])
.attr("class", "line")
.attr("d", valueline)
.attr("clip-path", "url(#clip)");
// add the X Axis
/* svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
*/
// add the Y Axis
svg.append("g")
.call(d3.axisLeft(y));
clipRect.transition()
.duration(5000)
.ease(d3.easeLinear)
.attr("width", width)
</script>
</body>
</html>
来源:https://stackoverflow.com/questions/65683163/need-to-have-two-different-colors-in-a-linear-gradient-after-a-specific-x-y-p