使用 AngularJS 进行数据可视化

Avatar of Nick Moreton
Nick Moreton

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 $200 免费积分!

以下内容是 Nick Moreton 的客座文章。我必须说,我发现这个想法相当有趣。我知道我喜欢在 HTML、SVG 和 CSS 中工作,所以当 Nick 分享我们可以用它来构建结构,并直接使用数据来设置图表样式时,这吸引了我。然后知道,因为您使用的是 Angular,图表可以随着数据的变化而自动更改……这太酷了。

当我开始玩 AngularJS 时,我发现它可以直接在标记中获取数据并使用它,这为创建数据可视化提供了一种非常快速简单的方案。

在本教程中,我将使用内联 CSS 和 SVG 创建三种不同类型的图表。

为什么选择 Angular?

如果您以前从 JavaScript 或 jQuery 中写入过 DOM,您会知道您的代码有多快就会变得杂乱无章,特别是如果您使用多个变量。Angular 允许直接在标记中使用数据,从而使代码清晰、易于阅读和理解。

还有一些很棒的可视化库,但它们自带许多默认样式。通过使用 Angular,可视化完全不受约束,并且会立即拾取您的样式。

我并不是说这是创建数据可视化的最佳方法,但它确实吸引了我!

注意:我不会在这里讲解 Angular 的各个方面,例如应用程序、控制器等的工作原理,我将重点介绍数据。我写了一篇关于 ‘AngularJS 中的 Hello World’ 的文章,它可以帮助您理解其中的一些内容,并且还有许多其他很棒的资源可以帮助您开始使用 Angular。

设置我们的 Angular 应用程序

基本应用程序设置

首先,我们需要设置一个 Angular 应用程序和一个控制器来容纳我们的功能和数据。

(function(){

  var app = angular.module('graphApp',[]);

  app.controller('graphController', function($scope){

  // Code goes here!

  });

})();

默认选项

接下来,我们设置一些默认变量,绑定到控制器 $scope,我们将使用它来控制图表的尺寸,以及 X 轴和 Y 轴的标签。

$scope.width = 600;
$scope.height = 400;
$scope.yAxis = "Sales";
$scope.xAxis = "2014"

数据

然后我们添加我们的数据,以 JSON 格式编写,绑定到控制器的 $scope。

$scope.data = [
  {
    label: 'January',
    value: 36
  },
  {
    label: 'February',
    value: 54
  },

  // .... and so on .....

  {
    label: 'November',
    value: 252
  },
  {
    label: 'December',
    value: 342
  }
];

查找最大值

最后,我们编写一个循环来遍历数据以查找最大值并将其设置为变量。我们将在以后使用它来定位可视化中的元素。

$scope.max = 0;

var arrLength = $scope.data.length;
for (var i = 0; i < arrLength; i++) {
  // Find Maximum X Axis Value
  if ($scope.data[i].value > $scope.max)
  $scope.max = $scope.data[i].value;
}

这就是我们 JavaScript 的全部内容。实际上,这都是关于设置数据和变量以便在以后的标记中使用。

设置标记、模板和 CSS

现在我们需要设置可视化应用程序标记和 CSS。

<div ng-app="graphApp">

  <div ng-controller="graphController as graph">

    <div class="graph" style="width:{{width}}px; height:{{height}}px;" >

      <div class="y" style="width:{{height}}px;">{{yAxis}}</div>

      <div class="x">{{xAxis}}</div>

    </div>

  </div>

</div>

在 HTML 中,我们可以开始将数据直接从 JavaScript 拉入标记中,既作为内容(例如 X 轴和 Y 轴标签),也作为内联样式来控制图表的宽度和高度。

注意:“y” div 使用 {{height}} 变量作为 width CSS 属性 - 这是因为在我们的 CSS 中,我们将逆时针旋转它 90 度。
.chart {
  border-left: 1px solid black;
  border-bottom: 1px solid black;
  margin: 60px auto;
  position: relative;
}

.y {
  position: absolute;
  transform: rotate(-90deg);
  transform-origin: bottom left;
  bottom: 0;
  padding: 5px;
}

.x {
  position: absolute;
  top: 100%;
  width: 100%;
  padding: 5px;
}

柱状图

好的,为了在图表上创建数据,我们将使用 Angular 的 ng-repeat 函数。这将遍历我们的数据数组,并为每个条目输出我们包装在 ng-repeat 中的任何标记。

首先,我们将创建一个柱状图。首先,我为 bar 类设置了一些默认 CSS,它将位置设置为绝对值,并添加背景颜色。

.bar {
  background: blue;
  position: absolute;
  bottom: 0;
}

然后,使用 ng-repeat,为每个条目创建一个带有 bar 类的 <div>,我们将写入

<div ng-repeat="bar in data" class="bar"></div>

此代码位于我们的 <div class="graph"></div> 中。

现在,我们可以使用我们的 Angular 数据(以及一些数学!) 在一些内联 CSS 中来控制每个柱形图的高度和宽度。

<div ng-repeat="bar in data" class="bar" style="height:{{bar.value / max * height}}px; width:{{width / data.length - 5}}px;"></div>

高度是值除以最大值(在我们 Angular 应用程序中设置的),乘以图表的总高度。这样可以确保数据中的最大值将占用图表的全部高度。

宽度是总宽度除以条目数量,并减去 5 像素,以便在我们将柱形图放置在 X 轴上时创建一些间距。

最后,我们需要使用 left CSS 属性将柱形图定位在 X 轴上。

<div ng-repeat="bar in data" class="bar" style="height:{{bar.value / max * height}}px; width:{{width / data.length - 5}}px; left:{{$index / data.length * width}}px;"></div>

在这里,我们使用 $index,一个 Angular 变量,它将从 0 开始,并在每个后续柱形图中增加。我们将索引除以条目总数,并将结果乘以图表的总宽度。这样会将第一个柱形图放置在 0 处,然后将其余柱形图等间隔地放置在图表上。

值得注意的是,如果您希望图表具有流畅性,则可以乘以 100 并使用 % 作为单位,而不是像素。

就是这样 - 我们的柱状图现在已经创建完成,您可以开始使用 CSS 进行美化了!

完整的最终图表代码如下所示

<div class="chart" style="width:{{width}}px; height:{{height}}px;">

  <!-- Labels -->
  <div class="y" style="width:{{height}}px;">{{yAxis}}</div>
  <div class="x">{{xAxis}}</div>

  <!-- Data -->
  <div ng-repeat="bar in data" class="bar" style="height:{{bar.value / max * height}}px; width:{{width / data.length - 5}}px; left:{{$index / data.length * width}}px;"></div>

</div>

查看 CodePen 上 Nick Moreton (@nickmoreton) 的 1fe35094c4c1d447bdf59c5588167274

点状图

点状图的方法非常类似。

同样,我为点设置了一些默认 CSS

.dot {
  background: blue;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  position: absolute;
}

对于点,我们不需要担心宽度或高度,我们只需使用相同的数据和数学方法从左侧和底部定位点即可。

唯一的区别是我们为 $index 添加了 0.5,这样点将位于分配给它的空间的中心。

<div ng-repeat="dot in data" class="dot" style="bottom:{{dot.value / max * height}}px; left:{{($index + 1) / data.length * width}}px;"></div>

完整的图表如下所示

<div class="chart" style="width:{{width}}px; height:{{height}}px;">

  <!-- Labels -->
  <div class="y" style="width:{{height}}px;">{{yAxis}}</div>
  <div class="x">{{xAxis}}</div>

  <!-- Data -->
  <div ng-repeat="dot in data" class="dot" style="bottom:{{dot.value / max * height}}px; left:{{($index + 0.5) / data.length * width}}px;"></div>

</div>

查看 CodePen 上 Nick Moreton (@nickmoreton) 的 0ee74365fd766311a52aa0d129c22ea8

SVG 线状图

除了内联 CSS,我们还可以在 SVG 数据中使用 Angular 数据值。

此 CSS 为

svg {
  position: absolute;
  transform: rotateX(180deg);
  left: 0;    
}

line  {
  stroke:red;
  stroke-width:3px;
}

我正在旋转 SVG,因为默认情况下它会从顶部处理值,我们需要将其翻转以从底部获取值。

首先,我们需要创建一个跨越图表整个区域的 SVG,然后在其中对 line 元素使用 ng-repeat。

每个 line 元素都需要 X 轴 (x1, x2) 和 Y 轴 (y1, y2) 上的起点和终点。

X 轴非常简单 - 我们遵循与之前相同的系统,以便每条线在图表上等间隔地分布,从 0 开始使用 $index,并在下一条线开始的地方结束,使用 $index + 1。

对于 Y 轴,这就是 $index 变量发挥作用的地方,因为它允许我们从数组中的前一个或下一个条目中选择值。

每条线的初始 Y 点值来自使用 `data[$index - 1].value` 的上一条数据条目,然后我们应用与之前类似的数学运算。对于第二个 Y 点,我们可以直接从条目中调用直线值。

这听起来可能很复杂(我可以向你保证,想通这一点有点让人头疼!),但希望这个解释加上下面的代码能帮助你理解它!

<svg style="width:{{width}}px; height:{{height}}px;">

  <line ng-repeat="line in data" x1="{{$index / data.length * width}}" y1="{{data[$index - 1].value / max * height}}" x2="{{($index + 1) / data.length * width}}" y2="{{line.value / max * height}}">
  </line>

</svg>

最终的图表代码如下所示

<div class="chart" style="width:{{width}}px; height:{{height}}px;">

  <!-- Labels -->
  <div class="y" style="width:{{height}}px;">{{yAxis}}</div>
  <div class="x">{{xAxis}}</div>

  <!-- Data -->
  <svg style="width:{{width}}px; height:{{height}}px;">

    <line ng-repeat="line in data" 
          x1="{{$index / data.length * width }}" 
          y1="{{data[$index - 1].value / max * height}}" 
          x2="{{($index + 1) / data.length * width}}" 
          y2="{{line.value / max * height}}">
    </line>

 </svg>

</div>

参见 Pen a3e89703d7671fa81d5c2ceb20f7b150 by Nick Moreton (@nickmoreton) on CodePen.

下一步做什么?

你也可以在类名中使用 `$index` 变量,例如 `<element class="classname{{$index}}">`,这允许对每个条形图、点或线进行精细控制 - 我们可以使用它来为这些可视化效果制作动画。

我制作了一个带有动画的完整点/线图表示例,以及使用我们数据中每个条目中的标签的 CSS 工具提示。你可以在 这里 查看它。

我还创建了一个包含所有这些示例的 Pen

我希望本教程对你有所帮助,我很乐意听到你使用 Angular 创建的任何可视化效果!