前言
3D 场景中的面不只有水平面这一个,空间是由无数个面组成的,所以我们有可能会在任意一个面上放置物体,而空间中的面如何确定呢?我们知道,空间中的面可以由一个点和一条法线组成。这个 Demo 左侧为面板,从面板中拖动物体到右侧的 3D 场景中,当然,我鼠标拖动到的位置就是物体放置的点,但是这次我们的重点是如何在斜面上放置模型。
效果图
代码生成
创建场景
dm = new ht.DataModel();//数据模型(http://hightopo.com/guide/guide/core/datamodel/ht-datamodel-guide.html) g3d = new ht.graph3d.Graph3dView(dm);//3D 场景组件(http://hightopo.com/guide/guide/core/3d/ht-3d-guide.html) palette = new ht.widget.Palette();//面板组件(http://hightopo.com/guide/guide/plugin/palette/ht-palette-guide.html) splitView = new ht.widget.SplitView(palette, g3d, 'h', 0.2);//分割组件,第三个参数为分割的方式 h 为左右分,v 为上下分;第四个参数为分割比例,大于 1 的值为绝对宽度,小于 1 则为比例 splitView.addToDOM();//将分割组件添加进 body 体中
关于这些组件的定义可以到对应的链接里面查看,至于将分割组件添加进 body 体中的 addToDOM 函数有必要解释一下(我每次都提,这个真的很重要!)。
HT 的组件一般都会嵌入 BorderPane、SplitView 和 TabView 等容器中使用,而最外层的 HT 组件则需要用户手工将 getView() 返回的底层 div 元素添加到页面的 DOM 元素中,这里需要注意的是,当父容器大小变化时,如果父容器是 BorderPane 和 SplitView 等这些HT
预定义的容器组件,则 HT 的容器会自动递归调用孩子组件 invalidate 函数通知更新。但如果父容器是原生的 html 元素, 则 HT 组件无法获知需要更新,因此最外层的 HT 组件一般需要监听 window 的窗口大小变化事件,调用最外层组件 invalidate 函数进行更新。
为了最外层组件加载填充满窗口的方便性,HT 的所有组件都有 addToDOM 函数,其实现逻辑如下,其中 iv 是 invalidate 的简写:
addToDOM = function(){ var self = this, view = self.getView(),//获取组件的底层 div style = view.style; document.body.appendChild(view);//将组件底层div添加进body中 style.left = '0';//ht 默认将所有的组件的position都设置为absolute绝对定位 style.right = '0'; style.top = '0'; style.bottom = '0'; window.addEventListener('resize', function () { self.iv(); }, false);//窗口大小改变事件,调用刷新函数 }
大家可能注意到了,场景中我添加的斜面实际上就是一个 ht.Node 节点,作为与地平面的参照,在这样的对比下立体感会更强一点。下面是这个节点的定义:
node = new ht.Node(); node.s3(1000, 1, 1000);//设置节点的大小 node.r3(0, 0, Math.PI/4);//设置节点旋转 这个旋转的角度是有学问的,跟下面我们要设置的拖拽放置的位置有关系 node.s('3d.movable', false);//设置节点在3d上不可移动 因为这个节点只是一个参照物,建议是不允许移动 dm.add(node);//将节点添加进数据容器中
左侧内容构建
Palette 和 GraphView 类似,由 ht.DataModel 驱动,用 ht.Group 展示分组,ht.Node 展示按钮元素。我将加载 Palette 面板中的图元函数封装为 initPalette,定义如下:
function initPalette() {//加载palette面板组件中的图元 var arrNode = ['displayDevice', 'cabinetRelative', 'deskChair', 'temperature', 'indoors', 'monitor','others']; var nameArr = ['展示设施', '机柜相关', '桌椅储物', '温度控制', '室内', '视频监控', '其他'];//arrNode中的index与nameArr中的一一对应 for (var i = 0; i < arrNode.length; i++) { var name = nameArr[i]; var vName = arrNode[i]; arrNode[i] = new ht.Group();//palette面板是将图元都分在“组”里面,然后向“组”中添加图元即可 palette.dm().add(arrNode[i]);//向palette面板组件中添加group图元 arrNode[i].setExpanded(true);//设置分组为打开的状态 arrNode[i].setName(name);//设置组的名字 显示在分组上 var imageArr = []; switch(i){//根据不同的分组设置每个分组中不同的图元 case 0: imageArr = ['models/机房/展示设施/大屏.png']; break; case 1: imageArr = ['models/机房/机柜相关/配电箱.png', 'models/机房/机柜相关/室外天线.png', 'models/机房/机柜相关/机柜1.png', 'models/机房/机柜相关/机柜2.png', 'models/机房/机柜相关/机柜3.png', 'models/机房/机柜相关/机柜4.png', 'models/机房/机柜相关/电池柜.png']; break; case 2: imageArr = ['models/机房/桌椅储物/储物柜.png', 'models/机房/桌椅储物/桌子.png', 'models/机房/桌椅储物/椅子.png']; break; case 3: imageArr = ['models/机房/温度控制/空调精简.png', 'models/机房/消防设施/消防设备.png']; break; case 4: imageArr = ['models/室内/办公桌简易.png', 'models/室内/书.png', 'models/室内/办公桌镜像.png', 'models/室内/办公椅.png']; break; case 5: imageArr = ['models/机房/视频监控/摄像头方.png', 'models/机房/视频监控/对讲维护摄像头.png', 'models/机房/视频监控/微型摄像头.png']; break; default: imageArr = ['models/其他/信号塔.png']; break; } setPalNode(imageArr, arrNode[i]);//创建palette上节点及设置名称、显示图片、父子关系 } }
我在 setPalNode 函数中做了一些名称的设置,主要是想要根据上面 initPalette 函数中我传入的路径名称来设置模型的名称以及在不同文件在不同的文件夹下的路径:
function setPalNode(imageArr, arr) { for (var j = 0; j < imageArr.length; j++) { var imageName = imageArr[j]; var jsonUrl = imageName.slice(0, imageName.lastIndexOf('.')) + '.json';//shape3d中的 json 路径 var name = imageName.slice(imageName.lastIndexOf('/')+1, imageName.lastIndexOf('.')); //取最后一个/和.之间的字符串用来设置节点名称 var url = imageName.slice(imageName.indexOf('/')+1, imageName.lastIndexOf('.'));//取第一个/和最后一个.之间的字符串用来设置拖拽生成模型obj文件的路径 createNode(name, imageName, arr, url, jsonUrl);//创建节点,这个节点是显示在palette面板上 } }
createNode 创建节点的函数比较简单:
function createNode(name, image, parent, urlName, jsonUrl) {//创建palette面板组件上的节点 var node = new ht.Node(); palette.dm().add(node); node.setName(name);//设置节点名称 palette面板上显示的文字也是通过这个属性设置名称 node.setImage(image);//设置节点的图片 node.setParent(parent);//设置父亲节点 node.s({ 'draggable': true,//设置节点可拖拽 'image.stretch': 'centerUniform',//设置节点图片的绘制方式 'label': ''//设置节点的label为空,这样即使设置了name也不会显示在3d中的模型下方 }); node.a('urlName', urlName);//a设置用户自定义属性 node.a('jsonUrl', jsonUrl); return node; }
虽然简单,但是还是要提一下,draggable: true 为设置节点可拖拽,否则节点不可拖拽;还有 node.s 是 HT 默认封装好的样式设置方法,如果用户需要自己添加方法,则可通过 node.a 方法来添加,参数一为用户自定义名称,参数二为用户自定义值,不仅能传常量,也能传变量、对象,还能传函数!又是一个非常强大的功能。
拖拽功能
拖拽基本上就是响应 windows 自带的 dragover 以及 drop 事件,要在放开鼠标的时候创建模型,就要在事件触发时生成模型:
function dragAndDrop() {//拖拽功能 g3d.getView().addEventListener("dragover", function(e) {//拖拽事件 e.dataTransfer.dropEffect = "copy"; handleOver(e); }); g3d.getView().addEventListener("drop", function(e) {//放开鼠标事件 handleDrop(e); }); } function handleOver(e) { e.preventDefault();//取消事件的默认动作。 } function handleDrop(e) {//鼠标放开时 e.preventDefault();//取消事件的默认动作。 var paletteNode = palette.dm().sm().ld();//获取palette面板中最后选中的节点 if (paletteNode) { loadObjFunc('assets/objs/' + paletteNode.a('urlName') + '.obj', 'assets/objs/' + paletteNode.a('urlName') + '.mtl', paletteNode.a('jsonUrl'), g3d.getHitPosition(e, [0, 0, 0], [-1, 1, 0]));//加载obj模型 } }
这里完全有必要说明一下,这个 Demo 的重点来了! loadObjFunc 函数中的最后一个参数为生成模型的 position3d 坐标,g3d.getHitPosition 这个方法总共有三个参数,第一个参数为事件类型,第二和第三个参数如果不设置,则默认为水平面的中心点也就是 [0, 0, 0] 以及法线为 y 轴,也就是 [0, 1, 0],一条法线和一个点就可以确定一个面,所以我们通过这个方法来设置这个节点所要放置的平面是在哪一个面上,我前面将 node 节点设置为绕 z 轴旋转 45° 角,所以这边的法线也就要好好想想如何设置了,这是数学上的问题,要自己思考了。
加载模型
HT 通过 ht.Default.loadObj 函数来加载模型,但是前提是要有一个节点,然后再在这个节点上加载模型:
function loadObjFunc(objUrl, mtlUrl, jsonUrl, p3) {//加载obj模型 var node = new ht.Node(); var shape3d = jsonUrl.slice(jsonUrl.lastIndexOf('/')+1, jsonUrl.lastIndexOf('.')); ht.Default.loadObj(objUrl, mtlUrl, {//HT 通过 loadObj 函数来加载 obj 模型 cube: true,//是否将模型缩放到单位1的尺寸范围内,默认为false center: true,//模型是否居中,默认为false,设置为true则会移动模型位置使其内容居中 shape3d: shape3d,//如果指定了shape3d名称,则HT将自动将加载解析后的所有材质模型构建成数组的方式,以该名称进行注册 finishFunc: function(modelMap, array, rawS3) {//用于加载后的回调处理 if (modelMap) { node.s({//设置节点样式 'shape3d': jsonUrl,//jsonUrl 为 obj 模型的 json 文件路径 'label': ''//设置label为空,label的优先级高于name,所以即使设置了name,节点的下方也不会显示name名称 }); g3d.dm().add(node);//将节点添加进数据容器中 node.s3(rawS3);//设置节点大小 rawS3 模型的原始尺寸 node.p3(p3);//设置节点的三维坐标 node.setName(shape3d);//设置节点名称 node.setElevation(node.s3()[1]/2);//控制Node图元中心位置所在3D坐标系的y轴位置 g3d.sm().ss(node);//设置选中当前节点 g3d.setFocus(node);//将焦点设置在当前节点上 return node; } } }); }
代码结束!
总结
说实在的这个 Demo 真的是非常容易,难度可能在于空间思维能力了,先确认法线和点,然后根据法线和点找到那个面,这个面按照我的这种方式有个对照还比较能够理解,真幻想的话,可能容易串。这个 Demo 容易主要还是因为封装的 hitPosition 函数简单好用,这个真的是功不可没。
相关推荐
HTML5 WebGL 3D 仓储管理系统是一种利用现代网络技术实现的高效、直观的库存管理解决方案。这个系统通过在网页上构建三维模型,为用户提供了更真实、更直观的仓库环境展示,使得库存物品的管理变得更加可视化和易于...
这个"HTML5+WebGL实现可拖拽的3D透明杯子效果源码.zip"文件,显然包含了一个使用这两种技术制作的互动3D模型示例,特别是一个可以被用户拖动的透明3D杯子。 首先,我们需要了解HTML5中的拖放(Drag and Drop)API。...
【3D模型】WebGL人体3D动态模型是一种在网页端实现的三维人体动画技术,它基于WebGL(Web Graphics Library)这一强大的图形渲染API,允许用户在浏览器中无需插件即可体验高质量的3D图形展示。这个模型特别之处在于...
HTML5、CSS和WebGL是现代网页开发中的关键技术,它们共同为创建互动性强、视觉效果丰富的3D网页游戏提供了强大的支持。在这个经典教程中,我们将深入探讨这些技术如何结合,以构建引人入胜的网页游戏。 HTML5是超...
总的来说,"效果超棒的WebGL模型-人体头部模型"融合了WebGL的基础知识和高级技术,包括3D几何、着色器编程、纹理映射、光照处理以及交互设计等,展示了WebGL在网页3D展示方面的强大能力。通过深入理解这些知识点,...
Three.js包括了物体创建、渲染器设置、相机操作、光照处理、动画控制等多个功能模块,非常适合快速开发3D应用。 CSS在本项目中的作用主要在于布局和一些基本的2D效果。例如,CSS可以用于设置HTML元素的位置,调整...
在“webGL3D机房模型”这个项目中,开发人员利用WebGL的强大功能,创建了一个交互式的3D机房演示。这个Demo不仅展示了机房的三维视图,还融入了多项实用功能,如模拟烟雾效果、虚拟巡检、漏水检测以及机柜容量管理等...
在本示例中,"SuperMap-webgl3D vue示例demo"是一个基于SuperMap、WebGL3D技术以及Vue.js框架构建的三维...总的来说,"SuperMap-webgl3D vue示例demo"是一个宝贵的资源,有助于提升开发者在GIS和Web开发领域的技能。
"Threejs 开发指南:基于 WebGL 和 HTML5 在网页上渲染 3D 图形和动画" 本文将对 Threejs 开发指南进行详细的介绍,包括 Threejs 的重要性、与其他 3D 渲染技术的比较、为什么要使用 Threejs 等。 首先,Threejs ...
HTML5、CSS3和WebGL是现代网页开发的三大核心技术,它们共同为创建交互式、图形丰富的HTML5游戏提供了强大的支持。在这个压缩包中,很可能包含了一系列教程、代码示例和资源,帮助开发者掌握如何利用这些技术来构建...
基于HTML5 WebGL 3D樱花飘落动画 1.网页作品简介 :HTML期末大学生网页设计作业 A+水平 ,喜欢的可以下载,文章页支持手机PC响应式布局。 2.网页作品编辑:作品下载后可使用任意HTML编辑软件(如:DW、HBuilder、...
用 WebGL 渲染的 3D 机房现在也不是什么新鲜事儿了,这篇文章的主要目的是说明一下,3D 机房中的 eye 和 center 的问题,刚好在项目中用上了,好生思考了一番,最终觉得这个例子最符合我的要求,就拿来作为记录。...
综上所述,"HTML5 WebGL 摇杆+按钮"的主题涵盖了WebGL的基本使用、浏览器安全策略、前端交互设计、3D动画和性能优化等多个方面,是Web开发中的一个重要实践。通过学习和实践这个项目,开发者能够提升自己的3D Web...
HTML5和WebGL是现代网页开发中的核心技术,它们一起为创建动态、交互式的3D图形提供了强大的支持。在这个“HTML5+WebGL 3D雪花飘落动画特效”项目中,开发者利用这两种技术构建了一个逼真的3D下雪场景,为网页增添了...
【基于WebGL实现3D图片特效】是一种利用现代网页技术为用户提供交互式3D体验的方法。WebGL(Web Graphics Library)是JavaScript API的一种,它允许在浏览器中进行硬件加速的三维图形渲染,无需任何插件。这个特效的...
它涉及到3D图形渲染、着色器编程、纹理映射、光照模型以及动画原理等多个方面,对于学习WebGL和3D网页开发的初学者来说,这是一个极好的学习资源。通过分析和理解这个源码,开发者可以深入掌握WebGL的使用,提升自己...
- **模型导入与管理**:WebGL允许使用3D模型,这些模型可以通过多种途径获得,例如从Turbosquid或Blendswap等网站购买或下载。在使用之前,需要确保符合相应的许可协议。 - **3D场景构建**:构建3D场景,包括模型、...
通过以上知识点,我们可以理解如何使用HTML5 WebGL技术来开发电信3D机房漫游应用。这个源代码可能包含实现这些特性的JavaScript代码、3D模型文件、纹理图片以及其他辅助资源,如样式表和字体文件。开发者可以通过...
Create engaging 3D applications for the Web with HTML5 and the emerging web graphics standard, WebGL. With this book, you'll learn hands-on how to take your website's production value to a new level ...