使用 Vue.js 构建交互式信息图
已发表: 2022-03-10本文介绍了一种构建交互式信息图的现代方法。 您肯定可以预先获得包含所有可用信息的简单信息图——无需任何用户交互。 但是,考虑构建交互式体验——改变了我们选择的技术环境。 因此,让我们先了解一下,为什么选择 Vue.js? 您会明白为什么 GSAP(GreenSock 动画平台)和 SVG(可缩放矢量图形)成为显而易见的选择。
Vue.js 提供了实用的方法来构建基于组件的动态用户界面,您可以在其中以强大的方式操作和管理 DOM 元素。 在本例中,它将是 SVG。 您可以轻松地更新和管理不同的 SVG 元素——动态地——仅使用 Vue.js 中可用的一小部分特性——这里符合要求的一些主要特性是,数据绑定、列表渲染、动态类绑定等。很少。 这也允许您将相关的 SVG 元素组合在一起,并将它们组件化。
Vue.js 与外部库配合得很好而不会失去它的荣耀,这就是 GSAP。 使用 Vue.js 还有很多其他好处,其中之一就是,Vue.js 允许您为每个组件隔离相关的模板、脚本和样式。 这样,Vue.js 促进了模块化的应用程序结构。
推荐阅读:用 Vue.js 替换 jQuery:无需构建步骤
Vue.js 还打包了强大的生命周期钩子,让您可以利用应用程序的不同阶段来修改应用程序行为。 设置和维护 Vue.js 应用程序不需要很大的投入,这意味着您可以采用分阶段的方法来扩展您的项目。
信息图在视觉上非常轻量级,因为本文的主要目的是学习如何从数据、视觉元素,当然还有 Vue.js(使所有交互成为可能的框架)方面进行思考。 此外,我们将使用 GreenSock,一个用于动画 SVG 元素的库。 在我们深入研究之前,先看一下演示。
我们将从:
- 信息图表数据概览;
- SVG图像准备;
- 在 SVG 图稿的上下文中概述 Vue 组件;
- 代码示例和关键交互性图表。
我们要制作的信息图是关于环法自行车赛的,这是在法国举行的年度自行车赛事。

环法自行车赛数据概览
在信息图表设计中,数据驱动信息图表的设计。 因此,在规划您的信息图表设计时,为给定主题准备好所有数据、信息和统计数据始终是一个好主意。
在 2017 年环法自行车赛期间,我在 7 月份的 21 天比赛中了解了有关这项最大自行车赛事的所有信息,并且我熟悉了这个主题。
我决定在我的设计中追求的基本种族实体是,
- 阶段,
- 团队,
- 路线,
- 优胜者,
- 每条路线的长度和分类。
该过程的下一部分取决于您的思维方式,因此您可以在这里发挥创造力。
我创建了两组数据,一组用于阶段,另一组用于团队。 这两个数据集有多行数据(但within limit
)——它们与自行车的两个轮子相匹配,每个轮子都有多个辐条。 这定义了设计的关键元素,即您在开始时看到The Bicycle Art
——每个辐条都将是交互式的,并负责驱动屏幕上显示的信息。
我在上面提到within limits
,因为在这种情况下,我们的目标不是大数据背景下的全面数据可视化,而是包含高级数据的信息图。
因此,花时间处理数据并寻找可以帮助您传达视觉故事的相似性、差异、层次结构或趋势。 并且不要忘记 SVG 和 Vue.js 的惊人组合,因为它将帮助您在信息(数据)、交互性(Vue.js)和设计元素(SVG Artwork ) 的信息图表。
这是阶段数据对象的片段:
{ "ID": 1, "NAME": "STAGE 01", "DISTANCE": "14", "ROUTE": "KMDUSSELDORF / DUSSELDORF", "WINNER": "THOMAS G.",
"UCI_CODE": "SKY",
"TYPE": "Individual Time Trial",
"DATE": "Saturday July 1st",
"KEY_MOMENT": " Geraint Thomas takes his first win at 32"
}
团队数据对象片段如下:
{ "ID": 1,
"UCI_CODE": "SKY",
"NAME": " TEAM SKY",
"COUNTRY": "Great Britain",
"STAGE_VICTORIES": 1,
"RIDERS": 8
}
该信息图由一个非常简单的逻辑操作。
UCI_CODE (Union Cycliste Internationale)是舞台和团队对象之间的连接键。 当一个阶段被点击时,首先我们将激活该阶段,同时使用UCI_CODE
键来激活相应的获胜队伍。
SVG 准备
准备好几个数据集和自行车艺术的粗略概念,这是我想出的信息图的静态 SVG CodePen。
请参阅 CodePen 上 Krutie(@krutie) 的 Pen Static Bicycle SVG。
我们只为每个车轮创建了一个辐条,这是因为我们将使用数据集中找到的许多记录动态创建其余的辐条,并使用 GreenSock 库为它们设置动画。
创建此 SVG 代码的工作流程也非常简单。 在 Adobe Illustrator 中创建您的信息图作品并保存为 SVG。 确保在 Illustrator 中工作时命名每个group
和layer
,因为您将需要这些 id 来分隔 SVG 代码的部分,这些部分最终将填充 Vue 组件的<template>
区域。 请记住,在 Illustrator 中给出的图层名称会成为 SVG 标记中的element ids
。
您还可以使用 SVGOMG 并进一步优化从 Adobe Illustrator 导出的 SVG 代码。
重要提示:如果您使用 SVGOMG 优化 SVG 标记,您的代码肯定会看起来很整洁,但请注意,它会将所有 <rect> 元素转换为具有d
属性的 <path>。 这会导致矩形的x
和y
值丢失,以防您希望稍后手动调整几个像素。
第二件事,确保取消选中Clean Id
选项(SVGOMG 界面中的右侧选项),这将有助于保持在 Illustrator 中创建的所有组和 ID 完好无损。
Vue 组件概述
即使您的信息图表项目中的交互性和数据流本质上非常简单,您也应该始终花点时间绘制组件的树形图。
如果您不使用任何共享数据机制,这将特别有帮助,其中子组件依赖于从父组件发送的值(即通过道具)或反之亦然(即 this.$emit 事件)。 这是您集思广益这些道具值、发出事件和本地数据的机会——并在开始编写代码之前记录它们。

上图是 Vue 组件的快照,部分源自交互性需求,部分基于 SVG 标记。 您应该能够看到 SVG 标记将如何基于此树结构进行拆分。 从层次结构的角度来看,这是不言自明的。
- 链轮将模仿辐条的旋转。
- Stage 组件是列出所有 21 个阶段的后轮。
- Stage-detail 组件将在曲线路径(左侧)上显示相关信息。
- 团队组件是前轮,它将列出辐条上的所有参与团队。
- Team-detail 组件将在曲线路径(右侧)上显示相关信息。
- 导航将包括用于访问阶段的返回和下一步按钮。
下图代表了上面看到的相同的 Vue 组件,但在信息图设计的上下文中。

少即是多——应该是你在从事类似项目时应该尝试采用的方法。 考虑一下您对动画和过渡的要求,如果您可以使用 TweenLite 而不是 TweenMax - 这样做。 如果您可以选择基本形状和更简单的路径而不是复杂的路径 - 一定要尝试选择易于动画的轻量级元素 - 而不会造成任何性能损失。
下一节将带您了解 GreenSock 动画和 Vue.js 的精彩部分。
绿袜动画
让我们仔细看看:
- 曲线路径上的文字动画;
- 在轮子上说话动画。
请记住在自行车车轮周围看到的曲线路径,该曲线路径略大于自行车车轮的半径。 因此,当我们对这条路径上的文本进行动画处理时,它看起来就好像它遵循了轮子的形状。
请参阅 CodePen 上 Krutie (@krutie) 的弯曲路径上的钢笔文本。
path
和textPath
是 SVG 元素的完美组合,允许您使用xlink:href
属性在任何路径上设置文本。
<pathlanguage-javascript">curvedPath
" stroke="none" fill="none" d="..."/>
<text>
<textPath xlink:href="
#curvedPath
"
class="stageDetail"
startOffset="0%">
{{ stage.KEY_MOMENT }}
</textPath>
</text>
要沿路径为文本设置动画,我们只需使用 GreenSock 为其startOffset
属性设置动画。

tl.fromTo( ".stageDetail", 1, { opacity: 0,
attr: { startOffset: "0%" }
},
{
opacity: 1,
attr: { startOffset: "10%" }
}, 0.5 );
随着您增加startOffset
百分比,文本将进一步穿过圆的周边。
在我们的最终项目中,每次单击任何辐条时都会触发此动画。 现在,让我们继续看动画中更令人兴奋的部分。
动画阶段/轮子内的辐条
从演示中可以看出, stage
和team
组件在本质上是相似的,但有一些小的差异。 所以,让我们只关注自行车的一个轮子。
下面的 CodePen 示例仅放大了三个关键思想:
- 获取阶段数据;
- 根据数据动态排列辐条;
- 单击舞台(辐条)时重新排列辐条。
请参阅 CodePen 上 Krutie (@krutie) 的 Pen TDF Wheel Animation。
您可能已经在上面的静态 SVG CodePen 中注意到,辐条只不过是 SVG 矩形和组合在一起的文本。 我将它们组合在一起,因为我想同时选择文本和矩形来制作动画。
<g v-for="stage in stages"
class="stage">
<rect x="249" y="250" width="215" height="1" stroke="#3F51B5" stroke-width="1"/>
<text transform="translate(410 245)" fill="#3F51B5" >
{{ stage.NAME }}
</text>
</g>
我们将使用从数据源获取的值在 Vue 组件的<template>
区域中呈现它们。
当屏幕上的所有 21 个阶段都可用时,我们将通过调用setSpokes()
来设置它们的初始位置。
// setSpokes() let stageSpokes = document.querySelectorAll(".stage") let stageAngle = 360/this.stages.length _.map(stageSpokes, (item, index) => { TweenMax.to(item, 2, { rotation: stageAngle*index, transformOrigin: "0% 100%" }, 1) }
搭建舞台的三个关键要素是:
- 回转
要旋转辐条,我们将简单地使用 classNamestage
映射所有元素,并设置为每个辐条计算的动态rotation
值。 - 变换原点
注意上面代码中的transformOrigin
值,它与index
值一样重要,因为“0% 100%”使每个辐条能够从车轮中心旋转。 - 舞台角度
这是使用总阶段数除以 360 度来计算的。 这将帮助我们将每个辐条均匀地放置在 360 度圆上。
增加交互性
下一步是在每个阶段添加点击事件,以使其对数据更改具有交互性和反应性——因此,它将为 SVG 图像注入更多活力!
比方说,如果单击舞台/轮辐,它会执行goAnimate()
,它负责使用stageId
参数激活和旋转被单击的舞台。
goAnimate (stageId) { // activate stage id this.activeId = stageId // rotate spokes }
我们将使用 DirectionalRotationPlugin……这是这种交互性的关键成分。 是的,它包含在 TweenMax 中。
使用此插件有三种不同的方式。 它以 1) 顺时针、2) 逆时针和 3) 计算到目的地的最短距离为旋转属性设置动画。
正如您现在已经猜到的那样,我们使用第三个选项来旋转当前阶段和新阶段之间的最短距离。
查看上面的 CodePen,您会看到Stage 01是如何不断地绕着圆圈移动的,它的原始位置以 0 度角留给新的活动阶段。
首先,我们需要找到被点击的舞台的角度,并将其旋转与Stage 01互换。 那么,我们如何找到被点击的舞台的旋转值呢? 看看下面的图表。

例如,如果单击Stage 05 (如上所示),从Stage 01到Stage 05的旅程需要 4 x 角度值。
因此,我们可以使用(Active stage Id - 1) * 17 degree,
后跟 '_short' 字符串后缀来触发定向旋转插件。
angle = 360/21 stages = 17 activeId = 5
new angle = (
(activeId-1)
*angle)+'_short'
= ((5-1)\*17)+'_short'
= 68
最终的goAnimate()
函数如下所示:
_.map(spokes, (item, index) => { if(activeId == index+1) { // active stage TweenMax.to(item, 2, { rotation: 0+'_short', transformOrigin: "0 100%" }) } else if (index == 0) { // first stage TweenMax.to(item, 2,
{ rotation: (activeId*angle)-angle+'_short',
transformOrigin: "0 100%"
})
} else {
TweenMax.to(item, 2,
{ rotation: index*angle+'_short',
transformOrigin: "0 100%"
})
}
}) // end of map
一旦我们准备好后轮,前轮(用于团队)应该遵循相同的逻辑并进行一些调整。
代替舞台,我们将获取团队数据并更新transformOrigin
属性的注册点,以启用从与舞台轮相反的注册点生成辐条。
// set team spokes map(teamSpokes, (index, key) => { TweenMax.to(index, 2, { rotation: angle*key, transformOrigin: "100% 100%"
}, 1)
})
最终项目
像我一样,如果你已经在 Vue 组件本身中编写了所有动画和数据相关的功能。 是时候使用 Vuex 和 Mixins 清理它们了。

VUEX
Vuex 简化了组件之间共享数据的管理,更重要的是,它简化了你的代码,保持methods
和data()
干净整洁,让组件只呈现数据,而不是处理数据。
生命周期钩子非常适合执行任何 HTTP 请求。 当 Vue 应用程序已初始化但尚未挂载到 DOM 中时,我们在created
钩子中获取初始数据。
空状态变量、 stages
和teams
在此阶段使用突变进行更新。 然后,我们使用 watcher(仅一次)来跟踪这两个变量,一旦它们更新,我们就会调用动画脚本(来自mixin.js
)。
每次用户与阶段或团队组件交互时,它都会与 Vuex 存储通信,执行setActiveData
并更新当前阶段和当前团队值。 这就是我们设置活动数据的方式。
当在状态更新后设置活动数据时, goAnimate
将使用更新的值启动(定向旋转)辐条动画。
推荐阅读:使用 Vue.js 创建自定义输入
混合
现在数据由 Vuex 处理,我们将分离出 GreenSock 动画。 这将防止我们的 Vue 组件被长动画脚本弄得杂乱无章。 所有 GreenSock 函数都组合在mixin.js
文件中。
由于您可以访问 Mixins 中的 Vuex Store,所有 GSAP 函数都使用state
变量来为 SVG 元素设置动画。 您可以在此处的 CodeSandbox 示例中看到功能齐全的store.js
和mixin.js
。
结论
创建交互式和引人入胜的信息图表需要您对数据进行分析,对视觉进行创意并使用您使用的技术高效,在这种情况下是 Vue.js。 您可以在项目中进一步使用这些概念。 作为结束语,我将在下面为您提供这个圆形交互式色轮,它使用类似于我们在本文中讨论的想法。
请参阅 CodePen 上的 Krutie (@krutie) 使用 Vue JS 和 GSAP 制作的 Pen Material UI 圆形调色板。
毫无疑问,Vue.js 有很多很棒的特性; 我们能够只用一些东西来创建交互式信息图表,例如观察者、计算属性、mixins、指令(参见色轮示例)和其他一些方法。 Vue.js 是有效地将 SVG 和 GreenSock 动画结合在一起的粘合剂,为您提供了充分的机会同时对任意数量的主题和自定义交互进行创意。