czsk/assets/inversion.js
Sergei Poljanski 7b679cc9e9
fix accessibility, performance, and security across the site
- Fix lang attribute, add alt text, rel=noopener, defer scripts, OG tags
- Extract inline styles to CSS, add custom properties, remove unused classes
- Add prefers-reduced-motion, fix footer contrast, fix .numbers grid
- Add loading=lazy, remove legacy snowflake.js, strip console.log
- Add nginx.conf with security headers, gzip, caching
- Fix typos (trough→through, Comander→Commander)
2026-03-20 15:10:24 +03:00

111 lines
No EOL
3.7 KiB
JavaScript

document.addEventListener('DOMContentLoaded', function() {
// Проверяем поддержку backdrop-filter
const hasBackdropFilter = 'backdropFilter' in document.body.style ||
'webkitBackdropFilter' in document.body.style;
if (window.innerWidth <= 768) return;
// Создаем элементы
const cursorOuter = document.createElement('div');
const cursorInner = document.createElement('div');
cursorOuter.className = 'cursor-outer';
cursorInner.className = 'cursor-inner';
// Если нет поддержки backdrop-filter, используем fallback
if (!hasBackdropFilter) {
cursorOuter.style.mixBlendMode = 'difference';
cursorOuter.style.backgroundColor = 'rgba(255, 255, 255, 0.2)';
cursorInner.style.mixBlendMode = 'difference';
}
document.body.appendChild(cursorOuter);
document.body.appendChild(cursorInner);
// Скрываем стандартный курсор
document.body.style.cursor = 'none';
// Переменные для анимации
let mouseX = window.innerWidth / 2;
let mouseY = window.innerHeight / 2;
let cursorX = mouseX;
let cursorY = mouseY;
let innerX = mouseX;
let innerY = mouseY;
let isVisible = false;
// Скорость следования
const outerSpeed = 0.25;
const innerSpeed = 0.15;
// Анимация
function animate() {
// Плавное движение
cursorX += (mouseX - cursorX) * outerSpeed;
cursorY += (mouseY - cursorY) * outerSpeed;
innerX += (mouseX - innerX) * innerSpeed;
innerY += (mouseY - innerY) * innerSpeed;
// Применяем позиции
cursorOuter.style.transform = `translate(${cursorX}px, ${cursorY}px)`;
cursorInner.style.transform = `translate(${innerX}px, ${innerY}px)`;
requestAnimationFrame(animate);
}
// Отслеживание мыши
document.addEventListener('mousemove', (e) => {
mouseX = e.clientX;
mouseY = e.clientY;
if (!isVisible) {
cursorOuter.style.opacity = '1';
cursorInner.style.opacity = '1';
isVisible = true;
}
});
// Ховер-эффекты
const interactive = document.querySelectorAll(
'a, button, .photo-card, .number, .photo-container, img, .navigation a'
);
interactive.forEach(el => {
el.addEventListener('mouseenter', () => {
cursorOuter.style.width = '90px';
cursorOuter.style.height = '90px';
cursorOuter.style.borderWidth = '3px';
});
el.addEventListener('mouseleave', () => {
cursorOuter.style.width = '70px';
cursorOuter.style.height = '70px';
cursorOuter.style.borderWidth = '2px';
});
});
// Скрываем при выходе из окна
document.addEventListener('mouseleave', () => {
cursorOuter.style.opacity = '0';
cursorInner.style.opacity = '0';
isVisible = false;
});
// Показываем при входе в окно
document.addEventListener('mouseenter', () => {
if (document.body.style.cursor === 'none') {
cursorOuter.style.opacity = '1';
cursorInner.style.opacity = '1';
isVisible = true;
}
});
// Запускаем анимацию
animate();
// Обновляем при ресайзе
window.addEventListener('resize', () => {
mouseX = Math.min(mouseX, window.innerWidth);
mouseY = Math.min(mouseY, window.innerHeight);
});
});