主题
闪耀正方形
鼠标移动正方形会散开并且发光点击下载案例
js
import "./style.scss";
import { MeshMatcapMaterial, Group, Mesh, Color, Vector3, Clock } from "three";
import { scene, camera, renderer, controls } from "./base.js";
import { RoundedBoxGeometry } from "three/examples/jsm/geometries/RoundedBoxGeometry.js";
import { EffectComposer, RenderPass, EffectPass, BloomEffect } from "postprocessing";
camera.position.set(10, 10, 10);
controls.enableDamping = true;
renderer.setAnimationLoop(animate);
// 后处理渲染器
const composer = new EffectComposer(renderer);
composer.addPass(new RenderPass(scene, camera));
// 辉光
const bloom = new BloomEffect({
mipmapBlur: true, // 启用mipmap
luminanceThreshold: 0.3, // 降低阈值,更容易触发辉光
intensity: 0, // 初始强度为0
levels: 7, // 辉光等级
});
const bloomPass = new EffectPass(camera, bloom);
composer.addPass(bloomPass);
const cubesGroup = new Group(); // 创建一个组,用于存储所有box
scene.add(cubesGroup); // 添加组
const stride = 4; // 创建一个4x4x4的立方体
const gap = 0.1; // 创建的间隔
const displacement = 3; // 创建的位移程度
const intensity = 1; // 辉光强度
const positions = []; // 创建一个positions数组,用于存储每个box的位置
const center = stride / 2 - stride * gap + gap; // 创建的立方体中心点
for (let x = 0; x < stride; x++) {
for (let y = 0; y < stride; y++) {
for (let z = 0; z < stride; z++) {
const pos = [x + x * gap - center, y + y * gap - center, z + z * gap - center];
positions.push(pos);
const geom = new RoundedBoxGeometry(1, 1, 1, 2, 0.15);
const mat = new MeshMatcapMaterial({ color: "white" });
const cube = new Mesh(geom, mat);
cube.position.set(...pos);
cubesGroup.add(cube); // 添加box
}
}
}
const clock = new Clock();
const cursor = new Vector3(); // 创建一个向量,用于存储鼠标位置
const oPos = new Vector3(); // 创建一个向量,用于存储box的原始位置
const vec = new Vector3(); // 创建一个向量,用于存储box的位置
const dir = new Vector3(); // 创建一个向量,用于存储box与鼠标之间的方向
function animate() {
const delta = clock.getDelta();
const elapsed = clock.elapsedTime;
cursor.set(pointer.x, pointer.y, 0.5).unproject(camera); // 计算鼠标位置
dir.copy(cursor).sub(camera.position).normalize(); // 创建一个向量,用于存储box与鼠标之间的方向
cursor.add(dir.multiplyScalar(camera.position.length())); // 计算box的位置
let count = 0; // 创建一个计数器,用于记录当前box的索引
let totalDisplacement = 0; // 记录所有box的总位移程度
const totalBoxes = cubesGroup.children.length; // 获取box的总数
for (let child of cubesGroup.children) {
oPos.set(...positions[count++]);
dir.copy(oPos).sub(cursor).normalize(); // 计算box与鼠标之间的方向
const dist = oPos.distanceTo(cursor); // 计算box与鼠标之间的距离
const distInv = displacement - dist; // 计算box与鼠标之间的距离与 displacement 的差
const col = Math.max(0.5, distInv) / 1.5; // 计算box与鼠标之间的颜色
const mov = 1 + Math.sin(elapsed * 2 + 1000 * count); // 创建一个移动效果
const targetColor = dist > displacement * 1.1 ? new Color("white") : new Color(col / 2, col * 2, col * 4); // 计算box与鼠标之间的颜色
child.material.color.lerp(targetColor, 0.1); // 改变box的颜色
const targetPos = dist > displacement ? oPos : vec.copy(oPos).add(dir.multiplyScalar(distInv * intensity + mov / 4)); // 创建一个目标位置
// 创建一个缓动效果
child.position.set(damp(child.position.x, targetPos.x, 5, delta), damp(child.position.y, targetPos.y, 5, delta), damp(child.position.z, targetPos.z, 5, delta));
// 计算当前box与原始位置的距离(散开程度)
const currentDisplacement = child.position.distanceTo(oPos); // 计算当前box与原始位置的距离
totalDisplacement += currentDisplacement; // 累加所有box的总位移程度
}
const averageDisplacement = totalDisplacement / totalBoxes; // 计算所有box的总位移平均值
const maxExpectedDisplacement = 2; // 设置最大期望位移
const bloomIntensity = Math.min(averageDisplacement / maxExpectedDisplacement, 1) * 2; // 计算辉光强度
bloom.intensity = damp(bloom.intensity, bloomIntensity, 5, delta); // 改变辉光强度
controls.update();
renderer.render(scene, camera);
composer.render();
}
function damp(current, target, lambda, delta) {
return current + (target - current) * (1 - Math.exp(-lambda * delta));
}
const pointer = { x: 0, y: 0 };
// 鼠标移动
window.addEventListener("pointermove", (e) => {
pointer.x = (e.clientX / window.innerWidth) * 2 - 1;
pointer.y = -(e.clientY / window.innerHeight) * 2 + 1;
});
window.addEventListener("resize", () => {
composer.setSize(window.innerWidth, window.innerHeight);
});应用场景
借鉴这个思路,可以做一个官网的特效,感觉很酷。