前端:优化加载图片动画
This commit is contained in:
parent
b29fe30c71
commit
76ee04bdde
@ -42,8 +42,10 @@ const createErrorParticles = (width: number, height: number) => {
|
|||||||
const randomY = y + (Math.random() - 0.5) * randomOffset;
|
const randomY = y + (Math.random() - 0.5) * randomOffset;
|
||||||
|
|
||||||
// 缩放到适合容器的大小
|
// 缩放到适合容器的大小
|
||||||
const scaledX = randomX * (width * 0.3);
|
const size = Math.min(width, height);
|
||||||
const scaledY = randomY * (height * 0.3);
|
const scaleFactor = size * 0.3;
|
||||||
|
const scaledX = randomX * scaleFactor;
|
||||||
|
const scaledY = randomY * scaleFactor;
|
||||||
|
|
||||||
particles.push({
|
particles.push({
|
||||||
x: scaledX,
|
x: scaledX,
|
||||||
@ -57,9 +59,9 @@ const createErrorParticles = (width: number, height: number) => {
|
|||||||
|
|
||||||
// 随机初始位置
|
// 随机初始位置
|
||||||
positionArray.push(
|
positionArray.push(
|
||||||
(Math.random() - 0.5) * width * 2,
|
(Math.random() - 0.5) * size * 2,
|
||||||
(Math.random() - 0.5) * height * 2,
|
(Math.random() - 0.5) * size * 2,
|
||||||
(Math.random() - 0.5) * 100
|
(Math.random() - 0.5) * 50
|
||||||
);
|
);
|
||||||
|
|
||||||
// 随机初始颜色
|
// 随机初始颜色
|
||||||
@ -67,21 +69,29 @@ const createErrorParticles = (width: number, height: number) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return { particles, positionArray, colorArray };
|
const size = Math.min(width, height);
|
||||||
|
const scale = size / 200;
|
||||||
|
const particleSize = Math.max(1.2, scale * 1.2);
|
||||||
|
|
||||||
|
return { particles, positionArray, colorArray, particleSize };
|
||||||
};
|
};
|
||||||
|
|
||||||
// 添加笑脸粒子生成函数
|
// 修改 createSmileParticles 函数
|
||||||
const createSmileParticles = (width: number, height: number) => {
|
const createSmileParticles = (width: number, height: number) => {
|
||||||
const particles: Particle[] = [];
|
const particles: Particle[] = [];
|
||||||
const positionArray: number[] = [];
|
const positionArray: number[] = [];
|
||||||
const colorArray: number[] = [];
|
const colorArray: number[] = [];
|
||||||
|
|
||||||
// 调整笑脸参数
|
// 根据容器大小动态调整参数
|
||||||
const radius = Math.min(width, height) * 0.35; // 脸部大小
|
const size = Math.min(width, height);
|
||||||
const particlesCount = 400; // 轮廓粒子数量
|
const scale = size / 200; // 基准子
|
||||||
|
|
||||||
// 修改颜色为更深的金色
|
// 调整笑脸参数
|
||||||
const particleColor = new THREE.Color(0.8, 0.6, 0); // 更深的金色
|
const radius = size * 0.25; // 更合理的脸部大小比例
|
||||||
|
const particlesCount = Math.floor(150 * scale); // 减少粒子数量
|
||||||
|
const particleSize = Math.max(1.2, scale * 1.2); // 确保粒子大小适应屏幕
|
||||||
|
|
||||||
|
const particleColor = new THREE.Color(0.8, 0.6, 0);
|
||||||
|
|
||||||
// 创建圆形脸部轮廓
|
// 创建圆形脸部轮廓
|
||||||
for (let i = 0; i < particlesCount / 2; i++) {
|
for (let i = 0; i < particlesCount / 2; i++) {
|
||||||
@ -98,24 +108,25 @@ const createSmileParticles = (width: number, height: number) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
positionArray.push(
|
positionArray.push(
|
||||||
(Math.random() - 0.5) * width * 2,
|
(Math.random() - 0.5) * size * 4,
|
||||||
(Math.random() - 0.5) * height * 2,
|
(Math.random() - 0.5) * size * 4,
|
||||||
(Math.random() - 0.5) * 100
|
(Math.random() - 0.5) * 150
|
||||||
);
|
);
|
||||||
colorArray.push(particleColor.r, particleColor.g, particleColor.b);
|
colorArray.push(particleColor.r, particleColor.g, particleColor.b);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 眼睛参数
|
// 眼睛参数调整
|
||||||
const eyeOffset = radius * 0.2; // 眼睛水平间距
|
const eyeOffset = radius * 0.3; // 增加眼睛间距
|
||||||
const eyeY = radius * 0.2; // 眼睛垂直位置
|
const eyeY = radius * 0.15; // 调整眼睛垂直位置
|
||||||
const eyeSize = radius * 0.08; // 眼睛大小
|
const eyeSize = radius * 0.12; // 增加眼睛大小
|
||||||
|
|
||||||
|
// 眼睛粒子数量也要根据比例调整
|
||||||
|
const eyeParticles = Math.floor(20 * scale);
|
||||||
|
|
||||||
// 创建实心眼睛
|
|
||||||
[-1, 1].forEach(side => {
|
[-1, 1].forEach(side => {
|
||||||
// 创建密集的点来填充眼睛
|
for (let i = 0; i < eyeParticles; i++) {
|
||||||
for (let i = 0; i < 30; i++) {
|
const r = Math.random() * eyeSize;
|
||||||
const r = Math.random() * eyeSize; // 随机半径
|
const angle = Math.random() * Math.PI * 2;
|
||||||
const angle = Math.random() * Math.PI * 2; // 随机角度
|
|
||||||
const x = side * eyeOffset + Math.cos(angle) * r;
|
const x = side * eyeOffset + Math.cos(angle) * r;
|
||||||
const y = eyeY + Math.sin(angle) * r;
|
const y = eyeY + Math.sin(angle) * r;
|
||||||
|
|
||||||
@ -128,18 +139,18 @@ const createSmileParticles = (width: number, height: number) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
positionArray.push(
|
positionArray.push(
|
||||||
(Math.random() - 0.5) * width * 2,
|
(Math.random() - 0.5) * size * 4,
|
||||||
(Math.random() - 0.5) * height * 2,
|
(Math.random() - 0.5) * size * 4,
|
||||||
(Math.random() - 0.5) * 100
|
(Math.random() - 0.5) * 150
|
||||||
);
|
);
|
||||||
colorArray.push(particleColor.r, particleColor.g, particleColor.b);
|
colorArray.push(particleColor.r, particleColor.g, particleColor.b);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 嘴巴参数
|
// 嘴巴参数调整
|
||||||
const smileWidth = radius * 0.5; // 嘴巴宽度
|
const smileWidth = radius * 0.6; // 增加嘴巴宽度
|
||||||
const smileY = -radius * 0.3; // 将嘴巴位置向下移动更多
|
const smileY = -radius * 0.25; // 调整嘴巴位置
|
||||||
const smilePoints = 40; // 嘴巴粒子数量
|
const smilePoints = Math.floor(30 * scale); // 根据大小调整嘴巴粒子数量
|
||||||
|
|
||||||
// 创建微笑
|
// 创建微笑
|
||||||
for (let i = 0; i < smilePoints; i++) {
|
for (let i = 0; i < smilePoints; i++) {
|
||||||
@ -158,14 +169,14 @@ const createSmileParticles = (width: number, height: number) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
positionArray.push(
|
positionArray.push(
|
||||||
(Math.random() - 0.5) * width * 2,
|
(Math.random() - 0.5) * size * 4,
|
||||||
(Math.random() - 0.5) * height * 2,
|
(Math.random() - 0.5) * size * 4,
|
||||||
(Math.random() - 0.5) * 100
|
(Math.random() - 0.5) * 150
|
||||||
);
|
);
|
||||||
colorArray.push(particleColor.r, particleColor.g, particleColor.b);
|
colorArray.push(particleColor.r, particleColor.g, particleColor.b);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { particles, positionArray, colorArray };
|
return { particles, positionArray, colorArray, particleSize };
|
||||||
};
|
};
|
||||||
|
|
||||||
// 在文件开头添加新的 helper 函数
|
// 在文件开头添加新的 helper 函数
|
||||||
@ -179,33 +190,104 @@ const customEase = (t: number) => {
|
|||||||
: 1 - Math.pow(-2 * t + 2, 3) / 2;
|
: 1 - Math.pow(-2 * t + 2, 3) / 2;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ParticleImage = ({ src, onLoad, onError }: {
|
interface ParticleImageProps {
|
||||||
src?: string;
|
src?: string;
|
||||||
onLoad?: () => void;
|
onLoad?: () => void;
|
||||||
onError?: () => void;
|
onError?: () => void;
|
||||||
}) => {
|
performanceMode?: boolean; // 新增性能模式开关
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ParticleImage = ({
|
||||||
|
src,
|
||||||
|
onLoad,
|
||||||
|
onError,
|
||||||
|
performanceMode = false // 默认关闭
|
||||||
|
}: ParticleImageProps) => {
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
const sceneRef = useRef<THREE.Scene>();
|
const sceneRef = useRef<THREE.Scene>();
|
||||||
const cameraRef = useRef<THREE.OrthographicCamera>();
|
const cameraRef = useRef<THREE.OrthographicCamera>();
|
||||||
const rendererRef = useRef<THREE.WebGLRenderer>();
|
const rendererRef = useRef<THREE.WebGLRenderer>();
|
||||||
const animationFrameRef = useRef<number>();
|
const animationFrameRef = useRef<number>();
|
||||||
|
|
||||||
// 添加 resize 处理函数
|
const width = containerRef.current?.offsetWidth || 0;
|
||||||
|
const height = containerRef.current?.offsetHeight || 0;
|
||||||
|
const size = Math.min(width, height);
|
||||||
|
const scale = size / 200; // 基准因子
|
||||||
|
|
||||||
|
// 在性能模式下使用更保守的参数
|
||||||
|
const particleCount = performanceMode ?
|
||||||
|
Math.floor(100 * scale) :
|
||||||
|
Math.floor(200 * scale);
|
||||||
|
|
||||||
|
// 添 resize 处理函数
|
||||||
const handleResize = useCallback(() => {
|
const handleResize = useCallback(() => {
|
||||||
if (!containerRef.current || !cameraRef.current || !rendererRef.current) return;
|
if (!containerRef.current || !cameraRef.current || !rendererRef.current || !sceneRef.current) return;
|
||||||
|
|
||||||
const width = containerRef.current.offsetWidth;
|
const width = containerRef.current.offsetWidth;
|
||||||
const height = containerRef.current.offsetHeight;
|
const height = containerRef.current.offsetHeight;
|
||||||
|
|
||||||
const camera = cameraRef.current;
|
const camera = cameraRef.current;
|
||||||
camera.left = width / -2.1;
|
camera.left = width / -2;
|
||||||
camera.right = width / 2.1;
|
camera.right = width / 2;
|
||||||
camera.top = height / 2.1;
|
camera.top = height / 2;
|
||||||
camera.bottom = height / -2.1;
|
camera.bottom = height / -2;
|
||||||
camera.updateProjectionMatrix();
|
camera.updateProjectionMatrix();
|
||||||
|
|
||||||
rendererRef.current.setSize(width, height);
|
rendererRef.current.setSize(width, height);
|
||||||
}, []);
|
|
||||||
|
// 重新生成粒子
|
||||||
|
if (src === '') {
|
||||||
|
// 清除现有的 GSAP 动画
|
||||||
|
gsap.killTweensOf('*');
|
||||||
|
|
||||||
|
// 重新生成笑脸
|
||||||
|
const { particles, positionArray, colorArray, particleSize } = createSmileParticles(width, height);
|
||||||
|
|
||||||
|
const material = new THREE.PointsMaterial({
|
||||||
|
size: particleSize,
|
||||||
|
vertexColors: true,
|
||||||
|
transparent: true,
|
||||||
|
opacity: 1,
|
||||||
|
sizeAttenuation: true,
|
||||||
|
blending: THREE.AdditiveBlending,
|
||||||
|
depthWrite: false,
|
||||||
|
depthTest: false
|
||||||
|
});
|
||||||
|
|
||||||
|
const geometry = new THREE.BufferGeometry();
|
||||||
|
geometry.setAttribute('position', new THREE.Float32BufferAttribute(positionArray, 3));
|
||||||
|
geometry.setAttribute('color', new THREE.Float32BufferAttribute(colorArray, 3));
|
||||||
|
|
||||||
|
sceneRef.current.clear();
|
||||||
|
const points = new THREE.Points(geometry, material);
|
||||||
|
sceneRef.current.add(points);
|
||||||
|
|
||||||
|
// 修改这部分,添加动画而不是直接设置位置
|
||||||
|
const positionAttribute = geometry.attributes.position;
|
||||||
|
|
||||||
|
particles.forEach((particle, i) => {
|
||||||
|
const i3 = i * 3;
|
||||||
|
const distanceToCenter = Math.sqrt(
|
||||||
|
Math.pow(particle.originalX, 2) +
|
||||||
|
Math.pow(particle.originalY, 2)
|
||||||
|
);
|
||||||
|
const maxDistance = Math.sqrt(Math.pow(width/2, 2) + Math.pow(height/2, 2));
|
||||||
|
const normalizedDistance = distanceToCenter / maxDistance;
|
||||||
|
|
||||||
|
gsap.to(positionAttribute.array, {
|
||||||
|
duration: 0.8,
|
||||||
|
delay: normalizedDistance * 0.6,
|
||||||
|
[i3]: particle.originalX,
|
||||||
|
[i3 + 1]: particle.originalY,
|
||||||
|
[i3 + 2]: 0,
|
||||||
|
ease: "sine.inOut",
|
||||||
|
onUpdate: () => {
|
||||||
|
positionAttribute.needsUpdate = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [src]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!containerRef.current) return;
|
if (!containerRef.current) return;
|
||||||
@ -218,14 +300,15 @@ export const ParticleImage = ({ src, onLoad, onError }: {
|
|||||||
sceneRef.current = scene;
|
sceneRef.current = scene;
|
||||||
|
|
||||||
const camera = new THREE.OrthographicCamera(
|
const camera = new THREE.OrthographicCamera(
|
||||||
width / -1.5, // 扩大视野范围,从 -2 改为 -1.5
|
width / -2,
|
||||||
width / 1.5, // 扩大视野范围,从 2 改为 1.5
|
width / 2,
|
||||||
height / 1.5, // 扩大视野范围
|
height / 2,
|
||||||
height / -1.5, // 扩大视野范围
|
height / -2,
|
||||||
1,
|
1,
|
||||||
1000
|
1000
|
||||||
);
|
);
|
||||||
camera.position.z = 100;
|
camera.position.z = 500;
|
||||||
|
camera.lookAt(new THREE.Vector3(0, 0, 0));
|
||||||
cameraRef.current = camera;
|
cameraRef.current = camera;
|
||||||
|
|
||||||
const renderer = new THREE.WebGLRenderer({
|
const renderer = new THREE.WebGLRenderer({
|
||||||
@ -237,9 +320,13 @@ export const ParticleImage = ({ src, onLoad, onError }: {
|
|||||||
rendererRef.current = renderer;
|
rendererRef.current = renderer;
|
||||||
containerRef.current.appendChild(renderer.domElement);
|
containerRef.current.appendChild(renderer.domElement);
|
||||||
|
|
||||||
const geometry = new THREE.BufferGeometry();
|
// 检查是否应该显示笑脸
|
||||||
|
if (src === '') {
|
||||||
|
console.log('Showing smile animation');
|
||||||
|
const { particles, positionArray, colorArray, particleSize } = createSmileParticles(width, height);
|
||||||
|
|
||||||
const material = new THREE.PointsMaterial({
|
const material = new THREE.PointsMaterial({
|
||||||
size: 1.2,
|
size: particleSize,
|
||||||
vertexColors: true,
|
vertexColors: true,
|
||||||
transparent: true,
|
transparent: true,
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
@ -249,28 +336,33 @@ export const ParticleImage = ({ src, onLoad, onError }: {
|
|||||||
depthTest: false
|
depthTest: false
|
||||||
});
|
});
|
||||||
|
|
||||||
// 检查是否应该显示笑脸
|
const geometry = new THREE.BufferGeometry();
|
||||||
if (src === '') {
|
|
||||||
console.log('Showing smile animation');
|
|
||||||
const { particles, positionArray, colorArray } = createSmileParticles(width, height);
|
|
||||||
|
|
||||||
geometry.setAttribute('position', new THREE.Float32BufferAttribute(positionArray, 3));
|
geometry.setAttribute('position', new THREE.Float32BufferAttribute(positionArray, 3));
|
||||||
geometry.setAttribute('color', new THREE.Float32BufferAttribute(colorArray, 3));
|
geometry.setAttribute('color', new THREE.Float32BufferAttribute(colorArray, 3));
|
||||||
|
|
||||||
const points = new THREE.Points(geometry, material);
|
const points = new THREE.Points(geometry, material);
|
||||||
scene.add(points);
|
scene.add(points);
|
||||||
|
|
||||||
|
// 修改动画效果
|
||||||
const positionAttribute = geometry.attributes.position;
|
const positionAttribute = geometry.attributes.position;
|
||||||
|
|
||||||
|
// 计算到中心的距离用于延迟
|
||||||
particles.forEach((particle, i) => {
|
particles.forEach((particle, i) => {
|
||||||
const i3 = i * 3;
|
const i3 = i * 3;
|
||||||
|
const distanceToCenter = Math.sqrt(
|
||||||
|
Math.pow(particle.originalX, 2) +
|
||||||
|
Math.pow(particle.originalY, 2)
|
||||||
|
);
|
||||||
|
const maxDistance = Math.sqrt(Math.pow(width/2, 2) + Math.pow(height/2, 2));
|
||||||
|
const normalizedDistance = distanceToCenter / maxDistance;
|
||||||
|
|
||||||
gsap.to(positionAttribute.array, {
|
gsap.to(positionAttribute.array, {
|
||||||
duration: 1,
|
duration: 0.8,
|
||||||
delay: Math.random() * 0.3,
|
delay: normalizedDistance * 0.6,
|
||||||
[i3]: particle.originalX,
|
[i3]: particle.originalX,
|
||||||
[i3 + 1]: particle.originalY,
|
[i3 + 1]: particle.originalY,
|
||||||
[i3 + 2]: 0,
|
[i3 + 2]: 0,
|
||||||
ease: "back.out(1.7)",
|
ease: "sine.inOut",
|
||||||
onUpdate: () => {
|
onUpdate: () => {
|
||||||
positionAttribute.needsUpdate = true;
|
positionAttribute.needsUpdate = true;
|
||||||
}
|
}
|
||||||
@ -286,19 +378,56 @@ export const ParticleImage = ({ src, onLoad, onError }: {
|
|||||||
};
|
};
|
||||||
animate();
|
animate();
|
||||||
|
|
||||||
return;
|
// 添加 resize 监听
|
||||||
|
const resizeObserver = new ResizeObserver(() => {
|
||||||
|
if (containerRef.current) {
|
||||||
|
handleResize();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
resizeObserver.observe(containerRef.current);
|
||||||
|
|
||||||
|
// 添加窗口 resize 监听
|
||||||
|
window.addEventListener('resize', handleResize);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (animationFrameRef.current) {
|
||||||
|
cancelAnimationFrame(animationFrameRef.current);
|
||||||
|
}
|
||||||
|
if (renderer && containerRef.current) {
|
||||||
|
containerRef.current.removeChild(renderer.domElement);
|
||||||
|
renderer.dispose();
|
||||||
|
}
|
||||||
|
gsap.killTweensOf('*');
|
||||||
|
if (containerRef.current) {
|
||||||
|
resizeObserver.unobserve(containerRef.current);
|
||||||
|
}
|
||||||
|
window.removeEventListener('resize', handleResize);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建错误动画函数
|
// 创建错误动画函数
|
||||||
const showErrorAnimation = () => {
|
const showErrorAnimation = () => {
|
||||||
if (!scene) return;
|
if (!scene) return;
|
||||||
|
|
||||||
const { particles, positionArray, colorArray } = createErrorParticles(width, height);
|
const { particles, positionArray, colorArray, particleSize } = createErrorParticles(width, height);
|
||||||
|
|
||||||
|
const material = new THREE.PointsMaterial({
|
||||||
|
size: particleSize,
|
||||||
|
vertexColors: true,
|
||||||
|
transparent: true,
|
||||||
|
opacity: 1,
|
||||||
|
sizeAttenuation: true,
|
||||||
|
blending: THREE.AdditiveBlending,
|
||||||
|
depthWrite: false,
|
||||||
|
depthTest: false
|
||||||
|
});
|
||||||
|
|
||||||
|
const geometry = new THREE.BufferGeometry();
|
||||||
geometry.setAttribute('position', new THREE.Float32BufferAttribute(positionArray, 3));
|
geometry.setAttribute('position', new THREE.Float32BufferAttribute(positionArray, 3));
|
||||||
geometry.setAttribute('color', new THREE.Float32BufferAttribute(colorArray, 3));
|
geometry.setAttribute('color', new THREE.Float32BufferAttribute(colorArray, 3));
|
||||||
|
|
||||||
const points = new THREE.Points(geometry, material);
|
const points = new THREE.Points(geometry, material);
|
||||||
scene.clear(); // 清除现有内容
|
scene.clear();
|
||||||
scene.add(points);
|
scene.add(points);
|
||||||
|
|
||||||
const positionAttribute = geometry.attributes.position;
|
const positionAttribute = geometry.attributes.position;
|
||||||
@ -360,14 +489,14 @@ export const ParticleImage = ({ src, onLoad, onError }: {
|
|||||||
offsetX = (width - drawWidth) / 2;
|
offsetX = (width - drawWidth) / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 绘制图片
|
// 绘制片
|
||||||
ctx.drawImage(img, offsetX, offsetY, drawWidth, drawHeight);
|
ctx.drawImage(img, offsetX, offsetY, drawWidth, drawHeight);
|
||||||
const imageData = ctx.getImageData(0, 0, width, height);
|
const imageData = ctx.getImageData(0, 0, width, height);
|
||||||
|
|
||||||
const particles: Particle[] = [];
|
const particles: Particle[] = [];
|
||||||
const positionArray = [];
|
const positionArray = [];
|
||||||
const colorArray = [];
|
const colorArray = [];
|
||||||
const samplingGap = Math.ceil(Math.max(width, height) / 100); // 动态采样间隔,确保粒子数量适中
|
const samplingGap = Math.ceil(Math.max(width, height) / 80); // 减少采样密度
|
||||||
|
|
||||||
// 采样图片像素
|
// 采样图片像素
|
||||||
for (let y = 0; y < height; y += samplingGap) {
|
for (let y = 0; y < height; y += samplingGap) {
|
||||||
@ -412,6 +541,22 @@ export const ParticleImage = ({ src, onLoad, onError }: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const size = Math.min(width, height);
|
||||||
|
const scale = size / 200;
|
||||||
|
const particleSize = Math.max(1.2, scale * 1.2);
|
||||||
|
|
||||||
|
const geometry = new THREE.BufferGeometry();
|
||||||
|
const material = new THREE.PointsMaterial({
|
||||||
|
size: particleSize,
|
||||||
|
vertexColors: true,
|
||||||
|
transparent: true,
|
||||||
|
opacity: 1,
|
||||||
|
sizeAttenuation: true,
|
||||||
|
blending: THREE.AdditiveBlending,
|
||||||
|
depthWrite: false,
|
||||||
|
depthTest: false
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
scene.clear();
|
scene.clear();
|
||||||
geometry.setAttribute('position', new THREE.Float32BufferAttribute(positionArray, 3));
|
geometry.setAttribute('position', new THREE.Float32BufferAttribute(positionArray, 3));
|
||||||
@ -487,6 +632,8 @@ export const ParticleImage = ({ src, onLoad, onError }: {
|
|||||||
duration: 0.8
|
duration: 0.8
|
||||||
}, "-=0.6"); // 提前开始消失
|
}, "-=0.6"); // 提前开始消失
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return { particles, positionArray, colorArray, particleSize };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -523,7 +670,7 @@ export const ParticleImage = ({ src, onLoad, onError }: {
|
|||||||
renderer.dispose();
|
renderer.dispose();
|
||||||
}
|
}
|
||||||
// 清除所有 GSAP 动画
|
// 清除所有 GSAP 动画
|
||||||
gsap.killTweensOf(geometry.attributes.position?.array);
|
gsap.killTweensOf('*');
|
||||||
|
|
||||||
// 移除 resize 监听
|
// 移除 resize 监听
|
||||||
if (containerRef.current) {
|
if (containerRef.current) {
|
||||||
|
Loading…
Reference in New Issue
Block a user