vue3+3d-force-graph渲染glb模型
准备工作:
1.找好要渲染的glb/gltf模型
2.安装three.js
3.安装3d-force-graph
4.准备图谱数据
开始操作
1、声明一个div标签 通过ref获取此div元素
<div class="force_style" ref="graphRef"></div>
2、定义初始化图谱方法(initGraph )
将准备好的glb加载进来
const loadGLBModel = async (url: string) => {
if (modelCache.has(url)) return modelCache.get(url).clone()
const loader = new GLTFLoader()
return new Promise<THREE.Object3D>((resolve, reject) => {
loader.load(
url,
gltf => {
const model = gltf.scene
model.traverse(child => {
if (child instanceof THREE.Mesh) {
child.castShadow = true
child.receiveShadow = true
}
})
modelCache.set(url, model.clone())
resolve(model)
},
undefined,
reject
)
})
}
定义力导向图谱
graphInstance = ForceGraph3D()(graphRef.value)
加载图谱数据
graphInstance .graphData({
nodes: nodes,
links: links
})
将默认节点替换为模型
graphInstance .nodeThreeObject(({ model }) => {
if (!model) return new THREE.Mesh() // 默认节点 // 克隆缓存的模型 const modelClone = modelCache.get('/models/MaiRealhot.glb').clone() modelClone.scale.set(10, 10, 10) // 调整模型大小 return modelClone })
效果图

完整代码:
<template> <div class="force_style" ref="graphRef"></div> </template> <script setup lang="ts">
import { onMounted, ref, onUnmounted } from 'vue' import ForceGraph3D from '3d-force-graph' import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader' import * as THREE from 'three' import {nodes, links} from '@/assets/graphData/miserables.json' import SpriteText from "three-spritetext"; const graphRef = ref<HTMLElement | null>(null) let graphInstance: any = null const modelCache = new Map() // 模型缓存 // 加载 GLB 模型 const loadGLBModel = async (url: string) => { if (modelCache.has(url)) return modelCache.get(url).clone() const loader = new GLTFLoader() return new Promise<THREE.Object3D>((resolve, reject) => { loader.load( url, gltf => { const model = gltf.scene model.traverse(child => { if (child instanceof THREE.Mesh) { child.castShadow = true child.receiveShadow = true } }) modelCache.set(url, model.clone()) resolve(model) }, undefined, reject ) }) } // 初始化图表 const initGraph = async () => { if (!graphRef.value) return // 加载示例模型(替换为你的实际模型路径) const model = await loadGLBModel('/models/MaiRealhot.glb') graphInstance = ForceGraph3D()(graphRef.value) .graphData({ nodes: nodes, links: links }) .nodeThreeObject(({ model }) => { if (!model) return new THREE.Mesh() // 默认节点 // 克隆缓存的模型 const modelClone = modelCache.get('/models/MaiRealhot.glb').clone() modelClone.scale.set(10, 10, 10) // 调整模型大小 return modelClone }) .nodeColor(() => '#ffffff') .nodeLabel((node) => { return node.id }) .nodeOpacity(0.8) .linkColor(() => 'rgba(255,255,255,0.2)') .linkWidth(0.5) .onNodeClick(node => { handleNodeClick(node) }) .cameraPosition({ z: 1000 }) // 添加环境光 const ambientLight = new THREE.AmbientLight(0xffffff, 0.8) graphInstance.scene().add(ambientLight) // 添加点光源 const pointLight = new THREE.PointLight(0xffffff, 1, 1000) pointLight.position.set(500, 500, 500) graphInstance.scene().add(pointLight) } // 节点点击处理 const handleNodeClick = (node: any) => { const distance = 100 const distRatio = 1 + distance / Math.hypot(node.x, node.y, node.z) graphInstance.cameraPosition( { x: node.x * distRatio, y: node.y * distRatio, z: node.z * distRatio }, node, 3000 ) } onMounted(async () => { await initGraph() }) onUnmounted(() => { if (graphInstance) { graphInstance._destructor() modelCache.clear() } }) </script> <style scoped lang="scss">.force_style { width: 100%; height: 100vh; background: #1a1a1a; overflow: hidden; } </style>
浙公网安备 33010602011771号