<style>
/* Убираем стандартный курсор для всех элементов */
* {
cursor: none !important;
}
/* Основной кастомный курсор */
.custom-cursor {
position: fixed; /* Курсор зафиксирован в окне браузера */
top: 0;
left: 0;
width: 16px;
height: 16px;
background-color: #ffffff; /* Цвет курсора — белый */
border-radius: 50%; /* Делает курсор круглым */
pointer-events: none; /* Курсор не мешает кликам по элементам под ним */
transform: translate(-50%, -50%); /* Центр курсора совпадает с точкой мыши */
z-index: 99999999; /* Поверх всех элементов */
mix-blend-mode: difference; /* Визуальный эффект — инвертирует фон под собой */
box-shadow: 0 0 6px #fff; /* Добавляем свечение курсору */
}
/* Маленькие вспышки (искра) — след от движения курсора */
.spark {
position: fixed;
width: 8px;
height: 8px;
background-color: #ffffff; /* Цвет искры — белый */
border-radius: 50%; /* Круглая форма */
pointer-events: none;
z-index: 99999998; /* Чуть ниже, чем основной курсор */
opacity: 0.8; /* Полупрозрачность */
animation: spark-fade 0.4s ease-out forwards; /* Анимация исчезновения */
mix-blend-mode: difference; /* Инверсия цвета фона */
}
/* Анимация затухания искры */
@keyframes spark-fade {
0% {
transform: translate(0, 0) scale(1); /* Начальное положение и размер */
opacity: 1;
}
100% {
transform: translate(var(--dx), var(--dy)) scale(0.2); /* Смещение и уменьшение */
opacity: 0; /* Полное исчезновение */
}
}
/* Скрываем кастомный курсор и искры на мобильных устройствах */
@media (max-width: 768px) {
.custom-cursor,
.spark {
display: none !important;
}
}
</style>
<script>
document.addEventListener("DOMContentLoaded", () => {
// Определяем, мобильное ли устройство
const isMobile = /Mobi|Android|iPhone|iPad|iPod/i.test(navigator.userAgent);
if (isMobile) return; // Не создаём кастомный курсор на мобильных
// Создаём основной курсор и добавляем в DOM
const cursor = document.createElement("div");
cursor.classList.add("custom-cursor");
document.body.appendChild(cursor);
// Начальные координаты курсора (по центру экрана)
let mouseX = window.innerWidth / 2;
let mouseY = window.innerHeight / 2;
const sparks = []; // Массив активных искр
const maxSparks = 20; // Максимум одновременно отображаемых искр
// Обновление позиции курсора
function updateCursorPosition() {
cursor.style.transform = `translate(${mouseX}px, ${mouseY}px)`;
}
// Создание одной искры в координатах x и y
function createSpark(x, y) {
const spark = document.createElement("div");
spark.classList.add("spark");
// Случайный угол и радиус для движения искры
const angle = Math.random() * 2 * Math.PI;
const radius = Math.random() * 20 + 5;
const dx = Math.cos(angle) * radius + "px";
const dy = Math.sin(angle) * radius + "px";
// Устанавливаем начальное положение искры
spark.style.left = `${x}px`;
spark.style.top = `${y}px`;
// Передаём смещение в CSS-переменные
spark.style.setProperty('--dx', dx);
spark.style.setProperty('--dy', dy);
// Добавляем искру в DOM
document.body.appendChild(spark);
sparks.push(spark);
// Удаляем старые искры, если их больше maxSparks
if (sparks.length > maxSparks) {
const oldSpark = sparks.shift();
oldSpark.remove();
}
// Удаляем искру после завершения анимации (400мс)
setTimeout(() => spark.remove(), 400);
}
// Событие при движении мыши
document.addEventListener("mousemove", e => {
mouseX = e.clientX;
mouseY = e.clientY;
updateCursorPosition(); // Перемещаем курсор
createSpark(mouseX, mouseY); // Создаём искру
});
// Первая отрисовка курсора
updateCursorPosition();
});
</script>