anime-cursor 0.3.1 → 1.0.0
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/dist/anime-cursor.esm.js +133 -43
- package/dist/anime-cursor.umd.js +133 -43
- package/dist/anime-cursor.umd.min.js +1 -1
- package/package.json +1 -1
package/dist/anime-cursor.esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// AnimeCursor by github@ShuninYu
|
|
2
|
-
//
|
|
2
|
+
// v1.0.0
|
|
3
3
|
|
|
4
4
|
// 静态变量存储唯一实例
|
|
5
5
|
let _instance = null;
|
|
@@ -173,14 +173,14 @@ class AnimeCursor {
|
|
|
173
173
|
// ----------------------------
|
|
174
174
|
_validateOptions() {
|
|
175
175
|
if (this.disabled) return;
|
|
176
|
-
|
|
176
|
+
|
|
177
177
|
if (!this.options || !this.options.cursors) {
|
|
178
178
|
console.error('[AnimeCursor] missing cursors set up');
|
|
179
179
|
throw new Error('AnimeCursor init failed');
|
|
180
180
|
}
|
|
181
|
-
|
|
181
|
+
|
|
182
182
|
this.defaultCursorType = null;
|
|
183
|
-
|
|
183
|
+
|
|
184
184
|
for (const [name, cfg] of Object.entries(this.options.cursors)) {
|
|
185
185
|
if (cfg.default === true) {
|
|
186
186
|
if (this.defaultCursorType) {
|
|
@@ -189,7 +189,7 @@ class AnimeCursor {
|
|
|
189
189
|
this.defaultCursorType = name;
|
|
190
190
|
}
|
|
191
191
|
}
|
|
192
|
-
|
|
192
|
+
|
|
193
193
|
for (const [name, cfg] of Object.entries(this.options.cursors)) {
|
|
194
194
|
const required = ['size', 'image'];
|
|
195
195
|
required.forEach(key => {
|
|
@@ -198,14 +198,51 @@ class AnimeCursor {
|
|
|
198
198
|
throw new Error('AnimeCursor init failed');
|
|
199
199
|
}
|
|
200
200
|
});
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
201
|
+
|
|
202
|
+
// 新增:校验 frames 与 duration 的类型一致性
|
|
203
|
+
if (cfg.frames !== undefined) {
|
|
204
|
+
if (Array.isArray(cfg.frames)) {
|
|
205
|
+
// frames 为数组时,duration 必须为等长数组
|
|
206
|
+
if (!Array.isArray(cfg.duration) || cfg.duration.length !== cfg.frames.length) {
|
|
207
|
+
console.error(`[AnimeCursor] cursor "${name}" has frames as array but duration is not an array of same length`);
|
|
208
|
+
throw new Error('AnimeCursor init failed');
|
|
209
|
+
}
|
|
210
|
+
// 检查 frames 数组元素是否为正整数
|
|
211
|
+
for (let f of cfg.frames) {
|
|
212
|
+
if (!Number.isInteger(f) || f <= 0) {
|
|
213
|
+
console.error(`[AnimeCursor] cursor "${name}" frames array must contain positive integers`);
|
|
214
|
+
throw new Error('AnimeCursor init failed');
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
// 检查 duration 数组元素是否为正数
|
|
218
|
+
for (let d of cfg.duration) {
|
|
219
|
+
if (typeof d !== 'number' || d <= 0) {
|
|
220
|
+
console.error(`[AnimeCursor] cursor "${name}" duration array must contain positive numbers`);
|
|
221
|
+
throw new Error('AnimeCursor init failed');
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
} else if (typeof cfg.frames === 'number') {
|
|
225
|
+
// frames 为数字时,duration 可选,但不能是数组
|
|
226
|
+
if (Array.isArray(cfg.duration)) {
|
|
227
|
+
console.error(`[AnimeCursor] cursor "${name}" frames is number but duration is array, must be consistent`);
|
|
228
|
+
throw new Error('AnimeCursor init failed');
|
|
229
|
+
}
|
|
230
|
+
if (!Number.isInteger(cfg.frames) || cfg.frames <= 0) {
|
|
231
|
+
console.error(`[AnimeCursor] cursor "${name}" frames must be a positive integer`);
|
|
232
|
+
throw new Error('AnimeCursor init failed');
|
|
233
|
+
}
|
|
234
|
+
if (cfg.duration !== undefined && (typeof cfg.duration !== 'number' || cfg.duration <= 0)) {
|
|
235
|
+
console.error(`[AnimeCursor] cursor "${name}" duration must be a positive number`);
|
|
236
|
+
throw new Error('AnimeCursor init failed');
|
|
237
|
+
}
|
|
238
|
+
} else {
|
|
239
|
+
console.error(`[AnimeCursor] cursor "${name}" frames must be a number or an array`);
|
|
240
|
+
throw new Error('AnimeCursor init failed');
|
|
241
|
+
}
|
|
205
242
|
}
|
|
206
|
-
|
|
207
|
-
if (cfg.
|
|
208
|
-
console.error(`[AnimeCursor] cursor "${name}" 's
|
|
243
|
+
|
|
244
|
+
if (cfg.tags !== undefined && !Array.isArray(cfg.tags)) {
|
|
245
|
+
console.error(`[AnimeCursor] cursor "${name}" 's tags must be an array if provided`);
|
|
209
246
|
throw new Error('AnimeCursor init failed');
|
|
210
247
|
}
|
|
211
248
|
}
|
|
@@ -361,57 +398,110 @@ class AnimeCursor {
|
|
|
361
398
|
|
|
362
399
|
/* 每种光标以及debug生成 CSS */
|
|
363
400
|
for (const [type, cfg] of Object.entries(this.options.cursors)) {
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
css += `
|
|
377
|
-
${className} {
|
|
401
|
+
const className = `.cursor-${type}`;
|
|
402
|
+
const size = cfg.size;
|
|
403
|
+
const image = cfg.image;
|
|
404
|
+
const offset = cfg.offset;
|
|
405
|
+
const zIndex = cfg.zIndex;
|
|
406
|
+
const scale = cfg.scale;
|
|
407
|
+
const isGif = image.toLowerCase().endsWith('.gif');
|
|
408
|
+
const pixel = cfg.pixel ? 'pixelated' : 'auto';
|
|
409
|
+
|
|
410
|
+
// 基础样式
|
|
411
|
+
css += `
|
|
412
|
+
${className} {
|
|
378
413
|
width: ${size[0]}px;
|
|
379
414
|
height: ${size[1]}px;
|
|
380
415
|
background-image: url("${image}");
|
|
381
416
|
image-rendering: ${pixel};
|
|
382
417
|
${(scale || offset) ? `transform: ${[scale && `scale(${scale[0]}, ${scale[1]})`, offset && `translate(-${offset[0]}px, -${offset[1]}px)`].filter(Boolean).join(' ')};` : ''}
|
|
383
|
-
|
|
384
418
|
${zIndex !== undefined ? `z-index:${zIndex};` : ''}
|
|
385
|
-
|
|
419
|
+
}`;
|
|
420
|
+
|
|
421
|
+
// 动画生成
|
|
422
|
+
const frames = cfg.frames;
|
|
423
|
+
const duration = cfg.duration;
|
|
386
424
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
425
|
+
// 判断是否使用新逻辑(数组形式)
|
|
426
|
+
if (Array.isArray(frames) && Array.isArray(duration) && frames.length === duration.length) {
|
|
427
|
+
// 计算总帧数和总时长
|
|
428
|
+
const totalFrames = frames.reduce((a, b) => a + b, 0);
|
|
429
|
+
const totalDuration = duration.reduce((a, b) => a + b, 0);
|
|
430
|
+
const hasAnimation = !isGif && totalFrames > 1 && totalDuration > 0;
|
|
393
431
|
|
|
394
432
|
if (hasAnimation) {
|
|
395
433
|
const animName = `animecursor_${type}`;
|
|
434
|
+
// 构建关键帧数组
|
|
435
|
+
const keyframes = [];
|
|
436
|
+
let cumPercent = 0;
|
|
437
|
+
let frameIndex = 0;
|
|
438
|
+
|
|
439
|
+
for (let p = 0; p < frames.length; p++) {
|
|
440
|
+
const segFrames = frames[p];
|
|
441
|
+
const segDuration = duration[p];
|
|
442
|
+
const segPercent = segDuration / totalDuration;
|
|
443
|
+
for (let j = 0; j < segFrames; j++) {
|
|
444
|
+
const startPercent = cumPercent + (j * segPercent) / segFrames;
|
|
445
|
+
keyframes.push({
|
|
446
|
+
percent: startPercent,
|
|
447
|
+
pos: -frameIndex * size[0]
|
|
448
|
+
});
|
|
449
|
+
frameIndex++;
|
|
450
|
+
}
|
|
451
|
+
cumPercent += segPercent;
|
|
452
|
+
}
|
|
453
|
+
// 添加最后一帧的结束点(100%)
|
|
454
|
+
keyframes.push({
|
|
455
|
+
percent: 1.0,
|
|
456
|
+
pos: -(totalFrames - 1) * size[0]
|
|
457
|
+
});
|
|
396
458
|
|
|
459
|
+
// 生成动画属性
|
|
397
460
|
css += `
|
|
398
461
|
${className} {
|
|
399
|
-
|
|
462
|
+
animation: ${animName} ${totalDuration}s infinite ${cfg.pingpong ? 'alternate' : ''};
|
|
463
|
+
}`;
|
|
464
|
+
|
|
465
|
+
// 生成 @keyframes
|
|
466
|
+
css += `
|
|
467
|
+
@keyframes ${animName} {`;
|
|
468
|
+
for (let i = 0; i < keyframes.length; i++) {
|
|
469
|
+
const kf = keyframes[i];
|
|
470
|
+
let percent = (kf.percent * 100).toFixed(5);
|
|
471
|
+
if (kf.percent === 1.0) percent = '100'; // 确保100%显示为整数
|
|
472
|
+
css += `
|
|
473
|
+
${percent}% {
|
|
474
|
+
background-position: ${kf.pos}px 0;
|
|
475
|
+
${i < keyframes.length - 1 ? 'animation-timing-function: steps(1, end);' : ''}
|
|
476
|
+
}`;
|
|
477
|
+
}
|
|
478
|
+
css += `
|
|
479
|
+
}`;
|
|
480
|
+
}
|
|
481
|
+
} else if (typeof frames === 'number' && typeof duration === 'number') {
|
|
482
|
+
// 旧逻辑:均匀动画
|
|
483
|
+
const hasAnimation = !isGif && frames > 1 && duration > 0;
|
|
484
|
+
if (hasAnimation) {
|
|
485
|
+
const animName = `animecursor_${type}`;
|
|
486
|
+
css += `
|
|
487
|
+
${className} {
|
|
488
|
+
animation: ${animName} steps(${frames}) ${duration}s infinite ${cfg.pingpong ? 'alternate' : ''};
|
|
400
489
|
}
|
|
401
490
|
|
|
402
491
|
@keyframes ${animName} {
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
}
|
|
406
|
-
`;
|
|
492
|
+
from { background-position: 0 0; }
|
|
493
|
+
to { background-position: -${size[0] * frames}px 0; }
|
|
494
|
+
}`;
|
|
407
495
|
}
|
|
408
496
|
}
|
|
409
|
-
|
|
410
|
-
style.textContent = css;
|
|
411
|
-
document.head.appendChild(style);
|
|
412
|
-
this.styleEl = style;
|
|
497
|
+
// 若 frames 未定义或为单帧,则不生成动画,仅保留基础样式
|
|
413
498
|
}
|
|
414
499
|
|
|
500
|
+
style.textContent = css;
|
|
501
|
+
document.head.appendChild(style);
|
|
502
|
+
this.styleEl = style;
|
|
503
|
+
}
|
|
504
|
+
|
|
415
505
|
// ----------------------------
|
|
416
506
|
// 给元素自动添加 data-cursor
|
|
417
507
|
// ----------------------------
|
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
|
-
//
|
|
8
|
+
// v1.0.0
|
|
9
9
|
|
|
10
10
|
// 静态变量存储唯一实例
|
|
11
11
|
let _instance = null;
|
|
@@ -179,14 +179,14 @@
|
|
|
179
179
|
// ----------------------------
|
|
180
180
|
_validateOptions() {
|
|
181
181
|
if (this.disabled) return;
|
|
182
|
-
|
|
182
|
+
|
|
183
183
|
if (!this.options || !this.options.cursors) {
|
|
184
184
|
console.error('[AnimeCursor] missing cursors set up');
|
|
185
185
|
throw new Error('AnimeCursor init failed');
|
|
186
186
|
}
|
|
187
|
-
|
|
187
|
+
|
|
188
188
|
this.defaultCursorType = null;
|
|
189
|
-
|
|
189
|
+
|
|
190
190
|
for (const [name, cfg] of Object.entries(this.options.cursors)) {
|
|
191
191
|
if (cfg.default === true) {
|
|
192
192
|
if (this.defaultCursorType) {
|
|
@@ -195,7 +195,7 @@
|
|
|
195
195
|
this.defaultCursorType = name;
|
|
196
196
|
}
|
|
197
197
|
}
|
|
198
|
-
|
|
198
|
+
|
|
199
199
|
for (const [name, cfg] of Object.entries(this.options.cursors)) {
|
|
200
200
|
const required = ['size', 'image'];
|
|
201
201
|
required.forEach(key => {
|
|
@@ -204,14 +204,51 @@
|
|
|
204
204
|
throw new Error('AnimeCursor init failed');
|
|
205
205
|
}
|
|
206
206
|
});
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
207
|
+
|
|
208
|
+
// 新增:校验 frames 与 duration 的类型一致性
|
|
209
|
+
if (cfg.frames !== undefined) {
|
|
210
|
+
if (Array.isArray(cfg.frames)) {
|
|
211
|
+
// frames 为数组时,duration 必须为等长数组
|
|
212
|
+
if (!Array.isArray(cfg.duration) || cfg.duration.length !== cfg.frames.length) {
|
|
213
|
+
console.error(`[AnimeCursor] cursor "${name}" has frames as array but duration is not an array of same length`);
|
|
214
|
+
throw new Error('AnimeCursor init failed');
|
|
215
|
+
}
|
|
216
|
+
// 检查 frames 数组元素是否为正整数
|
|
217
|
+
for (let f of cfg.frames) {
|
|
218
|
+
if (!Number.isInteger(f) || f <= 0) {
|
|
219
|
+
console.error(`[AnimeCursor] cursor "${name}" frames array must contain positive integers`);
|
|
220
|
+
throw new Error('AnimeCursor init failed');
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
// 检查 duration 数组元素是否为正数
|
|
224
|
+
for (let d of cfg.duration) {
|
|
225
|
+
if (typeof d !== 'number' || d <= 0) {
|
|
226
|
+
console.error(`[AnimeCursor] cursor "${name}" duration array must contain positive numbers`);
|
|
227
|
+
throw new Error('AnimeCursor init failed');
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
} else if (typeof cfg.frames === 'number') {
|
|
231
|
+
// frames 为数字时,duration 可选,但不能是数组
|
|
232
|
+
if (Array.isArray(cfg.duration)) {
|
|
233
|
+
console.error(`[AnimeCursor] cursor "${name}" frames is number but duration is array, must be consistent`);
|
|
234
|
+
throw new Error('AnimeCursor init failed');
|
|
235
|
+
}
|
|
236
|
+
if (!Number.isInteger(cfg.frames) || cfg.frames <= 0) {
|
|
237
|
+
console.error(`[AnimeCursor] cursor "${name}" frames must be a positive integer`);
|
|
238
|
+
throw new Error('AnimeCursor init failed');
|
|
239
|
+
}
|
|
240
|
+
if (cfg.duration !== undefined && (typeof cfg.duration !== 'number' || cfg.duration <= 0)) {
|
|
241
|
+
console.error(`[AnimeCursor] cursor "${name}" duration must be a positive number`);
|
|
242
|
+
throw new Error('AnimeCursor init failed');
|
|
243
|
+
}
|
|
244
|
+
} else {
|
|
245
|
+
console.error(`[AnimeCursor] cursor "${name}" frames must be a number or an array`);
|
|
246
|
+
throw new Error('AnimeCursor init failed');
|
|
247
|
+
}
|
|
211
248
|
}
|
|
212
|
-
|
|
213
|
-
if (cfg.
|
|
214
|
-
console.error(`[AnimeCursor] cursor "${name}" 's
|
|
249
|
+
|
|
250
|
+
if (cfg.tags !== undefined && !Array.isArray(cfg.tags)) {
|
|
251
|
+
console.error(`[AnimeCursor] cursor "${name}" 's tags must be an array if provided`);
|
|
215
252
|
throw new Error('AnimeCursor init failed');
|
|
216
253
|
}
|
|
217
254
|
}
|
|
@@ -367,57 +404,110 @@
|
|
|
367
404
|
|
|
368
405
|
/* 每种光标以及debug生成 CSS */
|
|
369
406
|
for (const [type, cfg] of Object.entries(this.options.cursors)) {
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
css += `
|
|
383
|
-
${className} {
|
|
407
|
+
const className = `.cursor-${type}`;
|
|
408
|
+
const size = cfg.size;
|
|
409
|
+
const image = cfg.image;
|
|
410
|
+
const offset = cfg.offset;
|
|
411
|
+
const zIndex = cfg.zIndex;
|
|
412
|
+
const scale = cfg.scale;
|
|
413
|
+
const isGif = image.toLowerCase().endsWith('.gif');
|
|
414
|
+
const pixel = cfg.pixel ? 'pixelated' : 'auto';
|
|
415
|
+
|
|
416
|
+
// 基础样式
|
|
417
|
+
css += `
|
|
418
|
+
${className} {
|
|
384
419
|
width: ${size[0]}px;
|
|
385
420
|
height: ${size[1]}px;
|
|
386
421
|
background-image: url("${image}");
|
|
387
422
|
image-rendering: ${pixel};
|
|
388
423
|
${(scale || offset) ? `transform: ${[scale && `scale(${scale[0]}, ${scale[1]})`, offset && `translate(-${offset[0]}px, -${offset[1]}px)`].filter(Boolean).join(' ')};` : ''}
|
|
389
|
-
|
|
390
424
|
${zIndex !== undefined ? `z-index:${zIndex};` : ''}
|
|
391
|
-
|
|
425
|
+
}`;
|
|
426
|
+
|
|
427
|
+
// 动画生成
|
|
428
|
+
const frames = cfg.frames;
|
|
429
|
+
const duration = cfg.duration;
|
|
392
430
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
431
|
+
// 判断是否使用新逻辑(数组形式)
|
|
432
|
+
if (Array.isArray(frames) && Array.isArray(duration) && frames.length === duration.length) {
|
|
433
|
+
// 计算总帧数和总时长
|
|
434
|
+
const totalFrames = frames.reduce((a, b) => a + b, 0);
|
|
435
|
+
const totalDuration = duration.reduce((a, b) => a + b, 0);
|
|
436
|
+
const hasAnimation = !isGif && totalFrames > 1 && totalDuration > 0;
|
|
399
437
|
|
|
400
438
|
if (hasAnimation) {
|
|
401
439
|
const animName = `animecursor_${type}`;
|
|
440
|
+
// 构建关键帧数组
|
|
441
|
+
const keyframes = [];
|
|
442
|
+
let cumPercent = 0;
|
|
443
|
+
let frameIndex = 0;
|
|
444
|
+
|
|
445
|
+
for (let p = 0; p < frames.length; p++) {
|
|
446
|
+
const segFrames = frames[p];
|
|
447
|
+
const segDuration = duration[p];
|
|
448
|
+
const segPercent = segDuration / totalDuration;
|
|
449
|
+
for (let j = 0; j < segFrames; j++) {
|
|
450
|
+
const startPercent = cumPercent + (j * segPercent) / segFrames;
|
|
451
|
+
keyframes.push({
|
|
452
|
+
percent: startPercent,
|
|
453
|
+
pos: -frameIndex * size[0]
|
|
454
|
+
});
|
|
455
|
+
frameIndex++;
|
|
456
|
+
}
|
|
457
|
+
cumPercent += segPercent;
|
|
458
|
+
}
|
|
459
|
+
// 添加最后一帧的结束点(100%)
|
|
460
|
+
keyframes.push({
|
|
461
|
+
percent: 1.0,
|
|
462
|
+
pos: -(totalFrames - 1) * size[0]
|
|
463
|
+
});
|
|
402
464
|
|
|
465
|
+
// 生成动画属性
|
|
403
466
|
css += `
|
|
404
467
|
${className} {
|
|
405
|
-
|
|
468
|
+
animation: ${animName} ${totalDuration}s infinite ${cfg.pingpong ? 'alternate' : ''};
|
|
469
|
+
}`;
|
|
470
|
+
|
|
471
|
+
// 生成 @keyframes
|
|
472
|
+
css += `
|
|
473
|
+
@keyframes ${animName} {`;
|
|
474
|
+
for (let i = 0; i < keyframes.length; i++) {
|
|
475
|
+
const kf = keyframes[i];
|
|
476
|
+
let percent = (kf.percent * 100).toFixed(5);
|
|
477
|
+
if (kf.percent === 1.0) percent = '100'; // 确保100%显示为整数
|
|
478
|
+
css += `
|
|
479
|
+
${percent}% {
|
|
480
|
+
background-position: ${kf.pos}px 0;
|
|
481
|
+
${i < keyframes.length - 1 ? 'animation-timing-function: steps(1, end);' : ''}
|
|
482
|
+
}`;
|
|
483
|
+
}
|
|
484
|
+
css += `
|
|
485
|
+
}`;
|
|
486
|
+
}
|
|
487
|
+
} else if (typeof frames === 'number' && typeof duration === 'number') {
|
|
488
|
+
// 旧逻辑:均匀动画
|
|
489
|
+
const hasAnimation = !isGif && frames > 1 && duration > 0;
|
|
490
|
+
if (hasAnimation) {
|
|
491
|
+
const animName = `animecursor_${type}`;
|
|
492
|
+
css += `
|
|
493
|
+
${className} {
|
|
494
|
+
animation: ${animName} steps(${frames}) ${duration}s infinite ${cfg.pingpong ? 'alternate' : ''};
|
|
406
495
|
}
|
|
407
496
|
|
|
408
497
|
@keyframes ${animName} {
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
}
|
|
412
|
-
`;
|
|
498
|
+
from { background-position: 0 0; }
|
|
499
|
+
to { background-position: -${size[0] * frames}px 0; }
|
|
500
|
+
}`;
|
|
413
501
|
}
|
|
414
502
|
}
|
|
415
|
-
|
|
416
|
-
style.textContent = css;
|
|
417
|
-
document.head.appendChild(style);
|
|
418
|
-
this.styleEl = style;
|
|
503
|
+
// 若 frames 未定义或为单帧,则不生成动画,仅保留基础样式
|
|
419
504
|
}
|
|
420
505
|
|
|
506
|
+
style.textContent = css;
|
|
507
|
+
document.head.appendChild(style);
|
|
508
|
+
this.styleEl = style;
|
|
509
|
+
}
|
|
510
|
+
|
|
421
511
|
// ----------------------------
|
|
422
512
|
// 给元素自动添加 data-cursor
|
|
423
513
|
// ----------------------------
|
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(e,s){"object"==typeof exports&&"undefined"!=typeof module?module.exports=s():"function"==typeof define&&define.amd?define(s):(e="undefined"!=typeof globalThis?globalThis:e||self).AnimeCursor=s()}(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(s={}){return e?(console.warn("[AnimeCursor] AnimeCursor already exists."),e):(this.options={displayOnLoad:!1,enableTouch:!1,debug:!1,...s},this.disabled=!1,this.options.enableTouch||this.isMouseLikeDevice()?(this.cursorEl=null,this.lastCursorType=null,this.debugEl=null,this.styleEl=null,this._onMouseMove=null,this._validateOptions(),this._injectPreload(),this._checkDomLoad(),void(e=this)):(this.disabled=!0,void(this.options.debug&&console.warn("[AnimeCursor] Touch device detected, cursor disabled."))))}isMouseLikeDevice(){if(!this.disabled)return window.matchMedia("(pointer: fine)").matches}refresh(){this.disabled||(this.options.debug&&console.info("[AnimeCursor] starting refresh..."),this._bindElements(!0))}destroy(){if(!this.disabled){this._onMouseMove&&(document.removeEventListener("mousemove",this._onMouseMove),this._onMouseMove=null),this.cursorEl&&(this.cursorEl.remove(),this.cursorEl=null),this.debugEl&&(this.debugEl.remove(),this.debugEl=null),this.styleEl&&(this.styleEl.remove(),this.styleEl=null);for(const e of Object.values(this.options.cursors))e.tags&&Array.isArray(e.tags)&&e.tags.forEach(e=>{document.querySelectorAll(e).forEach(e=>{e.dataset.cursorBound&&(delete e.dataset.cursor,delete e.dataset.cursorBound)})});this.lastCursorType=null,e===this&&(e=null)}}disable(){this.disabled||(this.disabled=!0,this.cursorEl&&(this.cursorEl.style.display="none",this.styleEl.innerHTML=this.styleEl.innerHTML.replace("* {cursor: none !important;}",""),console.log("[AnimeCursor] AnimeCursor disabled!")))}enable(){this.disabled&&(this.disabled=!1,this.cursorEl&&(this.cursorEl.style.display="",this.styleEl.innerHTML+="* {cursor: none; !important;}",console.log("[AnimeCursor] AnimeCursor enabled!")))}_validateOptions(){if(!this.disabled){if(!this.options||!this.options.cursors)throw console.error("[AnimeCursor] missing cursors set up"),new Error("AnimeCursor init failed");this.defaultCursorType=null;for(const[e,s]of Object.entries(this.options.cursors))if(!0===s.default){if(this.defaultCursorType)throw new Error("[AnimeCursor] There can only be one default cursor");this.defaultCursorType=e}for(const[e,s]of Object.entries(this.options.cursors)){if(["size","image"].forEach(t=>{if(void 0===s[t])throw console.error(`[AnimeCursor] cursor "${e}" missing required setting: ${t}`),new Error("AnimeCursor init failed")}),void 0!==s.tags&&!Array.isArray(s.tags))throw console.error(`[AnimeCursor] default cursor "${e}" 's tags must be an array if provided`),new Error("AnimeCursor init failed");if(void 0!==s.duration&&"number"!=typeof s.duration)throw console.error(`[AnimeCursor] cursor "${e}" 's duration must be a number(seconds)`),new Error("AnimeCursor init failed")}}}_checkDomLoad(){const e=()=>{this._injectHTML(),this._injectCSS(),this._bindElements(),this._bindMouse()};"loading"===document.readyState?document.addEventListener("DOMContentLoaded",e):e()}_injectPreload(){if(this.disabled)return;const e=new Set;for(const s of Object.values(this.options.cursors))s.image&&e.add(s.image);e.forEach(e=>{const s=document.createElement("link");s.rel="preload",s.as="image",s.href=e,e.startsWith("http")&&!e.startsWith(window.location.origin)&&(s.crossOrigin="anonymous"),document.head.appendChild(s),this.options.debug&&console.info(`[AnimeCursor] Preloading image: ${e}`)}),this.options.debug&&e.size>0&&console.info(`[AnimeCursor] Preloaded ${e.size} cursor image(s)`)}_injectHTML(){if(this.disabled)return;const e=document.createElement("div");if(e.id="anime-cursor",this.options.debug){e.className="cursor-default cursor-debugmode";const s=document.createElement("div");s.className="anime-cursor-debug",document.body.appendChild(s),this.debugEl=s}else e.className="cursor-default";this.options.displayOnLoad?e.style.display="block":(e.style.display="none",e.dataset.animecursorHide="true"),document.body.appendChild(e),this.cursorEl=e}_injectCSS(){if(this.disabled)return;const e=document.createElement("style");e.id="animecursor-styles";let s="";s+=`\n * {cursor: none !important;}\n #anime-cursor {\n position: fixed;\n top: 0;\n left: 0;\n pointer-events: none;\n background-repeat: no-repeat;\n transform-origin: 0 0;\n transform-style: preserve-3d;\n z-index: ${this._getMaxZIndex()};\n }\n .cursor-debugmode {\n border: 1px solid green;\n }\n .anime-cursor-debug {\n position: fixed;\n top: 0;\n left: 0;\n width: fit-content;\n height: fit-content;\n padding: 5px;\n font-size: 16px;\n text-wrap: nowrap;\n color: red;\n pointer-events: none;\n overflow: visible;\n z-index: 2147483647;\n }\n .anime-cursor-debug::before {\n position: absolute;\n content: "";\n top: 0;\n left: 0;\n width: 100vw;\n height: 1px;\n background-color: red;\n }\n .anime-cursor-debug::after {\n position: absolute;\n content: "";\n top: 0;\n left: 0;\n width: 1px;\n height: 100vh;\n background-color: red;\n }\n `;for(const[e,o]of Object.entries(this.options.cursors)){const n=`.cursor-${e}`,r=o.size,i=o.frames,l=o.image,d=o.offset,a=o.zIndex,u=o.scale,c=l.toLowerCase().endsWith(".gif");var t;t=o.pixel?"pixelated":"auto",s+=`\n ${n} {\n width: ${r[0]}px;\n height: ${r[1]}px;\n background-image: url("${l}");\n image-rendering: ${t};\n ${u||d?`transform: ${[u&&`scale(${u[0]}, ${u[1]})`,d&&`translate(-${d[0]}px, -${d[1]}px)`].filter(Boolean).join(" ")};`:""}\n \n ${void 0!==a?`z-index:${a};`:""}\n }`;const h=o.duration;if(!c&&i>1&&"number"==typeof h){const t=`animecursor_${e}`;s+=`\n ${n} {\n animation: ${t} steps(${i}) ${h}s infinite ${o.pingpong?"alternate":""};\n }\n\n @keyframes ${t} {\n from { background-position: 0 0; }\n to { background-position: -${r[0]*i}px 0; }\n }\n `}}e.textContent=s,document.head.appendChild(e),this.styleEl=e}_bindElements(e){if(!this.disabled){for(const[e,s]of Object.entries(this.options.cursors))s.tags&&Array.isArray(s.tags)&&0!==s.tags.length&&s.tags.forEach(s=>{const t=s.toUpperCase();document.querySelectorAll(t).forEach(s=>{s.dataset.cursor||(s.dataset.cursor=e,s.dataset.cursorBound="true")})});e&&console.info("[AnimeCursor] refresh done")}}_bindMouse(){this.disabled||(this._onMouseMove=e=>{if(this.disabled)return;const s=e.clientX,t=e.clientY;this.cursorEl.style.left=s+"px",this.cursorEl.style.top=t+"px",this.cursorEl.dataset.animecursorHide&&(this.cursorEl.style.display="block"),this.debugEl&&(this.debugEl.style.left=s+"px",this.debugEl.style.top=t+"px");let o=null;const n=document.elementFromPoint(s,t);n&&n.dataset&&n.dataset.cursor?o=n.dataset.cursor:this.defaultCursorType&&(o=this.defaultCursorType),o&&(this.debugEl&&(this.debugEl.textContent=`(${s}px , ${t}px) ${o}`),o!==this.lastCursorType&&(this.debugEl?this.cursorEl.className=`cursor-${o} cursor-debugmode`:this.cursorEl.className=`cursor-${o}`,this.lastCursorType=o))},document.addEventListener("mousemove",this._onMouseMove),console.log("[AnimeCursor] AnimeCursor setted up."))}_getMaxZIndex(){if(!this.disabled)return 2147483646}}});
|
|
1
|
+
!function(e,r){"object"==typeof exports&&"undefined"!=typeof module?module.exports=r():"function"==typeof define&&define.amd?define(r):(e="undefined"!=typeof globalThis?globalThis:e||self).AnimeCursor=r()}(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(r={}){return e?(console.warn("[AnimeCursor] AnimeCursor already exists."),e):(this.options={displayOnLoad:!1,enableTouch:!1,debug:!1,...r},this.disabled=!1,this.options.enableTouch||this.isMouseLikeDevice()?(this.cursorEl=null,this.lastCursorType=null,this.debugEl=null,this.styleEl=null,this._onMouseMove=null,this._validateOptions(),this._injectPreload(),this._checkDomLoad(),void(e=this)):(this.disabled=!0,void(this.options.debug&&console.warn("[AnimeCursor] Touch device detected, cursor disabled."))))}isMouseLikeDevice(){if(!this.disabled)return window.matchMedia("(pointer: fine)").matches}refresh(){this.disabled||(this.options.debug&&console.info("[AnimeCursor] starting refresh..."),this._bindElements(!0))}destroy(){if(!this.disabled){this._onMouseMove&&(document.removeEventListener("mousemove",this._onMouseMove),this._onMouseMove=null),this.cursorEl&&(this.cursorEl.remove(),this.cursorEl=null),this.debugEl&&(this.debugEl.remove(),this.debugEl=null),this.styleEl&&(this.styleEl.remove(),this.styleEl=null);for(const e of Object.values(this.options.cursors))e.tags&&Array.isArray(e.tags)&&e.tags.forEach(e=>{document.querySelectorAll(e).forEach(e=>{e.dataset.cursorBound&&(delete e.dataset.cursor,delete e.dataset.cursorBound)})});this.lastCursorType=null,e===this&&(e=null)}}disable(){this.disabled||(this.disabled=!0,this.cursorEl&&(this.cursorEl.style.display="none",this.styleEl.innerHTML=this.styleEl.innerHTML.replace("* {cursor: none !important;}",""),console.log("[AnimeCursor] AnimeCursor disabled!")))}enable(){this.disabled&&(this.disabled=!1,this.cursorEl&&(this.cursorEl.style.display="",this.styleEl.innerHTML+="* {cursor: none; !important;}",console.log("[AnimeCursor] AnimeCursor enabled!")))}_validateOptions(){if(!this.disabled){if(!this.options||!this.options.cursors)throw console.error("[AnimeCursor] missing cursors set up"),new Error("AnimeCursor init failed");this.defaultCursorType=null;for(const[e,r]of Object.entries(this.options.cursors))if(!0===r.default){if(this.defaultCursorType)throw new Error("[AnimeCursor] There can only be one default cursor");this.defaultCursorType=e}for(const[e,r]of Object.entries(this.options.cursors)){if(["size","image"].forEach(s=>{if(void 0===r[s])throw console.error(`[AnimeCursor] cursor "${e}" missing required setting: ${s}`),new Error("AnimeCursor init failed")}),void 0!==r.frames)if(Array.isArray(r.frames)){if(!Array.isArray(r.duration)||r.duration.length!==r.frames.length)throw console.error(`[AnimeCursor] cursor "${e}" has frames as array but duration is not an array of same length`),new Error("AnimeCursor init failed");for(let s of r.frames)if(!Number.isInteger(s)||s<=0)throw console.error(`[AnimeCursor] cursor "${e}" frames array must contain positive integers`),new Error("AnimeCursor init failed");for(let s of r.duration)if("number"!=typeof s||s<=0)throw console.error(`[AnimeCursor] cursor "${e}" duration array must contain positive numbers`),new Error("AnimeCursor init failed")}else{if("number"!=typeof r.frames)throw console.error(`[AnimeCursor] cursor "${e}" frames must be a number or an array`),new Error("AnimeCursor init failed");if(Array.isArray(r.duration))throw console.error(`[AnimeCursor] cursor "${e}" frames is number but duration is array, must be consistent`),new Error("AnimeCursor init failed");if(!Number.isInteger(r.frames)||r.frames<=0)throw console.error(`[AnimeCursor] cursor "${e}" frames must be a positive integer`),new Error("AnimeCursor init failed");if(void 0!==r.duration&&("number"!=typeof r.duration||r.duration<=0))throw console.error(`[AnimeCursor] cursor "${e}" duration must be a positive number`),new Error("AnimeCursor init failed")}if(void 0!==r.tags&&!Array.isArray(r.tags))throw console.error(`[AnimeCursor] cursor "${e}" 's tags must be an array if provided`),new Error("AnimeCursor init failed")}}}_checkDomLoad(){const e=()=>{this._injectHTML(),this._injectCSS(),this._bindElements(),this._bindMouse()};"loading"===document.readyState?document.addEventListener("DOMContentLoaded",e):e()}_injectPreload(){if(this.disabled)return;const e=new Set;for(const r of Object.values(this.options.cursors))r.image&&e.add(r.image);e.forEach(e=>{const r=document.createElement("link");r.rel="preload",r.as="image",r.href=e,e.startsWith("http")&&!e.startsWith(window.location.origin)&&(r.crossOrigin="anonymous"),document.head.appendChild(r),this.options.debug&&console.info(`[AnimeCursor] Preloading image: ${e}`)}),this.options.debug&&e.size>0&&console.info(`[AnimeCursor] Preloaded ${e.size} cursor image(s)`)}_injectHTML(){if(this.disabled)return;const e=document.createElement("div");if(e.id="anime-cursor",this.options.debug){e.className="cursor-default cursor-debugmode";const r=document.createElement("div");r.className="anime-cursor-debug",document.body.appendChild(r),this.debugEl=r}else e.className="cursor-default";this.options.displayOnLoad?e.style.display="block":(e.style.display="none",e.dataset.animecursorHide="true"),document.body.appendChild(e),this.cursorEl=e}_injectCSS(){if(this.disabled)return;const e=document.createElement("style");e.id="animecursor-styles";let r="";r+=`\n * {cursor: none !important;}\n #anime-cursor {\n position: fixed;\n top: 0;\n left: 0;\n pointer-events: none;\n background-repeat: no-repeat;\n transform-origin: 0 0;\n transform-style: preserve-3d;\n z-index: ${this._getMaxZIndex()};\n }\n .cursor-debugmode {\n border: 1px solid green;\n }\n .anime-cursor-debug {\n position: fixed;\n top: 0;\n left: 0;\n width: fit-content;\n height: fit-content;\n padding: 5px;\n font-size: 16px;\n text-wrap: nowrap;\n color: red;\n pointer-events: none;\n overflow: visible;\n z-index: 2147483647;\n }\n .anime-cursor-debug::before {\n position: absolute;\n content: "";\n top: 0;\n left: 0;\n width: 100vw;\n height: 1px;\n background-color: red;\n }\n .anime-cursor-debug::after {\n position: absolute;\n content: "";\n top: 0;\n left: 0;\n width: 1px;\n height: 100vh;\n background-color: red;\n }\n `;for(const[e,s]of Object.entries(this.options.cursors)){const t=`.cursor-${e}`,o=s.size,n=s.image,i=s.offset,a=s.zIndex,u=s.scale,l=n.toLowerCase().endsWith(".gif"),d=s.pixel?"pixelated":"auto";r+=`\n ${t} {\n width: ${o[0]}px;\n height: ${o[1]}px;\n background-image: url("${n}");\n image-rendering: ${d};\n ${u||i?`transform: ${[u&&`scale(${u[0]}, ${u[1]})`,i&&`translate(-${i[0]}px, -${i[1]}px)`].filter(Boolean).join(" ")};`:""}\n ${void 0!==a?`z-index:${a};`:""}\n }`;const c=s.frames,h=s.duration;if(Array.isArray(c)&&Array.isArray(h)&&c.length===h.length){const n=c.reduce((e,r)=>e+r,0),i=h.reduce((e,r)=>e+r,0);if(!l&&n>1&&i>0){const a=`animecursor_${e}`,u=[];let l=0,d=0;for(let e=0;e<c.length;e++){const r=c[e],s=h[e]/i;for(let e=0;e<r;e++){const t=l+e*s/r;u.push({percent:t,pos:-d*o[0]}),d++}l+=s}u.push({percent:1,pos:-(n-1)*o[0]}),r+=`\n ${t} {\n animation: ${a} ${i}s infinite ${s.pingpong?"alternate":""};\n }`,r+=`\n @keyframes ${a} {`;for(let e=0;e<u.length;e++){const s=u[e];let t=(100*s.percent).toFixed(5);1===s.percent&&(t="100"),r+=`\n ${t}% {\n background-position: ${s.pos}px 0;\n ${e<u.length-1?"animation-timing-function: steps(1, end);":""}\n }`}r+="\n }"}}else if("number"==typeof c&&"number"==typeof h){if(!l&&c>1&&h>0){const n=`animecursor_${e}`;r+=`\n ${t} {\n animation: ${n} steps(${c}) ${h}s infinite ${s.pingpong?"alternate":""};\n }\n\n @keyframes ${n} {\n from { background-position: 0 0; }\n to { background-position: -${o[0]*c}px 0; }\n }`}}}e.textContent=r,document.head.appendChild(e),this.styleEl=e}_bindElements(e){if(!this.disabled){for(const[e,r]of Object.entries(this.options.cursors))r.tags&&Array.isArray(r.tags)&&0!==r.tags.length&&r.tags.forEach(r=>{const s=r.toUpperCase();document.querySelectorAll(s).forEach(r=>{r.dataset.cursor||(r.dataset.cursor=e,r.dataset.cursorBound="true")})});e&&console.info("[AnimeCursor] refresh done")}}_bindMouse(){this.disabled||(this._onMouseMove=e=>{if(this.disabled)return;const r=e.clientX,s=e.clientY;this.cursorEl.style.left=r+"px",this.cursorEl.style.top=s+"px",this.cursorEl.dataset.animecursorHide&&(this.cursorEl.style.display="block"),this.debugEl&&(this.debugEl.style.left=r+"px",this.debugEl.style.top=s+"px");let t=null;const o=document.elementFromPoint(r,s);o&&o.dataset&&o.dataset.cursor?t=o.dataset.cursor:this.defaultCursorType&&(t=this.defaultCursorType),t&&(this.debugEl&&(this.debugEl.textContent=`(${r}px , ${s}px) ${t}`),t!==this.lastCursorType&&(this.debugEl?this.cursorEl.className=`cursor-${t} cursor-debugmode`:this.cursorEl.className=`cursor-${t}`,this.lastCursorType=t))},document.addEventListener("mousemove",this._onMouseMove),console.log("[AnimeCursor] AnimeCursor setted up."))}_getMaxZIndex(){if(!this.disabled)return 2147483646}}});
|