anime-cursor 2.1.2 → 2.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/anime-cursor.esm.js +82 -34
- package/dist/anime-cursor.umd.js +82 -34
- package/dist/anime-cursor.umd.min.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
[[简体中文]](#
|
|
9
|
+
[[简体中文]](#sc)
|
|
10
10
|
|
|
11
11
|
## [Visit the official website](https://shuninyu.github.io/anime-cursor/) for more informations
|
|
12
12
|
|
|
@@ -185,6 +185,7 @@ Because v2 uses native CSS `cursor` property and CSS animations, there is no Jav
|
|
|
185
185
|
|
|
186
186
|
---
|
|
187
187
|
|
|
188
|
+
<span id="sc"></span>
|
|
188
189
|
# AnimeCursor v2
|
|
189
190
|
|
|
190
191
|
<div align="center">
|
package/dist/anime-cursor.esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// AnimeCursor by github@ShuninYu
|
|
2
|
-
// v2.1.
|
|
2
|
+
// v2.1.3
|
|
3
3
|
|
|
4
4
|
let _instance = null;
|
|
5
5
|
|
|
@@ -51,16 +51,15 @@ class AnimeCursor {
|
|
|
51
51
|
enableTouch: false,
|
|
52
52
|
fallbackCursor: 'auto',
|
|
53
53
|
excludeSelectors: 'input, textarea, [contenteditable]',
|
|
54
|
-
combineAnimations: false,
|
|
54
|
+
combineAnimations: false,
|
|
55
55
|
...options
|
|
56
56
|
};
|
|
57
57
|
|
|
58
58
|
this.disabled = false;
|
|
59
59
|
this.cursors = this.options.cursors || {};
|
|
60
|
-
this.cursorAnimationStrings = {};
|
|
61
|
-
this.combinedRules = new Map();
|
|
60
|
+
this.cursorAnimationStrings = {};
|
|
61
|
+
this.combinedRules = new Map();
|
|
62
62
|
|
|
63
|
-
// 检查是否应启用(触摸设备且未强制启用则禁用)
|
|
64
63
|
if (!this.options.enableTouch && !this.isMouseLikeDevice()) {
|
|
65
64
|
this.disabled = true;
|
|
66
65
|
if (this.options.debug) {
|
|
@@ -71,6 +70,7 @@ class AnimeCursor {
|
|
|
71
70
|
|
|
72
71
|
this.styleEl = null;
|
|
73
72
|
this.debugEl = null;
|
|
73
|
+
this.crosshairEl = null;
|
|
74
74
|
this._onMouseMove = null;
|
|
75
75
|
|
|
76
76
|
this._validateOptions();
|
|
@@ -97,7 +97,6 @@ class AnimeCursor {
|
|
|
97
97
|
throw new Error(`[AnimeCursor] Cursor "${name}" missing required setting: image`);
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
// 处理 frames 和 duration
|
|
101
100
|
if (cfg.frames !== undefined && cfg.duration !== undefined) {
|
|
102
101
|
const framesType = typeof cfg.frames;
|
|
103
102
|
const durationType = typeof cfg.duration;
|
|
@@ -244,7 +243,6 @@ class AnimeCursor {
|
|
|
244
243
|
}
|
|
245
244
|
}
|
|
246
245
|
|
|
247
|
-
// 核心注入样式
|
|
248
246
|
_injectStyles() {
|
|
249
247
|
if (this.disabled) return;
|
|
250
248
|
this.combinedRules.clear();
|
|
@@ -253,14 +251,12 @@ class AnimeCursor {
|
|
|
253
251
|
style.id = 'animecursor-styles';
|
|
254
252
|
let css = '';
|
|
255
253
|
|
|
256
|
-
// 如果有默认光标,生成全局规则
|
|
257
254
|
if (this.defaultCursorName) {
|
|
258
255
|
const defaultCfg = this.cursors[this.defaultCursorName];
|
|
259
256
|
const defaultCursorDef = this._buildCursorCss(this.defaultCursorName, defaultCfg);
|
|
260
257
|
css += `* { ${defaultCursorDef} }\n`;
|
|
261
258
|
}
|
|
262
259
|
|
|
263
|
-
// 为每个光标生成独立的类和关键帧
|
|
264
260
|
for (const [name, cfg] of Object.entries(this.cursors)) {
|
|
265
261
|
const className = `.ac-cursor-${name}`;
|
|
266
262
|
const offset = cfg.offset || [0, 0];
|
|
@@ -292,7 +288,6 @@ class AnimeCursor {
|
|
|
292
288
|
cursorAnimation = animation;
|
|
293
289
|
css += `${className} { cursor: url("${frameUrls[0]}") ${offset[0]} ${offset[1]}, ${fallback}; animation: ${animation}; }\n`;
|
|
294
290
|
} else {
|
|
295
|
-
// 静态光标:生成一帧动画,只播放一次,结束后保持最后一帧
|
|
296
291
|
const staticKeyframeName = `ac_anim_${name}_static`;
|
|
297
292
|
css += `@keyframes ${staticKeyframeName} {\n`;
|
|
298
293
|
css += ` 0%, 100% { cursor: url("${frameUrls[0]}") ${offset[0]} ${offset[1]}, ${fallback}; }\n`;
|
|
@@ -304,7 +299,6 @@ class AnimeCursor {
|
|
|
304
299
|
|
|
305
300
|
this.cursorAnimationStrings[name] = cursorAnimation;
|
|
306
301
|
|
|
307
|
-
// 标签和 data-cursor 规则
|
|
308
302
|
if (cfg.tags && cfg.tags.length) {
|
|
309
303
|
const selector = cfg.tags.join(', ');
|
|
310
304
|
css += `${selector} { ${this._buildCursorCss(name, cfg)} }\n`;
|
|
@@ -316,7 +310,6 @@ class AnimeCursor {
|
|
|
316
310
|
css += `${this.options.excludeSelectors} { cursor: text !important; animation: none !important; }\n`;
|
|
317
311
|
}
|
|
318
312
|
|
|
319
|
-
// 自动组合动画
|
|
320
313
|
if (this.options.combineAnimations) {
|
|
321
314
|
const elements = document.querySelectorAll('[data-ac-animation]');
|
|
322
315
|
for (const el of elements) {
|
|
@@ -348,7 +341,6 @@ class AnimeCursor {
|
|
|
348
341
|
this.styleEl = style;
|
|
349
342
|
}
|
|
350
343
|
|
|
351
|
-
// 获取元素对应的光标类型(复用 debug 逻辑)
|
|
352
344
|
_getCursorTypeForElement(el) {
|
|
353
345
|
if (el.dataset.cursor && this.cursors[el.dataset.cursor]) {
|
|
354
346
|
return el.dataset.cursor;
|
|
@@ -358,7 +350,7 @@ class AnimeCursor {
|
|
|
358
350
|
return name;
|
|
359
351
|
}
|
|
360
352
|
}
|
|
361
|
-
return this.defaultCursorName;
|
|
353
|
+
return this.defaultCursorName;
|
|
362
354
|
}
|
|
363
355
|
|
|
364
356
|
_buildKeyframes(cfg, frameUrls) {
|
|
@@ -411,7 +403,6 @@ class AnimeCursor {
|
|
|
411
403
|
const totalDuration = Array.isArray(cfg.duration) ? cfg.duration.reduce((a, b) => a + b, 0) : cfg.duration;
|
|
412
404
|
css += ` animation: ac_anim_${name} ${totalDuration}s steps(1) infinite ${cfg.pingpong ? 'alternate' : ''};`;
|
|
413
405
|
} else {
|
|
414
|
-
// 静态光标:一帧动画,只播放一次,结束后保持
|
|
415
406
|
css += ` animation: ac_anim_${name}_static 0.001s forwards steps(1);`;
|
|
416
407
|
}
|
|
417
408
|
return css;
|
|
@@ -428,27 +419,82 @@ class AnimeCursor {
|
|
|
428
419
|
}
|
|
429
420
|
|
|
430
421
|
_initDebug() {
|
|
422
|
+
// 创建左上角信息浮层(现有 debug 面板)
|
|
431
423
|
const debugDiv = document.createElement('div');
|
|
432
424
|
debugDiv.className = 'animecursor-debug';
|
|
433
425
|
debugDiv.style.cssText = `
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
426
|
+
position: fixed;
|
|
427
|
+
top: 0;
|
|
428
|
+
left: 0;
|
|
429
|
+
background: rgba(0,0,0,0.7);
|
|
430
|
+
color: #0f0;
|
|
431
|
+
padding: 4px 8px;
|
|
432
|
+
font-family: monospace;
|
|
433
|
+
font-size: 12px;
|
|
434
|
+
z-index: 2147483647;
|
|
435
|
+
pointer-events: auto;
|
|
436
|
+
white-space: nowrap;
|
|
437
|
+
transition: opacity 0.2s ease;
|
|
438
|
+
`;
|
|
439
|
+
// hover 时半透明,便于查看被遮挡内容
|
|
440
|
+
debugDiv.addEventListener('mouseenter', () => { debugDiv.style.opacity = '0.5'; });
|
|
441
|
+
debugDiv.addEventListener('mouseleave', () => { debugDiv.style.opacity = '1'; });
|
|
446
442
|
document.body.appendChild(debugDiv);
|
|
447
443
|
this.debugEl = debugDiv;
|
|
448
444
|
|
|
445
|
+
// 创建跟随鼠标的十字辅助线层(横竖双色线)
|
|
446
|
+
const crosshair = document.createElement('div');
|
|
447
|
+
crosshair.className = 'animecursor-crosshair';
|
|
448
|
+
crosshair.style.cssText = `
|
|
449
|
+
position: fixed;
|
|
450
|
+
top: 0;
|
|
451
|
+
left: 0;
|
|
452
|
+
width: 0;
|
|
453
|
+
height: 0;
|
|
454
|
+
pointer-events: none;
|
|
455
|
+
z-index: 2147483646;
|
|
456
|
+
`;
|
|
457
|
+
// 横线
|
|
458
|
+
const hLine = document.createElement('div');
|
|
459
|
+
hLine.className = 'ac-crosshair-h';
|
|
460
|
+
hLine.style.cssText = `
|
|
461
|
+
position: fixed;
|
|
462
|
+
left: 0;
|
|
463
|
+
width: 100%;
|
|
464
|
+
height: 1px;
|
|
465
|
+
background-color: rgba(255,0,0,0.6);
|
|
466
|
+
pointer-events: none;
|
|
467
|
+
transform: translateY(-0.5px);
|
|
468
|
+
`;
|
|
469
|
+
// 竖线
|
|
470
|
+
const vLine = document.createElement('div');
|
|
471
|
+
vLine.className = 'ac-crosshair-v';
|
|
472
|
+
vLine.style.cssText = `
|
|
473
|
+
position: fixed;
|
|
474
|
+
top: 0;
|
|
475
|
+
width: 1px;
|
|
476
|
+
height: 100%;
|
|
477
|
+
background-color: rgba(255,0,0,0.6);
|
|
478
|
+
pointer-events: none;
|
|
479
|
+
transform: translateX(-0.5px);
|
|
480
|
+
`;
|
|
481
|
+
crosshair.appendChild(hLine);
|
|
482
|
+
crosshair.appendChild(vLine);
|
|
483
|
+
document.body.appendChild(crosshair);
|
|
484
|
+
this.crosshairEl = { container: crosshair, hLine, vLine };
|
|
485
|
+
|
|
449
486
|
let lastCursor = '';
|
|
450
487
|
this._onMouseMove = (e) => {
|
|
451
|
-
const
|
|
488
|
+
const x = e.clientX;
|
|
489
|
+
const y = e.clientY;
|
|
490
|
+
|
|
491
|
+
// 更新十字线位置
|
|
492
|
+
if (this.crosshairEl) {
|
|
493
|
+
this.crosshairEl.hLine.style.top = y + 'px';
|
|
494
|
+
this.crosshairEl.vLine.style.left = x + 'px';
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
const target = document.elementFromPoint(x, y);
|
|
452
498
|
let cursorType = null;
|
|
453
499
|
if (target) {
|
|
454
500
|
if (target.dataset.cursor && this.cursors[target.dataset.cursor]) {
|
|
@@ -469,9 +515,9 @@ class AnimeCursor {
|
|
|
469
515
|
}
|
|
470
516
|
if (cursorType !== lastCursor) {
|
|
471
517
|
lastCursor = cursorType;
|
|
472
|
-
debugDiv.textContent =
|
|
518
|
+
debugDiv.textContent = `${cursorType} @ (${x}, ${y})`;
|
|
473
519
|
} else {
|
|
474
|
-
debugDiv.textContent =
|
|
520
|
+
debugDiv.textContent = `${cursorType} @ (${x}, ${y})`;
|
|
475
521
|
}
|
|
476
522
|
};
|
|
477
523
|
document.addEventListener('mousemove', this._onMouseMove);
|
|
@@ -484,6 +530,7 @@ class AnimeCursor {
|
|
|
484
530
|
this._injectStyles();
|
|
485
531
|
if (this.options.debug) {
|
|
486
532
|
if (this.debugEl) this.debugEl.remove();
|
|
533
|
+
if (this.crosshairEl) this.crosshairEl.container.remove();
|
|
487
534
|
this._initDebug();
|
|
488
535
|
}
|
|
489
536
|
console.log('[AnimeCursor] Refresh complete');
|
|
@@ -493,6 +540,7 @@ class AnimeCursor {
|
|
|
493
540
|
if (this.disabled) return;
|
|
494
541
|
if (this.styleEl) this.styleEl.remove();
|
|
495
542
|
if (this.debugEl) this.debugEl.remove();
|
|
543
|
+
if (this.crosshairEl) this.crosshairEl.container.remove();
|
|
496
544
|
if (this._onMouseMove) {
|
|
497
545
|
document.removeEventListener('mousemove', this._onMouseMove);
|
|
498
546
|
}
|
|
@@ -503,16 +551,18 @@ class AnimeCursor {
|
|
|
503
551
|
|
|
504
552
|
disable() {
|
|
505
553
|
if (this.disabled) return;
|
|
506
|
-
// 移除样式表
|
|
507
554
|
if (this.styleEl) {
|
|
508
555
|
this.styleEl.remove();
|
|
509
556
|
this.styleEl = null;
|
|
510
557
|
}
|
|
511
|
-
// 移除 debug 相关
|
|
512
558
|
if (this.debugEl) {
|
|
513
559
|
this.debugEl.remove();
|
|
514
560
|
this.debugEl = null;
|
|
515
561
|
}
|
|
562
|
+
if (this.crosshairEl) {
|
|
563
|
+
this.crosshairEl.container.remove();
|
|
564
|
+
this.crosshairEl = null;
|
|
565
|
+
}
|
|
516
566
|
if (this._onMouseMove) {
|
|
517
567
|
document.removeEventListener('mousemove', this._onMouseMove);
|
|
518
568
|
this._onMouseMove = null;
|
|
@@ -524,9 +574,7 @@ class AnimeCursor {
|
|
|
524
574
|
enable() {
|
|
525
575
|
if (!this.disabled) return;
|
|
526
576
|
this.disabled = false;
|
|
527
|
-
// 重新注入样式
|
|
528
577
|
this._injectStyles();
|
|
529
|
-
// 如果 debug 模式开启,重新初始化 debug
|
|
530
578
|
if (this.options.debug) {
|
|
531
579
|
this._initDebug();
|
|
532
580
|
}
|
package/dist/anime-cursor.umd.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
})(this, (function () { 'use strict';
|
|
6
6
|
|
|
7
7
|
// AnimeCursor by github@ShuninYu
|
|
8
|
-
// v2.1.
|
|
8
|
+
// v2.1.3
|
|
9
9
|
|
|
10
10
|
let _instance = null;
|
|
11
11
|
|
|
@@ -57,16 +57,15 @@
|
|
|
57
57
|
enableTouch: false,
|
|
58
58
|
fallbackCursor: 'auto',
|
|
59
59
|
excludeSelectors: 'input, textarea, [contenteditable]',
|
|
60
|
-
combineAnimations: false,
|
|
60
|
+
combineAnimations: false,
|
|
61
61
|
...options
|
|
62
62
|
};
|
|
63
63
|
|
|
64
64
|
this.disabled = false;
|
|
65
65
|
this.cursors = this.options.cursors || {};
|
|
66
|
-
this.cursorAnimationStrings = {};
|
|
67
|
-
this.combinedRules = new Map();
|
|
66
|
+
this.cursorAnimationStrings = {};
|
|
67
|
+
this.combinedRules = new Map();
|
|
68
68
|
|
|
69
|
-
// 检查是否应启用(触摸设备且未强制启用则禁用)
|
|
70
69
|
if (!this.options.enableTouch && !this.isMouseLikeDevice()) {
|
|
71
70
|
this.disabled = true;
|
|
72
71
|
if (this.options.debug) {
|
|
@@ -77,6 +76,7 @@
|
|
|
77
76
|
|
|
78
77
|
this.styleEl = null;
|
|
79
78
|
this.debugEl = null;
|
|
79
|
+
this.crosshairEl = null;
|
|
80
80
|
this._onMouseMove = null;
|
|
81
81
|
|
|
82
82
|
this._validateOptions();
|
|
@@ -103,7 +103,6 @@
|
|
|
103
103
|
throw new Error(`[AnimeCursor] Cursor "${name}" missing required setting: image`);
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
// 处理 frames 和 duration
|
|
107
106
|
if (cfg.frames !== undefined && cfg.duration !== undefined) {
|
|
108
107
|
const framesType = typeof cfg.frames;
|
|
109
108
|
const durationType = typeof cfg.duration;
|
|
@@ -250,7 +249,6 @@
|
|
|
250
249
|
}
|
|
251
250
|
}
|
|
252
251
|
|
|
253
|
-
// 核心注入样式
|
|
254
252
|
_injectStyles() {
|
|
255
253
|
if (this.disabled) return;
|
|
256
254
|
this.combinedRules.clear();
|
|
@@ -259,14 +257,12 @@
|
|
|
259
257
|
style.id = 'animecursor-styles';
|
|
260
258
|
let css = '';
|
|
261
259
|
|
|
262
|
-
// 如果有默认光标,生成全局规则
|
|
263
260
|
if (this.defaultCursorName) {
|
|
264
261
|
const defaultCfg = this.cursors[this.defaultCursorName];
|
|
265
262
|
const defaultCursorDef = this._buildCursorCss(this.defaultCursorName, defaultCfg);
|
|
266
263
|
css += `* { ${defaultCursorDef} }\n`;
|
|
267
264
|
}
|
|
268
265
|
|
|
269
|
-
// 为每个光标生成独立的类和关键帧
|
|
270
266
|
for (const [name, cfg] of Object.entries(this.cursors)) {
|
|
271
267
|
const className = `.ac-cursor-${name}`;
|
|
272
268
|
const offset = cfg.offset || [0, 0];
|
|
@@ -298,7 +294,6 @@
|
|
|
298
294
|
cursorAnimation = animation;
|
|
299
295
|
css += `${className} { cursor: url("${frameUrls[0]}") ${offset[0]} ${offset[1]}, ${fallback}; animation: ${animation}; }\n`;
|
|
300
296
|
} else {
|
|
301
|
-
// 静态光标:生成一帧动画,只播放一次,结束后保持最后一帧
|
|
302
297
|
const staticKeyframeName = `ac_anim_${name}_static`;
|
|
303
298
|
css += `@keyframes ${staticKeyframeName} {\n`;
|
|
304
299
|
css += ` 0%, 100% { cursor: url("${frameUrls[0]}") ${offset[0]} ${offset[1]}, ${fallback}; }\n`;
|
|
@@ -310,7 +305,6 @@
|
|
|
310
305
|
|
|
311
306
|
this.cursorAnimationStrings[name] = cursorAnimation;
|
|
312
307
|
|
|
313
|
-
// 标签和 data-cursor 规则
|
|
314
308
|
if (cfg.tags && cfg.tags.length) {
|
|
315
309
|
const selector = cfg.tags.join(', ');
|
|
316
310
|
css += `${selector} { ${this._buildCursorCss(name, cfg)} }\n`;
|
|
@@ -322,7 +316,6 @@
|
|
|
322
316
|
css += `${this.options.excludeSelectors} { cursor: text !important; animation: none !important; }\n`;
|
|
323
317
|
}
|
|
324
318
|
|
|
325
|
-
// 自动组合动画
|
|
326
319
|
if (this.options.combineAnimations) {
|
|
327
320
|
const elements = document.querySelectorAll('[data-ac-animation]');
|
|
328
321
|
for (const el of elements) {
|
|
@@ -354,7 +347,6 @@
|
|
|
354
347
|
this.styleEl = style;
|
|
355
348
|
}
|
|
356
349
|
|
|
357
|
-
// 获取元素对应的光标类型(复用 debug 逻辑)
|
|
358
350
|
_getCursorTypeForElement(el) {
|
|
359
351
|
if (el.dataset.cursor && this.cursors[el.dataset.cursor]) {
|
|
360
352
|
return el.dataset.cursor;
|
|
@@ -364,7 +356,7 @@
|
|
|
364
356
|
return name;
|
|
365
357
|
}
|
|
366
358
|
}
|
|
367
|
-
return this.defaultCursorName;
|
|
359
|
+
return this.defaultCursorName;
|
|
368
360
|
}
|
|
369
361
|
|
|
370
362
|
_buildKeyframes(cfg, frameUrls) {
|
|
@@ -417,7 +409,6 @@
|
|
|
417
409
|
const totalDuration = Array.isArray(cfg.duration) ? cfg.duration.reduce((a, b) => a + b, 0) : cfg.duration;
|
|
418
410
|
css += ` animation: ac_anim_${name} ${totalDuration}s steps(1) infinite ${cfg.pingpong ? 'alternate' : ''};`;
|
|
419
411
|
} else {
|
|
420
|
-
// 静态光标:一帧动画,只播放一次,结束后保持
|
|
421
412
|
css += ` animation: ac_anim_${name}_static 0.001s forwards steps(1);`;
|
|
422
413
|
}
|
|
423
414
|
return css;
|
|
@@ -434,27 +425,82 @@
|
|
|
434
425
|
}
|
|
435
426
|
|
|
436
427
|
_initDebug() {
|
|
428
|
+
// 创建左上角信息浮层(现有 debug 面板)
|
|
437
429
|
const debugDiv = document.createElement('div');
|
|
438
430
|
debugDiv.className = 'animecursor-debug';
|
|
439
431
|
debugDiv.style.cssText = `
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
432
|
+
position: fixed;
|
|
433
|
+
top: 0;
|
|
434
|
+
left: 0;
|
|
435
|
+
background: rgba(0,0,0,0.7);
|
|
436
|
+
color: #0f0;
|
|
437
|
+
padding: 4px 8px;
|
|
438
|
+
font-family: monospace;
|
|
439
|
+
font-size: 12px;
|
|
440
|
+
z-index: 2147483647;
|
|
441
|
+
pointer-events: auto;
|
|
442
|
+
white-space: nowrap;
|
|
443
|
+
transition: opacity 0.2s ease;
|
|
444
|
+
`;
|
|
445
|
+
// hover 时半透明,便于查看被遮挡内容
|
|
446
|
+
debugDiv.addEventListener('mouseenter', () => { debugDiv.style.opacity = '0.5'; });
|
|
447
|
+
debugDiv.addEventListener('mouseleave', () => { debugDiv.style.opacity = '1'; });
|
|
452
448
|
document.body.appendChild(debugDiv);
|
|
453
449
|
this.debugEl = debugDiv;
|
|
454
450
|
|
|
451
|
+
// 创建跟随鼠标的十字辅助线层(横竖双色线)
|
|
452
|
+
const crosshair = document.createElement('div');
|
|
453
|
+
crosshair.className = 'animecursor-crosshair';
|
|
454
|
+
crosshair.style.cssText = `
|
|
455
|
+
position: fixed;
|
|
456
|
+
top: 0;
|
|
457
|
+
left: 0;
|
|
458
|
+
width: 0;
|
|
459
|
+
height: 0;
|
|
460
|
+
pointer-events: none;
|
|
461
|
+
z-index: 2147483646;
|
|
462
|
+
`;
|
|
463
|
+
// 横线
|
|
464
|
+
const hLine = document.createElement('div');
|
|
465
|
+
hLine.className = 'ac-crosshair-h';
|
|
466
|
+
hLine.style.cssText = `
|
|
467
|
+
position: fixed;
|
|
468
|
+
left: 0;
|
|
469
|
+
width: 100%;
|
|
470
|
+
height: 1px;
|
|
471
|
+
background-color: rgba(255,0,0,0.6);
|
|
472
|
+
pointer-events: none;
|
|
473
|
+
transform: translateY(-0.5px);
|
|
474
|
+
`;
|
|
475
|
+
// 竖线
|
|
476
|
+
const vLine = document.createElement('div');
|
|
477
|
+
vLine.className = 'ac-crosshair-v';
|
|
478
|
+
vLine.style.cssText = `
|
|
479
|
+
position: fixed;
|
|
480
|
+
top: 0;
|
|
481
|
+
width: 1px;
|
|
482
|
+
height: 100%;
|
|
483
|
+
background-color: rgba(255,0,0,0.6);
|
|
484
|
+
pointer-events: none;
|
|
485
|
+
transform: translateX(-0.5px);
|
|
486
|
+
`;
|
|
487
|
+
crosshair.appendChild(hLine);
|
|
488
|
+
crosshair.appendChild(vLine);
|
|
489
|
+
document.body.appendChild(crosshair);
|
|
490
|
+
this.crosshairEl = { container: crosshair, hLine, vLine };
|
|
491
|
+
|
|
455
492
|
let lastCursor = '';
|
|
456
493
|
this._onMouseMove = (e) => {
|
|
457
|
-
const
|
|
494
|
+
const x = e.clientX;
|
|
495
|
+
const y = e.clientY;
|
|
496
|
+
|
|
497
|
+
// 更新十字线位置
|
|
498
|
+
if (this.crosshairEl) {
|
|
499
|
+
this.crosshairEl.hLine.style.top = y + 'px';
|
|
500
|
+
this.crosshairEl.vLine.style.left = x + 'px';
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
const target = document.elementFromPoint(x, y);
|
|
458
504
|
let cursorType = null;
|
|
459
505
|
if (target) {
|
|
460
506
|
if (target.dataset.cursor && this.cursors[target.dataset.cursor]) {
|
|
@@ -475,9 +521,9 @@
|
|
|
475
521
|
}
|
|
476
522
|
if (cursorType !== lastCursor) {
|
|
477
523
|
lastCursor = cursorType;
|
|
478
|
-
debugDiv.textContent =
|
|
524
|
+
debugDiv.textContent = `${cursorType} @ (${x}, ${y})`;
|
|
479
525
|
} else {
|
|
480
|
-
debugDiv.textContent =
|
|
526
|
+
debugDiv.textContent = `${cursorType} @ (${x}, ${y})`;
|
|
481
527
|
}
|
|
482
528
|
};
|
|
483
529
|
document.addEventListener('mousemove', this._onMouseMove);
|
|
@@ -490,6 +536,7 @@
|
|
|
490
536
|
this._injectStyles();
|
|
491
537
|
if (this.options.debug) {
|
|
492
538
|
if (this.debugEl) this.debugEl.remove();
|
|
539
|
+
if (this.crosshairEl) this.crosshairEl.container.remove();
|
|
493
540
|
this._initDebug();
|
|
494
541
|
}
|
|
495
542
|
console.log('[AnimeCursor] Refresh complete');
|
|
@@ -499,6 +546,7 @@
|
|
|
499
546
|
if (this.disabled) return;
|
|
500
547
|
if (this.styleEl) this.styleEl.remove();
|
|
501
548
|
if (this.debugEl) this.debugEl.remove();
|
|
549
|
+
if (this.crosshairEl) this.crosshairEl.container.remove();
|
|
502
550
|
if (this._onMouseMove) {
|
|
503
551
|
document.removeEventListener('mousemove', this._onMouseMove);
|
|
504
552
|
}
|
|
@@ -509,16 +557,18 @@
|
|
|
509
557
|
|
|
510
558
|
disable() {
|
|
511
559
|
if (this.disabled) return;
|
|
512
|
-
// 移除样式表
|
|
513
560
|
if (this.styleEl) {
|
|
514
561
|
this.styleEl.remove();
|
|
515
562
|
this.styleEl = null;
|
|
516
563
|
}
|
|
517
|
-
// 移除 debug 相关
|
|
518
564
|
if (this.debugEl) {
|
|
519
565
|
this.debugEl.remove();
|
|
520
566
|
this.debugEl = null;
|
|
521
567
|
}
|
|
568
|
+
if (this.crosshairEl) {
|
|
569
|
+
this.crosshairEl.container.remove();
|
|
570
|
+
this.crosshairEl = null;
|
|
571
|
+
}
|
|
522
572
|
if (this._onMouseMove) {
|
|
523
573
|
document.removeEventListener('mousemove', this._onMouseMove);
|
|
524
574
|
this._onMouseMove = null;
|
|
@@ -530,9 +580,7 @@
|
|
|
530
580
|
enable() {
|
|
531
581
|
if (!this.disabled) return;
|
|
532
582
|
this.disabled = false;
|
|
533
|
-
// 重新注入样式
|
|
534
583
|
this._injectStyles();
|
|
535
|
-
// 如果 debug 模式开启,重新初始化 debug
|
|
536
584
|
if (this.options.debug) {
|
|
537
585
|
this._initDebug();
|
|
538
586
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).AnimeCursor=t()}(this,function(){"use strict";let e=null;return class{static get instance(){return e}static destroy(){return!!e&&(e.destroy(),!0)}static refresh(){return!!e&&(e.refresh(),!0)}static disable(){return!!e&&(e.disable(),!0)}static enable(){return!!e&&(e.enable(),!0)}constructor(t={}){return e?(console.warn("[AnimeCursor] Instance already exists, returning existing one"),e):(this.options={debug:!1,enableTouch:!1,fallbackCursor:"auto",excludeSelectors:"input, textarea, [contenteditable]",combineAnimations:!1,...t},this.disabled=!1,this.cursors=this.options.cursors||{},this.cursorAnimationStrings={},this.combinedRules=new Map,this.options.enableTouch||this.isMouseLikeDevice()?(this.styleEl=null,this.debugEl=null,this._onMouseMove=null,this._validateOptions(),this._preloadImages(),this._checkDomLoad(),void(e=this)):(this.disabled=!0,void(this.options.debug&&console.warn("[AnimeCursor] Touch device detected, cursor animations disabled"))))}isMouseLikeDevice(){return window.matchMedia("(pointer: fine)").matches}_validateOptions(){if(this.disabled)return;if(!this.cursors||0===Object.keys(this.cursors).length)throw new Error("[AnimeCursor] At least one cursor must be defined");let e=!1;for(const[t,s]of Object.entries(this.cursors)){if(!s.image)throw new Error(`[AnimeCursor] Cursor "${t}" missing required setting: image`);if(void 0!==s.frames&&void 0!==s.duration){if(typeof s.frames!==typeof s.duration)console.warn(`[AnimeCursor] Cursor "${t}" has mismatched types for frames and duration, treating as static cursor`),delete s.frames,delete s.duration;else if(Array.isArray(s.frames)&&Array.isArray(s.duration))if(s.frames.length!==s.duration.length)console.warn(`[AnimeCursor] Cursor "${t}" frames and duration arrays have different lengths, treating as static cursor`),delete s.frames,delete s.duration;else{for(let e of s.frames)if(!Number.isInteger(e)||e<=0){console.warn(`[AnimeCursor] Cursor "${t}" frames array contains invalid value, treating as static cursor`),delete s.frames,delete s.duration;break}for(let e of s.duration)if("number"!=typeof e||e<=0){console.warn(`[AnimeCursor] Cursor "${t}" duration array contains invalid value, treating as static cursor`),delete s.frames,delete s.duration;break}}else"number"==typeof s.frames&&"number"==typeof s.duration?(s.frames<=0||s.duration<=0)&&(console.warn(`[AnimeCursor] Cursor "${t}" frames or duration <= 0, treating as static cursor`),delete s.frames,delete s.duration):(console.warn(`[AnimeCursor] Cursor "${t}" frames and duration must be both numbers or both arrays, treating as static cursor`),delete s.frames,delete s.duration)}else void 0===s.frames&&void 0===s.duration||(console.warn(`[AnimeCursor] Cursor "${t}" has only frames or duration defined, treating as static cursor`),delete s.frames,delete s.duration);if(s.tags&&!Array.isArray(s.tags))throw new Error(`[AnimeCursor] Cursor "${t}" tags must be an array`);if(s.default){if(e)throw new Error("[AnimeCursor] Only one default cursor allowed");e=!0}if(s.offset&&(!Array.isArray(s.offset)||2!==s.offset.length))throw new Error(`[AnimeCursor] Cursor "${t}" offset must be [x, y] array`)}this.defaultCursorName=e?Object.keys(this.cursors).find(e=>this.cursors[e].default):null}_preloadImages(){const e=new Set;for(const t of Object.values(this.cursors)){this._getFrameUrls(t).forEach(t=>e.add(t))}e.forEach(e=>{const t=document.createElement("link");t.rel="preload",t.as="image",t.href=e,e.startsWith("http")&&!e.startsWith(window.location.origin)&&(t.crossOrigin="anonymous"),document.head.appendChild(t)}),this.options.debug&&e.size&&console.info(`[AnimeCursor] Preloaded ${e.size} cursor images`)}_getFrameUrls(e){let t=1;void 0!==e.frames&&(Array.isArray(e.frames)?t=e.frames.reduce((e,t)=>e+t,0):"number"==typeof e.frames&&(t=e.frames));const{image:s}=e;if(1===t)return[s];const{prefix:r,suffix:o,startNum:i,numFormat:n,ext:a}=this._parseImagePattern(s),u=[];for(let e=0;e<t;e++){const t=i+e,s=`${r}${n?this._formatNumber(t,n):t}${o}${a}`;u.push(s)}return u}_parseImagePattern(e){const t=e.match(/\.[^.]+$/),s=t?t[0]:"",r=e.slice(0,-s.length),o=r.match(/(\d+)(?!.*\d)/);if(!o)return{prefix:r+"_",suffix:"",startNum:1,numFormat:null,ext:s};const i=o[0],n=parseInt(i,10),a=i.length;return{prefix:r.slice(0,o.index),suffix:r.slice(o.index+i.length),startNum:n,numFormat:a,ext:s}}_formatNumber(e,t){return String(e).padStart(t,"0")}_checkDomLoad(){const e=()=>{this._injectStyles(),this.options.debug&&this._initDebug(),console.log("[AnimeCursor] Initialization complete")};"loading"===document.readyState?document.addEventListener("DOMContentLoaded",e):e()}_injectStyles(){if(this.disabled)return;this.combinedRules.clear();const e=document.createElement("style");e.id="animecursor-styles";let t="";if(this.defaultCursorName){const e=this.cursors[this.defaultCursorName];t+=`* { ${this._buildCursorCss(this.defaultCursorName,e)} }\n`}for(const[e,s]of Object.entries(this.cursors)){const r=`.ac-cursor-${e}`,o=s.offset||[0,0],i=s.fallback||this.options.fallbackCursor,n=this._getFrameUrls(s),a=n.length;let u="";if(void 0!==s.frames&&void 0!==s.duration&&(Array.isArray(s.frames)&&Array.isArray(s.duration)||"number"==typeof s.frames&&"number"==typeof s.duration)&&a>1){const a=`ac_anim_${e}`;let l=`@keyframes ${a} {\n`;const d=this._buildKeyframes(s,n);for(const e of d){let t=(100*e.percent).toFixed(5);1===e.percent&&(t="100");l+=` ${t}% { ${`cursor: url("${e.url}") ${o[0]} ${o[1]}, ${i};`} }\n`}l+="}\n",t+=l;const c=`${a} ${Array.isArray(s.duration)?s.duration.reduce((e,t)=>e+t,0):s.duration}s steps(1) infinite ${s.pingpong?"alternate":""}`;u=c,t+=`${r} { cursor: url("${n[0]}") ${o[0]} ${o[1]}, ${i}; animation: ${c}; }\n`}else{const s=`ac_anim_${e}_static`;t+=`@keyframes ${s} {\n`,t+=` 0%, 100% { cursor: url("${n[0]}") ${o[0]} ${o[1]}, ${i}; }\n`,t+="}\n";const a=`${s} 0.001s forwards steps(1)`;u=a,t+=`${r} { cursor: url("${n[0]}") ${o[0]} ${o[1]}, ${i}; animation: ${a}; }\n`}if(this.cursorAnimationStrings[e]=u,s.tags&&s.tags.length){t+=`${s.tags.join(", ")} { ${this._buildCursorCss(e,s)} }\n`}t+=`[data-cursor="${e}"] { ${this._buildCursorCss(e,s)} }\n`}if(this.options.excludeSelectors&&(t+=`${this.options.excludeSelectors} { cursor: text !important; animation: none !important; }\n`),this.options.combineAnimations){const e=document.querySelectorAll("[data-ac-animation]");for(const s of e){const e=s.getAttribute("data-ac-animation");if(!e)continue;let r=this._getCursorTypeForElement(s);if(!r)continue;const o=this.cursorAnimationStrings[r];if(!o)continue;const i=`${r}:${e}`;if(!this.combinedRules.has(i)){const s=`ac-combined-${this._simpleHash(i)}`;t+=`.${s} { animation: ${o}, ${e}; }\n`,this.combinedRules.set(i,s)}const n=this.combinedRules.get(i);s.classList.add(n)}}t+="body.animecursor-disabled * { cursor: auto !important; animation: none !important; }\n",e.textContent=t,document.head.appendChild(e),this.styleEl=e}_getCursorTypeForElement(e){if(e.dataset.cursor&&this.cursors[e.dataset.cursor])return e.dataset.cursor;for(const[t,s]of Object.entries(this.cursors))if(s.tags&&s.tags.some(t=>e.matches(t)))return t;return this.defaultCursorName}_buildKeyframes(e,t){let s=e.frames,r=e.duration;const o=t.length;if("number"==typeof s){const e=r/s;s=new Array(s).fill(1),r=new Array(s.length).fill(e)}const i=[];let n=r.reduce((e,t)=>e+t,0),a=0,u=0;for(let e=0;e<s.length;e++){const o=s[e],l=r[e]/o;for(let e=0;e<o;e++){const e=a/n;i.push({percent:e,url:t[u]}),a+=l,u++}}return i.push({percent:1,url:t[o-1]}),i}_buildCursorCss(e,t){const s=this._getFrameUrls(t),r=t.offset||[0,0],o=t.fallback||this.options.fallbackCursor;let i=`cursor: url("${s[0]}") ${r[0]} ${r[1]}, ${o};`;if(void 0!==t.frames&&void 0!==t.duration&&(Array.isArray(t.frames)&&Array.isArray(t.duration)||"number"==typeof t.frames&&"number"==typeof t.duration)&&s.length>1){i+=` animation: ac_anim_${e} ${Array.isArray(t.duration)?t.duration.reduce((e,t)=>e+t,0):t.duration}s steps(1) infinite ${t.pingpong?"alternate":""};`}else i+=` animation: ac_anim_${e}_static 0.001s forwards steps(1);`;return i}_simpleHash(e){let t=0;for(let s=0;s<e.length;s++){t=(t<<5)-t+e.charCodeAt(s),t|=0}return Math.abs(t).toString(36)}_initDebug(){const e=document.createElement("div");e.className="animecursor-debug",e.style.cssText="\n position: fixed;\n top: 0;\n left: 0;\n background: rgba(0,0,0,0.7);\n color: #0f0;\n padding: 4px 8px;\n font-family: monospace;\n font-size: 12px;\n z-index: 2147483647;\n pointer-events: none;\n white-space: nowrap;\n ",document.body.appendChild(e),this.debugEl=e;let t="";this._onMouseMove=s=>{const r=document.elementFromPoint(s.clientX,s.clientY);let o=null;if(r)if(r.dataset.cursor&&this.cursors[r.dataset.cursor])o=r.dataset.cursor;else for(const[e,t]of Object.entries(this.cursors))if(t.tags&&t.tags.some(e=>r.matches(e))){o=e;break}o||this.defaultCursorName?!o&&this.defaultCursorName&&(o=this.defaultCursorName):o="native",o!==t?(t=o,e.textContent=`🎯 ${o} @ (${s.clientX}, ${s.clientY})`):e.textContent=`🎯 ${o} @ (${s.clientX}, ${s.clientY})`},document.addEventListener("mousemove",this._onMouseMove)}refresh(){this.disabled||(this.styleEl&&this.styleEl.remove(),this.combinedRules.clear(),this._injectStyles(),this.options.debug&&(this.debugEl&&this.debugEl.remove(),this._initDebug()),console.log("[AnimeCursor] Refresh complete"))}destroy(){this.disabled||(this.styleEl&&this.styleEl.remove(),this.debugEl&&this.debugEl.remove(),this._onMouseMove&&document.removeEventListener("mousemove",this._onMouseMove),document.body.classList.remove("animecursor-disabled"),e=null,console.log("[AnimeCursor] Destroyed"))}disable(){this.disabled||(this.styleEl&&(this.styleEl.remove(),this.styleEl=null),this.debugEl&&(this.debugEl.remove(),this.debugEl=null),this._onMouseMove&&(document.removeEventListener("mousemove",this._onMouseMove),this._onMouseMove=null),this.disabled=!0,this.options.debug&&console.log("[AnimeCursor] Disabled"))}enable(){this.disabled&&(this.disabled=!1,this._injectStyles(),this.options.debug&&this._initDebug(),this.options.debug&&console.log("[AnimeCursor] Enabled"))}}});
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).AnimeCursor=t()}(this,function(){"use strict";let e=null;return class{static get instance(){return e}static destroy(){return!!e&&(e.destroy(),!0)}static refresh(){return!!e&&(e.refresh(),!0)}static disable(){return!!e&&(e.disable(),!0)}static enable(){return!!e&&(e.enable(),!0)}constructor(t={}){return e?(console.warn("[AnimeCursor] Instance already exists, returning existing one"),e):(this.options={debug:!1,enableTouch:!1,fallbackCursor:"auto",excludeSelectors:"input, textarea, [contenteditable]",combineAnimations:!1,...t},this.disabled=!1,this.cursors=this.options.cursors||{},this.cursorAnimationStrings={},this.combinedRules=new Map,this.options.enableTouch||this.isMouseLikeDevice()?(this.styleEl=null,this.debugEl=null,this.crosshairEl=null,this._onMouseMove=null,this._validateOptions(),this._preloadImages(),this._checkDomLoad(),void(e=this)):(this.disabled=!0,void(this.options.debug&&console.warn("[AnimeCursor] Touch device detected, cursor animations disabled"))))}isMouseLikeDevice(){return window.matchMedia("(pointer: fine)").matches}_validateOptions(){if(this.disabled)return;if(!this.cursors||0===Object.keys(this.cursors).length)throw new Error("[AnimeCursor] At least one cursor must be defined");let e=!1;for(const[t,s]of Object.entries(this.cursors)){if(!s.image)throw new Error(`[AnimeCursor] Cursor "${t}" missing required setting: image`);if(void 0!==s.frames&&void 0!==s.duration){if(typeof s.frames!==typeof s.duration)console.warn(`[AnimeCursor] Cursor "${t}" has mismatched types for frames and duration, treating as static cursor`),delete s.frames,delete s.duration;else if(Array.isArray(s.frames)&&Array.isArray(s.duration))if(s.frames.length!==s.duration.length)console.warn(`[AnimeCursor] Cursor "${t}" frames and duration arrays have different lengths, treating as static cursor`),delete s.frames,delete s.duration;else{for(let e of s.frames)if(!Number.isInteger(e)||e<=0){console.warn(`[AnimeCursor] Cursor "${t}" frames array contains invalid value, treating as static cursor`),delete s.frames,delete s.duration;break}for(let e of s.duration)if("number"!=typeof e||e<=0){console.warn(`[AnimeCursor] Cursor "${t}" duration array contains invalid value, treating as static cursor`),delete s.frames,delete s.duration;break}}else"number"==typeof s.frames&&"number"==typeof s.duration?(s.frames<=0||s.duration<=0)&&(console.warn(`[AnimeCursor] Cursor "${t}" frames or duration <= 0, treating as static cursor`),delete s.frames,delete s.duration):(console.warn(`[AnimeCursor] Cursor "${t}" frames and duration must be both numbers or both arrays, treating as static cursor`),delete s.frames,delete s.duration)}else void 0===s.frames&&void 0===s.duration||(console.warn(`[AnimeCursor] Cursor "${t}" has only frames or duration defined, treating as static cursor`),delete s.frames,delete s.duration);if(s.tags&&!Array.isArray(s.tags))throw new Error(`[AnimeCursor] Cursor "${t}" tags must be an array`);if(s.default){if(e)throw new Error("[AnimeCursor] Only one default cursor allowed");e=!0}if(s.offset&&(!Array.isArray(s.offset)||2!==s.offset.length))throw new Error(`[AnimeCursor] Cursor "${t}" offset must be [x, y] array`)}this.defaultCursorName=e?Object.keys(this.cursors).find(e=>this.cursors[e].default):null}_preloadImages(){const e=new Set;for(const t of Object.values(this.cursors)){this._getFrameUrls(t).forEach(t=>e.add(t))}e.forEach(e=>{const t=document.createElement("link");t.rel="preload",t.as="image",t.href=e,e.startsWith("http")&&!e.startsWith(window.location.origin)&&(t.crossOrigin="anonymous"),document.head.appendChild(t)}),this.options.debug&&e.size&&console.info(`[AnimeCursor] Preloaded ${e.size} cursor images`)}_getFrameUrls(e){let t=1;void 0!==e.frames&&(Array.isArray(e.frames)?t=e.frames.reduce((e,t)=>e+t,0):"number"==typeof e.frames&&(t=e.frames));const{image:s}=e;if(1===t)return[s];const{prefix:r,suffix:o,startNum:n,numFormat:i,ext:a}=this._parseImagePattern(s),u=[];for(let e=0;e<t;e++){const t=n+e,s=`${r}${i?this._formatNumber(t,i):t}${o}${a}`;u.push(s)}return u}_parseImagePattern(e){const t=e.match(/\.[^.]+$/),s=t?t[0]:"",r=e.slice(0,-s.length),o=r.match(/(\d+)(?!.*\d)/);if(!o)return{prefix:r+"_",suffix:"",startNum:1,numFormat:null,ext:s};const n=o[0],i=parseInt(n,10),a=n.length;return{prefix:r.slice(0,o.index),suffix:r.slice(o.index+n.length),startNum:i,numFormat:a,ext:s}}_formatNumber(e,t){return String(e).padStart(t,"0")}_checkDomLoad(){const e=()=>{this._injectStyles(),this.options.debug&&this._initDebug(),console.log("[AnimeCursor] Initialization complete")};"loading"===document.readyState?document.addEventListener("DOMContentLoaded",e):e()}_injectStyles(){if(this.disabled)return;this.combinedRules.clear();const e=document.createElement("style");e.id="animecursor-styles";let t="";if(this.defaultCursorName){const e=this.cursors[this.defaultCursorName];t+=`* { ${this._buildCursorCss(this.defaultCursorName,e)} }\n`}for(const[e,s]of Object.entries(this.cursors)){const r=`.ac-cursor-${e}`,o=s.offset||[0,0],n=s.fallback||this.options.fallbackCursor,i=this._getFrameUrls(s),a=i.length;let u="";if(void 0!==s.frames&&void 0!==s.duration&&(Array.isArray(s.frames)&&Array.isArray(s.duration)||"number"==typeof s.frames&&"number"==typeof s.duration)&&a>1){const a=`ac_anim_${e}`;let l=`@keyframes ${a} {\n`;const c=this._buildKeyframes(s,i);for(const e of c){let t=(100*e.percent).toFixed(5);1===e.percent&&(t="100");l+=` ${t}% { ${`cursor: url("${e.url}") ${o[0]} ${o[1]}, ${n};`} }\n`}l+="}\n",t+=l;const d=`${a} ${Array.isArray(s.duration)?s.duration.reduce((e,t)=>e+t,0):s.duration}s steps(1) infinite ${s.pingpong?"alternate":""}`;u=d,t+=`${r} { cursor: url("${i[0]}") ${o[0]} ${o[1]}, ${n}; animation: ${d}; }\n`}else{const s=`ac_anim_${e}_static`;t+=`@keyframes ${s} {\n`,t+=` 0%, 100% { cursor: url("${i[0]}") ${o[0]} ${o[1]}, ${n}; }\n`,t+="}\n";const a=`${s} 0.001s forwards steps(1)`;u=a,t+=`${r} { cursor: url("${i[0]}") ${o[0]} ${o[1]}, ${n}; animation: ${a}; }\n`}if(this.cursorAnimationStrings[e]=u,s.tags&&s.tags.length){t+=`${s.tags.join(", ")} { ${this._buildCursorCss(e,s)} }\n`}t+=`[data-cursor="${e}"] { ${this._buildCursorCss(e,s)} }\n`}if(this.options.excludeSelectors&&(t+=`${this.options.excludeSelectors} { cursor: text !important; animation: none !important; }\n`),this.options.combineAnimations){const e=document.querySelectorAll("[data-ac-animation]");for(const s of e){const e=s.getAttribute("data-ac-animation");if(!e)continue;let r=this._getCursorTypeForElement(s);if(!r)continue;const o=this.cursorAnimationStrings[r];if(!o)continue;const n=`${r}:${e}`;if(!this.combinedRules.has(n)){const s=`ac-combined-${this._simpleHash(n)}`;t+=`.${s} { animation: ${o}, ${e}; }\n`,this.combinedRules.set(n,s)}const i=this.combinedRules.get(n);s.classList.add(i)}}t+="body.animecursor-disabled * { cursor: auto !important; animation: none !important; }\n",e.textContent=t,document.head.appendChild(e),this.styleEl=e}_getCursorTypeForElement(e){if(e.dataset.cursor&&this.cursors[e.dataset.cursor])return e.dataset.cursor;for(const[t,s]of Object.entries(this.cursors))if(s.tags&&s.tags.some(t=>e.matches(t)))return t;return this.defaultCursorName}_buildKeyframes(e,t){let s=e.frames,r=e.duration;const o=t.length;if("number"==typeof s){const e=r/s;s=new Array(s).fill(1),r=new Array(s.length).fill(e)}const n=[];let i=r.reduce((e,t)=>e+t,0),a=0,u=0;for(let e=0;e<s.length;e++){const o=s[e],l=r[e]/o;for(let e=0;e<o;e++){const e=a/i;n.push({percent:e,url:t[u]}),a+=l,u++}}return n.push({percent:1,url:t[o-1]}),n}_buildCursorCss(e,t){const s=this._getFrameUrls(t),r=t.offset||[0,0],o=t.fallback||this.options.fallbackCursor;let n=`cursor: url("${s[0]}") ${r[0]} ${r[1]}, ${o};`;if(void 0!==t.frames&&void 0!==t.duration&&(Array.isArray(t.frames)&&Array.isArray(t.duration)||"number"==typeof t.frames&&"number"==typeof t.duration)&&s.length>1){n+=` animation: ac_anim_${e} ${Array.isArray(t.duration)?t.duration.reduce((e,t)=>e+t,0):t.duration}s steps(1) infinite ${t.pingpong?"alternate":""};`}else n+=` animation: ac_anim_${e}_static 0.001s forwards steps(1);`;return n}_simpleHash(e){let t=0;for(let s=0;s<e.length;s++){t=(t<<5)-t+e.charCodeAt(s),t|=0}return Math.abs(t).toString(36)}_initDebug(){const e=document.createElement("div");e.className="animecursor-debug",e.style.cssText="\n position: fixed;\n top: 0;\n left: 0;\n background: rgba(0,0,0,0.7);\n color: #0f0;\n padding: 4px 8px;\n font-family: monospace;\n font-size: 12px;\n z-index: 2147483647;\n pointer-events: auto;\n white-space: nowrap;\n transition: opacity 0.2s ease;\n ",e.addEventListener("mouseenter",()=>{e.style.opacity="0.5"}),e.addEventListener("mouseleave",()=>{e.style.opacity="1"}),document.body.appendChild(e),this.debugEl=e;const t=document.createElement("div");t.className="animecursor-crosshair",t.style.cssText="\n position: fixed;\n top: 0;\n left: 0;\n width: 0;\n height: 0;\n pointer-events: none;\n z-index: 2147483646;\n ";const s=document.createElement("div");s.className="ac-crosshair-h",s.style.cssText="\n position: fixed;\n left: 0;\n width: 100%;\n height: 1px;\n background-color: rgba(255,0,0,0.6);\n pointer-events: none;\n transform: translateY(-0.5px);\n ";const r=document.createElement("div");r.className="ac-crosshair-v",r.style.cssText="\n position: fixed;\n top: 0;\n width: 1px;\n height: 100%;\n background-color: rgba(255,0,0,0.6);\n pointer-events: none;\n transform: translateX(-0.5px);\n ",t.appendChild(s),t.appendChild(r),document.body.appendChild(t),this.crosshairEl={container:t,hLine:s,vLine:r};let o="";this._onMouseMove=t=>{const s=t.clientX,r=t.clientY;this.crosshairEl&&(this.crosshairEl.hLine.style.top=r+"px",this.crosshairEl.vLine.style.left=s+"px");const n=document.elementFromPoint(s,r);let i=null;if(n)if(n.dataset.cursor&&this.cursors[n.dataset.cursor])i=n.dataset.cursor;else for(const[e,t]of Object.entries(this.cursors))if(t.tags&&t.tags.some(e=>n.matches(e))){i=e;break}i||this.defaultCursorName?!i&&this.defaultCursorName&&(i=this.defaultCursorName):i="native",i!==o?(o=i,e.textContent=`${i} @ (${s}, ${r})`):e.textContent=`${i} @ (${s}, ${r})`},document.addEventListener("mousemove",this._onMouseMove)}refresh(){this.disabled||(this.styleEl&&this.styleEl.remove(),this.combinedRules.clear(),this._injectStyles(),this.options.debug&&(this.debugEl&&this.debugEl.remove(),this.crosshairEl&&this.crosshairEl.container.remove(),this._initDebug()),console.log("[AnimeCursor] Refresh complete"))}destroy(){this.disabled||(this.styleEl&&this.styleEl.remove(),this.debugEl&&this.debugEl.remove(),this.crosshairEl&&this.crosshairEl.container.remove(),this._onMouseMove&&document.removeEventListener("mousemove",this._onMouseMove),document.body.classList.remove("animecursor-disabled"),e=null,console.log("[AnimeCursor] Destroyed"))}disable(){this.disabled||(this.styleEl&&(this.styleEl.remove(),this.styleEl=null),this.debugEl&&(this.debugEl.remove(),this.debugEl=null),this.crosshairEl&&(this.crosshairEl.container.remove(),this.crosshairEl=null),this._onMouseMove&&(document.removeEventListener("mousemove",this._onMouseMove),this._onMouseMove=null),this.disabled=!0,this.options.debug&&console.log("[AnimeCursor] Disabled"))}enable(){this.disabled&&(this.disabled=!1,this._injectStyles(),this.options.debug&&this._initDebug(),this.options.debug&&console.log("[AnimeCursor] Enabled"))}}});
|