3D个人简历网站 4.小岛

1.模型素材

在Sketchfab上下载狐狸岛模型,然后转换为素材资源asset,嫌麻烦直接在网盘链接下载素材,

  • Fox’s islands
  • https://sketchfab.com/3d-models/foxs-islands-163b68e09fcc47618450150be7785907
  • https://gltf.pmnd.rs/

素材夸克网盘:

链接:https://pan.quark.cn/s/f02d30f07286

提取码:Yn3k


在 vite.config.js 或 vite.config.ts 文件里添加 assetsInclude 配置项,让 Vite 把 .glb 文件当作静态资源处理。
vite.config.js

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'// https://vite.dev/config/
export default defineConfig({plugins: [react()],assetsInclude: ['**/*.glb']
})

2.小岛代码

src下创建文件夹models,models下创建Island.jsx

Island.jsx

/*** IMPORTANT: 将 glTF 模型加载到 Three.js 场景中是一项复杂的工作。* 在我们能够配置或动画化模型的网格之前,需要遍历模型网格的每个部分并单独保存。** 但幸运的是,有一个工具可以将 gltf 或 glb 文件转换为 jsx 组件。* 对于这个模型,请访问 https://gltf.pmnd.rs/ * 获取代码,然后添加其余内容。* 你不必从零开始编写所有代码*/// 从 @react-spring/three 库导入 a 组件,用于创建动画效果
import { a } from "@react-spring/three";
// 从 react 库导入 useEffect 和 useRef 钩子,useEffect 用于处理副作用,useRef 用于创建可变引用
import { useEffect, useRef } from "react";
// 从 @react-three/drei 库导入 useGLTF 钩子,用于加载 glTF 模型
import { useGLTF } from "@react-three/drei";
// 从 @react-three/fiber 库导入 useFrame 和 useThree 钩子,useFrame 用于在每一帧更新时执行代码,useThree 用于获取 Three.js 上下文
import { useFrame, useThree } from "@react-three/fiber";// 导入岛屿模型的 glb 文件
import islandScene from "../assets/3d/island.glb";/*** Island 组件,用于渲染 3D 岛屿模型,并处理模型的旋转交互和阶段设置。* @param {Object} props - 组件的属性对象* @param {boolean} props.isRotating - 指示岛屿是否正在旋转的状态* @param {Function} props.setIsRotating - 用于设置岛屿旋转状态的函数* @param {Function} props.setCurrentStage - 用于设置当前阶段的函数* @param {*} props.currentFocusPoint - 当前焦点点* @returns {JSX.Element} 渲染的 3D 岛屿模型元素*/
export function Island({isRotating,setIsRotating,setCurrentStage,currentFocusPoint,...props
}) {// 创建一个 ref 用于引用岛屿模型const islandRef = useRef();// 使用 useThree 钩子获取 Three.js 渲染器和视口信息const { gl, viewport } = useThree();// 使用 useGLTF 钩子加载岛屿模型,获取模型的节点和材质const { nodes, materials } = useGLTF(islandScene);// 创建一个 ref 用于存储上一次鼠标的 x 坐标const lastX = useRef(0);// 创建一个 ref 用于存储旋转速度const rotationSpeed = useRef(0);// 定义阻尼因子,用于控制旋转减速效果const dampingFactor = 0.95;// 处理指针(鼠标或触摸)按下事件const handlePointerDown = (event) => {// 阻止事件冒泡和默认行为event.stopPropagation();event.preventDefault();// 设置岛屿为旋转状态setIsRotating(true);// 根据事件类型(触摸或鼠标)获取当前指针的 x 坐标const clientX = event.touches ? event.touches[0].clientX : event.clientX;// 存储当前指针的 x 坐标,供后续计算使用lastX.current = clientX;};// 处理指针(鼠标或触摸)抬起事件const handlePointerUp = (event) => {// 阻止事件冒泡和默认行为event.stopPropagation();event.preventDefault();// 设置岛屿为停止旋转状态setIsRotating(false);};// 处理指针(鼠标或触摸)移动事件const handlePointerMove = (event) => {// 阻止事件冒泡和默认行为event.stopPropagation();event.preventDefault();if (isRotating) {// 如果岛屿正在旋转,根据事件类型(触摸或鼠标)获取当前指针的 x 坐标const clientX = event.touches ? event.touches[0].clientX : event.clientX;// 计算指针在水平方向上的移动距离,相对于视口宽度的比例const delta = (clientX - lastX.current) / viewport.width;// 根据指针移动距离更新岛屿的旋转角度islandRef.current.rotation.y += delta * 0.01 * Math.PI;// 更新上一次指针的 x 坐标lastX.current = clientX;// 更新旋转速度rotationSpeed.current = delta * 0.01 * Math.PI;}};// 处理键盘按下事件const handleKeyDown = (event) => {if (event.key === "ArrowLeft") {// 如果按下左箭头键,且岛屿未旋转,则设置为旋转状态if (!isRotating) setIsRotating(true);// 向左旋转岛屿islandRef.current.rotation.y += 0.005 * Math.PI;// 设置旋转速度rotationSpeed.current = 0.007;} else if (event.key === "ArrowRight") {// 如果按下右箭头键,且岛屿未旋转,则设置为旋转状态if (!isRotating) setIsRotating(true);// 向右旋转岛屿islandRef.current.rotation.y -= 0.005 * Math.PI;// 设置旋转速度rotationSpeed.current = -0.007;}};// 处理键盘抬起事件const handleKeyUp = (event) => {if (event.key === "ArrowLeft" || event.key === "ArrowRight") {// 如果松开左箭头键或右箭头键,设置岛屿为停止旋转状态setIsRotating(false);}};// 处理触摸开始事件,用于移动设备const handleTouchStart = (e) => {// 阻止事件冒泡和默认行为e.stopPropagation();e.preventDefault();// 设置岛屿为旋转状态setIsRotating(true);// 获取触摸点的 x 坐标const clientX = e.touches ? e.touches[0].clientX : e.clientX;// 存储当前触摸点的 x 坐标lastX.current = clientX;}// 处理触摸结束事件,用于移动设备const handleTouchEnd = (e) => {// 阻止事件冒泡和默认行为e.stopPropagation();e.preventDefault();// 设置岛屿为停止旋转状态setIsRotating(false);}// 处理触摸移动事件,用于移动设备const handleTouchMove = (e) => {// 阻止事件冒泡和默认行为e.stopPropagation();e.preventDefault();if (isRotating) {// 如果岛屿正在旋转,获取触摸点的 x 坐标const clientX = e.touches ? e.touches[0].clientX : e.clientX;// 计算触摸点在水平方向上的移动距离,相对于视口宽度的比例const delta = (clientX - lastX.current) / viewport.width;// 根据触摸移动距离更新岛屿的旋转角度islandRef.current.rotation.y += delta * 0.01 * Math.PI;// 更新上一次触摸点的 x 坐标lastX.current = clientX;// 更新旋转速度rotationSpeed.current = delta * 0.01 * Math.PI;}}// 使用 useEffect 钩子添加和移除事件监听器useEffect(() => {// 获取 Three.js 渲染器的画布元素const canvas = gl.domElement;// 添加指针按下、抬起、移动事件监听器canvas.addEventListener("pointerdown", handlePointerDown);canvas.addEventListener("pointerup", handlePointerUp);canvas.addEventListener("pointermove", handlePointerMove);// 添加键盘按下、抬起事件监听器window.addEventListener("keydown", handleKeyDown);window.addEventListener("keyup", handleKeyUp);// 添加触摸开始、结束、移动事件监听器canvas.addEventListener("touchstart", handleTouchStart);canvas.addEventListener("touchend", handleTouchEnd);canvas.addEventListener("touchmove", handleTouchMove);// 组件卸载时移除事件监听器,避免内存泄漏return () => {canvas.removeEventListener("pointerdown", handlePointerDown);canvas.removeEventListener("pointerup", handlePointerUp);canvas.removeEventListener("pointermove", handlePointerMove);window.removeEventListener("keydown", handleKeyDown);window.removeEventListener("keyup", handleKeyUp);canvas.removeEventListener("touchstart", handleTouchStart);canvas.removeEventListener("touchend", handleTouchEnd);canvas.removeEventListener("touchmove", handleTouchMove);};}, [gl, handlePointerDown, handlePointerUp, handlePointerMove]);// 使用 useFrame 钩子在每一帧更新时执行代码useFrame(() => {// 如果岛屿未旋转,应用阻尼效果使旋转逐渐减速if (!isRotating) {// 应用阻尼因子,降低旋转速度rotationSpeed.current *= dampingFactor;// 当旋转速度非常小时,停止旋转if (Math.abs(rotationSpeed.current) < 0.001) {rotationSpeed.current = 0;}// 根据旋转速度更新岛屿的旋转角度islandRef.current.rotation.y += rotationSpeed.current;} else {// 当岛屿正在旋转时,根据岛屿的朝向确定当前阶段const rotation = islandRef.current.rotation.y;/*** 对旋转值进行归一化处理,确保其保持在 [0, 2 * Math.PI] 范围内。* 目的是保证旋转值在特定范围内,避免出现非常大或负的旋转值导致的潜在问题。* 以下是这段代码的分步解释:* 1. rotation % (2 * Math.PI) 计算旋转值除以 2 * Math.PI 的余数。*    这实际上会在旋转值达到一整圈(360 度)时将其环绕,使其保持在 0 到 2 * Math.PI 的范围内。* 2. (rotation % (2 * Math.PI)) + 2 * Math.PI 将步骤 1 的结果加上 2 * Math.PI。*    这样做是为了确保即使在步骤 1 的取模运算后结果为负,该值仍然为正且在 0 到 2 * Math.PI 的范围内。* 3. 最后,((rotation % (2 * Math.PI)) + 2 * Math.PI) % (2 * Math.PI) 对步骤 2 得到的值再次应用取模运算。*    这一步保证了该值始终保持在 0 到 2 * Math.PI 的范围内,这在弧度制中相当于一整圈。*/const normalizedRotation =((rotation % (2 * Math.PI)) + 2 * Math.PI) % (2 * Math.PI);// 根据岛屿的朝向设置当前阶段switch (true) {case normalizedRotation >= 5.45 && normalizedRotation <= 5.85:setCurrentStage(4);break;case normalizedRotation >= 0.85 && normalizedRotation <= 1.3:setCurrentStage(3);break;case normalizedRotation >= 2.4 && normalizedRotation <= 2.6:setCurrentStage(2);break;case normalizedRotation >= 4.25 && normalizedRotation <= 4.75:setCurrentStage(1);break;default:setCurrentStage(null);}}});return (// {岛屿 3D 模型来源: https://sketchfab.com/3d-models/foxs-islands-163b68e09fcc47618450150be7785907}// 使用 a.group 组件包裹岛屿模型,支持动画效果<a.group ref={islandRef} {...props}><meshgeometry={nodes.polySurface944_tree_body_0.geometry}material={materials.PaletteMaterial001}/><meshgeometry={nodes.polySurface945_tree1_0.geometry}material={materials.PaletteMaterial001}/><meshgeometry={nodes.polySurface946_tree2_0.geometry}material={materials.PaletteMaterial001}/><meshgeometry={nodes.polySurface947_tree1_0.geometry}material={materials.PaletteMaterial001}/><meshgeometry={nodes.polySurface948_tree_body_0.geometry}material={materials.PaletteMaterial001}/><meshgeometry={nodes.polySurface949_tree_body_0.geometry}material={materials.PaletteMaterial001}/><meshgeometry={nodes.pCube11_rocks1_0.geometry}material={materials.PaletteMaterial001}/></a.group>);
}// 导出 Island 组件作为默认导出,方便其他文件引入使用
export default Island

3.主页代码

Home.jsx

// 导入 React 库和 Suspense 组件,Suspense 用于处理异步组件加载
// 当异步组件还未加载完成时,可显示一个 fallback 组件
import React, { Suspense } from 'react'
// 从 @react-three/fiber 库中导入 Canvas 组件,用于创建 Three.js 渲染上下文,
// 借助该组件能在 React 应用里渲染 3D 场景
import { Canvas } from '@react-three/fiber'
// 从 ../components/Loader 路径导入 Loader 组件,该组件会在异步加载时显示加载状态
import Loader from '../components/Loader'
// 从 ../models/Island 路径导入 Island 组件,此组件用于渲染 3D 岛屿模型
import { Island } from "../models/Island"// <div className='absolute top-28 left-0 right-0 z-10 flex items-center justify-center'>
//   弹出窗口
// </div>/*** Home 组件,作为应用的主页组件。* 该组件会依据屏幕尺寸对 Island 组件的缩放、位置和旋转进行调整,* 并且在 Canvas 中渲染 Island 组件,同时处理异步加载状态。* @returns {JSX.Element} 渲染后的 JSX 元素*/
const Home = () => {/*** 根据屏幕尺寸调整 Island 组件的缩放、位置和旋转。* @returns {Array} 包含屏幕缩放比例、位置和旋转值的数组*/const adjustIslandForScreenSize = () => {// 初始化屏幕缩放比例,初始值设为 nulllet screenScale = null// 初始化 Island 组件的位置,默认值为 [0, -6.5, -43]let screenPosition = [0, -6.5, -43]// 初始化 Island 组件的旋转值,默认值为 [0.1, 4.7, 0]let rotation = [0.1, 4.7, 0]// 判断当前窗口宽度是否小于 768pxif (window.innerWidth < 768) {// 若窗口宽度小于 768px,将屏幕缩放比例设置为 [0.9, 0.9, 0.9]screenScale = [0.9, 0.9, 0.9];} else {// 若窗口宽度大于等于 768px,将屏幕缩放比例设置为 [1, 1, 1]screenScale = [1, 1, 1];}// 返回包含屏幕缩放比例、位置和旋转值的数组return [screenScale, screenPosition, rotation];}// 调用 adjustIslandForScreenSize 函数,获取调整后的岛屿缩放、位置和旋转参数const [islandScale, islandPosition, islandRotation] = adjustIslandForScreenSize();return (// 创建一个 section 元素,宽度和高度占满整个屏幕,且采用相对定位<section className='w-full h-screen relative'>{/* 创建 Three.js 渲染画布,宽度和高度占满整个屏幕,背景透明,并设置相机的近裁剪面和远裁剪面 */}<CanvasclassName='w-full h-screen bg-transparent'camera={{ near:0.1, far:1000 }}>{/* 使用 Suspense 组件处理异步加载,当 Island 组件未加载完成时,显示 Loader 组件 */}<Suspense fallback={<Loader/>}>{/* 添加定向光,为场景提供有方向的光照 */}<directionalLight/>{/* 添加环境光,为场景提供全局均匀的光照 */}<ambientLight />{/* 添加点光源,从一个点向四周发射光线 */}<pointLight />{/* 添加聚光灯,发射出类似圆锥形的光线 */}<spotLight />{/* 添加半球光,模拟天空和地面的光照效果 */}<hemisphereLight />{/* 渲染 Island 组件,设置其位置、缩放和旋转属性 */}<Islandposition={islandPosition}scale={islandScale}rotation={islandRotation}/></Suspense></Canvas></section>)
}// 导出 Home 组件,供其他文件引入使用
export default Home

4.安装依赖运行

npm install @react-spring/three
npm run dev

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/83713.shtml

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

相关文章

智能开发工具PhpStorm v2025.1——增强AI辅助编码功能

PhpStorm是一个轻量级且便捷的PHP IDE&#xff0c;其旨在提高用户效率&#xff0c;可深刻理解用户的编码&#xff0c;提供智能代码补全&#xff0c;快速导航以及即时错误检查。可随时帮助用户对其编码进行调整&#xff0c;运行单元测试或者提供可视化debug功能。 立即获取PhpS…

Spark 的运行模式(--master) 和 部署方式(--deploy-mode)

Spark 的 运行模式&#xff08;--master&#xff09; 和 部署方式&#xff08;--deploy-mode&#xff09;&#xff0c;两者的核心区别在于 资源调度范围 和 Driver 进程的位置。 一、核心概念对比 维度--master&#xff08;运行模式&#xff09;--deploy-mode&#xff08;部署…

sqli—labs第八关——布尔盲注

一&#xff1a;确定注入类型 按照我们之前的步骤来 输入 ?id1 and 11-- ?id1 and 12-- 界面正常 第二行界面异常空白 所以注入类型为单引号闭合型 二&#xff1a; 布尔盲注 1.判断是否使用条件 &#xff08;1&#xff09;&#xff1a;存在注入但不会直接显示查询结果 …

ARP 原理总结

&#x1f310; 一、ARP 原理总结 ARP&#xff08;Address Resolution Protocol&#xff09;是用于通过 IP 地址解析 MAC 地址的协议&#xff0c;工作在 链路层 与 网络层之间&#xff08;OSI 模型的第三层与第二层之间&#xff09;。 &#x1f501; ARP通信过程&#xff1a; …

SpringCloud——EureKa

目录 1.前言 1.微服务拆分及远程调用 3.EureKa注册中心 远程调用的问题 eureka原理 搭建EureKaServer 服务注册 服务发现 1.前言 分布式架构&#xff1a;根据业务功能对系统进行拆分&#xff0c;每个业务模块作为独立项目开发&#xff0c;称为服务。 优点&#xff1a; 降…

机顶盒刷机笔记

疑难杂症解决 hitool线刷网口不通tftp超时--》关闭防火墙cm201-2卡刷所有包提示失败abort install--》找个卡刷包只刷fastboot分区再卡刷就能通过了&#xff08;cm201救砖包 (M8273版子&#xff09;&#xff09; 刷机工具 海兔烧录工具HiTool-STB-5.3.12工具&#xff0c;需要…

Linux动静态库制作与原理

什么是库 库是写好的现有的&#xff0c;成熟的&#xff0c;可以复用的代码。现实中每个程序都要依赖很多基础的底层库&#xff0c;不可能每个人的代码都从零开始&#xff0c;因此库的存在意义非同寻常。 本质上来说库是一种可执行代码的二进制形式&#xff0c;可以被操作系统…

如何通过小智AI制作会说话的机器人玩具?

一、硬件准备与组装 1. 核心硬件选择 主控芯片&#xff1a;选择支持无线网络连接、音频处理和可编程接口的嵌入式开发板 音频模块&#xff1a;配备拾音麦克风与小型扬声器&#xff0c;确保语音输入/输出功能 显示模块&#xff1a;选择适配的交互显示屏用于可视化反馈 扩展模…

如何控制邮件发送频率避免打扰用户

一、用户行为 监测用户与邮件的互动数据&#xff0c;如打开率、点击率下滑或退订申请增多&#xff0c;可能是发送频率过高的警示信号。利用邮件营销平台的分析工具&#xff0c;识别这些指标的变动趋势&#xff0c;为调整提供依据。 二、行业特性与受众差异 不同行业用户对邮…

定积分的“偶倍奇零”性质及其使用条件

定积分的“偶倍奇零”性质是针对对称区间上的奇偶函数积分的重要简化方法。以下是其核心内容和应用要点&#xff1a; ​一、基本性质 ​偶函数&#xff08;偶倍&#xff09;​ 若 f(x) 在 [−a,a] 上为偶函数&#xff08;即 f(−x)f(x)&#xff09;&#xff0c;则&#xff1a; …

如何在 Windows 11 或 10 上安装 Fliqlo 时钟屏保

了解如何在 Windows 11 或 10 上安装 Fliqlo,为您的 PC 或笔记本电脑屏幕添加一个翻转时钟屏保以显示时间。 Fliqlo 是一款适用于 Windows 和 macOS 平台的免费时钟屏保。它也适用于移动设备,但仅限于 iPhone 和 iPad。Fliqlo 的主要功能是在用户不活动时在 PC 或笔记本电脑…

【C/C++】C++并发编程:std::async与std::thread深度对比

文章目录 C并发编程&#xff1a;std::async与std::thread深度对比1 核心设计目的以及区别2 详细对比分析3 代码对比示例4 适用场景建议5 总结 C并发编程&#xff1a;std::async与std::thread深度对比 在 C 中&#xff0c;std::async 和 std::thread 都是用于并发编程的工具&am…

Axure疑难杂症:垂直菜单展开与收回(4大核心问题与专家级解决方案)

亲爱的小伙伴,在您浏览之前,烦请关注一下,在此深表感谢!如有帮助请订阅专栏! Axure产品经理精品视频课已登录CSDN可点击学习https://edu.csdn.net/course/detail/40420 课程主题:垂直菜单展开与收回 主要内容:超长菜单实现、展开与收回bug解释、Axure9版本限制等问题解…

ASIC和FPGA,到底应该选择哪个?

ASIC和FPGA各有优缺点。 ASIC针对特定需求&#xff0c;具有高性能、低功耗和低成本&#xff08;在大规模量产时&#xff09;&#xff1b;但设计周期长、成本高、风险大。FPGA则适合快速原型验证和中小批量应用&#xff0c;开发周期短&#xff0c;灵活性高&#xff0c;适合初创企…

DAY 30 模块和库的导入

知识点回顾&#xff1a; 1.导入官方库的三种手段 2.导入自定义库/模块的方式 3.导入库/模块的核心逻辑&#xff1a;找到根目录&#xff08;python解释器的目录和终端的目录不一致&#xff09; 作业&#xff1a;自己新建几个不同路径文件尝试下如何导入 import math # 导入…

MyBatis:动态SQL

文章目录 动态SQLif标签trim标签where标签set标签foreach标签include标签和sql标签 Mybatis动态SQL的官方文档&#xff1a; https://mybatis.net.cn/dynamic-sql.html 动态SQL 动态SQL是 MyBatis的强大特性之一,如果是使用JDBC根据不同条件拼接sql很麻烦&#xff0c;例如拼接…

Java - Junit框架

单元测试&#xff1a;针对最小的功能单元(方法)&#xff0c;编写测试代码对该功能进行正确性测试。 Junit&#xff1a;Java语言实现的单元测试框架&#xff0c;很多开发工具已经集成了Junit框架&#xff0c;如IDEA。 优点 编写的测试代码很灵活&#xff0c;可以指某个测试方法…

学生成绩管理系统Java实战(Spring Boot+MyBatis Plus)

文章目录 一、系统需求分析&#xff08;避坑指南&#xff09;二、技术选型&#xff08;2024新版&#xff09;三、数据库设计&#xff08;三大核心表&#xff09;1. 学生表&#xff08;student&#xff09;2. 课程表&#xff08;course&#xff09;3. 成绩表&#xff08;score&a…

MySQL安装实战指南:Mac、Windows与Docker全平台详解

MySQL作为世界上最流行的开源关系型数据库&#xff0c;是每位开发者必须掌握的基础技能。本指南将手把手带你完成三大平台的MySQL安装&#xff0c;从下载到配置&#xff0c;每个步骤都配有详细说明和截图&#xff0c;特别适合新手学习。 一、Mac系统安装MySQL 1.1 通过Homebre…

多模态大语言模型arxiv论文略读(七十九)

AIM: Let Any Multi-modal Large Language Models Embrace Efficient In-Context Learning ➡️ 论文标题&#xff1a;AIM: Let Any Multi-modal Large Language Models Embrace Efficient In-Context Learning ➡️ 论文作者&#xff1a;Jun Gao, Qian Qiao, Ziqiang Cao, Zi…