#一个工程
开发文档里面的案例教程demo,这里只做了简单的封装,定义了onEvent方法,后面因为事件绑定太多就把一个html文件重构了,把一些方法写在了js里面进行引用,方便后面管理。
直接把源代码放进来是运行不起的,因为必须要定义apiOptions里的方法,方法可以为空,但必须要能回调。
再解决这个问题后,再次运行就可以了,把项目启动,再打开网页就有视频流传入了。
接下进行api的调用来实现项目展示时的需求。
<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<title>二开test</title>
<script src="js/ac.min.js"></script>
<script src="js/_onEvent.js"></script>
</head>
<body>
<div class="concent">
<div class="left" id="player"></div>
<div class="right"></div>
</div>
<script>
/*
默认基础配置
*/
//自定义键盘鼠标交互
let actionEventHander = {
'onkeydown': key => {
onKeyDown(key);
},
'onmousedown': mouse => {
onMouseDown(mouse);
},
}
//构造DigitalTwinPlayer对象所需的参数选项,更多参数详情请参考API开发手册里DigitalTwinPlayer对象
let options = {
//必选参数,网页显示视频流的domId
domId: 'player',
ui: {
//可选参数,是否显示页面加载详细信息,默认值false
startupInfo: true,
//左上角闪烁的状态指示灯,可以从不同的颜色看出当前的状态,具体含义请参考 CloudStatus.默认值:true
statusIndicator: true,
//左下角是否显示信息按钮,点击时,会在小窗口显示当前连接的实时运行状态;鼠标停留时,会显示当前所连接实例的详细信息。默认值:false
statusButton: true,
//是否在左下角显示“全屏”按钮,默认值:false
fullscreenButton: true,
//是否在左下角显示“回到初始位置”按钮,默认值:false
homeButton: true,
//是否显示Cloud工具栏
mainUI: false,
//是否显示指北针
campass: false,
//是否在下放显示任务队列,0用不显示,1执行比较耗时的操作时显示,2一直显示
taskListBar: 1,
},
//必选参数,二次开发时必须指定,否则无法进行二次开发
apiOptions: {
//必选参数,与云渲染主机通信成功后的回调函数
//注意:只有在onReady之后才可以调用DigitalTwinAPI接口
//但是,实际上直接不要这个,或定义空方法,也不影响
onReady: _onReady,
onEvent: _onEvent,
onLog: _onLog,
},
events: {
/*
参数类型:object,用于设置键盘、鼠标交互事件,后面可以随时通过 DigitalTwinPlayer#setActionEventEnabled 进行开关。
可以设置以下值(类型都是function):
onMouseEnter onMouseLeave onMouseMove onMouseDown
onMouseUp onKeyDown onKeyUp onKeyPress
*/
mouseKeyListener: {
//我的理解是,监听事件赋值给key,然后key执行后面的方法
onKeyDown: key => onKeyDown(key),
// onKeyDown: key => console.log(`KeyDown: ${key.code}`),
},
},
/*
鼠标、键盘交互事件的回调,和mouseKeyListener一样,监听键盘鼠标事件,二选一即可
但是actionEventHander会覆盖mouseKeyListener的监听
*/
'actionEventHander': actionEventHander,
/*
参数类型:string,取值:'document', 'video', 'none',默认值 'video'
简单来说,document是一直处于漫游状态,video是需要点击视频为焦点,none是禁止漫游
*/
keyEventTarget: 'video',
}
//AirCloud云渲染服务器地址和端口
let host = '127.0.0.1:8080';
//构造player对象
let demoPlayer = new DigitalTwinPlayer(host, options);
//构造DigitalTwinAPI对象并初始化
let airCityApi = demoPlayer.getAPI();
// let camera_moving_event_flag = __g.settings.setEnableCameraMovingEvent(true, 1);
</script>
</body>
<style>
/* 布局样式 */
.concent {
display: flex;
}
.left {
width: 1600px;
height: 800px;
}
</style>
</html>
这是onEvent的js,至于为什么要用async来定义方法,因为后面的方法里有用到await,这是这个封装的api的特性,用async和await来实现异步方法。onEvent自带有很多响应方式,和参数,合理利用就行,这里把工具栏里的几个常用功能给复制了出来,但是没有做到完美复刻,也几乎不可能,毕竟api也是有限的封装,自带的资源库没有工具栏里的多。但是作为展示用,也是够的。
/*
事件列表
主要用于自定义的方法,在需要用选择地点的时候进行调用api方法
固定剖切时,可设置相机位置,不是固定剖切的话,不建议使用,难以设置合理的相机角度
*/
async function _onEvent(event) {
//事件类型 参考交互事件类型枚举对象
let eventType = event.eventtype;
let objectId = event.ObjectID;
let modelname = event.ModelName;
let propertyname = event.PropertyName;
let layerType = event.Type;
let layerId = event.Id || event.ID;
let objectname = event.OID;
console.log(eventType)
//当前点击位置
let objectLocation = event.MouseClickPoint;
switch (eventType) {
// 鼠标左键点击时触发方法
case "LeftMouseButtonClick":
// objectLocation[x,y,z,pitch,yam]
let x = objectLocation[0];
let y = objectLocation[1];
let z = objectLocation[2];
//点选响应
if (high_light_actor_flag) {
highLightActor_event(objectId, modelname, propertyname, layerType, layerId, objectname);
break;
}
//剖切响应
if (startVolumeClip_flag) {
startVolumeClip_event(x, y, z);
break;
}
//标签响应
if (add_marker_flag) {
await addMarker_event(x, y, z);
break;
}
//动态标签响应
if (add_marker3d_flag) {
await addMarker3d_event(x, y, z)
break;
}
//添加车辆
if (add_vehicle_flag) {
addVehicle_event(x, y, z)
break;
}
//路径添加
if (set_point_flag && way_point_i <= way_max) {
await setWayMaxAndWayPoint_event(x, y, z);
}
break;
// //鼠标悬停时触发此事件
// //注意需提前开启鼠标拾取:__g.settings.setMousePickMask(7);
// 7: click, move, hover: 全开
// 0: click, move, hover: 全关
// case "MouseHovered":
// console.log('触发事件类型:鼠标悬停,eventType:' + eventType);
// break;
//
// //鼠标移动时触发此事件
// //注意需提前开启鼠标拾取:__g.settings.setMousePickMask(7);
// case "MouseMoved":
// console.log('触发事件类型:鼠标移动,eventType:' + eventType);
// break;
//
// //相机开始移动时触发此监听事件
// //注意需先开启事件:__g.settings.setEnableCameraMovingEvent(true);
// case "CameraStartMove":
// console.log('触发事件类型:相机开始飞行,eventType:' + eventType);
// break;
//
//相机正在移动时触发此监听事件
//注意需先开启事件:__g.settings.setEnableCameraMovingEvent(true);
// case "CameraMoving":
// console.log('触发事件类型:相机正在飞行,eventType:' + eventType);
// break;
//
// //相机停止移动时触发此监听事件
// //注意需先开启事件:__g.settings.setEnableCameraMovingEvent(true);
// case "CameraStopMove":
// console.log('触发事件类型:相机停止飞行,eventType:' + eventType);
// break;
//
// //对象执行focus()或相机执行set()/lookAt()/lookAtBBox()方法时触发
// case "CameraChanged":
// console.log('触发事件类型:相机位置发生变化,eventType:' + eventType);
// break;
//
// //进入面剖切模式,编辑面剖切位置后触发事件并返回剖切结果
// case "PlaneClipEdit":
// console.log('触发事件类型:编辑面剖切,eventType:' + eventType);
// break;
//
// //进入体剖切模式,编辑体剖切位置后触发事件并返回剖切结果
// case "VolumeClipEdit":
// console.log('触发事件类型:编辑体剖切,eventType:' + eventType);
// break;
//
// //进入测量模式后,测量完成时触发此事件并返回测量结果
// case "Measurement":
// console.log('触发事件类型:测量完成,eventType:' + eventType);
// break;
//
// //播放导览结束触发此事件
// //__g.camera.playAnimation(id)和导览对象播放导览结束__g.cameraTour.play(id)均触发此事件
// case "CameraTourFinished":
// console.log('触发事件类型:播放导览结束,eventType:' + eventType);
// break;
default:
break;
}
}
//api日志
function _onLog(a, b, c) {
// console.log(a);
// console.log(b);
// console.log(c);
}
//初始化api的入口,感觉像是let airCityApi = demoPlayer.getAPI()后的第一个执行方法入口
//可以用来初始化页面内容
function _onReady() {
}
function onMouseDown(mouse) {
if (set_point_flag && mouse.button == 2) {
set_point_flag = false;
startVehicle();
}
//鼠标回调输出
// console.log(`[MouseDn] button: ${mouse.button}, pos: ${mouse.x}, ${mouse.y}`);
}
function onKeyDown(key) {
//键盘回调输出
// console.log(`KeyDown: ${key.code}`);
}
本人并不是一个前端的开发人员,所以在页面上只是能用就行。没有对前端的页面设计有过多的思考,这些已经是极限了。
<body>
<div class="concent">
<div class="left" id="player"></div>
<div class="right">
<button type="button" onclick="reSet()" title="hello world">重载</button>
<button type="button" onclick="move_Flag()">移动</button>
<button id="Tools" type="button" onclick="hideOrShowUi(this)">UI开关</button>
<button id="Cp" type="button" onclick="hideOrShowUi(this)">CP开关</button>
<p>
<select id="ChoiceTime" onchange="changeTime(this)">
<option value="morning">清晨</option>
<option value="noon">中午</option>
<option value="evening">傍晚</option>
</select>
<input type="text" id="Input_Time" style="width: 100px; height: 12px;" placeholder="请输入切换小时">
<button id="InputTime" type="button" onclick="changeTime(this)">提交</button>
<button type="button" onclick="resetInputTime()">重置</button>
</p>
<p>
<input type="range" id="progress-bar" min="0" max="100" value="0" style="width: 300px">
<span id="progress-value"></span>
</p>
<textarea id="OutputTrees" rows="2" cols="40"
style="font-family: Arial, sans-serif; font-size: 16px;line-height: 2"
readonly></textarea>
<button type="button" onclick="outputTrees()">输出图层名字</button>
<p>
<input type="text" id="Layer" style="width: 100px; height: 12px;" placeholder="请输入图层名字">
<button type="button" onclick="locateByLayer()">定位</button>
<button type="button" onclick="locateByLocation()">定位到第一个路口</button>
<button type="button" onclick="resetLayer()">重置</button>
</p>
<textarea id="OutputVideos" rows="1" cols="40"
style="font-family: Arial, sans-serif; font-size: 16px;line-height: 2"
readonly></textarea>
<button type="button" onclick="outputVideos()">输出导览名字</button>
<p>
<input type="text" id="Video" style="width: 100px; height: 12px;" placeholder="请输入导览名字">
<button id="PlayerVideo" type="button" onclick="playerVideo(this)">播放</button>
<button id="PauseVideo" type="button" onclick="playerVideo(this)">暂停</button>
<button id="ResumeVideo" type="button" onclick="playerVideo(this)">恢复</button>
<button id="CloseVideo" type="button" onclick="playerVideo(this)">关闭</button>
<button type="button" onclick="resetVideo()">重置</button>
</p>
<p>
<button type="button" onclick="volume_Sectioning_Flexible()">体剖切</button>
<button type="button" onclick="stop_Volume_Sectioning()">结束体剖切</button>
<button type="button" onclick="volume_Sectioning()">隧道体剖切</button>
<button type="button" onclick="polyLine()">折线</button>
<button type="button" onclick="noPolyLine()">取消折线</button>
</p>
<p>
<input type="text" id="InputMarkerId" style="width: 40px; height: 12px;" placeholder="标签id">
<input type="text" id="InputMarkerText" style="width: 55px; height: 12px;" placeholder="文本信息">
<button type="button" onclick="addMarker()">普通</button>
<button type="button" onclick="addMarker3D()">3D</button>
<button type="button" onclick="resetInputMarker()">重置</button>
</p>
<p>
<input type="text" id="MarkerId" style="width: 80px; height: 12px;" placeholder="请输入标签id">
<button type="button" onclick="resetMarkerId()">重置</button>
<button type="button" onclick="deleteMarker()">删除</button>
<button type="button" onclick="deleteMarker3D()">3D删除</button>
</p>
<p>
<button id="hideMarkerById" type="button" onclick="showMarker(this)">隐藏</button>
<button id="showMarkerById" type="button" onclick="showMarker(this)">显示</button>
<button id="hideAllMarkersById" type="button" onclick="showMarker(this)">隐藏all</button>
<button id="showAllMarkersById" type="button" onclick="showMarker(this)">显示all</button>
<button id="clearAllMarkers" type="button" onclick="showMarker(this)">删除all</button>
</p>
<p>
<button id="hideMarker3DById" type="button" onclick="showMarker3D(this)">3D隐藏</button>
<button id="showMarker3DById" type="button" onclick="showMarker3D(this)">3D显示</button>
<button id="hideAllMarker3DsById" type="button" onclick="showMarker3D(this)">3D隐藏all</button>
<button id="showAllMarker3DsById" type="button" onclick="showMarker3D(this)">3D显示all</button>
<button id="clearAllMarker3Ds" type="button" onclick="showMarker3D(this)">3D删除all</button>
</p>
<p>
<button type="button" onclick="addVehicle()">添加车辆</button>
<button class="hover-tooltip" type="button" onclick="setWayMaxAndWayPoint()">设置轨迹</button>
<button type="button" onclick="resetPointWay()">清除轨迹</button>
<button type="button" onclick="startVehicle()">移动车辆</button>
<button id="pause" type="button" onclick="moveVehicle(this)">暂停车辆</button>
<button id="resume" type="button" onclick="moveVehicle(this)">恢复车辆</button>
<button id="stop" type="button" onclick="moveVehicle(this)">停止车辆</button>
<button type="button" onclick="focusVehicle()">跟随车辆</button>
<button type="button" onclick="focusVehicleHigher()">高跟随车辆</button>
<button type="button" onclick="outFocusVehicle()">停止跟随车辆</button>
<button type="button" onclick="resetVehicleAll()">重置</button>
</p>
<p id="highlightActor_layerId"></p>
<p id="highlightActor_layerType"></p>
<p id="highlightActor_objectId"></p>
<p id="highlightActor_modelname"></p>
<p id="highlightActor_objectname"></p>
<p id="highlightActor_propertyname"></p>
</div>
</div>
<button type="button" onclick="resetAllAdd()">清除所有添加</button>
<button type="button" onclick="addCustomObject()">添加cob</button>
<button type="button" onclick="clearCustomObject()">清除cob</button>
<button id="highlightActor_" type="button" onclick="highLightActor()">点选:关</button>
<button type="button" onclick="f1()">测试1</button>
<button type="button" onclick="f2()">测试2</button>
<button type="button" onclick="f3()">测试3</button>
后面就是一些js,简单调用api然后附加一些需求上的逻辑最后实现出来,并不太难,在开发文档里面都已经说的很清楚了,每个api的参数类型和返回参数类型,只要正确使用就行。这里就不一一展示了,主要看看这个感觉是最复杂的点选功能,其实是一个图层高亮功能和点击事件绑定组合的功能。
简单介绍一下,点击事件从定义开始就一直在触发,通过理解事件传出的参数,来绑定高亮功能,通过图层id和对象id来唯一高亮一个模型,然后点击另一个时,还要关闭前一个高亮,并展示出目前高亮模型的各种信息。这里对模型有个区分,分为软件导入模型和web导入模型,一个是存储在硬盘,一个存在内存,所以api只能控制web模型,所以有一些小模型,比如植被和物体就需要在web中导入才能高亮,这是后来才知道的,所以并没有对植被采取可选的操作,工程量太大了,但是一些物体就做了对应的重新导入。web导入的模型基本参数有些差别,不能使用api的高亮方法,只有自己定义一个方法通过其他方式来高亮模型,每次都要想一想之前有没有存在已经高亮的模型,高亮的模型又是哪种,不同的模型需要调用不同的方法来实现高亮和取消高亮,然后就是对信息输出,信息都是绑定在event参数里,直接读出来就行。这整个方法的关键是怎么取消前一个高亮,这里采用了存副本分方法,要是有其他更方法易懂的也欢迎大家分享交流,每次开关点选按钮都会重置副本以保证逻辑的正确性。
/*
点选操作
*/
let high_light_actor_flag = false;
//点选备份,方便二次点击取消之前的高亮
let layerId_a;
let objectI_a;
let layerId_b;
function highLightActor_event(objectId, modelname, propertyname, layerType, layerId, objectname) {
if (objectId === "" || objectId === null || objectId === undefined) {
console.log("物体")
if (layerId_b === "" || layerId_b === null || layerId_b === undefined) {
console.log("第一次")
if (objectI_a === "" || objectI_a === null || objectI_a === undefined) {
console.log("前面没有模型")
} else {
console.log("前面有模型")
__g.tileLayer.stopHighlightActor(layerId_a, objectI_a);
}
__g.customObject.highlight(layerId);
} else {
console.log("不是第一次")
__g.customObject.unhighlight(layerId_b);
if (objectI_a === "" || objectI_a === null || objectI_a === undefined) {
console.log("前面没有模型")
} else {
console.log("前面有模型")
__g.tileLayer.stopHighlightActor(layerId_a, objectI_a);
}
__g.customObject.highlight(layerId);
}
let a1 = document.getElementById('highlightActor_layerId');
a1.textContent = "layerId:" + layerId;
let a2 = document.getElementById('highlightActor_layerType');
a2.textContent = "layerType:" + layerType;
let a3 = document.getElementById('highlightActor_objectId');
a3.textContent = "objectId:" + objectId;
let a4 = document.getElementById('highlightActor_modelname');
a4.textContent = "modelname:" + objectname;
let a5 = document.getElementById('highlightActor_propertyname');
a5.textContent = "propertyname:" + propertyname;
layerId_b = layerId;
} else {
console.log("模型")
if (objectI_a === "" || objectI_a === null || objectI_a === undefined) {
console.log("第一次")
if (layerId_b === "" || layerId_b === null || layerId_b === undefined) {
console.log("前面没有物体")
} else {
console.log("前面有物体")
__g.customObject.unhighlight(layerId_b);
}
__g.tileLayer.highlightActor(layerId, objectId);
} else {
console.log("不是第一次")
__g.tileLayer.stopHighlightActor(layerId_a, objectI_a);
if (layerId_b === "" || layerId_b === null || layerId_b === undefined) {
console.log("前面没有物体")
} else {
console.log("前面有物体")
__g.customObject.unhighlight(layerId_b);
}
__g.tileLayer.highlightActor(layerId, objectId);
}
let a1 = document.getElementById('highlightActor_layerId');
a1.textContent = "layerId:" + layerId;
let a2 = document.getElementById('highlightActor_layerType');
a2.textContent = "layerType:" + layerType;
let a3 = document.getElementById('highlightActor_objectId');
a3.textContent = "objectId:" + objectId;
let a4 = document.getElementById('highlightActor_modelname');
a4.textContent = "modelname:" + modelname;
let a5 = document.getElementById('highlightActor_propertyname');
a5.textContent = "propertyname:" + propertyname;
layerId_a = layerId;
objectI_a = objectId;
}
}
function highLightActor() {
high_light_actor_flag = !high_light_actor_flag;
if (!high_light_actor_flag) {
__g.tileLayer.stopHighlightAllActors();
__g.customObject.unhighlight();
let a7 = document.getElementById("highlightActor_");
a7.textContent = "点选:关";
let a1 = document.getElementById('highlightActor_layerId');
a1.textContent = "";
let a2 = document.getElementById('highlightActor_layerType');
a2.textContent = "";
let a3 = document.getElementById('highlightActor_objectId');
a3.textContent = "";
let a4 = document.getElementById('highlightActor_modelname');
a4.textContent = "";
let a5 = document.getElementById('highlightActor_propertyname');
a5.textContent = "";
} else {
let a8 = document.getElementById("highlightActor_");
a8.textContent = "点选:开";
}
}
2023年9月5日