# 一、应用示例
# 1、大波浪特效
three.js 官方实例:waves (opens new window)
iview 官方界面就采用了类似的特效:

下面我们可以自己实现一下:
普通效果
- login.vue
<template> <WavesBg :top="250"></WavesBg> <div class="login-container">登录card</div> </template> <script setup lang="ts"> import WavesBg from '@/components/waves.vue' </script> <style scoped> /* 卡片居中 */ .login-container { width: 100%; height: 100vh; background-color: cadetblue; display: flex; align-items: center; justify-content: center; } </style>
Copied!
- waves.vue
<template> <div id="iviewBg"></div> </template> <script setup> import * as THREE from 'three' // 显示右上角fps框 import { onMounted } from 'vue' const props = defineProps({ //控制x轴波浪的长度 amountX: { type: Number, default: 50 }, //控制y轴波浪的长度 amountY: { type: Number, default: 50 }, //控制点颜色 color: { type: String, default: '#097bdb' }, //控制波浪的位置 top: { type: Number, default: 350 } }) const SEPARATION = 100 // let stats; let container, camera, scene, renderer let particles, count = 0 let mouseX = 0 let windowHalfX = window.innerWidth / 2 function init() { container = document.createElement('div') document.getElementById('iviewBg').appendChild(container) //创建透视相机 camera = new THREE.PerspectiveCamera( 75, //摄像机视锥体垂直视野角度 window.innerWidth / window.innerHeight, //摄像机视锥体长宽比 1, //摄像机视锥体近端面 10000 //摄像机视锥体远端面 ) //设置相机z轴视野 camera.position.z = 1000 //创建场景 scene = new THREE.Scene() const numParticles = props.amountX * props.amountY const positions = new Float32Array(numParticles * 3) const scales = new Float32Array(numParticles) let i = 0, j = 0 // 初始化粒子位置和大小 for (let ix = 0; ix < props.amountX; ix++) { for (let iy = 0; iy < props.amountY; iy++) { positions[i] = ix * SEPARATION - (props.amountX * SEPARATION) / 2 // x positions[i + 1] = 0 // y positions[i + 2] = iy * SEPARATION - (props.amountY * SEPARATION) / 2 // z scales[j] = 1 i += 3 j++ } } //是面片、线或点几何体的有效表述。包括顶点位置,面片索引、法相量、颜色值、UV 坐标和自定义缓存属性值。使用 BufferGeometry 可以有效减少向 GPU 传输上述数据所需的开销 const geometry = new THREE.BufferGeometry() geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)) geometry.setAttribute('scale', new THREE.BufferAttribute(scales, 1)) //着色器材质(ShaderMaterial),设置球的大小,颜色,等 const material = new THREE.ShaderMaterial({ uniforms: { //设置球的颜色 color: { value: new THREE.Color(props.color) } }, //控制球的大小 vertexShader: 'attribute float scale; void main() {vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );gl_PointSize = scale * ( 300.0 / - mvPosition.z );gl_Position = projectionMatrix * mvPosition;}', fragmentShader: 'uniform vec3 color;void main() {if ( length( gl_PointCoord - vec2( 0.5, 0.5 ) ) > 0.475 ) discard;gl_FragColor = vec4( color, 1.0 );}' }) //一个用于显示点的类。 particles = new THREE.Points(geometry, material) //往场景中添加点 scene.add(particles) //alpha - canvas是否包含alpha (透明度)。默认为 false。 //渲染器的背景色默认为黑色,设置渲染器的背景色为透明 renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }) renderer.setPixelRatio(window.devicePixelRatio) renderer.setClearAlpha(0) renderer.setSize(window.innerWidth, window.innerHeight) container.appendChild(renderer.domElement) //显示右上角fps框 // stats = new Stats(); // container.appendChild(stats.dom); container.style.touchAction = 'none' //监听鼠标移动事件 container.addEventListener('pointermove', onPointerMove) //调整波浪的位置 container.style.position = 'relative' container.style.top = `${props.top}px` window.addEventListener('resize', onWindowResize) } function render() { camera.position.x += (mouseX - camera.position.x) * 0.05 camera.position.y = 400 camera.lookAt(scene.position) const positions = particles.geometry.attributes.position.array const scales = particles.geometry.attributes.scale.array // 设置粒子位置和大小 let i = 0, j = 0 for (let ix = 0; ix < props.amountX; ix++) { for (let iy = 0; iy < props.amountY; iy++) { positions[i + 1] = Math.sin((ix + count) * 0.3) * 50 + Math.sin((iy + count) * 0.5) * 50 scales[j] = (Math.sin((ix + count) * 0.3) + 1) * 10 + (Math.sin((iy + count) * 0.5) + 1) * 10 i += 3 j++ } } particles.geometry.attributes.position.needsUpdate = true particles.geometry.attributes.scale.needsUpdate = true renderer.render(scene, camera) count += 0.1 } function onWindowResize() { windowHalfX = window.innerWidth / 2 camera.aspect = window.innerWidth / window.innerHeight camera.updateProjectionMatrix() renderer.setSize(window.innerWidth, window.innerHeight) } //监听鼠标移动事件 function onPointerMove(event) { // console.log(event) if (event.isPrimary === false) return mouseX = event.clientX - windowHalfX } function animate() { requestAnimationFrame(animate) render() //fps 实时更新 // stats.update(); } onMounted(() => { init() animate() }) </script> <style> #iviewBg { width: 100%; height: 100vh; position: absolute; top: 0; left: 0; background: url('../assets/wavesBg.png') no-repeat; background-repeat: no-repeat; background-size: cover; overflow: hidden; } </style>
Copied!
- types / three.d.ts
declare module 'three' declare module '@/components/waves.vue'
Copied!
3d 加强
https://mybj123.com/8316.html
波浪 plus
https://juejin.cn/post/6844904015671590919
# 2、粒子特效
常见的粒子特效有:雪花、星星、烟花等。这里我不得不提一下一个非常优秀的库Particles.js (opens new window)
Particles.js 库基于 HTML5 Canvas 技术开发,通过自定义粒子的大小、颜色、速度、方向、形状等参数,实现各种炫酷的粒子效果
基本使用
以前有一个 vue-particles (opens new window) 的 Vue 组件库,可以在在网页中创建粒子效果
使用示例
https://www.cnblogs.com/IwishIcould/p/11568563.html
vue-particles 是基于 Particles.js 开发,使用 Canvas 渲染粒子效果,可以在 vue2 中使用。
但在现在的 vue、react 框架开发下,我更加推荐使用tsParticles (opens new window)
tsparticles 是一个基于 Typescript 和 Particles.js 的 JavaScript 库。并且与 vue2、vue3 (opens new window)、react、angular、jQuery 等兼容。

使用示例
上图的目标效果:https://codepen.io/lencamo/pen/poxammE?editors=0010
- login.vue
<template> <Particle></Particle> <div class="login-container">登录card</div> </template> <script setup lang="ts"> import Particle from '@/components/particle.vue' </script> <style scoped> /* 卡片居中 */ .login-container { width: 100%; height: 100vh; background-color: cadetblue; display: flex; align-items: center; justify-content: center; } </style>
Copied!
- particle.vue
<script setup> import { loadFull } from 'tsparticles' const particlesInit = async (engine) => { await loadFull(engine) } const particlesLoaded = async (container) => { console.log('Particles container loaded', container) } </script> <template> <div id="app"> <Particles id="tsparticles" :particlesInit="particlesInit" :particlesLoaded="particlesLoaded" url="/particles.json" /> </div> </template>
Copied!
- particles.json
{ "background": { "color": { "value": "#0d47a1" } }, "fpsLimit": 120, "interactivity": { "events": { "onClick": { "enable": true, "mode": "push" }, "onHover": { "enable": true, "mode": "attract", "parallax": { "enable": false, "force": 60, "smooth": 10 } }, "resize": true }, "modes": { "push": { "quantity": 4 }, "attract": { "distance": 200, "duration": 0.4, "factor": 5 } } }, "particles": { "color": { "value": "#ffffff" }, "links": { "color": "#ffffff", "distance": 150, "enable": true, "opacity": 0.4, "width": 1 }, "move": { "attract": { "enable": false, "rotateX": 600, "rotateY": 1200 }, "bounce": false, "direction": "none", "enable": true, "out_mode": "out", "random": false, "speed": 2, "straight": false }, "number": { "density": { "enable": true, "area": 800 }, "value": 80 }, "opacity": { "value": 0.5 }, "shape": { "character": { "fill": false, "font": "Verdana", "style": "", "value": "*", "weight": "400" }, "polygon": { "nb_sides": 5 }, "stroke": { "color": "#000000", "width": 0 }, "type": "circle" }, "size": { "anim": { "enable": false, "size_min": 0.1, "speed": 40, "sync": false }, "random": true, "value": 5 } }, "polygon": { "draw": { "enable": false, "lineColor": "#ffffff", "lineWidth": 0.5 }, "move": { "radius": 10 }, "scale": 1, "type": "none", "url": "" }, "detectRetina": true }
Copied!
← 交互API