HTML5拓扑图形组件设计之道(一)

2024-08-30 11:48

本文主要是介绍HTML5拓扑图形组件设计之道(一),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

HT for Web(http://www.hightopo.com/guide/readme.html)提供了涵盖通用组件、2D拓扑图形组件以及3D引擎的一站式解决方案,正如Hightopo官网所表达的我们希望提供:Everything you need to create cutting-edge 2D and 3D visualization. 这个愿景从功能上是个相当长的战线,从设计架构上也是极具挑战性的,其实HT团队是非常保守的,我们从不贪多图大,只做我们感觉自己能得更好,能给用户综合体验更佳的功能,在这样理念驱动下我们慢慢形成了这样的愿景,慢慢实现了几个有意义的里程碑,慢慢积累下了不少图形组件设计上的创新和经验,我不知道这个系列会写多少篇,也许永远也不会结束,也没有系统的提纲规划,想到什么就写什么,只希望文章能启发有兴趣的同学对图形组件设计更深的思考就足够了。

 

讨论前先设定话题的边界,HT是基于HTML5的图形组件库,因此文章的案例更多会涉及HTML和JavaScript语言,但并不局限于Web前端,设计思想上同样适用于任何GUI语言平台。完整的前端设计是需要考虑到后台加载并发等因素,但本系列更侧重于纯客户端图形组件,不涉及网络通讯部分的思考,例如最近阿里无线前端招聘让谈谈:讲讲输入完网址按下回车,到看到网页这个过程中发生了什么。这是个能讨论出很多方方面面,让你了解面试者的好话题,但这里讨论的话题会与以下关键字更为相关:企业应用、Single Page Application、重客户端交互、监控、MV*等。

如Linus大神所言:Talk is cheap, show me the code.  因此我选择在话题展开之前,先用HT来扩展定制几个应用案例,以便大家了解HT组件及其扩展设计思路。

http://www.hightopo.com/guide/guide/core/propertyview/examples/example_custom.html

 

 

 

1282039088303

熟悉Flex的程序员应该都了解Tour de Flex这个包罗万象的大杂烩,其中的网络监控拓扑Network Monitor特别其动画切换效果一直给我很深印象,这里不可能有篇幅实现完整例子,我们仅尝试实现其展示CPU和MEM的界面部分。

IMG_3641

实现的最终效果如上图所示,模型数据就两个数值,一个代表CPU占用率,一个代表内存占用率,左侧通过HT的图形组件GraphView自定义了矢量图形展示,右上角自定义了属性页PropertyView的两单元格的Renderer,右下角两个Slider可拖动改变CPU和MEN值。

此例子麻雀虽小五脏俱全,三个部分分别采用三种方式实现了自定义组件,同时不同组件共享同一数据源,在呈现的基础上还支持桌面和移动端的Mouse和Touch的交互,还有不同终端屏幕的组件布局功能。

业务上需要在占用率小于40%时呈现律师,40%-70%时显示黄色,超过70%时呈现红色,因此定义了如下getColor的工具函数:

1
2
3
4
5
6
7
getColor = function (value) {
     if  (value < 40)
         return  '#00A406' ;
     if  (value < 70)
         return  '#FFCC00' ;
     return  '#A60000' ;
};

PropertyView上采用的最基础和原始的方式,通过Canvas画笔进行单元格的自定义绘制,在注册PropertyView时重载drawPropertyValue函数即可实现单元格自定义Renderer的绘制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
drawFunc = function (g, value, x, y, w, h){
     g.fillStyle = '#A1A1A3' ;
     g.beginPath();
     g.rect(x, y, w, h);
     g.fill();                   
     g.fillStyle = getColor(value);
     g.beginPath();
     g.rect(x, y, w * value / 100, h);
     g.fill();
     ht.Default.drawText(g, value + '%' , '12px Arial' , 'white' , x, y, w, h, 'center' );
};
propertyView.addProperties([
     {
         displayName: 'CPU' ,
         drawPropertyValue: function (g, property, value, rowIndex, x, y, w, h, data, view)   {
             drawFunc(g, data.a( 'cpu' ), x, y, w, h);
         }
     },
     {
         displayName: 'MEM' ,
         drawPropertyValue: function (g, property, value, rowIndex, x, y, w, h, data, view) {
             drawFunc(g, data.a( 'mem' ), x, y, w, h);
         }       
     }
]);

Slider拉条部分直接在HT封装的组件之上应用,因而无需接触到最底层的Canvas画笔绘制,仅需要在onValueChanged时更新leftBackgroud拉条左侧颜色即可,其实也可以通过重载Slider的getLeftBackground函数实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
formPane.addRow([ 'CPU' , {
     slider: {
         step: 1,
         onValueChanged: function (){
             var  value = this .getValue();
             node.a( 'cpu' , value);
             this .setLeftBackground(getColor(value));
         },
         value: node.a( 'cpu' )       
     }
}], [50, 0.1]);
formPane.addRow([ 'MEM' , {
     slider: {
         step: 1,                       
         onValueChanged: function (){
             var  value = this .getValue();
             node.a( 'mem' , value);
             this .setLeftBackground(getColor(value));
         },
         value: node.a( 'mem' )       
     }
}], [50, 0.1]);

GraphView部分采用了《HT全矢量化的图形组件设计》文章介绍的HT自定义的矢量方式来实现图形效果,这种方式介于以上两种扩展方式之间,需要自定义绘制效果,但通过HT提供的矢量格式,用户可采用较为直观易读的JSON格式来描述图形,并通过数据绑定的方式实现模型数据与界面呈现的关联,避免如第一种自定义renderer的方式,即需要接触到底层绘制函数,同时业务逻辑代码与绘制代码混杂一起不易维护的问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
ht.Default.setImage( 'server_image' , {
     width: 300,
     height: 200,
     comps: [
         {
             type: "roundRect" ,
             rect: [3, 5, 291, 189],
             background: "#E3E3E3" ,
             gradient: "linear.northeast" ,
             shadow: true
         },
         {
             type: "text" ,
             text: "CPU" ,
             font: "16px Arial" ,
             rect: [20, 45, 59, 41]
         },
         {
             type: "text" ,
             text: "MEM" ,
             font: "16px Arial" ,
             rect: [20, 108, 59, 41]
         },
         {
             type: "rect" ,
             rect: [82, 55, 145, 22],
             background: "#A1A1A3"
         },
         {
             type: "rect" ,
             rect: {
                 func: function (data) {
                     return  [82, 55, 145 * data.a( 'cpu' ) / 100, 22];
                 }
             },
             background: {
                 func: function (data) {
                     return  getColor(data.a( 'cpu' ));
                 }
             }
         },
         {
             type: "rect" ,
             rect: [82, 117, 145, 22],
             background: "#A1A1A3"
         },
         {
             type: "rect" ,
             rect: {
                 func: function (data) {
                     return  [82, 117, 145 * data.a( 'mem' ) / 100, 22];
                 }
             },
             background: {
                 func: function (data) {
                     return  getColor(data.a( 'mem' ));
                 }
             }
         },
         {
             type: "text" ,
             font: "16px Arial" ,
             rect: [240, 49, 53, 31],
             text: {
                 func: function (data) {
                     return  data.a( 'cpu' ) + '%' ;
                 }
             },
             color: {
                 func: function (data) {
                     return  getColor(data.a( 'cpu' ));
                 }
             }
         },
         {
             type: "text" ,
             font: "16px Arial" ,
             rect: [240, 108, 47, 39],
             text: {
                 func: function (data) {
                     return  data.a( 'mem' ) + '%' ;
                 }
             },
             color: {
                 func: function (data) {
                     return  getColor(data.a( 'mem' ));
                 }
             }
         }
     ]
});

以上代码注册了名为server-image的图片,绑定了attr上的mem和cpu的两个属性,因此做完这些手脚架的基础工作后,应用人员只需要构建ht.Node对象,通过node.setImage('server-image')即可实现该图元在GraphView上呈现'server-image'描述的矢量效果,并且PropertyView、Slider和GraphView三个组件都通过node的attr上的cpu和mem来显示界面,这样当后台获取到采集的实时数据后,只需要更新到node的attr上的cpu和mem属性,则界面上的所有组件就会自定更新显示:

1
2
3
4
5
6
7
8
node = new  ht.Node();
node.setName( 'SERVER' );
node.setImage( 'server_image' );
node.a({
     cpu: 30,
     mem: 70
});
dataModel.add(node);

当然实际应用中并不需要拉条改变CPU和MEN值,这些值一般通过后台采集实时自动更新仅作为呈现,但有了前端这些组件的一站式支持,我们不需要连接后台也可以很方便在客户端进行模拟测试,有了这样的机制我们就可以分离模块一步步测试,例如我们现在不需要连接服务器也可以测试矢量描述定义的是否正确,数值改变后绿黄红的业务颜色更新是否正确,各个组件的数据同步是否正常,Mouse和Touch交互是否能正常操作,界面在不同设备屏幕上显示是否正常等等,这些纯客户端组件的封装工作都做到位后,你就可以安心连接后台数据进行测试了。

见过太多客户出问题时只会说:界面显示不对。这样的问题描述完全无法定位根源,到底时后台数据库问题,网络通讯问题,解析数据问题,设置模型问题还是组件封装问题?这也是MVC/MVP/MVVM存在的另外一个层面的意义,MV*除了事件派发数据绑定外,能更好的进行呈现、模型和业务逻辑的分工切割进行独立测试的重要意义,就行TCP/IP七层协议的分类,每个协议层都应该确保正确实现自己的协定约定,并且每一层可进行独立的测试,这才是可维护可扩展的系统,因此对于HT客户遇到问题时,我们一般也是一层层的帮忙梳理找根源,如果矢量描述没问题呈现出错,那是HT组件库的问题,如果模拟到Node上的attr数据显示正确,那就去找找实际运行后台通信解析后的数据是否正确的设置到模型上,这样一步步的分析很容易就能定位到问题的根源。

以上三种扩展方式各有利弊,我将在下篇中继续展开分析,本篇结尾上一段该例子在移动终端的运行操作视频

这篇关于HTML5拓扑图形组件设计之道(一)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/1120739

相关文章

Spring组件实例化扩展点之InstantiationAwareBeanPostProcessor使用场景解析

《Spring组件实例化扩展点之InstantiationAwareBeanPostProcessor使用场景解析》InstantiationAwareBeanPostProcessor是Spring... 目录一、什么是InstantiationAwareBeanPostProcessor?二、核心方法解

使用vscode搭建pywebview集成vue项目实践

《使用vscode搭建pywebview集成vue项目实践》:本文主要介绍使用vscode搭建pywebview集成vue项目实践,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录环境准备项目源码下载项目说明调试与生成可执行文件核心代码说明总结本节我们使用pythonpywebv

使用Python和Matplotlib实现可视化字体轮廓(从路径数据到矢量图形)

《使用Python和Matplotlib实现可视化字体轮廓(从路径数据到矢量图形)》字体设计和矢量图形处理是编程中一个有趣且实用的领域,通过Python的matplotlib库,我们可以轻松将字体轮廓... 目录背景知识字体轮廓的表示实现步骤1. 安装依赖库2. 准备数据3. 解析路径指令4. 绘制图形关键

C++ RabbitMq消息队列组件详解

《C++RabbitMq消息队列组件详解》:本文主要介绍C++RabbitMq消息队列组件的相关知识,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录1. RabbitMq介绍2. 安装RabbitMQ3. 安装 RabbitMQ 的 C++客户端库4. A

使用Python和Tkinter实现html标签去除工具

《使用Python和Tkinter实现html标签去除工具》本文介绍用Python和Tkinter开发的HTML标签去除工具,支持去除HTML标签、转义实体并输出纯文本,提供图形界面操作及复制功能,需... 目录html 标签去除工具功能介绍创作过程1. 技术选型2. 核心实现逻辑3. 用户体验增强如何运行

CSS 样式表的四种应用方式及css注释的应用小结

《CSS样式表的四种应用方式及css注释的应用小结》:本文主要介绍了CSS样式表的四种应用方式及css注释的应用小结,本文通过实例代码给大家介绍的非常详细,详细内容请阅读本文,希望能对你有所帮助... 一、外部 css(推荐方式)定义:将 CSS 代码保存为独立的 .css 文件,通过 <link> 标签

使用Vue-ECharts实现数据可视化图表功能

《使用Vue-ECharts实现数据可视化图表功能》在前端开发中,经常会遇到需要展示数据可视化的需求,比如柱状图、折线图、饼图等,这类需求不仅要求我们准确地将数据呈现出来,还需要兼顾美观与交互体验,所... 目录前言为什么选择 vue-ECharts?1. 基于 ECharts,功能强大2. 更符合 Vue

Vue中插槽slot的使用示例详解

《Vue中插槽slot的使用示例详解》:本文主要介绍Vue中插槽slot的使用示例详解,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录一、插槽是什么二、插槽分类2.1 匿名插槽2.2 具名插槽2.3 作用域插槽三、插槽的基本使用3.1 匿名插槽

springboot+vue项目怎么解决跨域问题详解

《springboot+vue项目怎么解决跨域问题详解》:本文主要介绍springboot+vue项目怎么解决跨域问题的相关资料,包括前端代理、后端全局配置CORS、注解配置和Nginx反向代理,... 目录1. 前端代理(开发环境推荐)2. 后端全局配置 CORS(生产环境推荐)3. 后端注解配置(按接口

Vue 2 项目中配置 Tailwind CSS 和 Font Awesome 的最佳实践举例

《Vue2项目中配置TailwindCSS和FontAwesome的最佳实践举例》:本文主要介绍Vue2项目中配置TailwindCSS和FontAwesome的最... 目录vue 2 项目中配置 Tailwind css 和 Font Awesome 的最佳实践一、Tailwind CSS 配置1. 安