Skip to content

Commit ce2dec9

Browse files
authored
Create script.js
1 parent fc2b35b commit ce2dec9

1 file changed

Lines changed: 272 additions & 0 deletions

File tree

script.js

Lines changed: 272 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
document.addEventListener('DOMContentLoaded', () => {
2+
// --- Particle Animation ---
3+
const canvas = document.getElementById('particles');
4+
const ctx = canvas.getContext('2d');
5+
let particlesArray = [];
6+
let mouse = { x: null, y: null, radius: 100 };
7+
let currentTheme = localStorage.getItem('theme') || 'default';
8+
9+
// Helper do pobierania właściwości motywu
10+
function getThemeProps(theme) {
11+
switch(theme) {
12+
case 'space':
13+
return {
14+
particleCount: 200,
15+
baseSizeMin: 0.5,
16+
baseSizeMax: 1.5,
17+
speedMin: -0.2,
18+
speedMax: 0.2,
19+
opacityMin: 0.4,
20+
opacityMax: 0.6,
21+
useHue: false,
22+
connectLines: false,
23+
glowEffect: true
24+
};
25+
case 'cyber':
26+
return {
27+
particleCount: 120,
28+
baseSizeMin: 0.8,
29+
baseSizeMax: 2.2,
30+
speedMin: -0.25,
31+
speedMax: 0.25,
32+
opacityMin: 0.5,
33+
opacityMax: 0.9,
34+
useHue: true,
35+
hueMin: 180,
36+
hueMax: 300,
37+
connectLines: true,
38+
lineDistance: 120,
39+
glowEffect: true
40+
};
41+
default: // default
42+
return {
43+
particleCount: 50,
44+
baseSizeMin: 1,
45+
baseSizeMax: 2,
46+
speedMin: -0.15,
47+
speedMax: 0.15,
48+
opacityMin: 0.2,
49+
opacityMax: 0.4,
50+
useHue: true,
51+
hueMin: 200,
52+
hueMax: 230,
53+
connectLines: true,
54+
lineDistance: 100,
55+
glowEffect: false
56+
};
57+
}
58+
}
59+
60+
class Particle {
61+
constructor(themeProps) {
62+
this.themeProps = themeProps;
63+
this.x = Math.random() * canvas.width;
64+
this.y = Math.random() * canvas.height;
65+
this.size = Math.random() * (themeProps.baseSizeMax - themeProps.baseSizeMin) + themeProps.baseSizeMin;
66+
this.baseSize = this.size;
67+
this.speedX = Math.random() * (themeProps.speedMax - themeProps.speedMin) + themeProps.speedMin;
68+
this.speedY = Math.random() * (themeProps.speedMax - themeProps.speedMin) + themeProps.speedMin;
69+
this.opacity = Math.random() * (themeProps.opacityMax - themeProps.opacityMin) + themeProps.opacityMin;
70+
if (themeProps.useHue) {
71+
this.hue = Math.random() * (themeProps.hueMax - themeProps.hueMin) + themeProps.hueMin;
72+
} else {
73+
this.hue = 0; // nieużywane dla space
74+
}
75+
this.glow = themeProps.glowEffect;
76+
}
77+
78+
update(themeProps, mouse) {
79+
this.x += this.speedX;
80+
this.y += this.speedY;
81+
82+
// Spadek rozmiaru/opacity tylko dla domyślnego motywu
83+
if (currentTheme === 'default') {
84+
if (this.size > 0.5) this.size -= 0.003;
85+
if (this.opacity > 0.2) this.opacity -= 0.0003;
86+
}
87+
88+
// Interakcja z myszką tylko dla domyślnego
89+
if (currentTheme === 'default' && mouse.x !== null && mouse.y !== null) {
90+
const dx = mouse.x - this.x;
91+
const dy = mouse.y - this.y;
92+
const distance = Math.sqrt(dx * dx + dy * dy);
93+
if (distance < mouse.radius) {
94+
const force = (mouse.radius - distance) / mouse.radius;
95+
this.speedX += dx * force * 0.01;
96+
this.speedY += dy * force * 0.01;
97+
this.size = this.baseSize + force * 1.5;
98+
}
99+
}
100+
101+
// Odbijanie od krawędzi
102+
if (this.x < 0 || this.x > canvas.width) this.speedX *= -1;
103+
if (this.y < 0 || this.y > canvas.height) this.speedY *= -1;
104+
}
105+
106+
draw(ctx) {
107+
if (this.themeProps.useHue) {
108+
ctx.fillStyle = `hsla(${this.hue}, 70%, 60%, ${this.opacity})`;
109+
} else {
110+
// Dla space: białe cząstki
111+
ctx.fillStyle = `hsla(0, 0%, 100%, ${this.opacity})`;
112+
}
113+
114+
if (this.glow) {
115+
ctx.shadowBlur = 6;
116+
ctx.shadowColor = this.themeProps.useHue ? `hsl(${this.hue}, 80%, 60%)` : 'white';
117+
} else {
118+
ctx.shadowBlur = 0;
119+
}
120+
121+
ctx.beginPath();
122+
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
123+
ctx.fill();
124+
}
125+
}
126+
127+
function connectParticles(particlesArray, themeProps) {
128+
if (!themeProps.connectLines) return;
129+
const distanceLimit = themeProps.lineDistance || 100;
130+
for (let i = 0; i < particlesArray.length; i++) {
131+
for (let j = i + 1; j < particlesArray.length; j++) {
132+
const dx = particlesArray[i].x - particlesArray[j].x;
133+
const dy = particlesArray[i].y - particlesArray[j].y;
134+
const distance = Math.sqrt(dx * dx + dy * dy);
135+
if (distance < distanceLimit) {
136+
const opacity = (1 - distance / distanceLimit) * 0.2;
137+
if (themeProps.useHue) {
138+
ctx.strokeStyle = `hsla(${particlesArray[i].hue}, 70%, 60%, ${opacity})`;
139+
} else {
140+
ctx.strokeStyle = `rgba(255, 255, 255, ${opacity})`;
141+
}
142+
ctx.lineWidth = 0.5;
143+
ctx.beginPath();
144+
ctx.moveTo(particlesArray[i].x, particlesArray[i].y);
145+
ctx.lineTo(particlesArray[j].x, particlesArray[j].y);
146+
ctx.stroke();
147+
}
148+
}
149+
}
150+
}
151+
152+
function initParticles() {
153+
particlesArray.length = 0;
154+
const themeProps = getThemeProps(currentTheme);
155+
for (let i = 0; i < themeProps.particleCount; i++) {
156+
particlesArray.push(new Particle(themeProps));
157+
}
158+
}
159+
160+
function animateParticles() {
161+
ctx.clearRect(0, 0, canvas.width, canvas.height);
162+
const themeProps = getThemeProps(currentTheme);
163+
for (let i = 0; i < particlesArray.length; i++) {
164+
particlesArray[i].update(themeProps, mouse);
165+
particlesArray[i].draw(ctx);
166+
// Odświeżanie cząstek jeśli za małe (tylko default)
167+
if (currentTheme === 'default' && (particlesArray[i].size <= 0.5 || particlesArray[i].opacity <= 0.2)) {
168+
particlesArray.splice(i, 1);
169+
particlesArray.push(new Particle(themeProps));
170+
i--;
171+
}
172+
}
173+
connectParticles(particlesArray, themeProps);
174+
requestAnimationFrame(animateParticles);
175+
}
176+
177+
// Obsługa rozmiaru okna
178+
function handleResize() {
179+
canvas.width = window.innerWidth;
180+
canvas.height = window.innerHeight;
181+
initParticles();
182+
}
183+
184+
// --- Theme Toggle (3 motywy) ---
185+
const themeToggle = document.getElementById('themeToggle');
186+
const themes = ['default', 'space', 'cyber'];
187+
188+
function applyTheme(theme) {
189+
currentTheme = theme;
190+
document.body.classList.remove('space-theme', 'cyber-theme');
191+
if (theme === 'space') {
192+
document.body.classList.add('space-theme');
193+
} else if (theme === 'cyber') {
194+
document.body.classList.add('cyber-theme');
195+
}
196+
initParticles();
197+
localStorage.setItem('theme', theme);
198+
}
199+
200+
themeToggle.addEventListener('click', () => {
201+
let nextIndex = (themes.indexOf(currentTheme) + 1) % themes.length;
202+
applyTheme(themes[nextIndex]);
203+
});
204+
205+
// --- Mouse tracking dla interakcji (tylko domyślny motyw) ---
206+
document.addEventListener('mousemove', (e) => {
207+
mouse.x = e.clientX;
208+
mouse.y = e.clientY;
209+
});
210+
document.addEventListener('mouseout', () => {
211+
mouse.x = null;
212+
mouse.y = null;
213+
});
214+
215+
window.addEventListener('resize', handleResize);
216+
217+
// Inicjalizacja
218+
if (ctx) {
219+
handleResize();
220+
applyTheme(currentTheme);
221+
animateParticles();
222+
} else {
223+
console.warn('Canvas not supported');
224+
}
225+
226+
// --- Form validation ---
227+
const searchForm = document.querySelector('.search-form');
228+
const input = document.querySelector('.search-input');
229+
if (searchForm) {
230+
searchForm.addEventListener('submit', function(e) {
231+
const query = input.value.trim();
232+
if (!query) {
233+
e.preventDefault();
234+
alert('Proszę wpisać frazę wyszukiwania!');
235+
}
236+
});
237+
}
238+
239+
// --- Keyboard accessibility ---
240+
if (input) {
241+
document.addEventListener('keydown', (e) => {
242+
if (e.key === 'Enter' && document.activeElement !== input) {
243+
input.focus();
244+
}
245+
});
246+
}
247+
248+
// --- Language detection ---
249+
const userLang = navigator.language || navigator.userLanguage;
250+
if (!userLang.startsWith('pl')) {
251+
document.documentElement.lang = 'en';
252+
if (input) input.placeholder = 'Search with Ecosia...';
253+
const footer = document.querySelector('footer');
254+
if (footer) {
255+
footer.innerHTML = `
256+
Powered by <a href="https://www.ecosia.org" target="_blank" aria-label="Ecosia Website">Ecosia</a> |
257+
<a href="https://hackeros-linux-system.github.io/HackerOS-Website/" target="_blank" aria-label="HackerOS Website">HackerOS</a> | © 2026
258+
`;
259+
}
260+
// Uwaga: link w bottom-left-link jest już zmieniony statycznie w HTML, ale tekst pozostawiamy angielski
261+
const bottomLeftLink = document.querySelector('.bottom-left-link a');
262+
if (bottomLeftLink && bottomLeftLink.textContent === 'Odkryj nasz ekosystem') {
263+
bottomLeftLink.textContent = 'Discover our ecosystem';
264+
}
265+
} else {
266+
document.documentElement.lang = 'pl';
267+
const bottomLeftLink = document.querySelector('.bottom-left-link a');
268+
if (bottomLeftLink && bottomLeftLink.textContent === 'Discover our ecosystem') {
269+
bottomLeftLink.textContent = 'Odkryj nasz ekosystem';
270+
}
271+
}
272+
});

0 commit comments

Comments
 (0)