石鱼 發表於 2019-9-23 08:31:00

基于 HTML5 WebGL 的医疗物流系统

<h1>前言</h1>
<p>物联网( IoT ),简单的理解就是物体之间通过互联网进行链接。世界上的万事万物,都可以通过数据的改变进行智能化管理。IoT 的兴起在医疗行业中具有拯救生命的潜在作用。<br>不断的收集用户信息并且实时的进行诊断,所以未来&nbsp;&nbsp;IoT 肯定在医疗行业的应用会呈覆盖性。下面是我最近做的一个医疗物流系统,用来观察医疗物流过程。</p>
<p><img src="https://img2018.cnblogs.com/blog/1496396/201909/1496396-20190920135025781-2114795492.gif"></p>
<p>ht官网链接:http://www.hightopo.com/cn-index.html</p>
<p>demo链接:&nbsp;https://www.hightopo.com/demo/pivas/<br></p>
<h1>&nbsp;</h1>
<h1 id="云中穿行效果"><strong>实现过程</strong></h1>
<h2><strong>增加光源</strong></h2>
<p>&nbsp;整个原场景其实是非常暗的,所以需要使用灯光的效果照亮整个场景,使其接近真实世界的场景。</p>
<p>&nbsp;我们看下对比。</p>
<p><img src="https://img2018.cnblogs.com/blog/1496396/201909/1496396-20190920154613834-1841508251.png"></p>
<p><strong><span style="color: rgba(255, 102, 0, 1)">light&nbsp;</span></strong>的一些属性: </p>
<p><strong><span style="color: rgba(255, 102, 0, 1)">type&nbsp;</span></strong>代表灯光的类型</p>
<p><strong><span style="color: rgba(255, 102, 0, 1)">color&nbsp;</span></strong>代表灯光的颜色</p>
<p><strong><span style="color: rgba(255, 102, 0, 1)">intensity&nbsp;</span></strong>代表灯光的强度(1是最大值)</p>
<p><strong><span style="color: rgba(255, 102, 0, 1)">range&nbsp;</span></strong>代表范围</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">addLight() {
      const skyBox </span>= <span style="color: rgba(0, 0, 255, 1)">this</span>.dm.getDataByTag('skyBox'<span style="color: rgba(0, 0, 0, 1)">)

      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 限制视野在天空球之内</span>
      <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.gv.setSkyBox(skyBox)
      const light </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ht.Light()</span><span style="color: rgba(0, 0, 0, 1)">
      const lightSource </span>= <span style="color: rgba(0, 0, 255, 1)">this</span>.dm.getDataByTag('sunlight'<span style="color: rgba(0, 0, 0, 1)">).p3()</span><span style="color: rgba(0, 0, 0, 1)">

      const config </span>=<span style="color: rgba(0, 0, 0, 1)"> {
            </span>'light.type': 'point'<span style="color: rgba(0, 0, 0, 1)">,
            </span>'light.color': 'white'<span style="color: rgba(0, 0, 0, 1)">,
            </span>'light.intensity': 0.3<span style="color: rgba(0, 0, 0, 1)">,
            </span>'light.range': 10000<span style="color: rgba(0, 0, 0, 1)">
      }

      light.s(config)
      light.p3(lightSource)</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.dm.add(light)
}</span></pre>
</div>
<p>&nbsp;</p>
<h2><strong>看向物体</strong></h2>
<p><strong>&nbsp;</strong>看到左下角的一个小窗口,其实是另一个3d场景,把它定位到左下角的,两个场景都使用了反序列化(&nbsp;<span style="color: rgba(255, 102, 0, 1)"><strong>deserialize&nbsp;</strong></span>)。</p>
<p><strong><img src="https://img2018.cnblogs.com/blog/1496396/201909/1496396-20190920160926011-1412135203.png"></strong></p>
<p>&nbsp;因为要定位医疗箱移动,所以这里使用到了&nbsp;<span style="color: rgba(255, 102, 0, 1)"><strong>flyTo</strong></span><strong>&nbsp;</strong>方法 。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">var</span> renderCanvas = <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> (medical, duration) {
    ht.Default.startAnim({
       duration,
       easing(v, t) {
         </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> t
       },
       action(v, t) {
         outScreenG3d.flyTo(medical, { direction: [</span>-5, 3, 5], distance: 300<span style="color: rgba(0, 0, 0, 1)"> })
       }
    })
}</span></pre>
</div>
<p>&nbsp;</p>
<h2><strong>封装动画</strong></h2>
<p>如果要实现这么多的动画,首先想到的是一个个物体进行移动的过程。医疗箱的行走、电梯的升降、传送带运送医疗箱等我们都可以对它们的动作进行封装。</p>
<p>如图可以看到医疗箱总是在动,所以定义了一个行走的动画,每次医疗箱行走的距离、行走方向、动画的配置都进行传参。</p>
<p>这里要说明的参数:</p>
<p>1.<strong><span style="color: rgba(255, 102, 0, 1)">node</span></strong>(对应的元素)</p>
<p>2.<strong><span style="color: rgba(255, 102, 0, 1)">fn</span></strong>(动画执行完进行回调的函数)</p>
<p>3.<strong><span style="color: rgba(255, 102, 0, 1)">config</span></strong>(动画配置)</p>
<p>4.<strong><span style="color: rgba(255, 102, 0, 1)">coord</span></strong>(方向轴)</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 行走动画</span>
<span style="color: rgba(0, 0, 0, 1)">walkAnim(node, fn, config, coord) {
  const { duration, space } </span>=<span style="color: rgba(0, 0, 0, 1)"> config
  const positionArray </span>=<span style="color: rgba(0, 0, 0, 1)"> node.p3()
  let isShadow </span>= <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">
  let ShadowNode </span>= <span style="color: rgba(0, 0, 255, 1)">null</span>
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果移动的元素是icu车或者供应车的话 获取它的阴影跟随元素移动</span>
   <span style="color: rgba(0, 0, 255, 1)">if</span> (node.getTag() === 'supply' || node.getTag() === 'icuCar'<span style="color: rgba(0, 0, 0, 1)">) {
     isShadow </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">
     ShadowNode </span>= <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.dm.getDataByTag(`${node.getTag()}Shadow`)
   }

   ht.Default.startAnim({
     duration,
     easing: </span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> (t) {
        </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> t
      },
      action(v, t) {
        </span><span style="color: rgba(0, 0, 255, 1)">if</span> (coord === 'x'<span style="color: rgba(0, 0, 0, 1)">) {
            node.p3(positionArray[</span>0] + t * space, positionArray, positionArray)
            isShadow </span>&amp;&amp; ShadowNode.p3(positionArray + t * space, positionArray, positionArray)
        } </span><span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">if</span> (coord === 'y'<span style="color: rgba(0, 0, 0, 1)">) {
            node.p3(positionArray[</span>0], positionArray + t * space, positionArray)
            isShadow </span>&amp;&amp; ShadowNode.p3(positionArray, positionArray + t * space, positionArray)
         } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
           node.p3(positionArray[</span>0], positionArray, positionArray + t *<span style="color: rgba(0, 0, 0, 1)"> space)
           isShadow </span>&amp;&amp; ShadowNode.p3(positionArray, positionArray, positionArray + t *<span style="color: rgba(0, 0, 0, 1)"> space)
         }
      },
      finishFunc() {
         </span><span style="color: rgba(0, 0, 255, 1)">typeof</span> fn === 'function' &amp;&amp;<span style="color: rgba(0, 0, 0, 1)"> fn(node)
      }
   })
}</span></pre>
</div>
<p>&nbsp;</p>
<h2 id="云中穿行效果"><strong>物体之间的影响</strong></h2>
<p>电梯的升降会影响很多东西,比如频台的移动会带着传送带和医疗箱,这里我用到了&nbsp;<span style="color: rgba(255, 102, 0, 1)"><strong>sethost</strong></span>&nbsp;<span style="color: rgba(0, 0, 0, 1)">吸附</span>方法(吸附:节点指定宿主,宿主进行改变会影响节点)。</p>
<p>很多场景下非常合适,我需要电梯升降的过程中带用医疗箱和频台一起上升,还有医疗箱放到传送带的时候,医疗箱要动起来,感觉是这真的传送带在带动医疗箱进行运动。</p>
<p>&nbsp;</p>
<p>这里要说明的参数:</p>
<p>1.<strong><span style="color: rgba(255, 102, 0, 1)">node</span></strong>(操作的电梯元素)</p>
<p>2.<strong><span style="color: rgba(255, 102, 0, 1)">medicalKit</span></strong>(医疗箱)</p>
<p>3.<strong><span style="color: rgba(255, 102, 0, 1)">fn</span></strong>(动画执行完进行回调的函数)</p>
<p>4.<strong><span style="color: rgba(255, 102, 0, 1)">status</span></strong> (电梯上升和下降的状态)</p>
<p>5.<strong><span style="color: rgba(255, 102, 0, 1)">config</span></strong>(动画配置)</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 电梯升降动画</span>
<span style="color: rgba(0, 0, 0, 1)">    elevatorAnim(node, medicalKit, fn, status, config) {
      const self </span>= <span style="color: rgba(0, 0, 255, 1)">this</span>
      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 获取电梯的index 让对应的频台也跟着动</span>
      const elevatorIndex = node.getTag().replace(/[^0-9]/ig, '') - 0

      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 获取医疗箱的index 控制电梯升降的距离</span>
      const medicalKitIndex = medicalKit.getTag().replace(/[^0-9]/ig, '') - 0<span style="color: rgba(0, 0, 0, 1)">
      const positionArray </span>=<span style="color: rgba(0, 0, 0, 1)"> node.p3()
      const station </span>=<span style="color: rgba(0, 0, 0, 1)"> self.dm.getDataByTag(`station${elevatorIndex}`)
      
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">吸附宿主</span>
<span style="color: rgba(0, 0, 0, 1)">      station.setHost(node)
      medicalKit.setHost(node)

      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 设置升降状态</span>
      <span style="color: rgba(0, 0, 255, 1)">if</span> (elevatorIndex === 3) self.elevatorRunning = <span style="color: rgba(0, 0, 255, 1)">true</span>
      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 升降距离 status 为 0 的时候是下降 最低部位的距离是固定的 所以只需要控制上升的距离</span>
      const medicalKitLevel =<span style="color: rgba(0, 0, 0, 1)"> self.returnMedicalKitLevel(medicalKitIndex)

      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 电梯的属性</span>
      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 最低点的位置 Lowest</span>
      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果有轨道的话 就去轨道的位置否则就按照层数 orbitalP</span>
      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 第一层的位置distance</span>
<span style="color: rgba(0, 0, 0, 1)">      let space
      const addSpace </span>= medicalKitIndex === 7 ? 100 : 0
      <span style="color: rgba(0, 0, 255, 1)">if</span> (status == 1<span style="color: rgba(0, 0, 0, 1)">) {
            space </span>= config.orbitalP ? config.orbitalP : config.distance + addSpace + (400 *<span style="color: rgba(0, 0, 0, 1)"> medicalKitLevel)
      } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
            space </span>=<span style="color: rgba(0, 0, 0, 1)"> config.Lowest
      }

      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 下降状态时 医疗箱不会做动作</span>
      <span style="color: rgba(0, 0, 255, 1)">if</span> (status === 0<span style="color: rgba(0, 0, 0, 1)">) {
            medicalKit.setHost()
      }

      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> ht.Default.startAnim({
            duration: config.orbitalP </span>? 2000 : (medicalKitLevel === 0 &amp;&amp; elevatorIndex == 3 ? 700 : 2500 + (medicalKitLevel * 1000<span style="color: rgba(0, 0, 0, 1)">)),
            action(v, t) {
                node.p3(
                  positionArray[</span>0<span style="color: rgba(0, 0, 0, 1)">],
                  positionArray[</span>1] + ((space - positionArray) *<span style="color: rgba(0, 0, 0, 1)"> t),
                  positionArray[</span>2<span style="color: rgba(0, 0, 0, 1)">]
                )
            },
            finishFunc() {
                station.setHost()
                </span><span style="color: rgba(0, 0, 255, 1)">typeof</span> fn === 'function' &amp;&amp;<span style="color: rgba(0, 0, 0, 1)"> fn(node)
            }
      })
    }</span></pre>
</div>
<p>&nbsp;</p>
<h2 id="云中穿行效果">动画方法</h2>
<p>&nbsp;动画的过程中有个问题需要处理就是等待电梯的动画,医疗箱在动画过程中,需要判断电梯是否在上升,如果不在地面的话,需要等待。</p>
<p><img src="https://img2018.cnblogs.com/blog/1496396/201909/1496396-20190920165548438-1173893912.gif"></p>
<p>&nbsp;我的思路是,当医疗箱走到离电梯一点距离的时候,需要判断电梯是否在上升状态,如果是的话,需要调用动画暂停的方法。</p>
<p>&nbsp;当<strong><span style="color: rgba(255, 102, 0, 1)"> elevatorRunning</span></strong> 为<span> false</span> 的时候代表电梯没有运动,否则在运动中。</p>
<p>&nbsp;电梯动画开始的时候设置为 true,结束后设置变量为 false,&nbsp;&nbsp;就可以监控它的状态了。</p>
<p>&nbsp;<strong><span style="color: rgba(255, 102, 0, 1)">ht.Default.startAnim</span></strong> 方法返回一个实例,利用&nbsp;<strong><span style="color: rgba(255, 102, 0, 1)">action</span></strong> 方法, 实现轮询监听动画状态,然后进行操作。</p>
<p>&nbsp;当<span style="color: rgba(255, 102, 0, 1)">&nbsp;<strong>elevatorRunning&nbsp;</strong><span style="color: rgba(0, 0, 0, 1)">为&nbsp;true&nbsp;的话, 使用&nbsp;<span style="color: rgba(255, 102, 0, 1)"><strong>anim.</strong></span></span></span><strong><span style="color: rgba(255, 102, 0, 1)">pause()</span>&nbsp;</strong><span>暂停当前动画。</span></p>
<p><span>&nbsp;当&nbsp;<strong><span style="color: rgba(255, 102, 0, 1)">elevatorRunning</span></strong>&nbsp;为&nbsp;false&nbsp;的话, 使用&nbsp;<strong><span style="color: rgba(255, 102, 0, 1)">anim.resume()</span>&nbsp;</strong>继续<span>当前动画。</span></span></p>
<div class="cnblogs_code">
<pre>const anim =<span style="color: rgba(0, 0, 0, 1)"> ht.Default.startAnim({
  duration,
  action(v, t) {
    node.p3(
       positionArray[</span>0<span style="color: rgba(0, 0, 0, 1)">],
       positionArray[</span>1<span style="color: rgba(0, 0, 0, 1)">],
       positionArray[</span>2] - (tpMax - positionArray) *<span style="color: rgba(0, 0, 0, 1)"> t
     );
     </span><span style="color: rgba(0, 0, 255, 1)">if</span> (index &gt; 1 &amp;&amp; self.elevatorRunning === <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">) {
         </span><span style="color: rgba(0, 0, 255, 1)">if</span> (node.p3() &lt;=<span style="color: rgba(0, 0, 0, 1)"> stopMax) {
           anim.pause();
           const t </span>= setInterval(() =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
             </span><span style="color: rgba(0, 0, 255, 1)">if</span> (self.elevatorRunning === <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">) {
                anim.resume();
                clearInterval(t);
             }
           }, </span>100<span style="color: rgba(0, 0, 0, 1)">);
       }
   }
   },
   finishFunc() {
     </span><span style="color: rgba(0, 0, 255, 1)">typeof</span> fn === "function" &amp;&amp;<span style="color: rgba(0, 0, 0, 1)"> fn();
   }
});</span></pre>
</div>
<p>&nbsp;&nbsp;</p>
<h2 id="云中穿行效果">事件监听(发布、订阅)</h2>
<p>&nbsp;因为需要监听某个当前动画的结束,然后进行相机位移。</p>
<p>&nbsp;如图,我需要监听第一个 3d 场景中显示提示文字动画结束,然后执行第二个 3d 场景的显示。因为2个是不同的场景,是不能用回调的方法监听到的,所以这里就用到了<strong><span style="color: rgba(255, 102, 0, 1)"> eventBus</span></strong> 事件总线。</p>
<p><img src="https://img2018.cnblogs.com/blog/1496396/201909/1496396-20190920171551661-1001225928.gif"></p>
<p>&nbsp;下面是 <strong><span style="color: rgba(255, 102, 0, 1)">eventBus</span></strong> 的使用,第一个参数代表要监听的注册函数名,第二个是回调函数。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 事件总线 监听事件</span>
eventbus.on('animation1', _ =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
const medical </span>= dm.getDataByTag('medicalKit1'<span style="color: rgba(0, 0, 0, 1)">)
renderView(medical, dm, gv)
})</span></pre>
</div>
<p>下面是&nbsp;<strong><span style="color: rgba(255, 102, 0, 1)">eventBus&nbsp;</span></strong>发射的使用,第一个参数代表要触发的函数名,第二个是发射给函数的参数。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 触发事件</span>
eventbus.emit("animation1", <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">);</span></pre>
</div>
<h1>&nbsp;</h1>
<h1>总结</h1>
<p>&nbsp;做完这个 demo 之后,除了对&nbsp;HT for Web&nbsp;更加熟练之外,对物联网也有了更深刻的概念。</p>
<p>&nbsp;我身为一名前端工作者,在这个时代感觉非常的自豪,因为我能通过自己的技能创造出许多能造福和改善人们生活的东西。</p>
<p>&nbsp;希望大家看到我的 demo 能够得到一些启发,同时也要相信自己能够创造不可能,为社会做出贡献。</p><br><br>
来源:https://www.cnblogs.com/htdaydayup/p/11558748.html
頁: [1]
查看完整版本: 基于 HTML5 WebGL 的医疗物流系统