Files
sdk4.0/src/Obj/Base/TextObject/StandText/index.js

724 lines
26 KiB
JavaScript
Raw Normal View History

2025-07-03 13:54:01 +08:00
import Dialog from '../../../Element/Dialog';
import EventBinding from '../../../Element/Dialog/eventBinding';
import cy_tabs from "../../../Element/cy_html_tabs";
import richText from "../../../Element/richText";
import { html } from "./_element";
import Base from "../../index";
import LabelObject from '../../LabelObject'
import MouseEvent from '../../../../Event'
import MouseTip from '../../../../MouseTip'
import { syncData } from '../../../../Global/MultiViewportMode'
import { setSplitDirection, syncSplitData, setActiveId } from '../../../../Global/SplitScreen'
class StandText extends Base {
/**
* @constructor
* @param sdk
* @description 立体文字
* @param options {object}
* @param options.text {string} 文字
* @param options.color="#FFC107" {string} 颜色
* @param options.speed=1 {number} 文字移动速度
* @param {Array.<object>} positions 经纬度和高度的列表值交替 [{lon,lat,alt},...]
* @param _Dialog {object} 弹框事件
* @param _Dialog.confirmCallBack {function} 弹框确认时的回调
* */
constructor(sdk, options, _Dialog = {}) {
super(sdk, options);
this.options.text = options.text || "未命名对象"
let textArray = this.options.text.split('\n')
for (let i = 0; i < textArray.length; i++) {
if (textArray[i].length > 80) {
textArray[i] = textArray[i].slice(0, 80-textArray[i].length)
}
}
if (textArray.length > 70) {
textArray.splice(70 - textArray.length)
}
this.options.text = textArray.join('\n')
this.options.color = options.color || "#FFC107"
// this.options.cornerType = options.cornerType || 'MITERED'
this.options.positions = options.positions
// this.options.material = Number(options.material) || 0
this.options.speed = (options.speed || options.speed === 0) ? options.speed : 1
// this.options.duration = (options.duration || options.duration === 0) ? options.duration : 50000
this.options.show = (options.show || options.show === false) ? options.show : true
this.nodePoints = []
this.entity
this.options.instruct = options.instruct || ""
this.options.operatingPoint = options.operatingPoint || ""
this.options.attribute = options.attribute || {}
this.options.attribute.link = this.options.attribute.link || {}
this.options.attribute.link.content = this.options.attribute.link.content || []
this.options.attribute.camera = this.options.attribute.camera || []
this.options.attributeType = options.attributeType || 'richText'
this.extrudedHeight
this._EventBinding = new EventBinding()
this.Dialog = _Dialog
this._elms = {};
this.sdk.addIncetance(this.options.id, this)
StandText.create(this)
}
get text() {
return this.options.text
}
set text(v) {
this.options.text = v
let textArray = this.options.text.split('\n')
for (let i = 0; i < textArray.length; i++) {
if (textArray[i].length > 80) {
let _error = '行超过80个字符,请按回车Enter继续输入'
window.ELEMENT && window.ELEMENT.Message({
message: _error,
type: 'warning',
duration: 1000
});
textArray[i] = textArray[i].slice(0, 80-textArray[i].length)
}
}
if (textArray.length > 70) {
textArray.splice(70 - textArray.length)
let _error = '超过最大输入字符'
window.ELEMENT && window.ELEMENT.Message({
message: _error,
type: 'warning',
duration: 1000
});
}
this.options.text = textArray.join('\n')
if (this.entity) {
let positions = this.options.positions
let fromDegreesArray = []
let minimumHeights = []
let maximumHeights = []
let material = this.getMaterial()
2025-07-15 10:37:23 +08:00
let width = this.computeDistance2(positions)
2025-07-03 13:54:01 +08:00
let extrudedHeight = this.aspectRatio ? (width / this.aspectRatio) : 0
for (let i = 0; i < positions.length; i++) {
fromDegreesArray.push(positions[i].lng, positions[i].lat)
minimumHeights.push(positions[i].alt)
maximumHeights.push(positions[i].alt + extrudedHeight)
}
this.entity.wall.material = material
this.entity.wall.maximumHeights = maximumHeights
this.entity.wall.minimumHeights = minimumHeights
}
this._elms.text && this._elms.text.forEach((item) => {
item.value = v
})
}
get color() {
return this.options.color
}
set color(v) {
this.options.color = v
this.entity.wall.material = this.getMaterial()
if (this._elms.color) {
this._elms.color.forEach((item, i) => {
2025-07-16 15:02:18 +08:00
let colorPicker = new YJColorPicker({
2025-07-03 13:54:01 +08:00
el: item.el,
size: 'mini',//颜色box类型
alpha: true,//是否开启透明度
defaultColor: v,
disabled: false,//是否禁止打开颜色选择器
openPickerAni: 'opacity',//打开颜色选择器动画
sure: (c) => {
this.color = c
},//点击确认按钮事件回调
clear: () => {
this.color = 'rgba(255,255,255,1)'
},//点击清空按钮事件回调
})
this._elms.color[i] = colorPicker
})
}
}
// get material() {
// return this.options.material
// }
// set material(v) {
// this.options.material = Number(v)
// this.entity.wall.material = this.getMaterial()
// this._elms.material && this._elms.material.forEach((item) => {
// item.value = v
// })
// }
get speed() {
return this.options.speed
}
set speed(v) {
this.options.speed = v
this.entity.wall.material = this.getMaterial()
this._elms.speed && this._elms.speed.forEach((item) => {
item.value = v
})
}
// get attributeLink() {
// return this.options.attribute.link.content
// }
// set attributeLink(v) {
// this.options.attribute.link.content = v
// if (!this._DialogObject || !this._DialogObject._element || !this._DialogObject._element.content) {
// return
// }
// let table = this._DialogObject._element.content.getElementsByClassName('attribute-content-link')[1].getElementsByClassName('table')[0]
// let tableContent = table.getElementsByClassName('table-body')[0]
// tableContent.innerHTML = ''
// if (this.options.attribute.link.content.length > 0) {
// table.getElementsByClassName('table-empty')[0].style.display = 'none'
// }
// else {
// table.getElementsByClassName('table-empty')[0].style.display = 'flex'
// }
// for (let i = 0; i < this.options.attribute.link.content.length; i++) {
// let tr = `
// <div class="tr">
// <div class="td">` + this.options.attribute.link.content[i].name + `</div>
// <div class="td">` + this.options.attribute.link.content[i].url + `</div>
// <div class="td">
// <button class="text" @click="linkEdit">编辑</button>
// <button class="text" @click="linkDelete">删除</button>
// </div>
// </div>`
// let trElm = document.createRange().createContextualFragment(tr)
// tableContent.appendChild(trElm)
// }
// let item = tableContent.getElementsByClassName('tr')
// let fun = {
// linkEdit: async (index) => {
// this.attributeLink = await this.options.attribute.link.content
// let table = this._DialogObject._element.content.getElementsByClassName('attribute-content-link')[1].getElementsByClassName('table')[0]
// let tableContent = table.getElementsByClassName('table-body')[0]
// let item = tableContent.getElementsByClassName('tr')
// for (let i = 0; i < item.length; i++) {
// if (index === i) {
// let html = `
// <div class="td">
// <input class="input" type="text">
// </div>
// <div class="td">
// <div class="input-group">
// <input class="input" type="text" style="width: 140px;">
// <input type="file" accept=".mp4, .pdf" class="file-select" index="`+ i + `" style="display:none">
// <button @click="fileSelect">...</button>
// </div>
// </div>
// <div class="td">
// <button class="text" @click="confirmEdit">确认</button>
// <button class="text" @click="cancelEdit">取消</button>
// </div>`
// item[i].innerHTML = html
// let td = item[i].getElementsByClassName('td')
// td[0].getElementsByClassName('input')[0].value = this.options.attribute.link.content[index].name
// td[1].getElementsByClassName('input')[0].value = this.options.attribute.link.content[index].url
// let btn = item[i].getElementsByTagName('button')
// for (let n = 0; n < btn.length; n++) {
// for (let m of btn[n].attributes) {
// if (m.name === '@click') {
// btn[n].addEventListener('click', (e) => {
// if (typeof (fun[m.value]) === 'function') {
// fun[m.value]({ name: td[0].getElementsByClassName('input')[0].value, url: td[1].getElementsByClassName('input')[0].value }, i)
// }
// });
// btn[n].attributes.removeNamedItem(m.name)
// break
// }
// }
// }
// break
// }
// }
// },
// linkDelete: (i) => {
// this.options.attribute.link.content.splice(i, 1)
// this.attributeLink = this.options.attribute.link.content
// },
// confirmEdit: (value, i) => {
// this.options.attribute.link.content[i] = value
// this.attributeLink = this.options.attribute.link.content
// },
// cancelEdit: () => {
// this.attributeLink = this.options.attribute.link.content
// },
// fileSelect: (value, i) => {
// let fileElm = item[i].getElementsByClassName('file-select')[0]
// fileElm.click()
// fileElm.removeEventListener('change', fileSelect)
// fileElm.addEventListener('change', fileSelect)
// }
// }
// let fileSelect = (event) => {
// if (event.target.value) {
// let td = item[event.target.getAttribute('index')].getElementsByClassName('td')
// td[1].getElementsByClassName('input')[0].value = event.target.value
// event.target.value = null
// }
// }
// for (let i = 0; i < item.length; i++) {
// let btn = item[i].getElementsByTagName('button')
// for (let n = 0; n < btn.length; n++) {
// for (let m of btn[n].attributes) {
// if (m.name === '@click') {
// btn[n].addEventListener('click', (e) => {
// if (typeof (fun[m.value]) === 'function') {
// fun[m.value](i)
// }
// });
// btn[n].attributes.removeNamedItem(m.name)
// break
// }
// }
// }
// }
// }
get attributeCamera() {
return this.options.attribute.camera
}
set attributeCamera(v) {
this.options.attribute.camera = v
}
//创建
static async create(that) {
// console.log(new Cesium.CustomMaterialSource(), new Cesium.PolylineTrailLinkMaterialProperty())
let positions = that.options.positions
let fromDegreesArray = []
let minimumHeights = []
let maximumHeights = []
let material = that.getMaterial()
2025-07-15 10:37:23 +08:00
let width = that.computeDistance2(positions)
2025-07-03 13:54:01 +08:00
let extrudedHeight = that.aspectRatio ? (width / that.aspectRatio) : 0
// aspectRatio
for (let i = 0; i < positions.length; i++) {
fromDegreesArray.push(positions[i].lng, positions[i].lat)
minimumHeights.push(positions[i].alt)
maximumHeights.push(positions[i].alt + extrudedHeight)
}
that.entity = that.sdk.viewer.entities.add({
id: that.options.id,
show: that.options.show,
wall: {
positions: Cesium.Cartesian3.fromDegreesArray(fromDegreesArray),
cornerType: Cesium.CornerType.MITERED,
maximumHeights: maximumHeights,
minimumHeights: minimumHeights,
material: material,
},
// wall: {
// positions: Cesium.Cartesian3.fromDegreesArrayHeights(fromDegreesArray),
// maximumHeights: maximumHeights,
// minimumHeights: minimumHeights,
// material: new Cesium.PolylineTrailLinkMaterialProperty({duration: 1500}),
// outline: true,
// outlineColor: Cesium.Color.BLACK,
// }
})
syncData(that.sdk, that.options.id)
if(that.options.show) {
setSplitDirection(0, that.options.id)
}
}
// 编辑框
async edit(state) {
let _this = this
this.originalOptions = this.deepCopyObj(this.options)
if (this._DialogObject && this._DialogObject.close) {
this._DialogObject.close()
this._DialogObject = null
}
if (state) {
this._DialogObject = await new Dialog(this.sdk, this.options, {
title: '立体文字属性', left: '180px', top: '100px',
confirmCallBack: (options) => {
this.text = this.text.trim()
if (!this.text) {
this.text = '未命名对象'
}
this.originalOptions = this.deepCopyObj(this.options)
this._DialogObject.close()
this.Dialog.confirmCallBack && this.Dialog.confirmCallBack(this.originalOptions)
syncData(this.sdk, this.options.id)
syncSplitData(this.sdk, this.options.id)
},
resetCallBack: () => {
this.reset()
this.Dialog.resetCallBack && this.Dialog.resetCallBack()
},
removeCallBack: () => {
this.Dialog.removeCallBack && this.Dialog.removeCallBack()
},
closeCallBack: () => {
this.reset()
this.Dialog.closeCallBack && this.Dialog.closeCallBack()
for (let i = 0; i < this.nodePoints.length; i++) {
this.sdk.viewer.entities.remove(this.nodePoints[i])
}
this.nodePoints = []
YJ.Measure.SetMeasureStatus(false)
this.event && this.event.destroy()
this.tip && this.tip.destroy()
},
showCallBack: (show) => {
this.options.show = show
this.originalOptions.show = show
this.show = show
this.Dialog.showCallBack && this.Dialog.showCallBack()
},
secondaryEditCallBack: () => {
StandText.nodeEdit(this)
}
})
this._DialogObject._element.body.className = this._DialogObject._element.body.className + ' stand-text'
let contentElm = document.createElement('div');
contentElm.innerHTML = html(this)
this._DialogObject.contentAppChild(contentElm)
this.attributeType = this.options.attributeType
this.attributeCamera = this.options.attribute.camera
// setTimeout(() => {
// this.attributeLink = this.options.attribute.link.content
// this.cameraSelect()
// }, 500);
// 创建标签页
// let tabsElm = new cy_tabs('radar-scan-edit-tabs')
// 颜色组件
2025-07-16 15:02:18 +08:00
let colorPicker = new YJColorPicker({
2025-07-03 13:54:01 +08:00
el: contentElm.getElementsByClassName("color")[0],
size: 'mini',//颜色box类型
alpha: true,//是否开启透明度
defaultColor: this.color,
disabled: false,//是否禁止打开颜色选择器
openPickerAni: 'opacity',//打开颜色选择器动画
sure: (color) => {
this.color = color
},//点击确认按钮事件回调
clear: () => {
this.color = 'rgba(255,255,255,1)'
},//点击清空按钮事件回调
})
let all_elm = contentElm.getElementsByTagName("*")
this._EventBinding.on(this, all_elm)
this._elms = this._EventBinding.element
this._elms.color = [colorPicker]
} else {
if (this._DialogObject && this._DialogObject.remove) {
this._DialogObject.remove()
this._DialogObject = null
}
}
}
reset() {
if (!this.entity) {
return
}
this.options = this.deepCopyObj(this.originalOptions)
this.text = this.originalOptions.text
this.color = this.originalOptions.color
this.speed = this.originalOptions.speed
let positions = this.options.positions
let fromDegreesArray = []
let minimumHeights = []
let maximumHeights = []
2025-07-15 10:37:23 +08:00
let width = this.computeDistance2(positions)
2025-07-03 13:54:01 +08:00
let extrudedHeight = this.aspectRatio ? (width / this.aspectRatio) : 0
// aspectRatio
for (let i = 0; i < positions.length; i++) {
fromDegreesArray.push(positions[i].lng, positions[i].lat)
minimumHeights.push(positions[i].alt)
maximumHeights.push(positions[i].alt + extrudedHeight)
}
this.entity.wall.positions = Cesium.Cartesian3.fromDegreesArray(fromDegreesArray)
}
async remove() {
this.event && this.event.destroy()
this.tip && this.tip.destroy()
this.sdk.viewer.entities.remove(this.entity)
this.entity = null
if (this._DialogObject && !this._DialogObject.isDestroy) {
this._DialogObject.close()
this._DialogObject = null
}
await this.sdk.removeIncetance(this.options.id)
await syncData(this.sdk, this.options.id)
}
getMaterial() {
let material
let canvas = this.getcanvas()
material = new Cesium.CustomMaterialSource({
image: canvas.toDataURL("image/png"),
color: this.options.color,
repeat: new Cesium.Cartesian2(1, 1.0),
duration: 50000 / this.options.speed,
fltr: false
})
return material
}
getcanvas() {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d')
let textArray = this.options.text.split('\n')
for (let i = 0; i < textArray.length; i++) {
if (textArray[i].length > 80) {
textArray[i] = textArray[i].slice(0, 80-textArray[i].length)
}
}
if (textArray.length > 70) {
textArray.splice(70 - textArray.length)
}
this.options.text = textArray.join('\n')
let maxWidth = 0
for (let i = 0; i < textArray.length; i++) {
ctx.font = 200 + "px serif";
const width = ctx.measureText(textArray[i]).width;
if(maxWidth<width) {
maxWidth = width
}
}
canvas.width = maxWidth
canvas.height = 220 * textArray.length
for (let i = 0; i < textArray.length; i++) {
ctx.font = 200 + "px serif";
ctx.fillStyle = 'rgba(255, 255, 255, 0)'
ctx.fillRect(0, 0, maxWidth + 30, 210)
ctx.fillStyle = 'rgba(255, 255, 255, 1)';
2025-07-03 13:54:01 +08:00
ctx.font = "200px serif";
ctx.fillText(textArray[i], 0, 210 * (i+1));
}
this.aspectRatio = this.options.text ? (canvas.width / canvas.height) : 0
return canvas
}
/**
* 打开富文本框
*/
openRichTextEditor(e) {
richText.open(this.options.id, this.options.text, this.options.richTextContent)
richText.primaryCallBack = (content) => {
this.options.richTextContent = content
}
}
static nodeEdit(that, cb = () => { }) {
if (YJ.Measure.GetMeasureStatus()) {
cb('上一次测量未结束')
} else {
YJ.Measure.SetMeasureStatus(true)
that.tip = new MouseTip('请选择一个顶点,右键取消', that.sdk)
that.event = new MouseEvent(that.sdk)
that.nodePoints = []
let selectPoint
let originalPosition
let positions = that.options.positions
let fromDegreesArray = []
let minimumHeights = []
let maximumHeights = []
2025-07-15 10:37:23 +08:00
let width = that.computeDistance2(positions)
2025-07-03 13:54:01 +08:00
let extrudedHeight = that.aspectRatio ? (width / that.aspectRatio) : 0
for (let i = 0; i < positions.length; i++) {
fromDegreesArray.push(positions[i].lng, positions[i].lat)
minimumHeights.push(positions[i].alt)
maximumHeights.push(positions[i].alt + extrudedHeight)
}
let isAdd = false
let leftEvent = (movement, cartesian) => {
if (selectPoint) {
isAdd = true
let pos3 = that.sdk.viewer.scene.clampToHeight(cartesian, [that.entity])
that.options.positions[selectPoint.index] = that.cartesian3Towgs84(pos3, that.sdk.viewer)
originalPosition = that.options.positions[selectPoint.index]
let entity = that.sdk.viewer.entities.add({
name: 'node-secondary-edit-point',
position: Cesium.Cartesian3.fromDegrees(that.options.positions[selectPoint.index].lng, that.options.positions[selectPoint.index].lat, that.options.positions[selectPoint.index].alt),
billboard: {
image: that.getSourceRootPath() + '/img/point.png',
width: 15,
height: 15,
disableDepthTestDistance: Number.POSITIVE_INFINITY,
color: Cesium.Color.WHITE.withAlpha(0.99)
},
})
that.nodePoints.splice(selectPoint.index, 0, entity)
that.options.positions.splice(selectPoint.index, 0, that.options.positions[selectPoint.index])
let positions = that.options.positions
fromDegreesArray = []
minimumHeights = []
maximumHeights = []
2025-07-15 10:37:23 +08:00
width = that.computeDistance2(positions)
2025-07-03 13:54:01 +08:00
extrudedHeight = that.aspectRatio ? (width / that.aspectRatio) : 0
for (let i = 0; i < positions.length; i++) {
fromDegreesArray.push(positions[i].lng, positions[i].lat)
minimumHeights.push(positions[i].alt)
maximumHeights.push(positions[i].alt + extrudedHeight)
}
that.tip.setPosition(
cartesian,
movement.position.x,
movement.position.y
)
}
else {
var pick = that.sdk.viewer.scene.pick(movement.position);
if (pick && pick.id && pick.id.name && pick.id.name === 'node-secondary-edit-point') {
selectPoint = pick.id
that.nodePoints.splice(pick.id.index, 1)
that.sdk.viewer.entities.remove(pick.id)
that.tip.set_text('左键开始右键结束CTRL+右键撤销')
originalPosition = that.cartesian3Towgs84(cartesian, that.sdk.viewer)
that.entity.wall.positions = new Cesium.CallbackProperty(function () {
return Cesium.Cartesian3.fromDegreesArray(fromDegreesArray)
}, false)
that.entity.wall.maximumHeights = new Cesium.CallbackProperty(function () {
return maximumHeights
}, false)
that.entity.wall.minimumHeights = new Cesium.CallbackProperty(function () {
return minimumHeights
}, false)
}
}
}
let rightEvent = (movement, cartesian) => {
if (selectPoint) {
that.options.positions[selectPoint.index] = originalPosition
if(isAdd) {
that.options.positions.splice(selectPoint.index, 1)
}
cb(null, that.options.positions)
}
let positions = that.options.positions
fromDegreesArray = []
minimumHeights = []
maximumHeights = []
2025-07-15 10:37:23 +08:00
width = that.computeDistance2(positions)
2025-07-03 13:54:01 +08:00
extrudedHeight = that.aspectRatio ? (width / that.aspectRatio) : 0
for (let i = 0; i < positions.length; i++) {
fromDegreesArray.push(positions[i].lng, positions[i].lat)
minimumHeights.push(positions[i].alt)
maximumHeights.push(positions[i].alt + extrudedHeight)
}
that.entity.wall.positions = Cesium.Cartesian3.fromDegreesArray(fromDegreesArray)
for (let i = 0; i < that.nodePoints.length; i++) {
that.sdk.viewer.entities.remove(that.nodePoints[i])
}
that.nodePoints = []
YJ.Measure.SetMeasureStatus(false)
that.event.destroy()
that.tip.destroy()
}
that.event.mouse_left(leftEvent)
that.event.mouse_right(rightEvent)
that.event.mouse_move((movement, cartesian) => {
if (selectPoint) {
let pos3 = that.sdk.viewer.scene.clampToHeight(cartesian, [that.entity])
that.options.positions[selectPoint.index] = that.cartesian3Towgs84(pos3, that.sdk.viewer)
let positions = that.options.positions
fromDegreesArray = []
minimumHeights = []
maximumHeights = []
2025-07-15 10:37:23 +08:00
width = that.computeDistance2(positions)
2025-07-03 13:54:01 +08:00
extrudedHeight = that.aspectRatio ? (width / that.aspectRatio) : 0
for (let i = 0; i < positions.length; i++) {
fromDegreesArray.push(positions[i].lng, positions[i].lat)
minimumHeights.push(positions[i].alt)
maximumHeights.push(positions[i].alt + extrudedHeight)
}
}
that.tip.setPosition(
cartesian,
movement.endPosition.x,
movement.endPosition.y
)
})
that.event.mouse_right_keyboard_ctrl((movement, cartesian) => {
if (selectPoint) {
that.options.positions.pop()
that.sdk.viewer.entities.remove(that.nodePoints[that.nodePoints.length - 1])
that.nodePoints.pop()
if (selectPoint.index === that.options.positions.length) {
if (that.nodePoints[selectPoint.index - 1]) {
selectPoint = that.nodePoints[selectPoint.index - 1]
}
else {
selectPoint.index = 0
}
}
}
})
that.event.gesture_pinck_start((movement, cartesian) => {
let startTime = new Date()
let pos = {
position: {
x: (movement.position1.x + movement.position2.x) / 2,
y: (movement.position1.y + movement.position2.y) / 2
}
}
that.event.gesture_pinck_end(() => {
let endTime = new Date()
if (endTime - startTime >= 500) {
// 长按取消
rightEvent(pos, cartesian)
}
else {
leftEvent(pos, cartesian)
}
})
})
for (let i = 0; i < that.options.positions.length; i++) {
let entity = that.sdk.viewer.entities.add({
name: 'node-secondary-edit-point',
index: i,
position: Cesium.Cartesian3.fromDegrees(that.options.positions[i].lng, that.options.positions[i].lat, that.options.positions[i].alt),
billboard: {
image: that.getSourceRootPath() + '/img/point.png',
width: 15,
height: 15,
disableDepthTestDistance: Number.POSITIVE_INFINITY,
color: Cesium.Color.WHITE.withAlpha(0.99)
},
})
that.nodePoints.push(entity)
}
}
}
}
export default StandText