<style>
/* Убираем стандартный курсор */
* {
cursor: none !important;
}
/* Основной кружок курсора */
.cursor-dot,
.cursor-dot-outline {
mix-blend-mode: normal;
pointer-events: none; /* Игнорируем клики */
position: fixed;
top: 50%;
left: 50%;
border-radius: 50%;
opacity: 0; /* Изначально скрыты */
transform: translate(-50%, -50%);
transition:
opacity 0.3s ease-in-out,
transform 0.2s ease-in-out,
background-color 0.2s ease-in-out;
will-change: transform, opacity, background-color;
}
/* Внутренний маленький круг */
.cursor-dot {
z-index: 99999999;
width: 8px;
height: 8px;
background-color: rgba(221, 31, 59, 0.9);
box-shadow:
0 0 4px rgba(221, 31, 59, 0.8),
0 0 8px rgba(221, 31, 59, 0.6);
}
/* Внешний контурный круг */
.cursor-dot-outline {
z-index: 99999998;
width: 36px;
height: 36px;
background-color: rgba(221, 31, 59, 0.2);
box-shadow:
0 0 0 1px rgba(221, 31, 59, 0.1),
0 0 12px rgba(221, 31, 59, 0.3);
}
/* Скрываем кастомный курсор на мобильных устройствах */
@media (max-width: 768px) {
.cursor-dot,
.cursor-dot-outline {
display: none !important;
}
}
</style>
<script>
// Добавляем HTML-элементы кастомного курсора в DOM
document.body.insertAdjacentHTML('afterbegin',
'<div class="cursor-dot-outline"></div><div class="cursor-dot"></div>'
);
const cursor = {
delay: 8, // Задержка для плавного следования внешнего круга
_x: 0,
_y: 0,
endX: window.innerWidth / 2,
endY: window.innerHeight / 2,
visible: true,
enlarged: false,
$dot: document.querySelector('.cursor-dot'),
$outline: document.querySelector('.cursor-dot-outline'),
init() {
this.bindEvents();
this.animateOutline();
},
bindEvents() {
const self = this;
// При наведении на ссылку — увеличиваем курсор
document.addEventListener('mouseover', e => {
if (e.target.closest('a')) {
self.enlarged = true;
self.updateStyle();
}
});
document.addEventListener('mouseout', e => {
if (e.target.closest('a')) {
self.enlarged = false;
self.updateStyle();
}
});
// При нажатии мыши — увеличиваем курсор
document.addEventListener('mousedown', () => {
self.enlarged = true;
self.updateStyle();
});
document.addEventListener('mouseup', () => {
self.enlarged = false;
self.updateStyle();
});
// Движение мыши — обновляем позицию и показываем курсор
document.addEventListener('mousemove', e => {
self.visible = true;
self.toggleVisibility();
self.endX = e.clientX;
self.endY = e.clientY;
self.$dot.style.top = `${self.endY}px`;
self.$dot.style.left = `${self.endX}px`;
});
// Вход в окно — показываем курсор
document.addEventListener('mouseenter', () => {
self.visible = true;
self.toggleVisibility();
});
// Выход из окна — скрываем курсор
document.addEventListener('mouseleave', () => {
self.visible = false;
self.toggleVisibility();
});
},
// Анимация внешнего круга: следует с задержкой за внутренним
animateOutline() {
this._x += (this.endX - this._x) / this.delay;
this._y += (this.endY - this._y) / this.delay;
this.$outline.style.top = `${this._y}px`;
this.$outline.style.left = `${this._x}px`;
requestAnimationFrame(this.animateOutline.bind(this));
},
// Изменение размеров и цветов при наведении/клике
updateStyle() {
if (this.enlarged) {
this.$dot.style.transform = 'translate(-50%, -50%) scale(3)';
this.$dot.style.backgroundColor = '#FF2B4B';
this.$outline.style.transform = 'translate(-50%, -50%) scale(1.2)';
this.$outline.style.backgroundColor = 'rgba(255,43,75,0.2)';
} else {
this.$dot.style.transform = 'translate(-50%, -50%) scale(1)';
this.$dot.style.backgroundColor = 'rgba(221,31,59,0.9)';
this.$outline.style.transform = 'translate(-50%, -50%) scale(1)';
this.$outline.style.backgroundColor = 'rgba(221,31,59,0.2)';
}
},
// Показ/скрытие элементов курсора
toggleVisibility() {
const opacity = this.visible ? 1 : 0;
this.$dot.style.opacity = opacity;
this.$outline.style.opacity = opacity;
}
};
// Инициализация кастомного курсора
cursor.init();
</script>