使用React复刻ThreeJS官网示例——keyframes动画

最近在看three.js相关的东西,想着学习一下threejs给的examples。源码是用html结合js写的,恰好最近也在学习react,就用react框架学习一下。

本文参考的是threeJs给的第一个示例

 three.js examples (threejs.org)

 一、下载threeJS源码

通常我们只用通过npm引入threejs的包就可以使用threejs了。为什么这里需要下载源码呢?因为我们要复刻源码给的示例,相关的模型我们是没有的,需要使用源码里用到的模型及解析工具

GitHub - mrdoob/three.js: JavaScript 3D Library.

 从git上拉取代码后可以找到示例一的源码

 阅读源码可以发现,完成示例需要引入jsm/libs/draco/gltf/路径以及models/gltf/LittlestTokyo.glb模型。

拷贝threeJS的必要的模型和方法

为了方便后续学习,我们直接将这两个文件夹jsm和models拷贝到react项目中;注意路径最好是public下,public是默认的静态资源加载入口

 

 二、功能解析与改写

react搭建及threejs引入可以参考我的之前的博客,这里不多赘述

Three.js机器人与星系动态场景:实现3D渲染与交互式控制-CSDN博客

 引入必要信息

import { useEffect, useRef } from "react";
import * as THREE from "three";
import Stats from "three/examples/jsm/libs/stats.module.js";
import { GLTF, GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { RoomEnvironment } from "three/examples/jsm/environments/RoomEnvironment.js";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.js";

初始化Render渲染器/Scene场景/ camer相机/controls轨道控制器


// 初始化渲染器的函数
/**
 * 初始化 WebGL 渲染器
 * @returns {THREE.WebGLRenderer} 创建并配置好的渲染器实例
 */
// 初始化渲染
function initRender(): THREE.WebGLRenderer {
  // 创建一个WebGL渲染器
  const renderer = new THREE.WebGLRenderer({ antialias: true });
  // 根据设备像素比设置渲染器像素比
  renderer.setPixelRatio(window.devicePixelRatio);
  // 设置渲染器大小
  renderer.setSize(window.innerWidth, window.innerHeight);
  return renderer;
}

// 初始化场景的函数
/**
 * 初始化场景
 * @param {THREE.WebGLRenderer} renderer - 渲染器实例
 * @returns {THREE.Scene} 创建并配置好的场景实例
 */
function initScene(renderer: THREE.WebGLRenderer) {
  // 创建 PMREM 生成器
  const pmremGenerator = new THREE.PMREMGenerator(renderer);
  // 创建场景
  const scene = new THREE.Scene();
  // 设置场景背景
  scene.background = new THREE.Color(0xbfe3dd);
  // 设置场景环境
  scene.environment = pmremGenerator.fromScene(new RoomEnvironment(renderer), 0.04).texture;
  return scene;
}

// 初始化相机的函数
/**
 * 初始化相机
 * @param {number} x - 相机在 x 轴的位置
 * @param {number} y - 相机在 y 轴的位置
 * @param {number} z - 相机在 z 轴的位置
 * @returns {THREE.PerspectiveCamera} 创建并配置好位置的相机实例
 */
function initCamera(x: number, y: number, z: number) {
  const camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 100);
  camera.position.set(x, y, z);
  return camera;
}

// 初始化控制器的函数
/**
 * 初始化轨道控制器
 * @param {THREE.PerspectiveCamera} camera - 相机实例
 * @param {THREE.WebGLRenderer} renderer - 渲染器实例
 * @returns {OrbitControls} 创建并配置好的轨道控制器实例
 */
function initControls(camera: THREE.PerspectiveCamera, renderer: THREE.WebGLRenderer) {
  const controls = new OrbitControls(camera, renderer.domElement);
  controls.update();
  controls.enablePan = false;
  controls.enableDamping = true;
  return controls;
}

 组件核心方法Keyframes

采用react的函数式组件写法,首字母大写作为组件名,并导出

整个流程是初始化渲染器、scene场景、camera相机、controls轨道控制器;在场景中引入模型,并使用dracoLoader解压GLTFLoader引入的模型,开启模型上的动画,设置场景动画。

/**
 * Keyframes 组件函数
 */
function Keyframes() {
  const containerRef = useRef<HTMLDivElement>(null); // 创建用于引用 HTML 元素的 ref
  const clock = new THREE.Clock(); // 创建时钟实例
  const statsRef = useRef<Stats>(); // 创建用于引用统计信息的 ref
  const mixerRef = useRef<THREE.AnimationMixer>(); // 创建用于引用动画混合器的 ref

  const renderer = initRender(); // 初始化渲染器
  const scene = initScene(renderer); // 初始化场景
  const camera = initCamera(5, 2, 10); // 初始化相机
  const controls = initControls(camera, renderer); // 初始化控制器
  controls.target.set(0, 0.5, 0); // 设置控制器的目标

  const dracoLoader = new DRACOLoader(); // 创建 Draco 加载器
  dracoLoader.setDecoderPath("jsm/libs/draco/gltf/"); // 设置 Draco 解码器路径

  const loader = new GLTFLoader(); // 创建 GLTF 加载器
  loader.setDRACOLoader(dracoLoader); // 为 GLTF 加载器设置 Draco 加载器

  // 加载 GLTF 模型
  loader.load(
    "models/gltf/LittlestTokyo.glb",
    (gltf: GLTF) => {
      const model = gltf.scene; // 获取模型的场景
      model.position.set(1, 1, 0); // 设置模型的位置
      model.scale.set(0.01, 0.01, 0.01); // 设置模型的缩放
      scene.add(model); // 将模型添加到场景

      mixerRef.current = new THREE.AnimationMixer(model); // 创建动画混合器
      mixerRef.current.clipAction(gltf.animations[0]).play(); // 播放动画

      renderer.setAnimationLoop(animate); // 设置渲染循环
    },
    undefined,
    (e) => {
      console.error(e); // 处理加载错误
    },
  );

  // 渲染循环函数
  /**
   * 每一帧的更新和渲染逻辑
   */
  function animate() {
    const delta = clock.getDelta(); // 获取时间间隔
    mixerRef.current && mixerRef.current.update(delta); // 更新动画混合器
    controls.update(); // 更新控制器
    statsRef.current && statsRef.current.update(); // 更新统计信息
    renderer.render(scene, camera); // 渲染场景和相机
  }

  // 处理窗口大小改变的函数
  /**
   * 处理窗口大小改变时的相机和渲染器更新
   */
  function onWindowResize() {
    camera.aspect = window.innerWidth / window.innerHeight; // 更新相机的宽高比
    camera.updateProjectionMatrix(); // 更新相机的投影矩阵
    controls.update(); // 更新控制器
    renderer.setSize(window.innerWidth, window.innerHeight); // 更新渲染器的大小
  }

  // 使用 useEffect 钩子
  useEffect(() => {
    if (!containerRef.current) return;
    containerRef.current.appendChild(renderer.domElement); // 将渲染器的 DOM 元素添加到引用的元素中

    statsRef.current = new Stats(); // 创建统计信息实例

    containerRef.current.appendChild(statsRef.current.dom); // 将统计信息的 DOM 元素添加到引用的元素中

    window.addEventListener("resize", onWindowResize); // 添加窗口大小改变的监听事件
    return () => {
      window.removeEventListener("resize", onWindowResize); // 清除窗口大小改变的监听事件
      renderer.setAnimationLoop(null); // 清除渲染循环
    };
  }, []);

  return <div ref={containerRef}></div>; // 返回一个带有 ref 的 div 元素
}
export default Keyframes; // 导出 Keyframes 组件

通过<div ref={containerRef}></div> 创建一个dom元素,用于3D场景挂载

 模型加载与Draco解码

示例模型提供的是压缩后的模型,在页面加载时需要进行解压,必须使用dracoLoader方法,设置解码方法所在路径。在通过GLTFLoader导入。示例如下:


  const dracoLoader = new DRACOLoader(); // 创建 Draco 加载器
  dracoLoader.setDecoderPath("jsm/libs/draco/gltf/"); // 设置 Draco 解码器路径

  const loader = new GLTFLoader(); // 创建 GLTF 加载器
  loader.setDRACOLoader(dracoLoader); // 为 GLTF 加载器设置 Draco 加载器

  // 加载 GLTF 模型
  loader.load(
    "models/gltf/LittlestTokyo.glb",
    (gltf: GLTF) => {
      //处理模型
    },
    undefined,
    (e) => {
      console.error(e); // 处理加载错误
    },
  );

AnimationMixer 动画混合器 

AnimationMixer动画混合器是用于场景中特定对象的动画的播放器。当场景中的多个对象独立动画时,每个对象都可以使用同一个动画混合器。

  • 参数:rootObject 混合器播放的动画所属的对象。就是包含动画模型的场景对象。
  • 常用参数和属性:
  1. .time 全局的混合器时间。
  2. .clipAction(AnimationClip) 返回所传入的剪辑参数的AnimationAction对象。AnimationAction用来调度存储在AnimationClip中的动画。
  • AnimationClip 动画剪辑,是一个可重用的关键帧轨道集,它代表动画。
  1. .getRoot() 返回混合器的根对象。
  2. .update() 推进混合器时间并更新动画。在渲染函数中调用更新动画。

 在我们的示例中模型加载到场景时默认时没有动画的,也就是模型自身的动画比如小火车和风扇小人都是不动的。

 在模型加载的时候通过AnimationMixer开启模型动画

  // 加载 GLTF 模型
  loader.load(
    "models/gltf/LittlestTokyo.glb",
    (gltf: GLTF) => {
      const model = gltf.scene; // 获取模型的场景
      model.position.set(1, 1, 0); // 设置模型的位置
      model.scale.set(0.01, 0.01, 0.01); // 设置模型的缩放
      scene.add(model); // 将模型添加到场景

      mixerRef.current = new THREE.AnimationMixer(model); // 创建动画混合器
      mixerRef.current.clipAction(gltf.animations[0]).play(); // 播放动画

      renderer.setAnimationLoop(animate); // 设置渲染循环
    },
    undefined,
    (e) => {
      console.error(e); // 处理加载错误
    },
  );
setAnimationLoop动画循环  

在Three.js中,setAnimationLoop 方法是用来设置一个函数,这个函数会在每一帧被调用来进行渲染。这是必须的,因为在Three.js中,渲染循环不是自动开始的,你需要告诉渲染器何时以及如何进行渲染。

以下是为什么加载模型时必须使用 setAnimationLoop 的一些原因:

  1. 渲染控制:通过 setAnimationLoop,你可以控制渲染循环的开始和结束。如果你不设置它,即使模型加载完成,也不会自动开始渲染过程。

  2. 动画播放:在你的代码中,你使用了 AnimationMixer 来播放模型中的动画。这个动画需要在每一帧更新,以确保动画的连贯性和流畅性。setAnimationLoop 允许你在每一帧更新动画状态。

  3. 性能优化:使用 setAnimationLoop 可以让你在不需要渲染的时候停止渲染,比如在浏览器标签页不可见时,这样可以节省资源并提高性能。

  4. 逻辑更新:在 animate 函数中,你可以执行除了渲染之外的其他逻辑,比如更新动画、控制器和统计信息等。这些更新是渲染过程的一部分,需要在每一帧进行。

如果你不使用 setAnimationLoop,你需要自己手动创建一个循环来不断调用 renderer.render(scene, camera),并且确保在合适的时机更新动画和其他逻辑。这通常是通过 requestAnimationFrame 函数来实现的,但Three.js提供了 setAnimationLoop 来简化这一过程。

总之,setAnimationLoop 是Three.js中用来启动和维持渲染循环的关键方法,特别是在涉及到动画的情况下,它是必须的。

 可以看到模型自身的多个动画都动起来了

Stats.js帧检测工具 

不管是做游戏还是做普通网页,在这个时代基本都离不开动画。说到动画,第一个联想到的概念就是“帧”。这是用来衡量和描述动画是否流畅的一个单位。

示例程序的左上角有个工具窗口持续监测FPS数值 

FPS是“Frames Per Second”的缩写,意为“每秒帧数”。在视频游戏和计算机图形学中,FPS用来衡量显示设备每秒钟能够显示的静止图像(帧)的数量。这个数值越高,表示图像更新得越快,视觉效果就越流畅。

在游戏领域,高FPS通常意味着更平滑的游戏体验,尤其是在快速移动或复杂场景中。然而,FPS并不是唯一影响游戏体验的因素,图像质量、响应时间和系统稳定性也同样重要。

一般来说,人眼能够感知到的流畅动画大约需要30FPS以上,而60FPS或更高则被认为是高质量游戏体验的标准。不过,这也取决于个人的视觉感知能力和对流畅度的要求。

用法 

在使用 npm install three 下载的依赖包中已经包含了 Stats.js 了

可以这样引入到项目中

import Stats from "three/examples/jsm/libs/stats.module.js";

通过new Stats()方法创建一个stats实例 。默认showPanel是0,显示FPS面板。

 通过showPanel方法切换显示方式;可以根据dom改变stats面板的位置,使用示例如下

    const statsRef = useRef<Stats>(); // 创建用于引用统计信息的 ref

    statsRef.current = new Stats(); // 创建统计信息实例
    statsRef.current.showPanel(1);
    statsRef.current.dom.style.position = "absolute"; // 设置统计信息的 DOM 元素的位置
    statsRef.current.dom.style.top = "0px"; // 设置统计信息的 DOM 元素的位置
    statsRef.current.dom.style.left = "0px"; // 设置统计信息的 DOM 元素的位置

通过操作dom的方式将stats节点追加到3D场景中

    containerRef.current.appendChild(statsRef.current.dom); // 将统计信息的 DOM 元素添加到引用的元素中

 默认就显示在屏幕的左上角

当点击该面板时还可以切换监听的类型

 响应式窗口

页面加载时给了初始的renderer的宽高,但是如果用户使用过程中可视区域发生了变化renderer无法自动使用屏幕

 可以在useEffect里通过事件监听浏览器的resize事件,当浏览器尺寸变化时重新以最新的宽高设为renderer的尺寸信息

  // 处理窗口大小改变的函数
  /**
   * 处理窗口大小改变时的相机和渲染器更新
   */
  function onWindowResize() {
    camera.aspect = window.innerWidth / window.innerHeight; // 更新相机的宽高比
    camera.updateProjectionMatrix(); // 更新相机的投影矩阵
    controls.update(); // 更新控制器
    renderer.setSize(window.innerWidth, window.innerHeight); // 更新渲染器的大小
  }

  // 使用 useEffect 钩子
  useEffect(() => {
    if (!containerRef.current) return;
    containerRef.current.appendChild(renderer.domElement); // 将渲染器的 DOM 元素添加到引用的元素中

    statsRef.current = new Stats(); // 创建统计信息实例

    containerRef.current.appendChild(statsRef.current.dom); // 将统计信息的 DOM 元素添加到引用的元素中

    window.addEventListener("resize", onWindowResize); // 添加窗口大小改变的监听事件
    return () => {
      window.removeEventListener("resize", onWindowResize); // 清除窗口大小改变的监听事件
      renderer.setAnimationLoop(null); // 清除渲染循环
    };
  }, []);

完整代码 

import { useEffect, useRef } from "react";
import * as THREE from "three";
import Stats from "three/examples/jsm/libs/stats.module.js";
import { GLTF, GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { RoomEnvironment } from "three/examples/jsm/environments/RoomEnvironment.js";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.js";

// 初始化渲染器的函数
/**
 * 初始化 WebGL 渲染器
 * @returns {THREE.WebGLRenderer} 创建并配置好的渲染器实例
 */
// 初始化渲染
function initRender(): THREE.WebGLRenderer {
  // 创建一个WebGL渲染器
  const renderer = new THREE.WebGLRenderer({ antialias: true });
  // 根据设备像素比设置渲染器像素比
  renderer.setPixelRatio(window.devicePixelRatio);
  // 设置渲染器大小
  renderer.setSize(window.innerWidth, window.innerHeight);
  return renderer;
}

// 初始化场景的函数
/**
 * 初始化场景
 * @param {THREE.WebGLRenderer} renderer - 渲染器实例
 * @returns {THREE.Scene} 创建并配置好的场景实例
 */
function initScene(renderer: THREE.WebGLRenderer) {
  // 创建 PMREM 生成器
  const pmremGenerator = new THREE.PMREMGenerator(renderer);
  // 创建场景
  const scene = new THREE.Scene();
  // 设置场景背景
  scene.background = new THREE.Color(0xbfe3dd);
  // 设置场景环境
  scene.environment = pmremGenerator.fromScene(new RoomEnvironment(renderer), 0.04).texture;
  return scene;
}

// 初始化相机的函数
/**
 * 初始化相机
 * @param {number} x - 相机在 x 轴的位置
 * @param {number} y - 相机在 y 轴的位置
 * @param {number} z - 相机在 z 轴的位置
 * @returns {THREE.PerspectiveCamera} 创建并配置好位置的相机实例
 */
function initCamera(x: number, y: number, z: number) {
  const camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 100);
  camera.position.set(x, y, z);
  return camera;
}

// 初始化控制器的函数
/**
 * 初始化轨道控制器
 * @param {THREE.PerspectiveCamera} camera - 相机实例
 * @param {THREE.WebGLRenderer} renderer - 渲染器实例
 * @returns {OrbitControls} 创建并配置好的轨道控制器实例
 */
function initControls(camera: THREE.PerspectiveCamera, renderer: THREE.WebGLRenderer) {
  const controls = new OrbitControls(camera, renderer.domElement);
  controls.update();
  controls.enablePan = false;
  controls.enableDamping = true;
  return controls;
}

/**
 * Keyframes 组件函数
 */
function Keyframes() {
  const containerRef = useRef<HTMLDivElement>(null); // 创建用于引用 HTML 元素的 ref
  const clock = new THREE.Clock(); // 创建时钟实例
  const statsRef = useRef<Stats>(); // 创建用于引用统计信息的 ref
  const mixerRef = useRef<THREE.AnimationMixer>(); // 创建用于引用动画混合器的 ref

  const renderer = initRender(); // 初始化渲染器
  const scene = initScene(renderer); // 初始化场景
  const camera = initCamera(5, 2, 10); // 初始化相机
  const controls = initControls(camera, renderer); // 初始化控制器
  controls.target.set(0, 0.5, 0); // 设置控制器的目标

  const dracoLoader = new DRACOLoader(); // 创建 Draco 加载器
  dracoLoader.setDecoderPath("jsm/libs/draco/gltf/"); // 设置 Draco 解码器路径

  const loader = new GLTFLoader(); // 创建 GLTF 加载器
  loader.setDRACOLoader(dracoLoader); // 为 GLTF 加载器设置 Draco 加载器

  // 加载 GLTF 模型
  loader.load(
    "models/gltf/LittlestTokyo.glb",
    (gltf: GLTF) => {
      const model = gltf.scene; // 获取模型的场景
      model.position.set(1, 1, 0); // 设置模型的位置
      model.scale.set(0.01, 0.01, 0.01); // 设置模型的缩放
      scene.add(model); // 将模型添加到场景

      mixerRef.current = new THREE.AnimationMixer(model); // 创建动画混合器
      mixerRef.current.clipAction(gltf.animations[0]).play(); // 播放动画
      renderer.setAnimationLoop(animate); // 设置渲染循环
    },
    undefined,
    (e) => {
      console.error(e); // 处理加载错误
    },
  );

  // 渲染循环函数
  /**
   * 每一帧的更新和渲染逻辑
   */
  function animate() {
    const delta = clock.getDelta(); // 获取时间间隔
    mixerRef.current && mixerRef.current.update(delta); // 更新动画混合器
    controls.update(); // 更新控制器
    statsRef.current && statsRef.current.update(); // 更新统计信息
    renderer.render(scene, camera); // 渲染场景和相机
  }

  // 处理窗口大小改变的函数
  /**
   * 处理窗口大小改变时的相机和渲染器更新
   */
  function onWindowResize() {
    camera.aspect = window.innerWidth / window.innerHeight; // 更新相机的宽高比
    camera.updateProjectionMatrix(); // 更新相机的投影矩阵
    controls.update(); // 更新控制器
    renderer.setSize(window.innerWidth, window.innerHeight); // 更新渲染器的大小
  }

  // 使用 useEffect 钩子
  useEffect(() => {
    if (!containerRef.current) return;
    containerRef.current.appendChild(renderer.domElement); // 将渲染器的 DOM 元素添加到引用的元素中

    statsRef.current = new Stats(); // 创建统计信息实例

    containerRef.current.appendChild(statsRef.current.dom); // 将统计信息的 DOM 元素添加到引用的元素中

    window.addEventListener("resize", onWindowResize); // 添加窗口大小改变的监听事件
    return () => {
      window.removeEventListener("resize", onWindowResize); // 清除窗口大小改变的监听事件
      renderer.setAnimationLoop(null); // 清除渲染循环
    };
  }, []);

  return <div ref={containerRef}></div>; // 返回一个带有 ref 的 div 元素
}

export default Keyframes; // 导出 Keyframes 组件

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

视频监控业务平台LntonCVS国标视频综合管理平台功能及技术优势

随着安防行业的快速进步&#xff0c;传统的视频监控平台正在与先进的技术和互联网技术融合&#xff0c;包括5G通信、GIS、大数据、云计算、边缘计算、AI识别、智能分析和视频直播等。这些技术的整合形成了综合性视频监控管理平台&#xff0c;具备集中管理、多级联网共享、互联互…

中霖教育怎么样?税务师通过率高吗?

中霖教育怎么样?税务师通过率高吗? 我们在税务师考试培训方面有着不错的成绩&#xff0c;这都是老师与学员共同努力的结果。 采用小班教学模式&#xff0c;确保每位学员都能得到足够的关注和指导&#xff0c;在学习过程中针对学员的薄弱环节进行专项突破。 因为大部分学员…

Soul探索未来智能互动模式,人机交互重塑社交元宇宙体验

在当今快速发展的科技领域中,人机交互已成为一个备受关注的话题。随着人工智能和机器学习技术的不断进步,人们与计算机和智能设备之间的互动方式正在发生翻天覆地的变化。这种交互不止局限于键盘和鼠标,更涵盖了语音识别、手势控制、虚拟现实等多种形式。人机交互的创新不仅提高…

什么样的网工才是有前途的?

最近整个就业市场的变化&#xff0c;搞得人心惶惶。 可能很多朋友都在思考这样一个问题&#xff1a;现在做网工还有前途吗&#xff1f;什么样的网工才是有前途的&#xff1f;考HCIE认证还来得及吗&#xff1f; 作为网络工程师&#xff0c;该如何确保自己的职业发展方向正确&a…

Linux[高级管理]——Squid代理服务器的部署和应用(反向代理详解)

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f468;‍&#x1f4bb;Linux高级管理专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2024年6月24日11点11分 &#x1f004;️文章质量&#xff1a;95分 目录 ————前言———— Squid的几种模式…

咖啡消费旺季到来 为何想转让的库迪联营商却越来越多

文 | 智能相对论 作者 | 霖霖 去年还在朝“三年万店”计划狂奔的库迪&#xff0c;今年已出现明显“失速”。 早在今年2月&#xff0c;库迪就官宣其门店数已超过7000家&#xff0c;如今4个多月过去&#xff0c;据极海品牌监测数据显示&#xff0c;截至6月27日&#xff0c;其总…

数据库断言-数据库连接池

原因&#xff1a;现在的代码是单线程&#xff0c;如果遇到大并发的话就会崩溃&#xff0c;数据库查询就查不过来 措施&#xff1a;需要建立数据库连接池&#xff0c;可以设置连接池的数量 什么是大并发&#xff1a;很多客户端在idea写的程序和数据库建立连接 步骤&#xff1…

C++中的类型转换操作符:static_cast reinterpret_cast const_cast dynamic_cast

目录​​​​​​​ C语言中的类型转换 C中的类型转换 C中的类型转换操作符 static_cast reinterpret_cast const_cast volatile关键字 赋值兼容 dynamic_cast C语言中的类型转换 基本概念&#xff1a;赋值运算符左右两侧类型不同&#xff0c;或形参与实参类型不匹配…

数学知识——欧拉函数

数学知识&#xff08;二&#xff09; 20240628 求和N互质的个数公式 先分解N&#xff0c;再求个数fai n欧拉函数的证明&#xff1a;用容斥原理 不考 求质因子 p1, … , pk 1-N中与N互质的个数&#xff0c; 去掉质因子倍数 是pi的倍数的有N/pi个&#xff0c;但是会有既是p1也是…

计算机人说学校-南京大学-计算机方向

1. 专长、特点与特色 南京大学计算机专业在国内外享有很高的声誉&#xff0c;其专长、特点和特色主要体现在以下几个方面&#xff1a; 理论性强&#xff1a;重视数学、逻辑、数据结构、算法、电子设计、计算机体系结构和系统软件等方面的理论基础和专业技术基础。实践性强&am…

大厂10余年经验总结,用户研究领域入门标准书籍来了!

《用户研究方法&#xff1a;卓越产品和服务的用户研究技巧》一书近期出版&#xff0c;本书是用户研究领域入门标准书籍&#xff0c;是一本带你进入用户研究世界&#xff0c;通过研究用户让您工作更出色的书籍。 内容及特色 本书共 10 章&#xff0c;分为三篇。 第一篇&#xf…

Qt实现手动切换多种布局

引言 之前写了一个手动切换多个布局的程序&#xff0c;下面来记录一下。 程序运行效果如下&#xff1a; 示例 需求 通过点击程序界面上不同的布局按钮&#xff0c;使主工作区呈现出不同的页面布局&#xff0c;多个布局之间可以通过点击不同布局按钮切换。支持的最多的窗口…

鸿蒙应用更新跳转到应用市场

鸿蒙没有应用下载安装&#xff0c;只支持跳转到应用市场更新 gotoMarket(){try {const request: Want {parameters: {// 此处填入要加载的应用包名&#xff0c;例如&#xff1a; bundleName: "com.huawei.hmsapp.appgallery"bundleName: com.huawei.hmos.maps.app}}…

昇思25天学习打卡营第8天|模型训练

昇思25天学习打卡营第8天|模型训练 前言模型训练构建数据集定义神经网络模型定义超参、损失函数和优化器超参损失函数优化器 训练与评估 个人任务打卡&#xff08;读者请忽略&#xff09;个人理解与总结 前言 非常感谢华为昇思大模型平台和CSDN邀请体验昇思大模型&#xff01;从…

PFA滴定管带阀门耐酸碱本底值低

一、产品介绍 酸式滴定管为一细长的管状容器&#xff0c;一端具有活栓开关用来控制滴定的速度&#xff0c;其上具有刻度指示量度&#xff0c;是分析化学中常用的滴定仪器。可用于进行酸碱中和滴定试验等&#xff0c;量取对橡皮有侵蚀作用的液体。 我司生产的PFA酸式滴定管是用…

全球3DMAX插件界又更新了什么?

“3DMAX插件界”不这样称呼又叫他们什么呢&#xff1f;顾名思义就是开发3dmax插件的那个圈子。现在的3D类软件越来越多&#xff0c;但是&#xff0c;3dmax的地位仍然举足轻重。3dmax软件之所以受欢迎&#xff0c;不仅仅是因为自身的功能强大&#xff0c;还有其良好的可扩展性&a…

哪个麦克风音质好,麦克风哪种好,2024年热门家用麦克风推荐

​在这个信息爆炸的时代&#xff0c;网络直播和短视频成为了人们获取信息、娱乐和社交的重要方式。作为自媒体人&#xff0c;拥有一款优秀的领夹式无线麦克风是必不可少的。它不仅能够帮助你在各种环境中保持清晰的声音&#xff0c;还能提升你的作品质量和专业度。然而&#xf…

Parade接口芯片选型和应用,点击查看!

01 常见数据 / 媒体接口电路 接口电路是电子设备之间&#xff0c;电子设备与外围设备之间&#xff0c;电子设备内部部件之间起连接作用的逻辑电路&#xff0c;接口电路是设备处理器与外部设备进行信息交互的桥梁。 图1&#xff1a;常见高速数据/多媒体接口 1.1 USB接口 从最早…

思维模型:看透本质的思维框架,和它组合个个是王炸(非常详细)零基础入门到精通, 收藏这一篇就够了

为什么要从「为什么」开始&#xff1f; 如何想到又做到&#xff0c;提高行动力&#xff1f; 知行合一的途径&#xff1f;有用的工具&#xff1f; 剧透一下&#xff0c;读完本篇&#xff0c;你会收获一些王炸组合。 01 黄金思维圈 Why→How→What 黄金思维圈是西蒙斯涅克…

算法类学习笔记 ———— 车道线检测

文章目录 介绍基于传统计算机视觉地车道线检测基于道路特征的检测方法基于颜色特征的检测方法基于灰度特征的检测方法 基于彩色特征的检测方法基于纹理特征的检测方法基于多特征融合的检测方法 基于道路模型地检测方法直线模型曲线模型 基于深度学习的车道线检测LaneNet H-Net…