d3折线图的动画区域 [英] Animate area of a d3 line graph

查看:58
本文介绍了d3折线图的动画区域的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试转换d3区域.

I am trying a transition of a d3 area.

这是我尝试遵循的示例链接.它正在从数据线路径的外部进行区域转换.

Here's the example link which I tried to follow. It is doing the area transition from outside the data line path.

我需要这种过渡是从数据线区域内部进行的,并且随着数据线的进行,区域过渡也应该进行.

I need this transition to be from inside the data line area, and as the data line progresses the area transition should progress.

这是StackBlitz 链接.

Here's the StackBlitz link.

这是代码:

maxAllowed = 0;
  graphData = [
    {
      hrCount: 4,
      adjCount: 2
    },
    {
      hrCount: 8,
      adjCount: 5
    },
    {
      hrCount: 12,
      adjCount: 10
    },
    {
      hrCount: 16,
      adjCount: 13
    },
    {
      hrCount: 20,
      adjCount: 19
    },
    {
      hrCount: 24,
      adjCount: 25
    },
    {
      hrCount: 28,
      adjCount: 33
    },
    {
      hrCount: 32,
      adjCount: 37
    },
    {
      hrCount: 36,
      adjCount: 40
    },
    {
      hrCount: 40,
      adjCount: 42
    },
    {
      hrCount: 44,
      adjCount: 44
    },
    {
      hrCount: 48,
      adjCount: 47
    },
    {
      hrCount: 52,
      adjCount: 48
    },
    {
      hrCount: 56,
      adjCount: 50
    },
    {
      hrCount: 60,
      adjCount: 53
    }
  ];
  margin: {
    top: number;
    right: number;
    bottom: number;
    left: number;
  };
  width: number;
  height: number;
  g: any;
  xScale: any;
  yScale: any;
  xAxis: any;
  yAxis: any;


ngOnInit() {
    this.maxAllowed = d3.max(this.graphData, (d: any) => d.hrCount) + 5;
    this.drawLineGraph();
  }

  drawLineGraph() {
    this.margin = {
      top: 5,
      right: 10,
      bottom: 20,
      left: 25
    };
    this.width =
      document.getElementById("svgcontainer").parentElement.offsetWidth -
      (this.margin.left + this.margin.right);
    this.height =
      document.getElementById("svgcontainer").parentElement.offsetHeight -
      (this.margin.top + this.margin.bottom) +
      80;

    // Remove any existing SVG
    d3.select("#svgcontainer")
      .selectAll("svg > *")
      .remove();

    this.createGroup();
    this.createScale();
    this.createYAxisGridLine();
    this.createShadowEffect();
    this.createAxis();
    this.createDataPathAndDots();

    // Removing y-axis 0 tick-line
    d3.selectAll(".y-axis-tick .tick line").each(function(d, i) {
      if (i === 0) {
        this.remove();
      }
    });
  }

  createGroup(): void {
    this.g = d3
      .select("#svgcontainer")
      .append("svg")
      .attr("width", this.width + this.margin.left + this.margin.right)
      .attr("height", this.height + this.margin.top + this.margin.bottom)
      .style("background-color", "#0e1a30")
      .append("g")
      .attr(
        "transform",
        "translate(" + this.margin.left + ", " + this.margin.top + ")"
      );
  }

  createScale(): void {
    // x-scale
    this.xScale = d3
      .scaleBand()
      .domain(this.graphData.map((d: any) => d.hrCount))
      .range([0, this.width]);

    // y-scale
    this.yScale = d3
      .scaleLinear()
      .domain([0, this.maxAllowed])
      .range([this.height, 0]);
  }

  createYAxisGridLine(): void {
    this.g
      .append("g")
      .attr("class", "y-axis-grid")
      .call(
        d3
          .axisLeft(this.yScale)
          .tickSize(-this.width)
          .tickFormat("")
          .ticks(5)
      );
  }

  createShadowEffect(): void {
    const colorArray = [
      ["rgb(8, 141, 218)", "0.8"],
      ["rgb(8, 141, 218)", "0.5"],
      ["rgb(8, 141, 218)", "0"]
    ];
    const defs = this.g.append("defs");
    const grad = defs
      .append("linearGradient")
      .attr("id", "grad")
      .attr("x1", "0%")
      .attr("x2", "0%")
      .attr("y1", "0%")
      .attr("y2", "100%")
      .attr("gradientTransform", "rotate(-15)");
    grad
      .selectAll("stop")
      .data(colorArray)
      .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 = (datum, boolean) => {
      return d3
        .area()
        .y0(this.height)
        .y1((d: any) => this.yScale(d.adjCount))
        .x((d: any) =>
          boolean ? this.xScale(d.hrCount) + this.xScale.bandwidth() / 2 : 0
        )(datum);
    };
    this.g
      .append("path")
      .attr("d", area(this.graphData, false))
      .attr("fill", "url(#grad)")
      .transition()
      .duration(5000)
      .attr("d", area(this.graphData, true));
  }

  createAxis(): void {
    // x-axis
    this.xAxis = d3.axisBottom(this.xScale).tickSizeOuter(0);
    this.g
      .append("g")
      .attr("transform", "translate(0, " + this.height + ")")
      .attr("class", "graph-axis")
      .call(this.xAxis.scale(this.xScale))
      .append("text")
      .attr("x", this.width)
      .attr("y", -6)
      .attr("text-anchor", "end")
      .attr("font", "10px sans-serif")
      .attr("letter-spacing", "1px")
      .attr("fill", "#8997b1")
      .text("Hours");

    // y-axis
    this.yAxis = d3
      .axisLeft(this.yScale)
      .ticks(5)
      .tickSizeOuter(0);
    this.g
      .append("g")
      .attr("class", "graph-axis y-axis-tick")
      .call(this.yAxis.scale(this.yScale))
      .append("text")
      .attr("transform", "rotate(-90)")
      .attr("y", 6)
      .attr("dy", ".71em")
      .style("text-anchor", "end")
      .attr("font", "10px sans-serif")
      .attr("letter-spacing", "1px")
      .attr("fill", "#8997b1")
      .text("Adjusters");
  }

  createDataPathAndDots(): void {
    const line = d3
      .line()
      .x((d: any) => this.xScale(d.hrCount) + this.xScale.bandwidth() / 2)
      .y((d: any) => this.yScale(d.adjCount));
    const path = this.g
      .append("path")
      .attr("fill", "none")
      .attr("stroke", "#088dda")
      .attr("stroke-width", "2px")
      .attr("d", line(this.graphData));
    this.createPathTransition(path);

    // Data dots
    this.g
      .selectAll("line-circle")
      .data(this.graphData)
      .enter()
      .append("circle")
      .attr("r", 4)
      .attr("fill", (d: any) => {
        if (d.hrCount === 0) {
          return "none";
        } else {
          return "#088dda";
        }
      })
      .attr(
        "cx",
        (d: any) => this.xScale(d.hrCount) + this.xScale.bandwidth() / 2
      )
      .attr("cy", (d: any) => this.yScale(d.adjCount));
  }

  createPathTransition(path: any): void {
    const totLength = path.node().getTotalLength();
    path
      .attr("stroke-dasharray", totLength + " " + totLength)
      .attr("stroke-dashoffset", totLength);
    path
      .transition()
      .duration(5000)
      .attr("stroke-dashoffset", 0);
  }

推荐答案

这是一种潜在的解决方案.首先,我修改了您的区域生成器,如下所示:

Here's one potential solution. First, I modified your area generator like so:

const area = d3.area()
    .y0(this.height)
    .y1((d: any) => d.x)
    .x((d: any) => d.y );

然后我将区域转换设置为:

Then I set up the area transition as:

this.g
  .append("path")
  .attr("fill", "url(#grad)")
  .transition()
  .duration(5000)
  .attrTween("d", function(d){
    var p = d3.select(".line").node(), // find the line graph
        l = p.getTotalLength(), // get length
        i = d3.interpolate(0,l), // interpolator over length
        dAtT = []; // array to hold accumulated datapoints   
    return function(t) {
      dAtT.push(p.getPointAtLength(i(t))) // on every iteration get a point on the line
      return area(dAtT); // draw area
    };
  });

更新了 stackblitz .

这篇关于d3折线图的动画区域的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆