动画线的运动以匹配线性路径的速度

时间:2018-06-08 20:05:47

标签: javascript animation d3.js

我有一个bin http://jsbin.com/nazesivito,其中包含以下d3动画:

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);

但是当你看到与盒子一起绘制的路径时,你会注意到它们没有保持齐平。我相信这是因为线必须行进更长的距离以绘制完整的路径,并且盒子在直线上移动。我如何配对这两个东西的运动,以便它们保持一致?

1 个答案:

答案 0 :(得分:2)

一种更简单的替代方法,即保证行和框的x位置相同,使用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);

然后:

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));
}

以下是演示:

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>