ginipick commited on
Commit
c4cd915
ยท
verified ยท
1 Parent(s): 3c21081

Upload index (6).html

Browse files
Files changed (1) hide show
  1. index (6).html +1329 -0
index (6).html ADDED
@@ -0,0 +1,1329 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="ko">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>์›นํˆฐ ํŽธ์ง‘ ์ŠคํŠœ๋””์˜ค Pro</title>
7
+ <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@100;300;400;500;700;900&family=Black+Han+Sans&family=Jua&family=Do+Hyeon&family=Sunflower:wght@300;500;700&family=Cute+Font&family=Gamja+Flower&family=Hi+Melody&family=Nanum+Pen+Script&family=Stylish&family=Single+Day&family=Gaegu:wght@300;400;700&display=swap" rel="stylesheet">
8
+ <style>
9
+ * {
10
+ margin: 0;
11
+ padding: 0;
12
+ box-sizing: border-box;
13
+ }
14
+
15
+ body {
16
+ font-family: 'Noto Sans KR', sans-serif;
17
+ background: #1a1a1a;
18
+ color: #fff;
19
+ overflow: hidden;
20
+ }
21
+
22
+ .container {
23
+ display: flex;
24
+ height: 100vh;
25
+ }
26
+
27
+ /* ์ขŒ์ธก ํˆด๋ฐ” */
28
+ .toolbar {
29
+ width: 320px;
30
+ background: #2a2a2a;
31
+ padding: 20px;
32
+ overflow-y: auto;
33
+ border-right: 1px solid #444;
34
+ }
35
+
36
+ .toolbar::-webkit-scrollbar {
37
+ width: 8px;
38
+ }
39
+
40
+ .toolbar::-webkit-scrollbar-track {
41
+ background: #1a1a1a;
42
+ }
43
+
44
+ .toolbar::-webkit-scrollbar-thumb {
45
+ background: #4a9eff;
46
+ border-radius: 4px;
47
+ }
48
+
49
+ .toolbar h2 {
50
+ font-size: 18px;
51
+ margin-bottom: 20px;
52
+ color: #fff;
53
+ border-bottom: 2px solid #4a9eff;
54
+ padding-bottom: 10px;
55
+ }
56
+
57
+ .tool-section {
58
+ margin-bottom: 30px;
59
+ }
60
+
61
+ .tool-section h3 {
62
+ font-size: 14px;
63
+ margin-bottom: 15px;
64
+ color: #aaa;
65
+ text-transform: uppercase;
66
+ }
67
+
68
+ /* ํŒŒ์ผ ์—…๋กœ๋“œ ์˜์—ญ */
69
+ .file-upload {
70
+ border: 2px dashed #4a9eff;
71
+ border-radius: 8px;
72
+ padding: 20px;
73
+ text-align: center;
74
+ cursor: pointer;
75
+ transition: all 0.3s;
76
+ margin-bottom: 20px;
77
+ }
78
+
79
+ .file-upload:hover {
80
+ background: rgba(74, 158, 255, 0.1);
81
+ border-color: #6ab7ff;
82
+ }
83
+
84
+ .file-upload input {
85
+ display: none;
86
+ }
87
+
88
+ /* ํŒŒ์ผ ๋ฆฌ์ŠคํŠธ */
89
+ .file-list {
90
+ background: #333;
91
+ border-radius: 8px;
92
+ padding: 10px;
93
+ margin-top: 10px;
94
+ max-height: 150px;
95
+ overflow-y: auto;
96
+ }
97
+
98
+ .file-item {
99
+ font-size: 12px;
100
+ color: #aaa;
101
+ padding: 3px 0;
102
+ border-bottom: 1px solid #444;
103
+ }
104
+
105
+ .file-item:last-child {
106
+ border-bottom: none;
107
+ }
108
+
109
+ /* ์ปดํฌ๋„ŒํŠธ ๋ฒ„ํŠผ๋“ค */
110
+ .component-grid {
111
+ display: grid;
112
+ grid-template-columns: repeat(3, 1fr);
113
+ gap: 8px;
114
+ }
115
+
116
+ .component-btn {
117
+ background: #3a3a3a;
118
+ border: 1px solid #555;
119
+ border-radius: 8px;
120
+ padding: 10px;
121
+ cursor: pointer;
122
+ transition: all 0.3s;
123
+ text-align: center;
124
+ color: #fff;
125
+ }
126
+
127
+ .component-btn:hover {
128
+ background: #4a4a4a;
129
+ border-color: #4a9eff;
130
+ transform: translateY(-2px);
131
+ }
132
+
133
+ .component-btn svg {
134
+ width: 35px;
135
+ height: 35px;
136
+ margin-bottom: 3px;
137
+ }
138
+
139
+ .component-btn span {
140
+ display: block;
141
+ font-size: 11px;
142
+ }
143
+
144
+ /* ์†์„ฑ ํŽธ์ง‘ ํŒจ๋„ */
145
+ .properties-panel {
146
+ background: #333;
147
+ border-radius: 8px;
148
+ padding: 15px;
149
+ margin-top: 20px;
150
+ }
151
+
152
+ .property-group {
153
+ margin-bottom: 15px;
154
+ }
155
+
156
+ .property-group label {
157
+ display: block;
158
+ font-size: 12px;
159
+ color: #aaa;
160
+ margin-bottom: 5px;
161
+ }
162
+
163
+ .property-group input,
164
+ .property-group select,
165
+ .property-group textarea {
166
+ width: 100%;
167
+ padding: 8px;
168
+ background: #2a2a2a;
169
+ border: 1px solid #444;
170
+ border-radius: 4px;
171
+ color: #fff;
172
+ }
173
+
174
+ .color-picker {
175
+ display: grid;
176
+ grid-template-columns: repeat(6, 1fr);
177
+ gap: 5px;
178
+ }
179
+
180
+ .color-option {
181
+ width: 35px;
182
+ height: 35px;
183
+ border-radius: 4px;
184
+ cursor: pointer;
185
+ border: 2px solid transparent;
186
+ }
187
+
188
+ .color-option.selected {
189
+ border-color: #4a9eff;
190
+ box-shadow: 0 0 10px rgba(74, 158, 255, 0.5);
191
+ }
192
+
193
+ /* ๋ฉ”์ธ ์ž‘์—… ์˜์—ญ */
194
+ .workspace {
195
+ flex: 1;
196
+ display: flex;
197
+ flex-direction: column;
198
+ background: #1a1a1a;
199
+ }
200
+
201
+ .workspace-header {
202
+ background: #2a2a2a;
203
+ padding: 15px 20px;
204
+ border-bottom: 1px solid #444;
205
+ display: flex;
206
+ justify-content: space-between;
207
+ align-items: center;
208
+ }
209
+
210
+ .workspace-tools {
211
+ display: flex;
212
+ gap: 10px;
213
+ }
214
+
215
+ .tool-btn {
216
+ background: #3a3a3a;
217
+ border: 1px solid #555;
218
+ border-radius: 6px;
219
+ padding: 8px 16px;
220
+ color: #fff;
221
+ cursor: pointer;
222
+ transition: all 0.3s;
223
+ }
224
+
225
+ .tool-btn:hover {
226
+ background: #4a4a4a;
227
+ border-color: #4a9eff;
228
+ }
229
+
230
+ .tool-btn.primary {
231
+ background: #4a9eff;
232
+ border-color: #4a9eff;
233
+ }
234
+
235
+ .tool-btn.primary:hover {
236
+ background: #6ab7ff;
237
+ }
238
+
239
+ .tool-btn.secondary {
240
+ background: #28a745;
241
+ border-color: #28a745;
242
+ }
243
+
244
+ .tool-btn.secondary:hover {
245
+ background: #5cbf5c;
246
+ }
247
+
248
+ /* ์บ”๋ฒ„์Šค ์˜์—ญ */
249
+ .canvas-container {
250
+ flex: 1;
251
+ overflow: auto;
252
+ padding: 20px;
253
+ display: flex;
254
+ justify-content: center;
255
+ background: #222;
256
+ }
257
+
258
+ .canvas-wrapper {
259
+ position: relative;
260
+ background: white;
261
+ box-shadow: 0 10px 40px rgba(0,0,0,0.5);
262
+ min-height: 800px;
263
+ width: 700px;
264
+ }
265
+
266
+ /* ์ด๋ฏธ์ง€ ์ปจํ…Œ์ด๋„ˆ */
267
+ .image-container {
268
+ position: relative;
269
+ width: 100%;
270
+ }
271
+
272
+ .image-number {
273
+ position: absolute;
274
+ left: -40px;
275
+ top: 10px;
276
+ background: #4a9eff;
277
+ color: white;
278
+ width: 30px;
279
+ height: 30px;
280
+ border-radius: 50%;
281
+ display: flex;
282
+ align-items: center;
283
+ justify-content: center;
284
+ font-weight: bold;
285
+ font-size: 14px;
286
+ z-index: 10;
287
+ box-shadow: 0 2px 5px rgba(0,0,0,0.3);
288
+ }
289
+
290
+ .webtoon-image {
291
+ width: 100%;
292
+ display: block;
293
+ margin-bottom: 20px;
294
+ }
295
+
296
+ /* ๋“œ๋ž˜๊ทธ ๊ฐ€๋Šฅํ•œ ์ปดํฌ๋„ŒํŠธ */
297
+ .draggable-component {
298
+ position: absolute;
299
+ cursor: move;
300
+ user-select: none;
301
+ }
302
+
303
+ .draggable-component.selected {
304
+ outline: 2px solid #4a9eff;
305
+ z-index: 100;
306
+ }
307
+
308
+ .draggable-component .resize-handle {
309
+ position: absolute;
310
+ width: 10px;
311
+ height: 10px;
312
+ background: #4a9eff;
313
+ border: 1px solid #fff;
314
+ display: none;
315
+ }
316
+
317
+ .draggable-component.selected .resize-handle {
318
+ display: block;
319
+ }
320
+
321
+ .resize-handle.nw { top: -5px; left: -5px; cursor: nw-resize; }
322
+ .resize-handle.ne { top: -5px; right: -5px; cursor: ne-resize; }
323
+ .resize-handle.sw { bottom: -5px; left: -5px; cursor: sw-resize; }
324
+ .resize-handle.se { bottom: -5px; right: -5px; cursor: se-resize; }
325
+
326
+ /* ๋งํ’์„  ์Šคํƒ€์ผ๋“ค */
327
+ .speech-bubble {
328
+ background: white;
329
+ border: 2px solid black;
330
+ border-radius: 20px;
331
+ padding: 30px 15px 15px 15px;
332
+ min-width: 100px;
333
+ min-height: 50px;
334
+ position: relative;
335
+ width: 100%;
336
+ height: 100%;
337
+ }
338
+
339
+ .speech-bubble-tail {
340
+ position: absolute;
341
+ bottom: -10px;
342
+ left: 20px;
343
+ width: 0;
344
+ height: 0;
345
+ border-left: 10px solid transparent;
346
+ border-right: 10px solid transparent;
347
+ border-top: 10px solid black;
348
+ }
349
+
350
+ .speech-bubble-tail::after {
351
+ content: '';
352
+ position: absolute;
353
+ bottom: 3px;
354
+ left: -10px;
355
+ width: 0;
356
+ height: 0;
357
+ border-left: 10px solid transparent;
358
+ border-right: 10px solid transparent;
359
+ border-top: 10px solid white;
360
+ }
361
+
362
+ .thought-bubble {
363
+ background: white;
364
+ border: 2px solid black;
365
+ border-radius: 50%;
366
+ padding: 40px 20px 20px 20px;
367
+ min-width: 120px;
368
+ min-height: 80px;
369
+ position: relative;
370
+ width: 100%;
371
+ height: 100%;
372
+ }
373
+
374
+ .thought-bubble-dots {
375
+ position: absolute;
376
+ bottom: -25px;
377
+ left: 20px;
378
+ }
379
+
380
+ .thought-dot {
381
+ position: absolute;
382
+ background: white;
383
+ border: 2px solid black;
384
+ border-radius: 50%;
385
+ }
386
+
387
+ .thought-dot.large {
388
+ width: 20px;
389
+ height: 20px;
390
+ }
391
+
392
+ .thought-dot.small {
393
+ width: 12px;
394
+ height: 12px;
395
+ left: -5px;
396
+ top: 10px;
397
+ }
398
+
399
+ .shout-bubble {
400
+ background: white;
401
+ border: 3px solid black;
402
+ padding: 30px 15px 15px 15px;
403
+ min-width: 100px;
404
+ min-height: 50px;
405
+ position: relative;
406
+ width: 100%;
407
+ height: 100%;
408
+ clip-path: polygon(10% 0%, 90% 0%, 100% 10%, 100% 90%, 90% 100%, 10% 100%, 0% 90%, 0% 10%);
409
+ }
410
+
411
+ .narration-box {
412
+ background: rgba(0, 0, 0, 0.85);
413
+ border: 2px solid white;
414
+ padding: 30px 15px 15px 15px;
415
+ min-width: 150px;
416
+ min-height: 40px;
417
+ width: 100%;
418
+ height: 100%;
419
+ }
420
+
421
+ .narration-box .component-text {
422
+ color: white !important;
423
+ }
424
+
425
+ .text-box {
426
+ background: white;
427
+ border: 2px solid black;
428
+ padding: 20px 10px 10px 10px;
429
+ min-width: 100px;
430
+ min-height: 40px;
431
+ width: 100%;
432
+ height: 100%;
433
+ }
434
+
435
+ .transparent-box {
436
+ background: rgba(255, 255, 255, 0.8);
437
+ border: 1px dashed #999;
438
+ padding: 20px 10px 10px 10px;
439
+ min-width: 100px;
440
+ min-height: 40px;
441
+ width: 100%;
442
+ height: 100%;
443
+ }
444
+
445
+ .action-effect {
446
+ background: transparent;
447
+ border: none;
448
+ padding: 20px 10px 10px 10px;
449
+ min-width: 80px;
450
+ min-height: 40px;
451
+ width: 100%;
452
+ height: 100%;
453
+ font-weight: bold;
454
+ text-shadow: 2px 2px 0 white, -2px -2px 0 white, 2px -2px 0 white, -2px 2px 0 white;
455
+ }
456
+
457
+ .emotion-bubble {
458
+ background: #ffeb3b;
459
+ border: 2px solid black;
460
+ border-radius: 15px;
461
+ padding: 20px 10px 10px 10px;
462
+ min-width: 80px;
463
+ min-height: 40px;
464
+ width: 100%;
465
+ height: 100%;
466
+ }
467
+
468
+ .whisper-bubble {
469
+ background: #f0f0f0;
470
+ border: 1px dashed #666;
471
+ border-radius: 15px;
472
+ padding: 20px 10px 10px 10px;
473
+ min-width: 100px;
474
+ min-height: 40px;
475
+ width: 100%;
476
+ height: 100%;
477
+ opacity: 0.9;
478
+ }
479
+
480
+ .gradient-box {
481
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
482
+ border: 2px solid white;
483
+ border-radius: 10px;
484
+ padding: 30px 15px 15px 15px;
485
+ min-width: 100px;
486
+ min-height: 40px;
487
+ width: 100%;
488
+ height: 100%;
489
+ }
490
+
491
+ .gradient-box .component-text {
492
+ color: white !important;
493
+ }
494
+
495
+ .component-text {
496
+ width: 100%;
497
+ height: 100%;
498
+ border: none;
499
+ background: transparent;
500
+ resize: none;
501
+ outline: none;
502
+ font-family: inherit;
503
+ font-size: 14px;
504
+ color: black;
505
+ text-align: center;
506
+ padding: 5px;
507
+ overflow: visible;
508
+ line-height: 1.4;
509
+ }
510
+
511
+ /* ๋กœ๋”ฉ ์ธ๋””์ผ€์ดํ„ฐ */
512
+ .loading {
513
+ display: none;
514
+ position: fixed;
515
+ top: 50%;
516
+ left: 50%;
517
+ transform: translate(-50%, -50%);
518
+ background: rgba(0,0,0,0.9);
519
+ padding: 30px;
520
+ border-radius: 10px;
521
+ z-index: 1000;
522
+ text-align: center;
523
+ }
524
+
525
+ .loading.active {
526
+ display: block;
527
+ }
528
+
529
+ .loading-spinner {
530
+ border: 3px solid #f3f3f3;
531
+ border-top: 3px solid #4a9eff;
532
+ border-radius: 50%;
533
+ width: 40px;
534
+ height: 40px;
535
+ animation: spin 1s linear infinite;
536
+ margin: 0 auto 10px;
537
+ }
538
+
539
+ @keyframes spin {
540
+ 0% { transform: rotate(0deg); }
541
+ 100% { transform: rotate(360deg); }
542
+ }
543
+
544
+ /* ์ด๋ฏธ์ง€ ๊ฐ„๊ฒฉ ์กฐ์ ˆ */
545
+ .spacing-control {
546
+ display: flex;
547
+ align-items: center;
548
+ gap: 10px;
549
+ }
550
+
551
+ .spacing-control input[type="range"] {
552
+ flex: 1;
553
+ }
554
+
555
+ .spacing-value {
556
+ width: 50px;
557
+ text-align: center;
558
+ }
559
+
560
+ /* ๊ฐœ๋ณ„ ์ €์žฅ ์ž…๋ ฅ */
561
+ .save-individual {
562
+ display: flex;
563
+ gap: 5px;
564
+ margin-top: 10px;
565
+ }
566
+
567
+ .save-individual input {
568
+ width: 60px;
569
+ }
570
+
571
+ .save-individual button {
572
+ flex: 1;
573
+ padding: 5px;
574
+ background: #28a745;
575
+ border: none;
576
+ border-radius: 4px;
577
+ color: white;
578
+ cursor: pointer;
579
+ }
580
+
581
+ .save-individual button:hover {
582
+ background: #5cbf5c;
583
+ }
584
+ </style>
585
+ </head>
586
+ <body>
587
+ <div class="container">
588
+ <!-- ์ขŒ์ธก ํˆด๋ฐ” -->
589
+ <div class="toolbar">
590
+ <h2>๐ŸŽจ ์›นํˆฐ ํŽธ์ง‘ ๋„๊ตฌ</h2>
591
+
592
+ <!-- ํŒŒ์ผ ์—…๋กœ๋“œ -->
593
+ <div class="tool-section">
594
+ <h3>๐Ÿ“ ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ</h3>
595
+ <div class="file-upload" onclick="document.getElementById('fileInput').click()">
596
+ <svg width="40" height="40" fill="#4a9eff">
597
+ <rect x="5" y="10" width="30" height="25" fill="none" stroke="currentColor" stroke-width="2"/>
598
+ <polyline points="15,25 20,20 25,25" fill="none" stroke="currentColor" stroke-width="2"/>
599
+ <line x1="20" y1="20" x2="20" y2="30" stroke="currentColor" stroke-width="2"/>
600
+ </svg>
601
+ <p>ํด๋ฆญํ•˜์—ฌ ์ด๋ฏธ์ง€ ์„ ํƒ</p>
602
+ <p style="font-size: 11px; color: #888; margin-top: 5px;">ํŒŒ์ผ๋ช… ๊ธฐ์ค€ ์ž๋™ ์ •๋ ฌ (1.jpg, 2.jpg...)</p>
603
+ <input type="file" id="fileInput" multiple accept="image/*">
604
+ </div>
605
+ <div id="fileList"></div>
606
+
607
+ <!-- ์ด๋ฏธ์ง€ ๊ฐ„๊ฒฉ ์กฐ์ ˆ -->
608
+ <div class="property-group">
609
+ <label>์ด๋ฏธ์ง€ ๊ฐ„๊ฒฉ</label>
610
+ <div class="spacing-control">
611
+ <input type="range" id="imageSpacing" min="0" max="100" value="20" onchange="updateImageSpacing()">
612
+ <span class="spacing-value" id="spacingValue">20px</span>
613
+ </div>
614
+ </div>
615
+
616
+ <!-- ๊ฐœ๋ณ„ ์ด๋ฏธ์ง€ ์ €์žฅ -->
617
+ <div class="property-group">
618
+ <label>๊ฐœ๋ณ„ ์ด๋ฏธ์ง€ ์ €์žฅ</label>
619
+ <div class="save-individual">
620
+ <input type="number" id="imageNumber" placeholder="๋ฒˆํ˜ธ" min="1">
621
+ <button onclick="exportSingleImage()">์ €์žฅ</button>
622
+ </div>
623
+ </div>
624
+ </div>
625
+
626
+ <!-- ์ปดํฌ๋„ŒํŠธ -->
627
+ <div class="tool-section">
628
+ <h3>๐Ÿ’ฌ ๋งํ’์„  & ํšจ๊ณผ</h3>
629
+ <div class="component-grid">
630
+ <div class="component-btn" onclick="addComponent('speech')">
631
+ <svg viewBox="0 0 100 100" fill="none" stroke="white" stroke-width="2">
632
+ <ellipse cx="50" cy="40" rx="40" ry="30"/>
633
+ <path d="M 30 55 L 25 75 L 40 55"/>
634
+ </svg>
635
+ <span>๋งํ’์„ </span>
636
+ </div>
637
+ <div class="component-btn" onclick="addComponent('thought')">
638
+ <svg viewBox="0 0 100 100" fill="none" stroke="white" stroke-width="2">
639
+ <ellipse cx="50" cy="40" rx="35" ry="25"/>
640
+ <circle cx="30" cy="65" r="8"/>
641
+ <circle cx="20" cy="75" r="5"/>
642
+ </svg>
643
+ <span>์ƒ๊ฐํ’์„ </span>
644
+ </div>
645
+ <div class="component-btn" onclick="addComponent('shout')">
646
+ <svg viewBox="0 0 100 100" fill="none" stroke="white" stroke-width="3">
647
+ <polygon points="50,10 90,30 85,70 50,90 15,70 10,30"/>
648
+ </svg>
649
+ <span>์™ธ์นจํ’์„ </span>
650
+ </div>
651
+ <div class="component-btn" onclick="addComponent('whisper')">
652
+ <svg viewBox="0 0 100 100" fill="none" stroke="white" stroke-width="1" stroke-dasharray="3,3">
653
+ <ellipse cx="50" cy="45" rx="35" ry="25"/>
654
+ </svg>
655
+ <span>์†์‚ญ์ž„</span>
656
+ </div>
657
+ <div class="component-btn" onclick="addComponent('emotion')">
658
+ <svg viewBox="0 0 100 100" fill="#ffeb3b" stroke="white" stroke-width="2">
659
+ <ellipse cx="50" cy="45" rx="35" ry="25"/>
660
+ </svg>
661
+ <span>๊ฐ์ •ํ‘œํ˜„</span>
662
+ </div>
663
+ <div class="component-btn" onclick="addComponent('action')">
664
+ <svg viewBox="0 0 100 100" fill="none" stroke="white" stroke-width="2">
665
+ <text x="50" y="50" text-anchor="middle" font-size="30" fill="white">!</text>
666
+ </svg>
667
+ <span>ํšจ๊ณผ์Œ</span>
668
+ </div>
669
+ <div class="component-btn" onclick="addComponent('narration')">
670
+ <svg viewBox="0 0 100 100" fill="#333" stroke="white" stroke-width="2">
671
+ <rect x="15" y="30" width="70" height="40"/>
672
+ </svg>
673
+ <span>๋‚˜๋ ˆ์ด์…˜</span>
674
+ </div>
675
+ <div class="component-btn" onclick="addComponent('box')">
676
+ <svg viewBox="0 0 100 100" fill="none" stroke="white" stroke-width="2">
677
+ <rect x="20" y="30" width="60" height="40"/>
678
+ </svg>
679
+ <span>ํ…์ŠคํŠธ๋ฐ•์Šค</span>
680
+ </div>
681
+ <div class="component-btn" onclick="addComponent('transparent')">
682
+ <svg viewBox="0 0 100 100" fill="none" stroke="white" stroke-width="2" stroke-dasharray="5,5">
683
+ <rect x="20" y="30" width="60" height="40"/>
684
+ </svg>
685
+ <span>ํˆฌ๋ช…๋ฐ•์Šค</span>
686
+ </div>
687
+ <div class="component-btn" onclick="addComponent('gradient')">
688
+ <svg viewBox="0 0 100 100">
689
+ <defs>
690
+ <linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="100%">
691
+ <stop offset="0%" style="stop-color:#667eea;stop-opacity:1" />
692
+ <stop offset="100%" style="stop-color:#764ba2;stop-opacity:1" />
693
+ </linearGradient>
694
+ </defs>
695
+ <rect x="20" y="30" width="60" height="40" fill="url(#grad1)" stroke="white" stroke-width="2"/>
696
+ </svg>
697
+ <span>๊ทธ๋ผ๋””์–ธํŠธ</span>
698
+ </div>
699
+ </div>
700
+ </div>
701
+
702
+ <!-- ์†์„ฑ ํŽธ์ง‘ -->
703
+ <div class="tool-section">
704
+ <h3>โš™๏ธ ์†์„ฑ ํŽธ์ง‘</h3>
705
+ <div class="properties-panel">
706
+ <div class="property-group">
707
+ <label>ํฐํŠธ</label>
708
+ <select id="fontSelect" onchange="updateSelectedComponent()">
709
+ <optgroup label="๊ธฐ๋ณธ ํฐํŠธ">
710
+ <option value="Arial">Arial</option>
711
+ <option value="Verdana">Verdana</option>
712
+ <option value="Georgia">Georgia</option>
713
+ <option value="'Times New Roman'">Times New Roman</option>
714
+ <option value="'Courier New'">Courier New</option>
715
+ <option value="'Comic Sans MS'">Comic Sans MS</option>
716
+ </optgroup>
717
+ <optgroup label="ํ•œ๊ธ€ ํฐํŠธ">
718
+ <option value="'Noto Sans KR'">๋…ธํ†  ์‚ฐ์Šค</option>
719
+ <option value="'Black Han Sans'">๊ฒ€์€๊ณ ๋”•</option>
720
+ <option value="'Jua'">์ฃผ์•„</option>
721
+ <option value="'Do Hyeon'">๋„ํ˜„</option>
722
+ <option value="'Sunflower'">ํ•ด๋ฐ”๋ผ๊ธฐ</option>
723
+ <option value="'Cute Font'">๊ท€์—ฌ์šด ๊ธ€๊ผด</option>
724
+ <option value="'Gamja Flower'">๊ฐ์ž๊ฝƒ</option>
725
+ <option value="'Hi Melody'">ํ•˜์ด ๋ฉœ๋กœ๋””</option>
726
+ <option value="'Nanum Pen Script'">๋‚˜๋ˆ”ํŽœ</option>
727
+ <option value="'Stylish'">์Šคํƒ€์ผ๋ฆฌ์‹œ</option>
728
+ <option value="'Single Day'">์‹ฑ๊ธ€๋ฐ์ด</option>
729
+ <option value="'Gaegu'">๊ฐœ๊ตฌ์Ÿ์ด</option>
730
+ </optgroup>
731
+ </select>
732
+ </div>
733
+ <div class="property-group">
734
+ <label>๊ธ€์ž ํฌ๊ธฐ</label>
735
+ <input type="number" id="fontSizeInput" value="14" min="8" max="72" onchange="updateSelectedComponent()">
736
+ </div>
737
+ <div class="property-group">
738
+ <label>๊ธ€์ž ๊ตต๊ธฐ</label>
739
+ <select id="fontWeightSelect" onchange="updateSelectedComponent()">
740
+ <option value="100">์•„์ฃผ ์–‡๊ฒŒ</option>
741
+ <option value="300">์–‡๊ฒŒ</option>
742
+ <option value="400" selected>๋ณดํ†ต</option>
743
+ <option value="500">์•ฝ๊ฐ„ ๊ตต๊ฒŒ</option>
744
+ <option value="700">๊ตต๊ฒŒ</option>
745
+ <option value="900">์•„์ฃผ ๊ตต๊ฒŒ</option>
746
+ </select>
747
+ </div>
748
+ <div class="property-group">
749
+ <label>๊ธ€์ž ์ƒ‰์ƒ</label>
750
+ <div class="color-picker">
751
+ <div class="color-option selected" style="background: black" onclick="selectColor(this, 'black')"></div>
752
+ <div class="color-option" style="background: white; border: 1px solid #ccc;" onclick="selectColor(this, 'white')"></div>
753
+ <div class="color-option" style="background: #ff0000" onclick="selectColor(this, '#ff0000')"></div>
754
+ <div class="color-option" style="background: #ff6b6b" onclick="selectColor(this, '#ff6b6b')"></div>
755
+ <div class="color-option" style="background: #0066ff" onclick="selectColor(this, '#0066ff')"></div>
756
+ <div class="color-option" style="background: #4ecdc4" onclick="selectColor(this, '#4ecdc4')"></div>
757
+ <div class="color-option" style="background: #00b300" onclick="selectColor(this, '#00b300')"></div>
758
+ <div class="color-option" style="background: #95e1d3" onclick="selectColor(this, '#95e1d3')"></div>
759
+ <div class="color-option" style="background: #ffe100" onclick="selectColor(this, '#ffe100')"></div>
760
+ <div class="color-option" style="background: #ff9800" onclick="selectColor(this, '#ff9800')"></div>
761
+ <div class="color-option" style="background: #9c27b0" onclick="selectColor(this, '#9c27b0')"></div>
762
+ <div class="color-option" style="background: #ff00ff" onclick="selectColor(this, '#ff00ff')"></div>
763
+ </div>
764
+ </div>
765
+ <div class="property-group">
766
+ <label>ํ…์ŠคํŠธ</label>
767
+ <textarea id="textInput" rows="3" placeholder="ํ…์ŠคํŠธ ์ž…๋ ฅ..." onkeyup="updateSelectedComponent()"></textarea>
768
+ </div>
769
+ </div>
770
+ </div>
771
+ </div>
772
+
773
+ <!-- ๋ฉ”์ธ ์ž‘์—… ์˜์—ญ -->
774
+ <div class="workspace">
775
+ <div class="workspace-header">
776
+ <h1 style="font-size: 20px;">๐ŸŽจ ์›นํˆฐ ํŽธ์ง‘ ์ŠคํŠœ๋””์˜ค Pro</h1>
777
+ <div class="workspace-tools">
778
+ <button class="tool-btn" onclick="deleteSelected()">๐Ÿ—‘๏ธ ์„ ํƒ ์‚ญ์ œ</button>
779
+ <button class="tool-btn" onclick="clearAll()">๐Ÿ”„ ์ „์ฒด ์ดˆ๊ธฐํ™”</button>
780
+ <button class="tool-btn primary" onclick="exportSingleImage()">๐Ÿ’พ ์ด๋ฏธ์ง€ ์ €์žฅ</button>
781
+ </div>
782
+ </div>
783
+
784
+ <div class="canvas-container">
785
+ <div class="canvas-wrapper" id="canvas">
786
+ <!-- ์—…๋กœ๋“œ๋œ ์ด๋ฏธ์ง€์™€ ์ปดํฌ๋„ŒํŠธ๋“ค์ด ์—ฌ๊ธฐ์— ์ถ”๊ฐ€๋ฉ๋‹ˆ๋‹ค -->
787
+ </div>
788
+ </div>
789
+ </div>
790
+ </div>
791
+
792
+ <div class="loading" id="loading">
793
+ <div class="loading-spinner"></div>
794
+ <p>์ฒ˜๋ฆฌ ์ค‘...</p>
795
+ </div>
796
+
797
+ <!-- html2canvas ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ -->
798
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
799
+
800
+ <script>
801
+ let selectedComponent = null;
802
+ let isDragging = false;
803
+ let isResizing = false;
804
+ let dragOffset = { x: 0, y: 0 };
805
+ let componentCounter = 0;
806
+ let selectedColor = 'black';
807
+ let uploadedImages = [];
808
+
809
+ // ์ž์—ฐ์Šค๋Ÿฌ์šด ์ •๋ ฌ ํ•จ์ˆ˜ (์ˆซ์ž๊ฐ€ ํฌํ•จ๋œ ํŒŒ์ผ๋ช… ์ •๋ ฌ)
810
+ function naturalSort(a, b) {
811
+ // ํŒŒ์ผ๋ช…์—์„œ ์ˆซ์ž ์ถ”์ถœ
812
+ const extractNumber = (filename) => {
813
+ const match = filename.match(/(\d+)/);
814
+ return match ? parseInt(match[1]) : 0;
815
+ };
816
+
817
+ const aNum = extractNumber(a.name);
818
+ const bNum = extractNumber(b.name);
819
+
820
+ // ์ˆซ์ž๋กœ ๋น„๊ต
821
+ if (aNum !== bNum) {
822
+ return aNum - bNum;
823
+ }
824
+
825
+ // ์ˆซ์ž๊ฐ€ ๊ฐ™์œผ๋ฉด ์ „์ฒด ํŒŒ์ผ๋ช…์œผ๋กœ ๋น„๊ต
826
+ return a.name.localeCompare(b.name);
827
+ }
828
+
829
+ // ํŒŒ์ผ ์—…๋กœ๋“œ ์ฒ˜๋ฆฌ
830
+ document.getElementById('fileInput').addEventListener('change', async function(e) {
831
+ const files = Array.from(e.target.files);
832
+
833
+ // ์ž์—ฐ์Šค๋Ÿฌ์šด ์ •๋ ฌ (1.jpg, 2.jpg, 10.jpg ์ˆœ์„œ๋กœ)
834
+ files.sort(naturalSort);
835
+
836
+ const canvas = document.getElementById('canvas');
837
+
838
+ // ๊ธฐ์กด ์ด๋ฏธ์ง€ ์ œ๊ฑฐ
839
+ canvas.querySelectorAll('.image-container').forEach(container => container.remove());
840
+ uploadedImages = [];
841
+
842
+ // ํŒŒ์ผ ๋ฆฌ์ŠคํŠธ HTML
843
+ let fileListHTML = '<div class="file-list">';
844
+
845
+ // ๋ชจ๋“  ์ด๋ฏธ์ง€๋ฅผ ์ˆœ์„œ๋Œ€๋กœ ๋กœ๋“œํ•˜๊ธฐ ์œ„ํ•œ Promise ๋ฐฐ์—ด
846
+ const loadPromises = files.map((file, index) => {
847
+ return new Promise((resolve) => {
848
+ if (file.type.startsWith('image/')) {
849
+ const reader = new FileReader();
850
+
851
+ reader.onload = function(event) {
852
+ resolve({
853
+ index: index,
854
+ src: event.target.result,
855
+ name: file.name
856
+ });
857
+ };
858
+
859
+ reader.readAsDataURL(file);
860
+ } else {
861
+ resolve(null);
862
+ }
863
+ });
864
+ });
865
+
866
+ // ๋ชจ๋“  ์ด๋ฏธ์ง€ ๋กœ๋“œ ์™„๋ฃŒ ํ›„ ์ˆœ์„œ๋Œ€๋กœ ์ถ”๊ฐ€
867
+ const loadedImages = await Promise.all(loadPromises);
868
+
869
+ loadedImages.forEach((imageData, index) => {
870
+ if (imageData) {
871
+ // ์ด๋ฏธ์ง€ ์ปจํ…Œ์ด๋„ˆ ์ƒ์„ฑ
872
+ const container = document.createElement('div');
873
+ container.className = 'image-container';
874
+ container.id = 'image-container-' + (index + 1);
875
+
876
+ // ๋„˜๋ฒ„๋ง ํ‘œ์‹œ
877
+ const numberLabel = document.createElement('div');
878
+ numberLabel.className = 'image-number';
879
+ numberLabel.textContent = index + 1;
880
+
881
+ // ์ด๋ฏธ์ง€ ์ƒ์„ฑ
882
+ const img = document.createElement('img');
883
+ img.src = imageData.src;
884
+ img.className = 'webtoon-image';
885
+ img.alt = imageData.name;
886
+ img.id = 'webtoon-image-' + (index + 1);
887
+
888
+ container.appendChild(numberLabel);
889
+ container.appendChild(img);
890
+
891
+ // ์ด๋ฏธ์ง€๋ฅผ ์ˆœ์„œ๋Œ€๋กœ ์ถ”๊ฐ€
892
+ canvas.appendChild(container);
893
+
894
+ // ์—…๋กœ๋“œ๋œ ์ด๋ฏธ์ง€ ์ •๋ณด ์ €์žฅ
895
+ uploadedImages.push({
896
+ id: index + 1,
897
+ name: imageData.name,
898
+ element: img
899
+ });
900
+
901
+ // ํŒŒ์ผ ๋ฆฌ์ŠคํŠธ์— ์ถ”๊ฐ€
902
+ fileListHTML += `<div class="file-item">${index + 1}. ${imageData.name}</div>`;
903
+ }
904
+ });
905
+
906
+ fileListHTML += '</div>';
907
+
908
+ // ํŒŒ์ผ ๋ฆฌ์ŠคํŠธ ํ‘œ์‹œ
909
+ const fileList = document.getElementById('fileList');
910
+ fileList.innerHTML = '<p style="font-size: 12px; color: #4a9eff; margin-bottom: 10px;">โœ… ์—…๋กœ๋“œ๋œ ํŒŒ์ผ: ' + files.length + '๊ฐœ</p>' + fileListHTML;
911
+
912
+ updateImageSpacing();
913
+ });
914
+
915
+ // ์ด๋ฏธ์ง€ ๊ฐ„๊ฒฉ ์—…๋ฐ์ดํŠธ
916
+ function updateImageSpacing() {
917
+ const spacing = document.getElementById('imageSpacing').value;
918
+ document.getElementById('spacingValue').textContent = spacing + 'px';
919
+
920
+ document.querySelectorAll('.webtoon-image').forEach(img => {
921
+ img.style.marginBottom = spacing + 'px';
922
+ });
923
+ }
924
+
925
+ // ์ปดํฌ๋„ŒํŠธ ์ถ”๊ฐ€
926
+ function addComponent(type) {
927
+ const canvas = document.getElementById('canvas');
928
+ const component = document.createElement('div');
929
+ component.className = 'draggable-component';
930
+ component.id = 'component-' + componentCounter++;
931
+
932
+ // ์บ”๋ฒ„์Šค์˜ ์Šคํฌ๋กค ์œ„์น˜๋ฅผ ๊ณ ๋ คํ•œ ์ดˆ๊ธฐ ์œ„์น˜
933
+ const canvasRect = canvas.getBoundingClientRect();
934
+ const scrollTop = document.querySelector('.canvas-container').scrollTop;
935
+
936
+ component.style.left = '50px';
937
+ component.style.top = (scrollTop + 50) + 'px';
938
+ component.style.width = '150px';
939
+ component.style.height = '80px';
940
+
941
+ let innerContent = '';
942
+
943
+ switch(type) {
944
+ case 'speech':
945
+ innerContent = `
946
+ <div class="speech-bubble">
947
+ <textarea class="component-text" placeholder="๋Œ€์‚ฌ ์ž…๋ ฅ..."></textarea>
948
+ <div class="speech-bubble-tail"></div>
949
+ </div>`;
950
+ break;
951
+ case 'thought':
952
+ innerContent = `
953
+ <div class="thought-bubble">
954
+ <textarea class="component-text" placeholder="์ƒ๊ฐ ์ž…๋ ฅ..."></textarea>
955
+ <div class="thought-bubble-dots">
956
+ <div class="thought-dot large"></div>
957
+ <div class="thought-dot small"></div>
958
+ </div>
959
+ </div>`;
960
+ break;
961
+ case 'shout':
962
+ innerContent = `
963
+ <div class="shout-bubble">
964
+ <textarea class="component-text" placeholder="์™ธ์นจ!" style="font-weight: bold;"></textarea>
965
+ </div>`;
966
+ break;
967
+ case 'whisper':
968
+ innerContent = `
969
+ <div class="whisper-bubble">
970
+ <textarea class="component-text" placeholder="์†์‚ญ์ž„..." style="font-style: italic;"></textarea>
971
+ </div>`;
972
+ break;
973
+ case 'emotion':
974
+ innerContent = `
975
+ <div class="emotion-bubble">
976
+ <textarea class="component-text" placeholder="โ™ฅ โ™ช โ˜…"></textarea>
977
+ </div>`;
978
+ break;
979
+ case 'action':
980
+ innerContent = `
981
+ <div class="action-effect">
982
+ <textarea class="component-text" placeholder="์ฟต!" style="font-size: 24px; font-weight: bold;"></textarea>
983
+ </div>`;
984
+ break;
985
+ case 'narration':
986
+ innerContent = `
987
+ <div class="narration-box">
988
+ <textarea class="component-text" placeholder="๋‚˜๋ ˆ์ด์…˜..."></textarea>
989
+ </div>`;
990
+ break;
991
+ case 'box':
992
+ innerContent = `
993
+ <div class="text-box">
994
+ <textarea class="component-text" placeholder="ํ…์ŠคํŠธ ์ž…๋ ฅ..."></textarea>
995
+ </div>`;
996
+ break;
997
+ case 'transparent':
998
+ innerContent = `
999
+ <div class="transparent-box">
1000
+ <textarea class="component-text" placeholder="ํ…์ŠคํŠธ ์ž…๋ ฅ..."></textarea>
1001
+ </div>`;
1002
+ break;
1003
+ case 'gradient':
1004
+ innerContent = `
1005
+ <div class="gradient-box">
1006
+ <textarea class="component-text" placeholder="ํŠน์ˆ˜ ํšจ๊ณผ!"></textarea>
1007
+ </div>`;
1008
+ break;
1009
+ }
1010
+
1011
+ component.innerHTML = innerContent + `
1012
+ <div class="resize-handle nw"></div>
1013
+ <div class="resize-handle ne"></div>
1014
+ <div class="resize-handle sw"></div>
1015
+ <div class="resize-handle se"></div>
1016
+ `;
1017
+
1018
+ canvas.appendChild(component);
1019
+
1020
+ // ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ์ถ”๊ฐ€
1021
+ component.addEventListener('mousedown', startDrag);
1022
+
1023
+ // ํ…์ŠคํŠธ ์˜์—ญ ํด๋ฆญ ์‹œ ๋“œ๋ž˜๊ทธ ๋ฐฉ์ง€
1024
+ const textArea = component.querySelector('.component-text');
1025
+ if (textArea) {
1026
+ textArea.addEventListener('mousedown', function(e) {
1027
+ e.stopPropagation();
1028
+ });
1029
+ textArea.addEventListener('click', function(e) {
1030
+ e.stopPropagation();
1031
+ selectComponent(component);
1032
+ });
1033
+ }
1034
+
1035
+ // ๋ฆฌ์‚ฌ์ด์ฆˆ ํ•ธ๋“ค ์ด๋ฒคํŠธ
1036
+ component.querySelectorAll('.resize-handle').forEach(handle => {
1037
+ handle.addEventListener('mousedown', startResize);
1038
+ });
1039
+
1040
+ // ์ž๋™ ์„ ํƒ
1041
+ selectComponent(component);
1042
+ }
1043
+
1044
+ // ์ปดํฌ๋„ŒํŠธ ์„ ํƒ
1045
+ function selectComponent(component) {
1046
+ // ์ด์ „ ์„ ํƒ ํ•ด์ œ
1047
+ document.querySelectorAll('.draggable-component').forEach(c => {
1048
+ c.classList.remove('selected');
1049
+ });
1050
+
1051
+ // ์ƒˆ๋กœ์šด ์ปดํฌ๋„ŒํŠธ ์„ ํƒ
1052
+ component.classList.add('selected');
1053
+ selectedComponent = component;
1054
+
1055
+ // ์†์„ฑ ํŒจ๋„ ์—…๋ฐ์ดํŠธ
1056
+ const textArea = component.querySelector('.component-text');
1057
+ if (textArea) {
1058
+ document.getElementById('textInput').value = textArea.value;
1059
+ document.getElementById('fontSizeInput').value = parseInt(textArea.style.fontSize) || 14;
1060
+ document.getElementById('fontSelect').value = textArea.style.fontFamily || "'Noto Sans KR'";
1061
+ document.getElementById('fontWeightSelect').value = textArea.style.fontWeight || '400';
1062
+ }
1063
+ }
1064
+
1065
+ // ๋“œ๋ž˜๊ทธ ์‹œ์ž‘
1066
+ function startDrag(e) {
1067
+ if (e.target.classList.contains('resize-handle')) return;
1068
+ if (e.target.classList.contains('component-text')) return;
1069
+
1070
+ const component = e.currentTarget;
1071
+ selectComponent(component);
1072
+
1073
+ isDragging = true;
1074
+
1075
+ const rect = component.getBoundingClientRect();
1076
+
1077
+ dragOffset.x = e.clientX - rect.left;
1078
+ dragOffset.y = e.clientY - rect.top;
1079
+
1080
+ // ๋“œ๋ž˜๊ทธ ์ค‘ ํฌ์ธํ„ฐ ์ด๋ฒคํŠธ ๋น„ํ™œ์„ฑํ™”
1081
+ component.style.pointerEvents = 'none';
1082
+
1083
+ document.addEventListener('mousemove', drag);
1084
+ document.addEventListener('mouseup', stopDrag);
1085
+
1086
+ e.preventDefault();
1087
+ }
1088
+
1089
+ // ๋“œ๋ž˜๊ทธ ์ค‘
1090
+ function drag(e) {
1091
+ if (!isDragging || !selectedComponent) return;
1092
+
1093
+ const canvas = document.getElementById('canvas');
1094
+ const canvasRect = canvas.getBoundingClientRect();
1095
+ const containerScrollTop = document.querySelector('.canvas-container').scrollTop;
1096
+
1097
+ let newX = e.clientX - canvasRect.left - dragOffset.x;
1098
+ let newY = e.clientY - canvasRect.top - dragOffset.y + containerScrollTop;
1099
+
1100
+ // ์บ”๋ฒ„์Šค ๋ฒ”์œ„ ๋‚ด๋กœ ์ œํ•œ
1101
+ newX = Math.max(0, Math.min(newX, canvas.offsetWidth - selectedComponent.offsetWidth));
1102
+ newY = Math.max(0, newY);
1103
+
1104
+ selectedComponent.style.left = newX + 'px';
1105
+ selectedComponent.style.top = newY + 'px';
1106
+
1107
+ e.preventDefault();
1108
+ }
1109
+
1110
+ // ๋“œ๋ž˜๊ทธ ์ข…๋ฃŒ
1111
+ function stopDrag(e) {
1112
+ if (selectedComponent) {
1113
+ selectedComponent.style.pointerEvents = 'auto';
1114
+ }
1115
+ isDragging = false;
1116
+ document.removeEventListener('mousemove', drag);
1117
+ document.removeEventListener('mouseup', stopDrag);
1118
+
1119
+ e.preventDefault();
1120
+ }
1121
+
1122
+ // ๋ฆฌ์‚ฌ์ด์ฆˆ ์‹œ์ž‘
1123
+ function startResize(e) {
1124
+ e.stopPropagation();
1125
+ isResizing = true;
1126
+
1127
+ const handle = e.target;
1128
+ const component = handle.parentElement;
1129
+ selectComponent(component);
1130
+
1131
+ const startX = e.clientX;
1132
+ const startY = e.clientY;
1133
+ const startWidth = component.offsetWidth;
1134
+ const startHeight = component.offsetHeight;
1135
+ const startLeft = component.offsetLeft;
1136
+ const startTop = component.offsetTop;
1137
+
1138
+ function resize(e) {
1139
+ if (!isResizing) return;
1140
+
1141
+ const dx = e.clientX - startX;
1142
+ const dy = e.clientY - startY;
1143
+
1144
+ if (handle.classList.contains('se')) {
1145
+ component.style.width = Math.max(80, startWidth + dx) + 'px';
1146
+ component.style.height = Math.max(40, startHeight + dy) + 'px';
1147
+ } else if (handle.classList.contains('sw')) {
1148
+ component.style.width = Math.max(80, startWidth - dx) + 'px';
1149
+ component.style.height = Math.max(40, startHeight + dy) + 'px';
1150
+ component.style.left = Math.min(startLeft + dx, startLeft + startWidth - 80) + 'px';
1151
+ } else if (handle.classList.contains('ne')) {
1152
+ component.style.width = Math.max(80, startWidth + dx) + 'px';
1153
+ component.style.height = Math.max(40, startHeight - dy) + 'px';
1154
+ component.style.top = Math.min(startTop + dy, startTop + startHeight - 40) + 'px';
1155
+ } else if (handle.classList.contains('nw')) {
1156
+ component.style.width = Math.max(80, startWidth - dx) + 'px';
1157
+ component.style.height = Math.max(40, startHeight - dy) + 'px';
1158
+ component.style.left = Math.min(startLeft + dx, startLeft + startWidth - 80) + 'px';
1159
+ component.style.top = Math.min(startTop + dy, startTop + startHeight - 40) + 'px';
1160
+ }
1161
+ }
1162
+
1163
+ function stopResize() {
1164
+ isResizing = false;
1165
+ document.removeEventListener('mousemove', resize);
1166
+ document.removeEventListener('mouseup', stopResize);
1167
+ }
1168
+
1169
+ document.addEventListener('mousemove', resize);
1170
+ document.addEventListener('mouseup', stopResize);
1171
+ }
1172
+
1173
+ // ์„ ํƒ๋œ ์ปดํฌ๋„ŒํŠธ ์—…๋ฐ์ดํŠธ
1174
+ function updateSelectedComponent() {
1175
+ if (!selectedComponent) return;
1176
+
1177
+ const textArea = selectedComponent.querySelector('.component-text');
1178
+ if (textArea) {
1179
+ textArea.value = document.getElementById('textInput').value;
1180
+ textArea.style.fontSize = document.getElementById('fontSizeInput').value + 'px';
1181
+ textArea.style.fontFamily = document.getElementById('fontSelect').value;
1182
+ textArea.style.fontWeight = document.getElementById('fontWeightSelect').value;
1183
+ textArea.style.color = selectedColor;
1184
+ }
1185
+ }
1186
+
1187
+ // ์ƒ‰์ƒ ์„ ํƒ
1188
+ function selectColor(element, color) {
1189
+ document.querySelectorAll('.color-option').forEach(opt => {
1190
+ opt.classList.remove('selected');
1191
+ });
1192
+ element.classList.add('selected');
1193
+ selectedColor = color;
1194
+ updateSelectedComponent();
1195
+ }
1196
+
1197
+ // ์„ ํƒ๋œ ์ปดํฌ๋„ŒํŠธ ์‚ญ์ œ
1198
+ function deleteSelected() {
1199
+ if (selectedComponent) {
1200
+ selectedComponent.remove();
1201
+ selectedComponent = null;
1202
+ document.getElementById('textInput').value = '';
1203
+ }
1204
+ }
1205
+
1206
+ // ์ „์ฒด ์ดˆ๊ธฐํ™”
1207
+ function clearAll() {
1208
+ if (confirm('๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ญ์ œํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?')) {
1209
+ document.querySelectorAll('.draggable-component').forEach(c => c.remove());
1210
+ selectedComponent = null;
1211
+ document.getElementById('textInput').value = '';
1212
+ }
1213
+ }
1214
+
1215
+ // ์ „์ฒด ์ด๋ฏธ์ง€ ์ €์žฅ ๊ธฐ๋Šฅ ์ œ๊ฑฐ๋จ
1216
+
1217
+ // ๊ฐœ๋ณ„ ์ด๋ฏธ์ง€ ์ €์žฅ
1218
+ function exportSingleImage() {
1219
+ const imageNumber = parseInt(document.getElementById('imageNumber').value);
1220
+
1221
+ if (!imageNumber || imageNumber < 1) {
1222
+ alert('โš ๏ธ ์ €์žฅํ•  ์ด๋ฏธ์ง€ ๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.');
1223
+ return;
1224
+ }
1225
+
1226
+ const imageContainer = document.getElementById('image-container-' + imageNumber);
1227
+
1228
+ if (!imageContainer) {
1229
+ alert('โš ๏ธ ' + imageNumber + '๋ฒˆ ์ด๋ฏธ์ง€๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.');
1230
+ return;
1231
+ }
1232
+
1233
+ const loading = document.getElementById('loading');
1234
+ loading.classList.add('active');
1235
+
1236
+ // ์„ ํƒ ํ•ด์ œ ๋ฐ ๋ฒˆํ˜ธ ์ˆจ๊ธฐ๊ธฐ
1237
+ document.querySelectorAll('.draggable-component').forEach(c => {
1238
+ c.classList.remove('selected');
1239
+ });
1240
+
1241
+ const numberLabel = imageContainer.querySelector('.image-number');
1242
+ if (numberLabel) numberLabel.style.display = 'none';
1243
+
1244
+ // ์ž„์‹œ ์ปจํ…Œ์ด๋„ˆ ์ƒ์„ฑ
1245
+ const tempContainer = document.createElement('div');
1246
+ tempContainer.style.position = 'relative';
1247
+ tempContainer.style.width = imageContainer.offsetWidth + 'px';
1248
+ tempContainer.style.background = 'white';
1249
+
1250
+ // ์ด๋ฏธ์ง€ ๋ณต์‚ฌ
1251
+ const imgClone = imageContainer.querySelector('.webtoon-image').cloneNode(true);
1252
+ imgClone.style.marginBottom = '0';
1253
+ tempContainer.appendChild(imgClone);
1254
+
1255
+ // ํ•ด๋‹น ์ด๋ฏธ์ง€ ์œ„์˜ ์ปดํฌ๋„ŒํŠธ๋“ค๋งŒ ๋ณต์‚ฌ
1256
+ const imageRect = imageContainer.getBoundingClientRect();
1257
+ const canvasRect = document.getElementById('canvas').getBoundingClientRect();
1258
+ const imageTop = imageContainer.offsetTop;
1259
+ const imageBottom = imageTop + imageContainer.offsetHeight;
1260
+
1261
+ document.querySelectorAll('.draggable-component').forEach(component => {
1262
+ const compTop = parseInt(component.style.top);
1263
+ const compBottom = compTop + component.offsetHeight;
1264
+
1265
+ // ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ด๋ฏธ์ง€ ์˜์—ญ ๋‚ด์— ์žˆ๋Š”์ง€ ํ™•์ธ
1266
+ if (compTop < imageBottom && compBottom > imageTop) {
1267
+ const compClone = component.cloneNode(true);
1268
+ compClone.classList.remove('selected');
1269
+ compClone.style.top = (compTop - imageTop) + 'px';
1270
+ tempContainer.appendChild(compClone);
1271
+ }
1272
+ });
1273
+
1274
+ // ์ž„์‹œ๋กœ DOM์— ์ถ”๊ฐ€ (๋ณด์ด์ง€ ์•Š๊ฒŒ)
1275
+ tempContainer.style.position = 'absolute';
1276
+ tempContainer.style.left = '-9999px';
1277
+ document.body.appendChild(tempContainer);
1278
+
1279
+ html2canvas(tempContainer, {
1280
+ backgroundColor: '#ffffff',
1281
+ scale: 2,
1282
+ useCORS: true,
1283
+ logging: false
1284
+ }).then(function(canvas) {
1285
+ // ์ด๋ฏธ์ง€ ๋‹ค์šด๋กœ๋“œ
1286
+ const link = document.createElement('a');
1287
+ link.download = 'webtoon_' + imageNumber + '_' + new Date().getTime() + '.png';
1288
+ link.href = canvas.toDataURL('image/png');
1289
+ link.click();
1290
+
1291
+ // ์ž„์‹œ ์ปจํ…Œ์ด๋„ˆ ์ œ๊ฑฐ
1292
+ document.body.removeChild(tempContainer);
1293
+
1294
+ // ๋ฒˆํ˜ธ ๋‹ค์‹œ ํ‘œ์‹œ
1295
+ if (numberLabel) numberLabel.style.display = 'flex';
1296
+
1297
+ loading.classList.remove('active');
1298
+
1299
+ // ์„ฑ๊ณต ๋ฉ”์‹œ์ง€
1300
+ alert('โœ… ' + imageNumber + '๋ฒˆ ์ด๋ฏธ์ง€๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์ €์žฅ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!');
1301
+ }).catch(function(error) {
1302
+ document.body.removeChild(tempContainer);
1303
+ if (numberLabel) numberLabel.style.display = 'flex';
1304
+ loading.classList.remove('active');
1305
+ console.error('Error:', error);
1306
+ alert('โš ๏ธ ์ด๋ฏธ์ง€ ์ €์žฅ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.');
1307
+ });
1308
+ }
1309
+
1310
+ // ์บ”๋ฒ„์Šค ํด๋ฆญ ์ด๋ฒคํŠธ (๋นˆ ๊ณต๊ฐ„ ํด๋ฆญ ์‹œ ์„ ํƒ ํ•ด์ œ)
1311
+ document.getElementById('canvas').addEventListener('click', function(e) {
1312
+ if (e.target === this || e.target.classList.contains('webtoon-image')) {
1313
+ document.querySelectorAll('.draggable-component').forEach(c => {
1314
+ c.classList.remove('selected');
1315
+ });
1316
+ selectedComponent = null;
1317
+ document.getElementById('textInput').value = '';
1318
+ }
1319
+ });
1320
+
1321
+ // ํ‚ค๋ณด๋“œ ๋‹จ์ถ•ํ‚ค
1322
+ document.addEventListener('keydown', function(e) {
1323
+ if (e.key === 'Delete' && selectedComponent) {
1324
+ deleteSelected();
1325
+ }
1326
+ });
1327
+ </script>
1328
+ </body>
1329
+ </html>