console
// 创建容器元素
const container = document.createElement('div');
container.id = 'container';
container.style.position = 'relative';
container.style.width = '400px';
container.style.height = '400px';
container.style.margin = '0 auto';
// 创建六边形元素
const hexagon = document.createElement('div');
hexagon.id = 'hexagon';
hexagon.style.position = 'absolute';
hexagon.style.width = '100%';
hexagon.style.height = '100%';
hexagon.style.backgroundColor = '#333';
hexagon.style.clipPath = 'polygon(25% 5%, 75% 5%, 95% 50%, 75% 95%, 25% 95%, 5% 50%)';
hexagon.style.transformOrigin = 'center';
// 创建球元素
const ball = document.createElement('div');
ball.id = 'ball';
ball.style.position = 'absolute';
ball.style.width = '30px';
ball.style.height = '30px';
ball.style.backgroundColor = '#e74c3c';
ball.style.borderRadius = '50%';
ball.style.transformOrigin = 'center';
// 添加到容器和DOM
container.appendChild(hexagon);
container.appendChild(ball);
document.body.appendChild(container);
// 设置页面样式
document.body.style.margin = '0';
document.body.style.overflow = 'hidden';
document.body.style.display = 'flex';
document.body.style.justifyContent = 'center';
document.body.style.alignItems = 'center';
document.body.style.height = '100vh';
document.body.style.backgroundColor = '#f0f0f0';
// 六边形参数
const hexagonSize = 400;
const hexagonPoints = [
{x: 100, y: 20}, // 左上
{x: 300, y: 20}, // 右上
{x: 380, y: 200}, // 右
{x: 300, y: 380}, // 右下
{x: 100, y: 380}, // 左下
{x: 20, y: 200} // 左
];
// 球参数
const ballSize = 30;
let ballPos = {x: 200, y: 200};
let ballVel = {x: 0, y: 0};
// 物理参数
const gravity = 0.2;
const friction = 0.99;
const bounceFactor = 0.8;
const rotationSpeed = 0.5;
let rotationAngle = 0;
// 获取旋转后的六边形顶点
function getRotatedHexagonPoints() {
const centerX = hexagonSize / 2;
const centerY = hexagonSize / 2;
return hexagonPoints.map(point => {
// 转换为以中心为原点的坐标
const x = point.x - centerX;
const y = point.y - centerY;
// 旋转
const rotatedX = x * Math.cos(rotationAngle) - y * Math.sin(rotationAngle);
const rotatedY = x * Math.sin(rotationAngle) + y * Math.cos(rotationAngle);
// 转换回绝对坐标
return {
x: rotatedX + centerX,
y: rotatedY + centerY
};
});
}
// 检查球与六边形边的碰撞
function checkCollision() {
const points = getRotatedHexagonPoints();
for (let i = 0; i < points.length; i++) {
const p1 = points[i];
const p2 = points[(i + 1) % points.length];
// 计算边的向量
const edge = {
x: p2.x - p1.x,
y: p2.y - p1.y
};
// 边的法向量(指向六边形内部)
const normal = {
x: -edge.y,
y: edge.x
};
// 归一化法向量
const length = Math.sqrt(normal.x * normal.x + normal.y * normal.y);
normal.x /= length;
normal.y /= length;
// 球中心到边的距离
const ballToEdge = {
x: ballPos.x - p1.x,
y: ballPos.y - p1.y
};
// 计算投影距离
const distance = ballToEdge.x * normal.x + ballToEdge.y * normal.y;
// 如果距离小于球半径,则发生碰撞
if (distance < ballSize / 2 && distance > -ballSize / 2) {
// 检查球是否在边的范围内
const edgeLength = Math.sqrt(edge.x * edge.x + edge.y * edge.y);
const edgeDir = {
x: edge.x / edgeLength,
y: edge.y / edgeLength
};
const projection = ballToEdge.x * edgeDir.x + ballToEdge.y * edgeDir.y;
if (projection >= 0 && projection <= edgeLength) {
// 碰撞响应
const penetration = ballSize / 2 - distance;
// 移动球以避免穿透
ballPos.x += normal.x * penetration;
ballPos.y += normal.y * penetration;
// 计算速度在法向量上的投影
const velocityProjection = ballVel.x * normal.x + ballVel.y * normal.y;
// 如果球正在向边移动(避免粘滞)
if (velocityProjection < 0) {
// 反弹 - 反转法线方向的速度分量
ballVel.x -= (1 + bounceFactor) * velocityProjection * normal.x;
ballVel.y -= (1 + bounceFactor) * velocityProjection * normal.y;
// 添加摩擦力 - 只影响切线方向的速度
const tangentVel = {
x: ballVel.x - velocityProjection * normal.x,
y: ballVel.y - velocityProjection * normal.y
};
ballVel.x = tangentVel.x * friction + velocityProjection * normal.x * bounceFactor;
ballVel.y = tangentVel.y * friction + velocityProjection * normal.y * bounceFactor;
}
return true;
}
}
}
return false;
}
// 动画循环
function animate() {
// 旋转六边形
rotationAngle += rotationSpeed * Math.PI / 500;
hexagon.style.transform = `rotate(${rotationAngle}rad)`;
// 应用重力
ballVel.y += gravity;
// 更新球位置
ballPos.x += ballVel.x;
ballPos.y += ballVel.y;
// 检查碰撞
checkCollision();
// 更新球的位置
ball.style.left = `${ballPos.x - ballSize / 2}px`;
ball.style.top = `${ballPos.y - ballSize / 2}px`;
requestAnimationFrame(animate);
}
// 初始化球位置和速度
ballPos = {x: 200, y: 250};
ballVel = {x: 2, y: 0};
// 开始动画
animate();
<!-- <!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.1/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.1/addons/p5.sound.min.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
<meta charset="utf-8" />
</head>
<body>
<main>
</main>
<script src="gTnKp.js"></script>
</body>
</html> -->
<!-- <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>震撼3D粒子交互动画</title>
<style>
body {
margin: 0;
overflow: hidden;
background: #000;
}
canvas {
display: block;
}
.info {
position: absolute;
top: 20px;
left: 20px;
color: white;
font-family: Arial, sans-serif;
pointer-events: none;
text-shadow: 0 0 5px #000;
}
</style>
</head>
<body>
<div class="info">移动鼠标与粒子互动</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.7/dat.gui.min.js"></script>
<script>
// 初始化场景、相机和渲染器
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
document.body.appendChild(renderer.domElement);
// 添加环境光和点光源
const ambientLight = new THREE.AmbientLight(0x404040);
scene.add(ambientLight);
const pointLight = new THREE.PointLight(0xffffff, 1, 100);
pointLight.position.set(10, 10, 10);
scene.add(pointLight);
// 创建粒子系统
const particleCount = 15000;
const particles = new THREE.BufferGeometry();
const positions = new Float32Array(particleCount * 3);
const colors = new Float32Array(particleCount * 3);
const sizes = new Float32Array(particleCount);
// 随机位置、颜色和大小
for (let i = 0; i < particleCount; i++) {
// 初始位置在一个球体内
const radius = 20;
const theta = Math.random() * Math.PI * 2;
const phi = Math.random() * Math.PI;
positions[i * 3] = radius * Math.sin(phi) * Math.cos(theta);
positions[i * 3 + 1] = radius * Math.sin(phi) * Math.sin(theta);
positions[i * 3 + 2] = radius * Math.cos(phi);
// 随机颜色
colors[i * 3] = Math.random() * 0.5 + 0.5; // 红色分量
colors[i * 3 + 1] = Math.random() * 0.3 + 0.2; // 绿色分量
colors[i * 3 + 2] = Math.random() * 0.5 + 0.5; // 蓝色分量
// 随机大小
sizes[i] = Math.random() * 0.5 + 0.5;
}
particles.setAttribute('position', new THREE.BufferAttribute(positions, 3));
particles.setAttribute('color', new THREE.BufferAttribute(colors, 3));
particles.setAttribute('size', new THREE.BufferAttribute(sizes, 1));
// 创建粒子材质
const particleMaterial = new THREE.PointsMaterial({
size: 0.2,
vertexColors: true,
transparent: true,
opacity: 0.8,
blending: THREE.AdditiveBlending,
sizeAttenuation: true
});
// 创建粒子系统
const particleSystem = new THREE.Points(particles, particleMaterial);
scene.add(particleSystem);
// 鼠标交互变量
const mouse = new THREE.Vector2();
const mouseTarget = new THREE.Vector2();
const windowHalf = new THREE.Vector2(window.innerWidth / 2, window.innerHeight / 2);
// 鼠标移动事件
document.addEventListener('mousemove', (event) => {
mouse.x = (event.clientX - windowHalf.x) / windowHalf.x;
mouse.y = (event.clientY - windowHalf.y) / windowHalf.y;
// 平滑跟随鼠标
mouseTarget.x = (event.clientX - windowHalf.x) / windowHalf.x;
mouseTarget.y = (event.clientY - windowHalf.y) / windowHalf.y;
});
// 窗口大小调整
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
windowHalf.set(window.innerWidth / 2, window.innerHeight / 2);
});
// 动画变量
let time = 0;
const originalPositions = particles.attributes.position.array.slice();
const noise = new Float32Array(particleCount * 3);
// 生成一些随机噪声
for (let i = 0; i < particleCount * 3; i++) {
noise[i] = Math.random() * 2 - 1;
}
// 相机位置
camera.position.z = 30;
// 动画循环
function animate() {
requestAnimationFrame(animate);
time += 0.005;
// 平滑鼠标移动
mouse.x += (mouseTarget.x - mouse.x) * 0.05;
mouse.y += (mouseTarget.y - mouse.y) * 0.05;
// 更新粒子位置
const positions = particles.attributes.position.array;
for (let i = 0; i < particleCount; i++) {
const i3 = i * 3;
// 原始位置
const ox = originalPositions[i3];
const oy = originalPositions[i3 + 1];
const oz = originalPositions[i3 + 2];
// 鼠标影响
const dx = mouse.x * 10;
const dy = mouse.y * 10;
// 距离计算
const dist = Math.sqrt(
Math.pow(positions[i3] - dx, 2) +
Math.pow(positions[i3 + 1] - dy, 2)
);
// 鼠标排斥力
const force = Math.max(0, 1 - dist / 15);
const angle = Math.atan2(positions[i3 + 1] - dy, positions[i3] - dx);
// 应用各种力
positions[i3] = ox +
Math.sin(time + oz * 0.1) * 2 +
noise[i3] * 0.3 +
Math.cos(angle) * force * 10;
positions[i3 + 1] = oy +
Math.cos(time + ox * 0.1) * 2 +
noise[i3 + 1] * 0.3 +
Math.sin(angle) * force * 10;
positions[i3 + 2] = oz +
Math.sin(time + oy * 0.1) * 2 +
noise[i3 + 2] * 0.3;
}
particles.attributes.position.needsUpdate = true;
// 旋转粒子系统
particleSystem.rotation.x = time * 0.1;
particleSystem.rotation.y = time * 0.15;
// 渲染场景
renderer.render(scene, camera);
}
animate();
</script>
</body>
</html> -->