/**
* 气泡窗类
* @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 = `
${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