561 lines
21 KiB
HTML
561 lines
21 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>八卦阵 - 太极玄奥秘术</title>
|
|
<style>
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
}
|
|
body {
|
|
overflow: hidden;
|
|
background: #0a0a0f;
|
|
font-family: 'Microsoft YaHei', sans-serif;
|
|
}
|
|
#canvas-container {
|
|
width: 100vw;
|
|
height: 100vh;
|
|
}
|
|
#info {
|
|
position: fixed;
|
|
top: 20px;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
color: #ffd700;
|
|
text-align: center;
|
|
font-size: 14px;
|
|
text-shadow: 0 0 10px #ffd700, 0 0 20px #ffd700;
|
|
pointer-events: none;
|
|
z-index: 100;
|
|
letter-spacing: 3px;
|
|
}
|
|
#mode {
|
|
position: fixed;
|
|
bottom: 30px;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
color: #00ffff;
|
|
font-size: 12px;
|
|
text-shadow: 0 0 5px #00ffff;
|
|
pointer-events: none;
|
|
letter-spacing: 2px;
|
|
}
|
|
.glow-text {
|
|
animation: pulse 2s ease-in-out infinite;
|
|
}
|
|
@keyframes pulse {
|
|
0%, 100% { opacity: 0.8; }
|
|
50% { opacity: 1; }
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="info">
|
|
<div class="glow-text">☯ 八卦阵 · 太极玄奥秘术 ☯</div>
|
|
<div style="font-size: 12px; margin-top: 8px; color: #9d4edd;">点击切换阵法 · 乾坎艮震巽离坤兑</div>
|
|
</div>
|
|
<div id="mode">当前阵法:顺时针旋转</div>
|
|
<div id="canvas-container"></div>
|
|
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
|
<script>
|
|
// 场景设置
|
|
const scene = new THREE.Scene();
|
|
scene.fog = new THREE.FogExp2(0x0a0a1a, 0.02);
|
|
|
|
const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);
|
|
camera.position.set(0, 0, 25);
|
|
|
|
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
|
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
|
|
document.getElementById('canvas-container').appendChild(renderer.domElement);
|
|
|
|
// 颜色定义
|
|
const COLORS = {
|
|
gold: 0xffd700,
|
|
cyan: 0x00ffff,
|
|
purple: 0x9d4edd,
|
|
darkPurple: 0x1a0a2e,
|
|
white: 0xffffff,
|
|
black: 0x000000
|
|
};
|
|
|
|
// 创建发光材质
|
|
function createGlowMaterial(color, opacity = 1) {
|
|
return new THREE.LineBasicMaterial({
|
|
color: color,
|
|
transparent: true,
|
|
opacity: opacity,
|
|
blending: THREE.AdditiveBlending,
|
|
side: THREE.DoubleSide
|
|
});
|
|
}
|
|
|
|
// 创建点发光效果
|
|
function createPointGlow(color, size = 0.3) {
|
|
const geometry = new THREE.SphereGeometry(size, 16, 16);
|
|
const material = new THREE.MeshBasicMaterial({
|
|
color: color,
|
|
transparent: true,
|
|
opacity: 0.8,
|
|
blending: THREE.AdditiveBlending
|
|
});
|
|
return new THREE.Mesh(geometry, material);
|
|
}
|
|
|
|
// ===== 太极图 =====
|
|
const taijiGroup = new THREE.Group();
|
|
|
|
// 外圆
|
|
const outerCircleGeometry = new THREE.RingGeometry(3.8, 4, 64);
|
|
const outerCircleMaterial = new THREE.MeshBasicMaterial({
|
|
color: COLORS.gold,
|
|
transparent: true,
|
|
opacity: 0.6,
|
|
side: THREE.DoubleSide,
|
|
blending: THREE.AdditiveBlending
|
|
});
|
|
const outerCircle = new THREE.Mesh(outerCircleGeometry, outerCircleMaterial);
|
|
outerCircle.rotation.x = Math.PI / 2;
|
|
taijiGroup.add(outerCircle);
|
|
|
|
// 太极S曲线
|
|
const sCurvePoints = [];
|
|
for (let i = 0; i <= 100; i++) {
|
|
const t = i / 100;
|
|
const angle = t * Math.PI * 2;
|
|
const r = 2 + Math.cos(angle) * 2;
|
|
const x = Math.cos(angle) * r * 0.5;
|
|
const y = Math.sin(angle) * r;
|
|
sCurvePoints.push(new THREE.Vector3(x, y, 0));
|
|
}
|
|
const sCurveGeometry = new THREE.BufferGeometry().setFromPoints(sCurvePoints);
|
|
const sCurveMaterial = createGlowMaterial(COLORS.white, 0.9);
|
|
const sCurve = new THREE.Line(sCurveGeometry, sCurveMaterial);
|
|
taijiGroup.add(sCurve);
|
|
|
|
// 阴阳鱼眼
|
|
const yinEye = createPointGlow(COLORS.white, 0.4);
|
|
yinEye.position.set(0, 1, 0.1);
|
|
taijiGroup.add(yinEye);
|
|
|
|
const yangEye = createPointGlow(COLORS.black, 0.4);
|
|
yangEye.position.set(0, -1, 0.1);
|
|
taijiGroup.add(yangEye);
|
|
|
|
// 阴阳半圆(简化版)
|
|
const yinGeometry = new THREE.CircleGeometry(4, 64, Math.PI / 2, Math.PI);
|
|
const yinMaterial = new THREE.MeshBasicMaterial({
|
|
color: COLORS.black,
|
|
transparent: true,
|
|
opacity: 0.7,
|
|
side: THREE.DoubleSide
|
|
});
|
|
const yinCircle = new THREE.Mesh(yinGeometry, yinMaterial);
|
|
taijiGroup.add(yinCircle);
|
|
|
|
const yangGeometry = new THREE.CircleGeometry(4, 64, -Math.PI / 2, Math.PI);
|
|
const yangMaterial = new THREE.MeshBasicMaterial({
|
|
color: COLORS.white,
|
|
transparent: true,
|
|
opacity: 0.7,
|
|
side: THREE.DoubleSide
|
|
});
|
|
const yangCircle = new THREE.Mesh(yangGeometry, yangMaterial);
|
|
taijiGroup.add(yangCircle);
|
|
|
|
// 内圈装饰
|
|
const innerRingGeometry = new THREE.RingGeometry(3.5, 3.6, 64);
|
|
const innerRingMaterial = new THREE.MeshBasicMaterial({
|
|
color: COLORS.cyan,
|
|
transparent: true,
|
|
opacity: 0.4,
|
|
side: THREE.DoubleSide,
|
|
blending: THREE.AdditiveBlending
|
|
});
|
|
const innerRing = new THREE.Mesh(innerRingGeometry, innerRingMaterial);
|
|
innerRing.rotation.x = Math.PI / 2;
|
|
taijiGroup.add(innerRing);
|
|
|
|
scene.add(taijiGroup);
|
|
|
|
// ===== 八卦符号 =====
|
|
const baguaGroup = new THREE.Group();
|
|
const guaGroups = [];
|
|
|
|
// 八卦数据:名称、符号(三爻)、角度位置
|
|
const baguaData = [
|
|
{ name: '乾', symbol: [1, 1, 1], angle: 0, color: COLORS.gold },
|
|
{ name: '兑', symbol: [0, 1, 1], angle: Math.PI / 4, color: COLORS.cyan },
|
|
{ name: '离', symbol: [1, 0, 1], angle: Math.PI / 2, color: COLORS.gold },
|
|
{ name: '震', symbol: [0, 0, 1], angle: Math.PI * 3 / 4, color: COLORS.cyan },
|
|
{ name: '巽', symbol: [1, 1, 0], angle: Math.PI, color: COLORS.purple },
|
|
{ name: '坎', symbol: [0, 1, 0], angle: Math.PI * 5 / 4, color: COLORS.cyan },
|
|
{ name: '艮', symbol: [1, 0, 0], angle: Math.PI * 3 / 2, color: COLORS.purple },
|
|
{ name: '坤', symbol: [0, 0, 0], angle: Math.PI * 7 / 4, color: COLORS.gold }
|
|
];
|
|
|
|
// 创建单个卦象符号
|
|
function createGuaSymbol(gua, radius) {
|
|
const group = new THREE.Group();
|
|
|
|
// 三爻
|
|
const yaoHeight = 0.8;
|
|
const yaoWidth = 1.2;
|
|
const yaoGap = 0.3;
|
|
|
|
gua.symbol.forEach((yao, index) => {
|
|
const y = (1 - index) * (yaoHeight + yaoGap);
|
|
|
|
if (yao === 1) {
|
|
// 阳爻 — 实线
|
|
const points = [
|
|
new THREE.Vector3(-yaoWidth / 2, y, 0),
|
|
new THREE.Vector3(yaoWidth / 2, y, 0)
|
|
];
|
|
const geometry = new THREE.BufferGeometry().setFromPoints(points);
|
|
const material = createGlowMaterial(gua.color, 0.9);
|
|
const line = new THREE.Line(geometry, material);
|
|
line.userData = { isYang: true };
|
|
group.add(line);
|
|
|
|
// 发光增强
|
|
const glowPoints = [
|
|
new THREE.Vector3(-yaoWidth / 2 - 0.05, y, 0),
|
|
new THREE.Vector3(yaoWidth / 2 + 0.05, y, 0)
|
|
];
|
|
const glowGeometry = new THREE.BufferGeometry().setFromPoints(glowPoints);
|
|
const glowMaterial = createGlowMaterial(gua.color, 0.3);
|
|
const glowLine = new THREE.Line(glowGeometry, glowMaterial);
|
|
glowLine.scale.set(1.2, 1.2, 1.2);
|
|
group.add(glowLine);
|
|
} else {
|
|
// 阴爻 - - 断线
|
|
const segmentWidth = yaoWidth * 0.4;
|
|
const gap = yaoWidth * 0.2;
|
|
|
|
// 左段
|
|
const leftPoints = [
|
|
new THREE.Vector3(-yaoWidth / 2, y, 0),
|
|
new THREE.Vector3(-yaoWidth / 2 + segmentWidth, y, 0)
|
|
];
|
|
const leftGeometry = new THREE.BufferGeometry().setFromPoints(leftPoints);
|
|
const leftMaterial = createGlowMaterial(gua.color, 0.9);
|
|
group.add(new THREE.Line(leftGeometry, leftMaterial));
|
|
|
|
// 右段
|
|
const rightPoints = [
|
|
new THREE.Vector3(yaoWidth / 2 - segmentWidth, y, 0),
|
|
new THREE.Vector3(yaoWidth / 2, y, 0)
|
|
];
|
|
const rightGeometry = new THREE.BufferGeometry().setFromPoints(rightPoints);
|
|
group.add(new THREE.Line(rightGeometry, leftMaterial));
|
|
}
|
|
});
|
|
|
|
// 卦象外框
|
|
const frameSize = 1.5;
|
|
const framePoints = [
|
|
new THREE.Vector3(-frameSize, -frameSize * 1.2, 0),
|
|
new THREE.Vector3(frameSize, -frameSize * 1.2, 0),
|
|
new THREE.Vector3(frameSize, frameSize * 1.2, 0),
|
|
new THREE.Vector3(-frameSize, frameSize * 1.2, 0),
|
|
new THREE.Vector3(-frameSize, -frameSize * 1.2, 0)
|
|
];
|
|
const frameGeometry = new THREE.BufferGeometry().setFromPoints(framePoints);
|
|
const frameMaterial = createGlowMaterial(gua.color, 0.4);
|
|
const frame = new THREE.Line(frameGeometry, frameMaterial);
|
|
group.add(frame);
|
|
|
|
// 卦名标记点
|
|
const nameDot = createPointGlow(gua.color, 0.15);
|
|
nameDot.position.set(0, frameSize * 1.4, 0);
|
|
group.add(nameDot);
|
|
|
|
// 设置位置
|
|
const x = Math.cos(gua.angle) * radius;
|
|
const z = Math.sin(gua.angle) * radius;
|
|
group.position.set(x, 0, z);
|
|
|
|
// 让卦象面向中心
|
|
group.lookAt(0, 0, 0);
|
|
group.rotateY(Math.PI);
|
|
|
|
group.userData = {
|
|
gua: gua,
|
|
originalAngle: gua.angle,
|
|
radius: radius,
|
|
rotationSpeed: (Math.random() - 0.5) * 0.02
|
|
};
|
|
|
|
return group;
|
|
}
|
|
|
|
// 创建所有八卦
|
|
const mainRadius = 10;
|
|
baguaData.forEach(gua => {
|
|
const guaGroup = createGuaSymbol(gua, mainRadius);
|
|
guaGroups.push(guaGroup);
|
|
baguaGroup.add(guaGroup);
|
|
});
|
|
|
|
scene.add(baguaGroup);
|
|
|
|
// ===== 能量连接线 =====
|
|
const energyLinesGroup = new THREE.Group();
|
|
const energyLines = [];
|
|
|
|
function createEnergyLine(start, end, color) {
|
|
const points = [start, end];
|
|
const geometry = new THREE.BufferGeometry().setFromPoints(points);
|
|
const material = createGlowMaterial(color, 0.3);
|
|
const line = new THREE.Line(geometry, material);
|
|
return line;
|
|
}
|
|
|
|
// 相邻卦象间的连接线
|
|
for (let i = 0; i < guaGroups.length; i++) {
|
|
const current = guaGroups[i];
|
|
const next = guaGroups[(i + 1) % guaGroups.length];
|
|
|
|
const line = createEnergyLine(
|
|
current.position.clone(),
|
|
next.position.clone(),
|
|
COLORS.purple
|
|
);
|
|
energyLines.push({
|
|
line: line,
|
|
startIndex: i,
|
|
endIndex: (i + 1) % guaGroups.length
|
|
});
|
|
energyLinesGroup.add(line);
|
|
}
|
|
|
|
// 对角线连接(五行相生)
|
|
const diagonalPairs = [[0, 4], [1, 5], [2, 6], [3, 7]];
|
|
diagonalPairs.forEach(([a, b]) => {
|
|
const line = createEnergyLine(
|
|
guaGroups[a].position.clone(),
|
|
guaGroups[b].position.clone(),
|
|
COLORS.cyan
|
|
);
|
|
line.material.opacity = 0.15;
|
|
energyLines.push({
|
|
line: line,
|
|
startIndex: a,
|
|
endIndex: b
|
|
});
|
|
energyLinesGroup.add(line);
|
|
});
|
|
|
|
// 中心到各卦象的能量线
|
|
guaGroups.forEach((gua, index) => {
|
|
const centerPoint = new THREE.Vector3(0, 0, 0);
|
|
const guaPoint = gua.position.clone().multiplyScalar(0.4);
|
|
|
|
const points = [];
|
|
const segments = 20;
|
|
for (let i = 0; i <= segments; i++) {
|
|
const t = i / segments;
|
|
const point = centerPoint.clone().lerp(guaPoint, t);
|
|
// 添加一些波动
|
|
point.y += Math.sin(t * Math.PI * 3) * 0.3;
|
|
points.push(point);
|
|
}
|
|
|
|
const geometry = new THREE.BufferGeometry().setFromPoints(points);
|
|
const material = createGlowMaterial(gua.userData.gua.color, 0.25);
|
|
const line = new THREE.Line(geometry, material);
|
|
energyLinesGroup.add(line);
|
|
});
|
|
|
|
scene.add(energyLinesGroup);
|
|
|
|
// ===== 外围装饰环 =====
|
|
const outerRingGroup = new THREE.Group();
|
|
|
|
// 主外环
|
|
const mainRingGeometry = new THREE.RingGeometry(mainRadius - 0.5, mainRadius + 0.5, 64);
|
|
const mainRingMaterial = new THREE.MeshBasicMaterial({
|
|
color: COLORS.purple,
|
|
transparent: true,
|
|
opacity: 0.2,
|
|
side: THREE.DoubleSide,
|
|
blending: THREE.AdditiveBlending
|
|
});
|
|
const mainRing = new THREE.Mesh(mainRingGeometry, mainRingMaterial);
|
|
mainRing.rotation.x = Math.PI / 2;
|
|
outerRingGroup.add(mainRing);
|
|
|
|
// 装饰符文点
|
|
for (let i = 0; i < 24; i++) {
|
|
const angle = (i / 24) * Math.PI * 2;
|
|
const x = Math.cos(angle) * (mainRadius + 1.5);
|
|
const z = Math.sin(angle) * (mainRadius + 1.5);
|
|
|
|
const dot = createPointGlow(i % 2 === 0 ? COLORS.gold : COLORS.cyan, 0.08);
|
|
dot.position.set(x, 0, z);
|
|
outerRingGroup.add(dot);
|
|
}
|
|
|
|
// 内圈旋转环
|
|
const innerRotateRingGeometry = new THREE.RingGeometry(5.5, 5.7, 64);
|
|
const innerRotateRing = new THREE.Mesh(innerRotateRingGeometry, mainRingMaterial.clone());
|
|
innerRotateRing.material.color.setHex(COLORS.cyan);
|
|
innerRotateRing.material.opacity = 0.3;
|
|
innerRotateRing.rotation.x = Math.PI / 2;
|
|
outerRingGroup.add(innerRotateRing);
|
|
|
|
scene.add(outerRingGroup);
|
|
|
|
// ===== 粒子效果 =====
|
|
const particlesGeometry = new THREE.BufferGeometry();
|
|
const particlesCount = 200;
|
|
const posArray = new Float32Array(particlesCount * 3);
|
|
|
|
for (let i = 0; i < particlesCount * 3; i += 3) {
|
|
const angle = Math.random() * Math.PI * 2;
|
|
const radius = 6 + Math.random() * 8;
|
|
posArray[i] = Math.cos(angle) * radius;
|
|
posArray[i + 1] = (Math.random() - 0.5) * 4;
|
|
posArray[i + 2] = Math.sin(angle) * radius;
|
|
}
|
|
|
|
particlesGeometry.setAttribute('position', new THREE.BufferAttribute(posArray, 3));
|
|
|
|
const particlesMaterial = new THREE.PointsMaterial({
|
|
size: 0.05,
|
|
color: COLORS.gold,
|
|
transparent: true,
|
|
opacity: 0.6,
|
|
blending: THREE.AdditiveBlending
|
|
});
|
|
|
|
const particles = new THREE.Points(particlesGeometry, particlesMaterial);
|
|
scene.add(particles);
|
|
|
|
// ===== 动画状态 =====
|
|
let mode = 0; // 0: 顺时针, 1: 逆时针, 2: 内外翻转
|
|
const modes = ['顺时针旋转', '逆时针旋转', '内外翻转'];
|
|
|
|
// 点击切换
|
|
document.addEventListener('click', () => {
|
|
mode = (mode + 1) % 3;
|
|
document.getElementById('mode').textContent = `当前阵法:${modes[mode]}`;
|
|
});
|
|
|
|
// 鼠标移动视差效果
|
|
let mouseX = 0, mouseY = 0;
|
|
document.addEventListener('mousemove', (e) => {
|
|
mouseX = (e.clientX / window.innerWidth - 0.5) * 2;
|
|
mouseY = (e.clientY / window.innerHeight - 0.5) * 2;
|
|
});
|
|
|
|
// 动画循环
|
|
const clock = new THREE.Clock();
|
|
|
|
function animate() {
|
|
requestAnimationFrame(animate);
|
|
|
|
const time = clock.getElapsedTime();
|
|
const delta = clock.getDelta();
|
|
|
|
// 太极图旋转
|
|
taijiGroup.rotation.y = time * 0.1;
|
|
taijiGroup.rotation.z = Math.sin(time * 0.5) * 0.05;
|
|
|
|
// 内圈环反向旋转
|
|
innerRotateRing.rotation.z = -time * 0.3;
|
|
|
|
// 八卦整体旋转(根据模式)
|
|
let rotationSpeed = 0.05;
|
|
if (mode === 1) rotationSpeed = -0.05;
|
|
|
|
baguaGroup.rotation.y += rotationSpeed * 0.02;
|
|
outerRingGroup.rotation.y += rotationSpeed * 0.01;
|
|
|
|
// 各卦象自转
|
|
guaGroups.forEach((gua, index) => {
|
|
const offset = index * (Math.PI / 4);
|
|
|
|
if (mode === 2) {
|
|
// 内外翻转模式
|
|
const flipRadius = mainRadius + Math.sin(time * 0.5 + offset) * 3;
|
|
const angle = gua.userData.originalAngle + baguaGroup.rotation.y;
|
|
gua.position.x = Math.cos(angle) * flipRadius;
|
|
gua.position.z = Math.sin(angle) * flipRadius;
|
|
gua.lookAt(0, 0, 0);
|
|
gua.rotateY(Math.PI);
|
|
}
|
|
|
|
// 自转
|
|
gua.children.forEach(child => {
|
|
if (child.userData.isYang) {
|
|
child.rotation.z = time * 0.2;
|
|
}
|
|
});
|
|
});
|
|
|
|
// 更新能量线
|
|
energyLines.forEach(conn => {
|
|
const start = guaGroups[conn.startIndex].position.clone();
|
|
const end = guaGroups[conn.endIndex].position.clone();
|
|
conn.line.geometry.setFromPoints([start, end]);
|
|
});
|
|
|
|
// 粒子动画
|
|
particles.rotation.y = time * 0.02;
|
|
const positions = particles.geometry.attributes.position.array;
|
|
for (let i = 1; i < positions.length; i += 3) {
|
|
positions[i] += Math.sin(time + positions[i - 1]) * 0.002;
|
|
}
|
|
particles.geometry.attributes.position.needsUpdate = true;
|
|
|
|
// 视差效果
|
|
camera.position.x += (mouseX * 2 - camera.position.x) * 0.02;
|
|
camera.position.y += (-mouseY * 2 - camera.position.y) * 0.02;
|
|
camera.lookAt(0, 0, 0);
|
|
|
|
// 颜色脉动
|
|
outerCircle.material.opacity = 0.4 + Math.sin(time * 2) * 0.2;
|
|
mainRing.material.opacity = 0.15 + Math.sin(time * 1.5) * 0.1;
|
|
|
|
renderer.render(scene, camera);
|
|
}
|
|
|
|
animate();
|
|
|
|
// 窗口大小调整
|
|
window.addEventListener('resize', () => {
|
|
camera.aspect = window.innerWidth / window.innerHeight;
|
|
camera.updateProjectionMatrix();
|
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
});
|
|
|
|
// 添加星空背景效果
|
|
const starsGeometry = new THREE.BufferGeometry();
|
|
const starsCount = 1000;
|
|
const starsPos = new Float32Array(starsCount * 3);
|
|
|
|
for (let i = 0; i < starsCount * 3; i++) {
|
|
starsPos[i] = (Math.random() - 0.5) * 200;
|
|
}
|
|
|
|
starsGeometry.setAttribute('position', new THREE.BufferAttribute(starsPos, 3));
|
|
const starsMaterial = new THREE.PointsMaterial({
|
|
size: 0.1,
|
|
color: 0xffffff,
|
|
transparent: true,
|
|
opacity: 0.5
|
|
});
|
|
|
|
const stars = new THREE.Points(starsGeometry, starsMaterial);
|
|
scene.add(stars);
|
|
</script>
|
|
</body>
|
|
</html>
|