当前位置: 首页 > article >正文

实现人体模型可点击

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

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

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


http://www.kler.cn/a/315621.html

相关文章:

  • 【C++】string类(附题)
  • Java 多线程(三)—— 死锁
  • Spring Boot实现文件上传与OSS集成:从基础到应用
  • Scala学习记录,case class,迭代器
  • 基于Python的网上银行综合管理系统
  • SpringCloud学习笔记
  • Kotlin 枚举和 when 表达式(六)
  • 关于Python sklearn CountVectorizer使用详解
  • 多模态论文串讲-学习笔记(上)
  • docker配置镜像加速器
  • 亚马逊IP关联揭秘:发生ip关联如何处理
  • 【BEV 视图变换】Ray-based(2): 代码复现+画图解释 基于深度估计、bev_pool
  • MoveIt控制机械臂的运动实现——机器人抓取系统基础系列(二)
  • 带你0到1之QT编程:十七、Http协议实战,实现一个简单服务器和一个客户端进行http协议通信
  • 校园美食发现:Spring Boot技术的美食社交平台
  • Flyway 版本迁移文件
  • 【Kubernetes】常见面试题汇总(三十二)
  • Docker 系列完结
  • SparkSQL和Spark常用语句
  • Go语言并发编程:从理论到实践
  • QT widgets 窗口缩放,自适应窗口大小进行布局
  • 【鸿蒙OH-v5.0源码分析之 Linux Kernel 部分】003 - vmlinux.lds 链接脚本文件源码分析
  • 第k个排列 - 华为OD统一考试(E卷)
  • 跟着问题学12——GRU详解
  • Lucene详解介绍以及底层原理说明
  • 如何在Linux Centos7系统中挂载群晖共享文件夹