tuikit-atomicx-vue3 3.3.0 → 3.3.2-beta.1
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/components/ChatSetting/GroupChatSetting/GroupActions/GroupActions.js +1 -4
- package/dist/components/ChatSetting/GroupChatSetting/GroupChatSetting.js +1 -2
- package/dist/components/ChatSetting/GroupChatSetting/GroupManagement/GroupManagement.js +1 -2
- package/dist/components/ContactList/ContactInfo/GroupInfo/GroupInfo.js +1 -2
- package/dist/components/ConversationList/ConversationCreate/ConversationCreate.js +1 -2
- package/dist/components/ConversationList/ConversationSearch/ConversationSearch.js +0 -1
- package/dist/components/LiveAudienceList/LiveAudienceListH5.js +1 -1
- package/dist/components/LiveCoreView/PlayerControl/AudioControl.js +251 -0
- package/dist/components/LiveCoreView/PlayerControl/AudioControl.vue.d.ts +38 -0
- package/dist/components/LiveCoreView/PlayerControl/PlayerControl.js +271 -0
- package/dist/components/LiveCoreView/PlayerControl/PlayerControl.vue.d.ts +2 -0
- package/dist/components/LiveCoreView/PlayerControl/PlayerControlState.d.ts +27 -0
- package/dist/components/LiveCoreView/PlayerControl/PlayerControlState.js +407 -0
- package/dist/components/LiveCoreView/PlayerControl/index.d.ts +3 -0
- package/dist/components/LiveCoreView/PlayerControl/index.js +8 -0
- package/dist/components/LiveCoreView/PlayerControl/utils/deviceDetection.d.ts +85 -0
- package/dist/components/LiveCoreView/PlayerControl/utils/deviceDetection.js +129 -0
- package/dist/components/LiveCoreView/PlayerControl/utils/domHelpers.d.ts +75 -0
- package/dist/components/LiveCoreView/PlayerControl/utils/domHelpers.js +120 -0
- package/dist/components/LiveCoreView/PlayerControl/utils/fullscreenManager.d.ts +120 -0
- package/dist/components/LiveCoreView/PlayerControl/utils/fullscreenManager.js +311 -0
- package/dist/components/LiveCoreView/i18n/en-US/index.d.ts +9 -0
- package/dist/components/LiveCoreView/i18n/en-US/index.js +10 -1
- package/dist/components/LiveCoreView/i18n/zh-CN/index.d.ts +9 -0
- package/dist/components/LiveCoreView/i18n/zh-CN/index.js +10 -1
- package/dist/components/LiveCoreView/index.js +23 -3
- package/dist/styles/index.css +321 -49
- package/package.json +3 -3
- package/src/components/ChatSetting/GroupChatSetting/GroupActions/GroupActions.vue +0 -3
- package/src/components/ChatSetting/GroupChatSetting/GroupChatSetting.vue +0 -1
- package/src/components/ChatSetting/GroupChatSetting/GroupManagement/GroupManagement.vue +0 -1
- package/src/components/ContactList/ContactInfo/GroupInfo/GroupInfo.vue +0 -1
- package/src/components/ConversationList/ConversationCreate/ConversationCreate.vue +0 -1
- package/src/components/ConversationList/ConversationSearch/ConversationSearch.vue +0 -1
- package/src/components/LiveAudienceList/LiveAudienceListH5.vue +2 -2
- package/src/components/LiveCoreView/PlayerControl/AudioControl.vue +456 -0
- package/src/components/LiveCoreView/PlayerControl/PlayerControl.module.scss +52 -0
- package/src/components/LiveCoreView/PlayerControl/PlayerControl.vue +429 -0
- package/src/components/LiveCoreView/PlayerControl/PlayerControlState.ts +599 -0
- package/src/components/LiveCoreView/PlayerControl/index.ts +3 -0
- package/src/components/LiveCoreView/PlayerControl/utils/deviceDetection.ts +234 -0
- package/src/components/LiveCoreView/PlayerControl/utils/domHelpers.ts +145 -0
- package/src/components/LiveCoreView/PlayerControl/utils/fullscreenManager.ts +417 -0
- package/src/components/LiveCoreView/i18n/en-US/index.ts +9 -0
- package/src/components/LiveCoreView/i18n/zh-CN/index.ts +9 -0
- package/src/components/LiveCoreView/index.vue +14 -3
|
@@ -77,7 +77,7 @@ $text-color2: var(--text-color-secondary);
|
|
|
77
77
|
|
|
78
78
|
.viewers-panel {
|
|
79
79
|
box-sizing: border-box;
|
|
80
|
-
height:
|
|
80
|
+
height: 100%;
|
|
81
81
|
display: flex;
|
|
82
82
|
flex-direction: column;
|
|
83
83
|
color: $text-color1;
|
|
@@ -147,7 +147,7 @@ $text-color2: var(--text-color-secondary);
|
|
|
147
147
|
&:hover {
|
|
148
148
|
background-color: var(--uikit-color-gray-3);
|
|
149
149
|
}
|
|
150
|
-
|
|
150
|
+
|
|
151
151
|
.viewer-info {
|
|
152
152
|
display: flex;
|
|
153
153
|
align-items: center;
|
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="audio-control" :style="iconSizeStyle" @mouseenter="handleMouseEnter" @mouseleave="handleMouseLeave">
|
|
3
|
+
<span class="control-btn volume-btn" :title="isMuted ? t('Unmute') : t('Mute')" @click="handleVolumeIconClick">
|
|
4
|
+
<IconSpeakerOff size="20" v-if="isMuted" />
|
|
5
|
+
<IconSpeakerOn size="20" v-else />
|
|
6
|
+
</span>
|
|
7
|
+
<div v-show="isVolumeSliderVisible" class="volume-slider-container">
|
|
8
|
+
<div
|
|
9
|
+
class="volume-slider-wrapper"
|
|
10
|
+
@mouseenter="handleVolumeSliderMouseEnter"
|
|
11
|
+
@mouseleave="handleVolumeSliderMouseLeave"
|
|
12
|
+
>
|
|
13
|
+
<div class="volume-slider-wrapper-inner">
|
|
14
|
+
<div
|
|
15
|
+
ref="volumeSliderElement"
|
|
16
|
+
class="custom-volume-slider"
|
|
17
|
+
@mousedown="handleSliderMouseDown"
|
|
18
|
+
@touchstart="handleSliderTouchStart"
|
|
19
|
+
@click="handleVolumeSliderAreaClick"
|
|
20
|
+
>
|
|
21
|
+
<div class="slider-track">
|
|
22
|
+
<div
|
|
23
|
+
class="slider-progress"
|
|
24
|
+
:style="{ height: `${volumePercentage}%` }"
|
|
25
|
+
></div>
|
|
26
|
+
<div
|
|
27
|
+
class="slider-thumb"
|
|
28
|
+
:class="{ 'no-transition': isDragging }"
|
|
29
|
+
:style="{ bottom: `${volumePercentage}%` }"
|
|
30
|
+
></div>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
<div class="volume-value">{{ volumePercentage }}</div>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
</template>
|
|
39
|
+
|
|
40
|
+
<script setup lang="ts">
|
|
41
|
+
import { computed, ref, onUnmounted } from 'vue';
|
|
42
|
+
import { IconSpeakerOn, IconSpeakerOff, useUIKit } from '@tencentcloud/uikit-base-component-vue3';
|
|
43
|
+
import { isMobile } from '../../../utils';
|
|
44
|
+
|
|
45
|
+
interface AudioControlEmits {
|
|
46
|
+
(e: 'volume-change', value: number): void;
|
|
47
|
+
(e: 'muted-change', value: boolean): void;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
interface AudioControlProps {
|
|
51
|
+
iconSize?: number;
|
|
52
|
+
enableVolumeControl?: boolean;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const props = withDefaults(defineProps<AudioControlProps>(), {
|
|
56
|
+
iconSize: 20,
|
|
57
|
+
enableVolumeControl: true,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const emit = defineEmits<AudioControlEmits>();
|
|
61
|
+
|
|
62
|
+
const { t } = useUIKit();
|
|
63
|
+
|
|
64
|
+
// Volume state - merged into single object
|
|
65
|
+
const volumeState = ref({
|
|
66
|
+
current: 1,
|
|
67
|
+
previous: 1
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const isMuted = ref(false);
|
|
71
|
+
const isVolumeSliderVisible = ref(false);
|
|
72
|
+
const isDragging = ref(false);
|
|
73
|
+
const volumeSliderElement = ref<HTMLElement>();
|
|
74
|
+
const volumeSliderAutoHideTimer = ref<number | null>(null);
|
|
75
|
+
|
|
76
|
+
// Auto-hide delay for different platforms
|
|
77
|
+
const AUTO_HIDE_DELAY = {
|
|
78
|
+
PC: 500, // 0.5 seconds for PC
|
|
79
|
+
MOBILE: 3000 // 3 seconds for mobile
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// Simplified computed property - directly use isVolumeSliderVisible
|
|
83
|
+
const volumePercentage = computed(() => {
|
|
84
|
+
if (props.enableVolumeControl === false) {
|
|
85
|
+
return isMuted.value ? 0 : 100;
|
|
86
|
+
}
|
|
87
|
+
return Math.round(volumeState.value.current * 100);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const iconSizeStyle = computed(() => ({
|
|
91
|
+
width: `${props.iconSize || 20}px`,
|
|
92
|
+
height: `${props.iconSize || 20}px`,
|
|
93
|
+
}));
|
|
94
|
+
|
|
95
|
+
const updateVolume = (newVolume: number) => {
|
|
96
|
+
volumeState.value.current = newVolume;
|
|
97
|
+
volumeState.value.previous = newVolume;
|
|
98
|
+
isMuted.value = newVolume === 0;
|
|
99
|
+
emit('volume-change', newVolume);
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const toggleMute = () => {
|
|
103
|
+
if (isMuted.value) {
|
|
104
|
+
// Unmute
|
|
105
|
+
isMuted.value = false;
|
|
106
|
+
volumeState.value.current = volumeState.value.previous || 0.2;
|
|
107
|
+
emit('muted-change', false);
|
|
108
|
+
} else {
|
|
109
|
+
// Mute
|
|
110
|
+
isMuted.value = true;
|
|
111
|
+
volumeState.value.previous = volumeState.value.current;
|
|
112
|
+
volumeState.value.current = 0;
|
|
113
|
+
emit('muted-change', true);
|
|
114
|
+
}
|
|
115
|
+
updateVolume(volumeState.value.current);
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const handleVolumeIconClick = () => {
|
|
119
|
+
if (props.enableVolumeControl === false) {
|
|
120
|
+
// When volume control is disabled, handle mute/unmute on all platforms
|
|
121
|
+
toggleMute();
|
|
122
|
+
} else {
|
|
123
|
+
// When volume control is enabled
|
|
124
|
+
if (isMobile) {
|
|
125
|
+
// On mobile: toggle volume slider visibility
|
|
126
|
+
isVolumeSliderVisible.value = !isVolumeSliderVisible.value;
|
|
127
|
+
|
|
128
|
+
// Start auto-hide timer when showing volume slider
|
|
129
|
+
if (isVolumeSliderVisible.value) {
|
|
130
|
+
startVolumeSliderAutoHideTimer();
|
|
131
|
+
} else {
|
|
132
|
+
stopVolumeSliderAutoHideTimer();
|
|
133
|
+
}
|
|
134
|
+
} else {
|
|
135
|
+
// On PC: handle mute/unmute, volume slider will show on mouse hover
|
|
136
|
+
toggleMute();
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const startVolumeSliderAutoHideTimer = () => {
|
|
142
|
+
stopVolumeSliderAutoHideTimer();
|
|
143
|
+
const delay = isMobile ? AUTO_HIDE_DELAY.MOBILE : AUTO_HIDE_DELAY.PC;
|
|
144
|
+
volumeSliderAutoHideTimer.value = window.setTimeout(() => {
|
|
145
|
+
if (props.enableVolumeControl) {
|
|
146
|
+
isVolumeSliderVisible.value = false;
|
|
147
|
+
}
|
|
148
|
+
}, delay);
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const stopVolumeSliderAutoHideTimer = () => {
|
|
152
|
+
if (volumeSliderAutoHideTimer.value) {
|
|
153
|
+
clearTimeout(volumeSliderAutoHideTimer.value);
|
|
154
|
+
volumeSliderAutoHideTimer.value = null;
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const handleMouseEnter = () => {
|
|
159
|
+
if (props.enableVolumeControl === false) return;
|
|
160
|
+
|
|
161
|
+
// Only handle mouse events on PC
|
|
162
|
+
if (isMobile) return;
|
|
163
|
+
|
|
164
|
+
// On PC, show volume slider and start auto-hide timer
|
|
165
|
+
isVolumeSliderVisible.value = true;
|
|
166
|
+
startVolumeSliderAutoHideTimer();
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const handleMouseLeave = () => {
|
|
170
|
+
if (props.enableVolumeControl === false) return;
|
|
171
|
+
|
|
172
|
+
// Only handle mouse events on PC
|
|
173
|
+
if (isMobile) return;
|
|
174
|
+
|
|
175
|
+
// On PC, start auto-hide timer when mouse leaves icon area
|
|
176
|
+
// But don't start if currently dragging
|
|
177
|
+
if (!isDragging.value) {
|
|
178
|
+
startVolumeSliderAutoHideTimer();
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const calculateVolumeFromPosition = (clientY: number, target: HTMLElement): number => {
|
|
183
|
+
const rect = target.getBoundingClientRect();
|
|
184
|
+
const clickY = clientY - rect.top;
|
|
185
|
+
const height = rect.height;
|
|
186
|
+
return Math.max(0, Math.min(1, 1 - (clickY / height)));
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
const addGlobalEventListeners = () => {
|
|
190
|
+
document.addEventListener('mousemove', handleSliderMove);
|
|
191
|
+
document.addEventListener('mouseup', handleSliderEnd);
|
|
192
|
+
document.addEventListener('touchmove', handleSliderMove);
|
|
193
|
+
document.addEventListener('touchend', handleSliderEnd);
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
const removeGlobalEventListeners = () => {
|
|
197
|
+
document.removeEventListener('mousemove', handleSliderMove);
|
|
198
|
+
document.removeEventListener('mouseup', handleSliderEnd);
|
|
199
|
+
document.removeEventListener('touchmove', handleSliderMove);
|
|
200
|
+
document.removeEventListener('touchend', handleSliderEnd);
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
const startDragging = () => {
|
|
204
|
+
isDragging.value = true;
|
|
205
|
+
|
|
206
|
+
// Stop auto-hide timer when dragging starts
|
|
207
|
+
if (props.enableVolumeControl) {
|
|
208
|
+
stopVolumeSliderAutoHideTimer();
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
addGlobalEventListeners();
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
const handleSliderMove = (event: MouseEvent | TouchEvent) => {
|
|
215
|
+
if (!isDragging.value) return;
|
|
216
|
+
|
|
217
|
+
event.preventDefault();
|
|
218
|
+
|
|
219
|
+
let clientY: number;
|
|
220
|
+
if (event instanceof MouseEvent) {
|
|
221
|
+
clientY = event.clientY;
|
|
222
|
+
} else {
|
|
223
|
+
clientY = event.touches[0].clientY;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const volumeValue = calculateVolumeFromPosition(clientY, volumeSliderElement.value as HTMLElement);
|
|
227
|
+
updateVolume(volumeValue);
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
const handleSliderEnd = () => {
|
|
231
|
+
isDragging.value = false;
|
|
232
|
+
|
|
233
|
+
// Restart auto-hide timer when dragging ends
|
|
234
|
+
if (props.enableVolumeControl && isVolumeSliderVisible.value) {
|
|
235
|
+
startVolumeSliderAutoHideTimer();
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
removeGlobalEventListeners();
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
const handleSliderMouseDown = (event: MouseEvent) => {
|
|
242
|
+
if (props.enableVolumeControl === false) return;
|
|
243
|
+
|
|
244
|
+
startDragging();
|
|
245
|
+
event.preventDefault();
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
const handleSliderTouchStart = (event: TouchEvent) => {
|
|
249
|
+
if (props.enableVolumeControl === false) return;
|
|
250
|
+
|
|
251
|
+
startDragging();
|
|
252
|
+
event.preventDefault();
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
const handleVolumeSliderAreaClick = () => {
|
|
256
|
+
if (props.enableVolumeControl === false) return;
|
|
257
|
+
|
|
258
|
+
if (isMobile) {
|
|
259
|
+
// On mobile, toggle volume slider visibility
|
|
260
|
+
isVolumeSliderVisible.value = !isVolumeSliderVisible.value;
|
|
261
|
+
|
|
262
|
+
// Start auto-hide timer when showing volume slider
|
|
263
|
+
if (isVolumeSliderVisible.value) {
|
|
264
|
+
startVolumeSliderAutoHideTimer();
|
|
265
|
+
} else {
|
|
266
|
+
stopVolumeSliderAutoHideTimer();
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
const handleVolumeSliderMouseEnter = () => {
|
|
272
|
+
if (props.enableVolumeControl === false) return;
|
|
273
|
+
|
|
274
|
+
// Only handle mouse events on PC
|
|
275
|
+
if (isMobile) return;
|
|
276
|
+
|
|
277
|
+
// On PC, stop auto-hide timer when mouse enters slider area
|
|
278
|
+
stopVolumeSliderAutoHideTimer();
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
const handleVolumeSliderMouseLeave = () => {
|
|
282
|
+
if (props.enableVolumeControl === false) return;
|
|
283
|
+
|
|
284
|
+
// Only handle mouse events on PC
|
|
285
|
+
if (isMobile) return;
|
|
286
|
+
|
|
287
|
+
// On PC, start auto-hide timer when mouse leaves slider area
|
|
288
|
+
// But don't start if currently dragging
|
|
289
|
+
if (!isDragging.value) {
|
|
290
|
+
startVolumeSliderAutoHideTimer();
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
onUnmounted(() => {
|
|
295
|
+
removeGlobalEventListeners();
|
|
296
|
+
|
|
297
|
+
// Clean up timers
|
|
298
|
+
if (volumeSliderAutoHideTimer.value) {
|
|
299
|
+
clearTimeout(volumeSliderAutoHideTimer.value);
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
</script>
|
|
303
|
+
|
|
304
|
+
<style scoped lang="scss">
|
|
305
|
+
.audio-control {
|
|
306
|
+
--volume-control-primary: rgb(255, 255, 255);
|
|
307
|
+
--volume-control-primary-hover: rgba(255, 255, 255, 0.1);
|
|
308
|
+
--volume-control-background: rgba(0, 0, 0, 0.8);
|
|
309
|
+
--volume-control-background-light: rgba(0, 0, 0, 0.5);
|
|
310
|
+
--volume-control-border: rgba(255, 255, 255, 0.1);
|
|
311
|
+
--volume-control-shadow: rgba(0, 0, 0, 0.2);
|
|
312
|
+
|
|
313
|
+
position: relative;
|
|
314
|
+
display: flex;
|
|
315
|
+
align-items: center;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
.volume-btn {
|
|
319
|
+
width: 100%;
|
|
320
|
+
height: 100%;
|
|
321
|
+
flex-shrink: 0;
|
|
322
|
+
cursor: pointer;
|
|
323
|
+
transition: background-color 0.2s ease;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
.volume-slider-container {
|
|
327
|
+
position: absolute;
|
|
328
|
+
bottom: 100%;
|
|
329
|
+
left: 50%;
|
|
330
|
+
transform: translateX(-50%);
|
|
331
|
+
margin-bottom: 12px;
|
|
332
|
+
z-index: 100;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.volume-slider-wrapper {
|
|
336
|
+
position: relative;
|
|
337
|
+
display: flex;
|
|
338
|
+
flex-direction: column;
|
|
339
|
+
align-items: center;
|
|
340
|
+
gap: 8px;
|
|
341
|
+
background: var(--volume-control-background);
|
|
342
|
+
padding: 12px 8px;
|
|
343
|
+
border-radius: 8px;
|
|
344
|
+
backdrop-filter: blur(10px);
|
|
345
|
+
border: 1px solid var(--volume-control-border);
|
|
346
|
+
cursor: pointer;
|
|
347
|
+
user-select: none;
|
|
348
|
+
-webkit-user-select: none;
|
|
349
|
+
-moz-user-select: none;
|
|
350
|
+
-ms-user-select: none;
|
|
351
|
+
|
|
352
|
+
@media (hover: none) and (pointer: coarse) {
|
|
353
|
+
cursor: grab;
|
|
354
|
+
|
|
355
|
+
&:active {
|
|
356
|
+
cursor: grabbing;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
.volume-slider-wrapper-inner {
|
|
362
|
+
position: relative;
|
|
363
|
+
width: 20px;
|
|
364
|
+
height: 80px;
|
|
365
|
+
display: flex;
|
|
366
|
+
align-items: center;
|
|
367
|
+
justify-content: center;
|
|
368
|
+
margin-top: 12px;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
.custom-volume-slider {
|
|
372
|
+
position: relative;
|
|
373
|
+
width: 4px;
|
|
374
|
+
height: 80px;
|
|
375
|
+
cursor: pointer;
|
|
376
|
+
z-index: 2;
|
|
377
|
+
margin: 0;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
.slider-track {
|
|
381
|
+
position: absolute;
|
|
382
|
+
top: 0;
|
|
383
|
+
left: 0;
|
|
384
|
+
width: 100%;
|
|
385
|
+
height: 100%;
|
|
386
|
+
border-radius: 2px;
|
|
387
|
+
background: rgba(255, 255, 255, 0.2);
|
|
388
|
+
border: none;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
.slider-progress {
|
|
392
|
+
position: absolute;
|
|
393
|
+
bottom: 0;
|
|
394
|
+
left: 0;
|
|
395
|
+
width: 100%;
|
|
396
|
+
background: #ffffff;
|
|
397
|
+
border-radius: 2px;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.slider-thumb {
|
|
401
|
+
$thumb-size: 12px;
|
|
402
|
+
|
|
403
|
+
position: absolute;
|
|
404
|
+
left: 50%;
|
|
405
|
+
transform: translateX(-50%);
|
|
406
|
+
width: $thumb-size;
|
|
407
|
+
height: $thumb-size;
|
|
408
|
+
background: #ffffff;
|
|
409
|
+
border-radius: 50%;
|
|
410
|
+
border: 2px solid rgba(255, 255, 255, 0.8);
|
|
411
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
|
412
|
+
transition: transform 0.1s ease;
|
|
413
|
+
z-index: 3;
|
|
414
|
+
cursor: grab;
|
|
415
|
+
|
|
416
|
+
&.no-transition {
|
|
417
|
+
transition: none;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
&:active {
|
|
421
|
+
cursor: grabbing;
|
|
422
|
+
transform: translateX(-50%) scale(1.1);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
&:hover {
|
|
426
|
+
transform: translateX(-50%) scale(1.1);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
.volume-value {
|
|
431
|
+
color: var(--volume-control-primary);
|
|
432
|
+
font-size: 12px;
|
|
433
|
+
font-weight: 500;
|
|
434
|
+
text-align: center;
|
|
435
|
+
min-width: 32px;
|
|
436
|
+
padding: 2px 6px;
|
|
437
|
+
border-radius: 4px;
|
|
438
|
+
pointer-events: none;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
@media (hover: none) and (pointer: coarse) {
|
|
442
|
+
.volume-slider-wrapper {
|
|
443
|
+
padding: 16px 12px;
|
|
444
|
+
|
|
445
|
+
&:active {
|
|
446
|
+
background: var(--volume-control-background-light);
|
|
447
|
+
transform: scale(0.98);
|
|
448
|
+
transition: all 0.1s ease;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
.volume-slider-wrapper-inner {
|
|
453
|
+
height: 100px;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
</style>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// Fullscreen mode base styles
|
|
2
|
+
.fullscreen-mode {
|
|
3
|
+
width: 100vw !important;
|
|
4
|
+
height: 100vh !important;
|
|
5
|
+
z-index: 9999 !important;
|
|
6
|
+
background-color: #000 !important;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// H5 portrait stream fullscreen mode (preserve original width/height, no forced modification)
|
|
10
|
+
.fullscreen-mode-portrait {
|
|
11
|
+
z-index: 9999 !important;
|
|
12
|
+
background-color: #000 !important;
|
|
13
|
+
// Note: Do not modify width and height, preserve original styles
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Landscape mode styles - Only apply CSS rotation when device is in portrait
|
|
17
|
+
.landscape-mode {
|
|
18
|
+
transform: rotate(90deg) !important;
|
|
19
|
+
transform-origin: center center !important;
|
|
20
|
+
width: 100vh !important;
|
|
21
|
+
height: 100vw !important;
|
|
22
|
+
overflow: hidden !important;
|
|
23
|
+
// Add smooth transition effect
|
|
24
|
+
transition: transform 0.3s ease-in-out !important;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// When device is in landscape, remove CSS rotation to avoid double rotation
|
|
28
|
+
@media screen and (orientation: landscape) {
|
|
29
|
+
.landscape-mode {
|
|
30
|
+
transform: none !important;
|
|
31
|
+
width: 100vw !important;
|
|
32
|
+
height: 100vh !important;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Mobile device landscape optimization
|
|
37
|
+
@media screen and (orientation: landscape) and (max-width: 768px) {
|
|
38
|
+
.live-core-view.landscape-mode {
|
|
39
|
+
// Ensure correct display in landscape
|
|
40
|
+
background-color: #000;
|
|
41
|
+
transform: none !important;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Force landscape display on mobile portrait
|
|
46
|
+
@media screen and (orientation: portrait) and (max-width: 768px) {
|
|
47
|
+
.live-core-view.landscape-mode {
|
|
48
|
+
// Force landscape display in portrait
|
|
49
|
+
transform: rotate(90deg) !important;
|
|
50
|
+
transform-origin: center center !important;
|
|
51
|
+
}
|
|
52
|
+
}
|