基于 HTML5 Canvas 的 3D 渲染引擎构建生产管控系统
前言
大家好,老郑我又回来了。这一期为大家带来一个非常好玩的 demo,我们制作一套自己的 3d 管道控制系统,运用了( )ht 的 graph3dview 组件通过对 webgl 底层技术的封装,与 ht 其他组件一样,基于 ht 统一的 datamodel 数据模型来驱动图形显示。
效果图
此为 2d 主界面:
此为 3d 界面的部分分段演示:
由于 gif 上传有大小限制,所以请大家务必去网页感受和体验,双击进口阀开始。 ( 戳我进入!)
代码实现
主要教大家的是一种流程动画的制作方式,我用到包括动画在内的多种方法,下面我听我慢慢道来。由于是 3d 界面,关于创建 3d 渲染引擎组件,可视化呈现数据模型的三维环境场景我之前有讲过,就是 datamodel 和 graph3dview。(后面用简写的dm,gv代替)
我们先对整体界面的基础进行一下设置:
// 禁止拖动 gv.setmovablefunc(function() { return false }) // 设置眼睛 gv.seteye([-922, 1745, 4659]) // 设置中心点 gv.setcenter([98, 621, -318])
然后,把需要加动画的阀都获取到,我们按照步骤依次来,以免落下关键步:
var a = dm.getdatabytag('进口电动球阀') var b = dm.getdatabytag('旁通阀') var c = dm.getdatabytag('出口电动球阀') ... ...
可以开始我们的动画设计了!我用的是 flyto() 的方法,事实证明这种效果真的很不错。我们要事先准备好所有的动画组并把它们串联在一起,我设计的开始演示是通过双击进口阀来控制。比如第一步,应该打开将进口球阀由远程控制转为就地控制。所以,我们要让镜头从这里开始:
gv.mi(function (e) { if (e.kind === 'doubleclickdata') { if (e.data.gettag() === '进口电动球阀') { gv.flyto(n, { animation : true, direction : [-200, 0, 0], distance : 100 }) anim1() } } })
注意 mi 是增加交互事件监听器,addinteractorlistener 的缩写。
// 示例: gv.mi(function (event) { // event 格式: { kind: 'clickdata', // 事件类型 data: data, // 事件相关的数据元素 part: "part", // 事件的区域, icon、label 等 event: e // html 原生事件 } })
这里面 n 就是第一步的那个按钮,再介绍一下这个方法的相关参数:
flyto : 相机看向具体的节点或者节点列表,参数 (target, options),其中
target : 一个节点 node 或者节点列表(array)或者空(场景内的所有节点)
options : 可选属性,格式为对象({}),属性包括有:
animation : 默认 false,是否启用动画,可以设置为 true 或者 false 或者 animation 动画对象
center : 默认 undefined,新的场景 center 点,形如 [0,0,0](空的话,target 为一个则看向 node 中心,target 为列表则看向根据节点列表计算出来的中心)
direction : 默认 undefined,眼睛处于目标的方向(相对目标,受到目标自身旋转影响),例如 [0,1,5] 在目标正面的斜向上
worlddirection : 默认 undefined,眼睛处于目标的方向(相对场景,不受目标旋转影响),例如 [0,1,5] 在目标所在位置的斜向上
distance : 默认undefined(未定义的话则使用下面的 ratio 模式计算距离),浮点类型,表示眼睛跟中心的固定距离
ratio : 默认 0.8,浮点类型,表示眼睛跟中心的距离动态计算(例如 0.8 表示眼睛在上述方向上动态计算距离以将目标包围盒的 8 个角全部适配到屏幕 80% 范围内)
注意,direction跟worlddirection如果都不配置,则使用之前相机的角度保持不变化。
后面全部用到动画,解释一下。在 ht 的数据模型驱动图形组件的设计架构下,动画可理解为将某些属性由起始值逐渐变到目标值的过程, ht 提供了 ht.default.startanim 的动画函数。它支持 frame-based 和 time-based 两种方式的动画,frame-based 方式是用户通过指定 frames 动画帧数,以及 interval 动画帧间隔参数控制动画效果。
我用的是 time-based 方式,该方式用户只需要指定 duration 的动画周期的毫秒数即可,ht 将在指定的时间周期内完成动画, 不同于 frame-based 方式有明确固定的帧数,即 action 函数被调用多少次,time-based 方式帧数或 action 函数被调用次数取决于系统环境, 一般来说系统配置更好的机器,更高效的浏览器则调用帧数越多,动画过程更平滑。由于 js 语言无法精确控制 interval 时间间隔, 采用 frame-based 不能精确控制动画时间周期,即使相同的 frames 和 interval 参数在不同的环境,可能会出现动画周期差异较大的问题, 因此 ht 默认采用 time-based 的方式,如果不设置 duration 和 frames 参数,则 duration 参数将被系统自动设置为 ht.default.animduration 值。action 函数就是实现动画过程中的属性变化(变化参数和进度)。
紧接着我们要开始执行第一个动画—— anim1() 了:
function anim1() { ht.default.startanim({ duration: 2000, action: function (v, t) { // 让旋钮旋转,改变其角度 r3 n.r3(n.r3()[0] - 0.02, n.r3()[1], n.r3()[2]) }, finishfunc: function () { // 动画结束后调用的函数 gv.flyto(a, { animation : true, direction : [-2000, 1000, 2000], distance : 1000 }) anim2() } }) }
...
可以看到动画结束后我们再次用到 flyto() 向下一个步骤开关去拉近,然后再次执行它的动画,以此类推,关于一套清晰的操作流程的动画实现指日可待!
当所有步骤结束后我们应当将镜头拉回到最开始时的初始视角,所以我们要注意一点,在最开始的时候提前把位置复制一下:
var oeye = ht.default.clone(gv.geteye()) var ocenter = ht.default.clone(gv.getcenter())
这样,在最后一个 finishifunc 中我们还原位置:
gv.seteye(oeye) gv.setcenter(ocenter)
最后,一个简明的系统操作流程就做好了,想看不懂都难~
总结
ht for web 提供完整的基于 html5 图形界面组件库。您可以轻松构建现代化的,跨桌面和移动终端的企业应用,无需担忧跨平台兼容性,及触屏手势交互等棘手问题。也可用于快速创建和部署,高度可定制化,并具有强大交互功能的拓扑图形及表盘图表等应用。ht for web 非常适用于实时监控系统的界面呈现,广泛应用于电信网络拓扑和设备管理,以及电力、燃气等工业自动化 ( hmi / scada ) 领域。
下一篇: SqlServer 递归查询