Three.js + AI:AI 算法生成 3D 萤火虫飞舞效果~

news2024/11/23 2:08:12

AI 驱动 3D 动画

大家好,我是石小石!随着 Web 技术的发展,Three.js 成为构建 3D 图形和动画的主流工具。与此同时,人工智能(AI)在图像处理、动作生成等领域表现出强大能力。将 AI 与 Three.js 结合,可以创造更加智能、动态和个性化的 3D 动画体验。

在传统的 3D 动画中,我们需要通过手动编码或关键帧技术定义每一帧动画。然而,AI 可以帮助我们:

  • 动作生成:通过神经网络生成复杂的人体动作(如走路、跳跃)。
  • 路径优化:使用 AI 规划摄像机路径或物体运动轨迹。
  • 实时反应:通过用户输入或传感器数据,动态生成响应动画。
  • 纹理与场景生成:利用生成对抗网络(GAN)实时生成纹理或场景内容。

本文将带你探讨如何使用 AI 算法驱动 3D 动画的生成,实现一个简单的案例:萤火虫飞舞效果。

前置知识及目标

核心目标

本文将借助Threejs生成一组粒子系统,用于模拟场景中的萤火虫,并借助Threejs的效果合成器模拟萤火虫的发光效果。最后,我们将通过 AI 算法(如噪声函数或简单的强化学习策略),生成一组动态轨迹,控制萤火虫在 3D 空间中的运动,呈现自然且流畅的效果。

必备知识

本文核心是探讨AI算法在Threejs中的应用,不会着重介绍Threejs的使用。如果你对Threejs不熟悉或者有遗忘,可以参考我的其他文章复习threejs的基础知识。

一个案例带你从零入门Three.js,深度好文!

threejs专栏

技术方案

项目及场景搭建

我们采用vite的vue脚手架搭建此项目,此处为语雀卡片,点击链接查看

先安装threejs的依赖,然后搭建最基本的Threejs场景

npm i three --save
// App.vue
<template>
    <div ref="threeContainer" class="three-container"></div>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import * as THREE from 'three';

// 用于渲染的 DOM 容器
const threeContainer = ref(null); 

let scene, camera, renderer;

// 初始化 Three.js 场景
const initScene = () => {
    // 创建场景
    scene = new THREE.Scene();

    // 创建摄像机
    camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 20, 1000);
    camera.position.set(0, 150, 500); // 设置相机位置
    camera.lookAt(0, 0, 0); // 看向原点

    // 创建渲染器
    renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.setPixelRatio(window.devicePixelRatio);
    threeContainer.value.appendChild(renderer.domElement);
};


// 生命周期钩子
onMounted(() => {
    initScene();
});

</script>

<style>
.three-container {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    top: 0;
    overflow: hidden;
}
</style>

现在,我们就得到了一个没有任何内容的黑色场景。

创建粒子系统(萤火虫)

我们可以将萤火虫理解为一个个位置不一样的自发光球体,那么萤火虫的实现就非常简单。

let scene, camera, renderer, particles;

const initScene = () => {
  // ...
  
  // 创建粒子系统(球体粒子-萤火虫)
  particles = new THREE.Group();
  const particlesCount = 200;
  
  for (let i = 0; i < particlesCount; i++) {
      const geometry = new THREE.SphereGeometry(5, 5, 5); // 创建球体
      const material = new THREE.MeshStandardMaterial({
          emissive: new THREE.Color("#CCFF00"), // 自发光颜色
          emissiveIntensity: 1.5, // 自发光强度
      });
      const particle = new THREE.Mesh(geometry, material);

      // 设置萤火虫的随机位置
      particle.position.x = Math.random() * 1000 - 500; // X
      particle.position.y = Math.random() * 1000 - 500; // Y
      particle.position.z = Math.random() * 1000 - 500; // Z
  
      particles.add(particle);
  }
  scene.add(particles);
};

// 动画循环
const animate = () => {
    requestAnimationFrame(animate);
    renderer.render(scene, camera);
};

// 生命周期钩子
onMounted(() => {
    initScene();
    animate();
});

上述代码中,particles用于储存粒子系统对象,方便后期更改参数。particlesCount代表的是萤火虫的数量,由于萤火虫的光一般是黄绿色,所以我们将其自发光颜色设置为"#CCFF00"。

实现萤火虫的发光效果

要实现萤火虫的发光效果,可以借助Threejs的EffectComposer效果合成器。EffectComposer的知识点可以参考我的其他文章:

threejs做特效:实现物体的发光效果-EffectComposer详解!

我们先引入效果合成器的一些threejs依赖项

import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';

然后创建一个处理发光效果的函数

let scene, camera, renderer, composer, particles;

// 初始化 Three.js 场景
const initScene = () => {
    // ....
  
    initPostProcessing()
};

// 初始化后期处理
const initPostProcessing = () => {
    composer = new EffectComposer(renderer);
    // 场景渲染 Pass
    const renderPass = new RenderPass(scene, camera);
    composer.addPass(renderPass);
    // 光晕效果 Pass
    const bloomPass = new UnrealBloomPass(
        new THREE.Vector2(window.innerWidth, window.innerHeight),
        1.5, // 强度
        0.3, // 半径
        0.25 // 阈值
    );
    composer.addPass(bloomPass);
};

// 动画循环
const animate = () => {
    animationFrameId = requestAnimationFrame(animate);
    // 渲染场景(使用后期处理)
    composer.render();
};

// 生命周期钩子
onMounted(() => {
    initScene();
    animate();
});

</script>

借助AI实现萤火虫的飞舞

要实现萤火虫的运动效果,我们手写运动轨迹太复杂了,我们可以借助AI算法来实现其无规则或者规则的运动轨迹。

Simplex Noise

**simplex-noise** 是一种改良的噪声算法,由 Perlin 噪声的发明者 Ken Perlin 提出。它适合用于生成平滑的伪随机数据,在许多场景中(如自然模拟、动画、程序化生成纹理等)都有应用。

官方传送门:https://www.npmjs.com/package/simplex-noise

萤火虫飞舞的效果应该满足以下特点:

  1. 运动是平滑的:不能有突兀的跳跃和不规则的抖动。
  2. 运动轨迹是自然的:看起来像随机飞舞,但又遵循某种自然规律。
  3. 可控性强:可以基于噪声的值控制位置、发光强度等。

Simplex Noise 的平滑、自然和伪随机特性,特别适合这种需求

simplex-noise 库中,可以用 createNoise2DcreateNoise3D 创建 2D 或 3D 噪声生成器,用于生成平滑的、随时间变化的伪随机数据。可以看出,用Simplex Noise的AI算法数据模拟萤火虫的飞舞效果是非常合适的。

使用simplex-noise模拟运动轨迹

更改萤火虫的运动轨迹其实非常容易,我们只需要用simplex-noise的AI数据,改变每一个萤火虫在场景中的x,y,z轴位置即可。它的核心伪代码应该如下:

// 更新粒子位置
const updateParticles = () => {
    particles.children.forEach((particle) => {
        const { x, y, z } = particle.position;
        // 使用噪声来更新粒子的位置
        particle.position.x += AI位置; // X
        particle.position.y += AI位置; // Y
        particle.position.z += AI位置; // Z
    });
};

完成的代码如下

npm i simplex-noise
<template>
    <div ref="threeContainer" class="three-container"></div>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import * as THREE from 'three';
import { createNoise2D } from 'simplex-noise';
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';

// 设置引用和变量
const threeContainer = ref(null); // 用于渲染的 DOM 容器

let scene, camera, renderer, composer, particles;

const noise = createNoise2D(); // 使用 SimplexNoise 创建噪声生成器
let time = 0; // 时间变量,用于驱动噪声的变化

// 初始化 Three.js 场景
const initScene = () => {
    // 创建场景
    scene = new THREE.Scene();
    // 创建摄像机
    camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 20, 1000);
    camera.position.set(0, 150, 500); // 设置相机位置
    camera.lookAt(0, 0, 0); // 看向原点

    // 创建渲染器
    renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.setPixelRatio(window.devicePixelRatio);
    threeContainer.value.appendChild(renderer.domElement);

    // 创建粒子系统(球体粒子)
    particles = new THREE.Group();
    const particlesCount = 200;

    for (let i = 0; i < particlesCount; i++) {
        const geometry = new THREE.SphereGeometry(5, 5, 5); // 创建球体
        const material = new THREE.MeshStandardMaterial({
            emissive: new THREE.Color("#CCFF00"), // 自发光颜色
            emissiveIntensity: 1.5, // 自发光强度
        });
        const particle = new THREE.Mesh(geometry, material);
        particle.position.x = Math.random() * 1000 - 500; // X
        particle.position.y = Math.random() * 1000 - 500; // Y
        particle.position.z = Math.random() * 1000 - 500; // Z
        particles.add(particle);
    }
    scene.add(particles);
    // 初始化后期处理
    initPostProcessing();

};

// 初始化后期处理
const initPostProcessing = () => {
    composer = new EffectComposer(renderer);

    // 场景渲染 Pass
    const renderPass = new RenderPass(scene, camera);
    composer.addPass(renderPass);

    // 光晕效果 Pass
    const bloomPass = new UnrealBloomPass(
        new THREE.Vector2(window.innerWidth, window.innerHeight),
        1.5, // 强度
        0.3, // 半径
        0.25 // 阈值
    );
    composer.addPass(bloomPass);
};


// 更新粒子位置
const updateParticles = () => {
    particles.children.forEach((particle) => {
        const { x, y, z } = particle.position;
        // 使用噪声来更新粒子的位置
        particle.position.x += noise(x, time) * 3; // X
        particle.position.y += noise(y, time) * 3; // Y
        particle.position.z += noise(z, time) * 3; // Z
    });
    time += 0.1; // 增加时间,控制噪声变化
};

// 动画循环
const animate = () => {
    requestAnimationFrame(animate);
    updateParticles(); // 更新粒子的位置
    // 渲染场景(使用后期处理)
    composer.render();
};

// 生命周期钩子
onMounted(() => {
    initScene();
    animate();
});

</script>

<style>
.three-container {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    top: 0;
    overflow: hidden;
}
</style>

上述代码中,Simplex Noise 通过 createNoise2D 生成的一个二维噪声生成器,作为驱动萤火虫位置变化的核心工具。它为每个粒子生成平滑且自然的随机运动,使粒子看起来像真实的萤火虫飞舞。

代码中的粒子更新函数:

particle.position.x += noise(x, time) * 3; // X
particle.position.y += noise(y, time) * 3; // Y
particle.position.z += noise(z, time) * 3; // Z
  • **noise(x, time)**
    • 生成一个基于粒子当前位置 x 和时间 time 的噪声值。
    • xtime 作为输入,确保噪声在空间和时间上的连续性。
  • **乘以一个因子 ****3**
    • 噪声的值通常在 [-1, 1],通过乘以 3 将其映射到更大的运动幅度。
  • 时间 **time** 控制动态变化
    • 时间的推进使得噪声生成的值不断变化,从而推动粒子的位置变化。

场景优化

为了让场景更加真实,我们可以给场景添加背景图片,模拟更好的夜晚效果

    // 创建场景
    scene = new THREE.Scene();
    new THREE.CubeTextureLoader()
        .setPath("/sky/")
        .load(
            [
                "posx.jpg",
                "negx.jpg",
                "posy.jpg",
                "negy.jpg",
                "posz.jpg",
                "negz.jpg",
            ],
            (texture) => {
                scene.background = texture;
            }
        );

注意,贴图需要自己准备

我们还可以添加轨道控制器,让场景支持旋转移动,以更丰富的视角观察萤火虫效果。

import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

// 初始化 Three.js 场景
const initScene = () => {
    // ....

    // 添加相机控制器
    const controls = new OrbitControls(camera, renderer.domElement);
    controls.enabled = true;
    controls.update();

    // 初始化后期处理
    initPostProcessing();
};

此外,我们也可以添加场景的尺寸动态变化、注销等等其他优化逻辑。

// 初始化 Three.js 场景
const initScene = () => {
  // ...
  // 处理窗口大小变化
  window.addEventListener('resize', onWindowResize);
};

// 窗口大小变化时更新渲染器和相机
const onWindowResize = () => {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
    composer.setSize(window.innerWidth, window.innerHeight);
};

// 销毁场景
const destroyScene = () => {
    cancelAnimationFrame(animationFrameId);
    composer.dispose();
    renderer.dispose();
    window.removeEventListener('resize', onWindowResize);
};

onUnmounted(() => {
    destroyScene();
});

可运行的完整代码

<template>
    <div ref="threeContainer" class="three-container"></div>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import * as THREE from 'three';
import { createNoise2D } from 'simplex-noise';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';

// 设置引用和变量
const threeContainer = ref(null); // 用于渲染的 DOM 容器

let scene, camera, renderer, composer, particles, animationFrameId;
const noise = createNoise2D(); // 使用 SimplexNoise 创建噪声生成器
let time = 0; // 时间变量,用于驱动噪声的变化

// 初始化 Three.js 场景
const initScene = () => {
    // 创建场景
    scene = new THREE.Scene();

    // 创建摄像机
    camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 20, 1000);
    camera.position.set(0, 150, 500); // 设置相机位置
    camera.lookAt(0, 0, 0); // 看向原点

    // 创建渲染器
    renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.setPixelRatio(window.devicePixelRatio);
    threeContainer.value.appendChild(renderer.domElement);

    // 创建粒子系统(球体粒子)
    particles = new THREE.Group();
    const particlesCount = 200;

    for (let i = 0; i < particlesCount; i++) {
        const geometry = new THREE.SphereGeometry(5, 5, 5); // 创建球体
        const material = new THREE.MeshStandardMaterial({
            emissive: new THREE.Color("#CCFF00"), // 自发光颜色
            emissiveIntensity: 1.5, // 自发光强度
            // color: new THREE.Color(Math.random(), Math.random(), Math.random()), // 随机颜色
        });
        const particle = new THREE.Mesh(geometry, material);

        particle.position.x = Math.random() * 1000 - 500; // X
        particle.position.y = Math.random() * 1000 - 500; // Y
        particle.position.z = Math.random() * 1000 - 500; // Z

        particles.add(particle);
    }
    scene.add(particles);

    // 添加光源
    const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
    scene.add(ambientLight);

    const pointLight = new THREE.PointLight(0xffffff, 1.5, 1000);
    pointLight.position.set(200, 300, 400);
    scene.add(pointLight);

    // 添加相机控制器
    const controls = new OrbitControls(camera, renderer.domElement);
    controls.enabled = true;
    controls.update();

    // 初始化后期处理
    initPostProcessing();

    // 处理窗口大小变化
    window.addEventListener('resize', onWindowResize);
};

// 初始化后期处理
const initPostProcessing = () => {
    composer = new EffectComposer(renderer);

    // 场景渲染 Pass
    const renderPass = new RenderPass(scene, camera);
    composer.addPass(renderPass);

    // 光晕效果 Pass
    const bloomPass = new UnrealBloomPass(
        new THREE.Vector2(window.innerWidth, window.innerHeight),
        1.5, // 强度
        0.3, // 半径
        0.25 // 阈值
    );
    composer.addPass(bloomPass);
};

// 窗口大小变化时更新渲染器和相机
const onWindowResize = () => {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
    composer.setSize(window.innerWidth, window.innerHeight);
};

// 更新粒子位置
const updateParticles = () => {
    particles.children.forEach((particle) => {
        const { x, y, z } = particle.position;

        // 使用噪声来更新粒子的位置
        particle.position.x += noise(x, time) * 3; // X
        particle.position.y += noise(y, time) * 3; // Y
        particle.position.z += noise(z, time) * 3; // Z
    });

    time += 0.1; // 增加时间,控制噪声变化
};

// 动画循环
const animate = () => {
    animationFrameId = requestAnimationFrame(animate);

    updateParticles(); // 更新粒子的位置

    // 渲染场景(使用后期处理)
    composer.render();
};

// 销毁场景
const destroyScene = () => {
    cancelAnimationFrame(animationFrameId);
    composer.dispose();
    renderer.dispose();
    window.removeEventListener('resize', onWindowResize);
};

// 生命周期钩子
onMounted(() => {
    initScene();
    animate();
});

onUnmounted(() => {
    destroyScene();
});
</script>

<style>
.three-container {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    top: 0;
    overflow: hidden;
}
</style>

总结

本文使用 Three.js 创建一个 3D 场景,生成粒子(代表萤火虫),并使用 AI 算法(如 Simplex Noise噪声算法)来模拟萤火虫的动态运动轨迹。

它的核心步骤如下:

  1. 创建 3D 场景和渲染器:通过 Three.js 创建了一个基本的场景和渲染器,摄像机的位置设置使其可以从远处观察场景中的粒子。
  2. 生成粒子(萤火虫):使用 THREE.SphereGeometry 创建球体作为粒子,并设置自发光材质,模拟萤火虫的发光效果。
  3. 后期处理(发光效果):利用 Three.js 的 EffectComposerUnrealBloomPass 实现了萤火虫的光晕发光效果,使其看起来更自然。
  4. 使用 AI(Simplex Noise)模拟萤火虫飞舞:通过 simplex-noise 库生成的噪声数据,控制每个粒子的运动轨迹,产生平滑且自然的飞舞效果。
  5. 场景优化:添加了背景图片,模拟夜空效果;并通过 OrbitControls 实现了场景的交互式旋转和缩放。

如果本文对大家有帮助,欢迎点赞收藏~关注我,学习前端不迷路!

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

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

相关文章

如何判断注入点传参类型--理论

注入点传参类型 在我们找到注入点后&#xff0c;首先要判断传参的类型&#xff0c;才能以正确的形式向数据库查询数据。 注入点传参一般分为数字型和字符型。 数字型&#xff1a;当传入的参数为整形时&#xff0c;存在SQL注入漏洞&#xff0c;就可以认为是数字型注入。 字符…

01_初识GeoParquet数据集

概述 GeoParquet是一种用于存储地理空间数据的文件格式&#xff0c;基于Apache Parquet。它支持高效地存储和查询大型地理空间数据集&#xff0c;具有良好的压缩性能和**列式**存储结构。GeoParquet还与许多地理信息系统&#xff08;GIS&#xff09;和大数据处理工具兼容&#…

SAP B1 登陆报错解决方案 - 系统架构目录服务器选择

背景 登录时出现如下报错&#xff0c;报错显示为【系统架构目录服务器选择】 强行登录会发现过往账套都不见了 出现原因 出于各种原因在开机时没有把 SAP 所有的服务成功启动&#xff08;上一次启动科学上网后全局代理没关干净之类的&#xff09;。 解决方案 关机几分钟重启…

037集——JoinEntities连接多段线polyline和圆弧arc(CAD—C#二次开发入门)

如图&#xff1a;最终效果 polyline连接&#xff1a; 代码如下&#xff1a; public void joinentities() {Curve pLine Z.db.SelectEntities<Curve>().First().Clone() as Curve;pLine.ChangeEntityColor(1);Curve pLine1 Z.db.SelectEntities<Curve>().First()…

【划分型 DP-约束划分个数】【hard】【阿里笔试】力扣1278. 分割回文串 III

给你一个由小写字母组成的字符串 s&#xff0c;和一个整数 k。 请你按下面的要求分割字符串&#xff1a; 首先&#xff0c;你可以将 s 中的部分字符修改为其他的小写英文字母。 接着&#xff0c;你需要把 s 分割成 k 个非空且不相交的子串&#xff0c;并且每个子串都是回文串…

如何使用 Vivado 从源码构建 Infinite-ISP FPGA 项目

如约介绍源码构建 Infinite-ISP 项目&#xff0c;其实大家等的是源码&#xff0c;所以中间过程简洁略过&#xff0c;可以直接翻到文末获取链接。 开源ISP&#xff08;Infinite-ISP&#xff09;介绍 构建工程 第一步&#xff0c;从文末或者下面链接获取源码 https://github.com/…

JDK、MAVEN与IDEA的安装与配置

1.认识JDK、MAVEN与IDEA JDK 提供了编译和运行Java程序的基本环境。Maven 帮助管理项目的构建和依赖。IDEA 提供了一个强大的开发环境&#xff0c;使得编写、调试和运行Java程序更加高效。 2. 安装与环境配置 2.1 官网地址 选择你需要的版本下载&#xff1a; MAVEN下载传送…

八股(6)——MySQL(概念相关)

八股&#xff08;6&#xff09;——MySQL&#xff08;概念相关&#xff09; 4.2 MySQLMySQL 基础关系型数据库介绍ACIDMySQL 介绍 MySQL 基础架构MySQL 存储引擎MySQL 存储引擎架构了解吗&#xff1f;MySQL 支持哪些存储引擎&#xff1f;默认使用哪个&#xff1f; MySQL索引什么…

CTF--php伪协议结合Base64绕过

Base64绕过 在ctf中&#xff0c;base64是比较常见的编码方式&#xff0c;在做题的时候发现自己对于base64的编码和解码规则不是很了解&#xff0c;并且恰好碰到了类似的题目&#xff0c;在翻阅了大佬的文章后记录一下&#xff0c;对于base64编码的学习和一个工具 base64编码是…

可视化建模与UML《活动图实验报告》

你当像鸟飞往你的山。 一、实验目的&#xff1a; 1、熟悉活动图的基本功能和使用方法。 2、掌握使用建模工具软件绘制协作图的方法 二、实验环境&#xff1a; window7 | 10 | 11 EA15 三、实验内容&#xff1a; <1>绘制学生选课系统中添加课程(Add Course)用例的活动图…

Spring Cloud Alibaba、Spring Cloud 与 Spring Boot各版本的对应关系

参考spring-cloud-alibaba github wiki说明&#xff1a;版本说明 下面截取说明&#xff1a; 2022.x 分支 2021.x 分支 2.2.x 分支 组件版本关系

基于Java Springboot大学校园旧物捐赠网站

一、作品包含 源码数据库设计文档万字PPT全套环境和工具资源部署教程 二、项目技术 前端技术&#xff1a;Html、Css、Js、Vue、Element-ui 数据库&#xff1a;MySQL 后端技术&#xff1a;Java、Spring Boot、MyBatis 三、运行环境 开发工具&#xff1a;IDEA/eclipse 数据…

基于Java Springboot未央商城管理系统

一、作品包含 源码数据库设计文档万字全套环境和工具资源部署教程 二、项目技术 前端技术&#xff1a;Html、Css、Js、Vue、Element-ui 数据库&#xff1a;MySQL 后端技术&#xff1a;Java、Spring Boot、MyBatis 三、运行环境 开发工具&#xff1a;IDEA 数据库&#xf…

小米路由器用外网域名访问管理界面

本文在Redmi AX3000 (RA81)设置&#xff0c;其他型号路由器的管理界面端口可能各不相同。 开始之前需要保证路由器SSH功能正常&#xff0c;如果没有SSH可以参考这里。 1. 给WAN口开放80端口 可以通过下载mixbox的firewall插件或者其他防火墙插件开放端口。 2. 把域名解析到路…

ant-design-vue中table组件多列排序

antD中table组件多列排序 使用前注意实现效果图实现的功能点及相关代码1. 默认按某几个字段排序2. 点击排序按钮可同时对多个字段进行排序3. 点击重置按钮可恢复默认排序状态。 功能实现完整的关键代码 使用前注意 先要确认你使用的antD版本是否支持多列排序&#xff0c;我这里…

Linux开发工具:Vim 与 gcc,打造高效编程的魔法双剑

文章目录 一、初识Vim模式 二、vim基本操作2.1基础操作2.2命令模式/正常模式2.2.1光标定位2.2.2复制粘贴、删除2.2.3撤销2.2.4替换字符2.2.5替换模式 2.3底行模式2.3.1退出vim和**保存文件**2.3.2定位文本字符串2.3.3命令2.3.4实现分屏2.3.5替换指定字符串 2.4补充指令2.4.1视图…

算法.图论-习题全集(Updating)

文章目录 本节设置的意义并查集篇并查集简介以及常见技巧并查集板子(洛谷)情侣牵手问题相似的字符串组岛屿数量(并查集做法)省份数量移除最多的同行或同列石头最大的人工岛找出知晓秘密的所有专家 建图及其拓扑排序篇链式前向星建图板子课程表 本节设置的意义 主要就是为了复习…

云原生之k8s服务管理

文章目录 服务管理Service服务原理ClusterIP服务 对外发布应用服务类型NodePort服务Ingress安装配置Ingress规则 Dashboard概述 认证和授权ServiceAccount用户概述创建ServiceAccount 权限管理角色与授权 服务管理 Service 服务原理 容器化带来的问题 自动调度&#xff1a;…

Atomic原子操作类详解

Atomic原子操作类介绍 在并发编程中很容易出现并发安全的问题&#xff0c;有一个很简单的例子就是多线程更新变量i1,比如多个线程执行i操作&#xff0c;就有可能获取不到正确的值&#xff0c;而这个问题&#xff0c;最常用的方法是通过Synchronized进行控制来达到线程安全的目…

随手记: vue监听路由

随手记&#xff1a;没空记详细 newVal,oldVal 可以查看到最新路由和上一条路由详细 watch: {$route: {handler(newVal,oldVal) {console.log(newVal, oldVal)if(oldVal.path /organization/serviceManagement/list) {if(this.cacheId ! newVal.query.id) {this.cacheId newV…