anime-cursor 2.1.1 → 2.1.2
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 -2
- package/dist/anime-cursor.esm.js +16 -6
- package/dist/anime-cursor.umd.js +16 -6
- package/dist/anime-cursor.umd.min.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -33,7 +33,7 @@ AnimeCursor has no dependencies on any frameworks, making it suitable for person
|
|
|
33
33
|
### CDN
|
|
34
34
|
|
|
35
35
|
```html
|
|
36
|
-
<script src="https://cdn.jsdelivr.net/npm/anime-cursor@v2.1.
|
|
36
|
+
<script src="https://cdn.jsdelivr.net/npm/anime-cursor@v2.1.2/dist/anime-cursor.umd.min.js"></script>
|
|
37
37
|
```
|
|
38
38
|
|
|
39
39
|
### npm
|
|
@@ -218,7 +218,7 @@ AnimeCursor 无需依赖任何框架,适合个人网站、创意作品集以
|
|
|
218
218
|
### CDN
|
|
219
219
|
|
|
220
220
|
```html
|
|
221
|
-
<script src="https://cdn.jsdelivr.net/npm/anime-cursor@v2.1.
|
|
221
|
+
<script src="https://cdn.jsdelivr.net/npm/anime-cursor@v2.1.2/dist/anime-cursor.umd.min.js"></script>
|
|
222
222
|
```
|
|
223
223
|
|
|
224
224
|
### npm
|
package/dist/anime-cursor.esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// AnimeCursor by github@ShuninYu
|
|
2
|
-
// v2.1.
|
|
2
|
+
// v2.1.2
|
|
3
3
|
|
|
4
4
|
let _instance = null;
|
|
5
5
|
|
|
@@ -247,7 +247,7 @@ class AnimeCursor {
|
|
|
247
247
|
// 核心注入样式
|
|
248
248
|
_injectStyles() {
|
|
249
249
|
if (this.disabled) return;
|
|
250
|
-
this.combinedRules.clear();
|
|
250
|
+
this.combinedRules.clear();
|
|
251
251
|
|
|
252
252
|
const style = document.createElement('style');
|
|
253
253
|
style.id = 'animecursor-styles';
|
|
@@ -292,7 +292,14 @@ class AnimeCursor {
|
|
|
292
292
|
cursorAnimation = animation;
|
|
293
293
|
css += `${className} { cursor: url("${frameUrls[0]}") ${offset[0]} ${offset[1]}, ${fallback}; animation: ${animation}; }\n`;
|
|
294
294
|
} else {
|
|
295
|
-
|
|
295
|
+
// 静态光标:生成一帧动画,只播放一次,结束后保持最后一帧
|
|
296
|
+
const staticKeyframeName = `ac_anim_${name}_static`;
|
|
297
|
+
css += `@keyframes ${staticKeyframeName} {\n`;
|
|
298
|
+
css += ` 0%, 100% { cursor: url("${frameUrls[0]}") ${offset[0]} ${offset[1]}, ${fallback}; }\n`;
|
|
299
|
+
css += `}\n`;
|
|
300
|
+
const staticAnimation = `${staticKeyframeName} 0.001s forwards steps(1)`;
|
|
301
|
+
cursorAnimation = staticAnimation;
|
|
302
|
+
css += `${className} { cursor: url("${frameUrls[0]}") ${offset[0]} ${offset[1]}, ${fallback}; animation: ${staticAnimation}; }\n`;
|
|
296
303
|
}
|
|
297
304
|
|
|
298
305
|
this.cursorAnimationStrings[name] = cursorAnimation;
|
|
@@ -309,21 +316,19 @@ class AnimeCursor {
|
|
|
309
316
|
css += `${this.options.excludeSelectors} { cursor: text !important; animation: none !important; }\n`;
|
|
310
317
|
}
|
|
311
318
|
|
|
312
|
-
//
|
|
319
|
+
// 自动组合动画
|
|
313
320
|
if (this.options.combineAnimations) {
|
|
314
321
|
const elements = document.querySelectorAll('[data-ac-animation]');
|
|
315
322
|
for (const el of elements) {
|
|
316
323
|
const userAnim = el.getAttribute('data-ac-animation');
|
|
317
324
|
if (!userAnim) continue;
|
|
318
325
|
|
|
319
|
-
// 确定该元素应该使用哪个光标
|
|
320
326
|
let cursorName = this._getCursorTypeForElement(el);
|
|
321
327
|
if (!cursorName) continue;
|
|
322
328
|
|
|
323
329
|
const cursorAnim = this.cursorAnimationStrings[cursorName];
|
|
324
330
|
if (!cursorAnim) continue;
|
|
325
331
|
|
|
326
|
-
// 生成唯一标识
|
|
327
332
|
const key = `${cursorName}:${userAnim}`;
|
|
328
333
|
if (!this.combinedRules.has(key)) {
|
|
329
334
|
const hash = this._simpleHash(key);
|
|
@@ -397,12 +402,17 @@ class AnimeCursor {
|
|
|
397
402
|
const offset = cfg.offset || [0, 0];
|
|
398
403
|
const fallback = cfg.fallback || this.options.fallbackCursor;
|
|
399
404
|
let css = `cursor: url("${frameUrls[0]}") ${offset[0]} ${offset[1]}, ${fallback};`;
|
|
405
|
+
|
|
400
406
|
const hasAnimation = cfg.frames !== undefined && cfg.duration !== undefined &&
|
|
401
407
|
((Array.isArray(cfg.frames) && Array.isArray(cfg.duration)) ||
|
|
402
408
|
(typeof cfg.frames === 'number' && typeof cfg.duration === 'number'));
|
|
409
|
+
|
|
403
410
|
if (hasAnimation && frameUrls.length > 1) {
|
|
404
411
|
const totalDuration = Array.isArray(cfg.duration) ? cfg.duration.reduce((a, b) => a + b, 0) : cfg.duration;
|
|
405
412
|
css += ` animation: ac_anim_${name} ${totalDuration}s steps(1) infinite ${cfg.pingpong ? 'alternate' : ''};`;
|
|
413
|
+
} else {
|
|
414
|
+
// 静态光标:一帧动画,只播放一次,结束后保持
|
|
415
|
+
css += ` animation: ac_anim_${name}_static 0.001s forwards steps(1);`;
|
|
406
416
|
}
|
|
407
417
|
return css;
|
|
408
418
|
}
|
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.2
|
|
9
9
|
|
|
10
10
|
let _instance = null;
|
|
11
11
|
|
|
@@ -253,7 +253,7 @@
|
|
|
253
253
|
// 核心注入样式
|
|
254
254
|
_injectStyles() {
|
|
255
255
|
if (this.disabled) return;
|
|
256
|
-
this.combinedRules.clear();
|
|
256
|
+
this.combinedRules.clear();
|
|
257
257
|
|
|
258
258
|
const style = document.createElement('style');
|
|
259
259
|
style.id = 'animecursor-styles';
|
|
@@ -298,7 +298,14 @@
|
|
|
298
298
|
cursorAnimation = animation;
|
|
299
299
|
css += `${className} { cursor: url("${frameUrls[0]}") ${offset[0]} ${offset[1]}, ${fallback}; animation: ${animation}; }\n`;
|
|
300
300
|
} else {
|
|
301
|
-
|
|
301
|
+
// 静态光标:生成一帧动画,只播放一次,结束后保持最后一帧
|
|
302
|
+
const staticKeyframeName = `ac_anim_${name}_static`;
|
|
303
|
+
css += `@keyframes ${staticKeyframeName} {\n`;
|
|
304
|
+
css += ` 0%, 100% { cursor: url("${frameUrls[0]}") ${offset[0]} ${offset[1]}, ${fallback}; }\n`;
|
|
305
|
+
css += `}\n`;
|
|
306
|
+
const staticAnimation = `${staticKeyframeName} 0.001s forwards steps(1)`;
|
|
307
|
+
cursorAnimation = staticAnimation;
|
|
308
|
+
css += `${className} { cursor: url("${frameUrls[0]}") ${offset[0]} ${offset[1]}, ${fallback}; animation: ${staticAnimation}; }\n`;
|
|
302
309
|
}
|
|
303
310
|
|
|
304
311
|
this.cursorAnimationStrings[name] = cursorAnimation;
|
|
@@ -315,21 +322,19 @@
|
|
|
315
322
|
css += `${this.options.excludeSelectors} { cursor: text !important; animation: none !important; }\n`;
|
|
316
323
|
}
|
|
317
324
|
|
|
318
|
-
//
|
|
325
|
+
// 自动组合动画
|
|
319
326
|
if (this.options.combineAnimations) {
|
|
320
327
|
const elements = document.querySelectorAll('[data-ac-animation]');
|
|
321
328
|
for (const el of elements) {
|
|
322
329
|
const userAnim = el.getAttribute('data-ac-animation');
|
|
323
330
|
if (!userAnim) continue;
|
|
324
331
|
|
|
325
|
-
// 确定该元素应该使用哪个光标
|
|
326
332
|
let cursorName = this._getCursorTypeForElement(el);
|
|
327
333
|
if (!cursorName) continue;
|
|
328
334
|
|
|
329
335
|
const cursorAnim = this.cursorAnimationStrings[cursorName];
|
|
330
336
|
if (!cursorAnim) continue;
|
|
331
337
|
|
|
332
|
-
// 生成唯一标识
|
|
333
338
|
const key = `${cursorName}:${userAnim}`;
|
|
334
339
|
if (!this.combinedRules.has(key)) {
|
|
335
340
|
const hash = this._simpleHash(key);
|
|
@@ -403,12 +408,17 @@
|
|
|
403
408
|
const offset = cfg.offset || [0, 0];
|
|
404
409
|
const fallback = cfg.fallback || this.options.fallbackCursor;
|
|
405
410
|
let css = `cursor: url("${frameUrls[0]}") ${offset[0]} ${offset[1]}, ${fallback};`;
|
|
411
|
+
|
|
406
412
|
const hasAnimation = cfg.frames !== undefined && cfg.duration !== undefined &&
|
|
407
413
|
((Array.isArray(cfg.frames) && Array.isArray(cfg.duration)) ||
|
|
408
414
|
(typeof cfg.frames === 'number' && typeof cfg.duration === 'number'));
|
|
415
|
+
|
|
409
416
|
if (hasAnimation && frameUrls.length > 1) {
|
|
410
417
|
const totalDuration = Array.isArray(cfg.duration) ? cfg.duration.reduce((a, b) => a + b, 0) : cfg.duration;
|
|
411
418
|
css += ` animation: ac_anim_${name} ${totalDuration}s steps(1) infinite ${cfg.pingpong ? 'alternate' : ''};`;
|
|
419
|
+
} else {
|
|
420
|
+
// 静态光标:一帧动画,只播放一次,结束后保持
|
|
421
|
+
css += ` animation: ac_anim_${name}_static 0.001s forwards steps(1);`;
|
|
412
422
|
}
|
|
413
423
|
return css;
|
|
414
424
|
}
|
|
@@ -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 t+=`${r} { cursor: url("${n[0]}") ${o[0]} ${o[1]}, ${i}; }\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":""};`}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._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"))}}});
|