实现人体模型可点击

news2024/11/15 14:10:49

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

一:优先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.coloradmin.cn/o/2156552.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

深度学习——D2(数据操作)

N维数组 创建数组 访问元素 一列: [ : , 1 ] 反向累积、正向累积&#xff08;自动求导&#xff09; 梯度 梯度&#xff08;Gradient&#xff09;是微积分中的一个重要概念&#xff0c;主要用于描述一个函数在某个区域内的变化情况。以下是对梯度的详细解释&#xff1a; 一…

树莓派pico上手

0 介绍 不同于作为单板计算机的树莓派5&#xff0c;树莓派 pico 是一款低成本、高性能的微控制器板&#xff0c;具有灵活的数字接口。主要功能包括&#xff1a; 英国树莓派公司设计的 RP2040 微控制器芯片双核 Arm Cortex M0 处理器&#xff0c;弹性的时钟频率高达 133 MHz26…

Qt笔记(十七)cmake编译Qt项目

Qt笔记&#xff08;十七&#xff09;cmake编译Qt项目 1. 文件内容与文件结构1.1.文件目录1.2. CMakeLists.txt内容1.3. main.cpp文件1.4. mouseevent.h1.5. mouseevent.cpp1.6. 生成Visual Studio项目后编译报错1.7. 界面显示中文乱码问题 1. 文件内容与文件结构 1.1.文件目录…

神奇的可变模板参数的应用(C++标准库双向链表 list 中的emplace函数实现)

我们先来看一个可以构造任意对象的函数&#xff1a; /// <summary> /// 可以构造任意对象的函数 /// </summary> /// <typeparam name"MyClass">要转换对象的类型</typeparam> /// <typeparam name"...MyClassConstructorParameterT…

传输层 II(TCP协议——协议的特点、报文段、连接管理)【★★★★】

&#xff08;★★&#xff09;代表非常重要的知识点&#xff0c;&#xff08;★&#xff09;代表重要的知识点。 一、TCP 协议的特点 TCP 是在不可靠的 IP 层之上实现的可靠的数据传输协议&#xff0c;它主要解决传输的可靠、有序、无丢失和不重复问题。TCP 是 TCP/IP 体系中非…

使用Stream实现事件流

文章目录 1 概念介绍2 使用方法3 示例代码 我们在上一章回中介绍了Flutter中的异步操作&#xff0c;本章回中将介绍Flutter中的事件流.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1 概念介绍 我们在上一章回中介绍了异步操作相关的内容&#xff0c;本章回中将介绍如何把…

施耐德EcoStruxure Machine SCADA Expert(EMSE)与SQL数据库连接(十五)

我习惯使用SQL Server 数据库与EMSE进行连接。 用的是sql 2017 关于数据库软件的安装教程 网上一大把。 1.新建数据库 打开数据库管理工具&#xff0c;新建数据库 2.新建表单 &#xff08;ps:这里先做一个小测试-----目的是验证与EMSE软件的链接是否顺畅。) 添加两个元素进去…

图神经网络的新篇章:通用、强大、可扩展的图变换器

人工智能咨询培训老师叶梓 转载标明出处 图变换器&#xff08;Graph Transformers, GTs&#xff09;因其在处理节点间全局依赖关系方面的能力而受到广泛关注。然而&#xff0c;现有的GTs模型在处理大规模图时面临着计算复杂度高、泛化能力有限等问题。为了解决这些问题&#x…

对比评测5款实用在线翻译工具,包括有道在线翻译

大家好&#xff0c;今天咱们来聊聊在线翻译工具。在这个信息爆炸的时代&#xff0c;语言不再是沟通的障碍&#xff0c;多亏了这些强大的翻译神器。今天&#xff0c;我将带大家比较五款热门的在线翻译工具&#xff0c;究竟谁更胜一筹呢&#xff1f;让我们一探究竟&#xff01; …

用友U8CRM relobjreportlist.php SQL注入漏洞复现

0x01 漏洞描述&#xff1a; 用友U8 CRM客户关系管理系统是一款专业的企业级CRM软件&#xff0c;旨在帮助企业高效管理客户关系、提升销售业绩和提供优质的客户服务。 用友 U8 CRM客户关系管理系统relobjreportlist.php 文件存在SQL注入漏洞&#xff0c;未经身份验证的攻击者通过…

Linux 一些快捷键使用操作技巧

ctrl c : 强制停止 如图仅输入tail命令时程序会卡住&#xff0c;这时就需要强制停止 ctrl d : 退出或者登出 history : 查看历史输入命令 &#xff01;命令 &#xff1a;自动执行上一次匹配前缀的命令 &#xff08;注意不要用这个命令执行太过久远的&#xff0c;容易执行错误…

字节数据转16进制对应十进制数

在数据处理中经常面临字节数据需要转换成不同位宽的十进制数据&#xff0c;尤其是在嵌入式处理中该现象特别常见&#xff0c;这里以转换为16位位宽的十进制为例&#xff0c;采用python校本进行数据转换&#xff0c;具体数据如下&#xff1a; 要将上面数据转换为双字节十进制数…

英语六级-学习

01 英语分值比例 02听力学习 听力练习&#xff0c;基础好选择标准VOA和BBC。基础差选择VOA慢速。 听力内容包括不受政治争议的内容&#xff0c;社会生活类(奇闻趣事、日常生活)、经济类(商务、职场相关)、环保类、互联网类---------根据各类主题快速找到录音材料中心点。 研…

文心一言 VS 讯飞星火 VS chatgpt (352)-- 算法导论24.1 3题

三、给定 G(V,E) 是一带权重且没有权重为负值的环路的有向图&#xff0c;对于所有结点 v∈V ,从源结点 s 到结点 v 之间的最短路径中&#xff0c;包含边的条数的最大值为 m 。&#xff08;这里&#xff0c;判断最短路径的根据是权重&#xff0c;不是边的条数。&#xff09;请对…

leetcode:最高乘法得分

用auto可以过 class Solution { public:long long maxScore(vector<int>& a, vector<int>& b) {int n b.size();vector<vector<long long>> memo(4,vector<long long>(b.size(), LLONG_MIN));auto dfs [&](auto&& dfs, i…

Java-Part 0

Advanced Java and Cutting-edge Applications Part 0: Course presentation Part 1 其实就是个括号匹配问题&#xff0c;Stack 经典问题&#xff0c;但是好久没用Java&#xff0c;有一点点生疏&#xff0c;感觉老师的版本要简洁的多 package tiei.ajp.test;import java.uti…

二叉树的层序遍历(含八道leetcode相关题目)

文章目录 二叉树层序遍历模板102. 二叉树的层序遍历107. 二叉树的层序遍历 II199. 二叉树的右视图637. 二叉树的层平均值515. 在每个树行中找最大值429. N 叉树的层序遍历116. 填充每个节点的下一个右侧节点指针117. 填充每个节点的下一个右侧节点指针 II 二叉树层序遍历模板 …

深度学习笔记(8)预训练模型

深度学习笔记&#xff08;8&#xff09;预训练模型 文章目录 深度学习笔记&#xff08;8&#xff09;预训练模型一、预训练模型构建一、微调模型&#xff0c;训练自己的数据1.导入数据集2.数据集处理方法3.完形填空训练 使用分词器将文本转换为模型的输入格式参数 return_tenso…

C++迭代器 iterator详解

目录 什么是迭代器 迭代器的类型 迭代器的用法 三种迭代器 范围for 什么是迭代器 它提供了一种访问容器&#xff08;如列表、集合等&#xff09;中元素的方法&#xff0c;而无需暴露容器的内部表示。迭代器使得程序员能够以统一的方式遍历不同的数据结构&#xff0c;而无需…

项目集成sharding-jdbc

目录 项目集成sharding-jdbc 1.业务分析 2.数据库构建 3.分库分表策略 项目配置默认数据源 一&#xff1a;导入sharding-jdbc依赖 二&#xff1a;在application文件中编写配置 三&#xff1a;注释掉主配置文件中配置的数据源 注意&#xff1a;这里添加了spring.main.allow…