前言
如今的制造行业, 基于数据进行生产策略制定与管理已经成为一种趋势, 特别是 工业 4.0 的浪潮下, 数据战略已经成为很多制造企业的优先战略, 而数据可视化以更直观的方式, 帮助指导决策, 成为数据分析传递信息的重要工具. 通过数据可视化系统助力实现数据驱动的工业世界, 为 工业 4.0 提供更加灵活, 敏捷, 高效, 个性化的数据支撑. 今天就给大家带来一个采用 Hightopo 的 HT for web 产品实现了一个水泥工厂可视化系统.
系统预览
本案例共有七个子系统:
数据概况 -- 展示全厂年月时间单位的各项数据概况
窑系统运行 -- 用窑工艺流程动画展示窑系统实时运行状态
系统运行情况 -- 用动画流程图展示整个系统运行情况
生料质量控制 -- 用图表和流程图展示各种生料的配比情况
熟料质量控制 -- 用动画流程图展示各种熟料的配比情况
煤粉质量控制 -- 用图表和流程图对煤粉质量进行监控
智能物流 -- 通过 3D 场景实时监控进出厂车辆, 和各项原料运输情
子系统页面切换
切换不同子系统时, 左侧菜单和顶部标题是不需要切换的, 所以我们把需要切换的内容部分别放在不同的 Block 中, Block 类型, 本身不绘制任何内容, 用于作为其它节点的父节点, 可以与子节点同步大小, 当它隐藏或显示时, 所有子节点都会跟着隐藏或显示. 所以当我们切换子系统时只需要控制对应的 Block 显示隐藏, 而不需要去加载切换多张图纸.
流向地图
在数据概况页面中, 流向地图展示年度水泥向各地区的销售情况, 这里我们用 Shape 类型绘制线段来连接源地和汇地, 用流动效果表示销售关系. 流动效果只需引入 HT 的 ht-flow.JS 插件, 即可通过简单的属性设置实现, 代码如下:
- // 获取线段的父节点
- this.flowParent = dm.getDataByTag('saleFlowParent');
- // 遍历得到所有线段
- this.flowParent.eachChild(child => {
- // 开启流动, 设置流动样式
- child.s({
- // 开启流动
- 'flow': true,
- // 设置流动组中最大元素的尺寸
- 'flow.element.max': 4,
- // 设置流动组中的元素的渐变阴影中心颜色
- 'flow.element.shadow.begincolor': '#49e5fe',
- // 设置流动组中的最大元素的渐变阴影尺寸
- 'flow.element.shadow.max': 16,
- // 设置流动组中的元素的渐变阴影边缘颜色
- 'flow.element.shadow.endcolor': 'rgba(73, 229, 254, 0)',
- });
- });
窑系统动画
在窑系统运行页面中, 窑工艺流程动画很直观的展示了窑系统实时运行状态. 画面中火焰, 水和熟料在传送带上运输的动画效果, 为了在性能较差的设备上也能流畅运行, 我通过切换不同矢量图形的方式实现. 这里用到了 HT 矢量中状态机制, 先绘制多个不同的矢量组件, 每个组件都可以定义状态来决定自己在哪个状态下显示, 只要通过 data.s('state') 修改节点状态就可以实现如下效果:
使用一个定时器, 不断地改变节点的状态值, 相关代码如下:
- this._stateTimer = setInterval(() => {
- stateNodes.forEach(node => {
- this.stateAnimation(node);
- });
- }, 180);
- // 切换状态
- stateAnimation(node) {
- let stateIndex = (node.a('stateIndex') || 0) % stateEnum.length,
- state = stateEnum[stateIndex].value;
- node.s('state', state);
- node.a('stateIndex', ++stateIndex);
- }
流程图动画
流程图中流动线同样是使用 ht-flow.JS 插件实现. 由于图纸上的线段比较多, 我把不同的线段分组放在不同的 Block 下, 遍历其子节点设置样式, 代码如下:
- // 设置流动属性
- setNodeFlow (data, value) {
- if (data instanceof ht.Block) {
- data.eachChild(child => {
- this.setNodeFlow(child, value);
- });
- }
- else if (data.getDisplayName() === 'line'){
- data.s({
- 'flow': value,
- 'flow.element.max': 4,
- 'flow.element.count': 1,
- 'flow.count': 5,
- 'flow.step': 10
- });
- }
- }
- // 设置虚线流动属性
- setNodeDashFlow(data, value) {
- if (data instanceof ht.Block) {
- data.eachChild(child => {
- this.setNodeDashFlow(child, value);
- });
- }
- else if (data.getDisplayName() === 'border'){
- if (value) {
- data.s({
- 'shape.dash.flow': true,
- 'shape.dash': true
- });
- }
- else {
- data.s({
- 'shape.dash.flow': false,
- 'shape.dash': false
- });
- }
- }
- }
为了使动画看起来更顺畅, 我给一些节点加上透明度动画, 设置节点透明度的代码如下:
- // 设置节点透明度
- setNodeOpacity (data, value = 0.5) {
- if (data instanceof ht.Block) {
- data.eachChild(child => {
- this.setNodeOpacity(child, value);
- });
- }
- else {
- data.s('opacity', value);
- }
- }
接下来只需要依次执行动画:
- // 开始流程图动画
- start() {
- let {eo, eoInput, eoLine1, eoKind, eoCalu} = this;
- // 工况输入透明度动画
- this.gv.enableFlow(30);
- this.setNodeOpacity(eo);
- this.setNodeFlow(eo, false);
- (new Promise((resolve, reject) => {
- this.animtion = startAnim({
- frames: 16,
- interval: 5,
- finishFunc: () => {resolve()},
- action: (v, t) => {
- this.setNodeOpacity(eoInput, 0.5 + 0.5 * v);
- }
- });
- })).then(() => {
- // 连线连线透明动画, 流动
- return new Promise((resolve, reject) => {
- this.animtion = startAnim({
- frames: 12,
- interval: 10,
- finishFunc: () => {
- this.setNodeFlow(eoLine1, true);
- this.timer = setTimeout(() => {resolve()}, 1500);
- },
- action: (v, t) => {
- this.setNodeOpacity(eoLine1, 0.5 + 0.5 * v);
- }
- });
- })
- }).then(() => {
- // 软计算透明动画
- return new Promise(resolve => {
- this.animtion = startAnim({
- frames: 16,
- interval: 5,
- finishFunc: () => {resolve()},
- action: (v, t) => {
- this.setNodeOpacity(eoKind, 0.5 + 0.5 * v);
- this.setNodeOpacity(eoCalu, 0.5 + 0.5 * v);
- }
- });
- });
- }).then(() => {
- // 软计算透明虚线流动
- return new Promise(resolve => {
- this.setNodeDashFlow(eoKind, true);
- this.setNodeDashFlow(eoCalu, true);
- this.timer = setTimeout(() => {
- this.setNodeDashFlow(eoKind, false);
- this.setNodeDashFlow(eoCalu, false);
- resolve();
- }, 3000);
- });
- }).then(() => {
- ......
- })
- }
智能物流
前面六个子系统均为 2D 界面, 而智能物流页面则是嵌入了一个 3D 场景. 实现方式是通过定义 HT 矢量 JSON 的 函数属性, 可实现在 GraphView 拓扑图上, 嵌入任意第三方 html DOM 元素. 不过这里也要注意一点, HT 的图纸是 Canvas 实现的, renderHTML 的 DOM 元素一定在 Canvas 之上, 使用 renderHTML 的 DOM 与常规 Canvas 上绘制的图元不可能有层级控制可能性. 下面展示一下 renderHTML 函数属性里的代码:
- renderHTML : function (data, gv, cache) {
- // 避免重复创建 g3d
- if (!cache.g3d) {
- // 创建 3D 视图组件
- var g3d = cache.g3d = new ht.graph3d.Graph3dView();
- // 布局函数, 根据图元的位置信息摆放 HTML 元素
- g3d.layoutHTML = function () {
- gv.layoutHTML(data, g3d, true);
- };
- // 阻止事件冒泡
- g3d.getView().addEventListener('mousedown', function (event) {
- event.stopPropagation();
- });
- g3d.getView().addEventListener('touchstart', function (event) {
- event.stopPropagation();
- });
- }
- // 获取图元自定义属性 sceneURL 的值
- var sceneURL = data.a('sceneURL');
- // 获取图元自定义属性 onPostDeserialize 的值
- var onPostDeserialize = data.a('onPostDeserialize');
- // 当图元自定义属性 sceneURL 改变时, 清除旧 dataModel, 反序列化新的 sceneURL
- if (cache.g3d.sceneURL !== sceneURL) {
- cache.g3d.dm().clear();
- cache.g3d.sceneURL = sceneURL;
- if (sceneURL) {
- cache.g3d.deserialize(sceneURL, function (JSON, dm, g3d, datas) {
- // 在反序列化后的回调函数中, 执行 onPostDeserialize 函数
- onPostDeserialize && onPostDeserialize(JSON, dm, g3d, datas);
- });
- }
- }
- return cache.g3d;
- }
3D 场景嵌入后, 接下来实现水泥厂内的车辆动画. 根据后台传来车辆进入工厂的数据, 我们创建运载不同原料的车辆模型, 让它们沿着不同的路径抵达对应的厂房. 同样是用 Shape 类型事先绘制好路径, 根据 Shape 的 Points 和 Segments 信息, 实现车辆沿着路径行驶动画. 相关代码如下:
- carAnimation(car, path, duration) {
- // 车辆行驶动画
- ht.Default.startAnim({
- duration: duration,
- easing: Easing.easeNone,
- action: function (v, t) {
- // 设置偏移量
- let offset = Math.floor(v * 100);
- // 根据偏移量得到在路径上的点坐标
- let position = ht.Default.getPercentPositionOnPoints(path.getPoints(), path.getSegments(), offset);
- // 根据偏移量得到在路径上的点于路径切线角度
- let angle = ht.Default.getPercentAngle(path.getPoints(), path.getSegments(), offset);
- // 设置车辆位置坐标及旋转角度
- car.setX(position.x);
- car.setY(position.y);
- car.setRotationY(Math.PI / 2 - angle);
- },
- });
- }
总结
工业互联网是工业发展的必经之路, 我们国家是一个工业大国, 正处在工业转型升级的关键时刻, 面临着人工成本上升, 原材料价格波动, 贸易竞争日益加剧等问题, 迫切需要提高效率, 降低生产成本. 只有坚定不移地推动工业互联网落地, 加快更多企业的数字化转型和智能化改造, 才有能让在全球化竞争中立于不败之地. 可视化作为智能化数字化的最后一环, 让复杂抽象的数据变得真正可知可感, 帮助决策者发现规律, 洞悉未来, 为企业提速增效.
还有更多的可视化案例可以参考: https://www.hightopo.com/demos/index.html
来源: https://www.cnblogs.com/xhload3d/p/12688752.html