头图

引言

在大屏产品中,可视化扮演着信息展示和传达、用户体验和互动、数据分析和决策支持、品牌展示和差异化、故事叙述和信息呈现等至关重要的角色。作为可视化图表的重要载体之一,大屏与智能BI产品不管是在产品设计,还是可视化设计的侧重点都有很大不同。

数据大屏产品

本文以 DataWind 数据大屏产品为例,为您揭示如何建设令人叹为观止的数据大屏。

场景化的主题色彩配置

为不同行业的数据大屏使用不同的颜色主题可以提高数据可视化效果、增强数据传达的意义、提高品牌识别度和满足用户需求,从而更好地呈现数据。

图表库能够支持场景化的主题色彩配置,这意味着用户可以根据不同的行业需求来选择不同的主题色彩,以更好地呈现数据。在不同的行业中,用户对于数据可视化的需求和期望可能会有所不同,因此场景化的主题色彩配置可以帮助用户更好地满足其特定的需求。

例如,在金融行业中,用户可能更注重数据的准确性和可靠性,因此金融行业的图表库可能需要提供更加严肃和专业的主题色彩配置;而在广告行业中,用户更注重图表的视觉效果和吸引力,因此广告行业的图表库可能需要提供更加鲜艳和夸张的主题色彩配置。

不同场景下的案例效果

分析场景

金融场景

文旅场景

实现揭秘

从上述案例中,我们可以注意到大屏可视化色彩设计有两个明显的特点:1、行业相关联的颜色主题;2、图元渐变着色。

颜色主题注册和切换

主题色板的构造基于于语义化及美观设计原则,即结合使用场景(保证大屏主题的场景表现力)、配色公式(保证图元在美观度、差异度等方面的配色效果和信息表达力)等逻辑进行设计。而针对大屏业务场景,我们沉淀出一套色彩方案,涵盖党建、金融、科技、文旅等行业,结合 VChart 主题注册和切换能力,做到开箱即用。

色板的具体信息开放出来供大家参考: https://github.com/VisActor/VChart/blob/develop/docs/assets/themes/colors.json

核心代码

const response = await fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/theme.json');
const colorTheme = await response.json();

// 注册主题
const theme = {};
for (const colorKey in colorTheme) {
  const colorName = colorTheme[colorKey].name;
  theme[colorName] = {
    colorScheme: {
      default: colorTheme[colorKey].colors
    }
  };
  VChart.ThemeManager.registerTheme(colorKey, theme[colorName]);
}

// 主题切换
  VChart.ThemeManager.setCurrentTheme('volcanoBlue');

在线示例:https://www.visactor.io/vchart/guide/tutorial_docs/Theme/Cust...

渐变效果实现

纯色到渐变色的转换:纯色 => 图元填充渐变 + 图元描边边渐变。

示例地址: https://codesandbox.io/s/bar-gradient-ycr8m8

核心代码

const gradientCallback = (datum, ctx, type) => {
  return {
    gradient: "linear",
    x0: 0,
    y0: 0,
    x1: 0,
    y1: 1,
    stops: [
      {
        offset: 0,
        fillOpacity: 0,
        color: hexToRgba(ctx.seriesColor(datum.type), 1),
      },
      {
        offset: 1,
        fillOpacity: 1,
        color: hexToRgba(ctx.seriesColor(datum.type), 0),
      }
    ]
  };
};

// 以同样的方式在主题中注册和切换
const theme = {
    series: {
      bar: {
        bar: {
          style: {
            fill: (datum, ctx) => gradientCallback(datum, ctx, "fill"),
            stroke: (datum, ctx) => gradientCallback(datum, ctx, "stroke"),
            lineWidth: 2
          }
        }
      }
    }
}

VChart.ThemeManager.registerTheme(theme, 'gradient');
VChart.ThemeManager.setCurrentTheme('gradient');

渐变色详细说明参考:https://www.visactor.io/vchart/guide/tutorial_docs/Chart_Conc...

最终效果

最终呈现的图表视觉效果不管是在提高吸引力,引流观众方面,还是在提升场景辨识度上都颇具成效。

在线Demo

在这里插入图片描述

示例地址:https://www.visactor.io/vchart/demo/theme/theme-style

更多主题教程见: https://www.visactor.io/vchart/guide/tutorial_docs/Theme/Theme

高度定制化的辅助装饰

在图表图元上做细微的装饰不仅可以提高视觉吸引力,增加图表的美感和吸引力,使得读者更愿意阅读和理解数据;更重要地,它还可以增加品牌识别度,帮助提高品牌识别度和专业感,使得数据大屏更具个性化和品牌化。

在图表中,图元负责实现数据到图形的映射,比如:数值的大小映射为矩形的高度,数值的类型映射为矩形的颜色等。而组件则负责数据的数值标记、图元的交互,比如:坐标轴以标签和刻度的形式标记某个高度对应的具体数值大小。

辅助装饰通常围绕着图元和组件展开,对图元的辅助装饰负责突出数据,对组件的装饰则负责个性化展示。

实现揭秘

图元装饰

由于装饰的位置强依赖于图元,在大屏侧无法准确定位并添加。通过VChart提供拓展Mark配置能力可以直接渲染出依附于既有图元的装饰图元,完美解决这一问题。

示例地址: https://codesandbox.io/s/line-with-halo-j54hv8


示例地址: https://codesandbox.io/s/line-with-halo-forked-xccmvq?file=/s...

核心代码

extensionMarks: [
    {
        name: 'markSymbol', // 拓展mark名称
        type: 'symbol', // 拓展mark类型
        from: {
            mark: 'symbol' // 当前图表的既有图元
        },
        spec: { // 拓展mark样式
            normal: {
                size: 10,
                fillOpacity: 0.1,
                fill: '#FFF',
                strokeOpacity: 0.5,
                stroke: {
                  gradient: 'conical', // // 锥形渐变,startAngle 代表起始弧度,endAngle 代表结束弧度,x, y 为坐标,取值范围 0 - 1
                  x: 0.5,
                  y: 0.5,
                  startAngle: 0,
                  endAngle: 6.283185307179586,
                  stops: [
                    { offset: 0, color: 'red' },      // 0% 处的颜色
                    { offset: 0.2, color: 'blue' },   // 20% 处的颜色
                    { offset: 0.4, color: 'orange' }, // 40% 处的颜色
                    { offset: 0.6, color: 'pink' },   // 60% 处的颜色
                    { offset: 0.8, color: 'green' },  // 80% 处的颜色
                    { offset: 1, color: 'purple' }    // 100% 处的颜色
                  ],
                }
            }
        }
    }
]

通过VChart 自定义渲染能力,还可以支持更多图元的纹理装饰。

组件装饰

为了标记出坐标轴的覆盖范围,我们需要增加轴辅助装饰。用VChart的轴tick回调函数可以实现这一效果。实现原理是,根据回调中的index判断tick是否是第一个或最后一个,如果是的话则设置为可见,不是的话则隐藏。

示例地址: https://codesandbox.io/s/axes-with-tick-sign-9n9jtf

核心代码

axes: [{
  // ...
    tick: {
      size: 4,
      normal: {
        strokeWidth: 2,
        stroke: tickStroke,
        visible: (...args) => {
          return args[0].index === 0 || args[0].index === 1
        },
      },
    }
}]

丰富灵活的动画效果

从数据场景而言,大屏通常用于展示实时数据、动态信息和即时反馈。通过动态效果,可以更好地呈现和展示这些数据的动态变化和趋势,使观众能够及时了解最新的数据情况。

从现实场景而言,大屏通常在公共场所或会议展览等场合使用,需要通过瞬间的视觉冲击来吸引人们的注意,使他们停下来观看。

在这里插入图片描述

不同图表的动画效果

设计动画的前提是明确动画的目标,不同的目标可能需要不同类型、频率和复杂程度的动画实现。显而易见,贯穿大屏数据可视化场景的动画目标如下:

  1. 强调数据重点和变换,通过设计合适的过渡和动作,可以使关键数据或信息在动画中突出显示。重点和关键变化的动画应该被放在视觉的焦点位置,使其更易于观察和理解。
  2. 吸引观众的注意,通过炫酷的动态效果可以迅速抓人眼球,但同时又需要控制速度和流畅度,以免影响观感。

根据目标在大屏中可以总结出数据更新动画、高亮动画和氛围动画,不同图元的动画效果各不相同。

柱图数据更新动画

示例地址: https://codesandbox.io/s/animation-bar-yypwgs?file=/src/index.ts

柱图氛围动画

示例地址: https://www.visactor.io/vchart/guide/tutorial_docs/extend/cus...

面积图数据更新动画

示例地址: https://codesandbox.io/s/animation-line-6nlpd4?file=/src/inde...

除上述图表外,还有饼图、散点图等基本图表类型对应的动画,在此不一一赘述。

实现揭秘

VChart动画的实现依赖于VRender绘图引擎与VGrammar可视化语法。从实现分工而言,VRender提供任意图形的形变能力,VGrammar负责控制形变动画的流程,VChart进行上层封装并将配置以简洁易用的方式暴露给用户。

对于上述动画,VChart层的实现主要依赖于对VGrammar 动画语法的封装

柱图数据更新动画

示例地址:https://codesandbox.io/s/bar-update-animation-7jkd3j?file=/sr...

核心代码

animationUpdate: {
     type: 'moveIn',
    duration: 500
  }

柱图数据高亮动画

在这里插入图片描述

示例地址: https://codesandbox.io/s/animation-highlight-j6d4f2?file=/src...

核心代码

animationNormal: {
    bar: [
      {
        loop: true,
        startTime: 100,
        oneByOne: 100,
        timeSlices: [
          {
            delay: 1000,
            effects: {
              channel: {
                fillOpacity: { to: 0.5 }
              },
              easing: "linear"
            },
            duration: 500
          },
          {
            effects: {
              channel: {
                fillOpacity: { to: 1 }
              },
              easing: "linear"
            },
            duration: 500
          }
        ]
      }
    ]
  }

柱图氛围动画

示例地址: https://www.visactor.io/vchart/guide/tutorial_docs/extend/cus...

核心代码

animationNormal: {
    bar: {
      loop: 100,
      duration: 1500,
      easing: 'quadIn',
      custom: VRender.StreamLight,
      customParameters: {
        attribute: {
          fillColor: '#bcdeff',
          opacity: 0.3,
          blur: 20,
          shadowColor: '#bcdeff',
          width: 160
        }
      }
    }
}

图表功能的可拓展性

除了提供多种预定义的图表类型,如柱状图、折线图、饼图等,我们还支持用户根据自己的数据特点和展示需求,创建和自定义各种类型的图表。使用VGranmar图形语法,你可以完成数据到图形的自定义映射,画布的自定义布局,动画效果和流程的自定义编排以及交互功能的自定义配置。

比如在大屏新增的排行榜组件并非VChart既有图表类型,而是通过图形语法VGrammar完全自定义实现。

实现揭秘

自定义映射

首先,要区分构成排行榜需要的图元类型,它们分别是矩形、标题、标签、装饰点。其次,需要确定图元的属性与数据的对应关系。

以如下数据为例:

const data = [
  { category: '吉林', value: 50 },
  { category: '内蒙古', value: 40 },
  { category: '河北', value: 30 },
  { category: '湖南', value: 30 },
  { category: '江西', value: 24 },
]

图元及数据与数据的映射关系:

image.png

映射结果

在线示例: https://codesandbox.io/s/vgrammar-ranking-list-hk8d43?file=/s...

核心代码

marks: [
      // 矩形
      {
        type: 'rect',
        from: { data: chartSpec.data[0].id },
        dependency: ['viewBox', 'xScale', 'yScale'],
        encode: {
          update: (datum, element, params) => {
            return {
              x: params.xScale.scale(dataMin), // 根据xScale做数据映射计算
              y: params.yScale.scale(datum['category']), // 根据yScale做数据映射计算
              width: params.xScale.scale(datum['value']), // 根据xScale做数据映射计算
              height: barWidth,
              // ...省略其他属性
            }
          },
        },
      },
      // 装饰点
      {
        type: 'symbol', 
        from: { data: chartSpec.data[0].id },
        dependency: ['viewBox', 'yScale', 'xScale'],
        encode: {
          update: (datum, element, params) => {
            return {
              x: params.xScale.scale(replaceNilDatum(datum, 'value', dataMin)),
              y: params.yScale.scale(datum['category']),
              // ...省略其他属性
            }
          },
        },
      },
      // 标题
      {
        type: 'text',
        from: { data: chartSpec.data[0].id },
        dependency: ['viewBox', 'yScale', 'xScale'],
        encode: {
          update: (datum, element, params) => {
            return {
              text: leftTextFormatMethod(datum['category']),
              x: params.xScale.scale(dataMin),
              y: params.yScale.scale(datum['category']),
              // ...省略其他属性
            }
          },
        },
      },
      // 标签
      {
        type: 'text',
        from: { data: chartSpec.data[0].id },
        dependency: ['viewBox', 'yScale'],
        encode: {
          update: (datum, element, params) => {
            return {
              text: rightTextFormatMethod(datum['value']),
              x: params.viewBox.x2,
              y: params.yScale.scale(datum['category']),
              // ...省略其他属性
            }
          },
        },
      },
    ],
自定义动画

排行榜组件的动画分为:入场动画、数据更新动画和退场动画。

入场动画时,所有元素的y属性从画布外,变为正常状态。

数据更新时,矩形图元的width属性从0变为正常状态。

退场动画时,所有元素的y属性从正常状态变为画布外。

在线示例: https://codesandbox.io/s/vgrammar-ranking-list-hk8d43?file=/s...

核心代码

// 以矩形图元为例

// 入场动画
enter: [
    {
      delay: 0,
      duration: durationTime,
      channel: {
        dy: {
          from: (datum, element, params) => {
            return params.viewBox.y2
          },
          to: 0,
        },
      },
    },
  ],
  
  // 更新动画
  enter: [
    {
      delay: 0,
      duration: durationTime,
      channel: {
        width: {
          from: 0,
          to: (datum, element, params) => {
            return params.xScale.scale(datum['value'])
          }
        }
      }
  ],
  
  // 退场动画
  exit: [
    {
      delay: 0,
      duration: durationTime,
      channel: {
        dy: {
          from: 0,
          to: (datum, element, params) => {
            return params.viewBox.y2
          },
        },
      },
    },
  ],
自定义交互

在VChart中,每个内置图表都有对应的图元点击事件用于数据联动。为了对齐这个能力,自定义图表排行榜也需要增加图元点击事件。依赖于vgrammr的事件注册能力,只需要在实例上调用addEventListener即可实现。

在这里插入图片描述

在线示例: https://codesandbox.io/s/vgrammar-ranking-list-hk8d43?file=/s...

核心代码

this.chartInstance.addEventListener('pointerdown', this.clickEventHandler)

结语

本文以 DataWind 数据大屏产品为例,详细介绍了实现一个优秀的数据大屏在技术和设计上要考虑的内容和实现方法,希望对您有所启发。

联系 与体验 方式

火山引擎DataWind: https://www.volcengine.com/product/datawind

VisActor 项目官网:https://www.visactor.io/

微信公众号:

今夜无月,期待你点亮星空,感谢Star:

githubhttps://github.com/VisActor/VChart

更多参考:

  1. 探索 VChart 图表库:简单、易用、强大、炫酷的可视化利器
  2. VTable——不只是高性能的多维数据分析表格,开源,免费,百万数据秒级渲染
  3. GPT遇到可视化——一句话生成图表、图片和视频
  4. 魔力之帧(上):前端图表库动画实现原理 - 掘金
  5. 火山引擎DataWind产品可视化能力揭秘
  6. VisActor——面向叙事的智能可视化解决方案
  7. 基于 VTable 的多维数据展示的原理与实践 - 掘金
本文参与了SegmentFault 思否写作挑战赛活动,欢迎正在阅读的你也加入。

玄魂
911 声望215 粉丝

开源可视化解决方案 VisActor 布道师