Reputation: 3459
I have a bin http://jsbin.com/nazesivito with the following d3 animation:
var w = 500;
var h = 300;
var svg = d3.select("#line")
.append("svg")
.attr("width", w)
.attr("height", h)
.attr("id", "visualization");
var data = d3.range(0, 7.01, .1);
function overlapFormula(index) {
rect1 = {
left: index,
right: index + 3,
top: 0,
bottom: 10,
};
rect2 = {
left: 3.5,
right: 6.5,
top: 0,
bottom: 10,
}
x_overlap = Math.max(0, Math.min(rect1.right, rect2.right) - Math.max(rect1.left, rect2.left));
y_overlap = Math.max(0, Math.min(rect1.bottom, rect2.bottom) - Math.max(rect1.top, rect2.top));
overlapArea = x_overlap * y_overlap;
return overlapArea;
}
var x = d3.scaleLinear().domain([0, 10]).range([0, 500]);
var y = d3.scaleLinear().domain([0, 10]).range([0, 300]);
var line = d3.line()
.x((d, i) => x(i))
.y(d => y(d))
.curve(d3.curveLinear);
var x2 = d3.scaleLinear().domain([0, 10]).range([0, 500]);
var y2 = d3.scaleLinear().domain([30, 0]).range([0, 300]);
var line2 = d3.line()
.x(d => x2(d))
.y(d => y2(overlapFormula(d)))
.curve(d3.curveLinear);
var area = d3.area()
.x( d => x2(d))
.y0( y(10) )
.y1( d => y2(overlapFormula(d)) );
const line_area = svg.append("path")
.attr("d", area(data))
.attr("fill", "green");
function repeat(path) {
path
.attr("stroke-dasharray", 725 + " " + 725)
.attr("stroke-dashoffset", 725)
.transition()
.ease(d3.easeLinear)
.duration(7000)
.attr("stroke-dashoffset", 0)
.transition()
.duration(1000)
.on("end", () => repeat(path));
}
const path = svg.append("path")
.attr("d", line2(data))
.attr("stroke", "steelblue")
.attr("stroke-width", "5")
.attr("fill", "none");
const zeroAxis = svg.append("path")
.attr("d", line(d3.range(11).map(() => 10)))
.attr("stroke", "black")
.attr("stroke-width", "2")
.attr("fill", "none");
const box = svg.append("rect")
.attr("x", x(3.5))
.attr("y", y(1))
.attr("width", x(3))
.attr("height", y(10))
.attr("fill", "white")
.attr("fill-opacity", "0")
.attr("stroke", "black")
.attr("stroke-width", 2);
function moveBox(box) {
box.transition()
.duration(7000)
.attr("transform", `translate(${x(7)}, 0)`)
.ease(d3.easeLinear)
.transition()
.duration(1000)
.transition()
.duration(0)
.attr("transform", `translate(${x(0)}, 0)`)
.on("end", () => {
moveBox(box)
})
}
const box2 = svg.append("rect")
.attr("x", x(0))
.attr("y", y(1))
.attr("width", x(3))
.attr("height", y(10))
.attr("fill", "white")
.attr("fill-opacity", "0")
.attr("stroke", "black")
.attr("stroke-width", 3)
;
moveBox(box2);
repeat(path);
But when you look at the path being drawn along with the box, you'll notice they don't stay flush. I believe this is because the line has to travel a longer distance to draw the full path, and the box is moving on a straight line. How can I pair these two thing's movement so they stay aligned?
Upvotes: 1
Views: 43
Reputation: 102174
An easier alternative, that guarantees that the x
position of both line and box are the same, is using a clipPath
:
var clip = defs.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", x(7) - x(0))
.attr("height", h)
.attr("x", -(x(7) - x(0)))
.attr("y", 0);
Then:
function repeat(path) {
clip.attr("x", -(x(7) - x(0)))
.transition()
.ease(d3.easeLinear)
.duration(7000)
.attr("x", 0)
.transition()
.duration(1000)
.on("end", () => repeat(path));
}
Here is the demo:
console.clear();
var w = 500;
var h = 300;
var svg = d3.select("#line")
.append("svg")
.attr("width", w)
.attr("height", h)
.attr("id", "visualization");
var data = d3.range(0, 7.01, .1);
function overlapFormula(index) {
rect1 = {
left: index,
right: index + 3,
top: 0,
bottom: 10,
};
rect2 = {
left: 3.5,
right: 6.5,
top: 0,
bottom: 10,
}
x_overlap = Math.max(0, Math.min(rect1.right, rect2.right) - Math.max(rect1.left, rect2.left));
y_overlap = Math.max(0, Math.min(rect1.bottom, rect2.bottom) - Math.max(rect1.top, rect2.top));
overlapArea = x_overlap * y_overlap;
return overlapArea;
}
var x = d3.scaleLinear().domain([0, 10]).range([0, 500]);
var y = d3.scaleLinear().domain([0, 10]).range([0, 300]);
var line = d3.line()
.x((d, i) => x(i))
.y(d => y(d))
.curve(d3.curveLinear);
var x2 = d3.scaleLinear().domain([0, 10]).range([0, 500]);
var y2 = d3.scaleLinear().domain([30, 0]).range([0, 300]);
var line2 = d3.line()
.x(d => x2(d))
.y(d => y2(overlapFormula(d)))
.curve(d3.curveLinear);
var area = d3.area()
.x(d => x2(d))
.y0(y(10))
.y1(d => y2(overlapFormula(d)));
const line_area = svg.append("path")
.attr("d", area(data))
.attr("fill", "green");
var defs = svg.append("defs");
var clip = defs.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", x(7) - x(0))
.attr("height", h)
.attr("x", -(x(7) - x(0)))
.attr("y", 0);
function repeat(path) {
clip.attr("x", -(x(7) - x(0)))
.transition()
.ease(d3.easeLinear)
.duration(7000)
.attr("x", 0)
.transition()
.duration(1000)
.on("end", () => repeat(path));
}
const path = svg.append("path")
.attr("d", line2(data))
.attr("stroke", "steelblue")
.attr("stroke-width", "5")
.attr("fill", "none")
.attr("clip-path", "url(#clip)");
const zeroAxis = svg.append("path")
.attr("d", line(d3.range(11).map(() => 10)))
.attr("stroke", "black")
.attr("stroke-width", "2")
.attr("fill", "none");
const box = svg.append("rect")
.attr("x", x(3.5))
.attr("y", y(1))
.attr("width", x(3))
.attr("height", y(10))
.attr("fill", "white")
.attr("fill-opacity", "0")
.attr("stroke", "black")
.attr("stroke-width", 2);
function moveBox(box) {
box.transition()
.duration(7000)
.attr("transform", `translate(${x(7)}, 0)`)
.ease(d3.easeLinear)
.transition()
.duration(1000)
.transition()
.duration(0)
.attr("transform", `translate(${x(0)}, 0)`)
.on("end", () => {
moveBox(box)
})
}
const box2 = svg.append("rect")
.attr("x", x(0))
.attr("y", y(1))
.attr("width", x(3))
.attr("height", y(10))
.attr("fill", "white")
.attr("fill-opacity", "0")
.attr("stroke", "black")
.attr("stroke-width", 3);
moveBox(box2);
repeat(path);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.3/d3.min.js"></script>
<body>
<div id="line" />
</body>
Upvotes: 2