1
This commit is contained in:
98
src/hotspot/HotSpot.ts
Normal file
98
src/hotspot/HotSpot.ts
Normal file
@ -0,0 +1,98 @@
|
||||
import { Point_Pool } from './Point_Pool'
|
||||
import { Point } from './Point'
|
||||
import {
|
||||
AbstractMesh,
|
||||
ArcRotateCamera,
|
||||
Engine,
|
||||
Matrix,
|
||||
Scene,
|
||||
Vector3,
|
||||
Texture,
|
||||
StandardMaterial,
|
||||
MeshBuilder,
|
||||
TransformNode
|
||||
} from '@babylonjs/core'
|
||||
import { HotspotPrams } from './HotspotPrams'
|
||||
import type { MainApp } from '../babylonjs/MainApp'
|
||||
|
||||
class HotSpot {
|
||||
_point_Pool!: Point_Pool
|
||||
body!: HTMLElement
|
||||
_camera!: ArcRotateCamera
|
||||
mainApp!: MainApp
|
||||
hotspotTexture!: Texture
|
||||
hotspotMaterial!: StandardMaterial
|
||||
hotspotContainer!: TransformNode
|
||||
|
||||
vector!: Vector3
|
||||
halfW!: number
|
||||
halfH!: number
|
||||
annotation!: HTMLElement
|
||||
modedl!: AbstractMesh
|
||||
|
||||
|
||||
constructor(mainAPP: MainApp) {
|
||||
this.mainApp = mainAPP
|
||||
}
|
||||
|
||||
Awake() {
|
||||
this._camera = this.mainApp.appCamera.object
|
||||
this._point_Pool = new Point_Pool()
|
||||
|
||||
// 创建热点容器
|
||||
this.hotspotContainer = new TransformNode('hotspotContainer', this.mainApp.appScene.object)
|
||||
}
|
||||
|
||||
|
||||
//创建圆点并且生成事件 类型
|
||||
Point_Event(prams: HotspotPrams) {
|
||||
const iconPath = prams.icon
|
||||
|
||||
// 为每个热点创建独立的材质
|
||||
const texture = new Texture(iconPath, this.mainApp.appScene.object)
|
||||
texture.hasAlpha = true
|
||||
texture.getAlphaFromRGB = false
|
||||
|
||||
|
||||
const material = new StandardMaterial(`hotspotMaterial_${Math.random()}`, this.mainApp.appScene.object)
|
||||
material.diffuseTexture = texture
|
||||
material.emissiveTexture = texture
|
||||
material.opacityTexture = texture
|
||||
material.useAlphaFromDiffuseTexture = true
|
||||
material.transparencyMode = 2 // ALPHABLEND 模式
|
||||
material.disableLighting = true
|
||||
material.backFaceCulling = false
|
||||
|
||||
// 检查纹理是否已加载
|
||||
if (texture.isReady()) {
|
||||
// 纹理已准备好,立即创建热点
|
||||
this.createPointPlane(prams, material)
|
||||
} else {
|
||||
// 纹理未准备好,等待加载完成
|
||||
texture.onLoadObservable.addOnce(() => {
|
||||
this.createPointPlane(prams, material)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 创建点平面的具体实现
|
||||
createPointPlane(prams: HotspotPrams, material: StandardMaterial) {
|
||||
let { position, disposition, onload, onCallBack } = prams
|
||||
let _point = new Point(material, this.hotspotContainer, this.mainApp.appScene.object)
|
||||
_point.init(position, disposition, onload, onCallBack, prams.radius)
|
||||
|
||||
// 将热点添加到热点池中
|
||||
this._point_Pool.Add_point(_point)
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
Enable_All(visible: boolean) {
|
||||
if (this._point_Pool) {
|
||||
this._point_Pool.Enable_All(visible)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { HotSpot }
|
||||
24
src/hotspot/HotspotPrams.ts
Normal file
24
src/hotspot/HotspotPrams.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { Vector3 } from '@babylonjs/core'
|
||||
export class HotspotPrams {
|
||||
constructor(
|
||||
position: Vector3,
|
||||
disposition: Vector3,
|
||||
onload: Function,
|
||||
onCallBack: Function,
|
||||
icon?: string,
|
||||
radius?: number
|
||||
) {
|
||||
this.position = position
|
||||
this.disposition = disposition
|
||||
this.onload = onload
|
||||
this.onCallBack = onCallBack
|
||||
this.icon = icon
|
||||
this.radius = radius
|
||||
}
|
||||
position!: Vector3
|
||||
disposition!: Vector3
|
||||
onload!: Function
|
||||
onCallBack!: Function
|
||||
icon?: string
|
||||
radius?: number
|
||||
}
|
||||
105
src/hotspot/Point.ts
Normal file
105
src/hotspot/Point.ts
Normal file
@ -0,0 +1,105 @@
|
||||
import {
|
||||
Vector3,
|
||||
ActionManager,
|
||||
ExecuteCodeAction,
|
||||
StandardMaterial,
|
||||
MeshBuilder,
|
||||
Mesh,
|
||||
TransformNode,
|
||||
Scene,
|
||||
Ray,
|
||||
Observer
|
||||
} from '@babylonjs/core'
|
||||
|
||||
export class Point {
|
||||
annotation!: HTMLElement
|
||||
showBox!: HTMLElement
|
||||
position!: Vector3
|
||||
disposition!: Vector3
|
||||
onload!: Function
|
||||
onCallBack!: Function
|
||||
offCallBack!: Function
|
||||
isClick!: boolean
|
||||
img!: any
|
||||
plane!: Mesh
|
||||
spriteBehindObject!: boolean
|
||||
hotspotMaterial!: StandardMaterial
|
||||
hotspotContainer!: TransformNode
|
||||
scene!: Scene
|
||||
occlusionObserver!: Observer<Scene> | null
|
||||
|
||||
constructor(hotspotMaterial: StandardMaterial, hotspotContainer: TransformNode, scene: Scene) {
|
||||
this.hotspotMaterial = hotspotMaterial
|
||||
this.hotspotContainer = hotspotContainer
|
||||
this.scene = scene
|
||||
this.occlusionObserver = null
|
||||
}
|
||||
|
||||
init(
|
||||
position: Vector3,
|
||||
disposition: Vector3,
|
||||
onload: Function,
|
||||
onCallBack: Function,
|
||||
radius?: number
|
||||
) {
|
||||
this.position = position
|
||||
this.disposition = disposition
|
||||
this.onCallBack = onCallBack
|
||||
this.onload = onload
|
||||
|
||||
|
||||
this.Create_plane(radius)
|
||||
this.setupEvents()
|
||||
//this.Create_annotation(onload, onCallBack)
|
||||
//this.isClick = false
|
||||
}
|
||||
|
||||
Create_plane(radius?: number) {
|
||||
// 创建一个平面作为热点
|
||||
this.plane = MeshBuilder.CreatePlane(
|
||||
Math.random().toString(36).slice(-6),
|
||||
{
|
||||
size: radius ? radius / 10 : 0.14, // 热点大小,如果传入 radius 则缩放
|
||||
sideOrientation: Mesh.DOUBLESIDE
|
||||
},
|
||||
this.scene
|
||||
)
|
||||
|
||||
// 设置热点位置
|
||||
this.plane.position.copyFrom(this.position)
|
||||
|
||||
// 应用材质
|
||||
this.plane.material = this.hotspotMaterial
|
||||
|
||||
// 启用深度测试,让热点被模型遮挡时不显示
|
||||
if (this.plane.material) {
|
||||
this.plane.material.disableDepthWrite = false
|
||||
this.plane.material.needDepthPrePass = true
|
||||
}
|
||||
|
||||
// 设置为公告牌模式,让热点始终面向摄像机
|
||||
this.plane.billboardMode = Mesh.BILLBOARDMODE_ALL
|
||||
|
||||
// 设置父节点为热点容器
|
||||
this.plane.parent = this.hotspotContainer
|
||||
|
||||
// 确保热点可见和可交互
|
||||
this.plane.isVisible = true
|
||||
this.plane.isPickable = true
|
||||
this.plane.renderingGroupId = 1
|
||||
|
||||
}
|
||||
|
||||
setupEvents() {
|
||||
if (this.plane && this.onCallBack) {
|
||||
this.plane.actionManager = new ActionManager(this.scene)
|
||||
this.plane.actionManager.registerAction(new ExecuteCodeAction(
|
||||
ActionManager.OnPickTrigger,
|
||||
() => {
|
||||
console.log('热点被点击:', this.plane.name)
|
||||
this.onCallBack(this)
|
||||
}
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
20
src/hotspot/Point_Pool.ts
Normal file
20
src/hotspot/Point_Pool.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { Point } from './Point'
|
||||
|
||||
export class Point_Pool {
|
||||
points: Point[]
|
||||
constructor() {
|
||||
this.points = new Array()
|
||||
}
|
||||
|
||||
Add_point(point_Class: Point) {
|
||||
this.points.push(point_Class)
|
||||
}
|
||||
|
||||
Enable_All(visible: boolean) {
|
||||
for (let i = 0, item; (item = this.points[i++]); ) {
|
||||
if (item.plane) {
|
||||
item.plane.isVisible = visible
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
src/hotspot/btn_热点.png
Normal file
BIN
src/hotspot/btn_热点.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.1 KiB |
3
src/hotspot/index.ts
Normal file
3
src/hotspot/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export * from './HotSpot'
|
||||
export * from './Point'
|
||||
export * from './HotspotPrams'
|
||||
157
src/hotspot/style/point.css
Normal file
157
src/hotspot/style/point.css
Normal file
@ -0,0 +1,157 @@
|
||||
/* .canvas {
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.annotation {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 0;
|
||||
margin-left: -10px;
|
||||
margin-top: -10px;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 10%;
|
||||
font-size: 12px;
|
||||
line-height: 1.2;
|
||||
transition: opacity 0.5s;
|
||||
|
||||
}
|
||||
|
||||
|
||||
.line_Right {
|
||||
position: absolute;
|
||||
top: 35px;
|
||||
left: 55px;
|
||||
z-index: 0;
|
||||
margin-left: 30px;
|
||||
margin-top: -30px;
|
||||
width: 241px;
|
||||
height: 104px;
|
||||
border-radius: 10%;
|
||||
font-size: 12px;
|
||||
line-height: 1.2;
|
||||
transform-origin: 0 0;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.line_Left {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
left: -210px;
|
||||
z-index: 0;
|
||||
margin-left: 30px;
|
||||
margin-top: -30px;
|
||||
width: 241px;
|
||||
height: 104px;
|
||||
border-radius: 10%;
|
||||
font-size: 12px;
|
||||
line-height: 1.2;
|
||||
transform-origin: 100% 0;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ShowBox_left {
|
||||
position: absolute;
|
||||
top: 120px;
|
||||
left: -55px;
|
||||
z-index: 1;
|
||||
margin-left: -10px;
|
||||
margin-top: -10px;
|
||||
width: 70px;
|
||||
height: 50px;
|
||||
border-radius: 10%;
|
||||
font-size: 12px;
|
||||
line-height: 50px;
|
||||
transition: opacity 0.5s;
|
||||
background-size: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.ShowBox_right {
|
||||
position: absolute;
|
||||
top: 120px;
|
||||
left: 240px;
|
||||
z-index: 1;
|
||||
margin-left: -10px;
|
||||
margin-top: -10px;
|
||||
width: 70px;
|
||||
height: 50px;
|
||||
border-radius: 10%;
|
||||
font-size: 12px;
|
||||
line-height: 50px;
|
||||
transition: opacity 0.5s;
|
||||
background-size: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.after {
|
||||
content: attr(Text);
|
||||
position: absolute;
|
||||
top: -110px;
|
||||
left: 50px;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border: 2px solid #fff;
|
||||
border-radius: .5em;
|
||||
font-size: 16px;
|
||||
line-height: 30px;
|
||||
text-align: center;
|
||||
background: rgba(0, 0, 0, 1);
|
||||
|
||||
}
|
||||
|
||||
#number {
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.linimg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
background-size: 100%;
|
||||
}
|
||||
|
||||
.pointimg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-size: 100%;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease-in;
|
||||
border-radius: 50%;
|
||||
animation: shrink 1s infinite alternate;
|
||||
}
|
||||
|
||||
|
||||
@keyframes shrink {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
}
|
||||
100% {
|
||||
transform: scale(0.8);
|
||||
}
|
||||
}
|
||||
|
||||
.pointimg:hover {
|
||||
transform: scale(1.3);
|
||||
}
|
||||
|
||||
.ChangeShowBox {
|
||||
position: absolute;
|
||||
top: -20px;
|
||||
left: 50px;
|
||||
z-index: 1;
|
||||
margin-left: -20px;
|
||||
margin-top: -60px;
|
||||
width: 150px;
|
||||
height: 120px;
|
||||
border-radius: 10%;
|
||||
font-size: 12px;
|
||||
line-height: 1.2;
|
||||
transition: opacity 0.5s;
|
||||
background-size: 100%;
|
||||
} */
|
||||
Reference in New Issue
Block a user