Three.js学习项目--3D抗美援朝数据可视化

news2024/9/26 5:25:05

文章目录

          • 部分场景
          • 体验地址
          • 操作说明 视频
          • 我做了哪些(功能)
          • 局限
          • 源代码地址
          • 部分逻辑
            • 按需渲染
            • 模型加载
            • 动画控制器
            • 模型纹理条件切换
            • 模型加载同时请求部分纹理 生成进度条
            • 模型缩放小动画

部分场景

在这里插入图片描述
在这里插入图片描述在这里插入图片描述

体验地址

https://kmyc.hongbin.xyz/

操作说明 视频

操作说明

我做了哪些(功能)
  • draco解析glb模型 同时处理部分纹理请求 减轻一次加载纹理压力
  • 手动控制轨道控制器镜头动画
  • 多音频拼接 控制
  • 封装动画播放器 控制进度切换
  • 动画进度控制器 同步音频 模拟视频体验
  • useContext状态共享
  • 自定义多级右键菜单 模拟原生菜单体验
  • 空闲时间加载后续用到的模型
  • 模型纹理&位置动态切换
  • echart图表使用
  • 浏览器自适应单位vw vmax使用(大面积使用)
  • 兼容移动端手机浏览
  • 模型的销毁和动画加载
  • useRef暴露方法多方调用(大量使用)
  • css-in-js 方案实践 css引擎styled-component
  • 未完成请求避免产生影响
  • 未执行计数器清理 字幕播放中 镜头切换动画执行中切换 后续的功能等
  • 点击不同模型产生不同效果 点击的事件监听 鼠标hover的样式
局限
  • 性能拉垮 考虑到诸多原因 未采用按需渲染在低配机上帧率大概只有30帧左右
  • 模型做的不精细-第一次建模
  • 在手机或者高刷设备上动画模型播放渲染速度与60帧设备不一致
源代码地址

https://gitee.com/honbingitee/kmyc

部分逻辑
按需渲染
controls.addEventListener('change', () => {
	renderer.render(scene, camera);
});
模型加载

https://hongbin.blog.csdn.net/article/details/122594047

动画控制器

https://hongbin.blog.csdn.net/article/details/123662686

模型纹理条件切换

/**
 * @description: 加载相册模型 返回相关操作回调
 * @param {number} animationIndex 战役索引
 * @param {THREE.TextureLoader} textureLoader 纹理加载器
 * @return {XCBack} XCBack
 */
export async function loadXCModel(
  animationIndex: number,
  textureLoader: THREE.TextureLoader
): Promise<XCBack> {
  const gltf = await window.gltfLoader.loadAsync(xcModel);
  const [model] = gltf.scene.children;
  const multiple = 5;
  model.position.y = 1.8 * multiple;

  if (animationIndex === 1) {
    model.rotateY(-Math.PI / 2);
    model.rotateX(-Math.PI / 10);
    model.rotateZ(Math.PI / 10);
  }

  const setZero = (mash: typeof model) => {
    mash.scale.x = 0;
    mash.scale.y = 0;
    mash.scale.z = 0;
  };
  setZero(model);
  /**
   * 设置不同的纹理 -- 切换图片
   */
  const setMaterial = (mash: Object3D, url: string) => {
    const texture = textureLoader.load(url);
    texture.flipY = false;
    texture.encoding = 3001;
    //@ts-ignore
    const mater = mash.material.clone(); //不能共用一个material 以为 instance.material 指向的都是同一个对象

    mater.map = texture;
    //@ts-ignore
    mash.material = mater;
  };

  const pictures: XCBack["models"] = [];

  for (let i = 0; i < 4; i++) {
    const instance = model.clone();
    //hover tip
    instance.userData.type = ModelType["Picture"];
    instance.userData.desc = XCDesc[animationIndex]
      ? XCDesc[animationIndex][i]
      : "";

    setMaterial(
      instance,
      `${process.env.REACT_APP_URL}xc/${animationIndex}-${i}-y.jpg`
    );
    if (animationIndex === 1) {
      instance.position.x = (i - 1) * -5 * multiple;
      instance.position.z = -14 * multiple;
    } else if (animationIndex === 2) {
      instance.position.x = -10 * multiple;
      instance.position.z = (i - 2) * 5 * multiple;
    } else {
      instance.position.x = -10 * multiple;
      instance.position.z = (i - 1) * 5 * multiple;
    }
    pictures.push(instance);
  }

  let timer1: number;
  let count = 0;
  const range = 30;
  const show = () => {
    if (count < range) {
      pictures.forEach(item => {
        item.scale.y += multiple / range;
        item.scale.z += (multiple / range) * 1.8;
        item.scale.x += multiple / range / 10;
      });
      count++;
      timer1 = requestAnimationFrame(show);
    }
  };

  const hide = () => {
    pictures.forEach(setZero);
    count = 0;
    cancelAnimationFrame(timer1);
    requestAnimationFrame(() => {
      cancelAnimationFrame(timer1);
    });
  };

  let prevIndex = animationIndex;

  const toggle: XCBack["toggle"] = nextIndex => {
    pictures.forEach((item, index) => {
      /**
       * hover 显示图片介绍
       */
      item.userData.type = ModelType["Picture"];
      item.userData.desc = XCDesc[nextIndex][index];

      setMaterial(
        item,
        `${process.env.REACT_APP_URL}xc/${nextIndex}-${index}-y.jpg`
      );
      //旋转角度
      if (nextIndex === 1) {
        if (prevIndex !== 1) {
          item.rotateY(-Math.PI / 2);
          item.rotateX(-Math.PI / 10);
          item.rotateZ(Math.PI / 10);
        }
      } else {
        if (prevIndex === 1) {
          item.rotateY(Math.PI / 2);
          item.rotateX(Math.PI / 10);
          item.rotateZ(Math.PI / 10);
        }
      }
      //位置
      if (nextIndex === 1) {
        item.position.x = (index - 1) * -5 * multiple;
        item.position.z = -14 * multiple;
      } else if (nextIndex === 2) {
        item.position.x = -10 * multiple;
        item.position.z = (index - 2) * 5 * multiple;
      } else {
        item.position.x = -10 * multiple;
        item.position.z = (index - 1) * 5 * multiple;
      }
    });
    prevIndex = nextIndex;
  };

  return {
    show,
    hide,
    models: pictures,
    toggle,
  };
}
模型加载同时请求部分纹理 生成进度条
//加载10个纹理
const loadTexture = () => {
    const textureLoader = new TextureLoader();

    for (let i = 0; i < 10; i++) {
      const index = i.toString().padStart(2, "0");
      const url = `${process.env.REACT_APP_URL}q/${i}.jpg`;
      const texture = textureLoader.load(
        url,
        _ => {
          setProgress(timeCheck(5));
        },
        undefined,
        err => {
          console.error("load texture fail:", err);
          setProgress(timeCheck(5));
        }
      );
      texture.flipY = false;
      texture.encoding = sRGBEncoding;
      addTexture(index, texture);
    }
  };
  
//draco解析模型
const dracoLoader = () => {
    let prevModel = 0;
    const manager = new LoadingManager();
    manager.onProgress = (_, loaded, total) => {
      const progress = Math.floor((loaded / total) * 100);
      if (progress === 100) return setProgress(timeCheck(50 - prevModel));
      prevModel += progress / 4;
      setProgress(timeCheck(progress / 4));
    };
    //设置错误信息
    manager.onError = setIsLoadFail;
    //创建draco解析器
    const dracoLoader = new DRACOLoader(manager);
    dracoLoader.setDecoderConfig({ type: "js" });
    dracoLoader.setDecoderPath(process.env.REACT_APP_URL as string);
    // gltf 加载器
    const gltfLoader = new GLTFLoader(manager);
    gltfLoader.setDRACOLoader(dracoLoader);
    gltfLoader.load(mapModel, setMap);
    //不带LoadingManager的加载器 如果使用gltfLoader会触发事件改变progress状态造成内存泄漏
    const normalGltfLoader = new GLTFLoader();
    normalGltfLoader.setDRACOLoader(dracoLoader);
    window.gltfLoader = normalGltfLoader;
  };
  
/**
 * 进度增长检测
  * @param {number} increase 增长的数值
  * @param {number} prev state原本数值
  * @return {number} newValue
  */
 const timeCheck = (increase: number) => (prev: number) => {
   if (increase + prev < 100) return prev + increase;
   // >= 100 检测时间
   if (Date.now() - enterTime > maxLoadTime) return 100;
   //time < maxLoadTime
   timer = setTimeout(() => {
     setProgress(100);
   }, maxLoadTime - (Date.now() - enterTime));
   //显示跳过按钮
   setIsCanJump(true);
   return prev;
 };
模型缩放小动画
let timer: number;
let i = 0;
const r = () => {
 if (i < 15) {
   timer = requestAnimationFrame(r);
 }
 for (const item of iconScene.children) {
   item.scale.x += 0.03;
   item.scale.y += 0.03;
   item.scale.z += 0.03;
 }
 i++;
};
r();

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

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

相关文章

LeetCode——查询后矩阵的和

目录 1、题目 2、题目解读 3、代码 1、题目 2718. 查询后矩阵的和 - 力扣&#xff08;Leetcode&#xff09; 给你一个整数 n 和一个下标从 0 开始的 二维数组 queries &#xff0c;其中 queries[i] [typei, indexi, vali] 。 一开始&#xff0c;给你一个下标从 0 开始的…

数学建模常用模型(一):灰色预测法

数学建模常用模型&#xff08;一&#xff09;&#xff1a;灰色预测法 灰色预测法是一种用于处理少量数据、数据质量较差或者缺乏历史数据的预测方法。它适用于一些非线性、非平稳的系统&#xff0c;尤其在短期预测和趋势分析方面有着广泛的应用。灰色预测法作为一种强大的数学…

基于STM32+OneNet设计的物联网智慧路灯

一、前言 近年来,构筑智慧城市、推动城镇发展被国家列入重要工作范畴。发布的《超级智慧城市报告》显示,全球已启动或在建的智慧城市有1000多个,中国在建500个,远超排名第二的欧洲(90个)。从在建智慧城市的分布来看,我国已初步形成环渤海、长三角、珠三角、中西部四大智…

FreeRTOS 任务优先级 【杂记】

FreeRTOS任务优先级 FreeRTOS任务优先级&#xff1a;任务优先级数值越小&#xff0c;任务优先级越低。 1、 FreeRTOS 中任务的最高优先级是通过 FreeRTOSConfig.h 文件中的configMAX_PRIORITIES 进行配置的&#xff0c;用户实际可以使用的优先级范围是 0 到 configMAX_PRIORIT…

python 第七章 字典dict {}

系列文章目录 第一章 初识python 第二章 变量 第三章 基础语句 第四章 字符串str 第五章 列表list [] 第六章 元组tuple ( ) 文章目录 字典的应用场景创建字典的语法字典常见操作增改删查 字典的循环遍历遍历字典的key遍历字典的value遍历字典的元素遍历字典的键值对&#xff0…

【新款DVR、NVR、直播、录播机单芯片解决方案】

新款DVR、NVR、直播、录播机单芯片解决方案 一、 22AP80或SS522V100是入门级DVR解决方案&#xff0c;能做到4路1080p30fps编码 2路 1080p30fps解码 多路图像分析方法智能算法&#xff1b;可以平替Hi3520DV510 二、 22AP10或SS524V100&#xff0c;这是一款中端的DVR芯片&#…

java语言中方法的多态

文章目录 前言一、多态是什么&#xff1f;二、使用步骤 1.实操展示2.注意事项总结 前言 自然界中&#xff0c;生物是多种形态的&#xff0c;繁殖这一行为也是多样的&#xff0c;细菌是裂殖&#xff0c;禽类是卵生&#xff0c;哺乳动物是胎生......java语言中的一个创建的方法&a…

Nike登录的acw_sc__v2参数逆向详细思路分析(非常简单,建议入手)含AST解混淆代码

分析目录 前言一、分析三、总结四、番外1.AST解混淆 前言 最近周末闲着无事&#xff0c;看了一下Nike的登录&#xff0c;发现连环境都不用补acw_sc__v2这个参数&#xff0c;分享出来给大家趣味性娱乐一下 一、分析 打开F12抓包看看登录 老样子复制curl给抓到Postman里面去…

Qt多线程编程之线程池

QThreadPool与QRunnable 线程的创建及销毁需要与系统交互&#xff0c;会产生很大的开销。若需要频繁的创建线程建议使用线程池&#xff0c;有线程池维护一定数量的线程&#xff0c;当需要进行多线程运算时将运算函数传递给线程池即可。线程池会根据可用线程进行任务安排。 QT…

Android studio自动登录和记住密码的实现

Android studio自动登录和记住密码的实现 文章目录 Android studio自动登录和记住密码的实现前言一、效果二、设计思路三、知识点介绍1. SharedPreferenced2. checkButton就不介绍了 四、自动登录及记住密码实现总结与补充 前言 大家好&#xff0c;我是oy&#xff0c;今天介绍…

浅层神经网络

目录 1、神经网络表示 2、计算神经网络的输出 3、多个样本的向量化 4、激活函数 5、激活函数的导数 6、神经网络的梯度下降法 1、神经网络表示 输入层&#xff1a;有输入特征&#x1d465;1、&#x1d465;2、&#x1d465;3隐藏层&#xff1a;四个结点&#xff0c;表示你…

验证性实验 - 逻辑回归

练习2&#xff1a;逻辑回归 介绍 在本练习中&#xff0c;您将实现逻辑回归并将其应用于两个不同的数据集。还将通过将正则化加入训练算法&#xff0c;来提高算法的鲁棒性&#xff0c;并用更复杂的情形来测试模型算法。 在开始练习前&#xff0c;需要下载如下的文件进行数据上…

前端Vue非常简单实用商品分类展示组件 侧边商品分类组件

前端vue非常简单实用商品分类展示组件 侧边商品分类组件 &#xff0c; 下载完整代码请访问uni-app插件市场址:https://ext.dcloud.net.cn/plugin?id13084 效果图如下&#xff1a; #### 使用方法 使用方法 <!-- flist:第一级数组 slist&#xff1a;第二级数组 tlist&…

JS 介绍 Babel 的使用及 presets plugins 的概念

一、Babel 是什么 Bebal 可以帮助我们将新 JS 语法编译为可执行且兼容旧浏览器版本的一款编译工具。 举个例子&#xff0c;ES6&#xff08;编译前&#xff09;&#xff1a; const fn () > {};ES5&#xff08;编译后&#xff09;&#xff1a; var fn function() {}二、B…

NLP实战:使用Word2vec实现文本分类

目录 一、数据预处理 1、加载数据 2. 构建词典 3.生成数据批次和迭代器 二、模型构建 1.搭建模型 2.初始化模型 3.定义训练与评估函数 三、训练模型 1. 拆分数据集并运行模型 2. 测试指定数据 &#x1f368; 本文为[&#x1f517;365天深度学习训练营]内部限免文章&…

设计模式篇---单例模式

文章目录 概念结构与实现优缺点 概念 单例模式是结构最简单的设计模式&#xff0c;通过单例模式可以保证在整个系统中的一个类只有一个实例&#xff0c;从而节约系统资源。举个例子&#xff0c;比如windows电脑下的任务管理器只能打开一个&#xff0c;这个就是单例模式&#x…

【C语言进阶】程序员必备技能之文件操作

目录 &#x1f945;什么是文件&#xff1a; &#x1f3d1;程序文件&#xff1a;&#x1f3d1;数据文件&#xff1a; &#x1f3d1;文件名&#xff1a; &#x1f945;文件的打开和关闭&#xff1a;&#x1f3d1;文件指针&#xff1a; &#x1f3d1;fopen和fclose&#xff1a; &a…

genlogic GLG -CE 4.3 For Java/C#/C++ Crack

GLG CE工具包是一个极其灵活和强大的图形框架&#xff0c;用于构建显示实时数据的可视化界面&#xff0c;例如过程控制和监控的操作员显示、SCADA / HMI模拟和图表、 交通、遥测和网络监控显示&#xff0c;以及其他任务关键应用程序。 航电仪表板演示 该工具包包括 用于创建动…

面向对象三大特征

面向对象三大特征 众所周知&#xff0c;面向对象有三大特征 封装继承多态 封装继承多态&#xff0c;就好像武侠小说里的“金、木、水、火、土”一样&#xff0c;相生相克 封装 封装就像是武侠里的金钟罩铁布衫&#xff0c;把对象的数据和方法封装起来&#xff0c;对外只暴露…

C语言strstr函数的使用和模拟实现

strstr 函数原型&#xff1a; char *strstr( const char *string, const char *strCharSet );const char *string 要搜索的字符串const char *strCharSet 子串char *strstr 返回第一个出现字串的起始地址&#xff0c;方便函数链式访问 函数作用&#xff1a; 在 string 字符串…