TkAstral3D/packages/sdk/lib/core/objects/Billboard.ts
2025-10-04 23:36:07 +08:00

168 lines
4.8 KiB
TypeScript

/**
* @author ErSan
* @email mlt131220@163.com
* @date 2025/4/6 12:02
* @description 广告牌对象
*/
import * as THREE from 'three'
import {POSITION} from "@/constant";
import BillboardTexture from "./texture/BillboardTexture";
import {deepAssign} from "@/utils";
export interface BillboardEventMap extends THREE.Object3DEventMap {
imgLoaded: { url:string };
redraw: { url:string }
}
export const getDefaultBillboardOptions = () => ({
name: "Billboard",
position: [0, 0, 0],
image: {
// 图像地址
url: '',
// 可见性
visible: false,
// 宽度
width: 32,
// 高度
height: 32,
// 旋转角度 deg
rotate: 0,
// 与文本的间距
margin: 2,
// 位置
position: POSITION.CENTER,
// 置顶
top: false,
},
text: {
// 内容
value: '',
// 可见性
visible: false,
// 字体大小
fontSize: 16,
// 字体颜色
fontColor: "#ffffff",
// 字体
fontFamily: `sans-serif,"Source Han Sans SC","Source Han Sans","WenQuanYi Micro Hei", "Times New Roman", "隶书", "幼圆"`,
// 加粗
fontWeight: 400,
// 字体风格(斜体)
fontStyle: "normal",
// 行间距
lineGap: 0,
// 内边距
padding: 0,
// 对齐方式, left, center, right
align: "center",
// 文本基线, top, middle, bottom,alphabetic,hanging,ideographic
baseline: "top",
// 描边宽度
strokeWidth: 0,
// 描边颜色
strokeColor: "#FFFFFF",
// 是否填充
fill: false,
// 填充颜色
fillColor: "#000000",
}
})
export default class Billboard extends THREE.Sprite<BillboardEventMap> {
type = 'Billboard';
isBillboard = true;
options = getDefaultBillboardOptions();
constructor(options: IBillboard.options, material?: THREE.SpriteMaterial) {
super()
deepAssign(this.options, options);
this.name = this.options.name;
const texture = new BillboardTexture(this.options, material ? (material.map?.image) : undefined);
texture.colorSpace = THREE.SRGBColorSpace;
if (material) {
if (material.map) {
texture.mapping = material.map.mapping;
texture.wrapS = material.map.wrapS;
texture.wrapT = material.map.wrapT;
texture.magFilter = material.map.magFilter;
texture.minFilter = material.map.minFilter;
texture.anisotropy = material.map.anisotropy;
texture.format = material.map.format;
texture.type = material.map.type;
texture.colorSpace = material.map.colorSpace;
texture.repeat.copy(material.map.repeat);
texture.offset.copy(material.map.offset);
texture.center.copy(material.map.center);
texture.matrix.copy(material.map.matrix);
}
this.material = material;
this.material.map = texture;
} else {
this.material = new THREE.SpriteMaterial({
map: texture,
sizeAttenuation: true,
depthWrite: true,
});
}
// @ts-ignore
texture.addEventListener("imgLoaded", (event) => {
this.material.needsUpdate = true;
// @ts-ignore
this.dispatchEvent({type: "imgLoaded", url: event.url})
})
// @ts-ignore
texture.addEventListener("redraw", (event) => {
const wh = {
width: texture.width,
height: texture.height,
}
if (wh.width > wh.height) {
wh.width = wh.width / wh.height;
wh.height = 1;
} else {
wh.height = wh.height / wh.width;
wh.width = 1;
}
this.geometry = new THREE.PlaneGeometry(wh.width, wh.height);
// @ts-ignore
this.dispatchEvent({type: "redraw", url: event.url})
})
this.position.set(this.options.position[0], this.options.position[1], this.options.position[2]);
// this.center = new THREE.Vector2(0.5, 0);
}
/**
* 获取json配置
*/
toJSON(meta?: THREE.JSONMeta) {
const options = JSON.parse(JSON.stringify(this.options));
options.name = this.name;
options.position = this.position.toArray();
const superJSON = super.toJSON(meta);
superJSON.object.options = options;
return superJSON;
}
/**
* 从json配置解析
*/
static fromJSON(json: { material: THREE.SpriteMaterial, options: IBillboard.options }) {
return new Billboard(json.options, json.material);
}
}