跳到主要内容

打标签

本章你可以学习到
  • 在三维空间中拾取点并映射到屏幕,进行 DOM 标签渲染。
  • 通过 project2d 在相机移动时刷新标签位置。

Vue 示例:屏幕标签

<template>
  <div style="width: 100vw; height: 100vh">
    <div ref="container" style="width: 100%; height: 100%; overflow: hidden; position: relative" />
    <div v-for="t in validTags" :key="t.id" :style="t.style">
      <div style="background: #333; color: #fff; padding: 6px 10px; border-radius: 4px; font-size: 12px">标签</div>
    </div>
    <div style="position: fixed; top: 16px; left: 16px"><button class="btn btn-primary" @click="enabled = !enabled">{{
      enabled ? '关闭拾取' : '开启拾取' }}</button></div>
  </div>
</template>

<script setup>
import { onMounted, onUnmounted, ref, computed } from 'vue'
import { Five, parseWork } from '@realsee/five'

const container = ref(null)
let five = null
const enabled = ref(false)
const tags = ref([]) // { id, position: THREE.Vector3 }
const tick = ref(0)
const workURL = 'https://vr-public.realsee-cdn.cn/release/static/image/release/five/work-sample/07bdc58f413bc5494f05c7cbb5cbdce4/work.json'

// 计算属性:安全地过滤和投影标签
const validTags = computed(() => {
  // 依赖 tick 值来触发重新计算
  tick.value
  return tags.value
    .filter(tag => tag && tag.position) // 确保 tag 和 position 都存在
    .map(tag => {
      const projected = project(tag.position)
      return projected ? { ...tag, projected, style: toStyle(projected) } : null
    })
    .filter(Boolean) // 移除无效的投影结果
})

onMounted(() => {
  five = new Five()
  if (container.value) five.appendTo(container.value)
  fetch(workURL).then(r => r.json()).then(json => five.load(parseWork(json)))
  const onResize = () => five.refresh()
  window.addEventListener('resize', onResize, false)
  const onCameraUpdate = () => { tick.value++ }
  five.on('cameraUpdate', onCameraUpdate)

  const onWantsTap = (raycaster) => {
    if (!enabled.value) return true
    const hit = five.model.intersectRaycaster(raycaster)
    if (hit && hit[0] && hit[0].point) {
      tags.value.push({ id: Date.now(), position: hit[0].point })
    }
    return false
  }
  five.on('wantsTapGesture', onWantsTap)

  onUnmounted(() => {
    window.removeEventListener('resize', onResize, false)
    five.off('cameraUpdate', onCameraUpdate)
    five.off('wantsTapGesture', onWantsTap)
    five.dispose()
  })
})

function project(pos) {
  if (!five || !pos) return null
  return five.project2d(pos, true)
}
function toStyle(v2) {
  if (!v2 || typeof v2.x !== 'number' || typeof v2.y !== 'number') {
    return { display: 'none' }
  }
  return { position: 'absolute', transform: 'translate(-50%, 0)', left: `${v2.x}px`, top: `${v2.y}px` }
}
</script>

要点:

  • 使用 wantsTapGesture 阻止默认点位移动;
  • 通过 five.model.intersectRaycaster 拾取三维点;
  • 使用 cameraUpdate 触发重渲染,project2d 将三维点映射为屏幕坐标。