实现人体模型可点击
简化需求:实现项目内嵌人体模型,实现点击不同部位弹出部位名称
一:优先3d,
方案:基于three.js,.gltf格式模型,vue3
缺点:合适且免费的3d模型找不到,因为项目对部位有要求。
注意:模型地址请使用绝对路径。
效果图:
代码:
<template>
<div ref="canvasContainer" class="canvas-container"></div>
</template>
<script setup>
import * as THREE from 'three'
import { onMounted, ref } from 'vue'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
const canvasContainer = ref(null)
onMounted(() => {
let scene, camera, renderer, model
let raycaster = new THREE.Raycaster()
let mouse = new THREE.Vector2()
let selectedPart = null
let isDragging = false
let previousMousePosition = { x: 0, y: 0 }
const init = () => {
// 创建场景
scene = new THREE.Scene()
scene.background = new THREE.Color(0xeeeeee)
// 创建相机
camera = new THREE.PerspectiveCamera(45, 500 / 500, 0.1, 1000)
camera.position.set(0, 1.6, 3) // 相机位置
// 创建渲染器,并设置大小为500px × 500px
renderer = new THREE.WebGLRenderer({ antialias: true })
renderer.setSize(500, 500)
canvasContainer.value.appendChild(renderer.domElement)
// 添加光源
const light = new THREE.DirectionalLight(0xffffff, 1)
light.position.set(5, 10, 7.5)
scene.add(light)
// 加载3D模型
const loader = new GLTFLoader()
loader.load('src/assets/models/b1_battle_droid/scene.gltf', (gltf) => {
model = gltf.scene
scene.add(model)
})
// 鼠标事件监听
window.addEventListener('mousemove', onMouseMove)
window.addEventListener('click', onClick)
window.addEventListener('mousedown', onMouseDown)
window.addEventListener('mouseup', onMouseUp)
window.addEventListener('mousemove', onMouseMoveRotation)
}
const onMouseMove = (event) => {
// 将鼠标位置标准化为Three.js坐标
const rect = renderer.domElement.getBoundingClientRect()
mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1
mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1
raycaster.setFromCamera(mouse, camera)
const intersects = raycaster.intersectObjects(scene.children, true)
if (intersects.length > 0) {
if (selectedPart) {
// 恢复之前选中的部件颜色
selectedPart.material.emissive.setHex(selectedPart.currentHex)
}
selectedPart = intersects[0].object
selectedPart.currentHex = selectedPart.material.emissive.getHex()
selectedPart.material.emissive.setHex(0xff0000) // 悬浮变色
} else if (selectedPart) {
// 鼠标离开时恢复颜色
selectedPart.material.emissive.setHex(selectedPart.currentHex)
selectedPart = null
}
}
const onClick = (event) => {
if (selectedPart) {
alert(`你点击了: ${selectedPart.name}`)
}
}
const onMouseDown = () => {
isDragging = true
}
const onMouseUp = () => {
isDragging = false
}
const onMouseMoveRotation = (event) => {
if (isDragging) {
const deltaMove = {
x: event.clientX - previousMousePosition.x,
y: event.clientY - previousMousePosition.y,
}
const rotationSpeed = 0.005
model.rotation.y += deltaMove.x * rotationSpeed
model.rotation.x += deltaMove.y * rotationSpeed
}
previousMousePosition = {
x: event.clientX,
y: event.clientY,
}
}
const animate = () => {
requestAnimationFrame(animate)
renderer.render(scene, camera)
}
init()
animate()
})
</script>
<style>
.canvas-container {
width: 500px;
height: 500px;
overflow: hidden;
margin: 0 auto;
/* 居中对齐 */
}
</style>
方案二:差强人意选2d
方案:基于canvas,.png格式图片,vue3
缺点:效果差一些,旋转没有了,身体部位区分做不到很细致。
注意:图片地址import引入,再引用。
效果图:
代码:
<template>
<div>
<canvas ref="canvas" @click="handleClick"></canvas>
</div>
</template>
<script setup>
import { onMounted, ref } from 'vue'
import imgsrc from '@/assets/models/person.png'
const canvas = ref(null)
const bodyParts = [
{ name: 'Head', x: 50, y: 30, width: 100, height: 100 },
{ name: 'Chest', x: 50, y: 150, width: 100, height: 100 },
{ name: 'Abdomen', x: 50, y: 270, width: 100, height: 100 },
{ name: 'Legs', x: 50, y: 390, width: 100, height: 100 },
]
onMounted(() => {
handleImage()
})
const handleImage = () => {
const ctx = canvas.value.getContext('2d')
canvas.value.width = 200 // 设置 canvas 的宽度
canvas.value.height = 500 // 设置 canvas 的高度
const img = new Image()
img.src = imgsrc // 使用 Vue 项目中的图片路径
img.onload = function () {
// 确保图片加载完成后绘制到 canvas 上
ctx.clearRect(0, 0, canvas.value.width, canvas.value.height) // 清空 canvas
ctx.drawImage(img, 0, 0, canvas.value.width, canvas.value.height) // 绘制图片
// 调试用:绘制点击区域的边框
bodyParts.forEach((part) => {
ctx.strokeStyle = 'red' // 用红色边框标记部位
ctx.strokeRect(part.x, part.y, part.width, part.height)
})
}
console.log('path is ' + img.src)
}
const handleClick = (event) => {
const rect = canvas.value.getBoundingClientRect()
const x = event.clientX - rect.left
const y = event.clientY - rect.top
const clickedPart = bodyParts.find((part) => {
return (
x >= part.x &&
x <= part.x + part.width &&
y >= part.y &&
y <= part.y + part.height
)
})
if (clickedPart) {
alert(`You clicked on: ${clickedPart.name}`)
}
}
</script>
<style scoped>
canvas {
border: 1px solid black;
}
</style>