import type { Point } from './types'; import styles from './cursor.module.css'; export default class Cursor { private readonly isMobile: boolean; private readonly cursor: HTMLDivElement; private tagElement: HTMLDivElement; private coords = { x: 0, y: 0 }; private isMoving = false; private onClick: () => void; constructor(overlay: HTMLDivElement, isMobile: boolean) { this.cursor = document.createElement('div'); this.cursor.className = styles.cursor; if (isMobile) this.cursor.style.backgroundImage = 'unset'; overlay.appendChild(this.cursor); this.isMobile = isMobile; } toggle(flag: boolean) { if (flag) { this.cursor.style.display = 'block'; } else { this.cursor.style.display = 'none'; } } showTag(tag?: string) { if (!this.tagElement) { this.tagElement = document.createElement('div'); Object.assign(this.tagElement.style, { position: 'absolute', padding: '4px 6px', borderRadius: '8px', backgroundColor: '#3EAAAF', color: 'white', bottom: '-25px', left: '80%', fontSize: '12px', whiteSpace: 'nowrap', }); this.cursor.appendChild(this.tagElement); } if (!tag) { this.tagElement.style.display = 'none'; } else { this.tagElement.style.display = 'block'; const nameStr = tag.length > 10 ? `${tag.slice(0, 9)}...` : tag; this.tagElement.innerHTML = `${nameStr}`; } } move({ x, y }: Point) { this.isMoving = true; this.cursor.style.left = `${x}px`; this.cursor.style.top = `${y}px`; this.coords = { x, y }; setTimeout(() => (this.isMoving = false), 60); } setDefaultStyle() { this.cursor.style.width = `${18}px`; this.cursor.style.height = `${30}px`; this.cursor.style.transition = 'top .125s linear, left .125s linear'; } shake() { this.cursor.classList.add(styles.shaking); setTimeout(() => { this.cursor.classList.remove(styles.shaking); }, 500); } click() { const styleList = styles.clicked; this.cursor.classList.add(styleList); this.onClick?.(); setTimeout(() => { this.cursor.classList.remove(styleList); }, 600); } clickTimeout?: NodeJS.Timeout; mobileClick() { const styleList = styles.mobileTouch; if (this.clickTimeout) { clearTimeout(this.clickTimeout); this.cursor.classList.remove(styleList); this.clickTimeout = undefined; } this.cursor.classList.add(styleList); this.onClick?.(); this.clickTimeout = setTimeout(() => { this.cursor.classList.remove(styleList); this.clickTimeout = undefined; }, 600); } setOnClickHook(callback: () => void) { this.onClick = callback; } }