javascriptd3.jsgradientarea-chart

D3js unable to show gradient area chart


I have a project where I need to recreate a gradient chart. I have found some documentation online on how to recreate a area chart with gradient: Documentation.

The main problem is that I cannot get this code to work. I have changed it to work with my data and the chart won't show up. The axis are being rendered correctly. But the chart itself doesn't show up.

I have replaced the var x = d3.scaleTime().range([0, width]); to var x = d3.scaleBand().range([0, width]); because in the documentation they were working with Date object on the x axis.

There are no console errors but the chart won't simply show up. The code seems very straight forward but I can't seem to get what is wrong with it. I am very new to D3.js and still learning a lot about the library.

<!DOCTYPE html>
    <html>
      <head>
        <title></title>
        <script src="https://unpkg.com/vue"></script>
        <script src="https://d3js.org/d3.v6.js"></script>
      </head>
      <body>
        <div class="p-3 flex flex-col" id="one">
          <div class="w-full flex-1">
            <div id="chart"></div>
          </div>
        </div>

        <script>
          new Vue({
            el: '#one',
            data: {
              type: Array,
              required: true,
            },
  mounted() {
    // set the dimensions and margins of the graph
    var margin = { top: 20, right: 20, bottom: 30, left: 50 },
      width = 960 - margin.left - margin.right,
      height = 500 - margin.top - margin.bottom;

    // 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("#chart")
      .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 = [
      {
        date: "Jan",
        value: 1507
      },
      {
        date: "Feb",
        value: 1600
      },
      {
        date: "Mar",
        value: 1281
      },
      {
        date: "Apr",
        value: 1898
      },
      {
        date: "May",
        value: 1749
      },
      {
        date: "June",
        value: 1270
      },
      {
        date: "July",
        value: 1712
      },
      {
        date: "Aug",
        value: 1270
      },
      {
        date: "Sept",
        value: 1257
      },
      {
        date: "Oct",
        value: 1257
      },
      {
        date: "Nov",
        value: 1257
      },
      {
        date: "Dec",
        value: 1257
      }
    ];

    // set the ranges
    var x = d3.scaleBand().range([0, width]);

    var y = d3.scaleLinear().rangeRound([height, 0]);

    // define the area
    var area = d3
      .area()
      .x(function(d) {
        console.log(d.date);
        console.log(x.domain());
        return x(d.date);
      })

      .y(function(d) {
        console.log(d.value);
        console.log(y.domain());
        return y(d.value);
      });

    x.domain(
      data.map(function(d) {
        return d.date;
      })
    );

    y.domain([
      0,
      d3.max(data, function(d) {
        return d.value;
      })
    ]);

    // set the gradient
    svg
      .append("linearGradient")
      .attr("id", "area-gradient")
      .attr("gradientUnits", "userSpaceOnUse")
      .attr("x1", 0)
      .attr("y1", y(0))
      .attr("x2", 0)
      .attr("y2", y(1000))
      .selectAll("stop")
      .data([
        { offset: "0%", color: "red" },
        { offset: "30%", color: "red" },
        { offset: "45%", color: "black" },
        { offset: "55%", color: "black" },
        { offset: "60%", color: "lawngreen" },
        { offset: "100%", color: "lawngreen" }
      ])
      .enter()
      .append("stop")
      .attr("offset", function(d) {
        return d.offset;
      })
      .attr("stop-color", function(d) {
        return d.color;
      });

    // Add the area.
    svg
      .append("path")
      .datum(data)
      .attr("class", "area")
      .attr("d", area);

    // 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));
  }
          });
        </script>
      </body>
    </html>


Solution

  • Couple things.

    First, you missed the css which sets the path to fill with the gradient:

    .area {                         
      fill: url(#area-gradient);                    
      stroke-width: 0px;            
    }
    

    Second, in order to draw an "area" the d3.area generator requires .y0 and .y1 accessors. It's what says fill from here to there.

    var area = d3
     .area()
     .x(function (d) {
       return x(d.date);
     })
     .y1(function (d) {
       return y(d.value);
     })
     .y0(height);
    

    Running snippet:

    <!DOCTYPE html>
    <html>
      <head>
        <title></title>
        <script src="https://unpkg.com/vue"></script>
        <script src="https://d3js.org/d3.v6.js"></script>
        <style>
          .area {
            fill: url(#area-gradient);
            stroke-width: 0px;
          }
        </style>
      </head>
      <body>
        <div class="p-3 flex flex-col" id="one">
          <div class="w-full flex-1">
            <div id="chart"></div>
          </div>
        </div>
    
        <script>
          new Vue({
            el: '#one',
            data: {
              type: Array,
              required: true,
            },
            mounted() {
              // set the dimensions and margins of the graph
              var margin = { top: 20, right: 20, bottom: 30, left: 50 },
                width = 960 - margin.left - margin.right,
                height = 500 - margin.top - margin.bottom;
    
              // 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('#chart')
                .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 = [
                {
                  date: 'Jan',
                  value: 1507,
                },
                {
                  date: 'Feb',
                  value: 1600,
                },
                {
                  date: 'Mar',
                  value: 1281,
                },
                {
                  date: 'Apr',
                  value: 1898,
                },
                {
                  date: 'May',
                  value: 1749,
                },
                {
                  date: 'June',
                  value: 1270,
                },
                {
                  date: 'July',
                  value: 1712,
                },
                {
                  date: 'Aug',
                  value: 1270,
                },
                {
                  date: 'Sept',
                  value: 1257,
                },
                {
                  date: 'Oct',
                  value: 1257,
                },
                {
                  date: 'Nov',
                  value: 1257,
                },
                {
                  date: 'Dec',
                  value: 1257,
                },
              ];
    
              // set the ranges
              var x = d3.scaleBand().range([0, width]);
    
              var y = d3.scaleLinear().rangeRound([height, 0]);
    
              // define the area
              var area = d3
                .area()
                .x(function (d) {
                  return x(d.date);
                })
                .y1(function (d) {
                  return y(d.value);
                })
                .y0(height);
    
              x.domain(
                data.map(function (d) {
                  return d.date;
                })
              );
    
              y.domain([
                0,
                d3.max(data, function (d) {
                  return d.value;
                }),
              ]);
    
              // set the gradient
              svg
                .append('linearGradient')
                .attr('id', 'area-gradient')
                .attr('gradientUnits', 'userSpaceOnUse')
                .attr('x1', 0)
                .attr('y1', y(0))
                .attr('x2', 0)
                .attr('y2', y(1000))
                .selectAll('stop')
                .data([
                  { offset: '0%', color: 'red' },
                  { offset: '30%', color: 'red' },
                  { offset: '45%', color: 'black' },
                  { offset: '55%', color: 'black' },
                  { offset: '60%', color: 'lawngreen' },
                  { offset: '100%', color: 'lawngreen' },
                ])
                .enter()
                .append('stop')
                .attr('offset', function (d) {
                  return d.offset;
                })
                .attr('stop-color', function (d) {
                  return d.color;
                });
    
              // Add the area.
              svg.append('path').datum(data).attr('class', 'area').attr('d', area);
    
              // 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));
            },
          });
        </script>
      </body>
    </html>