自学内容网 自学内容网

实现人体模型可点击

简化需求:实现项目内嵌人体模型,实现点击不同部位弹出部位名称

一:优先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>

三:蹲蹲更好的解决方法。


原文地址:https://blog.csdn.net/qq_48161632/article/details/142379918

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!