# 一、应用示例
# 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>
- 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>
- types / three.d.ts
declare module 'three'
declare module '@/components/waves.vue'
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>
- 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>
- 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
}
← 交互API