/** * 气泡窗类 * @class * */ import * as Cesium from 'cesium' import "./prompt.css" class Prompt { /** * @param {Cesium.Viewer} viewer 地图viewer对象 * @param {Object} opt * @param {Cesium.Cartesian3 | Array} [opt.position] 弹窗坐标 (type=2时生效) * @param {Boolean} opt.show 是否显示 * @param {Function} [opt.success] 创建成功的回调函数 * @param {Number} [opt.type=1] 1~位置变化提示框 / 2~固定坐标提示框 * @param {Cesium.Cartesian3 | Array} opt.position 固定坐标提示框的坐标( cartesian3 / [101,30] ),type为1时,可不设置此参数 * @param {Boolean} [opt.anchor=true] 是否显示锚点 * @param {Boolean} [opt.closeBtn=true] 是否显示关闭按钮 * @param {String} opt.className 自定义class * @param {String} opt.content 弹窗内容 * @param {Function} [opt.close] 关闭弹窗时的回调函数 * @param {Object} [opt.offset] 偏移参数 * @param {Number} [opt.offset.x] 横坐标偏移像素单位 * @param {Number} [opt.offset.y] 纵坐标偏移像素单位 * @param {Object} [opt.style] 弹窗面板样式 * @param {String} [opt.style.background='white'] 背景色 * @param {String} [opt.style.boxShadow] 弹窗阴影(css属性) * @param {String} [opt.style.color] 弹窗颜色 * */ constructor(viewer, opt) { this.viewer = viewer if (!this.viewer) return this.type = "prompt" // 默认值 opt = opt || {} const promptType = opt.type == undefined ? 1 : opt.type let defaultOpt = { id: (new Date().getTime() + "" + Math.floor(Math.random() * 10000)), type: promptType, anchor: promptType == 2 ? true : false, closeBtn: promptType == 2 ? true : false, offset: promptType == 2 ? { x: 0, y: -20 } : { x: 10, y: 10 }, content: "", show: true, style: { background: "rgba(0,0,0,0.5)", color: "white" } } this.opt = Object.assign(defaultOpt, opt) /** * @property {Object} attr 相关属性 */ this.attr = this.opt // ====================== 创建弹窗内容 start ====================== const mapid = this.viewer.container.id /** * @property {Boolearn} isShow 当前显示状态 */ this.isShow = this.opt.show == undefined ? true : this.opt.show // 是否显示 let anchorHtml = `` let closeHtml = `` const background = this.opt.style.background const color = this.opt.style.color if (this.opt.anchor) { anchorHtml += `
` } if (this.opt.closeBtn) { // 移动提示框 不显示关闭按钮 closeHtml = `x` } let boxShadow = this.opt.style.boxShadow const promptId = "prompt-" + this.opt.id const promptConenet = `
${this.opt.content}
${anchorHtml} ${closeHtml} ` // 构建弹窗元素 this.promptDiv = window.document.createElement("div") this.promptDiv.className = `easy3d-prompt ${this.opt.className}` this.promptDiv.id = promptId this.promptDiv.innerHTML = promptConenet let mapDom = window.document.getElementById(mapid) mapDom.appendChild(this.promptDiv) const clsBtn = window.document.getElementById(`prompt-close-${this.opt.id}`) let that = this if (clsBtn) { clsBtn.addEventListener("click", (e) => { that.hide() if (that.opt.close) that.opt.close() }) } /** * @property {Object} promptDom 弹窗div */ this.promptDom = window.document.getElementById(promptId) this.position = this.transPosition(this.opt.position) // ====================== 创建弹窗内容 end ====================== if (promptType == 2) this.bindRender() // 固定位置弹窗 绑定实时渲染 当到地球背面时 隐藏 if (this.opt.show == false) this.hide() this.containerW = this.viewer.container.offsetWidth this.containerH = this.viewer.container.offsetHeight this.containerLeft = this.viewer.container.offsetLeft this.containerTop = this.viewer.container.offsetTop /** * @property {Number} contentW 弹窗宽度 */ this.contentW = Math.ceil(Number(this.promptDom.offsetWidth)) // 宽度 /** * @property {Number} contentH 弹窗高度 */ this.contentH = this.promptDom.offsetHeight // 高度 if (this.opt.success) this.opt.success() } /** * 销毁 */ destroy () { if (this.promptDiv) { window.document.getElementById(this.viewer.container.id).removeChild(this.promptDiv) this.promptDiv = null } if (this.rendHandler) { this.rendHandler() this.rendHandler = null } } // 实时监听 bindRender () { let that = this this.rendHandler = this.viewer.scene.postRender.addEventListener(function () { if (!that.isShow && that.promptDom) { that.promptDom.style.display = "none" return } if (!that.position) return if (that.position instanceof Cesium.Cartesian3) { let px = Cesium.SceneTransforms.wgs84ToWindowCoordinates(that.viewer.scene, that.position) if (!px) return const occluder = new Cesium.EllipsoidalOccluder(that.viewer.scene.globe.ellipsoid, that.viewer.scene.camera.position) // 当前点位是否可见 是否在地球背面 const res = occluder.isPointVisible(that.position) if (res) { if (that.promptDom) that.promptDom.style.display = "block" } else { if (that.promptDom) that.promptDom.style.display = "none" } that.setByPX({ x: px.x, y: px.y }) } else { that.setByPX({ x: that.position.x, y: that.position.y }) } }, this) } /** * * @param {Cesium.Cartesian3 | Object} px 弹窗坐标 * @param {String} html 弹窗内容 */ update (px, html) { if (px instanceof Cesium.Cartesian3) { this.position = px.clone() px = Cesium.SceneTransforms.wgs84ToWindowCoordinates(this.viewer.scene, px) } this.contentW = Math.ceil(Number(this.promptDom.offsetWidth)) // 宽度 this.contentH = this.promptDom.offsetHeight // 高度 if (px) this.setByPX(px) if (html) this.setContent(html) } // 判断是否在当前视野内 isInView () { if (!this.position) return false let px = null if (this.position instanceof Cesium.Cartesian2) { px = this.position } else { px = Cesium.SceneTransforms.wgs84ToWindowCoordinates(this.viewer.scene, this.position) } const occluder = new Cesium.EllipsoidalOccluder(this.viewer.scene.globe.ellipsoid, this.viewer.scene.camera.position) // 是否在地球背面 const res = occluder.isPointVisible(this.position) let isin = false if (!px) return isin if ( px.x > this.containerLeft && px.x < (this.containerLeft + this.containerW) && px.y > this.containerTop && px.y < (this.containerTop + this.containerH) ) { isin = true } return res && isin } /** * 是否可见 * @param {Boolean} isShow true可见,false不可见 */ setVisible (isShow) { let isin = this.isInView(this.position) if (isin && isShow) { this.isShow = true if (this.promptDom) this.promptDom.style.display = "block" } else { this.isShow = false if (this.promptDom) this.promptDom.style.display = "none" } } showTip () { if (this.promptDom) this.promptDom.style.display = "block" } hideTip () { if (this.promptDom) this.promptDom.style.display = "none" } /** * 显示 */ show () { this.setVisible(true) } /** * 隐藏 */ hide () { this.setVisible(false) } /** * 设置弹窗内容 * @param {String} content 内容 */ setContent (content) { let pc = window.document.getElementById(`prompt-content-${this.opt.id}`) pc.innerHTML = content } /** * 设置弹窗坐标 * @param {Object} opt 屏幕坐标 */ setByPX (opt) { if (!opt) return if (this.promptDom) { const contentW = this.promptDom.offsetWidth // 宽度 const contentH = this.promptDom.offsetHeight // 高度 if (this.opt.type == 1) { this.promptDom.style.left = ((Number(opt.x) + Number(this.opt.offset.x || 0))) + "px" this.promptDom.style.top = ((Number(opt.y) + Number(this.opt.offset.y || 0))) + "px" } else { this.promptDom.style.left = ((Number(opt.x) + Number(this.opt.offset.x || 0)) - Number(this.contentW) / 2) + "px" this.promptDom.style.top = ((Number(opt.y) + Number(this.opt.offset.y || 0)) - Number(this.contentH)) + "px" } } } // 坐标转换 transPosition (p) { let position if (Array.isArray(p)) { const posi = Cesium.Cartesian3.fromDegrees(p[0], p[1], p[2] || 0) position = posi.clone() } else if (p instanceof Cesium.Cartesian3) { position = p.clone() } else { // 像素类型 position = p } return position } } export default Prompt