masoneffect 0.1.19 → 0.1.21

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.
@@ -1,235 +0,0 @@
1
- import React, { useEffect, useRef, forwardRef, useImperativeHandle } from 'react';
2
- import { MasonEffect } from '../core/index.js';
3
-
4
- export interface MasonEffectOptions {
5
- text?: string;
6
- densityStep?: number;
7
- maxParticles?: number;
8
- pointSize?: number;
9
- ease?: number;
10
- repelRadius?: number;
11
- repelStrength?: number;
12
- particleColor?: string;
13
- fontFamily?: string;
14
- fontSize?: number | null;
15
- width?: number | null;
16
- height?: number | null;
17
- devicePixelRatio?: number | null;
18
- onReady?: (instance: MasonEffect) => void;
19
- onUpdate?: (instance: MasonEffect) => void;
20
- }
21
-
22
- export interface MasonEffectRef {
23
- morph: (textOrOptions?: string | Partial<MasonEffectOptions>) => void;
24
- scatter: () => void;
25
- updateConfig: (config: Partial<MasonEffectOptions>) => void;
26
- destroy: () => void;
27
- }
28
-
29
- interface MasonEffectProps extends MasonEffectOptions {
30
- className?: string;
31
- style?: React.CSSProperties;
32
- }
33
-
34
- const MasonEffectComponent = forwardRef<MasonEffectRef, MasonEffectProps>(
35
- (props, ref) => {
36
- const containerRef = useRef<HTMLDivElement>(null);
37
- const instanceRef = useRef<MasonEffect | null>(null);
38
-
39
- useEffect(() => {
40
- if (!containerRef.current) return;
41
-
42
- let resizeObserver: ResizeObserver | null = null;
43
- let initTimeout: number | null = null;
44
-
45
- // 컨테이너가 실제 크기를 가지도록 대기
46
- const initEffect = () => {
47
- const container = containerRef.current;
48
- if (!container) return;
49
-
50
- // 컨테이너 크기가 0이거나 너무 작으면 다음 프레임에 다시 시도
51
- const rect = container.getBoundingClientRect();
52
- if (rect.width <= 0 || rect.height <= 0) {
53
- initTimeout = window.setTimeout(initEffect, 50);
54
- return;
55
- }
56
-
57
- // 최소 크기 보장 (너무 작으면 기본값 사용)
58
- const minSize = 100;
59
- if (rect.width < minSize || rect.height < minSize) {
60
- container.style.minWidth = minSize + 'px';
61
- container.style.minHeight = minSize + 'px';
62
- }
63
-
64
- const {
65
- className,
66
- style,
67
- text,
68
- densityStep,
69
- maxParticles,
70
- pointSize,
71
- ease,
72
- repelRadius,
73
- repelStrength,
74
- particleColor,
75
- fontFamily,
76
- fontSize,
77
- width,
78
- height,
79
- devicePixelRatio,
80
- onReady,
81
- onUpdate,
82
- } = props;
83
-
84
- const options: MasonEffectOptions = {
85
- text,
86
- densityStep,
87
- maxParticles,
88
- pointSize,
89
- ease,
90
- repelRadius,
91
- repelStrength,
92
- particleColor,
93
- fontFamily,
94
- fontSize,
95
- width,
96
- height,
97
- devicePixelRatio,
98
- onReady,
99
- onUpdate,
100
- };
101
-
102
- instanceRef.current = new MasonEffect(container, options);
103
-
104
- // ResizeObserver로 컨테이너 크기 변경 감지
105
- if (typeof ResizeObserver !== 'undefined') {
106
- resizeObserver = new ResizeObserver(() => {
107
- if (instanceRef.current) {
108
- instanceRef.current.resize();
109
- }
110
- });
111
- resizeObserver.observe(container);
112
- }
113
- };
114
-
115
- // 다음 프레임에 초기화 (DOM이 완전히 렌더링된 후)
116
- requestAnimationFrame(initEffect);
117
-
118
- return () => {
119
- if (initTimeout) {
120
- clearTimeout(initTimeout);
121
- }
122
- if (resizeObserver) {
123
- resizeObserver.disconnect();
124
- }
125
- if (instanceRef.current) {
126
- instanceRef.current.destroy();
127
- instanceRef.current = null;
128
- }
129
- };
130
- }, []);
131
-
132
- // props 변경 시 설정 업데이트
133
- useEffect(() => {
134
- if (!instanceRef.current) return;
135
-
136
- const {
137
- text,
138
- densityStep,
139
- maxParticles,
140
- pointSize,
141
- ease,
142
- repelRadius,
143
- repelStrength,
144
- particleColor,
145
- fontFamily,
146
- fontSize,
147
- width,
148
- height,
149
- devicePixelRatio,
150
- } = props;
151
-
152
- instanceRef.current.updateConfig({
153
- text,
154
- densityStep,
155
- maxParticles,
156
- pointSize,
157
- ease,
158
- repelRadius,
159
- repelStrength,
160
- particleColor,
161
- fontFamily,
162
- fontSize,
163
- width,
164
- height,
165
- devicePixelRatio,
166
- });
167
- }, [
168
- props.text,
169
- props.densityStep,
170
- props.maxParticles,
171
- props.pointSize,
172
- props.ease,
173
- props.repelRadius,
174
- props.repelStrength,
175
- props.particleColor,
176
- props.fontFamily,
177
- props.fontSize,
178
- props.width,
179
- props.height,
180
- props.devicePixelRatio,
181
- ]);
182
-
183
- useImperativeHandle(ref, () => ({
184
- morph: (textOrOptions?: string | Partial<MasonEffectOptions>) => {
185
- if (!instanceRef.current) {
186
- console.warn('MasonEffect: 인스턴스가 아직 초기화되지 않았습니다.');
187
- return;
188
- }
189
- instanceRef.current.morph(textOrOptions);
190
- },
191
- scatter: () => {
192
- if (!instanceRef.current) {
193
- console.warn('MasonEffect: 인스턴스가 아직 초기화되지 않았습니다.');
194
- return;
195
- }
196
- instanceRef.current.scatter();
197
- },
198
- updateConfig: (config: Partial<MasonEffectOptions>) => {
199
- if (!instanceRef.current) {
200
- console.warn('MasonEffect: 인스턴스가 아직 초기화되지 않았습니다.');
201
- return;
202
- }
203
- instanceRef.current.updateConfig(config);
204
- },
205
- destroy: () => {
206
- if (instanceRef.current) {
207
- instanceRef.current.destroy();
208
- instanceRef.current = null;
209
- }
210
- },
211
- }));
212
-
213
- // 기본 스타일: 컨테이너가 크기를 가지도록 함
214
- const defaultStyle: React.CSSProperties = {
215
- width: '100%',
216
- height: '100%',
217
- minHeight: props.height || 400,
218
- position: 'relative',
219
- ...props.style,
220
- };
221
-
222
- return (
223
- <div
224
- ref={containerRef}
225
- className={props.className}
226
- style={defaultStyle}
227
- />
228
- );
229
- }
230
- );
231
-
232
- MasonEffectComponent.displayName = 'MasonEffect';
233
-
234
- export default MasonEffectComponent;
235
-
@@ -1,4 +0,0 @@
1
- // Re-export for convenience
2
- export { default } from './MasonEffect';
3
- export type { MasonEffectRef, MasonEffectOptions } from './MasonEffect';
4
-
@@ -1,140 +0,0 @@
1
- <template>
2
- <div ref="container" :class="className" :style="style"></div>
3
- </template>
4
-
5
- <script setup lang="ts">
6
- import { ref, onMounted, onBeforeUnmount, watch } from 'vue';
7
- import { MasonEffect } from '../core/index';
8
- import type { MasonEffectOptions } from '../core/index';
9
-
10
- interface Props extends Partial<MasonEffectOptions> {
11
- className?: string;
12
- style?: Record<string, any>;
13
- }
14
-
15
- const props = withDefaults(defineProps<Props>(), {
16
- text: 'mason effect',
17
- densityStep: 2,
18
- maxParticles: 3200,
19
- pointSize: 0.5,
20
- ease: 0.05,
21
- repelRadius: 150,
22
- repelStrength: 1,
23
- particleColor: '#fff',
24
- fontFamily: 'Inter, system-ui, Arial',
25
- fontSize: null,
26
- width: null,
27
- height: null,
28
- devicePixelRatio: null,
29
- className: '',
30
- style: () => ({}),
31
- onReady: null,
32
- onUpdate: null,
33
- });
34
-
35
- const emit = defineEmits<{
36
- ready: [instance: MasonEffect];
37
- update: [instance: MasonEffect];
38
- }>();
39
-
40
- const container = ref<HTMLElement | null>(null);
41
- let instance: MasonEffect | null = null;
42
-
43
- const init = () => {
44
- if (!container.value) return;
45
-
46
- const options: MasonEffectOptions = {
47
- text: props.text,
48
- densityStep: props.densityStep,
49
- maxParticles: props.maxParticles,
50
- pointSize: props.pointSize,
51
- ease: props.ease,
52
- repelRadius: props.repelRadius,
53
- repelStrength: props.repelStrength,
54
- particleColor: props.particleColor,
55
- fontFamily: props.fontFamily,
56
- fontSize: props.fontSize,
57
- width: props.width,
58
- height: props.height,
59
- devicePixelRatio: props.devicePixelRatio,
60
- onReady: (inst) => {
61
- if (props.onReady) props.onReady(inst);
62
- emit('ready', inst);
63
- },
64
- onUpdate: (inst) => {
65
- if (props.onUpdate) props.onUpdate(inst);
66
- emit('update', inst);
67
- },
68
- };
69
-
70
- instance = new MasonEffect(container.value, options);
71
- };
72
-
73
- // props 변경 감지
74
- watch(
75
- () => [
76
- props.text,
77
- props.densityStep,
78
- props.maxParticles,
79
- props.pointSize,
80
- props.ease,
81
- props.repelRadius,
82
- props.repelStrength,
83
- props.particleColor,
84
- props.fontFamily,
85
- props.fontSize,
86
- props.width,
87
- props.height,
88
- props.devicePixelRatio,
89
- ],
90
- () => {
91
- if (instance) {
92
- instance.updateConfig({
93
- text: props.text,
94
- densityStep: props.densityStep,
95
- maxParticles: props.maxParticles,
96
- pointSize: props.pointSize,
97
- ease: props.ease,
98
- repelRadius: props.repelRadius,
99
- repelStrength: props.repelStrength,
100
- particleColor: props.particleColor,
101
- fontFamily: props.fontFamily,
102
- fontSize: props.fontSize,
103
- width: props.width,
104
- height: props.height,
105
- devicePixelRatio: props.devicePixelRatio,
106
- });
107
- }
108
- }
109
- );
110
-
111
- onMounted(() => {
112
- init();
113
- });
114
-
115
- onBeforeUnmount(() => {
116
- if (instance) {
117
- instance.destroy();
118
- instance = null;
119
- }
120
- });
121
-
122
- // 외부에서 사용할 수 있는 메서드 노출
123
- defineExpose({
124
- morph: (textOrOptions?: string | Partial<MasonEffectOptions>) => {
125
- if (instance) instance.morph(textOrOptions);
126
- },
127
- scatter: () => {
128
- if (instance) instance.scatter();
129
- },
130
- updateConfig: (config: Partial<MasonEffectOptions>) => {
131
- if (instance) instance.updateConfig(config);
132
- },
133
- destroy: () => {
134
- if (instance) {
135
- instance.destroy();
136
- instance = null;
137
- }
138
- },
139
- });
140
- </script>