block by denisemauldin 5e44e790ae875a12de2f6b0faab44c3d

Area chart with dark top stroke and smooth transitions

Full Screen

Built with blockbuilder.org

forked from anonymous‘s block: fresh block

forked from anonymous‘s block: fresh block

forked from anonymous‘s block: fresh block

forked from anonymous‘s block: fresh block

index.html

<!DOCTYPE html>
<head>
  <meta charset="utf-8">
  <script src="https://d3js.org/d3.v4.min.js"></script>
  <script src="https://peterbeshai.com/d3-interpolate-path/d3-interpolate-path.js"></script>
  <style>
    body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
    button {
      font-size: 16px;
      margin: 15px;
      padding: 10px;
    }
    svg {
      border: 1px solid gray;
    }
  </style>
</head>

<body>
  <button id="update">Press Me</button>
  <script>
    const margin = {left: 15, top: 15, right: 15, bottom: 40};
    const width = 960 - margin.left - margin.right;
    const height = 500 - margin.top - margin.bottom;
    const svg = d3.select("body").append("svg")
      .attr("width", 960)
      .attr("height", 500);

    const data = Array(3).fill(0).map(generateData.bind(null, 10));

    const xMax = Math.max(...data.reduce((acc, x) => acc.concat([x.length - 1]), []));

    const xScale = d3.scaleLinear()
    	.domain([0, xMax])
    	.range([0, width]);
    
    const yScale = d3.scaleLinear()
    	.domain(d3.extent(data.reduce((acc, x) => acc.concat(x), [])))
    	.range([height, 0]);

    const container = svg
    	.append('g')
    	.attr('transform', `translate(${margin.left}, ${margin.top})`);

    const area = d3.area()
    	.x((d, i) => xScale(i))
    	.y1(d => yScale(d))
    	.y0(yScale(0))
    	.curve(d3.curveCatmullRom);

    const color = (i) =>
    	`hsl(${Math.floor(240/data.length) * i}, 80%, 50%)`;

		container
  		.append('clipPath')
    	.attr('id', 'rect-clip')
  		.append('rect')
      .attr('x', 2)
      .attr('y', -10)
      .attr('width', width - 4)
    	.attr('height', height + 8);

    container
    	.selectAll('path')
    	.data(data, (d, i) => i)
      .enter()
      .append('path')
    	.attr('clip-path', 'url(#rect-clip)')
      .attr('d', area)
    	.attr('fill', (d, i) => color(i))
    	.attr('fill-opacity', 0.3)
    	.attr('stroke', (d, i) => color(i))
    	.attr('stroke-width', 2);

    const xAxis = d3.axisBottom(xScale);
    
    container
      .append('g')
    	.attr('class', 'x-axis')
    	.attr('transform', `translate(0, ${height})`)
    	.call(xAxis);

    function update() {
      const x = Math.floor(Math.random() * 20) + 2;
      data.forEach((d, i) => data[i] = generateData(x));

      const y = data.reduce((acc, x) => acc.concat(x), []);

      xScale.domain([0, x - 1]);
      yScale.domain(d3.extent(y));

      const excludeSegment = (a, b) => a.x === b.x && a.x === width;
      
      container
        .selectAll('path')
	      .data(data, (d, i) => i)
        .transition()
        .duration(500)
        .attrTween('d', (d, i, el) => {
          const prev = d3.select(el[i]).attr('d');
          const next = area(d);
          return d3.interpolatePath(prev, next, excludeSegment);
        });
      
      container
        .select('.x-axis')
        .transition()
        .duration(500)
        .call(d3.axisBottom(xScale));
    }
    
    function generateData(size = 10) {
      return Array(size).fill(0).map(() => Math.random());
    }
    
    window.addEventListener('load', () => {
      document.querySelector('#update')
        .addEventListener('click', () => update());
		});
  </script>
</body>