使用d3.js创建水平图例(v4)

时间:2016-10-04 19:11:30

标签: javascript d3.js

目前我正在研究FCC的一个项目,基本上我完成了整个项目。

但是,我很难制作一个简单的传奇,特别是当我使用scaleQuantile作为我的colorScale

这是我的codepen链接:http://codepen.io/neotriz/pen/PGOLBb

基本上我想创建一个输出所有颜色(在数组color内)的水平图例,并根据数据附加其文本。

我可以对图例的文本进行硬编码,但我更喜欢抽象(文本基于数据本身) 如果有人能指出正确的方向,我会很感激。

let toolTip = d3.select("#canvas")
                .append("div")
                .classed("toolTip",true)
                .style("opacity",0)

const colors = ["#5e4fa2", "#3288bd", "#66c2a5", "#abdda4",
            "#e6f598", "#ffffbf", "#fee08b", "#fdae61",
            "#f46d43", "#d53e4f", "#9e0142"];

const width = w - (margin.left + margin.right);
const height = h - (margin.top + margin.bottom);
const yOffset = 40;

//lets create new object to add degree key and its value
data = rawData.map( oneData  => {
  let degree = base + oneData.variance
  return Object.assign({}, oneData, {degree: degree})
})


const svg = d3.select("#canvas")
              .append("svg")
              .attr("id","chart")
              .attr("width", w)
              .attr("height", h)

const chart = svg.append("g")
                .classed("display", true)
                .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

const yearParser = d3.timeParse("%Y")
const monthParser = d3.timeParse("%m")

const x = d3.scaleTime()
            .domain(d3.extent(data,function(d){
              let year = yearParser(d.year)
              return year
            }))
            .range([0,width]);

const y = d3.scaleTime()
            .domain([monthParser(data[0].month),monthParser(data[11].month)])
            .range([0,height-yOffset])

const xAxis = d3.axisBottom(x)
                .tickFormat(d3.timeFormat("%Y")).tickSize(9)

const yAxis = d3.axisLeft(y)
                .tickFormat(d3.timeFormat("%B")).tickSize(0).tickPadding(6);

const colorScale = d3.scaleQuantile()
                      .domain(d3.extent(data,function(d){
                        return d.degree
                      }))
                      .range(colors)
function toolTipText(d){
  const rawMonth = monthParser(d.month);
  const monthFormat = d3.timeFormat("%B");

  const month = monthFormat(rawMonth)
  let text = '<strong>' + month + " " + d.year + '</strong>'+ '<br>';
  text +=  d.degree.toFixed(3) +' °C'+ '<br>';
  text += 'Variance: '+d.variance +' °C'+ '<br>';
  return text
}

function drawAxis(params){
  //draw xAxis
  this.append("g")
      .call(params.axis.x)
      .classed("x axis", true)
      .attr("transform", "translate(0,"+ height +")")
      .selectAll("text")
        .style("font-size",16)

  //draw yAxis
  this.append("g")
      .call(params.axis.y)
      .classed("y axis",true)
      .attr("transform","translate(0,0)")
        .selectAll("text")
        .attr("dy",25)
        .style("font-size",14)

  //label x axis
  this.select(".x.axis")
      .append("text")
      .classed("x axis-label",true)
      .attr("transform","translate(-60,"+ -height/2 +") rotate(-90)")
      .style("fill","black")
      .text("Months")

  this.select(".y.axis")
      .append("text")
      .classed("y axis-label",true)
      .attr("transform","translate("+ width/2 +","+ (height+60) +")")
      .style("fill","black")
      .text("Years")
}

function plot(params){
  if (params.initialize){
    drawAxis.call(this,params)
  }
  //enter()
  this.selectAll(".degree")
    .data(params.data)
    .enter()
      .append("rect")
      .classed("degree", true)

  //update
  this.selectAll(".degree")
    .transition()
    .delay(100)
    .attr("x",function(d,i){
      let year = yearParser(d.year)
      return x(year)
    })
  this.selectAll(".degree")
    .attr("y",function(d,i){
      let month = monthParser(d.month)
      return y(month)
    })
  this.selectAll(".degree")
    .attr("width", 4)
  this.selectAll(".degree")
    .attr("height", yOffset)
  this.selectAll(".degree")
    .style("fill", function(d,i){
      return colorScale(d.degree)
    })
  .on("mouseover",function(d,i){
    let text = toolTipText(d)
    toolTip.transition()
          .style("opacity",.9)
    toolTip.html(text)
          .style("left", (d3.event.pageX + 15) + "px")
          .style("top", (d3.event.pageY - 28) + "px")

    d3.select(this)
      .style("stroke","gray")
      .style("stroke-width", 3)
  })
  .on("mouseout",function(d,i){
    toolTip.transition()
      .style("opacity",0)
    d3.select(this)
      .style("stroke","none")
  })

  //exit()
  this.selectAll(".degree")
    .data(params.data)
    .exit()
    .remove()
}

plot.call(chart,{
  base: base,
  data: data,
  axis: {
    x: xAxis,
    y: yAxis
  },
  initialize: true
})

1 个答案:

答案 0 :(得分:3)

这是一个快速实施:

   function drawLegend(){
      var legend = this.select(".x.axis").append("g"),
        legW = 40;

      legend.selectAll('rect')
        .data(colorScale.range())
        .enter()
        .append('rect')
        .attr('width', legW)
        .attr('x', function(d,i){
          return i * legW;
        })
        .attr('y', 50)
        .attr('height', 20)
        .style('fill', function(d){
          return d;
        });

      legend.selectAll('text')
        .data(colorScale.quantiles())
        .enter()
        .append('text')
        .attr('x', function(d,i){
          return (i + 1) * legW;
        })
        .attr('y', 80)
        .text(function(d,i){
          var rv = Math.round(d*10)/10;
          if (i === 0) rv = '<' + rv;
          else if (i === (colorScale.quantiles().length - 1))  rv = '>' + rv;
          return  rv;
        })
        .style('fill', 'black')
        .style('stroke', 'none');
    }

完整运行代码:

&#13;
&#13;
<html lang="en">

<head>
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <meta charset="UTF-8" />
  <title>FCC Heat-Map</title>
  <style>
    body,
    html {
      text-align: center;
      background: #e2e2e2;
    }
    
    #canvas {
      margin: 65px auto;
      width: 1300px;
      height: 600;
      background: white;
      -webkit-box-shadow: 0px 0px 31px -5px rgba(0, 0, 0, 0.5);
      -moz-box-shadow: 0px 0px 31px -5px rgba(0, 0, 0, 0.5);
      box-shadow: 0px 0px 31px -5px rgba(0, 0, 0, 0.5);
    }
    
    #chart {}
    
    .title {
      padding-top: 11px;
      font-size: 2em;
      font-weight: bold;
    }
    
    .year {
      font-size: 1.7em;
    }
    
    .description {
      font-size: 0.9em;
      margin-bottom: -35px;
    }
    
    .axis-label {
      font-size: 25px;
    }
    
    .toolTip {
      color: white;
      position: absolute;
      text-align: center;
      max-width: 250px;
      max-height: 98px;
      padding: 7px;
      font: 15px sans-serif;
      background: black;
      border: 25px;
      border-radius: 12px;
      line-height: 18px;
      pointer-events: none;
      box-shadow: 0px 0px 12px -10px;
    }
  </style>
</head>

<body>
  <div id="canvas"></div>
  <script src="//d3js.org/d3.v4.min.js"></script>
  <script src="//ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>

  <script>
    var colorScale = null;
    $(document).ready(function() {
      const w = 1300;
      const h = 600;
      const margin = {
        top: 50,
        bottom: 80,
        left: 100,
        right: 20
      }

      function title() {
        const mainTitle = document.createElement("div");
        mainTitle.innerHTML = "Monthly Global Land-Surface Temperature"
        mainTitle.className = "title";

        const year = document.createElement("div");
        year.innerHTML = "1753 - 2015";
        year.className = "year";

        const description = document.createElement("div")
        description.innerHTML = 'Temperatures are in Celsius and reported as anomalies relative to the Jan 1951-Dec 1980 average.<br>' +
          'Estimated Jan 1951-Dec 1980 absolute temperature ℃: 8.66 +/- 0.07';
        description.className = "description"

        let chart = document.getElementById("canvas")

        chart.appendChild(mainTitle)
        chart.appendChild(year)
        chart.appendChild(description)


      }

      function render(base, rawData) {
        let toolTip = d3.select("#canvas")
          .append("div")
          .classed("toolTip", true)
          .style("opacity", 0)

        const colors = ["#5e4fa2", "#3288bd", "#66c2a5", "#abdda4",
          "#e6f598", "#ffffbf", "#fee08b", "#fdae61",
          "#f46d43", "#d53e4f", "#9e0142"
        ];

        const width = w - (margin.left + margin.right);
        const height = h - (margin.top + margin.bottom);
        const yOffset = 40;

        //lets create new object to add degree key and its value
        data = rawData.map(oneData => {
          let degree = base + oneData.variance
          return Object.assign({}, oneData, {
            degree: degree
          })
        })


        const svg = d3.select("#canvas")
          .append("svg")
          .attr("id", "chart")
          .attr("width", w)
          .attr("height", h)

        const chart = svg.append("g")
          .classed("display", true)
          .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

        const yearParser = d3.timeParse("%Y")
        const monthParser = d3.timeParse("%m")

        const x = d3.scaleTime()
          .domain(d3.extent(data, function(d) {
            let year = yearParser(d.year)
            return year
          }))
          .range([0, width]);

        const y = d3.scaleTime()
          .domain([monthParser(data[0].month), monthParser(data[11].month)])
          .range([0, height - yOffset])

        const xAxis = d3.axisBottom(x)
          .tickFormat(d3.timeFormat("%Y")).tickSize(9)

        const yAxis = d3.axisLeft(y)
          .tickFormat(d3.timeFormat("%B")).tickSize(0).tickPadding(6);

        colorScale = d3.scaleQuantile()
          .domain(d3.extent(data, function(d) {
            return d.degree
          }))
          .range(colors)

        function toolTipText(d) {
          const rawMonth = monthParser(d.month);
          const monthFormat = d3.timeFormat("%B");

          const month = monthFormat(rawMonth)
          let text = '<strong>' + month + " " + d.year + '</strong>' + '<br>';
          text += d.degree.toFixed(3) + ' °C' + '<br>';
          text += 'Variance: ' + d.variance + ' °C' + '<br>';
          return text
        }

        function drawAxis(params) {
          //draw xAxis
          this.append("g")
            .call(params.axis.x)
            .classed("x axis", true)
            .attr("transform", "translate(0," + height + ")")
            .selectAll("text")
            .style("font-size", 16)

          //draw yAxis
          this.append("g")
            .call(params.axis.y)
            .classed("y axis", true)
            .attr("transform", "translate(0,0)")
            .selectAll("text")
            .attr("dy", 25)
            .style("font-size", 14)

          //label x axis
          this.select(".x.axis")
            .append("text")
            .classed("x axis-label", true)
            .attr("transform", "translate(-60," + -height / 2 + ") rotate(-90)")
            .style("fill", "black")
            .text("Months")

          this.select(".y.axis")
            .append("text")
            .classed("y axis-label", true)
            .attr("transform", "translate(" + width / 2 + "," + (height + 60) + ")")
            .style("fill", "black")
            .text("Years")
        }
        
        function drawLegend(){
          var legend = this.select(".x.axis").append("g"),
            legW = 40;
          
          legend.selectAll('rect')
            .data(colorScale.range())
            .enter()
            .append('rect')
            .attr('width', legW)
            .attr('x', function(d,i){
              return i * legW;
            })
            .attr('y', 40)
            .attr('height', 20)
            .style('fill', function(d){
              return d;
            });
            
          legend.selectAll('text')
            .data(colorScale.quantiles())
            .enter()
            .append('text')
            .attr('x', function(d,i){
              return (i + 1) * legW;
            })
            .attr('y', 70)
            .text(function(d,i){
              var rv = Math.round(d*10)/10;
              if (i === 0) rv = '<' + rv;
              else if (i === (colorScale.quantiles().length - 1))  rv = '>' + rv;
              return  rv;
            })
            .style('fill', 'black')
            .style('stroke', 'none');
        }

        function plot(params) {
          if (params.initialize) {
            drawAxis.call(this, params)
          }
          //enter()
          this.selectAll(".degree")
            .data(params.data)
            .enter()
            .append("rect")
            .classed("degree", true)

          //update
          this.selectAll(".degree")
            .transition()
            .delay(100)
            .attr("x", function(d, i) {
              let year = yearParser(d.year)
              return x(year)
            })
          this.selectAll(".degree")
            .attr("y", function(d, i) {
              let month = monthParser(d.month)
              return y(month)
            })
          this.selectAll(".degree")
            .attr("width", 4)
          this.selectAll(".degree")
            .attr("height", yOffset)
          this.selectAll(".degree")
            .style("fill", function(d, i) {
              return colorScale(d.degree)
            })
            .on("mouseover", function(d, i) {
              let text = toolTipText(d)
              toolTip.transition()
                .style("opacity", .9)
              toolTip.html(text)
                .style("left", (d3.event.pageX + 15) + "px")
                .style("top", (d3.event.pageY - 28) + "px")

              d3.select(this)
                .style("stroke", "gray")
                .style("stroke-width", 3)
            })
            .on("mouseout", function(d, i) {
              toolTip.transition()
                .style("opacity", 0)
              d3.select(this)
                .style("stroke", "none")
            })

          //exit()
          this.selectAll(".degree")
            .data(params.data)
            .exit()
            .remove()
            
          drawLegend.call(this);
        }

        plot.call(chart, {
          base: base,
          data: data,
          axis: {
            x: xAxis,
            y: yAxis
          },
          initialize: true
        })

      }
      const url = 'https://raw.githubusercontent.com/FreeCodeCamp/ProjectReferenceData/master/global-temperature.json';
      $.ajax({
        type: "GET",
        dataType: "json",
        url: url,
        beforeSend: () => {},
        complete: () => {},
        success: data => {
          title()
          const baseTemperature = data.baseTemperature;
          const dataAPI = data.monthlyVariance;
          render(baseTemperature, dataAPI);
        },
        fail: () => {
          console.log('failure!')
        },
        error: () => {
          let chart = document.getElementById('card');
          chart.style.display = "table"
          let errorMessage = document.createElement("h1");
          errorMessage.innerHTML = "ERROR 404: File Not Found!"
          errorMessage.className = "errorMessage";
          chart.appendChild(errorMessage)
        }
      });
    });
  </script>


</body>

</html>
&#13;
&#13;
&#13;