unified-video-framework 1.4.438 → 1.4.439
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/package.json +1 -1
- package/packages/core/dist/version.d.ts +1 -1
- package/packages/core/dist/version.js +1 -1
- package/packages/core/src/version.ts +1 -1
- package/packages/web/dist/WebPlayer.d.ts +26 -0
- package/packages/web/dist/WebPlayer.d.ts.map +1 -1
- package/packages/web/dist/WebPlayer.js +609 -0
- package/packages/web/dist/WebPlayer.js.map +1 -1
- package/packages/web/dist/index.d.ts +1 -1
- package/packages/web/dist/index.d.ts.map +1 -1
- package/packages/web/dist/index.js.map +1 -1
- package/packages/web/dist/react/types/FlashNewsTickerTypes.d.ts +61 -0
- package/packages/web/dist/react/types/FlashNewsTickerTypes.d.ts.map +1 -1
- package/packages/web/src/WebPlayer.ts +824 -1
- package/packages/web/src/index.ts +10 -1
- package/packages/web/src/react/types/FlashNewsTickerTypes.ts +214 -1
|
@@ -20,7 +20,19 @@ import {
|
|
|
20
20
|
VideoSegment,
|
|
21
21
|
ChapterEvents
|
|
22
22
|
} from './chapters/types/ChapterTypes';
|
|
23
|
-
import type {
|
|
23
|
+
import type {
|
|
24
|
+
FlashNewsTickerConfig,
|
|
25
|
+
FlashNewsTickerItem,
|
|
26
|
+
BroadcastStyleConfig,
|
|
27
|
+
BroadcastTheme,
|
|
28
|
+
TickerStyleVariant,
|
|
29
|
+
TickerLayoutStyle,
|
|
30
|
+
IntroAnimationType,
|
|
31
|
+
TwoLineDisplayConfig,
|
|
32
|
+
DecorativeShapesConfig,
|
|
33
|
+
ItemCyclingConfig,
|
|
34
|
+
SeparatorType
|
|
35
|
+
} from './react/types/FlashNewsTickerTypes';
|
|
24
36
|
import YouTubeExtractor from './utils/YouTubeExtractor';
|
|
25
37
|
import { DRMManager, DRMErrorHandler } from './drm';
|
|
26
38
|
import type { DRMInitResult, DRMError } from './drm';
|
|
@@ -71,6 +83,19 @@ export class WebPlayer extends BasePlayer {
|
|
|
71
83
|
private flashTickerTopElement: HTMLDivElement | null = null;
|
|
72
84
|
private flashTickerBottomElement: HTMLDivElement | null = null;
|
|
73
85
|
|
|
86
|
+
// Professional Ticker State (for item cycling and intro animations)
|
|
87
|
+
private tickerCurrentItemIndex: number = 0;
|
|
88
|
+
private tickerCycleTimer: number | null = null;
|
|
89
|
+
private tickerConfig: FlashNewsTickerConfig | null = null;
|
|
90
|
+
private tickerHeadlineElement: HTMLDivElement | null = null;
|
|
91
|
+
private tickerDetailElement: HTMLDivElement | null = null;
|
|
92
|
+
private tickerIntroOverlay: HTMLDivElement | null = null;
|
|
93
|
+
private tickerProgressBar: HTMLDivElement | null = null;
|
|
94
|
+
private tickerProgressFill: HTMLDivElement | null = null;
|
|
95
|
+
private tickerIsPaused: boolean = false;
|
|
96
|
+
private tickerPauseStartTime: number = 0;
|
|
97
|
+
private tickerRemainingTime: number = 0;
|
|
98
|
+
|
|
74
99
|
// Free preview gate state
|
|
75
100
|
private previewGateHit: boolean = false;
|
|
76
101
|
private paymentSuccessTime: number = 0;
|
|
@@ -2550,6 +2575,11 @@ export class WebPlayer extends BasePlayer {
|
|
|
2550
2575
|
private createTickerElement(config: FlashNewsTickerConfig, position: 'top' | 'bottom'): HTMLDivElement {
|
|
2551
2576
|
// Route to broadcast style if configured
|
|
2552
2577
|
if (config.styleVariant === 'broadcast') {
|
|
2578
|
+
const layoutStyle = config.broadcastStyle?.layoutStyle || 'broadcast';
|
|
2579
|
+
// Route to professional ticker for two-line or professional layouts
|
|
2580
|
+
if (layoutStyle === 'two-line' || layoutStyle === 'professional') {
|
|
2581
|
+
return this.createProfessionalTickerElement(config, position);
|
|
2582
|
+
}
|
|
2553
2583
|
return this.createBroadcastTickerElement(config, position);
|
|
2554
2584
|
}
|
|
2555
2585
|
|
|
@@ -2917,14 +2947,19 @@ export class WebPlayer extends BasePlayer {
|
|
|
2917
2947
|
const style = document.createElement('style');
|
|
2918
2948
|
style.id = 'uvf-ticker-animation';
|
|
2919
2949
|
style.textContent = `
|
|
2950
|
+
/* Basic ticker scroll */
|
|
2920
2951
|
@keyframes ticker-scroll {
|
|
2921
2952
|
0% { transform: translateX(0%); }
|
|
2922
2953
|
100% { transform: translateX(-100%); }
|
|
2923
2954
|
}
|
|
2955
|
+
|
|
2956
|
+
/* Globe rotation */
|
|
2924
2957
|
@keyframes globe-rotate {
|
|
2925
2958
|
0% { transform: rotate(0deg); }
|
|
2926
2959
|
100% { transform: rotate(360deg); }
|
|
2927
2960
|
}
|
|
2961
|
+
|
|
2962
|
+
/* LIVE badge animations */
|
|
2928
2963
|
@keyframes live-pulse {
|
|
2929
2964
|
0%, 100% { opacity: 1; transform: scale(1); }
|
|
2930
2965
|
50% { opacity: 0.85; transform: scale(1.02); }
|
|
@@ -2933,6 +2968,85 @@ export class WebPlayer extends BasePlayer {
|
|
|
2933
2968
|
0%, 100% { opacity: 1; }
|
|
2934
2969
|
50% { opacity: 0.3; }
|
|
2935
2970
|
}
|
|
2971
|
+
|
|
2972
|
+
/* Intro Animations */
|
|
2973
|
+
@keyframes intro-slide-in {
|
|
2974
|
+
0% { transform: translateX(-100%); opacity: 0; }
|
|
2975
|
+
20% { transform: translateX(0); opacity: 1; }
|
|
2976
|
+
80% { transform: translateX(0); opacity: 1; }
|
|
2977
|
+
100% { transform: translateX(100%); opacity: 0; }
|
|
2978
|
+
}
|
|
2979
|
+
|
|
2980
|
+
@keyframes intro-flash {
|
|
2981
|
+
0%, 100% { opacity: 0; }
|
|
2982
|
+
10%, 30%, 50%, 70%, 90% { opacity: 1; background: rgba(255,255,255,0.3); }
|
|
2983
|
+
20%, 40%, 60%, 80% { opacity: 1; background: transparent; }
|
|
2984
|
+
}
|
|
2985
|
+
|
|
2986
|
+
@keyframes intro-scale {
|
|
2987
|
+
0% { transform: scale(0); opacity: 0; }
|
|
2988
|
+
20% { transform: scale(1.2); opacity: 1; }
|
|
2989
|
+
30% { transform: scale(1); }
|
|
2990
|
+
80% { transform: scale(1); opacity: 1; }
|
|
2991
|
+
100% { transform: scale(0); opacity: 0; }
|
|
2992
|
+
}
|
|
2993
|
+
|
|
2994
|
+
@keyframes intro-pulse {
|
|
2995
|
+
0% { transform: scale(0.8); opacity: 0; }
|
|
2996
|
+
25% { transform: scale(1.1); opacity: 1; }
|
|
2997
|
+
50% { transform: scale(1); opacity: 1; }
|
|
2998
|
+
75% { transform: scale(1); opacity: 1; }
|
|
2999
|
+
100% { transform: scale(0.8); opacity: 0; }
|
|
3000
|
+
}
|
|
3001
|
+
|
|
3002
|
+
@keyframes intro-shake {
|
|
3003
|
+
0% { transform: translateX(0); opacity: 0; }
|
|
3004
|
+
10% { opacity: 1; }
|
|
3005
|
+
15%, 35%, 55%, 75% { transform: translateX(-5px); }
|
|
3006
|
+
25%, 45%, 65%, 85% { transform: translateX(5px); }
|
|
3007
|
+
90% { transform: translateX(0); opacity: 1; }
|
|
3008
|
+
100% { transform: translateX(0); opacity: 0; }
|
|
3009
|
+
}
|
|
3010
|
+
|
|
3011
|
+
@keyframes intro-none {
|
|
3012
|
+
0% { opacity: 1; }
|
|
3013
|
+
80% { opacity: 1; }
|
|
3014
|
+
100% { opacity: 0; }
|
|
3015
|
+
}
|
|
3016
|
+
|
|
3017
|
+
/* Item Transitions */
|
|
3018
|
+
@keyframes item-fade-out {
|
|
3019
|
+
from { opacity: 1; }
|
|
3020
|
+
to { opacity: 0; }
|
|
3021
|
+
}
|
|
3022
|
+
@keyframes item-fade-in {
|
|
3023
|
+
from { opacity: 0; }
|
|
3024
|
+
to { opacity: 1; }
|
|
3025
|
+
}
|
|
3026
|
+
@keyframes item-slide-out {
|
|
3027
|
+
from { transform: translateY(0); opacity: 1; }
|
|
3028
|
+
to { transform: translateY(-100%); opacity: 0; }
|
|
3029
|
+
}
|
|
3030
|
+
@keyframes item-slide-in {
|
|
3031
|
+
from { transform: translateY(100%); opacity: 0; }
|
|
3032
|
+
to { transform: translateY(0); opacity: 1; }
|
|
3033
|
+
}
|
|
3034
|
+
|
|
3035
|
+
/* Decorative Separator Animations */
|
|
3036
|
+
@keyframes separator-pulse {
|
|
3037
|
+
0%, 100% { opacity: 0.8; transform: scaleY(1); }
|
|
3038
|
+
50% { opacity: 1; transform: scaleY(1.1); }
|
|
3039
|
+
}
|
|
3040
|
+
|
|
3041
|
+
@keyframes chevron-pulse {
|
|
3042
|
+
0%, 100% { transform: translateX(0); opacity: 0.8; }
|
|
3043
|
+
50% { transform: translateX(3px); opacity: 1; }
|
|
3044
|
+
}
|
|
3045
|
+
|
|
3046
|
+
@keyframes diamond-spin {
|
|
3047
|
+
from { transform: rotate(0deg); }
|
|
3048
|
+
to { transform: rotate(360deg); }
|
|
3049
|
+
}
|
|
2936
3050
|
`;
|
|
2937
3051
|
document.head.appendChild(style);
|
|
2938
3052
|
}
|
|
@@ -2951,6 +3065,715 @@ export class WebPlayer extends BasePlayer {
|
|
|
2951
3065
|
return Math.max(totalWidth / speed, 15); // Minimum 15s for smooth scrolling
|
|
2952
3066
|
}
|
|
2953
3067
|
|
|
3068
|
+
/**
|
|
3069
|
+
* Create professional TV-style ticker with two-line display and item cycling
|
|
3070
|
+
*/
|
|
3071
|
+
private createProfessionalTickerElement(config: FlashNewsTickerConfig, position: 'top' | 'bottom'): HTMLDivElement {
|
|
3072
|
+
const broadcastStyle = config.broadcastStyle || {};
|
|
3073
|
+
const theme = broadcastStyle.theme || 'breaking-red';
|
|
3074
|
+
const twoLineConfig = broadcastStyle.twoLineDisplay || {};
|
|
3075
|
+
const itemCycling = config.itemCycling || {};
|
|
3076
|
+
|
|
3077
|
+
// Store config for item cycling
|
|
3078
|
+
this.tickerConfig = config;
|
|
3079
|
+
this.tickerCurrentItemIndex = 0;
|
|
3080
|
+
|
|
3081
|
+
// Get theme colors
|
|
3082
|
+
const themeColors = this.getBroadcastThemeColors(theme, broadcastStyle);
|
|
3083
|
+
|
|
3084
|
+
const ticker = document.createElement('div');
|
|
3085
|
+
ticker.className = `uvf-flash-ticker ticker-${position} ticker-professional`;
|
|
3086
|
+
|
|
3087
|
+
const bottomOffset = config.bottomOffset || 0;
|
|
3088
|
+
const topOffset = config.topOffset || 0;
|
|
3089
|
+
const headerHeight = broadcastStyle.headerHeight || 28;
|
|
3090
|
+
|
|
3091
|
+
// Calculate body height based on two-line config
|
|
3092
|
+
const topLineConfig = twoLineConfig.topLine || {};
|
|
3093
|
+
const bottomLineConfig = twoLineConfig.bottomLine || {};
|
|
3094
|
+
const topLineHeight = topLineConfig.minHeight || 32;
|
|
3095
|
+
const bottomLineHeight = bottomLineConfig.height || 28;
|
|
3096
|
+
const bodyHeight = twoLineConfig.enabled !== false ? topLineHeight + bottomLineHeight : (config.height || 36);
|
|
3097
|
+
const progressHeight = itemCycling.showProgress ? (itemCycling.progressHeight || 3) : 0;
|
|
3098
|
+
const totalHeight = headerHeight + bodyHeight + progressHeight;
|
|
3099
|
+
|
|
3100
|
+
ticker.style.cssText = `
|
|
3101
|
+
position: absolute;
|
|
3102
|
+
left: 0;
|
|
3103
|
+
right: 0;
|
|
3104
|
+
height: ${totalHeight}px;
|
|
3105
|
+
${position === 'top' ? `top: ${topOffset}px;` : `bottom: ${bottomOffset}px;`}
|
|
3106
|
+
overflow: hidden;
|
|
3107
|
+
pointer-events: ${itemCycling.pauseOnHover !== false ? 'auto' : 'none'};
|
|
3108
|
+
display: flex;
|
|
3109
|
+
flex-direction: column;
|
|
3110
|
+
`;
|
|
3111
|
+
|
|
3112
|
+
// Create header row
|
|
3113
|
+
const header = this.createProfessionalHeader(broadcastStyle, themeColors, headerHeight);
|
|
3114
|
+
ticker.appendChild(header);
|
|
3115
|
+
|
|
3116
|
+
// Create body with two-line display
|
|
3117
|
+
const body = this.createTwoLineBody(config, twoLineConfig, themeColors.bodyBg, bodyHeight);
|
|
3118
|
+
ticker.appendChild(body);
|
|
3119
|
+
|
|
3120
|
+
// Create progress bar if enabled
|
|
3121
|
+
if (itemCycling.showProgress) {
|
|
3122
|
+
const progressBar = this.createProgressBar(itemCycling);
|
|
3123
|
+
ticker.appendChild(progressBar);
|
|
3124
|
+
}
|
|
3125
|
+
|
|
3126
|
+
// Create intro overlay (hidden by default)
|
|
3127
|
+
const introOverlay = this.createIntroOverlay(broadcastStyle);
|
|
3128
|
+
ticker.appendChild(introOverlay);
|
|
3129
|
+
this.tickerIntroOverlay = introOverlay;
|
|
3130
|
+
|
|
3131
|
+
// Add hover pause functionality
|
|
3132
|
+
if (itemCycling.pauseOnHover !== false && itemCycling.enabled) {
|
|
3133
|
+
ticker.addEventListener('mouseenter', () => this.pauseItemCycling());
|
|
3134
|
+
ticker.addEventListener('mouseleave', () => this.resumeItemCycling());
|
|
3135
|
+
}
|
|
3136
|
+
|
|
3137
|
+
// Add all animation keyframes
|
|
3138
|
+
this.ensureTickerAnimations();
|
|
3139
|
+
|
|
3140
|
+
// Start item cycling if enabled
|
|
3141
|
+
if (itemCycling.enabled && config.items && config.items.length > 1) {
|
|
3142
|
+
// Initial display with intro animation if configured
|
|
3143
|
+
const firstItem = config.items[0];
|
|
3144
|
+
if (firstItem.showIntro) {
|
|
3145
|
+
this.playIntroAnimation(firstItem).then(() => {
|
|
3146
|
+
this.startItemCycling();
|
|
3147
|
+
});
|
|
3148
|
+
} else {
|
|
3149
|
+
this.startItemCycling();
|
|
3150
|
+
}
|
|
3151
|
+
}
|
|
3152
|
+
|
|
3153
|
+
return ticker;
|
|
3154
|
+
}
|
|
3155
|
+
|
|
3156
|
+
/**
|
|
3157
|
+
* Create professional header row with globe, separator, text, and LIVE badge
|
|
3158
|
+
*/
|
|
3159
|
+
private createProfessionalHeader(
|
|
3160
|
+
broadcastStyle: BroadcastStyleConfig,
|
|
3161
|
+
themeColors: { headerBg: string; bodyBg: string },
|
|
3162
|
+
headerHeight: number
|
|
3163
|
+
): HTMLDivElement {
|
|
3164
|
+
const header = document.createElement('div');
|
|
3165
|
+
header.className = 'uvf-ticker-header-row';
|
|
3166
|
+
header.style.cssText = `
|
|
3167
|
+
display: flex;
|
|
3168
|
+
align-items: center;
|
|
3169
|
+
height: ${headerHeight}px;
|
|
3170
|
+
background: ${themeColors.headerBg};
|
|
3171
|
+
padding: 0 12px;
|
|
3172
|
+
position: relative;
|
|
3173
|
+
`;
|
|
3174
|
+
|
|
3175
|
+
// Add globe graphic if enabled
|
|
3176
|
+
if (broadcastStyle.showGlobe !== false) {
|
|
3177
|
+
const globe = this.createGlobeElement(broadcastStyle.animateGlobe !== false);
|
|
3178
|
+
header.appendChild(globe);
|
|
3179
|
+
}
|
|
3180
|
+
|
|
3181
|
+
// Add decorative separator between globe and text
|
|
3182
|
+
if (broadcastStyle.decorativeShapes?.headerSeparator) {
|
|
3183
|
+
const separator = this.createDecorativeSeparator(broadcastStyle.decorativeShapes.headerSeparator);
|
|
3184
|
+
header.appendChild(separator);
|
|
3185
|
+
} else if (broadcastStyle.showGlobe !== false) {
|
|
3186
|
+
// Default simple separator
|
|
3187
|
+
const defaultSeparator = document.createElement('div');
|
|
3188
|
+
defaultSeparator.className = 'uvf-ticker-separator';
|
|
3189
|
+
defaultSeparator.style.cssText = `
|
|
3190
|
+
width: 2px;
|
|
3191
|
+
height: 18px;
|
|
3192
|
+
background: rgba(255,255,255,0.6);
|
|
3193
|
+
margin: 0 10px;
|
|
3194
|
+
`;
|
|
3195
|
+
header.appendChild(defaultSeparator);
|
|
3196
|
+
}
|
|
3197
|
+
|
|
3198
|
+
// Add header text (BREAKING NEWS)
|
|
3199
|
+
const headerText = document.createElement('span');
|
|
3200
|
+
headerText.className = 'uvf-ticker-header-text';
|
|
3201
|
+
headerText.textContent = broadcastStyle.headerText || 'BREAKING NEWS';
|
|
3202
|
+
headerText.style.cssText = `
|
|
3203
|
+
color: ${broadcastStyle.headerTextColor || '#ffffff'};
|
|
3204
|
+
font-size: ${broadcastStyle.headerFontSize || 16}px;
|
|
3205
|
+
font-weight: 800;
|
|
3206
|
+
text-transform: uppercase;
|
|
3207
|
+
letter-spacing: 1px;
|
|
3208
|
+
text-shadow: 1px 1px 2px rgba(0,0,0,0.3);
|
|
3209
|
+
`;
|
|
3210
|
+
header.appendChild(headerText);
|
|
3211
|
+
|
|
3212
|
+
// Add LIVE badge if enabled
|
|
3213
|
+
if (broadcastStyle.showLiveBadge !== false) {
|
|
3214
|
+
const liveBadge = this.createLiveBadgeElement(broadcastStyle.pulseLiveBadge !== false);
|
|
3215
|
+
header.appendChild(liveBadge);
|
|
3216
|
+
}
|
|
3217
|
+
|
|
3218
|
+
return header;
|
|
3219
|
+
}
|
|
3220
|
+
|
|
3221
|
+
/**
|
|
3222
|
+
* Create decorative separator element
|
|
3223
|
+
*/
|
|
3224
|
+
private createDecorativeSeparator(config: NonNullable<DecorativeShapesConfig['headerSeparator']>): HTMLDivElement {
|
|
3225
|
+
const separator = document.createElement('div');
|
|
3226
|
+
separator.className = 'uvf-ticker-separator';
|
|
3227
|
+
|
|
3228
|
+
const width = config.width || 2;
|
|
3229
|
+
const height = config.height || 20;
|
|
3230
|
+
const color = config.color || '#ffffff';
|
|
3231
|
+
const animated = config.animated !== false;
|
|
3232
|
+
const type = config.type || 'line';
|
|
3233
|
+
|
|
3234
|
+
let animationStyle = '';
|
|
3235
|
+
let content = '';
|
|
3236
|
+
|
|
3237
|
+
switch (type) {
|
|
3238
|
+
case 'pulse-line':
|
|
3239
|
+
animationStyle = animated ? 'animation: separator-pulse 1.5s ease-in-out infinite;' : '';
|
|
3240
|
+
break;
|
|
3241
|
+
case 'chevron':
|
|
3242
|
+
content = '›';
|
|
3243
|
+
animationStyle = animated ? 'animation: chevron-pulse 1s ease-in-out infinite;' : '';
|
|
3244
|
+
break;
|
|
3245
|
+
case 'diamond':
|
|
3246
|
+
content = '◆';
|
|
3247
|
+
animationStyle = animated ? 'animation: diamond-spin 3s linear infinite;' : '';
|
|
3248
|
+
break;
|
|
3249
|
+
case 'dot':
|
|
3250
|
+
content = '•';
|
|
3251
|
+
animationStyle = animated ? 'animation: dot-blink 1s ease-in-out infinite;' : '';
|
|
3252
|
+
break;
|
|
3253
|
+
default:
|
|
3254
|
+
// Simple line
|
|
3255
|
+
break;
|
|
3256
|
+
}
|
|
3257
|
+
|
|
3258
|
+
if (type === 'line' || type === 'pulse-line') {
|
|
3259
|
+
separator.style.cssText = `
|
|
3260
|
+
width: ${width}px;
|
|
3261
|
+
height: ${height}px;
|
|
3262
|
+
background: ${color};
|
|
3263
|
+
margin: 0 10px;
|
|
3264
|
+
opacity: 0.8;
|
|
3265
|
+
${animationStyle}
|
|
3266
|
+
`;
|
|
3267
|
+
} else {
|
|
3268
|
+
separator.textContent = content;
|
|
3269
|
+
separator.style.cssText = `
|
|
3270
|
+
color: ${color};
|
|
3271
|
+
font-size: ${height}px;
|
|
3272
|
+
margin: 0 10px;
|
|
3273
|
+
opacity: 0.8;
|
|
3274
|
+
display: flex;
|
|
3275
|
+
align-items: center;
|
|
3276
|
+
justify-content: center;
|
|
3277
|
+
${animationStyle}
|
|
3278
|
+
`;
|
|
3279
|
+
}
|
|
3280
|
+
|
|
3281
|
+
return separator;
|
|
3282
|
+
}
|
|
3283
|
+
|
|
3284
|
+
/**
|
|
3285
|
+
* Create two-line body with static headline and scrolling detail
|
|
3286
|
+
*/
|
|
3287
|
+
private createTwoLineBody(
|
|
3288
|
+
config: FlashNewsTickerConfig,
|
|
3289
|
+
twoLineConfig: TwoLineDisplayConfig,
|
|
3290
|
+
bodyBg: string,
|
|
3291
|
+
bodyHeight: number
|
|
3292
|
+
): HTMLDivElement {
|
|
3293
|
+
const body = document.createElement('div');
|
|
3294
|
+
body.className = 'uvf-ticker-body-row';
|
|
3295
|
+
body.style.cssText = `
|
|
3296
|
+
display: flex;
|
|
3297
|
+
flex-direction: column;
|
|
3298
|
+
height: ${bodyHeight}px;
|
|
3299
|
+
background: ${bodyBg};
|
|
3300
|
+
overflow: hidden;
|
|
3301
|
+
position: relative;
|
|
3302
|
+
`;
|
|
3303
|
+
|
|
3304
|
+
const topLineConfig = twoLineConfig.topLine || {};
|
|
3305
|
+
const bottomLineConfig = twoLineConfig.bottomLine || {};
|
|
3306
|
+
|
|
3307
|
+
// Get first item for initial display
|
|
3308
|
+
const firstItem = config.items?.[0];
|
|
3309
|
+
|
|
3310
|
+
// Create headline line (top - static)
|
|
3311
|
+
const headlineLine = document.createElement('div');
|
|
3312
|
+
headlineLine.className = 'uvf-ticker-headline-line';
|
|
3313
|
+
this.tickerHeadlineElement = headlineLine;
|
|
3314
|
+
|
|
3315
|
+
const topLineHeight = topLineConfig.minHeight || 32;
|
|
3316
|
+
const topLineFontSize = topLineConfig.fontSize || 16;
|
|
3317
|
+
const topLineLineHeight = topLineConfig.lineHeight || 1.3;
|
|
3318
|
+
const topLineMultiLine = topLineConfig.multiLine !== false;
|
|
3319
|
+
const topLineMaxLines = topLineConfig.maxLines || 2;
|
|
3320
|
+
|
|
3321
|
+
headlineLine.style.cssText = `
|
|
3322
|
+
display: flex;
|
|
3323
|
+
align-items: center;
|
|
3324
|
+
min-height: ${topLineHeight}px;
|
|
3325
|
+
padding: ${topLineConfig.padding || 8}px 12px;
|
|
3326
|
+
background: ${topLineConfig.backgroundColor || 'transparent'};
|
|
3327
|
+
overflow: hidden;
|
|
3328
|
+
${topLineMultiLine ? '' : 'white-space: nowrap;'}
|
|
3329
|
+
`;
|
|
3330
|
+
|
|
3331
|
+
const headlineText = document.createElement('span');
|
|
3332
|
+
headlineText.className = 'uvf-ticker-headline-text';
|
|
3333
|
+
|
|
3334
|
+
// Support multi-line text with CSS
|
|
3335
|
+
headlineText.style.cssText = `
|
|
3336
|
+
color: ${topLineConfig.textColor || config.textColor || '#ffffff'};
|
|
3337
|
+
font-size: ${topLineFontSize}px;
|
|
3338
|
+
font-weight: 700;
|
|
3339
|
+
line-height: ${topLineLineHeight};
|
|
3340
|
+
${topLineMultiLine ? `
|
|
3341
|
+
display: -webkit-box;
|
|
3342
|
+
-webkit-line-clamp: ${topLineMaxLines};
|
|
3343
|
+
-webkit-box-orient: vertical;
|
|
3344
|
+
overflow: hidden;
|
|
3345
|
+
text-overflow: ellipsis;
|
|
3346
|
+
white-space: normal;
|
|
3347
|
+
word-wrap: break-word;
|
|
3348
|
+
` : `
|
|
3349
|
+
white-space: nowrap;
|
|
3350
|
+
overflow: hidden;
|
|
3351
|
+
text-overflow: ellipsis;
|
|
3352
|
+
`}
|
|
3353
|
+
`;
|
|
3354
|
+
|
|
3355
|
+
// Set initial headline content
|
|
3356
|
+
if (firstItem) {
|
|
3357
|
+
if (firstItem.headlineHtml) {
|
|
3358
|
+
headlineText.innerHTML = firstItem.headlineHtml;
|
|
3359
|
+
} else if (firstItem.headline) {
|
|
3360
|
+
headlineText.textContent = firstItem.headline;
|
|
3361
|
+
} else {
|
|
3362
|
+
// Fallback to text if no headline
|
|
3363
|
+
headlineText.textContent = firstItem.text;
|
|
3364
|
+
}
|
|
3365
|
+
}
|
|
3366
|
+
|
|
3367
|
+
headlineLine.appendChild(headlineText);
|
|
3368
|
+
body.appendChild(headlineLine);
|
|
3369
|
+
|
|
3370
|
+
// Add separator line between headline and detail
|
|
3371
|
+
if (twoLineConfig.showSeparator !== false) {
|
|
3372
|
+
const separatorLine = document.createElement('div');
|
|
3373
|
+
separatorLine.style.cssText = `
|
|
3374
|
+
height: 1px;
|
|
3375
|
+
background: ${twoLineConfig.separatorColor || 'rgba(255,255,255,0.2)'};
|
|
3376
|
+
margin: 0 12px;
|
|
3377
|
+
`;
|
|
3378
|
+
body.appendChild(separatorLine);
|
|
3379
|
+
}
|
|
3380
|
+
|
|
3381
|
+
// Create detail line (bottom - scrolling)
|
|
3382
|
+
const detailLine = document.createElement('div');
|
|
3383
|
+
detailLine.className = 'uvf-ticker-detail-line';
|
|
3384
|
+
this.tickerDetailElement = detailLine;
|
|
3385
|
+
|
|
3386
|
+
const bottomLineHeight = bottomLineConfig.height || 28;
|
|
3387
|
+
const bottomLineFontSize = bottomLineConfig.fontSize || 14;
|
|
3388
|
+
const bottomLineSpeed = bottomLineConfig.speed || 80;
|
|
3389
|
+
|
|
3390
|
+
detailLine.style.cssText = `
|
|
3391
|
+
display: flex;
|
|
3392
|
+
align-items: center;
|
|
3393
|
+
height: ${bottomLineHeight}px;
|
|
3394
|
+
background: ${bottomLineConfig.backgroundColor || 'transparent'};
|
|
3395
|
+
overflow: hidden;
|
|
3396
|
+
position: relative;
|
|
3397
|
+
`;
|
|
3398
|
+
|
|
3399
|
+
// Create scrolling track for detail
|
|
3400
|
+
const track = document.createElement('div');
|
|
3401
|
+
track.className = 'uvf-ticker-track';
|
|
3402
|
+
|
|
3403
|
+
// Calculate scroll duration based on content
|
|
3404
|
+
const detailText = firstItem?.text || '';
|
|
3405
|
+
const textWidth = detailText.length * 10; // Approximate
|
|
3406
|
+
const duration = Math.max(textWidth / bottomLineSpeed, 10);
|
|
3407
|
+
|
|
3408
|
+
track.style.cssText = `
|
|
3409
|
+
display: flex;
|
|
3410
|
+
white-space: nowrap;
|
|
3411
|
+
animation: ticker-scroll ${duration}s linear infinite;
|
|
3412
|
+
will-change: transform;
|
|
3413
|
+
padding-left: 100%;
|
|
3414
|
+
`;
|
|
3415
|
+
|
|
3416
|
+
// Render detail text multiple times for seamless loop
|
|
3417
|
+
const renderDetail = (text: string, html?: string) => {
|
|
3418
|
+
for (let i = 0; i < 10; i++) {
|
|
3419
|
+
const span = document.createElement('span');
|
|
3420
|
+
if (html) {
|
|
3421
|
+
span.innerHTML = html;
|
|
3422
|
+
} else {
|
|
3423
|
+
span.textContent = text;
|
|
3424
|
+
}
|
|
3425
|
+
span.style.cssText = `
|
|
3426
|
+
color: ${bottomLineConfig.textColor || config.textColor || '#ffffff'};
|
|
3427
|
+
font-size: ${bottomLineFontSize}px;
|
|
3428
|
+
font-weight: 500;
|
|
3429
|
+
margin-right: 100px;
|
|
3430
|
+
display: inline-flex;
|
|
3431
|
+
align-items: center;
|
|
3432
|
+
`;
|
|
3433
|
+
track.appendChild(span);
|
|
3434
|
+
}
|
|
3435
|
+
};
|
|
3436
|
+
|
|
3437
|
+
if (firstItem) {
|
|
3438
|
+
renderDetail(firstItem.text, firstItem.html);
|
|
3439
|
+
}
|
|
3440
|
+
|
|
3441
|
+
detailLine.appendChild(track);
|
|
3442
|
+
body.appendChild(detailLine);
|
|
3443
|
+
|
|
3444
|
+
return body;
|
|
3445
|
+
}
|
|
3446
|
+
|
|
3447
|
+
/**
|
|
3448
|
+
* Create progress bar for item cycling
|
|
3449
|
+
*/
|
|
3450
|
+
private createProgressBar(itemCycling: ItemCyclingConfig): HTMLDivElement {
|
|
3451
|
+
const progressBar = document.createElement('div');
|
|
3452
|
+
progressBar.className = 'uvf-ticker-progress';
|
|
3453
|
+
this.tickerProgressBar = progressBar;
|
|
3454
|
+
|
|
3455
|
+
const height = itemCycling.progressHeight || 3;
|
|
3456
|
+
progressBar.style.cssText = `
|
|
3457
|
+
height: ${height}px;
|
|
3458
|
+
background: rgba(0,0,0,0.3);
|
|
3459
|
+
position: relative;
|
|
3460
|
+
overflow: hidden;
|
|
3461
|
+
`;
|
|
3462
|
+
|
|
3463
|
+
const progressFill = document.createElement('div');
|
|
3464
|
+
progressFill.className = 'uvf-progress-fill';
|
|
3465
|
+
this.tickerProgressFill = progressFill;
|
|
3466
|
+
|
|
3467
|
+
progressFill.style.cssText = `
|
|
3468
|
+
height: 100%;
|
|
3469
|
+
width: 0%;
|
|
3470
|
+
background: ${itemCycling.progressColor || '#ffffff'};
|
|
3471
|
+
transition: width 0.1s linear;
|
|
3472
|
+
`;
|
|
3473
|
+
|
|
3474
|
+
progressBar.appendChild(progressFill);
|
|
3475
|
+
return progressBar;
|
|
3476
|
+
}
|
|
3477
|
+
|
|
3478
|
+
/**
|
|
3479
|
+
* Create intro overlay for "JUST IN" / "BREAKING" animations
|
|
3480
|
+
*/
|
|
3481
|
+
private createIntroOverlay(broadcastStyle: BroadcastStyleConfig): HTMLDivElement {
|
|
3482
|
+
const overlay = document.createElement('div');
|
|
3483
|
+
overlay.className = 'uvf-ticker-intro-overlay';
|
|
3484
|
+
overlay.style.cssText = `
|
|
3485
|
+
position: absolute;
|
|
3486
|
+
top: 0;
|
|
3487
|
+
left: 0;
|
|
3488
|
+
right: 0;
|
|
3489
|
+
bottom: 0;
|
|
3490
|
+
display: none;
|
|
3491
|
+
align-items: center;
|
|
3492
|
+
justify-content: center;
|
|
3493
|
+
background: rgba(200, 0, 0, 0.95);
|
|
3494
|
+
z-index: 10;
|
|
3495
|
+
`;
|
|
3496
|
+
|
|
3497
|
+
const introText = document.createElement('span');
|
|
3498
|
+
introText.className = 'uvf-intro-text';
|
|
3499
|
+
introText.style.cssText = `
|
|
3500
|
+
color: #ffffff;
|
|
3501
|
+
font-size: 24px;
|
|
3502
|
+
font-weight: 900;
|
|
3503
|
+
text-transform: uppercase;
|
|
3504
|
+
letter-spacing: 3px;
|
|
3505
|
+
text-shadow: 2px 2px 4px rgba(0,0,0,0.5);
|
|
3506
|
+
`;
|
|
3507
|
+
introText.textContent = 'BREAKING';
|
|
3508
|
+
|
|
3509
|
+
overlay.appendChild(introText);
|
|
3510
|
+
return overlay;
|
|
3511
|
+
}
|
|
3512
|
+
|
|
3513
|
+
/**
|
|
3514
|
+
* Play intro animation for a news item
|
|
3515
|
+
*/
|
|
3516
|
+
private playIntroAnimation(item: FlashNewsTickerItem): Promise<void> {
|
|
3517
|
+
return new Promise((resolve) => {
|
|
3518
|
+
if (!this.tickerIntroOverlay || !item.showIntro) {
|
|
3519
|
+
resolve();
|
|
3520
|
+
return;
|
|
3521
|
+
}
|
|
3522
|
+
|
|
3523
|
+
const broadcastStyle = this.tickerConfig?.broadcastStyle || {};
|
|
3524
|
+
const animation = item.introAnimation || broadcastStyle.defaultIntroAnimation || 'slide-in';
|
|
3525
|
+
const duration = item.introDuration || broadcastStyle.defaultIntroDuration || 2000;
|
|
3526
|
+
const introText = item.introText || 'BREAKING';
|
|
3527
|
+
|
|
3528
|
+
// Update intro text
|
|
3529
|
+
const textElement = this.tickerIntroOverlay.querySelector('.uvf-intro-text') as HTMLSpanElement;
|
|
3530
|
+
if (textElement) {
|
|
3531
|
+
textElement.textContent = introText;
|
|
3532
|
+
}
|
|
3533
|
+
|
|
3534
|
+
// Apply animation
|
|
3535
|
+
this.tickerIntroOverlay.style.display = 'flex';
|
|
3536
|
+
this.tickerIntroOverlay.style.animation = `intro-${animation} ${duration}ms ease-out forwards`;
|
|
3537
|
+
|
|
3538
|
+
// Hide after animation completes
|
|
3539
|
+
setTimeout(() => {
|
|
3540
|
+
if (this.tickerIntroOverlay) {
|
|
3541
|
+
this.tickerIntroOverlay.style.display = 'none';
|
|
3542
|
+
this.tickerIntroOverlay.style.animation = '';
|
|
3543
|
+
}
|
|
3544
|
+
resolve();
|
|
3545
|
+
}, duration);
|
|
3546
|
+
});
|
|
3547
|
+
}
|
|
3548
|
+
|
|
3549
|
+
/**
|
|
3550
|
+
* Start automatic item cycling
|
|
3551
|
+
*/
|
|
3552
|
+
private startItemCycling(): void {
|
|
3553
|
+
if (!this.tickerConfig?.items || this.tickerConfig.items.length <= 1) return;
|
|
3554
|
+
|
|
3555
|
+
const itemCycling = this.tickerConfig.itemCycling || {};
|
|
3556
|
+
if (!itemCycling.enabled) return;
|
|
3557
|
+
|
|
3558
|
+
const currentItem = this.tickerConfig.items[this.tickerCurrentItemIndex];
|
|
3559
|
+
const duration = (currentItem.duration || itemCycling.defaultDuration || 10) * 1000;
|
|
3560
|
+
|
|
3561
|
+
// Start progress animation
|
|
3562
|
+
this.animateProgress(duration);
|
|
3563
|
+
|
|
3564
|
+
// Set timer for next item
|
|
3565
|
+
this.tickerCycleTimer = window.setTimeout(() => {
|
|
3566
|
+
this.transitionToNextItem();
|
|
3567
|
+
}, duration);
|
|
3568
|
+
}
|
|
3569
|
+
|
|
3570
|
+
/**
|
|
3571
|
+
* Stop item cycling
|
|
3572
|
+
*/
|
|
3573
|
+
private stopItemCycling(): void {
|
|
3574
|
+
if (this.tickerCycleTimer) {
|
|
3575
|
+
clearTimeout(this.tickerCycleTimer);
|
|
3576
|
+
this.tickerCycleTimer = null;
|
|
3577
|
+
}
|
|
3578
|
+
}
|
|
3579
|
+
|
|
3580
|
+
/**
|
|
3581
|
+
* Pause item cycling (for hover)
|
|
3582
|
+
*/
|
|
3583
|
+
private pauseItemCycling(): void {
|
|
3584
|
+
if (!this.tickerCycleTimer || this.tickerIsPaused) return;
|
|
3585
|
+
|
|
3586
|
+
this.tickerIsPaused = true;
|
|
3587
|
+
this.tickerPauseStartTime = Date.now();
|
|
3588
|
+
|
|
3589
|
+
// Calculate remaining time
|
|
3590
|
+
const itemCycling = this.tickerConfig?.itemCycling || {};
|
|
3591
|
+
const currentItem = this.tickerConfig?.items?.[this.tickerCurrentItemIndex];
|
|
3592
|
+
const totalDuration = (currentItem?.duration || itemCycling.defaultDuration || 10) * 1000;
|
|
3593
|
+
|
|
3594
|
+
// Stop the timer
|
|
3595
|
+
clearTimeout(this.tickerCycleTimer);
|
|
3596
|
+
this.tickerCycleTimer = null;
|
|
3597
|
+
|
|
3598
|
+
// Pause progress bar animation
|
|
3599
|
+
if (this.tickerProgressFill) {
|
|
3600
|
+
const computedWidth = window.getComputedStyle(this.tickerProgressFill).width;
|
|
3601
|
+
this.tickerProgressFill.style.transition = 'none';
|
|
3602
|
+
this.tickerProgressFill.style.width = computedWidth;
|
|
3603
|
+
}
|
|
3604
|
+
}
|
|
3605
|
+
|
|
3606
|
+
/**
|
|
3607
|
+
* Resume item cycling (after hover)
|
|
3608
|
+
*/
|
|
3609
|
+
private resumeItemCycling(): void {
|
|
3610
|
+
if (!this.tickerIsPaused) return;
|
|
3611
|
+
|
|
3612
|
+
this.tickerIsPaused = false;
|
|
3613
|
+
|
|
3614
|
+
// Calculate remaining time based on progress
|
|
3615
|
+
const itemCycling = this.tickerConfig?.itemCycling || {};
|
|
3616
|
+
const currentItem = this.tickerConfig?.items?.[this.tickerCurrentItemIndex];
|
|
3617
|
+
const totalDuration = (currentItem?.duration || itemCycling.defaultDuration || 10) * 1000;
|
|
3618
|
+
|
|
3619
|
+
// Get current progress percentage
|
|
3620
|
+
let remainingTime = totalDuration;
|
|
3621
|
+
if (this.tickerProgressFill) {
|
|
3622
|
+
const currentWidth = parseFloat(this.tickerProgressFill.style.width) || 0;
|
|
3623
|
+
remainingTime = totalDuration * (1 - currentWidth / 100);
|
|
3624
|
+
}
|
|
3625
|
+
|
|
3626
|
+
// Resume progress animation
|
|
3627
|
+
this.animateProgress(remainingTime);
|
|
3628
|
+
|
|
3629
|
+
// Set timer for remaining time
|
|
3630
|
+
this.tickerCycleTimer = window.setTimeout(() => {
|
|
3631
|
+
this.transitionToNextItem();
|
|
3632
|
+
}, remainingTime);
|
|
3633
|
+
}
|
|
3634
|
+
|
|
3635
|
+
/**
|
|
3636
|
+
* Animate progress bar
|
|
3637
|
+
*/
|
|
3638
|
+
private animateProgress(duration: number): void {
|
|
3639
|
+
if (!this.tickerProgressFill) return;
|
|
3640
|
+
|
|
3641
|
+
// Reset to start
|
|
3642
|
+
this.tickerProgressFill.style.transition = 'none';
|
|
3643
|
+
this.tickerProgressFill.style.width = '0%';
|
|
3644
|
+
|
|
3645
|
+
// Force reflow
|
|
3646
|
+
this.tickerProgressFill.offsetHeight;
|
|
3647
|
+
|
|
3648
|
+
// Animate to 100%
|
|
3649
|
+
this.tickerProgressFill.style.transition = `width ${duration}ms linear`;
|
|
3650
|
+
this.tickerProgressFill.style.width = '100%';
|
|
3651
|
+
}
|
|
3652
|
+
|
|
3653
|
+
/**
|
|
3654
|
+
* Transition to the next news item
|
|
3655
|
+
*/
|
|
3656
|
+
private async transitionToNextItem(): Promise<void> {
|
|
3657
|
+
if (!this.tickerConfig?.items || this.tickerConfig.items.length <= 1) return;
|
|
3658
|
+
|
|
3659
|
+
const itemCycling = this.tickerConfig.itemCycling || {};
|
|
3660
|
+
const transitionType = itemCycling.transitionType || 'fade';
|
|
3661
|
+
const transitionDuration = itemCycling.transitionDuration || 500;
|
|
3662
|
+
|
|
3663
|
+
// Move to next item
|
|
3664
|
+
this.tickerCurrentItemIndex = (this.tickerCurrentItemIndex + 1) % this.tickerConfig.items.length;
|
|
3665
|
+
const nextItem = this.tickerConfig.items[this.tickerCurrentItemIndex];
|
|
3666
|
+
|
|
3667
|
+
// Play intro animation if configured
|
|
3668
|
+
if (nextItem.showIntro) {
|
|
3669
|
+
await this.playIntroAnimation(nextItem);
|
|
3670
|
+
}
|
|
3671
|
+
|
|
3672
|
+
// Apply transition animation
|
|
3673
|
+
await this.applyItemTransition(transitionType, transitionDuration, nextItem);
|
|
3674
|
+
|
|
3675
|
+
// Continue cycling
|
|
3676
|
+
this.startItemCycling();
|
|
3677
|
+
}
|
|
3678
|
+
|
|
3679
|
+
/**
|
|
3680
|
+
* Apply transition animation between items
|
|
3681
|
+
*/
|
|
3682
|
+
private applyItemTransition(
|
|
3683
|
+
transitionType: string,
|
|
3684
|
+
duration: number,
|
|
3685
|
+
item: FlashNewsTickerItem
|
|
3686
|
+
): Promise<void> {
|
|
3687
|
+
return new Promise((resolve) => {
|
|
3688
|
+
const headline = this.tickerHeadlineElement?.querySelector('.uvf-ticker-headline-text') as HTMLSpanElement;
|
|
3689
|
+
const detailTrack = this.tickerDetailElement?.querySelector('.uvf-ticker-track') as HTMLDivElement;
|
|
3690
|
+
|
|
3691
|
+
if (!headline || !detailTrack) {
|
|
3692
|
+
this.updateItemContent(item);
|
|
3693
|
+
resolve();
|
|
3694
|
+
return;
|
|
3695
|
+
}
|
|
3696
|
+
|
|
3697
|
+
if (transitionType === 'none') {
|
|
3698
|
+
this.updateItemContent(item);
|
|
3699
|
+
resolve();
|
|
3700
|
+
return;
|
|
3701
|
+
}
|
|
3702
|
+
|
|
3703
|
+
// Apply fade or slide out animation
|
|
3704
|
+
const outAnimation = transitionType === 'slide' ? 'item-slide-out' : 'item-fade-out';
|
|
3705
|
+
const inAnimation = transitionType === 'slide' ? 'item-slide-in' : 'item-fade-in';
|
|
3706
|
+
|
|
3707
|
+
headline.style.animation = `${outAnimation} ${duration / 2}ms ease-out forwards`;
|
|
3708
|
+
|
|
3709
|
+
setTimeout(() => {
|
|
3710
|
+
// Update content
|
|
3711
|
+
this.updateItemContent(item);
|
|
3712
|
+
|
|
3713
|
+
// Apply fade or slide in animation
|
|
3714
|
+
headline.style.animation = `${inAnimation} ${duration / 2}ms ease-out forwards`;
|
|
3715
|
+
|
|
3716
|
+
setTimeout(() => {
|
|
3717
|
+
headline.style.animation = '';
|
|
3718
|
+
resolve();
|
|
3719
|
+
}, duration / 2);
|
|
3720
|
+
}, duration / 2);
|
|
3721
|
+
});
|
|
3722
|
+
}
|
|
3723
|
+
|
|
3724
|
+
/**
|
|
3725
|
+
* Update headline and detail content for current item
|
|
3726
|
+
*/
|
|
3727
|
+
private updateItemContent(item: FlashNewsTickerItem): void {
|
|
3728
|
+
// Update headline
|
|
3729
|
+
const headline = this.tickerHeadlineElement?.querySelector('.uvf-ticker-headline-text') as HTMLSpanElement;
|
|
3730
|
+
if (headline) {
|
|
3731
|
+
if (item.headlineHtml) {
|
|
3732
|
+
headline.innerHTML = item.headlineHtml;
|
|
3733
|
+
} else if (item.headline) {
|
|
3734
|
+
headline.textContent = item.headline;
|
|
3735
|
+
} else {
|
|
3736
|
+
headline.textContent = item.text;
|
|
3737
|
+
}
|
|
3738
|
+
}
|
|
3739
|
+
|
|
3740
|
+
// Update detail track
|
|
3741
|
+
const detailTrack = this.tickerDetailElement?.querySelector('.uvf-ticker-track') as HTMLDivElement;
|
|
3742
|
+
if (detailTrack) {
|
|
3743
|
+
// Clear existing content
|
|
3744
|
+
detailTrack.innerHTML = '';
|
|
3745
|
+
|
|
3746
|
+
// Get style config
|
|
3747
|
+
const bottomLineConfig = this.tickerConfig?.broadcastStyle?.twoLineDisplay?.bottomLine || {};
|
|
3748
|
+
const bottomLineFontSize = bottomLineConfig.fontSize || 14;
|
|
3749
|
+
|
|
3750
|
+
// Re-render detail text
|
|
3751
|
+
for (let i = 0; i < 10; i++) {
|
|
3752
|
+
const span = document.createElement('span');
|
|
3753
|
+
if (item.html) {
|
|
3754
|
+
span.innerHTML = item.html;
|
|
3755
|
+
} else {
|
|
3756
|
+
span.textContent = item.text;
|
|
3757
|
+
}
|
|
3758
|
+
span.style.cssText = `
|
|
3759
|
+
color: ${bottomLineConfig.textColor || this.tickerConfig?.textColor || '#ffffff'};
|
|
3760
|
+
font-size: ${bottomLineFontSize}px;
|
|
3761
|
+
font-weight: 500;
|
|
3762
|
+
margin-right: 100px;
|
|
3763
|
+
display: inline-flex;
|
|
3764
|
+
align-items: center;
|
|
3765
|
+
`;
|
|
3766
|
+
detailTrack.appendChild(span);
|
|
3767
|
+
}
|
|
3768
|
+
|
|
3769
|
+
// Update scroll speed
|
|
3770
|
+
const bottomLineSpeed = bottomLineConfig.speed || 80;
|
|
3771
|
+
const textWidth = item.text.length * 10;
|
|
3772
|
+
const duration = Math.max(textWidth / bottomLineSpeed, 10);
|
|
3773
|
+
detailTrack.style.animation = `ticker-scroll ${duration}s linear infinite`;
|
|
3774
|
+
}
|
|
3775
|
+
}
|
|
3776
|
+
|
|
2954
3777
|
setAutoQuality(enabled: boolean): void {
|
|
2955
3778
|
this.autoQuality = enabled;
|
|
2956
3779
|
|