ThingJS 开发使用感悟

ThingJS 开发使用感悟

前言

  • 近日,笔者所在公司购买了一个付费 JS 三维可视化框架开发平台——thingjs, 打算基于 thingjs 制作一个数字乡村可视化平台,笔者大概花了 3 个月的时间了解学习 thingjs 并初步制作了一版符合 UI 设计稿及交互的前端项目。初步完成后感触良多,这里写下一些个人感悟点用于有相关业务的开发人员避坑。

什么是 ThingJS

ThingJS 隶属于北京优锘科技有限公司,是优锘科技旗下物联网三维可视化开发平台。ThingJS采用JavaScript开发语言,主要面向前端程序员和实施人员。ThingJS平台让传统企业无需组建3D可视化开发团队,也能开发3D可视化应用。

  • 以上解释来自百度百科,我相信应该是由其官方人员编写的。简单的说,ThingJS 是由国人开发的(北京优锘),需要付费使用的,基于 ThreeJS 封装的一个 3d 可视化开发平台。

为何要使用一个付费产品, 不采用开源解决方案?

  • 因为传统上,如果我们需要开发图表类的应用,比如说常见的大屏应用,我们可以使用百度的echarts, 阿里的AntV,开源大热的D3JS,但是这些框架本质上说都是 2d 的图表展示效果,当然,它们也提供了一些三维图表,但是总体上说,echarts等平台提供的三维图表不满足很多复杂的带有一些特定交互场景的业务,所以在 web 开发方面,尤其是当下智慧城市方向特别火热时,我们有必要寻求一款可以在 web 端进行较复杂 3d 可视化场景开发的平台。那么在这一方向,目前的技术/框架有:WebGL, ThreeJS. 那它们的关系如何呢:

WebGL可以处理3D图像,听起来是非常高兴的一件事,但是WebGL实在是太底层了,WebGl解决是如何再画布上画图的问题,怎么画点,线,面,怎么上色,怎么贴图,怎么处理光线,视角转动之后怎么换算绘制等等。这些对于一个做3D应用的开发者来说要学的东西太多了。
Threejs库的出现解决了底层的渲染细节和复杂的数据结构,终于将复杂的底层细节抽象出来,使得大家开发3d应用更容易了一些。和很多开发者交流threejs都是他们首次接触的WebGL 3D库,并能很容易的就能开始做一些实验。
但是使用Threejs开发应用还是门槛很高,单就一个加载模型,调光,选择模型弹框的功能,就能干出Threejs上百行代码。同时还有很多复杂的3D概念需要理解。这时就需要ThingJS了。

  • 来自来自 ThingJS 团队的文章。

简单的说,WebGL 是需要从零开发 3d 场景的基础知识,而 ThreeJS 封装了 WebGL 的很多底层细节,用 ThreeJS 来做 3d 开发更简便,而 ThingJS 在 ThreeJS 的基础上又做了一层封装,进一步简化非专业人员开发 3d 场景的门槛。

如何使用 ThingJS

  • 笔者以公司目前需要开发的一个业务场景为例:笔者所在公司需要开发一个 3d 可视化的智慧乡村场景,在这个 3d 场景上加上一些 echarts 大屏的图表,同时还会有一些交互。大致场景如下:
    在这里插入图片描述

  • 该项目的地图背景是一个倾斜摄影模型,当然倾斜摄影模型只是一部分,在其外围是一个普通的,由 ThingJS 官方提供的一个三维底图。我们需要在这个模型基础上加入一些图表的展示,同时在此模型上加入大量的地图点位,每个地图点位都是一个摄像头,可以通过点击地图点位播放实时监控视频。

  • 开发流程

    • 首先便是购买 ThingJS 付费服务
      在这里插入图片描述

    来自 thingjs 官网,可以说是价格不菲了,入手需谨慎。

    • 其次,导入倾斜摄影模型,倾斜摄影模型的源数据来自无人机航拍。这里也是 ThingJS 提供的付费服务,我们只需付钱,将源数据交给官方人员,平台帮我们生成倾斜摄影模型。
      在这里插入图片描述

    • 到此开始正式开发,我们的思路是将项目分成两部分,即地图模型端图表端。地图模型端主要负责处理地图模型的一些交互,如切换地图底图;从后端获取地图点位数据并渲染到地图上;点击地图点位显示点位信息,并支持视频播放。最终项目在运行时,地图模型端是以一个 iframe 的形式嵌入到图表端的。

    • 这里有一点值得注意的是如果要实现最终的效果,不把项目分成两部分也是可以的,即把 echarts 图表部分也写在地图模型端,这样做的好处时之后不需要考虑 iframe 交互的问题。官方也提供了一个类似的例子。于是第一个坑点出现了,thingjs 是不支持目前主流的 JS 模块化方案的(ESM,UMD, CMD……),引入模块化文件需要靠官方提供的一个 api

const loadViedeJs = async () => {
    return new Promise(resolve => {
        THING.Utils.dynamicLoad([
            "https://www.thingjs.com/uploads/wechat/xxxx/static/video-js.min.css",
            "https://www.thingjs.com/uploads/wechat/xxxx/static/video.min.js"],
            function (result) {
                resolve(true)
            },
            true, // 选填 是否带时间戳
            true, // 选填 是否按顺序下载
            false //选填 文件是否包含加密文件
        )
    })
}

类似如上的写法,而如果要实现一个较为庞大复杂的业务场景时,就不太方便了,所以项目一开始便是分为地图模型端图表端两部分的。

开发流程

在开发流程上笔者先开发了项目的图表端的主要内容,之后再开发地图端部分。因为相比之下图表端的部分以 echarts和传统 html 网页为主,开发起来较为简单。先易后难的开发流程较好。

  • 图表端
    • 静态文件的编写
      这里笔者新建了一个本地代码备份调试仓库,将所有静态代码现在本地用最基本的 html 文件实现出来。下面以设计稿中的乡村文明-农村基层党建为例
      在这里插入图片描述
      要开发这个小模块,还需要将其细分为两部分,即 echarts 图表和普通 html 部分。新建一个 html 静态文件编写开发内容:
<!DOCTYPE html>
<html>

<head>
  <title>HTML</title>
</head>

<body>
  <div style="display: inline-block">
    <div style="
					padding: 2px 0 4px 29px;
					background: url(./assets/images/common/title-bg.png) no-repeat;
					font-family: YouSheBiaoTiHei;
					font-size: 20px;
					color: rgba(255, 247, 224, 0.9);
					letter-spacing: 0.4px;
				">
      农村基层党建
    </div>
    <div style="
					margin-top: 4px;
					padding: 35px 20px;
					padding-top: 0;
					background: url(./assets/images/common/data-bg.png) no-repeat;
					background-size: cover;
					overflow: hidden;
				">
      <div style="
						margin: 21px auto;
						width: 130px;
						text-align: center;
						background: url(./assets/images/common/blue.png) no-repeat;
						background-size: cover;
					">
        <div style="
							font-family: DINAlternate-Bold;
							font-size: 32px;
							color: #409aff;
							text-shadow: 0 0 20px #19acb5;
						">
          500
        </div>
        <div style="
							margin-top: 34px;
							font-family: PingFangSC-Regular;
							font-size: 18px;
							color: #a8dedd;
							letter-spacing: 0;
							text-shadow: 0 1px 2px rgba(35, 26, 124, 0.61);
						">
          基层党支部数量
        </div>
      </div>
      <div>
        <div style="
							font-family: YouSheBiaoTiHei;
							font-size: 20px;
							color: rgba(255, 247, 224, 0.9);
							letter-spacing: 0.4px;
						">
          <img src="./assets/images/manage/decoration-1.png" style="margin-right: 7px" />
          党员类型
        </div>
        <div style="height: 132px">
          <div style="
								background: url(./assets/images/common/circle.png) no-repeat;
							">
            <span>4,961</span>
          </div>
          <div>
            <div><span>预备党员</span> 2,845 <span>44%</span></div>
            <div><span>预备党员</span> 2,845 <span>44%</span></div>
          </div>
        </div>
      </div>
      <div>
        <div style="
							font-family: YouSheBiaoTiHei;
							font-size: 20px;
							color: rgba(255, 247, 224, 0.9);
							letter-spacing: 0.4px;
						">
          <img src="./assets/images/manage/decoration-1.png" style="margin-right: 7px" />党员年龄分布情况
        </div>
        <div style="
							padding: 25px 0;
							margin-left: 46px;
							width: 314px;
							background: url(./assets/images/common/Academic-degree.png)
								no-repeat;
							background-size: cover;
						">
          <div style="
								text-align: center;
								font-family: DINAlternate-Bold;
								font-size: 32px;
								color: #ffffff;
								letter-spacing: 0;
								text-shadow: 0 0 16px #91ffff;
							">
            200
          </div>
          <div style="
								text-align: center;
								font-family: PingFangSC-Regular;
								font-size: 18px;
								color: #a8dedd;
								letter-spacing: 0;
								text-shadow: 0 1px 2px rgba(35, 26, 124, 0.61);
							">
            大专以以上学历
          </div>
        </div>
        <div style="height: 224px">柱状图预留</div>
      </div>
      <div>
        <div style="
							font-family: YouSheBiaoTiHei;
							font-size: 20px;
							color: rgba(255, 247, 224, 0.9);
							letter-spacing: 0.4px;
						">
          <img src="./assets/images/manage/decoration-1.png" style="margin-right: 7px" />男女占比
        </div>
        <div style="
							display: flex;
							justify-content: center;
							align-items: center;
							margin-top: 22px;
						">
          <div>
            <div style="
									font-family: PingFangSC-Medium;
									font-size: 16px;
									color: #e0f7ff;
								">
              男生
            </div>
            <div style="
									font-family: DINAlternate-Bold;
									font-size: 16px;
									color: #44dbda;
								">
              567
            </div>
          </div>
          <div style="margin: 0 22px">
            <img src="./assets/images/man.png" />
            <img src="./assets/images/man.png" />
            <img src="./assets/images/man.png" />
            <img src="./assets/images/man.png" />
            <img src="./assets/images/man.png" />
            <img src="./assets/images/man.png" />
            <img src="./assets/images/man.png" />
            <img src="./assets/images/man.png" />
            <img src="./assets/images/man.png" />
            <img src="./assets/images/man.png" />
          </div>
          <div>
            <div style="
									font-family: PingFangSC-Medium;
									font-size: 16px;
									color: #e0f7ff;
								">
              女生
            </div>
            <div style="
									font-family: DINAlternate-Bold;
									font-size: 16px;
									color: #44dbda;
								">
              567
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</body>

</html>

注意这里笔者没有把样式代码写在 style 标签中,之前是考虑到最终 html 代码会以模板的形式加入到 JS 代码中去,感觉如果将 CSS 代码写在 style 标签会不好插入到模板中。但是后面笔者发现其实将 CSS 代码写在模板中也未尝不可,但是需要注意的时类名的命名上尽量不要重复,因为一旦类名重复可能会影响到其他组件的样式(推荐使用 BEM 方法进行命名)。

  • 将静态文件改写成能在 thingjs 图表端空白文件中运行的 JS 文件
    如下:
this.clearDom() //在每次进入创建DOM之前必须清除之前的DOM
let dom = this.getDom() //获取DOM节点
let jq = this.getJquery() //获取Jquery
let data = this.getComponentData() //获取组件数据

let topTitle = data.topTitle //标签数据
let title = data.title //标题数据
let content = data.content //内容数据

const aa = `<div class="civilization" style="display: inline-block">
      <div
        style="padding: 2px 0 4px 29px;
        background: url(/uploads/wechat/oLX7p00hNvyDvp6C44l-mJtTtmAo/file/数字清水/static/images/common/title-bg.png) no-repeat;
        font-family: YouSheBiaoTiHei;
        font-size: 20px;
        color: rgba(255, 247, 224, 0.9);
        letter-spacing: 0.4px;">
        农村基层党建
      </div>
      <div
        style="margin-top: 4px;
        padding: 35px 20px;
        padding-top: 0;
        background: url(/uploads/wechat/oLX7p00hNvyDvp6C44l-mJtTtmAo/file/数字清水/static/images/common/data-bg.png) no-repeat;
        background-size: cover;
        overflow: hidden;">
        <div
          style="margin: 21px auto;
          width: 140px;
          text-align: center;
          background: url(/uploads/wechat/oLX7p00hNvyDvp6C44l-mJtTtmAo/file/数字清水/static/images/common/blue.png) no-repeat;
          background-size: cover;">
          <div
            style="font-family: DINAlternate-Bold;
            text-align: center;
            font-size: 32px;
            color: #409aff;
            text-shadow: 0 0 20px #19acb5;">
            500
          </div>
          <div
            style="margin-top: 34px;
            text-align: center;
            font-family: PingFangSC-Regular;
            font-size: 18px;
            color: #a8dedd;
            letter-spacing: 0;
            text-shadow: 0 1px 2px rgba(35, 26, 124, 0.61);">
            基层党支部数量
          </div>
        </div>
        <div>
          <div
            style="font-family: YouSheBiaoTiHei;
            font-size: 20px;
            color: rgba(255, 247, 224, 0.9);
            letter-spacing: 0.4px;">
            <img
              src="/uploads/wechat/oLX7p00hNvyDvp6C44l-mJtTtmAo/file/数字清水/static/images/manage/decoration-1.png"
              style="margin-right: 7px"
              />
            党员类型
          </div>
          <div style="height: 110px">
            <div
              style="background: url(/uploads/wechat/oLX7p00hNvyDvp6C44l-mJtTtmAo/file/数字清水/static/images/common/circle.png)
              no-repeat;">
              <span>4,961</span>
            </div>
            <div>
              <div><span>预备党员</span> 2,845 <span>44%</span></div>
              <div><span>预备党员</span> 2,845 <span>44%</span></div>
            </div>
          </div>
        </div>
        <div>
          <div
            style="font-family: YouSheBiaoTiHei;
            font-size: 20px;
            color: rgba(255, 247, 224, 0.9);
            letter-spacing: 0.4px;">
            <img
              src="/uploads/wechat/oLX7p00hNvyDvp6C44l-mJtTtmAo/file/数字清水/static/images/manage/decoration-1.png"
              style="margin-right: 7px"
              />党员年龄分布情况
          </div>
          <div
            style="padding: 25px 0;
            margin-left: 46px;
            width: 314px;
            background: url(/uploads/wechat/oLX7p00hNvyDvp6C44l-mJtTtmAo/file/数字清水/static/images/common/Academic-degree.png)
            no-repeat;
            background-size: cover;">
            <div
              style="text-align: center;
              font-family: DINAlternate-Bold;
              font-size: 32px;
              color: #ffffff;
              letter-spacing: 0;
              text-shadow: 0 0 16px #91ffff;">
              200
            </div>
            <div
              style="text-align: center;
              font-family: PingFangSC-Regular;
              font-size: 18px;
              color: #a8dedd;
              letter-spacing: 0;
              text-shadow: 0 1px 2px rgba(35, 26, 124, 0.61);">
              大专以以上学历
            </div>
          </div>
          <div style="height: 190px"></div>
        </div>
        <div>
          <div
            style="font-family: YouSheBiaoTiHei;
            font-size: 20px;
            color: rgba(255, 247, 224, 0.9);
            letter-spacing: 0.4px;">
            <img
              src="/uploads/wechat/oLX7p00hNvyDvp6C44l-mJtTtmAo/file/数字清水/static/images/manage/decoration-1.png"
              style="margin-right: 7px"
              />男女占比
          </div>
          <div style="display: flex;justify-content: center; align-items: center; margin-top: 22px">
            <div>
              <div
                style="font-family: PingFangSC-Medium;
                font-size: 16px;
                color: #e0f7ff;">
                男生
              </div>
              <div
                style="font-family: DINAlternate-Bold;
                font-size: 16px;
                color: #44dbda;">
                567
              </div>
            </div>
            <div style="margin: 0 22px">
              <img src="/uploads/wechat/oLX7p00hNvyDvp6C44l-mJtTtmAo/file/数字清水/static/images/man.png" />
              <img src="/uploads/wechat/oLX7p00hNvyDvp6C44l-mJtTtmAo/file/数字清水/static/images/man.png" />
              <img src="/uploads/wechat/oLX7p00hNvyDvp6C44l-mJtTtmAo/file/数字清水/static/images/man.png" />
              <img src="/uploads/wechat/oLX7p00hNvyDvp6C44l-mJtTtmAo/file/数字清水/static/images/man.png" />
              <img src="/uploads/wechat/oLX7p00hNvyDvp6C44l-mJtTtmAo/file/数字清水/static/images/man.png" />
              <img src="/uploads/wechat/oLX7p00hNvyDvp6C44l-mJtTtmAo/file/数字清水/static/images/man.png" />
              <img src="/uploads/wechat/oLX7p00hNvyDvp6C44l-mJtTtmAo/file/数字清水/static/images/man.png" />
              <img src="/uploads/wechat/oLX7p00hNvyDvp6C44l-mJtTtmAo/file/数字清水/static/images/man.png" />
              <img src="/uploads/wechat/oLX7p00hNvyDvp6C44l-mJtTtmAo/file/数字清水/static/images/man.png" />
              <img src="/uploads/wechat/oLX7p00hNvyDvp6C44l-mJtTtmAo/file/数字清水/static/images/man.png" />
            </div>
            <div>
              <div
                style="font-family: PingFangSC-Medium;
                font-size: 16px;
                color: #e0f7ff;">
                女生
              </div>
              <div
                style="font-family: DINAlternate-Bold;
                font-size: 16px;
                color: #44dbda;">
                567
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>`
    
jq(dom).append(aa) //创建标签、标题和内容的div

这里需要注意的是类似 this.getJquery() //获取Jquery 这样的代码,通过 this 获取 jquery 函数是由 thingsjs 平台提供的能力,作为使用开发者却又无法在全局自定义挂载一个可使用的全局工具函数。

  • 改写完成后,将上述 JS 代码复制粘贴到 thingjs 图表端对应的空白组件代码编写区中去。
    在这里插入图片描述
    这里需要注意的一点是因为在本地调试完毕之后再复制粘贴到 thingjs 在线平台运行,一般来说是不会报什么问题的,但是因为开发环境的不同及一切其他原因,当我们粘贴代码后点击保存&运行按钮后仍可能运行不正常,这时候只需要 f12 打开浏览器控制台慢慢排查问题即可,待问题排查解决完毕该部分的 HTML 部分功能算是暂时完成了。之后再来处理 echarts 图表部分。

  • echarts 图表部分的开发
    在这里插入图片描述
    在这部分的开发里,笔者发现其有些效果会触发一些 bug 导致无法实现。比如说设计稿给出的 x轴的坐标名是带有图片背景的,笔者在本地开发可以实现这样的效果,但是将代码复制到在线开发平台就不行。

  • 接入接口数据
    在将一个个模块开发完毕之后,需要接入服务端接口数据。这里笔者是通过新建一个小的空白组件的方式进行的。
    在这里插入图片描述
    这里是用 jquery 自带的 ajax 请求方法进行的请求。之后将请求到的数据通过数据总线发布订阅的方式分发到各个业务组件中。

  • 图表端开发综述
    图表端的开发难度比想象中要大不少,这种难度不仅体现在对新工具平台的使用探索上,也体现在利用发布订阅的模式总览全局上。在编码方面,由于平台提供的代码编码端体验不佳,我们不得不先在本地进行开发再将相关代码复制到在线平台,而调试修改代码也是如此繁琐,目前暂未探索出更好的方式。

  • 6
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 12
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值