飞渡的二次创作

该博客介绍了一个前端工程开发过程。对HTML文件进行重构,将部分方法写在JS里引用。解决运行问题后启动项目可传入视频流,接着调用API实现需求。还介绍了onEvent的JS异步方法,重点阐述了复杂的点选功能,包括图层高亮和点击事件绑定,以及不同模型高亮处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#一个工程

        开发文档里面的案例教程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日


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值