如何同步路径和区域的动画?

时间:2017-12-27 04:55:23

标签: animation d3.js highcharts line area

我遇到了 D3.js v4 动画 这两行&的区域

  • 可以分别为line&区域
  • 当组合2个动画时,即使在相同的过渡时间内,它们也不会同时出现。
  • 出于造型的原因,我不能放弃这条线。

见下图: enter image description here

为了做上面这样的事情,我做了2个大步骤:

  1. 通过设置属性stroke-dasharrowstroke-dashoffset为线条制作动画。 (灵感来自Visual Cinnamon
  2. 通过调整d3.area()函数的参数(来自other Stackoverlfow post的灵感来源)为区域做动画
  3. 结果相当令人失望,因为下面的线和面不会平行出现。

    我的目标是模仿Highchart库,查看示例here,并在下面插图:

    enter image description here

    Highchart库似乎使用了不同的动画技术,因为在DOM检查期间,沿着动画的DOM路径没有任何变化的迹象。

    赞赏是否有人可以建议我尝试一些想法。

    我的代码示例如下:

    let animationDuration = 5000;
    // set the dimensions and margins of the graph
    var margin = { top: 20, right: 20, bottom: 30, left: 50 },
        width = 480 - margin.left - margin.right,
        height = 250 - 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 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 the area
        svg.append("path")
            .data([data])
            .attr("class", "area")
            .attr("d", d => area(d, false))
            .attr("fill", "lightsteelblue")
            .transition()
            .duration(animationDuration)
            .attr("d", d => area(d, true));
    
        // add the valueline path.
        svg.append("path")
            .data([data])
            .attr("class", "line")
            .attr("d", valueline)
            .style("stroke-dasharray", d => {
                let path = document.querySelector(".line");
                const totalLength = path.getTotalLength();
    
                return `${totalLength} ${totalLength}`;
            })
            .style("stroke-dashoffset", d => {
                let path = document.querySelector(".line");
                const totalLength = path.getTotalLength();
                return `${totalLength}`;
            })
            .transition()
            .duration(animationDuration)
            .style("stroke-dashoffset", 0);
    
        // 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));
    .line {
            fill: none;
            stroke: steelblue;
            stroke-width: 2px;
        }
        
    pre#data {display:none;}
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <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>

1 个答案:

答案 0 :(得分:4)

有一种方法可以使用自定义插值器为线条和区域设置动画。

但是,由于您的目标是模仿Highcharts animation you linked,因此有一种方式更轻松替代方案:使用<clipPath>

在我提出的解决方案中,我们以常规方式创建区域和线。但是,我们引用剪切路径......

.attr("clip-path", "url(#clip)");

......在地区和线路上。使用0 width:

创建剪切路径
var clip = svg.append("clipPath")
    .attr("id", "clip");
var clipRect = clip.append("rect")
  .attr("width", 0)

然后,在此之后,只需将转换应用于剪切路径:

clipRect.transition()
    .duration(5000)
    .ease(d3.easeLinear)
    .attr("width", someValue)

这是一个演示:

&#13;
&#13;
var svg = d3.select("svg");
var data = d3.range(30).map(d => Math.random() * 150);
var clip = svg.append("clipPath")
  .attr("id", "clip");
var clipRect = clip.append("rect")
  .attr("width", 0)
  .attr("height", 150)
var lineGenerator = d3.line()
  .x((_, i) => i * 10)
  .y(d => d)
  .curve(d3.curveMonotoneX)
var areaGenerator = d3.area()
  .x((_, i) => i * 10)
  .y1(d => d)
  .y0(150)
  .curve(d3.curveMonotoneX)
svg.append("path")
  .attr("d", areaGenerator(data))
  .attr("class", "area")
  .attr("clip-path", "url(#clip)");
svg.append("path")
  .attr("d", lineGenerator(data))
  .attr("class", "line")
  .attr("clip-path", "url(#clip)");
clipRect.transition()
  .duration(5000)
  .ease(d3.easeLinear)
  .attr("width", 300)
&#13;
.line {
  fill: none;
  stroke: #222;
  stroke-width: 2px;
}

.area {
  fill: limegreen;
  stroke: none;
}
&#13;
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg></svg>
&#13;
&#13;
&#13;

以下是包含这些更改的代码:

&#13;
&#13;
let animationDuration = 5000;
// set the dimensions and margins of the graph
var margin = {
    top: 20,
    right: 20,
    bottom: 30,
    left: 50
  },
  width = 480 - margin.left - margin.right,
  height = 250 - 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 clip = svg.append("clipPath")
  .attr("id", "clip");
var clipRect = clip.append("rect")
  .attr("width", 0)
  .attr("height", height);

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 the area
svg.append("path")
  .data([data])
  .attr("class", "area")
  .attr("d", d => area(d, true))
  .attr("fill", "lightsteelblue")
  .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)
&#13;
.line {
  fill: none;
  stroke: steelblue;
  stroke-width: 2px;
}

pre#data {
  display: none;
}
&#13;
<script src="https://d3js.org/d3.v4.min.js"></script>
<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>
&#13;
&#13;
&#13;