masoneffect 0.1.13 → 0.1.14
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/core/index.d.ts +91 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.esm.js +346 -388
- package/dist/index.esm.js.map +1 -1
- package/dist/index.esm.min.js +1 -1
- package/dist/index.js +346 -388
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.umd.js +380 -422
- package/dist/index.umd.js.map +1 -1
- package/dist/index.umd.min.js +1 -1
- package/dist/react/MasonEffect.cjs +346 -388
- package/dist/react/MasonEffect.cjs.map +1 -1
- package/dist/react/MasonEffect.js +346 -388
- package/dist/react/MasonEffect.js.map +1 -1
- package/dist/react/MasonEffect.min.cjs +1 -1
- package/dist/react/MasonEffect.min.js +1 -1
- package/dist/react/core/index.d.ts +91 -0
- package/dist/react/core/index.d.ts.map +1 -0
- package/dist/react/index.d.ts +8 -0
- package/dist/react/index.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/core/{index.js → index.ts} +109 -38
- package/src/{index.js → index.ts} +1 -0
- package/src/core/index.d.ts +0 -59
|
@@ -3,24 +3,87 @@
|
|
|
3
3
|
* 바닐라 JS 코어 클래스
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
export interface MasonEffectOptions {
|
|
7
|
+
text?: string;
|
|
8
|
+
densityStep?: number;
|
|
9
|
+
maxParticles?: number;
|
|
10
|
+
pointSize?: number;
|
|
11
|
+
ease?: number;
|
|
12
|
+
repelRadius?: number;
|
|
13
|
+
repelStrength?: number;
|
|
14
|
+
particleColor?: string;
|
|
15
|
+
fontFamily?: string;
|
|
16
|
+
fontSize?: number | null;
|
|
17
|
+
width?: number | null;
|
|
18
|
+
height?: number | null;
|
|
19
|
+
devicePixelRatio?: number | null;
|
|
20
|
+
debounceDelay?: number;
|
|
21
|
+
onReady?: (instance: MasonEffect) => void;
|
|
22
|
+
onUpdate?: (instance: MasonEffect) => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface Particle {
|
|
26
|
+
x: number;
|
|
27
|
+
y: number;
|
|
28
|
+
vx: number;
|
|
29
|
+
vy: number;
|
|
30
|
+
tx: number;
|
|
31
|
+
ty: number;
|
|
32
|
+
initialX?: number;
|
|
33
|
+
initialY?: number;
|
|
34
|
+
j: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
6
37
|
// 디바운스 유틸리티 함수
|
|
7
|
-
function debounce(
|
|
8
|
-
|
|
9
|
-
|
|
38
|
+
function debounce<T extends (...args: any[]) => any>(
|
|
39
|
+
func: T,
|
|
40
|
+
wait: number
|
|
41
|
+
): (...args: Parameters<T>) => void {
|
|
42
|
+
let timeout: ReturnType<typeof setTimeout> | null = null;
|
|
43
|
+
return function executedFunction(...args: Parameters<T>) {
|
|
10
44
|
const later = () => {
|
|
11
|
-
|
|
45
|
+
timeout = null;
|
|
12
46
|
func.apply(this, args);
|
|
13
47
|
};
|
|
14
|
-
|
|
48
|
+
if (timeout !== null) {
|
|
49
|
+
clearTimeout(timeout);
|
|
50
|
+
}
|
|
15
51
|
timeout = setTimeout(later, wait);
|
|
16
52
|
};
|
|
17
53
|
}
|
|
18
54
|
|
|
19
55
|
export class MasonEffect {
|
|
20
|
-
|
|
56
|
+
container: HTMLElement;
|
|
57
|
+
config: Required<Omit<MasonEffectOptions, 'onReady' | 'onUpdate'>> & {
|
|
58
|
+
onReady: MasonEffectOptions['onReady'];
|
|
59
|
+
onUpdate: MasonEffectOptions['onUpdate'];
|
|
60
|
+
};
|
|
61
|
+
canvas: HTMLCanvasElement;
|
|
62
|
+
ctx: CanvasRenderingContext2D;
|
|
63
|
+
offCanvas: HTMLCanvasElement;
|
|
64
|
+
offCtx: CanvasRenderingContext2D;
|
|
65
|
+
W: number;
|
|
66
|
+
H: number;
|
|
67
|
+
DPR: number;
|
|
68
|
+
particles: Particle[];
|
|
69
|
+
mouse: { x: number; y: number; down: boolean };
|
|
70
|
+
animationId: number | null;
|
|
71
|
+
isRunning: boolean;
|
|
72
|
+
isVisible: boolean;
|
|
73
|
+
intersectionObserver: IntersectionObserver | null;
|
|
74
|
+
debounceDelay: number;
|
|
75
|
+
handleResize: () => void;
|
|
76
|
+
handleMouseMove: (e: MouseEvent) => void;
|
|
77
|
+
handleMouseLeave: () => void;
|
|
78
|
+
handleMouseDown: () => void;
|
|
79
|
+
handleMouseUp: () => void;
|
|
80
|
+
_debouncedMorph: (textOrOptions?: string | Partial<MasonEffectOptions> | null) => void;
|
|
81
|
+
_debouncedUpdateConfig: (newConfig: Partial<MasonEffectOptions>) => void;
|
|
82
|
+
|
|
83
|
+
constructor(container: HTMLElement | string, options: MasonEffectOptions = {}) {
|
|
21
84
|
// 컨테이너 요소
|
|
22
85
|
this.container = typeof container === 'string'
|
|
23
|
-
? document.querySelector(container)
|
|
86
|
+
? document.querySelector(container) as HTMLElement
|
|
24
87
|
: container;
|
|
25
88
|
|
|
26
89
|
if (!this.container) {
|
|
@@ -38,23 +101,31 @@ export class MasonEffect {
|
|
|
38
101
|
repelStrength: options.repelStrength ?? 1,
|
|
39
102
|
particleColor: options.particleColor || '#fff',
|
|
40
103
|
fontFamily: options.fontFamily || 'Inter, system-ui, Arial',
|
|
41
|
-
fontSize: options.fontSize || null,
|
|
42
|
-
width: options.width || null,
|
|
43
|
-
height: options.height || null,
|
|
44
|
-
devicePixelRatio: options.devicePixelRatio ?? null,
|
|
104
|
+
fontSize: options.fontSize || null,
|
|
105
|
+
width: options.width || null,
|
|
106
|
+
height: options.height || null,
|
|
107
|
+
devicePixelRatio: options.devicePixelRatio ?? null,
|
|
45
108
|
onReady: options.onReady || null,
|
|
46
109
|
onUpdate: options.onUpdate || null,
|
|
47
110
|
};
|
|
48
111
|
|
|
49
112
|
// 캔버스 생성
|
|
50
113
|
this.canvas = document.createElement('canvas');
|
|
51
|
-
|
|
114
|
+
const ctx = this.canvas.getContext('2d');
|
|
115
|
+
if (!ctx) {
|
|
116
|
+
throw new Error('Canvas context not available');
|
|
117
|
+
}
|
|
118
|
+
this.ctx = ctx;
|
|
52
119
|
this.container.appendChild(this.canvas);
|
|
53
120
|
this.canvas.style.display = 'block';
|
|
54
121
|
|
|
55
122
|
// 오프스크린 캔버스 (텍스트 렌더링용)
|
|
56
123
|
this.offCanvas = document.createElement('canvas');
|
|
57
|
-
|
|
124
|
+
const offCtx = this.offCanvas.getContext('2d');
|
|
125
|
+
if (!offCtx) {
|
|
126
|
+
throw new Error('Offscreen canvas context not available');
|
|
127
|
+
}
|
|
128
|
+
this.offCtx = offCtx;
|
|
58
129
|
|
|
59
130
|
// 상태
|
|
60
131
|
this.W = 0;
|
|
@@ -68,7 +139,7 @@ export class MasonEffect {
|
|
|
68
139
|
this.intersectionObserver = null;
|
|
69
140
|
|
|
70
141
|
// 디바운스 설정 (ms)
|
|
71
|
-
this.debounceDelay = options.debounceDelay ?? 150;
|
|
142
|
+
this.debounceDelay = options.debounceDelay ?? 150;
|
|
72
143
|
|
|
73
144
|
// 이벤트 핸들러 바인딩 (디바운스 적용 전에 바인딩)
|
|
74
145
|
const boundHandleResize = this.handleResize.bind(this);
|
|
@@ -86,7 +157,7 @@ export class MasonEffect {
|
|
|
86
157
|
this.init();
|
|
87
158
|
}
|
|
88
159
|
|
|
89
|
-
init() {
|
|
160
|
+
init(): void {
|
|
90
161
|
this.resize();
|
|
91
162
|
this.setupEventListeners();
|
|
92
163
|
this.setupIntersectionObserver();
|
|
@@ -96,7 +167,7 @@ export class MasonEffect {
|
|
|
96
167
|
}
|
|
97
168
|
}
|
|
98
169
|
|
|
99
|
-
setupIntersectionObserver() {
|
|
170
|
+
setupIntersectionObserver(): void {
|
|
100
171
|
// IntersectionObserver가 지원되지 않는 환경에서는 항상 재생
|
|
101
172
|
if (typeof window === 'undefined' || typeof window.IntersectionObserver === 'undefined') {
|
|
102
173
|
this.isVisible = true;
|
|
@@ -131,7 +202,7 @@ export class MasonEffect {
|
|
|
131
202
|
this.intersectionObserver.observe(this.container);
|
|
132
203
|
}
|
|
133
204
|
|
|
134
|
-
resize() {
|
|
205
|
+
resize(): void {
|
|
135
206
|
const width = this.config.width || this.container.clientWidth || window.innerWidth;
|
|
136
207
|
const height = this.config.height || this.container.clientHeight || window.innerHeight * 0.7;
|
|
137
208
|
|
|
@@ -149,7 +220,7 @@ export class MasonEffect {
|
|
|
149
220
|
}
|
|
150
221
|
}
|
|
151
222
|
|
|
152
|
-
buildTargets() {
|
|
223
|
+
buildTargets(): void {
|
|
153
224
|
const text = this.config.text;
|
|
154
225
|
this.offCanvas.width = this.W;
|
|
155
226
|
this.offCanvas.height = this.H;
|
|
@@ -176,7 +247,7 @@ export class MasonEffect {
|
|
|
176
247
|
// 픽셀 샘플링
|
|
177
248
|
const step = Math.max(2, this.config.densityStep);
|
|
178
249
|
const img = this.offCtx.getImageData(0, 0, this.W, this.H).data;
|
|
179
|
-
const targets = [];
|
|
250
|
+
const targets: Array<{ x: number; y: number }> = [];
|
|
180
251
|
|
|
181
252
|
for (let y = 0; y < this.H; y += step) {
|
|
182
253
|
for (let x = 0; x < this.W; x += step) {
|
|
@@ -211,7 +282,7 @@ export class MasonEffect {
|
|
|
211
282
|
}
|
|
212
283
|
}
|
|
213
284
|
|
|
214
|
-
makeParticle() {
|
|
285
|
+
makeParticle(): Particle {
|
|
215
286
|
// 캔버스 전체에 골고루 분포 (여백 없이)
|
|
216
287
|
const sx = Math.random() * this.W;
|
|
217
288
|
const sy = Math.random() * this.H;
|
|
@@ -228,7 +299,7 @@ export class MasonEffect {
|
|
|
228
299
|
};
|
|
229
300
|
}
|
|
230
301
|
|
|
231
|
-
initParticles() {
|
|
302
|
+
initParticles(): void {
|
|
232
303
|
// 캔버스 전체에 골고루 분포 (여백 없이)
|
|
233
304
|
for (const p of this.particles) {
|
|
234
305
|
const sx = Math.random() * this.W;
|
|
@@ -242,7 +313,7 @@ export class MasonEffect {
|
|
|
242
313
|
}
|
|
243
314
|
}
|
|
244
315
|
|
|
245
|
-
scatter() {
|
|
316
|
+
scatter(): void {
|
|
246
317
|
// 각 파티클을 초기 위치로 돌아가도록 설정
|
|
247
318
|
for (const p of this.particles) {
|
|
248
319
|
// 초기 위치가 저장되어 있으면 그 위치로, 없으면 현재 위치 유지
|
|
@@ -259,13 +330,13 @@ export class MasonEffect {
|
|
|
259
330
|
}
|
|
260
331
|
}
|
|
261
332
|
|
|
262
|
-
morph(textOrOptions
|
|
333
|
+
morph(textOrOptions?: string | Partial<MasonEffectOptions> | null): void {
|
|
263
334
|
// 즉시 실행이 필요한 경우 (예: 초기화 시)를 위해 내부 메서드 직접 호출
|
|
264
335
|
// 일반적인 경우에는 디바운스 적용
|
|
265
336
|
this._debouncedMorph(textOrOptions);
|
|
266
337
|
}
|
|
267
338
|
|
|
268
|
-
_morphInternal(textOrOptions
|
|
339
|
+
_morphInternal(textOrOptions?: string | Partial<MasonEffectOptions> | null): void {
|
|
269
340
|
// W와 H가 0이면 resize 먼저 실행
|
|
270
341
|
if (this.W === 0 || this.H === 0) {
|
|
271
342
|
this.resize();
|
|
@@ -288,7 +359,7 @@ export class MasonEffect {
|
|
|
288
359
|
}
|
|
289
360
|
}
|
|
290
361
|
|
|
291
|
-
update() {
|
|
362
|
+
update(): void {
|
|
292
363
|
this.ctx.clearRect(0, 0, this.W, this.H);
|
|
293
364
|
|
|
294
365
|
for (const p of this.particles) {
|
|
@@ -336,19 +407,19 @@ export class MasonEffect {
|
|
|
336
407
|
}
|
|
337
408
|
}
|
|
338
409
|
|
|
339
|
-
animate() {
|
|
410
|
+
animate(): void {
|
|
340
411
|
if (!this.isRunning) return;
|
|
341
412
|
this.update();
|
|
342
413
|
this.animationId = requestAnimationFrame(() => this.animate());
|
|
343
414
|
}
|
|
344
415
|
|
|
345
|
-
start() {
|
|
416
|
+
start(): void {
|
|
346
417
|
if (this.isRunning) return;
|
|
347
418
|
this.isRunning = true;
|
|
348
419
|
this.animate();
|
|
349
420
|
}
|
|
350
421
|
|
|
351
|
-
stop() {
|
|
422
|
+
stop(): void {
|
|
352
423
|
this.isRunning = false;
|
|
353
424
|
if (this.animationId) {
|
|
354
425
|
cancelAnimationFrame(this.animationId);
|
|
@@ -356,7 +427,7 @@ export class MasonEffect {
|
|
|
356
427
|
}
|
|
357
428
|
}
|
|
358
429
|
|
|
359
|
-
setupEventListeners() {
|
|
430
|
+
setupEventListeners(): void {
|
|
360
431
|
window.addEventListener('resize', this.handleResize);
|
|
361
432
|
this.canvas.addEventListener('mousemove', this.handleMouseMove);
|
|
362
433
|
this.canvas.addEventListener('mouseleave', this.handleMouseLeave);
|
|
@@ -364,7 +435,7 @@ export class MasonEffect {
|
|
|
364
435
|
window.addEventListener('mouseup', this.handleMouseUp);
|
|
365
436
|
}
|
|
366
437
|
|
|
367
|
-
removeEventListeners() {
|
|
438
|
+
removeEventListeners(): void {
|
|
368
439
|
window.removeEventListener('resize', this.handleResize);
|
|
369
440
|
this.canvas.removeEventListener('mousemove', this.handleMouseMove);
|
|
370
441
|
this.canvas.removeEventListener('mouseleave', this.handleMouseLeave);
|
|
@@ -372,35 +443,35 @@ export class MasonEffect {
|
|
|
372
443
|
window.removeEventListener('mouseup', this.handleMouseUp);
|
|
373
444
|
}
|
|
374
445
|
|
|
375
|
-
handleResize() {
|
|
446
|
+
handleResize(): void {
|
|
376
447
|
this.resize();
|
|
377
448
|
}
|
|
378
449
|
|
|
379
|
-
handleMouseMove(e) {
|
|
450
|
+
handleMouseMove(e: MouseEvent): void {
|
|
380
451
|
const rect = this.canvas.getBoundingClientRect();
|
|
381
452
|
this.mouse.x = (e.clientX - rect.left) * this.DPR;
|
|
382
453
|
this.mouse.y = (e.clientY - rect.top) * this.DPR;
|
|
383
454
|
}
|
|
384
455
|
|
|
385
|
-
handleMouseLeave() {
|
|
456
|
+
handleMouseLeave(): void {
|
|
386
457
|
this.mouse.x = this.mouse.y = 0;
|
|
387
458
|
}
|
|
388
459
|
|
|
389
|
-
handleMouseDown() {
|
|
460
|
+
handleMouseDown(): void {
|
|
390
461
|
this.mouse.down = true;
|
|
391
462
|
}
|
|
392
463
|
|
|
393
|
-
handleMouseUp() {
|
|
464
|
+
handleMouseUp(): void {
|
|
394
465
|
this.mouse.down = false;
|
|
395
466
|
}
|
|
396
467
|
|
|
397
468
|
// 설정 업데이트
|
|
398
|
-
updateConfig(newConfig) {
|
|
469
|
+
updateConfig(newConfig: Partial<MasonEffectOptions>): void {
|
|
399
470
|
// 디바운스 적용
|
|
400
471
|
this._debouncedUpdateConfig(newConfig);
|
|
401
472
|
}
|
|
402
473
|
|
|
403
|
-
_updateConfigInternal(newConfig) {
|
|
474
|
+
_updateConfigInternal(newConfig: Partial<MasonEffectOptions>): void {
|
|
404
475
|
this.config = { ...this.config, ...newConfig };
|
|
405
476
|
if (newConfig.text) {
|
|
406
477
|
this.buildTargets();
|
|
@@ -408,7 +479,7 @@ export class MasonEffect {
|
|
|
408
479
|
}
|
|
409
480
|
|
|
410
481
|
// 파괴 및 정리
|
|
411
|
-
destroy() {
|
|
482
|
+
destroy(): void {
|
|
412
483
|
this.stop();
|
|
413
484
|
this.removeEventListeners();
|
|
414
485
|
if (this.intersectionObserver) {
|
package/src/core/index.d.ts
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MasonEffect 타입 정의
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
export interface MasonEffectOptions {
|
|
6
|
-
text?: string;
|
|
7
|
-
densityStep?: number;
|
|
8
|
-
maxParticles?: number;
|
|
9
|
-
pointSize?: number;
|
|
10
|
-
ease?: number;
|
|
11
|
-
repelRadius?: number;
|
|
12
|
-
repelStrength?: number;
|
|
13
|
-
particleColor?: string;
|
|
14
|
-
fontFamily?: string;
|
|
15
|
-
fontSize?: number | null;
|
|
16
|
-
width?: number | null;
|
|
17
|
-
height?: number | null;
|
|
18
|
-
devicePixelRatio?: number | null;
|
|
19
|
-
onReady?: (instance: MasonEffect) => void;
|
|
20
|
-
onUpdate?: (instance: MasonEffect) => void;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export interface Particle {
|
|
24
|
-
x: number;
|
|
25
|
-
y: number;
|
|
26
|
-
vx: number;
|
|
27
|
-
vy: number;
|
|
28
|
-
tx: number;
|
|
29
|
-
ty: number;
|
|
30
|
-
j: number;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export class MasonEffect {
|
|
34
|
-
constructor(container: HTMLElement | string, options?: MasonEffectOptions);
|
|
35
|
-
|
|
36
|
-
config: MasonEffectOptions;
|
|
37
|
-
canvas: HTMLCanvasElement;
|
|
38
|
-
ctx: CanvasRenderingContext2D;
|
|
39
|
-
particles: Particle[];
|
|
40
|
-
mouse: { x: number; y: number; down: boolean };
|
|
41
|
-
isRunning: boolean;
|
|
42
|
-
|
|
43
|
-
init(): void;
|
|
44
|
-
resize(): void;
|
|
45
|
-
buildTargets(): void;
|
|
46
|
-
makeParticle(): Particle;
|
|
47
|
-
initParticles(): void;
|
|
48
|
-
scatter(): void;
|
|
49
|
-
morph(textOrOptions?: string | Partial<MasonEffectOptions>): void;
|
|
50
|
-
update(): void;
|
|
51
|
-
animate(): void;
|
|
52
|
-
start(): void;
|
|
53
|
-
stop(): void;
|
|
54
|
-
updateConfig(newConfig: Partial<MasonEffectOptions>): void;
|
|
55
|
-
destroy(): void;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export default MasonEffect;
|
|
59
|
-
|