唯杰地图 logo唯杰地图
VJMAP
EN

Marker3D标记

vjmap3d/references/ui/marker3D.md 12.8 KB397 行
原文

Marker3D是基于threejs中 CSS3DObject 实现的div标记。

常用方法

方法 描述
isShow 是否显示
show 显示
hide 隐藏
getElement 获取元素对象
updateSize 更新大小
getPosition 获取位置
setPosition 设置位置
updatePopupPosition 更新弹出框信息窗口位置
addTo 增加至应用或实体中
isCss3dSprite 是否为Css3dSprite
togglePopup 切换弹出信息框显示或隐藏
setPopup 设置弹出信息框
getOpacity 获取透明度
setOpacity 设置透明度
remove 移除
setAnimation 设置动画
setCursor 设置光标

信号名

方法 描述
click 点击
occlusion 遮挡状态变化,回调参数 {object, occlusion}。⚠️ 只有构造选项 occlusionOpacity < 1(默认 1)时才做遮挡检测,否则永不触发;被遮挡时只把透明度设为 occlusionOpacity,元素不隐藏
elementSizeReady 元素大小已就绪。⚠️ 同时给了 pixelWidth 和 pixelHeight 时在构造函数内同步派发,new 之后再 .add 的监听收不到;否则在挂载后由 ResizeObserver 量到尺寸时异步派发
onAdd 增加
onRemove 移除

对于div的事件,可以通过getElement获取div元素,对此div增加事件。如

marker3d.getElement().addEventListener("dblclick", event => {
    console.log("双击")
});

⚠️ isHide: true 不是 addTo 同步生效:isHide 在内部 ResizeObserver 回调的 setTimeout(...,10ms) 里才被应用——addTo(app) 后立即 isShow() 仍返回 true,要等元素完成一次尺寸测量(约 10ms+ 之后)才会变 false。不要在 addTo 后立刻根据 isShow() 判断初始隐藏是否生效。

⚠️ 另外(headless/无布局环境):若 ResizeObserver 回调始终不触发(如 headless Chrome 无布局刷新),isShow() 可恒为 true——等待翻 false 的轮询必须有超时上限,不能假定它一定会翻。

用法示例

创建一个marker3D

let marker3d = new vjmap3d.Marker3D({
    color: vjmap3d.randHtmlColor(),
    width: 1
});
marker3d.setPosition([2, 0, 1]);
marker3d.addTo(app);

事件

marker3d.signal.click.add(() => {
    app.logInfo("click");
});
marker3d.signal.occlusion.add(() => { // 需构造时设 occlusionOpacity < 1 才会触发
    app.logInfo("occlusion");
});
marker3d.signal.elementSizeReady.add(() => {
    app.logInfo("elementSizeReady");
});
marker3d.signal.onAdd.add(() => {
    app.logInfo("onAdd");
});
marker3d.signal.onRemove.add(() => {
    app.logInfo("onRemove");
});
marker3d.getElement().addEventListener("contextmenu", event => {
    // @ts-ignore
    if (event.button != 2) return;//不是右键
    // 阻止地图默认右键菜单触发
    event.preventDefault();
    event.stopPropagation();
    new vjmap3d.ContextMenu({
        event: event,
        theme: "dark", //light
        width: "250px",
        items: [
            {type: 'custom', markup: `&lt;span style="color: #ffff00; padding-left: 30px">我是标注Marker的右键菜单&lt;/span>`},
            {
                label: '删除Marker',
                onClick: () => {
                    marker3d.remove()
                }
            }
        ]
    });
})
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
    container: "map", // 容器id
    stat: { show: true, left: "0" },
    scene: {
        gridHelper: { visible: true } // 是否显示坐标网格
    },
    camera: {  // 相机设置
        viewHelper: { enable: true, position: "leftBottom" } // 是否显示视角指示器
    }
})
let marker3d = new vjmap3d.Marker3D({
    color: vjmap3d.randHtmlColor(),
    width: 1
});
marker3d.setPosition([2, 0, 1]);
marker3d.addTo(app);
marker3d.signal.click.add(() => {
    app.logInfo("click");
});
marker3d.signal.occlusion.add(() => { // 需构造时设 occlusionOpacity < 1 才会触发
    app.logInfo("occlusion");
});
marker3d.signal.elementSizeReady.add(() => {
    app.logInfo("elementSizeReady");
});
marker3d.signal.onAdd.add(() => {
    app.logInfo("onAdd");
});
marker3d.signal.onRemove.add(() => {
    app.logInfo("onRemove");
});
marker3d.getElement().addEventListener("contextmenu", event => {
    // @ts-ignore
    if (event.button != 2) return;//不是右键
    // 阻止地图默认右键菜单触发
    event.preventDefault();
    event.stopPropagation();
    new vjmap3d.ContextMenu({
        event: event,
        theme: "dark", //light
        width: "250px",
        items: [
            {type: 'custom', markup: `<span style="color: #ffff00; padding-left: 30px">我是标注Marker的右键菜单</span>`},
            {
                label: '删除Marker',
                onClick: () => {
                    marker3d.remove()
                }
            }
        ]
    });
})

关联信息提示框

for (let i = 0; i < 100; i++) {
    let marker3d = new vjmap3d.Marker3D({
        color: vjmap3d.randHtmlColor(),
        asSprite: true,
        width: 1
    });
    marker3d.setPosition(vjmap3d.toVector3(vjmap3d.randPoint3D()));
    marker3d.addTo(app);
    marker3d.setPopup(
        new vjmap3d.Popup2D({
            backgroundColor: "#A0FFA0",
            buttonColor: "#f00",
            closeOnClick: true,
            closeButton: true
        }).setHTML(`index${i + 1}`)
    );
    // marker3d.togglePopup();
}
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
    container: "map", // 容器id
    stat: { show: true, left: "0" },
    scene: {
        gridHelper: { visible: true } // 是否显示坐标网格
    },
    camera: {  // 相机设置
        viewHelper: { enable: true, position: "leftBottom" } // 是否显示视角指示器
    }
})
for (let i = 0; i < 100; i++) {
    let marker3d = new vjmap3d.Marker3D({
        color: vjmap3d.randHtmlColor(),
        asSprite: true,
        width: 1
    });
    marker3d.setPosition(vjmap3d.toVector3(vjmap3d.randPoint3D()));
    marker3d.addTo(app);
    marker3d.setPopup(
        new vjmap3d.Popup2D({
            backgroundColor: "#A0FFA0",
            buttonColor: "#f00",
            closeOnClick: true,
            closeButton: true
        }).setHTML(`index${i + 1}`)
    );
    // marker3d.togglePopup();
}

遮挡时设置透明度

let box = new THREE.BoxGeometry(2, 2, 2)
const material = new THREE.MeshStandardMaterial({
    color: '#0ff'
});
let mesh = new THREE.Mesh(box, material);
mesh.position.set(0, 0, 0)
app.scene.add(mesh);
//
let marker3d = new vjmap3d.Marker3D({
    color: "#f00",
    width: 1,
    occlusionOpacity: 0.2 // 遮挡时透明度
});
marker3d.setPosition([0, -1, -1.5]);
marker3d.addTo(app);  
marker3d.signal.occlusion.add(e => {
    if (e.occlusion) {
        app.logInfo("被遮挡", "warn");
    } else {
        app.logInfo("没被遮挡", "success");
    }
});
app.signal.onAppAfterRender.add(t => {
    app.cameraControl.azimuthAngle += 20 * t.deltaTime * THREE.MathUtils.DEG2RAD;
})
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
    container: "map", // 容器id
    stat: { show: true, left: "0" },
    scene: {
        gridHelper: { visible: true } // 是否显示坐标网格
    },
    camera: {  // 相机设置
        viewHelper: { enable: true, position: "leftBottom" } // 是否显示视角指示器
    }
})
let box = new THREE.BoxGeometry(2, 2, 2)
const material = new THREE.MeshStandardMaterial({
    color: '#0ff'
});
let mesh = new THREE.Mesh(box, material);
mesh.position.set(0, 0, 0)
app.scene.add(mesh);
//
let marker3d = new vjmap3d.Marker3D({
    color: "#f00",
    width: 1,
    occlusionOpacity:0.2 // 遮挡时透明度
});
marker3d.setPosition([0, -1, -1.5]);
marker3d.addTo(app);  
marker3d.signal.occlusion.add(e => {
    if (e.occlusion) {
        app.logInfo("被遮挡", "warn");
    } else {
        app.logInfo("没被遮挡", "success");
    }
});
app.signal.onAppAfterRender.add(t => {
    app.cameraControl.azimuthAngle += 20 * t.deltaTime * THREE.MathUtils.DEG2RAD;
})

动画Marker

let marker3d = new vjmap3d.Marker3D({
    color: vjmap3d.randHtmlColor(),
    width: 1,
});
marker3d.setPosition(vjmap3d.toVector3(vjmap3d.toVector3(vjmap3d.randPoint3D({maxX: 5, maxY: 2, maxZ: 5, minX: -5, minY: -2, minZ: -5}))))
marker3d.addTo(app);
marker3d.setAnimation("MAP_ANIMATION_BOUNCE");

RotatingTextBorderMarker3d

// 创建一个缺省的
let markerEle = new vjmap3d.RotatingTextBorderMarker(
    {
        position: vjmap3d.randPoint3D({maxX: 5, maxY: 2, maxZ: 5, minX: -5, minY: -2, minZ: -5}),
        text: "3D地图"
    },
    {}
);
let marker3d = markerEle.createMarker3d({
    pixelWidth: 120,
    pixelHeight: 50,
    width: 5,
    asSprite: true
});
marker3d.addTo(app);

BreathingApertureMarker3D

// 创建一个缺省的
let markerEle = new vjmap3d.BreathingApertureMarker(
    {
        position: vjmap3d.randPoint3D({maxX: 5, maxY: 2, maxZ: 5, minX: -5, minY: -2, minZ: -5}),
        text: "3D地图"
    },
    {
        textColor: vjmap3d.randHtmlColor()
    }
);
let marker3d = markerEle.createMarker3d({
    pixelWidth: 120,
    pixelHeight: 50,
    width: 5,
    asSprite: true
});
marker3d.addTo(app);
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
    container: "map", // 容器id
    stat: { show: true, left: "0" },
    scene: {
        gridHelper: { visible: true } // 是否显示坐标网格
    },
    camera: {  // 相机设置
        viewHelper: { enable: true, position: "leftBottom" } // 是否显示视角指示器
    }
})
const ui = await app.getConfigPane({ title: "Marker", style: { width: "250px"}});
let gui = new vjmap3d.UiPanelJsonConfig();
gui.addButton("动画Marker", async () => {
    let marker3d = new vjmap3d.Marker3D({
        color: vjmap3d.randHtmlColor(),
        width: 1,
    });
    marker3d.setPosition(vjmap3d.toVector3(vjmap3d.toVector3(vjmap3d.randPoint3D({maxX: 5, maxY: 2, maxZ: 5, minX: -5, minY: -2, minZ: -5}))))
    marker3d.addTo(app);
    marker3d.setAnimation("MAP_ANIMATION_BOUNCE");
});
gui.addButton("RotatingTextBorderMarker3d", async () => {
    // 创建一个缺省的
    let markerEle = new vjmap3d.RotatingTextBorderMarker(
        {
            position: vjmap3d.randPoint3D({maxX: 5, maxY: 2, maxZ: 5, minX: -5, minY: -2, minZ: -5}),
            text: "3D地图"
        },
        {}
    );
    let marker3d = markerEle.createMarker3d({
        pixelWidth: 120,
        pixelHeight: 50,
        width: 5,
        asSprite: true
    });
    marker3d.addTo(app);
});
gui.addButton("BreathingApertureMarker", async () => {
    // 创建一个缺省的
    let markerEle = new vjmap3d.BreathingApertureMarker(
        {
            position: vjmap3d.randPoint3D({maxX: 5, maxY: 2, maxZ: 5, minX: -5, minY: -2, minZ: -5}),
            text: "3D地图"
        },
        {
            textColor: vjmap3d.randHtmlColor()
        }
    );
    let marker3d = markerEle.createMarker3d({
        pixelWidth: 120,
        pixelHeight: 50,
        width: 5,
        asSprite: true
    });
    marker3d.addTo(app);
});
gui.toJson().forEach(c => ui.appendChild(c));