Appearance
快速上手
目录
目标
通过一个 30 分钟的简单快速上手教程,带您编写一个静态网页,用于加载端渲染场景,并且具备基础的接口调用和事件监听功能的演示案例。
预先条件
如果您准备使用图观统一 API,基于三维端渲染场景开发,需要具备以下硬件和软件的条件。
硬件
- 最低硬件要求:8G 内存,无独立显卡笔记本或者台式机。
- 推荐硬件要求:8G 内存,有独立显卡,NVIDIA GTX 960 及以上的笔记本或者台式机。
软件
- 操作系统:Windows,MacOS,Linux 均可
- Microsoft Visual Studio Code
- 推荐使用Google Chrome 浏览器,版本 88 及以上(2021 年 1 月之后版本),或者 Microsoft Edge 版本 88 及以上、360 浏览器版本 13 及以上
- 如需查看自己的 Google Chrome 浏览器版本,您可以在浏览器地址栏中输入
chrome://settings/help,或者点击菜单 -> 设置 -> 关于 Chrome

图观场景和代码起始包
开发图观场景,您需要具备一个已经发布的图观场景,和图观统一开发 API SDK。
如果是图观端场景,可以使用在线图观端渲染场景编辑器完成场景的编辑并且发布。发布之后,场景会生成一个全局唯一的 URL(统一资源定位符),通过查看详情可以复制二次开发地址。这个地址是您在进行 TGAPI 开发时需要传入的。

如果是图观流场景,需要将打包好的流场景,上传到图观流渲染服务平台。在添加场景后,同样可以获得访问地址和 token。

- 在本指南中,我们已经为您提供了一个已经制作好的端渲染场景。

- 推荐硬件配置使用:https://www.tuguan.net/api/sceneEditor/v1/scenes/cb205f6b-62e7-45ff-9734-a25883517b6e/loadAVWS

- 这两个场景的内容和功能没有本质的区别,只是在场景的多边形和材质部分有不同的针对性优化。
- 点击这里下载代码包
- 代码包中包括一个 handsonDemo.html 文件和一个 handsonDemoStart.html 文件。
- handsonDemoStart.html,可以作为您上手编写代码的起点。
- handsonDemo.html,是这个案例编写完成后的全部代码。
- html 文件当中引用了在线版本的 TGAPI SDK。
创建端渲染三维应用程序
引入版本:3.1 起支持
1. 打开 handsonDemoStart.html 页面
查看 handsonDemoStart.html 文件,只有非常少量的 HTML 代码,表示页面的基本head、body节点。head节点中包括后续会用到的基本style样式,在 body 节点中已经有几个后续会使用到的div元素。
HTML
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<title>图观统一开发API 快速上手</title>
<style>
/* 这里省略部分代码,具体请下载代码文件查看。 */
</style>
</head>
<body>
<div class="app" id="app">
<div id="container" style="width: 100%; height: 100%"></div>
<div id="loading" class="appLoading">
<img src="/startGuide/loading.gif" alt="" />
<div>正在加载场景</div>
</div>
</div>
<div class="btns hide">
<div>添加地标点</div>
<div>沿路径移动相机</div>
</div>
<script>
/* 程序逻辑主要实现位置 */
</script>
</body>
</html>2. 引入 TGAPI SDK 地址
在head标签的title和style之间插入如下代码。
JavaScript
<script src="https://www.tuguan.net/public/tgapp/TGApp.min.js"></script>这里使用了在线最新版本的 TGAPI,您也可以选用特定版本的 TGAPI。
3. 添加初始化加载场景代码
在body元素中添加onload属性,并且关联一个新的onBodyLoaded()方法。
HTML
<body onload="onBodyLoaded()">在body的script标签中创建一个 TGAPP 对象。然后实现onBodyLoaded()方法,在这个方法中首先需要请求一个场景访问 token。
JavaScript
<script>
// 初始化图观App
var appInstance = new TGApp.App();
// 获取场景Token
function onBodyLoaded() {
// 场景所在服务器
let server = "www.tuguan.net";
// 高配版本,写实场景ID:cb205f6b-62e7-45ff-9734-a25883517b6e
// 低配版本,卡通场景ID:ed402a51-a00a-48f2-8b60-f51963440e82
// 根据您的硬件配置选取不同的场景,将ID替换到下方的变量中。
let id = "ed402a51-a00a-48f2-8b60-f51963440e82";
// 定义URL模版
let sceneUrl = `http://${server}/api/sceneEditor/v1/scenes/${id}/loadAVWS`;
let tokenUrl = `http://${server}/api/user/v1/visitorScene/${id}`;
let xhr = new XMLHttpRequest();
// 设置请求地址和参数
xhr.open("post", tokenUrl, true);
// 设置请求头
xhr.setRequestHeader("Content-Type", "application/json charset=utf-8");
// 监听readystate状态
xhr.onreadystatechange = () => {
if (xhr.readyState == 4 && xhr.status == 200) {
let result = JSON.parse(xhr.responseText);
// 请求成功后,初始化地图
loadScene(sceneUrl, result.accessToken);
}
};
// 发送请求
xhr.send();
}
</script>在上面的onBodyLoaded方法中,我们需要在获取完 token 后,继续加载场景,需要实现loadScene方法。在onBodyLoaded方法下面,添加如下代码。
JavaScript
// 初始化加载图观三维场景服务
function loadScene(url, token) {
appInstance.initService({
container: document.getElementById("container"),
mode: "scene",
url: url,
token: token
}, (e) => {
console.log(e);
// 隐藏正在加载界面
document.querySelector('.appLoading').className = 'appLoading hide';
});
});
}特别说明的是,在initService指令的callback回调中,我们隐藏了正在加载的提示。这个提示框通过className选择器找到,然后增加了一个hide的 CSS 样式。
到这里,一个端渲染场景的加载已经基本完成了,可以运行页面进行浏览测试。
为了继续进行后续的功能演示,可以通过addEventListener订阅图观 API 的事件,获得场景服务初始化完成onServiceInit的事件。并且在callback回调函数中,显示出后续需要继续操作的交互按钮。在loadScene方法后面添加如下代码。
实际上,这个操作通过在上方的initService指令的callback回调中编写也可以,这里只是为了举例说明addEventListener的使用。
JavaScript
// 监听场景初始化成功事件
window.appInstance.uniCall('addEventListener', {
eventName: 'onServiceInit',
callback: (res) => {
// 显示交互按钮
document.querySelector('.btns').className = 'btns';
}
})4. 添加地标点 & 弹出标牌
接下来演示如何向场景中添加一个地标点,并且在点击地标点后弹出一个 HTML 的 DIV 标牌。
首先我们在上一步的调用addEventListener事件方法之后,添加点击添加地标点按钮的调用方法,并且实现addLandmark方法。
JavaScript
// 点击添加地标点
document.querySelectorAll('.btns div')[0].onclick = function () {
addLandmark();
}
// 添加一个地标点
function addLandmark() {
let jsonData = {
id: 'pointLayer', // 图层对象 id,新建时调用者自己传入的唯一标识,用于各种操作的对象识别
coordType: 0, // XY 轴坐标类别,0:X 经度,Y 纬度;1:X 米,Y米,相对世界00点
coordTypeZ: 0, // Z 轴坐标类别,0:相对 3D 世界 0 海拔;1:相对 3D 世界地表;2:相对 3D 模型表面(单位:米)
iconName: 'residence', // 内置图标名称,见图观官网统一API开发手册
label: '', // 图标标签文本
tag: 'custominfo', // 用户自定标签,用户保存用户的扩展数据
coord: [116.34953788361443, 40.08387756599562], // XY 轴坐标,X:经度;Y:纬度
coordZ: 65, // Z 轴高度(单位:米)
visible: true // 添加当前图层时默认是显示还是隐藏
}
// 开启图层可点击
window.appInstance.uniCall('addLandmark', jsonData, (res) => {
window.appInstance.uniCall('pickOverlay', {
overlayType: 'landmark',
idLayer: 'pointLayer',
type: 'click',
isShowDecorator: false
})
})
}注意,在addLandmark的回调函数中,代码又再一次调用了pickOverlay指令,开启地标点的点击。这样会在用户点击地标点后,触发一个onLandmarkClick事件。这样我们可以继续订阅这个事件,并且通过addOverlayTip指令添加指定地标点对象的标牌。
JavaScript
// 添加图层tip
function addOverlayTip() {
// 创建HTML里的div标牌
const landmarkerTipDiv = document.createElement('div');
landmarkerTipDiv.id = 'landmarkerTip';
landmarkerTipDiv.innerHTML = "居民楼";
landmarkerTipDiv.className = "landmarkerTipDiv";
document.body.appendChild(landmarkerTipDiv);
// 添加覆盖物的的弹出标牌
let jsonData = {
id: 'pointLayer', // 图层对象 id,新建时调用者自己传入的唯一标识,用于各种操作的对象识别
url: '', // Tip 窗口页面地址
divId: 'landmarkerTip', // 悬浮层DOM元素id
isShowClose: true, // 是否显示右上角关闭按钮,默认显示关闭按钮
size: [300, 150], // Tip 窗口宽高xy,x:窗口宽;y:窗口高(单位:像素)
offset: [50, 50], // Tip 左上角相对 对象中心点 偏移量 xy,x:为正,向右偏移;y:为正,向上偏移(单位:像素)
overlayType: 'landmark' // 覆盖物类型
}
appInstance.uniCall('addOverlayTip', jsonData, (result) => {
console.log(result);
})
}
// 注册地标点击事件
window.appInstance.uniCall('addEventListener', {
eventName: 'onLandmarkClick',
callback: (res) => {
addOverlayTip();
}
})到这里完成了向场景中添加一个地标点,并且在点击地标点后弹出 HTML 的 DIV 标牌的功能。
5. 添加路径 & 镜头沿路径移动
接下来演示如何向场景中添加一个路径,并且让镜头沿路径游历。
首先我们在上一步的代码之后,继续添加一个添加沿路径移动相机按钮的调用方法,并且实现addPath方法。
JavaScript
// 添加沿路径移动镜头
document.querySelectorAll('.btns div')[1].onclick = function () {
addPath();
}
// 添加路径图层
function addPath() {
let jsonData = {
id: 'path', // 图层对象 id,新建时调用者自己传入的唯一标识,用于各种操作的对象识别
name: '路径', // 图层名称,支持为图层自定义名称
coordType: 0, // XY 轴坐标类别,0:X 经度,Y 纬度;1:X 米,Y米,相对世界00点
coordTypeZ: 0, // Z 轴坐标类别,0:相对 3D 世界 0 海拔;1:相对 3D 世界地表;2:相对 3D 模型表面(单位:米)
type: 'Segment06', // 路线样式类别 Arrow01 Arrow02 Arrow03 Arrow04 Arrow05 Arrow06 Segment01 Segment02 Segment03 Segment04 Segment05 Segment06
color: '#00ff00', // 路线颜色,颜色透明(HEX 颜色值)
colorPass: '#00ff00', // 模型对象 移动经过后的 路径颜色(HEX 颜色值)
width: 2, // 路径宽度(单位:米)
tag: 'custominfo', // 用户自定标签,用户保存用户的扩展数据
visible: true, // 添加当前图层时默认是显示还是隐藏
points: [ // 定义路径途径点
{
"coord": [116.3500030557184, 40.08284427622822],
"coordZ": 0.3570831396935906
}, {
"coord": [116.3500530557184, 40.083374423769506],
"coordZ": 0.2769105811405785
}, {
"coord": [116.35011073784217, 40.084077407712066],
"coordZ": 0.2769105811404267
}, {
"coord": [116.35011073784217, 40.08480525607828],
"coordZ": 0.2769105811405026
}, {
"coord": [116.34898573742421, 40.08480525607828],
"coordZ": 0.2769105811403509
}
]
}
// 添加路径
appInstance.uniCall('addPath', jsonData, (result) => {
if (result.result === 1) {
pathingCamera();
}
})
}虽然上面的代码比较长,实际上只是路径点的数据比较多。这里只有一个图观 API 指令就是addPath,在addPath指令的回调函数里,再次调用让相机沿路径移动的方法,该方法实现的代码如下。
JavaScript
// 路径移动镜头
function pathingCamera() {
let jsonData = {
pathId: 'path', // 路径的id
loopMode: 'round', //循环模式:none,不循环;round,往返;repeat,从头循环
reverse: false, //是否反向移动
distance: 30, //镜头距覆盖物距离(单位:米)
pitch: 5, //镜头俯仰角(0~89)
speed: 10 //移动速度 (单位:米/秒)
}
// 开启路径移动镜头
appInstance.uniCall('pathingCamera', jsonData, (result) => {
console.log(result);
})
}到这里完成了添加路径并且让相机沿路径移动的效果。
6. 三维容器跟随浏览器缩放自适应
虽然图观场景可以自动填充container容器的大小,但是app节点并无法根据浏览器大小自适应。如果想实现这个特性,需要继续在代码最后添加如下内容。
JavaScript
// 地图自适应
function scale() {
// 可视区域的尺寸
let visibleWidth = window.innerWidth;
let visibleHeight = window.innerHeight;
// 手动配置的面板尺寸
let manualWidth = 1920;
let manualHeight = 1080;
// 计算横向和纵向的缩放系数
var widthPercentage = (1.0 * visibleWidth) / manualWidth;
var heightPercentage = (1.0 * visibleHeight) / manualHeight;
// 取得最小缩放系数
let scale = Math.min(widthPercentage, heightPercentage);
document.querySelector('#app').style.transform = `translateX(-50%) translateY(-50%) scale(${scale})`;
}
scale();
// 窗口调整大小时重新计算缩放尺寸
window.onresize = scale;拖动浏览器大小,内容也会随之缩放,比例始终保持正确。