如何使用D3使用条形图可视化数据
#javascript #网络开发人员 #初学者 #d3

也许您只是想到,自编码旅程开始以前,您以前还没有看到任何数据,或者也许您还没有找到一个很好的博客来解释如何可视化数据。

不再说。

在这篇博客文章中,我们正在使用我的freecodecamp数据可视化项目作为现实生活中的demo

我们还将使用一个名为d3.js的JavaScript库,对于那些不熟悉D3的人,它是一个JavaScript库,用于在Web浏览器中生成动态的交互式数据可视化。

第一步是在html的头部添加D3脚本标签。

<script src="https://d3js.org/d3.v7.min.js"></script>

添加了此信息,恭喜我们现在可以可视化数据。

我牢记D3可能是一见钟情的非常令人困惑的事情,这就是为什么我要将其分解成小得多的原因,以便您了解我们为什么要做的事情。

HTML

1.在您的HTML中添加一个SVG元素:

这可以通过添加以下代码
来手动完成

   <svg id="chart"></svg>

您的svg id是您喜欢的任何东西,只要它与正在做的事情有关。

现在,添加SVG无需做任何事情,而是将其余的元素放在我们稍后将要放置的元素。在进入JavaScript方面之前,我们的条形图需要标题。

2.添加文本元素:

我们可以再次将其直接在我们的svg中加入text标签,在svg标签中添加下面的行。

 <text id="title" x="280" y="40">United States GDP</text>

不是数学的粉丝吗?不要混淆上面的xy仅有助于定位,因为它的行为不像正常的html元素,x是左侧,y是最高的,将其视为左边的余量和保证金的顶部。

总而言之,我们的html文件应该看起来与此相似

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>bar chart</title>
    <script src="https://d3js.org/d3.v7.min.js"></script>
</head>
    <body>
       <svg id="canvas">
         <text id="title" x="280" y="40">United States GDP</text>
        </svg>
    </body>
</html>

一些CSS

如果您不准备给您的条形图一些样式,我建议给svg至少一个background-color,以便您可以看到将要进行的更改,对于此项目,我添加了一些样式,您可以自定义这适合您的口味。

 body {
     width: 100%;
     height: 100vh;
     margin: 0;
     padding: 0;
     display: flex;
     flex-direction: column;
     justify-content: center;
     align-items: center;
     background-color: #4a4e69;
     font-family: Arial, Helvetica, sans-serif;
  }
  svg {
     background-color: white;
     border-radius: 10px;
     padding: 10px;
  }
  #title {
     font-size: 20px;
  }
   #tooltip {
     margin-top: 20px;
     font-size: 18px;
     color: white;
  }
  rect {
     fill: #4a4e69;

  }
  rect:hover {
     fill: #f2e9e4
  }

使用htmlcss,让我们了解你在这里的原因。

JavaScript

1.定义一些变量:

在您的script中,我们将定义将在我们的条形图创建中将使用的变量,在您的script
中添加下面的代码

let values = [];

let heightScale;
let xScale;
let xAxisScale;
let yAxisScale;

let width = 800;
let height = 600;
let padding = 40; 

values数组是空的,因为稍后它将包含我们将要从API获得的数据。

heightScale将决定每个条的高度,因为它不能说这样的恒定值像50或60一样,如果是这样的话,那么所有条形都将具有相同的高度,而不论其代表的值如何。

xScale将确定棒在图表中的水平放置位置。

xAxisScale将用于在底部创建xAxis,而yAxisScale将用于创建左侧的yAxis,您可以将其视为图形上的x和y轴。

widthheightpadding是将添加到svg的属性。

2.选择svg

要在普通JavaScript中选择一个元素,我们通常会经过document.getElementById()document.querySelector()或Whate的路线

let svg = d3.select('svg')

就是这样,您已经选择了html中的svg元素,并将其保存在称为svg的变量中,如果文档中有多个svg,则它将返回第一个元素,您也可以通过id选择。或元素的class,例如

let svg = d3.select('#chart')

两者都是在D3中选择元素的有效方法。

3.创建功能:

既然我们已经设置了变量,我们需要创建一些将要求做一件事或另一件事以创建我们的条形图的功能。

I.让我们drawChart()

svg是所有其他元素都将坐在xAxisyAxisbars的地方。为此,我们需要使用函数来定义IT的widthheight
选择svg
之后添加下面的代码

  let drawChart = () => {
        svg.attr('width', width)
           .attr('height', height)
   }

上面的代码是一个简单的箭头函数,可将svg变量添加为svg变量。 d3 .attr()首先采用两个参数是我们要添加的属性,在这种情况下是widthheight,第二个是值。

ii。让我们fetch()一些数据:

在进行创建条形图之前,我们需要获取将使用条形图创建条形图的data,现在FreeCodeCamp为我们提供了一个API。

https://raw.githubusercontent.com/freeCodeCamp/ProjectReferenceData/master/GDP-data.json

您可以在新选项卡中打开它,以查看数据的结构。
要获取此数据,我要使用JavaScript自己的fetch方法,在drawChart()之后添加以下代码

  fetch('https://raw.githubusercontent.com/freeCodeCamp/ProjectReferenceData/master/GDP-data.json') //get the data
      .then(res => res.json()) //convert the data to json
      .then(data => { 
       values = data.data //save the data in the values array
       console.log(values) //just so you can see that values now contains the data
 });

上面的代码只需获取数据并将它们存储在values数组中,如果您检查控制台,则应该看到此

data from an api

最后在fetch方法中,我们要调用稍后将要创建的函数,以下面的代码添加到您的fetch

drawChart()
generateScales()
drawBars()
generateAxes()

你应该有这样的东西

  fetch('https://raw.githubusercontent.com/freeCodeCamp/ProjectReferenceData/master/GDP-data.json') 
      .then(res => res.json()) 
      .then(data => { 
       values = data.data;
       console.log(values) 

       drawChart()
       generateScales()
       drawBars()
       generateAxes()
 });

iii。让我们generateScales()

我们需要创建轴将要使用的量表,在您的drawChart()之后添加以下代码

let generateScales = () => {
    heightScale = d3.scaleLinear()
                    .domain([0, d3.max(values, (item) => {
                    return item[1]
                    })])
                   .range([0, height - (2 * padding)])

    xScale = d3.scaleLinear()
              .domain([0, values.length - 1])
              .range([padding, width - padding]) 
}

首先,我们通过调用d3.scaleLinear()函数来定义heightScale,之后我们不得不致电domaindomain只是告诉它,我们期望在这里有最大值和最小值的values,以下面的代码为示例< br>

const arr = [100, 200, 300, 400, 500, 600];

在上面的最小数字上方的数组中,最大值为100,最大值为600。这本质上是域的作用,它获得了数据的最小值和最大数据。

但是域不能神奇地知道数据的最小值和最大,我们必须通过在其中打开一个数组并将最低值指定为第一个项目,而最高值则是第二个。

.domain([0, ])

由于我们正在使用GDP数据,因此我们确定最低的只能是0,否则我们已经与d3.min()一起工作以找到数组中的最低值。

现在设置了域的最小值,我们必须找到最大值作为域数组中的第二个项目,现在在现实生活中,人们不一定总是知道确切的数字,它可能会在将来发生变化,这就是为什么以下代码是数组中的第二个项目,它在返回第二个索引的最高值之​​前将值数组和映射添加到每个项目上。

d3.max(values, (item) => {
 return item[1]
})

这就是我们拥有这个的方式

.domain([0, d3.max(values, (item) => {
            return item[1]
})])

现在到range,它也采用了一个具有两个值的数组,第一个是最小值,然后将最大值以下面的代码示例

// our data
const arr = [100, 200, 300, 400, 500, 600];

如果您考虑一下,如何代表像600这样的巨大价值,那么身高应该是600px?那么,当我们以数千或数百万的方式工作时,会发生什么?这就是range做的

const arr = [100, 200, 300, 400, 500, 600];
// we can say, to represent this data the range can be;
.range([10, 100])

这样做的是,100的值将在10高的高度表示,因为它是最小的,而600将在100的高度表示,因为它是最大值,同样,该值的其余高度将是在我们为其设置的范围内计算。

我希望现在的代码对您来说更有意义

.range([0, height - (2 * padding)])

它将最小范围设置为0,将最大值设置为SVG的高度,将两侧的填充物带走,因此有一些空间。

然后,我们类似地定义了XScale

xScale = d3.scaleLinear()
            .domain([0, values.length - 1])
            .range([padding, width - padding]) 

此量表用于定位xAxis,它将水平位于底部。

xScale添加以下代码之后,在generateScale()中继续前进。

 let datesArr = values.map(item => {
    return new Date(item[0]);
    })  
   console.log(datesArr) // so you can see

   xAxisScale = d3.scaleTime()
                  .domain([d3.min(datesArr), d3.max(datesArr)])
                  .range([padding, width - padding])
  yAxisScale = d3.scaleLinear()
                 .domain([0, d3.max(values, (item) => {
                            return item[1]
                        })])
                 .range([height - padding, padding])

现在,要将此底轴与下面的日期配对

x axis of a bar chart

我们需要将字符串日期从数据转换为实际日期,如果您研究了数据,则会看到每个值的日期位于0

的索引。

data from an api

为了进行这种转换,我们创建了一个datesArr来保持新转换的日期

 let datesArr = values.map(item => {
    return new Date(item[0]);
  })  

如果您console.log(datesArr),您会发现我们的数组包含新转换的日期

an array of converted dates

之后,我们通过调用d3.scaleTime()来定义xAxisScale,因为我们正在使用的值是日期,然后我们称为domainrange,同样适用于yAxisScale

iv。让我们generateAxes()

现在,generateScales()的困难部分已经不在我们的两个轴开始了,请在generateScales()函数下方添加此代码。

 let generateAxes = () => {
       let xAxis = d3.axisBottom(xAxisScale)

       svg.append('g')
       .call(xAxis)
       .attr('id', 'x-axis')
       .attr('transform', `translate(0, ${height - padding})`);

       let yAxis = d3.axisLeft(yAxisScale);

       svg.append('g')
       .call(yAxis)
       .attr('id', 'y-axis')
       .attr('transform', `translate(${padding}, 0)`)
    }

在上面的函数中,我们定义了代表图形的xxAxis,然后使用d3.axisBottom(xAxisScale)方法调用并给予xAxisScale

现在要在屏幕上显示此信息,将g元素附加了已选择的svg,然后我们称为xAxis,该元素实际上告诉它在g中绘制xAxis

 let xAxis = d3.axisBottom(xAxisScale)

       svg.append('g')
       .call(xAxis)
       .attr('id', 'x-axis')
       .attr('transform', `translate(0, ${height - padding})`);

id属性添加到g和一个transform属性中,如果未添加的属性自然会坐在SVG的顶部,请尝试取出transform属性,您将拥有类似的东西

a bar chart containing the x axis only

之后,yScale被定义并使用d3.axisLeft()来调用,就像我们为xAxis所做的那样,另一个g附加到svg,然后我们告诉它绘制其中的yAxis,然后给出了id属性transform属性可以更好地将其定位在svg中,尝试将transform拿出来查看其自然位置。

   let yAxis = d3.axisLeft(yAxisScale);

       svg.append('g')
       .call(yAxis)
       .attr('id', 'y-axis')
       .attr('transform', `translate(${padding}, 0)`)

所以我们总共有类似的东西

a bar chart with both x and y axis

V.让我们kude97:

最后,我们将用代表其数据的条填充图形,在generateScales()之后添加下面的代码

       let drawBars = () => {
        svg.selectAll('rect')
            .data(values)
            .enter()
            .append('rect')
            .attr('class', 'bar')
            .attr('width', (width - (2 * padding)) / values.length)
            .attr('data-date', (item) => {
                return item[0];
            })
            .attr('data-gdp', (item) => {
                return item[1];
            })
            .attr('height', (item) => {
                return heightScale(item[1])
            })
            .attr('x', (item, index) => {
                return  xScale(index)
            })
            .attr('y', (item) => {
                return (height - padding) - heightScale(item[1])
            })

分解

  svg.selectAll('rect')
            .data(values)
            .enter()
            .append('rect')

首先,我们在SVG中选择了所有rect或矩形,无论它们是否存在,我们都将rectvalues.data(values)绑定在一起,将每个rect与每个value与每个value相关在此之后发现并在此之后称呼为每个值,该.append(rect)将创建新的矩形。

然后,我们添加了一些属性,例如widthheightclass,'X'和Y',以适当地显示和定位每个条,其他属性(例如data-datedata-gdp)是为了实现FreeCodeCamp的测试他们也是。

再次尝试拿走xy属性,以查看如何将它们定位为默认位置。

  .attr('class', 'bar')
  .attr('width', (width - (2 * padding)) / values.length)
  .attr('data-date', (item) => {
            return item[0];
         })
   .attr('data-gdp', (item) => {
            return item[1];
    })
    .attr('height', (item) => {
            return heightScale(item[1])
     })
    .attr('x', (item, index) => {
            return  xScale(index)
     })
    .attr('y', (item) => {
            return (height - padding) - heightScale(item[1])
}

您现在应该有类似的东西

A bar chart displaying data

vi。工具提示:

现在,我们已经显示了条形图的最后一件事是添加一个工具提示以显示有关徘徊的特定条形的一些信息,现在为此,我创建了一个非常简单的工具提示,请在下面的下面添加代码drawBars()rect选择之前

  let tooltip = d3.select('body')
                   .append('div')
                   .attr('id', 'tooltip')
                    .style('visibility', 'hidden')
                    .style('width', 'auto')
                    .style('height', 'auto')

这样做的是选择body向其添加div,并给它一个id的属性,该属性设置为tooltip,此后,我们给它提供了一些样式,例如将visibility设置为“将visibility”设置为默认情况widthheightauto

现在我们拥有tooltip,下一件事是在徘徊时显示必要的信息,为此,D3对我们有一种方法,即.on(),在svgy属性后立即添加代码,请想想它是将事件听众添加到酒吧的。

 .on('mouseover', (item, index) => {

     tooltip.style('visibility', 'visible')
            .html(`Date: ${index[0]} Data: <b>${index[1]}</b>`)
            .attr('data-date', index[0])

  })

 .on('mouseout', (item) => {
     tooltip.style('visibility', 'hidden')
   })

使用.on()方法,第一个参数以事件的名称命名,而第二个参数是事件发生时应运行的函数,此函数为itemitemindex。在函数内部,工具提示visibility样式将鼠标效率设置为visible,并给出一些html,其中包括徘徊的特定栏的日期和值。还将data-date属性给出了该工具提示的值,该工具提示的值是悬停在栏上的栏的日期。

 .on('mouseover', (item, index) => {

     tooltip.style('visibility', 'visible')
            .html(`Date: ${index[0]} Data: <b>${index[1]}</b>`)
            .attr('data-date', index[0])

  })

现在,当鼠标离开时,我们在下面有此代码行,该行将工具提示的visibility设置为hidden,以便根据事件发生的事件打开和关闭。

 .on('mouseout', (item) => {
     tooltip.style('visibility', 'hidden')
   })

完整的代码

let values = [];

let heightScale;
let xScale;
let xAxisScale;
let yAxisScale;

let width = 800;
let height = 600;
let padding = 40;

let svg = d3.select('#canvas');

// the drawChart function

let drawChart = () => {
        svg.attr('width', width)
            .attr('height', height)
 }

// the generateScales function

let generateScales = () => {
          heightScale = d3.scaleLinear()
                .domain([0, d3.max(values, (item) => {
                    return item[1]
                })])
                .range([0, height - (2 * padding)])

        xScale = d3.scaleLinear()
            .domain([0, values.length - 1])
            .range([padding, width - padding]) 

       let datesArr = values.map(item => {
           return new Date(item[0]);
       })  
      console.log(datesArr)

       xAxisScale = d3.scaleTime()
                      .domain([d3.min(datesArr), d3.max(datesArr)])
                       .range([padding, width - padding])

       yAxisScale = d3.scaleLinear()
                       .domain([0, d3.max(values, (item) => {
                            return item[1]
                        })])
                       .range([height - padding, padding])
   }

// the drawBars function

  let drawBars = () => {
      let tooltip = d3.select('body')
                       .append('div')
                       .attr('id', 'tooltip')
                       .style('visibility', 'hidden')
                       .style('width', 'auto')
                       .style('height', 'auto')

      svg.selectAll('rect')
           .data(values)
           .enter()
           .append('rect')
           .attr('class', 'bar')
           .attr('width', (width - (2 * padding)) / values.length)
           .attr('data-date', (item) => {
                return item[0];
            })
           .attr('data-gdp', (item) => {
                return item[1];
            })
           .attr('height', (item) => {
                return heightScale(item[1])
            })
           .attr('x', (item, index) => {
                return  xScale(index)
            })
           .attr('y', (item) => {
                return (height - padding) - heightScale(item[1])
            })
           .on('mouseover', (item, index) => {

                tooltip.style('visibility', 'visible')
                .html(`Date: ${index[0]} Data: <b>${index[1]}</b>`)
                .attr('data-date', index[0])

            })

          .on('mouseout', (item) => {
                tooltip.style('visibility', 'hidden')
           })
    }

     // the generateAxes function

      let generateAxes = () => {
       let xAxis = d3.axisBottom(xAxisScale)

       svg.append('g')
       .call(xAxis)
       .attr('id', 'x-axis')
       .attr('transform', `translate(0, ${height - padding})`);

       let yAxis = d3.axisLeft(yAxisScale);

       svg.append('g')
       .call(yAxis)
       .attr('id', 'y-axis')
       .attr('transform', `translate(${padding}, 0)`)
  }

// fetching the data

      fetch('https://raw.githubusercontent.com/freeCodeCamp/ProjectReferenceData/master/GDP-data.json')
      .then(res => res.json())
      .then(data => {

       values = data.data
       console.log(values)

       drawChart()
       generateScales()
       drawBars()
       generateAxes()
 });

结论

恭喜!您刚刚创建了一个条形图。上面的代码显示了HTML,SVG,CSS和JavaScript如何一起创建令人惊叹的数据图。了解每个不同元素的工作方式及其功能对于开发更复杂的数据图至关重要,如果您想了解更多有关D3的信息,那么有许多教程和在线资源可以使您更好地了解它。

这可能是很多要掌握的,但不要太灰心,如果您想让我对D3进行详细的教程,您需要做的就是在下面发表评论。

感谢您的阅读!