Skip to content

Commit 665ffe7

Browse files
committed
feat: 完善测试系统 - 卡片式UI、计时器、答题卡、持久化、评分逻辑
1 parent e471831 commit 665ffe7

17 files changed

Lines changed: 1295 additions & 2565 deletions

css/styles.css

Lines changed: 482 additions & 115 deletions
Large diffs are not rendered by default.

js/app.js

Lines changed: 0 additions & 523 deletions
This file was deleted.

js/components/page-loader.js

Lines changed: 0 additions & 22 deletions
This file was deleted.

js/components/particle-bg.js

Lines changed: 79 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -1,118 +1,102 @@
11
class ParticleBackground {
22
constructor(canvasId) {
33
this.canvas = document.getElementById(canvasId);
4-
this.scene = null;
5-
this.camera = null;
6-
this.renderer = null;
7-
this.particles = null;
8-
this.animationId = null;
4+
if (!this.canvas) return;
5+
6+
this.ctx = this.canvas.getContext('2d');
7+
this.particles = [];
98
this.mouseX = 0;
109
this.mouseY = 0;
11-
this.colors = [
12-
0xed751c, 0x0ea5e9, 0xd946ef,
13-
0x10b981, 0xf59e0b, 0x8b5cf6
14-
];
1510

16-
this.init();
11+
this.resize();
12+
this.initParticles();
13+
this.bindEvents();
14+
this.animate();
1715
}
1816

19-
init() {
20-
if (!this.canvas) return;
21-
22-
const width = window.innerWidth;
23-
const height = window.innerHeight;
24-
25-
this.scene = new THREE.Scene();
26-
this.camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
27-
this.camera.position.z = 50;
28-
29-
this.renderer = new THREE.WebGLRenderer({
30-
canvas: this.canvas,
31-
alpha: true,
32-
antialias: true
33-
});
34-
this.renderer.setSize(width, height);
35-
this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
36-
37-
this.createParticles();
38-
39-
window.addEventListener('resize', () => this.onResize());
40-
window.addEventListener('mousemove', (e) => this.onMouseMove(e));
41-
42-
this.animate();
17+
resize() {
18+
this.canvas.width = window.innerWidth;
19+
this.canvas.height = window.innerHeight;
4320
}
4421

45-
createParticles() {
46-
const particleCount = 300;
47-
const positions = new Float32Array(particleCount * 3);
48-
const colors = new Float32Array(particleCount * 3);
49-
50-
for (let i = 0; i < particleCount; i++) {
51-
positions[i * 3] = (Math.random() - 0.5) * 100;
52-
positions[i * 3 + 1] = (Math.random() - 0.5) * 100;
53-
positions[i * 3 + 2] = (Math.random() - 0.5) * 50;
54-
55-
const color = new THREE.Color(this.colors[Math.floor(Math.random() * this.colors.length)]);
56-
colors[i * 3] = color.r;
57-
colors[i * 3 + 1] = color.g;
58-
colors[i * 3 + 2] = color.b;
22+
initParticles() {
23+
const count = Math.floor((this.canvas.width * this.canvas.height) / 15000);
24+
for (let i = 0; i < count; i++) {
25+
this.particles.push({
26+
x: Math.random() * this.canvas.width,
27+
y: Math.random() * this.canvas.height,
28+
vx: (Math.random() - 0.5) * 0.5,
29+
vy: (Math.random() - 0.5) * 0.5,
30+
size: Math.random() * 2 + 1,
31+
alpha: Math.random() * 0.5 + 0.2,
32+
color: this.getRandomColor()
33+
});
5934
}
60-
61-
const geometry = new THREE.BufferGeometry();
62-
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
63-
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
64-
65-
const material = new THREE.PointsMaterial({
66-
size: 2,
67-
vertexColors: true,
68-
transparent: true,
69-
opacity: 0.8,
70-
sizeAttenuation: true,
71-
blending: THREE.AdditiveBlending
72-
});
73-
74-
this.particles = new THREE.Points(geometry, material);
75-
this.scene.add(this.particles);
7635
}
7736

78-
onResize() {
79-
const width = window.innerWidth;
80-
const height = window.innerHeight;
81-
82-
this.camera.aspect = width / height;
83-
this.camera.updateProjectionMatrix();
84-
this.renderer.setSize(width, height);
37+
getRandomColor() {
38+
const colors = [
39+
'139, 92, 246',
40+
'217, 70, 239',
41+
'244, 114, 182',
42+
'59, 130, 246',
43+
'6, 182, 212',
44+
'16, 185, 129'
45+
];
46+
return colors[Math.floor(Math.random() * colors.length)];
8547
}
8648

87-
onMouseMove(event) {
88-
this.mouseX = (event.clientX / window.innerWidth) * 2 - 1;
89-
this.mouseY = -(event.clientY / window.innerHeight) * 2 + 1;
49+
bindEvents() {
50+
window.addEventListener('resize', () => this.resize());
51+
52+
document.addEventListener('mousemove', (e) => {
53+
this.mouseX = e.clientX;
54+
this.mouseY = e.clientY;
55+
});
9056
}
9157

9258
animate() {
93-
this.animationId = requestAnimationFrame(() => this.animate());
59+
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
9460

95-
if (this.particles) {
96-
this.particles.rotation.x += 0.0005;
97-
this.particles.rotation.y += 0.0008;
61+
this.particles.forEach(p => {
62+
p.x += p.vx;
63+
p.y += p.vy;
9864

99-
this.particles.rotation.x += (this.mouseY * 0.5 - this.particles.rotation.x) * 0.02;
100-
this.particles.rotation.y += (this.mouseX * 0.5 - this.particles.rotation.y) * 0.02;
101-
}
65+
if (p.x < 0 || p.x > this.canvas.width) p.vx *= -1;
66+
if (p.y < 0 || p.y > this.canvas.height) p.vy *= -1;
67+
68+
const dx = this.mouseX - p.x;
69+
const dy = this.mouseY - p.y;
70+
const dist = Math.sqrt(dx * dx + dy * dy);
71+
72+
if (dist < 150) {
73+
const force = (150 - dist) / 150;
74+
p.vx += (dx / dist) * force * 0.02;
75+
p.vy += (dy / dist) * force * 0.02;
76+
}
77+
78+
this.ctx.beginPath();
79+
this.ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
80+
this.ctx.fillStyle = `rgba(${p.color}, ${p.alpha})`;
81+
this.ctx.fill();
82+
});
10283

103-
this.renderer.render(this.scene, this.camera);
104-
}
105-
106-
destroy() {
107-
if (this.animationId) {
108-
cancelAnimationFrame(this.animationId);
109-
}
110-
if (this.renderer) {
111-
this.renderer.dispose();
112-
}
113-
window.removeEventListener('resize', this.onResize);
114-
window.removeEventListener('mousemove', this.onMouseMove);
84+
this.particles.forEach((p1, i) => {
85+
this.particles.slice(i + 1).forEach(p2 => {
86+
const dx = p1.x - p2.x;
87+
const dy = p1.y - p2.y;
88+
const dist = Math.sqrt(dx * dx + dy * dy);
89+
90+
if (dist < 120) {
91+
this.ctx.beginPath();
92+
this.ctx.moveTo(p1.x, p1.y);
93+
this.ctx.lineTo(p2.x, p2.y);
94+
this.ctx.strokeStyle = `rgba(${p1.color}, ${0.1 * (1 - dist / 120)})`;
95+
this.ctx.stroke();
96+
}
97+
});
98+
});
99+
100+
requestAnimationFrame(() => this.animate());
115101
}
116102
}
117-
118-
window.ParticleBackground = ParticleBackground;

0 commit comments

Comments
 (0)