HT图形组件设计之道(四)

2024-08-30 12:08
文章标签 设计 组件 图形 之道 ht

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

在《HT图形组件设计之道(二)》我们展示了HT在2D图形矢量的数据绑定功能,这种机制不仅可用于2D图形,HT的通用组件甚至3D引擎都具备这种数据绑定机制,此篇我们将构建一个3D飞机模型,展示如果将数据绑定机制运用于3D模型,同时会运用到HT的动画机制,以及OBJ 3D模型加载等技术细节,正巧赶上刚发布的iOS8我们终于能将基于HT for Web开发的HTML5 3D应用跑在iOS系统了。

Screen Shot 2014-10-08 at 7.45.25 PM

首选我们需要一个飞机模型,采用HT for Web构建3D模型可采用API组合各种基础模型的方式,但今天我们将采用读入OBJ的方式,毕竟网上已有很多不错的现成模型素材,搜查了一番后我在www.turbosquid.com选择了的这款免费的飞机模型,这个飞机模型是3dsmax格式,飞机模型是一体化的,由于我还需要控制机头的螺旋桨,因此我用3dsmax做了点改造,将螺旋桨分离了机身独立作为一个材质,同时导出成HT for Web可读取的OBJ格式,接下来就没美工设计师什么事了,剩下就全靠我们程序员自己的代码手艺活了。

Screen Shot 2014-10-08 at 8.26.18 PM

读取OBJ文件一般采用AJAX的方式远程加载,这对于喜欢纯前端的程序员来说很不爽,开发或演示个例子还得启服务,我喜欢本地文件打开就能跑不受跨域安全限制,因此我们需要将OBJ的文本信息放在在HTML或者JS代码中。解决这类问题有很多种方式,例如对于WebGL开发来说vertex shader和fragment shader代码同样面临这个问题,一种方式是写成一堆的string的array然后进行join的方式,另一种方式是增加<script id=”shader-vs” type=”x-shader/x-vertex”></script>和<script id=”shader-fs” type=”x-shader/x-fragment”></script>的自定义类似script块,然后读取相应DOM元素的textContent来获取文本内容。

但这两种方式都不适合OBJ内容,因为OBJ内容太长,采用数组方式对于成千上万行的OBJ文件行行加引号是不可思议的工作量(当然你可以再写个工具干这事),而采用<script>的方式会使得页面的HTML代码太长不易阅读编辑,我喜欢采用下面代码所示的这种方式,obj和mtl文件就像普通的js文件,可分离HTML页面代码,可给多个例子复用,且没有跨域安全问题,当然代码有点tricky,将function转换成字符串再截取中间文本内容:

复制代码
var flight_mtl = getRawText(function(){/*newmtl bodyNs 10.0000Ni 1.5000d 1.0000Tr 0.0000Tf 1.0000 1.0000 1.0000 illum 2Ka 0.3608 0.4353 0.2549Kd 0.3608 0.4353 0.2549Ks 0.0000 0.0000 0.0000Ke 0.0000 0.0000 0.0000...
*/});var flight_obj = getRawText(function(){/*v  -21.7990 -2.5094 -157.4279v  -34.5972 -20.3459 -42.9317v  -36.7638 -6.2029 -43.0833...
*/});            function getRawText(obj){var text = String(obj); return text.substring(14, text.length-3);
}
复制代码

以下为注册飞机模型的代码,通过代码的注解可知我们对飞机模型做了调整,通过r3: [0, -Math.PI/2, 0]我将整体飞机模型沿着y轴旋转了-Math.PI弧度使之朝向右边,通过s3:[0.1, 0.1, 0.1]将飞机模型缩小了10倍。

复制代码
ht.Default.loadObj(flight_obj, flight_mtl, {                    center: true,r3: [0, -Math.PI/2, 0], // make plane face rights3: [0.1, 0.1, 0.1], // make plane smallerfinishFunc: function(modelMap, array, rawS3){if(modelMap){                            modelMap.propeller.r3 = {func: function(data){return [data.a('angle'), 0, 0]; }                                };                             // make propeller a litter biggermodelMap.propeller.s3 = [1, 1.2, 1.2]; modelMap.propeller.color = 'yellow';// add a sphere model as an indicator light
            array.push({shape3d: ht.Default.createSmoothSphereModel(),t3: [-40, 10, 0],s3: [6, 6, 6],color: {func: function(data){return data.a('light') ? 'red': 'black';}}});ht.Default.setShape3dModel('plane', array);createPlane(rawS3);createFormPane();  } }
});
复制代码

Screen Shot 2014-10-08 at 9.46.53 PM

飞机的螺旋桨模型绑定了data.a(‘angle’)属性,原始螺旋桨模型有点小,通过modelMap.propeller.s3 = [1, 1.2, 1.2];在yz面做了1.2倍的放大,通过modelMap.propeller.color = ‘yellow’;将原始模型的颜色改成更显眼的黄色,当然你也可以通过修改mtl文件实现,甚至再将该属性绑定数据模型进行动态变化。

飞机尾部原始模型并没有指示灯,我们通过ht.Default.createSmoothSphereModel()用API创建了一个模型,与OBJ的模型进行了组合,指示灯的颜色通过return data.a(‘light’) ? ‘red’: ‘black’;的函数逻辑进行数据绑定,后续我们将在飞机运行过程动态变化data.a(‘light’)参数,实现飞机飞行过程指示灯的闪烁效果。

Screen Shot 2014-10-08 at 10.01.56 PM

飞行路线是通过ht.Polyline类型构建的,上图的几个黄色球是飞行路线Polyline对象的部分控制点,通过这几个控制点我们甚至可以在飞机飞行过程动态改变飞行路线。

复制代码
params = {delay: 1500,duration: 20000,easing: function(t){return (t *= 2) < 1 ? 0.5 * t * t : 0.5 * (1 - (--t) * (t - 2));                     },action: function(v, t){var point = getPoint(v),px = point.x,py = point.y,pz = point.z,tangent = getTangent(v),tx = tangent.x,ty = tangent.y,tz = tangent.z;plane.p3(px, py, pz);plane.lookAt([px + tx, py + ty, pz + tz], 'right');  var camera = formPane.v('Camera');if(camera === 'Look At'){g3d.setCenter(px, py, pz);}else if(camera === 'First Person'){                           g3d.setEye(px - tx * 400, py - ty * 400 + 30, pz - tz * 400);g3d.setCenter(px, py, pz);                           }plane.a('angle', v*Math.PI*120);                       if(this.duration * t % 1000 > 500){plane.a('light', false);}else{plane.a('light', true);}                       },finishFunc: function(){animation = ht.Default.startAnim(params);plane.a('light', false);}                 };                              animation = ht.Default.startAnim(params);
复制代码

以上为飞行动画的相关代码,ht.Default.startAnim可启动Frame-Based和Time-Based两种方式的动画,本例中我们需要动态改变飞行的周期,同时Frame-Based的方式会导致不同硬件设备总体运行周期差异太大,因此我们采用设置Duration的Time-Based的动画方式。

动画过程主要要改变飞机的位置,以及保持机头朝向切线方向,同时在Look At的模式下,我们不断让HT的Graph3dView的eye属性盯着飞机的位置,First Person模式下我们还需要改变Graph3dView的center属性。通过if(this.duration * t % 1000 > 500)的代码逻辑,实现了半秒钟改变一次light属性的闪烁效果。

为了达到更逼真的现实效果我们定义了Easing函数,采用了easeBoth这种起始结束较慢中间过程较快的动画函数,可参考《透过WebGL 3D看动画Easing函数本质》文章,从而实现飞机逐渐加速启动启动,慢慢减速着落的效果,螺旋桨的旋转角度也在动画过程中根据Easing相关参数值设置,因此螺旋桨的旋转速度也一致的放映了这种动画效果。

Screen Shot 2014-10-08 at 10.36.35 PM

该例子综合运用了HT for Web的多种技术功能,大家能体会到HT这种数据绑定机制灵活且强大的特点,通过数据绑定机制,我们可以动态修改从2D拓扑图、到通用组件渲染,甚至到3D引擎的数据模型,所有图形元素的颜色、大小和角度等参数皆可灵活控制,并且以最直观易用的方式供程序员二次开发与实际业务数据绑定关联。

最后上段该HTML5例子在iOS、Android和Mac等多平台下的运行视频和抓图,有兴趣的同学还可对该例子做更多有意思的改造扩展。http://v.youku.com/v_show/id_XNzk5MzI3MzMy.html

Screen Shot 2014-10-08 at 7.44.54 PM

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



http://www.chinasem.cn/article/1120777

相关文章

Vue中组件之间传值的六种方式(完整版)

《Vue中组件之间传值的六种方式(完整版)》组件是vue.js最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引用,针对不同的使用场景,如何选择行之有效的通信方式... 目录前言方法一、props/$emit1.父组件向子组件传值2.子组件向父组件传值(通过事件形式)方

Spring组件初始化扩展点BeanPostProcessor的作用详解

《Spring组件初始化扩展点BeanPostProcessor的作用详解》本文通过实战案例和常见应用场景详细介绍了BeanPostProcessor的使用,并强调了其在Spring扩展中的重要性,感... 目录一、概述二、BeanPostProcessor的作用三、核心方法解析1、postProcessB

kotlin中的行为组件及高级用法

《kotlin中的行为组件及高级用法》Jetpack中的四大行为组件:WorkManager、DataBinding、Coroutines和Lifecycle,分别解决了后台任务调度、数据驱动UI、异... 目录WorkManager工作原理最佳实践Data Binding工作原理进阶技巧Coroutine

Vue项目的甘特图组件之dhtmlx-gantt使用教程和实现效果展示(推荐)

《Vue项目的甘特图组件之dhtmlx-gantt使用教程和实现效果展示(推荐)》文章介绍了如何使用dhtmlx-gantt组件来实现公司的甘特图需求,并提供了一个简单的Vue组件示例,文章还分享了一... 目录一、首先 npm 安装插件二、创建一个vue组件三、业务页面内 引用自定义组件:四、dhtmlx

Vue ElementUI中Upload组件批量上传的实现代码

《VueElementUI中Upload组件批量上传的实现代码》ElementUI中Upload组件批量上传通过获取upload组件的DOM、文件、上传地址和数据,封装uploadFiles方法,使... ElementUI中Upload组件如何批量上传首先就是upload组件 <el-upl

Vue3中的动态组件详解

《Vue3中的动态组件详解》本文介绍了Vue3中的动态组件,通过`component:is=动态组件名或组件对象/component`来实现根据条件动态渲染不同的组件,此外,还提到了使用`markRa... 目录vue3动态组件动态组件的基本使用第一种写法第二种写法性能优化解决方法总结Vue3动态组件动态

四种Flutter子页面向父组件传递数据的方法介绍

《四种Flutter子页面向父组件传递数据的方法介绍》在Flutter中,如果父组件需要调用子组件的方法,可以通过常用的四种方式实现,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录方法 1:使用 GlobalKey 和 State 调用子组件方法方法 2:通过回调函数(Callb

Vue项目中Element UI组件未注册的问题原因及解决方法

《Vue项目中ElementUI组件未注册的问题原因及解决方法》在Vue项目中使用ElementUI组件库时,开发者可能会遇到一些常见问题,例如组件未正确注册导致的警告或错误,本文将详细探讨这些问题... 目录引言一、问题背景1.1 错误信息分析1.2 问题原因二、解决方法2.1 全局引入 Element

vue解决子组件样式覆盖问题scoped deep

《vue解决子组件样式覆盖问题scopeddeep》文章主要介绍了在Vue项目中处理全局样式和局部样式的方法,包括使用scoped属性和深度选择器(/deep/)来覆盖子组件的样式,作者建议所有组件... 目录前言scoped分析deep分析使用总结所有组件必须加scoped父组件覆盖子组件使用deep前言

Python中的可视化设计与UI界面实现

《Python中的可视化设计与UI界面实现》本文介绍了如何使用Python创建用户界面(UI),包括使用Tkinter、PyQt、Kivy等库进行基本窗口、动态图表和动画效果的实现,通过示例代码,展示... 目录从像素到界面:python带你玩转UI设计示例:使用Tkinter创建一个简单的窗口绘图魔法:用