@adstage/web-sdk 2.1.2 โ 2.1.4
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/index.cjs.js +364 -22
- package/dist/index.d.ts +40 -0
- package/dist/index.esm.js +364 -22
- package/dist/index.standalone.js +364 -22
- package/package.json +1 -1
- package/src/modules/ads/AdsModule.ts +427 -22
|
@@ -82,7 +82,7 @@ export class AdsModule implements BaseModule {
|
|
|
82
82
|
|
|
83
83
|
const adstageOptions = {
|
|
84
84
|
width: options?.width || '100%',
|
|
85
|
-
height: options?.height ||
|
|
85
|
+
height: options?.height || 'auto', // ๐ง ๋์ ํฌ๊ธฐ ์กฐ์ ํ์ฉ
|
|
86
86
|
autoSlide: options?.autoSlide || false,
|
|
87
87
|
slideInterval: options?.slideInterval || 5000,
|
|
88
88
|
onClick: options?.onClick,
|
|
@@ -284,8 +284,10 @@ export class AdsModule implements BaseModule {
|
|
|
284
284
|
adElement.setAttribute('data-adstage-type', type);
|
|
285
285
|
adElement.setAttribute('data-adstage-slot', slotId);
|
|
286
286
|
|
|
287
|
-
|
|
288
|
-
|
|
287
|
+
// ์ค๋งํธํ ํฌ๊ธฐ ์ค์
|
|
288
|
+
const { width, height } = this.calculateAdSize(container, type, options);
|
|
289
|
+
adElement.style.width = width;
|
|
290
|
+
adElement.style.height = height;
|
|
289
291
|
adElement.style.border = '1px dashed #ccc';
|
|
290
292
|
adElement.style.display = 'flex';
|
|
291
293
|
adElement.style.alignItems = 'center';
|
|
@@ -297,7 +299,313 @@ export class AdsModule implements BaseModule {
|
|
|
297
299
|
container.appendChild(adElement);
|
|
298
300
|
|
|
299
301
|
if (this._config?.debug) {
|
|
300
|
-
console.log(`๐ฆ Placeholder created for slot: ${slotId}`);
|
|
302
|
+
console.log(`๐ฆ Placeholder created for slot: ${slotId} (${width} x ${height})`);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* ์ปจํ
์ด๋์ ๊ด๊ณ ํ์
์ ๋ฐ๋ฅธ ์ค๋งํธํ ํฌ๊ธฐ ๊ณ์ฐ
|
|
308
|
+
*/
|
|
309
|
+
private calculateAdSize(container: HTMLElement, type: AdType, options: any): { width: string; height: string } {
|
|
310
|
+
// ์ฌ์ฉ์๊ฐ ๋ช
์์ ์ผ๋ก ํฌ๊ธฐ๋ฅผ ์ง์ ํ ๊ฒฝ์ฐ
|
|
311
|
+
const explicitWidth = options.width;
|
|
312
|
+
const explicitHeight = options.height;
|
|
313
|
+
|
|
314
|
+
// ๋๋น ์ฒ๋ฆฌ
|
|
315
|
+
let width: string;
|
|
316
|
+
if (typeof explicitWidth === 'number') {
|
|
317
|
+
width = `${explicitWidth}px`;
|
|
318
|
+
} else if (typeof explicitWidth === 'string') {
|
|
319
|
+
width = explicitWidth;
|
|
320
|
+
} else {
|
|
321
|
+
width = '100%'; // ๊ธฐ๋ณธ๊ฐ์ 100%
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// ๋์ด ์ฒ๋ฆฌ - ํต์ฌ ๋ก์ง
|
|
325
|
+
let height: string;
|
|
326
|
+
if (typeof explicitHeight === 'number') {
|
|
327
|
+
height = `${explicitHeight}px`;
|
|
328
|
+
} else if (typeof explicitHeight === 'string' && explicitHeight !== '100%' && explicitHeight !== 'auto') {
|
|
329
|
+
// ๋ช
์์ ์ธ ํฌ๊ธฐ ๋ฌธ์์ด (์: '200px', '50vh' ๋ฑ)
|
|
330
|
+
height = explicitHeight;
|
|
331
|
+
} else {
|
|
332
|
+
// 100%, auto์ด๊ฑฐ๋ ๋์ด๊ฐ ์ง์ ๋์ง ์์ ๊ฒฝ์ฐ ์ค๋งํธ ๊ณ์ฐ
|
|
333
|
+
const containerHeight = this.getContainerHeight(container);
|
|
334
|
+
|
|
335
|
+
if (containerHeight > 0) {
|
|
336
|
+
// ์ปจํ
์ด๋์ ๋์ด๊ฐ ์์ผ๋ฉด 100% ์ฌ์ฉ
|
|
337
|
+
height = '100%';
|
|
338
|
+
if (this._config?.debug) {
|
|
339
|
+
console.log(`๐ Using 100% height (container: ${containerHeight}px)`);
|
|
340
|
+
}
|
|
341
|
+
} else {
|
|
342
|
+
// ์ปจํ
์ด๋์ ๋์ด๊ฐ ์์ผ๋ฉด ํ์
๋ณ ๊ธฐ๋ณธ๊ฐ ์ฌ์ฉ (๋์ค์ ๋์ ์กฐ์ ๋จ)
|
|
343
|
+
height = this.getDefaultHeightForAdType(type);
|
|
344
|
+
if (this._config?.debug) {
|
|
345
|
+
console.log(`๐ Using default height ${height} (will be optimized for ${type})`);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return { width, height };
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* ์ปจํ
์ด๋์ ์ค์ ๋์ด ๊ณ์ฐ
|
|
355
|
+
*/
|
|
356
|
+
private getContainerHeight(container: HTMLElement): number {
|
|
357
|
+
// ํ์ฌ ๊ณ์ฐ๋ ์คํ์ผ์์ ๋์ด ํ์ธ
|
|
358
|
+
const computedStyle = window.getComputedStyle(container);
|
|
359
|
+
const height = parseFloat(computedStyle.height);
|
|
360
|
+
|
|
361
|
+
// height๊ฐ auto์ด๊ฑฐ๋ 0์ด๋ฉด ๋ค๋ฅธ ๋ฐฉ๋ฒ๋ค ์๋
|
|
362
|
+
if (!height || height === 0) {
|
|
363
|
+
// min-height ํ์ธ
|
|
364
|
+
const minHeight = parseFloat(computedStyle.minHeight);
|
|
365
|
+
if (minHeight > 0) return minHeight;
|
|
366
|
+
|
|
367
|
+
// CSS๋ก ์ค์ ๋ ๊ณ ์ ๋์ด ํ์ธ
|
|
368
|
+
if (container.style.height && container.style.height !== 'auto') {
|
|
369
|
+
const styleHeight = parseFloat(container.style.height);
|
|
370
|
+
if (styleHeight > 0) return styleHeight;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// ์์ฑ์ผ๋ก ์ค์ ๋ ๋์ด ํ์ธ
|
|
374
|
+
const heightAttr = container.getAttribute('height');
|
|
375
|
+
if (heightAttr) {
|
|
376
|
+
const attrHeight = parseFloat(heightAttr);
|
|
377
|
+
if (attrHeight > 0) return attrHeight;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return height || 0;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* ๊ด๊ณ ํ์
๋ณ ๊ธฐ๋ณธ ๋์ด ๋ฐํ
|
|
386
|
+
*/
|
|
387
|
+
private getDefaultHeightForAdType(type: AdType): string {
|
|
388
|
+
switch (type) {
|
|
389
|
+
case AdType.BANNER:
|
|
390
|
+
return '250px'; // ์ผ๋ฐ ๋ฐฐ๋
|
|
391
|
+
case AdType.TEXT:
|
|
392
|
+
return '120px'; // ํ
์คํธ๋ ์ข ๋ ์๊ฒ
|
|
393
|
+
case AdType.VIDEO:
|
|
394
|
+
return '360px'; // ๋น๋์ค๋ 16:9 ๋น์จ ๊ณ ๋ ค
|
|
395
|
+
case AdType.NATIVE:
|
|
396
|
+
return '200px'; // ๋ค์ดํฐ๋ธ๋ ์ค๊ฐ ํฌ๊ธฐ
|
|
397
|
+
case AdType.INTERSTITIAL:
|
|
398
|
+
return '400px'; // ์ ๋ฉด๊ด๊ณ ๋ ํฌ๊ฒ
|
|
399
|
+
default:
|
|
400
|
+
return '250px';
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* ์ด๋ฏธ์ง ํฌ๊ธฐ ์ ๋ณด ๋ก๋ (ํ๋ฆฌ๋ก๋ฉ)
|
|
406
|
+
*/
|
|
407
|
+
private async loadImageDimensions(imageUrl: string): Promise<{ width: number; height: number }> {
|
|
408
|
+
return new Promise((resolve, reject) => {
|
|
409
|
+
const img = new Image();
|
|
410
|
+
img.onload = () => {
|
|
411
|
+
resolve({ width: img.naturalWidth, height: img.naturalHeight });
|
|
412
|
+
};
|
|
413
|
+
img.onerror = () => {
|
|
414
|
+
reject(new Error(`Failed to load image: ${imageUrl}`));
|
|
415
|
+
};
|
|
416
|
+
img.src = imageUrl;
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* ์ฌ๋ฌ ๊ด๊ณ ์ ์ต์ ์ปจํ
์ด๋ ํฌ๊ธฐ ๊ณ์ฐ (๋์ ํฌ๊ธฐ ์กฐ์ )
|
|
422
|
+
*/
|
|
423
|
+
private async calculateOptimalContainerSize(
|
|
424
|
+
advertisements: Advertisement[],
|
|
425
|
+
containerWidth: number,
|
|
426
|
+
adType: AdType
|
|
427
|
+
): Promise<{ width: string; height: string; aspectRatio: number }> {
|
|
428
|
+
if (!advertisements.length || adType !== AdType.BANNER) {
|
|
429
|
+
// ๋ฐฐ๋๊ฐ ์๋๊ฑฐ๋ ๊ด๊ณ ๊ฐ ์์ผ๋ฉด ๊ธฐ๋ณธ๊ฐ
|
|
430
|
+
return {
|
|
431
|
+
width: '100%',
|
|
432
|
+
height: this.getDefaultHeightForAdType(adType),
|
|
433
|
+
aspectRatio: 16/9
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
try {
|
|
438
|
+
// ๋ชจ๋ ๋ฐฐ๋ ์ด๋ฏธ์ง์ ํฌ๊ธฐ ์ ๋ณด ๋ก๋
|
|
439
|
+
const imageDimensions = await Promise.allSettled(
|
|
440
|
+
advertisements
|
|
441
|
+
.filter(ad => ad.imageUrl)
|
|
442
|
+
.map(ad => this.loadImageDimensions(ad.imageUrl!))
|
|
443
|
+
);
|
|
444
|
+
|
|
445
|
+
const validDimensions = imageDimensions
|
|
446
|
+
.filter((result): result is PromiseFulfilledResult<{ width: number; height: number }> =>
|
|
447
|
+
result.status === 'fulfilled'
|
|
448
|
+
)
|
|
449
|
+
.map(result => result.value);
|
|
450
|
+
|
|
451
|
+
if (validDimensions.length === 0) {
|
|
452
|
+
// ์ด๋ฏธ์ง ๋ก๋ ์คํจ์ ๊ธฐ๋ณธ๊ฐ
|
|
453
|
+
return {
|
|
454
|
+
width: '100%',
|
|
455
|
+
height: this.getDefaultHeightForAdType(adType),
|
|
456
|
+
aspectRatio: 16/9
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// ์ต์ ์ ๋ต ์ ํ
|
|
461
|
+
const strategy = this.selectOptimalSizeStrategy(validDimensions);
|
|
462
|
+
const optimalHeight = this.calculateOptimalHeight(validDimensions, containerWidth, strategy);
|
|
463
|
+
|
|
464
|
+
if (this._config?.debug) {
|
|
465
|
+
console.log(`๐ Optimal container calculated: ${containerWidth}x${optimalHeight} (strategy: ${strategy})`);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
return {
|
|
469
|
+
width: '100%',
|
|
470
|
+
height: `${optimalHeight}px`,
|
|
471
|
+
aspectRatio: containerWidth / optimalHeight
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
} catch (error) {
|
|
475
|
+
console.warn('Failed to calculate optimal size, using defaults:', error);
|
|
476
|
+
return {
|
|
477
|
+
width: '100%',
|
|
478
|
+
height: this.getDefaultHeightForAdType(adType),
|
|
479
|
+
aspectRatio: 16/9
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* ์ต์ ํฌ๊ธฐ ์กฐ์ ์ ๋ต ์ ํ
|
|
486
|
+
*/
|
|
487
|
+
private selectOptimalSizeStrategy(dimensions: { width: number; height: number }[]): 'average' | 'common' | 'dominant' {
|
|
488
|
+
const aspectRatios = dimensions.map(d => d.width / d.height);
|
|
489
|
+
|
|
490
|
+
// 1. ๊ณตํต ๋น์จ์ด ์๋์ง ํ์ธ (ยฑ0.1 ํ์ฉ)
|
|
491
|
+
const ratioGroups = new Map<string, number>();
|
|
492
|
+
aspectRatios.forEach(ratio => {
|
|
493
|
+
const roundedRatio = Math.round(ratio * 10) / 10;
|
|
494
|
+
const key = roundedRatio.toString();
|
|
495
|
+
ratioGroups.set(key, (ratioGroups.get(key) || 0) + 1);
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
const maxGroup = Math.max(...ratioGroups.values());
|
|
499
|
+
const totalImages = dimensions.length;
|
|
500
|
+
|
|
501
|
+
// 70% ์ด์์ด ๋น์ทํ ๋น์จ์ด๋ฉด dominant ์ ๋ต
|
|
502
|
+
if (maxGroup / totalImages >= 0.7) {
|
|
503
|
+
return 'dominant';
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// ํ์ค ๋น์จ๋ค์ด ๋ง์ผ๋ฉด common ์ ๋ต
|
|
507
|
+
const standardRatios = [16/9, 4/3, 1/1, 3/2];
|
|
508
|
+
const standardCount = aspectRatios.filter(ratio =>
|
|
509
|
+
standardRatios.some(standard => Math.abs(ratio - standard) < 0.1)
|
|
510
|
+
).length;
|
|
511
|
+
|
|
512
|
+
if (standardCount / totalImages >= 0.5) {
|
|
513
|
+
return 'common';
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// ๊ธฐ๋ณธ์ ํ๊ท ์ ๋ต
|
|
517
|
+
return 'average';
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* ์ ๋ต์ ๋ฐ๋ฅธ ์ต์ ๋์ด ๊ณ์ฐ
|
|
522
|
+
*/
|
|
523
|
+
private calculateOptimalHeight(
|
|
524
|
+
dimensions: { width: number; height: number }[],
|
|
525
|
+
containerWidth: number,
|
|
526
|
+
strategy: 'average' | 'common' | 'dominant'
|
|
527
|
+
): number {
|
|
528
|
+
const aspectRatios = dimensions.map(d => d.width / d.height);
|
|
529
|
+
|
|
530
|
+
switch (strategy) {
|
|
531
|
+
case 'dominant':
|
|
532
|
+
// ๊ฐ์ฅ ๋ง์ ๋น์จ์ ๊ธฐ์ค์ผ๋ก
|
|
533
|
+
const ratioGroups = new Map<string, { ratio: number; count: number }>();
|
|
534
|
+
aspectRatios.forEach(ratio => {
|
|
535
|
+
const roundedRatio = Math.round(ratio * 10) / 10;
|
|
536
|
+
const key = roundedRatio.toString();
|
|
537
|
+
const existing = ratioGroups.get(key);
|
|
538
|
+
if (existing) {
|
|
539
|
+
existing.count++;
|
|
540
|
+
} else {
|
|
541
|
+
ratioGroups.set(key, { ratio: roundedRatio, count: 1 });
|
|
542
|
+
}
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
const dominantGroup = Array.from(ratioGroups.values())
|
|
546
|
+
.reduce((max, current) => current.count > max.count ? current : max);
|
|
547
|
+
|
|
548
|
+
return Math.round(containerWidth / dominantGroup.ratio);
|
|
549
|
+
|
|
550
|
+
case 'common':
|
|
551
|
+
// ํ์ค ๋น์จ ์ค ๊ฐ์ฅ ์ ํฉํ ๊ฒ ์ ํ
|
|
552
|
+
const standardRatios = [
|
|
553
|
+
{ ratio: 16/9, name: '16:9' },
|
|
554
|
+
{ ratio: 4/3, name: '4:3' },
|
|
555
|
+
{ ratio: 1/1, name: '1:1' },
|
|
556
|
+
{ ratio: 3/2, name: '3:2' }
|
|
557
|
+
];
|
|
558
|
+
|
|
559
|
+
const avgRatio = aspectRatios.reduce((sum, ratio) => sum + ratio, 0) / aspectRatios.length;
|
|
560
|
+
const bestStandard = standardRatios.reduce((best, current) =>
|
|
561
|
+
Math.abs(current.ratio - avgRatio) < Math.abs(best.ratio - avgRatio) ? current : best
|
|
562
|
+
);
|
|
563
|
+
|
|
564
|
+
if (this._config?.debug) {
|
|
565
|
+
console.log(`๐ Using standard ratio: ${bestStandard.name} (avg: ${avgRatio.toFixed(2)})`);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
return Math.round(containerWidth / bestStandard.ratio);
|
|
569
|
+
|
|
570
|
+
case 'average':
|
|
571
|
+
default:
|
|
572
|
+
// ํ๊ท ๋น์จ ์ฌ์ฉ
|
|
573
|
+
const averageRatio = aspectRatios.reduce((sum, ratio) => sum + ratio, 0) / aspectRatios.length;
|
|
574
|
+
return Math.round(containerWidth / averageRatio);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
/**
|
|
579
|
+
* ๊ฐ๋ณ ์ด๋ฏธ์ง์ ์ต์ ํ๋ ๋ ๋๋ง ์คํ์ผ ์ ์ฉ
|
|
580
|
+
*/
|
|
581
|
+
private applyOptimizedImageStyle(
|
|
582
|
+
img: HTMLImageElement,
|
|
583
|
+
imageAspectRatio: number,
|
|
584
|
+
containerAspectRatio: number
|
|
585
|
+
): void {
|
|
586
|
+
const ratio = imageAspectRatio / containerAspectRatio;
|
|
587
|
+
|
|
588
|
+
if (Math.abs(ratio - 1) < 0.1) {
|
|
589
|
+
// ๋น์จ์ด ๊ฑฐ์ ๊ฐ์ผ๋ฉด cover ์ฌ์ฉ
|
|
590
|
+
img.style.objectFit = 'cover';
|
|
591
|
+
img.style.objectPosition = 'center';
|
|
592
|
+
} else if (ratio > 1.3) {
|
|
593
|
+
// ์ด๋ฏธ์ง๊ฐ ํจ์ฌ ๊ฐ๋กํ์ด๋ฉด contain์ผ๋ก ์ ์ฒด ๋ณด์ด๊ธฐ
|
|
594
|
+
img.style.objectFit = 'contain';
|
|
595
|
+
img.style.objectPosition = 'center';
|
|
596
|
+
img.style.backgroundColor = '#f0f0f0'; // ๋น ๊ณต๊ฐ ๋ฐฐ๊ฒฝ์
|
|
597
|
+
} else if (ratio < 0.7) {
|
|
598
|
+
// ์ด๋ฏธ์ง๊ฐ ํจ์ฌ ์ธ๋กํ์ด๋ฉด cover๋ก ์ฑ์ฐ๊ธฐ
|
|
599
|
+
img.style.objectFit = 'cover';
|
|
600
|
+
img.style.objectPosition = 'center';
|
|
601
|
+
} else {
|
|
602
|
+
// ์ ๋นํ ์ฐจ์ด๋ฉด ์ค๋งํธ cover
|
|
603
|
+
img.style.objectFit = 'cover';
|
|
604
|
+
img.style.objectPosition = 'center';
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
if (this._config?.debug) {
|
|
608
|
+
console.log(`๐จ Image style applied: objectFit=${img.style.objectFit}, ratio=${ratio.toFixed(2)}`);
|
|
301
609
|
}
|
|
302
610
|
}
|
|
303
611
|
|
|
@@ -314,6 +622,11 @@ export class AdsModule implements BaseModule {
|
|
|
314
622
|
return;
|
|
315
623
|
}
|
|
316
624
|
|
|
625
|
+
// ๐ ๋์ ํฌ๊ธฐ ์กฐ์ : ๋ฐฐ๋ ๊ด๊ณ ์ ๊ฒฝ์ฐ ์ด๋ฏธ์ง ํฌ๊ธฐ ๊ธฐ๋ฐ์ผ๋ก ์ปจํ
์ด๋ ์ต์ ํ
|
|
626
|
+
if (slot.adType === AdType.BANNER && adstageData.length > 0) {
|
|
627
|
+
await this.optimizeContainerForBannerAds(slot, adstageData);
|
|
628
|
+
}
|
|
629
|
+
|
|
317
630
|
// ๊ด๊ณ ๊ฐ ์ฌ๋ฌ ๊ฐ์ด๊ฑฐ๋ autoSlide ์ต์
์ด ์์ผ๋ฉด ์ฌ๋ผ์ด๋๋ก ๋ ๋๋ง
|
|
318
631
|
if (adstageData.length > 1 || (slot.config as any)?.autoSlide) {
|
|
319
632
|
await this.renderAdSlider(slot, adstageData);
|
|
@@ -337,6 +650,38 @@ export class AdsModule implements BaseModule {
|
|
|
337
650
|
}
|
|
338
651
|
}
|
|
339
652
|
|
|
653
|
+
/**
|
|
654
|
+
* ๋ฐฐ๋ ๊ด๊ณ ๋ฅผ ์ํ ์ปจํ
์ด๋ ์ต์ ํ
|
|
655
|
+
*/
|
|
656
|
+
private async optimizeContainerForBannerAds(slot: AdSlot, advertisements: Advertisement[]): Promise<void> {
|
|
657
|
+
try {
|
|
658
|
+
const container = document.getElementById(slot.containerId);
|
|
659
|
+
const adElement = document.getElementById(slot.id);
|
|
660
|
+
|
|
661
|
+
if (!container || !adElement) return;
|
|
662
|
+
|
|
663
|
+
// ํ์ฌ ์ปจํ
์ด๋ ๋๋น ํ์ธ
|
|
664
|
+
const containerWidth = container.getBoundingClientRect().width || 300;
|
|
665
|
+
|
|
666
|
+
// ์ต์ ํฌ๊ธฐ ๊ณ์ฐ
|
|
667
|
+
const optimalSize = await this.calculateOptimalContainerSize(advertisements, containerWidth, slot.adType);
|
|
668
|
+
|
|
669
|
+
// ์ปจํ
์ด๋ ํฌ๊ธฐ ๋์ ์กฐ์
|
|
670
|
+
adElement.style.height = optimalSize.height;
|
|
671
|
+
|
|
672
|
+
// ์ฌ๋กฏ ์ ๋ณด ์
๋ฐ์ดํธ
|
|
673
|
+
(slot as any).optimizedHeight = optimalSize.height;
|
|
674
|
+
(slot as any).aspectRatio = optimalSize.aspectRatio;
|
|
675
|
+
|
|
676
|
+
if (this._config?.debug) {
|
|
677
|
+
console.log(`๐ง Container optimized for ${advertisements.length} banner ads: ${optimalSize.height}`);
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
} catch (error) {
|
|
681
|
+
console.warn('Container optimization failed, using default size:', error);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
|
|
340
685
|
/**
|
|
341
686
|
* ๊ธฐ๋ณธ viewability ์ถ์ ์์
|
|
342
687
|
*/
|
|
@@ -606,15 +951,21 @@ export class AdsModule implements BaseModule {
|
|
|
606
951
|
|
|
607
952
|
let sliderElement: HTMLElement;
|
|
608
953
|
|
|
954
|
+
// ์ต์ ํ๋ ์ฌ๋ผ์ด๋ ์ต์
์ค๋น
|
|
955
|
+
const optimizedSliderOptions = {
|
|
956
|
+
autoSlideInterval: ((slot.config as any)?.slideInterval || 5000) / 1000,
|
|
957
|
+
...slot.config,
|
|
958
|
+
// ๐ ๋์ ํฌ๊ธฐ ์ ๋ณด ์ ๋ฌ
|
|
959
|
+
optimizedHeight: (slot as any).optimizedHeight,
|
|
960
|
+
aspectRatio: (slot as any).aspectRatio
|
|
961
|
+
};
|
|
962
|
+
|
|
609
963
|
// ํ
์คํธ ๊ด๊ณ ๋ TextTransitionManager ์ฌ์ฉ, ๊ทธ ์ธ๋ CarouselSliderManager ์ฌ์ฉ
|
|
610
964
|
if (slot.adType === AdType.TEXT) {
|
|
611
965
|
sliderElement = TextTransitionManager.createTextTransitionContainer(
|
|
612
966
|
slot,
|
|
613
967
|
advertisements,
|
|
614
|
-
|
|
615
|
-
autoSlideInterval: ((slot.config as any)?.slideInterval || 5000) / 1000,
|
|
616
|
-
...slot.config
|
|
617
|
-
},
|
|
968
|
+
optimizedSliderOptions,
|
|
618
969
|
trackEventCallback
|
|
619
970
|
);
|
|
620
971
|
|
|
@@ -625,15 +976,12 @@ export class AdsModule implements BaseModule {
|
|
|
625
976
|
sliderElement = CarouselSliderManager.createSliderContainer(
|
|
626
977
|
slot,
|
|
627
978
|
advertisements,
|
|
628
|
-
|
|
629
|
-
autoSlideInterval: ((slot.config as any)?.slideInterval || 5000) / 1000,
|
|
630
|
-
...slot.config
|
|
631
|
-
},
|
|
979
|
+
optimizedSliderOptions,
|
|
632
980
|
trackEventCallback
|
|
633
981
|
);
|
|
634
982
|
|
|
635
983
|
if (this._config?.debug) {
|
|
636
|
-
console.log(`๐ Carousel slider created for ${slot.adType} slot: ${slot.id} with ${advertisements.length} ads`);
|
|
984
|
+
console.log(`๐ Carousel slider created for ${slot.adType} slot: ${slot.id} with ${advertisements.length} ads (optimized: ${(slot as any).optimizedHeight || 'default'})`);
|
|
637
985
|
}
|
|
638
986
|
}
|
|
639
987
|
|
|
@@ -664,20 +1012,25 @@ export class AdsModule implements BaseModule {
|
|
|
664
1012
|
// ๊ธฐ๋ณธ HTML ๊ตฌ์กฐ ์์ฑ
|
|
665
1013
|
const adElement = document.createElement('div');
|
|
666
1014
|
adElement.className = 'adstage-ad';
|
|
667
|
-
|
|
668
|
-
|
|
1015
|
+
|
|
1016
|
+
// ์ค๋งํธํ ํฌ๊ธฐ ์ค์ - ์ต์ ํ๋ ํฌ๊ธฐ๊ฐ ์์ผ๋ฉด ์ฌ์ฉ
|
|
1017
|
+
const optimizedHeight = (slot as any).optimizedHeight;
|
|
1018
|
+
const containerElement = container.parentElement || container;
|
|
1019
|
+
|
|
1020
|
+
if (optimizedHeight) {
|
|
1021
|
+
adElement.style.width = '100%';
|
|
1022
|
+
adElement.style.height = optimizedHeight;
|
|
1023
|
+
} else {
|
|
1024
|
+
const { width, height } = this.calculateAdSize(containerElement, slot.adType, slot.config || {});
|
|
1025
|
+
adElement.style.width = width;
|
|
1026
|
+
adElement.style.height = height;
|
|
1027
|
+
}
|
|
669
1028
|
|
|
670
1029
|
// ๊ด๊ณ ํ์
๋ณ ๋ ๋๋ง
|
|
671
1030
|
switch (slot.adType) {
|
|
672
1031
|
case AdType.BANNER:
|
|
673
1032
|
if (ad.imageUrl) {
|
|
674
|
-
|
|
675
|
-
img.src = ad.imageUrl;
|
|
676
|
-
img.alt = ad.title;
|
|
677
|
-
img.style.width = '100%';
|
|
678
|
-
img.style.height = '100%';
|
|
679
|
-
img.style.objectFit = 'cover';
|
|
680
|
-
adElement.appendChild(img);
|
|
1033
|
+
await this.renderOptimizedBannerImage(adElement, ad, slot);
|
|
681
1034
|
}
|
|
682
1035
|
break;
|
|
683
1036
|
|
|
@@ -718,6 +1071,58 @@ export class AdsModule implements BaseModule {
|
|
|
718
1071
|
container.appendChild(adElement);
|
|
719
1072
|
}
|
|
720
1073
|
|
|
1074
|
+
/**
|
|
1075
|
+
* ์ต์ ํ๋ ๋ฐฐ๋ ์ด๋ฏธ์ง ๋ ๋๋ง
|
|
1076
|
+
*/
|
|
1077
|
+
private async renderOptimizedBannerImage(adElement: HTMLElement, ad: Advertisement, slot: AdSlot): Promise<void> {
|
|
1078
|
+
try {
|
|
1079
|
+
// ์ด๋ฏธ์ง ํฌ๊ธฐ ์ ๋ณด ๋ก๋
|
|
1080
|
+
const imageDimensions = await this.loadImageDimensions(ad.imageUrl!);
|
|
1081
|
+
const imageAspectRatio = imageDimensions.width / imageDimensions.height;
|
|
1082
|
+
|
|
1083
|
+
// ์ปจํ
์ด๋ ๋น์จ ๊ณ์ฐ
|
|
1084
|
+
const containerAspectRatio = (slot as any).aspectRatio || 16/9;
|
|
1085
|
+
|
|
1086
|
+
// ์ด๋ฏธ์ง ์์ ์์ฑ
|
|
1087
|
+
const img = document.createElement('img');
|
|
1088
|
+
img.src = ad.imageUrl!;
|
|
1089
|
+
img.alt = ad.title;
|
|
1090
|
+
img.style.width = '100%';
|
|
1091
|
+
img.style.height = '100%';
|
|
1092
|
+
|
|
1093
|
+
// ๐จ ์ต์ ํ๋ ์คํ์ผ ์ ์ฉ
|
|
1094
|
+
this.applyOptimizedImageStyle(img, imageAspectRatio, containerAspectRatio);
|
|
1095
|
+
|
|
1096
|
+
// ์ด๋ฏธ์ง ๋ก๋ ์๋ฃ ์ฒ๋ฆฌ
|
|
1097
|
+
img.onload = () => {
|
|
1098
|
+
if (this._config?.debug) {
|
|
1099
|
+
console.log(`๐ผ๏ธ Optimized banner image loaded: ${imageDimensions.width}x${imageDimensions.height} (ratio: ${imageAspectRatio.toFixed(2)})`);
|
|
1100
|
+
}
|
|
1101
|
+
};
|
|
1102
|
+
|
|
1103
|
+
// ์๋ฌ ์ฒ๋ฆฌ
|
|
1104
|
+
img.onerror = () => {
|
|
1105
|
+
console.warn(`Failed to load banner image: ${ad.imageUrl}`);
|
|
1106
|
+
// ํด๋ฐฑ ํ
์คํธ ํ์
|
|
1107
|
+
adElement.innerHTML = `<div style="display: flex; align-items: center; justify-content: center; background: #f0f0f0; color: #666;">${ad.title}</div>`;
|
|
1108
|
+
};
|
|
1109
|
+
|
|
1110
|
+
adElement.appendChild(img);
|
|
1111
|
+
|
|
1112
|
+
} catch (error) {
|
|
1113
|
+
console.warn('Failed to optimize banner image, using fallback:', error);
|
|
1114
|
+
|
|
1115
|
+
// ๊ธฐ๋ณธ ์ด๋ฏธ์ง ๋ ๋๋ง (ํด๋ฐฑ)
|
|
1116
|
+
const img = document.createElement('img');
|
|
1117
|
+
img.src = ad.imageUrl!;
|
|
1118
|
+
img.alt = ad.title;
|
|
1119
|
+
img.style.width = '100%';
|
|
1120
|
+
img.style.height = '100%';
|
|
1121
|
+
img.style.objectFit = 'cover';
|
|
1122
|
+
adElement.appendChild(img);
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
|
|
721
1126
|
/**
|
|
722
1127
|
* ๊ด๊ณ ์ฌ๋กฏ ์๋ก๊ณ ์นจ
|
|
723
1128
|
*/
|