tuikit-atomicx-vue3 3.3.2-beta.1 → 3.3.2
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/CoGuestPanel/CoGuestPanel.js +6 -6
- package/dist/components/LiveCoreView/PlayerControl/AudioControl.js +5 -4
- package/dist/components/LiveCoreView/PlayerControl/PlayerControl.js +10 -2
- package/dist/components/LiveCoreView/PlayerControl/PlayerControl.vue.d.ts +14 -1
- package/dist/components/LiveCoreView/PlayerControl/PlayerControlState.d.ts +4 -2
- package/dist/components/LiveCoreView/PlayerControl/PlayerControlState.js +12 -7
- package/dist/components/LiveCoreView/index.js +11 -5
- package/dist/components/StreamView/Layout/CustomLayout.js +2 -2
- package/dist/components/StreamView/Layout/GridLayout.js +2 -2
- package/dist/components/StreamView/common/StreamList/index.js +2 -2
- package/dist/styles/index.css +88 -55
- package/package.json +3 -3
- package/src/components/LiveCoreView/PlayerControl/AudioControl.vue +22 -44
- package/src/components/LiveCoreView/PlayerControl/PlayerControl.vue +75 -20
- package/src/components/LiveCoreView/PlayerControl/PlayerControlState.ts +17 -14
- package/src/components/LiveCoreView/index.vue +4 -4
|
@@ -135,8 +135,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
135
135
|
default: withCtx(() => [
|
|
136
136
|
createTextVNode(toDisplayString(unref(t)("Accept")), 1)
|
|
137
137
|
]),
|
|
138
|
-
_:
|
|
139
|
-
},
|
|
138
|
+
_: 1
|
|
139
|
+
}, 8, ["onClick"]),
|
|
140
140
|
createVNode(unref(TUIButton), {
|
|
141
141
|
color: "red",
|
|
142
142
|
onClick: ($event) => handleRejectCoGuestRequest(user.userId)
|
|
@@ -144,8 +144,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
144
144
|
default: withCtx(() => [
|
|
145
145
|
createTextVNode(toDisplayString(unref(t)("Reject")), 1)
|
|
146
146
|
]),
|
|
147
|
-
_:
|
|
148
|
-
},
|
|
147
|
+
_: 1
|
|
148
|
+
}, 8, ["onClick"])
|
|
149
149
|
])
|
|
150
150
|
])
|
|
151
151
|
]);
|
|
@@ -187,8 +187,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
187
187
|
default: withCtx(() => [
|
|
188
188
|
createTextVNode(toDisplayString(unref(t)("Disconnect")), 1)
|
|
189
189
|
]),
|
|
190
|
-
_:
|
|
191
|
-
},
|
|
190
|
+
_: 1
|
|
191
|
+
}, 8, ["onClick"])
|
|
192
192
|
])) : createCommentVNode("", true)
|
|
193
193
|
])
|
|
194
194
|
]);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defineComponent, ref, computed, onUnmounted, createElementBlock, openBlock, normalizeStyle, createElementVNode, withDirectives, unref, createBlock, normalizeClass, toDisplayString, vShow } from "vue";
|
|
1
|
+
import { defineComponent, ref, computed, onUnmounted, createElementBlock, openBlock, normalizeStyle, createElementVNode, withDirectives, unref, createBlock, normalizeClass, toDisplayString, vShow, toRaw } from "vue";
|
|
2
2
|
import { useUIKit, IconSpeakerOff, IconSpeakerOn } from "@tencentcloud/uikit-base-component-vue3";
|
|
3
3
|
import { isMobile } from "../../../utils/env.js";
|
|
4
4
|
import { _ as _export_sfc } from "../../../_plugin-vue_export-helper-1tPrXgE0.js";
|
|
@@ -44,8 +44,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
44
44
|
height: `${props.iconSize || 20}px`
|
|
45
45
|
}));
|
|
46
46
|
const updateVolume = (newVolume) => {
|
|
47
|
+
volumeState.value.previous = toRaw(volumeState.value.current);
|
|
47
48
|
volumeState.value.current = newVolume;
|
|
48
|
-
volumeState.value.previous = newVolume;
|
|
49
49
|
isMuted.value = newVolume === 0;
|
|
50
50
|
emit("volume-change", newVolume);
|
|
51
51
|
};
|
|
@@ -54,13 +54,14 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
54
54
|
isMuted.value = false;
|
|
55
55
|
volumeState.value.current = volumeState.value.previous || 0.2;
|
|
56
56
|
emit("muted-change", false);
|
|
57
|
+
emit("volume-change", volumeState.value.current);
|
|
57
58
|
} else {
|
|
58
59
|
isMuted.value = true;
|
|
59
60
|
volumeState.value.previous = volumeState.value.current;
|
|
60
61
|
volumeState.value.current = 0;
|
|
61
62
|
emit("muted-change", true);
|
|
63
|
+
emit("volume-change", volumeState.value.current);
|
|
62
64
|
}
|
|
63
|
-
updateVolume(volumeState.value.current);
|
|
64
65
|
};
|
|
65
66
|
const handleVolumeIconClick = () => {
|
|
66
67
|
if (props.enableVolumeControl === false) {
|
|
@@ -245,7 +246,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
245
246
|
};
|
|
246
247
|
}
|
|
247
248
|
});
|
|
248
|
-
const AudioControl = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-
|
|
249
|
+
const AudioControl = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-22bb0b88"]]);
|
|
249
250
|
export {
|
|
250
251
|
AudioControl as default
|
|
251
252
|
};
|
|
@@ -14,6 +14,9 @@ const _hoisted_6 = ["title"];
|
|
|
14
14
|
const AUTO_HIDE_DELAY = 3e3;
|
|
15
15
|
const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
16
16
|
__name: "PlayerControl",
|
|
17
|
+
props: {
|
|
18
|
+
isLandscapeStyleMode: { type: Boolean }
|
|
19
|
+
},
|
|
17
20
|
setup(__props) {
|
|
18
21
|
const {
|
|
19
22
|
isPlaying,
|
|
@@ -28,6 +31,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
28
31
|
setVolume,
|
|
29
32
|
cleanup
|
|
30
33
|
} = usePlayerControlState();
|
|
34
|
+
const props = __props;
|
|
31
35
|
const { t } = useUIKit();
|
|
32
36
|
const currentVolume = ref(1);
|
|
33
37
|
const isMuted = ref(false);
|
|
@@ -213,7 +217,11 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
213
217
|
withDirectives(createElementVNode("div", {
|
|
214
218
|
ref_key: "playerControlRef",
|
|
215
219
|
ref: playerControlRef,
|
|
216
|
-
class: normalizeClass([
|
|
220
|
+
class: normalizeClass([
|
|
221
|
+
"playback-controls",
|
|
222
|
+
unref(isMobile) ? "mobile-mode" : "pc-mode",
|
|
223
|
+
{ "mobile-landscape-mode": props.isLandscapeStyleMode }
|
|
224
|
+
])
|
|
217
225
|
}, [
|
|
218
226
|
createElementVNode("div", _hoisted_1, [
|
|
219
227
|
createElementVNode("span", {
|
|
@@ -265,7 +273,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
265
273
|
};
|
|
266
274
|
}
|
|
267
275
|
});
|
|
268
|
-
const PlayerControl = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-
|
|
276
|
+
const PlayerControl = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-28ed6eb9"]]);
|
|
269
277
|
export {
|
|
270
278
|
PlayerControl as default
|
|
271
279
|
};
|
|
@@ -1,2 +1,15 @@
|
|
|
1
|
-
declare const _default: import('vue').DefineComponent<
|
|
1
|
+
declare const _default: import('vue').DefineComponent<import('vue').ExtractPropTypes<__VLS_TypePropsToRuntimeProps<{
|
|
2
|
+
isLandscapeStyleMode?: boolean;
|
|
3
|
+
}>>, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<import('vue').ExtractPropTypes<__VLS_TypePropsToRuntimeProps<{
|
|
4
|
+
isLandscapeStyleMode?: boolean;
|
|
5
|
+
}>>> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
|
|
2
6
|
export default _default;
|
|
7
|
+
type __VLS_NonUndefinedable<T> = T extends undefined ? never : T;
|
|
8
|
+
type __VLS_TypePropsToRuntimeProps<T> = {
|
|
9
|
+
[K in keyof T]-?: {} extends Pick<T, K> ? {
|
|
10
|
+
type: import('vue').PropType<__VLS_NonUndefinedable<T[K]>>;
|
|
11
|
+
} : {
|
|
12
|
+
type: import('vue').PropType<T[K]>;
|
|
13
|
+
required: true;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Ref } from 'vue';
|
|
2
|
+
import { FullscreenResult } from './utils/fullscreenManager';
|
|
2
3
|
|
|
3
4
|
export declare enum FillMode {
|
|
4
5
|
CONTAIN = "contain",
|
|
@@ -9,12 +10,13 @@ export interface PlayerControlState {
|
|
|
9
10
|
isPlaying: Ref<boolean>;
|
|
10
11
|
currentFillMode: Ref<FillMode>;
|
|
11
12
|
isFullscreen: Ref<boolean>;
|
|
13
|
+
isLandscapeStyleMode: Ref<boolean>;
|
|
12
14
|
isPictureInPicture: Ref<boolean>;
|
|
13
15
|
currentVolume: Ref<number>;
|
|
14
16
|
resume: () => Promise<boolean>;
|
|
15
17
|
pause: () => Promise<boolean>;
|
|
16
|
-
requestFullscreen: () => Promise<
|
|
17
|
-
exitFullscreen: () => Promise<
|
|
18
|
+
requestFullscreen: () => Promise<FullscreenResult>;
|
|
19
|
+
exitFullscreen: () => Promise<FullscreenResult>;
|
|
18
20
|
requestPictureInPicture: () => Promise<boolean>;
|
|
19
21
|
exitPictureInPicture: () => Promise<boolean>;
|
|
20
22
|
setVolume: (volume: number) => Promise<boolean>;
|
|
@@ -4,7 +4,7 @@ import { useLiveState } from "../../../states/LiveState/index.js";
|
|
|
4
4
|
import { useLiveSeatState } from "../../../states/LiveSeatState/index.js";
|
|
5
5
|
import { TRTCCloud } from "@tencentcloud/tuiroom-engine-js";
|
|
6
6
|
import { getDeviceType, getCurrentOrientation, shouldRotateToLandscapeForFullscreen, hadLandscapeRotationToUndo } from "./utils/deviceDetection.js";
|
|
7
|
-
import { OrientationManager, StyleManager,
|
|
7
|
+
import { OrientationManager, StyleManager, FullscreenMode, FullscreenManager } from "./utils/fullscreenManager.js";
|
|
8
8
|
import { EventListenerManager, DOMElementGetter } from "./utils/domHelpers.js";
|
|
9
9
|
import { LiveStatus } from "../../../types/live.js";
|
|
10
10
|
var FillMode = /* @__PURE__ */ ((FillMode2) => {
|
|
@@ -19,12 +19,13 @@ const currentFillMode = ref(
|
|
|
19
19
|
/* CONTAIN */
|
|
20
20
|
);
|
|
21
21
|
const isFullscreen = ref(false);
|
|
22
|
+
const isLandscapeStyleMode = ref(false);
|
|
22
23
|
const isPictureInPicture = ref(false);
|
|
23
24
|
const currentVolume = ref(1);
|
|
25
|
+
const roomEngine = useRoomEngine();
|
|
24
26
|
function usePlayerControlState() {
|
|
25
27
|
const { localLiveStatus } = useLiveState();
|
|
26
28
|
const { canvas } = useLiveSeatState();
|
|
27
|
-
const roomEngine = useRoomEngine();
|
|
28
29
|
const eventManager = new EventListenerManager();
|
|
29
30
|
const orientationListenerId = `player-control-${Date.now()}`;
|
|
30
31
|
const isLandscapeStream = computed(
|
|
@@ -151,8 +152,9 @@ function usePlayerControlState() {
|
|
|
151
152
|
} else {
|
|
152
153
|
console.error("Fullscreen request failed:", result.error);
|
|
153
154
|
}
|
|
154
|
-
|
|
155
|
-
|
|
155
|
+
isLandscapeStyleMode.value = result.shouldRotateToLandscape;
|
|
156
|
+
return result;
|
|
157
|
+
}, "Request fullscreen", { success: false, mode: FullscreenMode.CSS_SIMULATED, shouldRotateToLandscape: false });
|
|
156
158
|
};
|
|
157
159
|
const exitFullscreen = async () => {
|
|
158
160
|
return withErrorHandling(async () => {
|
|
@@ -182,8 +184,9 @@ function usePlayerControlState() {
|
|
|
182
184
|
} else {
|
|
183
185
|
console.error("Fullscreen exit failed:", result.error);
|
|
184
186
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
+
isLandscapeStyleMode.value = false;
|
|
188
|
+
return result;
|
|
189
|
+
}, "Exit fullscreen", { success: false, mode: FullscreenMode.CSS_SIMULATED, shouldRotateToLandscape: false });
|
|
187
190
|
};
|
|
188
191
|
const requestPictureInPicture = async () => {
|
|
189
192
|
return withErrorHandling(async () => {
|
|
@@ -260,6 +263,7 @@ function usePlayerControlState() {
|
|
|
260
263
|
console.warn("Failed to unlock orientation during cleanup:", error);
|
|
261
264
|
});
|
|
262
265
|
}
|
|
266
|
+
isLandscapeStyleMode.value = false;
|
|
263
267
|
console.log("Fullscreen exit style cleanup completed");
|
|
264
268
|
} catch (error) {
|
|
265
269
|
console.error("Fullscreen exit style cleanup failed:", error);
|
|
@@ -308,7 +312,7 @@ function usePlayerControlState() {
|
|
|
308
312
|
const handleLeavePictureInPicture = () => {
|
|
309
313
|
console.log("Left picture-in-picture mode");
|
|
310
314
|
isPictureInPicture.value = false;
|
|
311
|
-
resume
|
|
315
|
+
setTimeout(resume, 300);
|
|
312
316
|
};
|
|
313
317
|
const handlePlay = () => {
|
|
314
318
|
console.log("Video play event detected");
|
|
@@ -386,6 +390,7 @@ function usePlayerControlState() {
|
|
|
386
390
|
isPlaying,
|
|
387
391
|
currentFillMode,
|
|
388
392
|
isFullscreen,
|
|
393
|
+
isLandscapeStyleMode,
|
|
389
394
|
isPictureInPicture,
|
|
390
395
|
currentVolume,
|
|
391
396
|
// Methods
|
|
@@ -24,7 +24,7 @@ const _hoisted_3 = {
|
|
|
24
24
|
const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
25
25
|
__name: "index",
|
|
26
26
|
setup(__props) {
|
|
27
|
-
const { isFullscreen } = usePlayerControlState();
|
|
27
|
+
const { isFullscreen, isLandscapeStyleMode } = usePlayerControlState();
|
|
28
28
|
const { t } = useUIKit();
|
|
29
29
|
const { seatList, canvas, startPlayStream, stopPlayStream } = useLiveSeatState();
|
|
30
30
|
const { currentLive } = useLiveState();
|
|
@@ -239,7 +239,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
239
239
|
fillMode.value = "fit";
|
|
240
240
|
}
|
|
241
241
|
handleStreamRegionSize();
|
|
242
|
-
});
|
|
242
|
+
}, { deep: true });
|
|
243
243
|
function handleStreamRegionSize() {
|
|
244
244
|
if (!liveCoreViewContainerRef.value) {
|
|
245
245
|
return;
|
|
@@ -344,14 +344,20 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
344
344
|
to: "body",
|
|
345
345
|
disabled: !unref(isMobile)
|
|
346
346
|
}, [
|
|
347
|
-
isShowPlayerControl.value ? (openBlock(), createBlock(PlayerControl, {
|
|
347
|
+
isShowPlayerControl.value ? (openBlock(), createBlock(PlayerControl, {
|
|
348
|
+
key: 0,
|
|
349
|
+
isLandscapeStyleMode: unref(isLandscapeStyleMode)
|
|
350
|
+
}, null, 8, ["isLandscapeStyleMode"])) : createCommentVNode("", true)
|
|
348
351
|
], 8, ["disabled"])) : createCommentVNode("", true),
|
|
349
|
-
isShowPlayerControl.value && unref(isFullscreen) ? (openBlock(), createBlock(PlayerControl, {
|
|
352
|
+
isShowPlayerControl.value && unref(isFullscreen) ? (openBlock(), createBlock(PlayerControl, {
|
|
353
|
+
key: 2,
|
|
354
|
+
isLandscapeStyleMode: unref(isLandscapeStyleMode)
|
|
355
|
+
}, null, 8, ["isLandscapeStyleMode"])) : createCommentVNode("", true)
|
|
350
356
|
], 2);
|
|
351
357
|
};
|
|
352
358
|
}
|
|
353
359
|
});
|
|
354
|
-
const LiveCoreViewComponent = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-
|
|
360
|
+
const LiveCoreViewComponent = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-4d9101b9"]]);
|
|
355
361
|
addI18n("en-US", { translation: resource });
|
|
356
362
|
addI18n("zh-CN", { translation: resource$1 });
|
|
357
363
|
export {
|
|
@@ -232,8 +232,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
232
232
|
streamViewUI: withCtx((slotProps) => [
|
|
233
233
|
renderSlot(_ctx.$slots, "streamViewUI", mergeProps({ ref_for: true }, slotProps), void 0, true)
|
|
234
234
|
]),
|
|
235
|
-
_:
|
|
236
|
-
},
|
|
235
|
+
_: 3
|
|
236
|
+
}, 8, ["userInfo", "stream-type", "style"]);
|
|
237
237
|
}), 128))
|
|
238
238
|
], 4)
|
|
239
239
|
], 512);
|
|
@@ -221,8 +221,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
221
221
|
streamViewUI: withCtx((slotProps) => [
|
|
222
222
|
renderSlot(_ctx.$slots, "streamViewUI", mergeProps({ ref_for: true }, slotProps), void 0, true)
|
|
223
223
|
]),
|
|
224
|
-
_:
|
|
225
|
-
},
|
|
224
|
+
_: 3
|
|
225
|
+
}, 8, ["userInfo", "stream-type", "style"]);
|
|
226
226
|
}), 128))
|
|
227
227
|
], 36)
|
|
228
228
|
], 2);
|
|
@@ -195,8 +195,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
195
195
|
streamViewUI: withCtx((slotProps) => [
|
|
196
196
|
renderSlot(_ctx.$slots, "streamViewUI", mergeProps({ ref_for: true }, slotProps), void 0, true)
|
|
197
197
|
]),
|
|
198
|
-
_:
|
|
199
|
-
},
|
|
198
|
+
_: 3
|
|
199
|
+
}, 8, ["user-info", "stream-type", "streamInfo", "style", "streamPlayMode", "streamPlayQuality"]);
|
|
200
200
|
}), 128))
|
|
201
201
|
], 36)
|
|
202
202
|
], 2);
|
package/dist/styles/index.css
CHANGED
|
@@ -9877,7 +9877,7 @@ to {
|
|
|
9877
9877
|
text-overflow: ellipsis;
|
|
9878
9878
|
white-space: nowrap;
|
|
9879
9879
|
overflow: hidden;
|
|
9880
|
-
}.audio-control[data-v-
|
|
9880
|
+
}.audio-control[data-v-22bb0b88] {
|
|
9881
9881
|
--volume-control-primary: rgb(255, 255, 255);
|
|
9882
9882
|
--volume-control-primary-hover: rgba(255, 255, 255, 0.1);
|
|
9883
9883
|
--volume-control-background: rgba(0, 0, 0, 0.8);
|
|
@@ -9888,14 +9888,14 @@ to {
|
|
|
9888
9888
|
display: flex;
|
|
9889
9889
|
align-items: center;
|
|
9890
9890
|
}
|
|
9891
|
-
.volume-btn[data-v-
|
|
9891
|
+
.volume-btn[data-v-22bb0b88] {
|
|
9892
9892
|
width: 100%;
|
|
9893
9893
|
height: 100%;
|
|
9894
9894
|
flex-shrink: 0;
|
|
9895
9895
|
cursor: pointer;
|
|
9896
9896
|
transition: background-color 0.2s ease;
|
|
9897
9897
|
}
|
|
9898
|
-
.volume-slider-container[data-v-
|
|
9898
|
+
.volume-slider-container[data-v-22bb0b88] {
|
|
9899
9899
|
position: absolute;
|
|
9900
9900
|
bottom: 100%;
|
|
9901
9901
|
left: 50%;
|
|
@@ -9903,7 +9903,7 @@ to {
|
|
|
9903
9903
|
margin-bottom: 12px;
|
|
9904
9904
|
z-index: 100;
|
|
9905
9905
|
}
|
|
9906
|
-
.volume-slider-wrapper[data-v-
|
|
9906
|
+
.volume-slider-wrapper[data-v-22bb0b88] {
|
|
9907
9907
|
position: relative;
|
|
9908
9908
|
display: flex;
|
|
9909
9909
|
flex-direction: column;
|
|
@@ -9921,14 +9921,14 @@ to {
|
|
|
9921
9921
|
-ms-user-select: none;
|
|
9922
9922
|
}
|
|
9923
9923
|
@media (hover: none) and (pointer: coarse) {
|
|
9924
|
-
.volume-slider-wrapper[data-v-
|
|
9924
|
+
.volume-slider-wrapper[data-v-22bb0b88] {
|
|
9925
9925
|
cursor: grab;
|
|
9926
9926
|
}
|
|
9927
|
-
.volume-slider-wrapper[data-v-
|
|
9927
|
+
.volume-slider-wrapper[data-v-22bb0b88]:active {
|
|
9928
9928
|
cursor: grabbing;
|
|
9929
9929
|
}
|
|
9930
9930
|
}
|
|
9931
|
-
.volume-slider-wrapper-inner[data-v-
|
|
9931
|
+
.volume-slider-wrapper-inner[data-v-22bb0b88] {
|
|
9932
9932
|
position: relative;
|
|
9933
9933
|
width: 20px;
|
|
9934
9934
|
height: 80px;
|
|
@@ -9937,7 +9937,7 @@ to {
|
|
|
9937
9937
|
justify-content: center;
|
|
9938
9938
|
margin-top: 12px;
|
|
9939
9939
|
}
|
|
9940
|
-
.custom-volume-slider[data-v-
|
|
9940
|
+
.custom-volume-slider[data-v-22bb0b88] {
|
|
9941
9941
|
position: relative;
|
|
9942
9942
|
width: 4px;
|
|
9943
9943
|
height: 80px;
|
|
@@ -9945,7 +9945,7 @@ to {
|
|
|
9945
9945
|
z-index: 2;
|
|
9946
9946
|
margin: 0;
|
|
9947
9947
|
}
|
|
9948
|
-
.slider-track[data-v-
|
|
9948
|
+
.slider-track[data-v-22bb0b88] {
|
|
9949
9949
|
position: absolute;
|
|
9950
9950
|
top: 0;
|
|
9951
9951
|
left: 0;
|
|
@@ -9955,7 +9955,7 @@ to {
|
|
|
9955
9955
|
background: rgba(255, 255, 255, 0.2);
|
|
9956
9956
|
border: none;
|
|
9957
9957
|
}
|
|
9958
|
-
.slider-progress[data-v-
|
|
9958
|
+
.slider-progress[data-v-22bb0b88] {
|
|
9959
9959
|
position: absolute;
|
|
9960
9960
|
bottom: 0;
|
|
9961
9961
|
left: 0;
|
|
@@ -9963,7 +9963,7 @@ to {
|
|
|
9963
9963
|
background: #ffffff;
|
|
9964
9964
|
border-radius: 2px;
|
|
9965
9965
|
}
|
|
9966
|
-
.slider-thumb[data-v-
|
|
9966
|
+
.slider-thumb[data-v-22bb0b88] {
|
|
9967
9967
|
position: absolute;
|
|
9968
9968
|
left: 50%;
|
|
9969
9969
|
transform: translateX(-50%);
|
|
@@ -9977,17 +9977,17 @@ to {
|
|
|
9977
9977
|
z-index: 3;
|
|
9978
9978
|
cursor: grab;
|
|
9979
9979
|
}
|
|
9980
|
-
.slider-thumb.no-transition[data-v-
|
|
9980
|
+
.slider-thumb.no-transition[data-v-22bb0b88] {
|
|
9981
9981
|
transition: none;
|
|
9982
9982
|
}
|
|
9983
|
-
.slider-thumb[data-v-
|
|
9983
|
+
.slider-thumb[data-v-22bb0b88]:active {
|
|
9984
9984
|
cursor: grabbing;
|
|
9985
9985
|
transform: translateX(-50%) scale(1.1);
|
|
9986
9986
|
}
|
|
9987
|
-
.slider-thumb[data-v-
|
|
9987
|
+
.slider-thumb[data-v-22bb0b88]:hover {
|
|
9988
9988
|
transform: translateX(-50%) scale(1.1);
|
|
9989
9989
|
}
|
|
9990
|
-
.volume-value[data-v-
|
|
9990
|
+
.volume-value[data-v-22bb0b88] {
|
|
9991
9991
|
color: var(--volume-control-primary);
|
|
9992
9992
|
font-size: 12px;
|
|
9993
9993
|
font-weight: 500;
|
|
@@ -9998,53 +9998,85 @@ to {
|
|
|
9998
9998
|
pointer-events: none;
|
|
9999
9999
|
}
|
|
10000
10000
|
@media (hover: none) and (pointer: coarse) {
|
|
10001
|
-
.volume-slider-wrapper[data-v-
|
|
10001
|
+
.volume-slider-wrapper[data-v-22bb0b88] {
|
|
10002
10002
|
padding: 16px 12px;
|
|
10003
10003
|
}
|
|
10004
|
-
.volume-slider-wrapper[data-v-
|
|
10004
|
+
.volume-slider-wrapper[data-v-22bb0b88]:active {
|
|
10005
10005
|
background: var(--volume-control-background-light);
|
|
10006
10006
|
transform: scale(0.98);
|
|
10007
10007
|
transition: all 0.1s ease;
|
|
10008
10008
|
}
|
|
10009
|
-
.volume-slider-wrapper-inner[data-v-
|
|
10009
|
+
.volume-slider-wrapper-inner[data-v-22bb0b88] {
|
|
10010
10010
|
height: 100px;
|
|
10011
10011
|
}
|
|
10012
|
-
}.
|
|
10012
|
+
}.playback-controls[data-v-28ed6eb9] {
|
|
10013
|
+
background: #000000;
|
|
10014
|
+
padding: 12px 0;
|
|
10015
|
+
display: flex;
|
|
10016
|
+
width: calc(100% + 1px);
|
|
10017
|
+
align-items: center;
|
|
10018
|
+
box-sizing: border-box;
|
|
10019
|
+
}
|
|
10020
|
+
.pc-mode[data-v-28ed6eb9] {
|
|
10013
10021
|
position: absolute;
|
|
10014
10022
|
bottom: 0;
|
|
10015
10023
|
left: 0;
|
|
10016
10024
|
right: 0;
|
|
10017
10025
|
z-index: 10;
|
|
10018
10026
|
}
|
|
10019
|
-
.mobile-mode[data-v-
|
|
10020
|
-
position: fixed
|
|
10027
|
+
.mobile-mode[data-v-28ed6eb9] {
|
|
10028
|
+
position: fixed;
|
|
10021
10029
|
bottom: 0;
|
|
10022
10030
|
left: 0;
|
|
10023
10031
|
right: 0;
|
|
10024
10032
|
z-index: 999999;
|
|
10025
10033
|
pointer-events: auto;
|
|
10026
10034
|
}
|
|
10027
|
-
|
|
10028
|
-
|
|
10029
|
-
|
|
10030
|
-
|
|
10031
|
-
|
|
10032
|
-
|
|
10033
|
-
|
|
10035
|
+
@media screen and (orientation: portrait) {
|
|
10036
|
+
.mobile-landscape-mode[data-v-28ed6eb9] {
|
|
10037
|
+
position: fixed;
|
|
10038
|
+
bottom: unset;
|
|
10039
|
+
transform: rotate(90deg);
|
|
10040
|
+
transform-origin: left bottom;
|
|
10041
|
+
top: -60px;
|
|
10042
|
+
bottom: unset;
|
|
10043
|
+
width: 100vh;
|
|
10044
|
+
padding-right: 16px;
|
|
10045
|
+
}
|
|
10046
|
+
.mobile-landscape-mode.player-control-enter-active[data-v-28ed6eb9], .mobile-landscape-mode.player-control-leave-active[data-v-28ed6eb9] {
|
|
10047
|
+
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
10048
|
+
will-change: transform, opacity;
|
|
10049
|
+
}
|
|
10050
|
+
.mobile-landscape-mode.player-control-enter-from[data-v-28ed6eb9] {
|
|
10051
|
+
opacity: 0;
|
|
10052
|
+
transform: rotate(90deg) translateY(60px);
|
|
10053
|
+
}
|
|
10054
|
+
.mobile-landscape-mode.player-control-enter-to[data-v-28ed6eb9] {
|
|
10055
|
+
opacity: 1;
|
|
10056
|
+
transform: rotate(90deg) translateY(0);
|
|
10057
|
+
}
|
|
10058
|
+
.mobile-landscape-mode.player-control-leave-from[data-v-28ed6eb9] {
|
|
10059
|
+
opacity: 1;
|
|
10060
|
+
transform: rotate(90deg) translateY(0);
|
|
10061
|
+
}
|
|
10062
|
+
.mobile-landscape-mode.player-control-leave-to[data-v-28ed6eb9] {
|
|
10063
|
+
opacity: 0;
|
|
10064
|
+
transform: rotate(90deg) translateY(60px);
|
|
10034
10065
|
}
|
|
10035
|
-
|
|
10066
|
+
}
|
|
10067
|
+
.control-buttons[data-v-28ed6eb9] {
|
|
10036
10068
|
display: flex;
|
|
10037
10069
|
align-items: center;
|
|
10038
10070
|
justify-content: space-around;
|
|
10039
10071
|
width: 100%;
|
|
10040
10072
|
pointer-events: all;
|
|
10041
10073
|
}
|
|
10042
|
-
.center-controls[data-v-
|
|
10074
|
+
.center-controls[data-v-28ed6eb9] {
|
|
10043
10075
|
display: flex;
|
|
10044
10076
|
align-items: center;
|
|
10045
10077
|
gap: 16px;
|
|
10046
10078
|
}
|
|
10047
|
-
.control-btn[data-v-
|
|
10079
|
+
.control-btn[data-v-28ed6eb9] {
|
|
10048
10080
|
background: transparent;
|
|
10049
10081
|
border: none;
|
|
10050
10082
|
border-radius: 50%;
|
|
@@ -10054,63 +10086,62 @@ to {
|
|
|
10054
10086
|
align-items: center;
|
|
10055
10087
|
justify-content: center;
|
|
10056
10088
|
cursor: pointer;
|
|
10057
|
-
transition: all 0.2s ease;
|
|
10058
10089
|
color: white;
|
|
10059
10090
|
}
|
|
10060
|
-
.control-btn[data-v-
|
|
10091
|
+
.control-btn[data-v-28ed6eb9]:hover {
|
|
10061
10092
|
background: rgba(255, 255, 255, 0.1);
|
|
10062
10093
|
}
|
|
10063
|
-
.control-btn[data-v-
|
|
10094
|
+
.control-btn[data-v-28ed6eb9]:active {
|
|
10064
10095
|
transform: scale(0.95);
|
|
10065
10096
|
}
|
|
10066
|
-
.control-btn .btn-icon[data-v-
|
|
10097
|
+
.control-btn .btn-icon[data-v-28ed6eb9] {
|
|
10067
10098
|
width: 20px;
|
|
10068
10099
|
height: 20px;
|
|
10069
10100
|
fill: currentColor;
|
|
10070
10101
|
}
|
|
10071
|
-
.play-pause-btn .tui-icon[data-v-
|
|
10102
|
+
.play-pause-btn .tui-icon[data-v-28ed6eb9] {
|
|
10072
10103
|
transform: scale(1.5);
|
|
10073
10104
|
}
|
|
10074
|
-
.audio-control-btn[data-v-
|
|
10105
|
+
.audio-control-btn[data-v-28ed6eb9]:active {
|
|
10075
10106
|
transform: unset;
|
|
10076
10107
|
}
|
|
10077
|
-
.playback-time[data-v-
|
|
10108
|
+
.playback-time[data-v-28ed6eb9] {
|
|
10078
10109
|
color: white;
|
|
10079
10110
|
font-size: 14px;
|
|
10080
10111
|
font-weight: 500;
|
|
10081
10112
|
margin-left: 16px;
|
|
10082
10113
|
}
|
|
10083
|
-
.right-controls[data-v-
|
|
10114
|
+
.right-controls[data-v-28ed6eb9] {
|
|
10084
10115
|
display: flex;
|
|
10085
10116
|
align-items: center;
|
|
10086
10117
|
gap: 16px;
|
|
10087
10118
|
}
|
|
10088
|
-
.fullscreen-btn .btn-icon[data-v-
|
|
10119
|
+
.fullscreen-btn .btn-icon[data-v-28ed6eb9] {
|
|
10089
10120
|
width: 18px;
|
|
10090
10121
|
height: 18px;
|
|
10091
10122
|
}
|
|
10092
|
-
.more-btn .btn-icon[data-v-
|
|
10123
|
+
.more-btn .btn-icon[data-v-28ed6eb9] {
|
|
10093
10124
|
width: 18px;
|
|
10094
10125
|
height: 18px;
|
|
10095
10126
|
}
|
|
10096
|
-
.player-control-enter-active[data-v-
|
|
10097
|
-
.player-control-leave-active[data-v-
|
|
10127
|
+
.player-control-enter-active[data-v-28ed6eb9],
|
|
10128
|
+
.player-control-leave-active[data-v-28ed6eb9] {
|
|
10098
10129
|
transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
|
10099
10130
|
will-change: transform, opacity;
|
|
10100
10131
|
}
|
|
10101
|
-
.player-control-enter-from[data-v-
|
|
10132
|
+
.player-control-enter-from[data-v-28ed6eb9] {
|
|
10102
10133
|
opacity: 0;
|
|
10103
10134
|
transform: translateY(100%);
|
|
10104
10135
|
}
|
|
10105
|
-
.player-control-enter-to[data-v-
|
|
10136
|
+
.player-control-enter-to[data-v-28ed6eb9] {
|
|
10106
10137
|
opacity: 1;
|
|
10107
10138
|
transform: translateY(0);
|
|
10108
10139
|
}
|
|
10109
|
-
.player-control-leave-from[data-v-
|
|
10140
|
+
.player-control-leave-from[data-v-28ed6eb9] {
|
|
10110
10141
|
opacity: 1;
|
|
10111
10142
|
transform: translateY(0);
|
|
10112
10143
|
}
|
|
10113
|
-
.player-control-leave-to[data-v-
|
|
10144
|
+
.player-control-leave-to[data-v-28ed6eb9] {
|
|
10114
10145
|
opacity: 0;
|
|
10115
10146
|
transform: translateY(100%);
|
|
10116
10147
|
}.fullscreen-mode {
|
|
@@ -10149,17 +10180,17 @@ to {
|
|
|
10149
10180
|
transform: rotate(90deg) !important;
|
|
10150
10181
|
transform-origin: center center !important;
|
|
10151
10182
|
}
|
|
10152
|
-
}.live-core-view-container[data-v-
|
|
10183
|
+
}.live-core-view-container[data-v-4d9101b9] {
|
|
10153
10184
|
width: 100%;
|
|
10154
10185
|
height: 100%;
|
|
10155
10186
|
display: flex;
|
|
10156
10187
|
justify-content: center;
|
|
10157
10188
|
overflow: hidden;
|
|
10158
10189
|
}
|
|
10159
|
-
.live-core-view-container.align-center[data-v-
|
|
10190
|
+
.live-core-view-container.align-center[data-v-4d9101b9] {
|
|
10160
10191
|
align-items: center;
|
|
10161
10192
|
}
|
|
10162
|
-
.live-core-view-container .live-core-placeholder[data-v-
|
|
10193
|
+
.live-core-view-container .live-core-placeholder[data-v-4d9101b9] {
|
|
10163
10194
|
position: absolute;
|
|
10164
10195
|
top: 0;
|
|
10165
10196
|
left: 0;
|
|
@@ -10169,26 +10200,26 @@ to {
|
|
|
10169
10200
|
align-items: center;
|
|
10170
10201
|
justify-content: center;
|
|
10171
10202
|
}
|
|
10172
|
-
.live-core-view-container .live-core-placeholder .placeholder-text[data-v-
|
|
10203
|
+
.live-core-view-container .live-core-placeholder .placeholder-text[data-v-4d9101b9] {
|
|
10173
10204
|
color: var(--text-color-secondary, rgba(255, 255, 255, 0.55));
|
|
10174
10205
|
font-size: 14px;
|
|
10175
10206
|
font-style: normal;
|
|
10176
10207
|
font-weight: 400;
|
|
10177
10208
|
line-height: 22px;
|
|
10178
10209
|
}
|
|
10179
|
-
.live-core-view-container .live-core-view[data-v-
|
|
10210
|
+
.live-core-view-container .live-core-view[data-v-4d9101b9] {
|
|
10180
10211
|
width: 100%;
|
|
10181
10212
|
height: 100%;
|
|
10182
10213
|
position: absolute;
|
|
10183
10214
|
}
|
|
10184
|
-
.live-core-view-container .live-core-view .stream-content[data-v-
|
|
10215
|
+
.live-core-view-container .live-core-view .stream-content[data-v-4d9101b9] {
|
|
10185
10216
|
width: 100%;
|
|
10186
10217
|
height: 100%;
|
|
10187
10218
|
position: absolute;
|
|
10188
10219
|
top: 0;
|
|
10189
10220
|
left: 0;
|
|
10190
10221
|
}
|
|
10191
|
-
.live-core-view-container .live-core-view .live-core-ui[data-v-
|
|
10222
|
+
.live-core-view-container .live-core-view .live-core-ui[data-v-4d9101b9] {
|
|
10192
10223
|
width: 100%;
|
|
10193
10224
|
height: 100%;
|
|
10194
10225
|
position: absolute;
|
|
@@ -10436,7 +10467,6 @@ to {
|
|
|
10436
10467
|
width: 100%;
|
|
10437
10468
|
overflow: auto;
|
|
10438
10469
|
align-items: center;
|
|
10439
|
-
scrollbar-width: none;
|
|
10440
10470
|
}
|
|
10441
10471
|
.live-list[data-v-96626ee9]::-webkit-scrollbar {
|
|
10442
10472
|
width: 0px;
|
|
@@ -10454,6 +10484,9 @@ to {
|
|
|
10454
10484
|
.live-list[data-v-96626ee9]::-webkit-scrollbar-thumb:hover {
|
|
10455
10485
|
background: var(--uikit-color-gray-3);
|
|
10456
10486
|
}
|
|
10487
|
+
.live-list[data-v-96626ee9] {
|
|
10488
|
+
scrollbar-width: none;
|
|
10489
|
+
}
|
|
10457
10490
|
.live-list-items[data-v-96626ee9] {
|
|
10458
10491
|
flex: 1;
|
|
10459
10492
|
width: 100%;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tuikit-atomicx-vue3",
|
|
3
|
-
"version": "3.3.2
|
|
3
|
+
"version": "3.3.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -48,10 +48,10 @@
|
|
|
48
48
|
"publish:github": "node scripts/publish-github.js"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
|
-
"@tencentcloud/chat": "^3.5.
|
|
51
|
+
"@tencentcloud/chat": "^3.5.8",
|
|
52
52
|
"@tencentcloud/chat-uikit-engine": "~2.5.1",
|
|
53
53
|
"@tencentcloud/tui-core": "latest",
|
|
54
|
-
"@tencentcloud/tuiroom-engine-js": "~3.3.2
|
|
54
|
+
"@tencentcloud/tuiroom-engine-js": "~3.3.2",
|
|
55
55
|
"@tencentcloud/uikit-base-component-vue3": "1.0.1",
|
|
56
56
|
"vue": "^3.4.21"
|
|
57
57
|
},
|
|
@@ -5,13 +5,13 @@
|
|
|
5
5
|
<IconSpeakerOn size="20" v-else />
|
|
6
6
|
</span>
|
|
7
7
|
<div v-show="isVolumeSliderVisible" class="volume-slider-container">
|
|
8
|
-
<div
|
|
8
|
+
<div
|
|
9
9
|
class="volume-slider-wrapper"
|
|
10
10
|
@mouseenter="handleVolumeSliderMouseEnter"
|
|
11
11
|
@mouseleave="handleVolumeSliderMouseLeave"
|
|
12
12
|
>
|
|
13
13
|
<div class="volume-slider-wrapper-inner">
|
|
14
|
-
<div
|
|
14
|
+
<div
|
|
15
15
|
ref="volumeSliderElement"
|
|
16
16
|
class="custom-volume-slider"
|
|
17
17
|
@mousedown="handleSliderMouseDown"
|
|
@@ -19,11 +19,8 @@
|
|
|
19
19
|
@click="handleVolumeSliderAreaClick"
|
|
20
20
|
>
|
|
21
21
|
<div class="slider-track">
|
|
22
|
-
<div
|
|
23
|
-
|
|
24
|
-
:style="{ height: `${volumePercentage}%` }"
|
|
25
|
-
></div>
|
|
26
|
-
<div
|
|
22
|
+
<div class="slider-progress" :style="{ height: `${volumePercentage}%` }"></div>
|
|
23
|
+
<div
|
|
27
24
|
class="slider-thumb"
|
|
28
25
|
:class="{ 'no-transition': isDragging }"
|
|
29
26
|
:style="{ bottom: `${volumePercentage}%` }"
|
|
@@ -38,7 +35,7 @@
|
|
|
38
35
|
</template>
|
|
39
36
|
|
|
40
37
|
<script setup lang="ts">
|
|
41
|
-
import { computed, ref, onUnmounted } from 'vue';
|
|
38
|
+
import { computed, ref, onUnmounted, toRaw } from 'vue';
|
|
42
39
|
import { IconSpeakerOn, IconSpeakerOff, useUIKit } from '@tencentcloud/uikit-base-component-vue3';
|
|
43
40
|
import { isMobile } from '../../../utils';
|
|
44
41
|
|
|
@@ -64,7 +61,7 @@ const { t } = useUIKit();
|
|
|
64
61
|
// Volume state - merged into single object
|
|
65
62
|
const volumeState = ref({
|
|
66
63
|
current: 1,
|
|
67
|
-
previous: 1
|
|
64
|
+
previous: 1,
|
|
68
65
|
});
|
|
69
66
|
|
|
70
67
|
const isMuted = ref(false);
|
|
@@ -75,8 +72,8 @@ const volumeSliderAutoHideTimer = ref<number | null>(null);
|
|
|
75
72
|
|
|
76
73
|
// Auto-hide delay for different platforms
|
|
77
74
|
const AUTO_HIDE_DELAY = {
|
|
78
|
-
PC: 500,
|
|
79
|
-
MOBILE: 3000 // 3 seconds for mobile
|
|
75
|
+
PC: 500, // 0.5 seconds for PC
|
|
76
|
+
MOBILE: 3000, // 3 seconds for mobile
|
|
80
77
|
};
|
|
81
78
|
|
|
82
79
|
// Simplified computed property - directly use isVolumeSliderVisible
|
|
@@ -93,26 +90,25 @@ const iconSizeStyle = computed(() => ({
|
|
|
93
90
|
}));
|
|
94
91
|
|
|
95
92
|
const updateVolume = (newVolume: number) => {
|
|
93
|
+
volumeState.value.previous = toRaw(volumeState.value.current);
|
|
96
94
|
volumeState.value.current = newVolume;
|
|
97
|
-
volumeState.value.previous = newVolume;
|
|
98
95
|
isMuted.value = newVolume === 0;
|
|
99
96
|
emit('volume-change', newVolume);
|
|
100
97
|
};
|
|
101
98
|
|
|
102
99
|
const toggleMute = () => {
|
|
103
100
|
if (isMuted.value) {
|
|
104
|
-
// Unmute
|
|
105
101
|
isMuted.value = false;
|
|
106
102
|
volumeState.value.current = volumeState.value.previous || 0.2;
|
|
107
103
|
emit('muted-change', false);
|
|
104
|
+
emit('volume-change', volumeState.value.current);
|
|
108
105
|
} else {
|
|
109
|
-
// Mute
|
|
110
106
|
isMuted.value = true;
|
|
111
107
|
volumeState.value.previous = volumeState.value.current;
|
|
112
108
|
volumeState.value.current = 0;
|
|
113
109
|
emit('muted-change', true);
|
|
110
|
+
emit('volume-change', volumeState.value.current);
|
|
114
111
|
}
|
|
115
|
-
updateVolume(volumeState.value.current);
|
|
116
112
|
};
|
|
117
113
|
|
|
118
114
|
const handleVolumeIconClick = () => {
|
|
@@ -124,7 +120,7 @@ const handleVolumeIconClick = () => {
|
|
|
124
120
|
if (isMobile) {
|
|
125
121
|
// On mobile: toggle volume slider visibility
|
|
126
122
|
isVolumeSliderVisible.value = !isVolumeSliderVisible.value;
|
|
127
|
-
|
|
123
|
+
|
|
128
124
|
// Start auto-hide timer when showing volume slider
|
|
129
125
|
if (isVolumeSliderVisible.value) {
|
|
130
126
|
startVolumeSliderAutoHideTimer();
|
|
@@ -157,10 +153,10 @@ const stopVolumeSliderAutoHideTimer = () => {
|
|
|
157
153
|
|
|
158
154
|
const handleMouseEnter = () => {
|
|
159
155
|
if (props.enableVolumeControl === false) return;
|
|
160
|
-
|
|
156
|
+
|
|
161
157
|
// Only handle mouse events on PC
|
|
162
158
|
if (isMobile) return;
|
|
163
|
-
|
|
159
|
+
|
|
164
160
|
// On PC, show volume slider and start auto-hide timer
|
|
165
161
|
isVolumeSliderVisible.value = true;
|
|
166
162
|
startVolumeSliderAutoHideTimer();
|
|
@@ -168,10 +164,8 @@ const handleMouseEnter = () => {
|
|
|
168
164
|
|
|
169
165
|
const handleMouseLeave = () => {
|
|
170
166
|
if (props.enableVolumeControl === false) return;
|
|
171
|
-
|
|
172
167
|
// Only handle mouse events on PC
|
|
173
168
|
if (isMobile) return;
|
|
174
|
-
|
|
175
169
|
// On PC, start auto-hide timer when mouse leaves icon area
|
|
176
170
|
// But don't start if currently dragging
|
|
177
171
|
if (!isDragging.value) {
|
|
@@ -183,7 +177,7 @@ const calculateVolumeFromPosition = (clientY: number, target: HTMLElement): numb
|
|
|
183
177
|
const rect = target.getBoundingClientRect();
|
|
184
178
|
const clickY = clientY - rect.top;
|
|
185
179
|
const height = rect.height;
|
|
186
|
-
return Math.max(0, Math.min(1, 1 -
|
|
180
|
+
return Math.max(0, Math.min(1, 1 - clickY / height));
|
|
187
181
|
};
|
|
188
182
|
|
|
189
183
|
const addGlobalEventListeners = () => {
|
|
@@ -202,63 +196,53 @@ const removeGlobalEventListeners = () => {
|
|
|
202
196
|
|
|
203
197
|
const startDragging = () => {
|
|
204
198
|
isDragging.value = true;
|
|
205
|
-
|
|
206
199
|
// Stop auto-hide timer when dragging starts
|
|
207
200
|
if (props.enableVolumeControl) {
|
|
208
201
|
stopVolumeSliderAutoHideTimer();
|
|
209
202
|
}
|
|
210
|
-
|
|
211
203
|
addGlobalEventListeners();
|
|
212
204
|
};
|
|
213
205
|
|
|
214
206
|
const handleSliderMove = (event: MouseEvent | TouchEvent) => {
|
|
215
207
|
if (!isDragging.value) return;
|
|
216
|
-
|
|
217
208
|
event.preventDefault();
|
|
218
|
-
|
|
219
209
|
let clientY: number;
|
|
220
210
|
if (event instanceof MouseEvent) {
|
|
221
211
|
clientY = event.clientY;
|
|
222
212
|
} else {
|
|
223
213
|
clientY = event.touches[0].clientY;
|
|
224
214
|
}
|
|
225
|
-
|
|
226
215
|
const volumeValue = calculateVolumeFromPosition(clientY, volumeSliderElement.value as HTMLElement);
|
|
227
216
|
updateVolume(volumeValue);
|
|
228
217
|
};
|
|
229
218
|
|
|
230
219
|
const handleSliderEnd = () => {
|
|
231
220
|
isDragging.value = false;
|
|
232
|
-
|
|
233
221
|
// Restart auto-hide timer when dragging ends
|
|
234
222
|
if (props.enableVolumeControl && isVolumeSliderVisible.value) {
|
|
235
223
|
startVolumeSliderAutoHideTimer();
|
|
236
224
|
}
|
|
237
|
-
|
|
238
225
|
removeGlobalEventListeners();
|
|
239
226
|
};
|
|
240
227
|
|
|
241
228
|
const handleSliderMouseDown = (event: MouseEvent) => {
|
|
242
229
|
if (props.enableVolumeControl === false) return;
|
|
243
|
-
|
|
244
230
|
startDragging();
|
|
245
231
|
event.preventDefault();
|
|
246
232
|
};
|
|
247
233
|
|
|
248
234
|
const handleSliderTouchStart = (event: TouchEvent) => {
|
|
249
235
|
if (props.enableVolumeControl === false) return;
|
|
250
|
-
|
|
251
236
|
startDragging();
|
|
252
237
|
event.preventDefault();
|
|
253
238
|
};
|
|
254
239
|
|
|
255
240
|
const handleVolumeSliderAreaClick = () => {
|
|
256
241
|
if (props.enableVolumeControl === false) return;
|
|
257
|
-
|
|
258
242
|
if (isMobile) {
|
|
259
243
|
// On mobile, toggle volume slider visibility
|
|
260
244
|
isVolumeSliderVisible.value = !isVolumeSliderVisible.value;
|
|
261
|
-
|
|
245
|
+
|
|
262
246
|
// Start auto-hide timer when showing volume slider
|
|
263
247
|
if (isVolumeSliderVisible.value) {
|
|
264
248
|
startVolumeSliderAutoHideTimer();
|
|
@@ -270,20 +254,16 @@ const handleVolumeSliderAreaClick = () => {
|
|
|
270
254
|
|
|
271
255
|
const handleVolumeSliderMouseEnter = () => {
|
|
272
256
|
if (props.enableVolumeControl === false) return;
|
|
273
|
-
|
|
274
257
|
// Only handle mouse events on PC
|
|
275
258
|
if (isMobile) return;
|
|
276
|
-
|
|
277
259
|
// On PC, stop auto-hide timer when mouse enters slider area
|
|
278
260
|
stopVolumeSliderAutoHideTimer();
|
|
279
261
|
};
|
|
280
262
|
|
|
281
263
|
const handleVolumeSliderMouseLeave = () => {
|
|
282
264
|
if (props.enableVolumeControl === false) return;
|
|
283
|
-
|
|
284
265
|
// Only handle mouse events on PC
|
|
285
266
|
if (isMobile) return;
|
|
286
|
-
|
|
287
267
|
// On PC, start auto-hide timer when mouse leaves slider area
|
|
288
268
|
// But don't start if currently dragging
|
|
289
269
|
if (!isDragging.value) {
|
|
@@ -293,8 +273,6 @@ const handleVolumeSliderMouseLeave = () => {
|
|
|
293
273
|
|
|
294
274
|
onUnmounted(() => {
|
|
295
275
|
removeGlobalEventListeners();
|
|
296
|
-
|
|
297
|
-
// Clean up timers
|
|
298
276
|
if (volumeSliderAutoHideTimer.value) {
|
|
299
277
|
clearTimeout(volumeSliderAutoHideTimer.value);
|
|
300
278
|
}
|
|
@@ -351,7 +329,7 @@ onUnmounted(() => {
|
|
|
351
329
|
|
|
352
330
|
@media (hover: none) and (pointer: coarse) {
|
|
353
331
|
cursor: grab;
|
|
354
|
-
|
|
332
|
+
|
|
355
333
|
&:active {
|
|
356
334
|
cursor: grabbing;
|
|
357
335
|
}
|
|
@@ -412,16 +390,16 @@ onUnmounted(() => {
|
|
|
412
390
|
transition: transform 0.1s ease;
|
|
413
391
|
z-index: 3;
|
|
414
392
|
cursor: grab;
|
|
415
|
-
|
|
393
|
+
|
|
416
394
|
&.no-transition {
|
|
417
395
|
transition: none;
|
|
418
396
|
}
|
|
419
|
-
|
|
397
|
+
|
|
420
398
|
&:active {
|
|
421
399
|
cursor: grabbing;
|
|
422
400
|
transform: translateX(-50%) scale(1.1);
|
|
423
401
|
}
|
|
424
|
-
|
|
402
|
+
|
|
425
403
|
&:hover {
|
|
426
404
|
transform: translateX(-50%) scale(1.1);
|
|
427
405
|
}
|
|
@@ -441,14 +419,14 @@ onUnmounted(() => {
|
|
|
441
419
|
@media (hover: none) and (pointer: coarse) {
|
|
442
420
|
.volume-slider-wrapper {
|
|
443
421
|
padding: 16px 12px;
|
|
444
|
-
|
|
422
|
+
|
|
445
423
|
&:active {
|
|
446
424
|
background: var(--volume-control-background-light);
|
|
447
425
|
transform: scale(0.98);
|
|
448
426
|
transition: all 0.1s ease;
|
|
449
427
|
}
|
|
450
428
|
}
|
|
451
|
-
|
|
429
|
+
|
|
452
430
|
.volume-slider-wrapper-inner {
|
|
453
431
|
height: 100px;
|
|
454
432
|
}
|
|
@@ -3,7 +3,11 @@
|
|
|
3
3
|
<div
|
|
4
4
|
v-show="showControls"
|
|
5
5
|
ref="playerControlRef"
|
|
6
|
-
:class="[
|
|
6
|
+
:class="[
|
|
7
|
+
'playback-controls',
|
|
8
|
+
isMobile ? 'mobile-mode' : 'pc-mode',
|
|
9
|
+
{ 'mobile-landscape-mode': props.isLandscapeStyleMode },
|
|
10
|
+
]"
|
|
7
11
|
>
|
|
8
12
|
<div class="control-buttons">
|
|
9
13
|
<span class="control-btn play-pause-btn" :title="isPlaying ? t('Pause') : t('Play')" @click="handlePlayPause">
|
|
@@ -43,7 +47,15 @@
|
|
|
43
47
|
|
|
44
48
|
<script setup lang="ts">
|
|
45
49
|
import { onMounted, ref, onBeforeUnmount } from 'vue';
|
|
46
|
-
import {
|
|
50
|
+
import {
|
|
51
|
+
IconFullScreen,
|
|
52
|
+
IconPictureInPicture,
|
|
53
|
+
IconPause,
|
|
54
|
+
IconPlay,
|
|
55
|
+
useUIKit,
|
|
56
|
+
TUIToast,
|
|
57
|
+
TOAST_TYPE,
|
|
58
|
+
} from '@tencentcloud/uikit-base-component-vue3';
|
|
47
59
|
import { usePlayerControlState } from './PlayerControlState';
|
|
48
60
|
import AudioControl from './AudioControl.vue';
|
|
49
61
|
import { isMobile } from '../../../utils';
|
|
@@ -63,12 +75,14 @@ const {
|
|
|
63
75
|
cleanup,
|
|
64
76
|
} = usePlayerControlState();
|
|
65
77
|
|
|
78
|
+
const props = defineProps<{
|
|
79
|
+
isLandscapeStyleMode?: boolean;
|
|
80
|
+
}>();
|
|
81
|
+
|
|
66
82
|
const { t } = useUIKit();
|
|
67
83
|
const currentVolume = ref(1);
|
|
68
84
|
const isMuted = ref(false);
|
|
69
|
-
|
|
70
85
|
const playerControlRef = ref<HTMLElement>();
|
|
71
|
-
|
|
72
86
|
const showControls = ref(false);
|
|
73
87
|
const hideTimeout = ref<number | null>(null);
|
|
74
88
|
|
|
@@ -214,29 +228,29 @@ const handleScreenTouchStart = (event: TouchEvent) => {
|
|
|
214
228
|
}
|
|
215
229
|
};
|
|
216
230
|
|
|
217
|
-
const handleScreenTouchMove = (event: TouchEvent) => {
|
|
231
|
+
const handleScreenTouchMove = (event: TouchEvent) => {
|
|
218
232
|
if (playerControlRef.value && playerControlRef.value.contains(event.target as Node)) {
|
|
219
233
|
stopAutoHideControl();
|
|
220
234
|
return;
|
|
221
235
|
}
|
|
222
|
-
}
|
|
236
|
+
};
|
|
223
237
|
|
|
224
238
|
const handleScreenTouchEnd = (event: TouchEvent) => {
|
|
225
239
|
if (!touchStartCoords.value) {
|
|
226
240
|
return;
|
|
227
241
|
}
|
|
228
|
-
|
|
242
|
+
|
|
229
243
|
const touchEnd = event.changedTouches[0];
|
|
230
244
|
const distance = calculateTouchDistance(touchStartCoords.value, touchEnd);
|
|
231
|
-
|
|
245
|
+
|
|
232
246
|
const MAX_CLICK_DISTANCE = 20;
|
|
233
247
|
if (distance > MAX_CLICK_DISTANCE) {
|
|
234
248
|
touchStartCoords.value = null;
|
|
235
249
|
return;
|
|
236
250
|
}
|
|
237
|
-
|
|
251
|
+
|
|
238
252
|
const target = event.target as Node;
|
|
239
|
-
|
|
253
|
+
|
|
240
254
|
if (isPlayerControlTarget(target)) {
|
|
241
255
|
handlePlayerControlTouch();
|
|
242
256
|
} else if (isLiveCoreViewTarget(target)) {
|
|
@@ -244,7 +258,7 @@ const handleScreenTouchEnd = (event: TouchEvent) => {
|
|
|
244
258
|
} else {
|
|
245
259
|
showControls.value = false;
|
|
246
260
|
}
|
|
247
|
-
|
|
261
|
+
|
|
248
262
|
touchStartCoords.value = null;
|
|
249
263
|
};
|
|
250
264
|
|
|
@@ -291,6 +305,15 @@ onBeforeUnmount(() => {
|
|
|
291
305
|
</script>
|
|
292
306
|
|
|
293
307
|
<style scoped lang="scss">
|
|
308
|
+
.playback-controls {
|
|
309
|
+
background: #000000;
|
|
310
|
+
padding: 12px 0;
|
|
311
|
+
display: flex;
|
|
312
|
+
width: calc(100% + 1px); // Solve the problem of 1px deviation during absolute positioning
|
|
313
|
+
align-items: center;
|
|
314
|
+
box-sizing: border-box;
|
|
315
|
+
}
|
|
316
|
+
|
|
294
317
|
.pc-mode {
|
|
295
318
|
position: absolute;
|
|
296
319
|
bottom: 0;
|
|
@@ -300,20 +323,53 @@ onBeforeUnmount(() => {
|
|
|
300
323
|
}
|
|
301
324
|
|
|
302
325
|
.mobile-mode {
|
|
303
|
-
position: fixed
|
|
326
|
+
position: fixed;
|
|
304
327
|
bottom: 0;
|
|
305
328
|
left: 0;
|
|
306
329
|
right: 0;
|
|
307
330
|
z-index: 999999;
|
|
308
331
|
pointer-events: auto;
|
|
309
332
|
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
333
|
+
|
|
334
|
+
@media screen and (orientation: portrait) {
|
|
335
|
+
.mobile-landscape-mode {
|
|
336
|
+
position: fixed;
|
|
337
|
+
bottom: unset;
|
|
338
|
+
transform: rotate(90deg);
|
|
339
|
+
transform-origin: left bottom;
|
|
340
|
+
top: -60px;
|
|
341
|
+
bottom: unset;
|
|
342
|
+
width: 100vh;
|
|
343
|
+
padding-right: 16px;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
.mobile-landscape-mode {
|
|
347
|
+
&.player-control-enter-active,
|
|
348
|
+
&.player-control-leave-active {
|
|
349
|
+
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
350
|
+
will-change: transform, opacity;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
&.player-control-enter-from {
|
|
354
|
+
opacity: 0;
|
|
355
|
+
transform: rotate(90deg) translateY(60px);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
&.player-control-enter-to {
|
|
359
|
+
opacity: 1;
|
|
360
|
+
transform: rotate(90deg) translateY(0);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
&.player-control-leave-from {
|
|
364
|
+
opacity: 1;
|
|
365
|
+
transform: rotate(90deg) translateY(0);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
&.player-control-leave-to {
|
|
369
|
+
opacity: 0;
|
|
370
|
+
transform: rotate(90deg) translateY(60px);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
317
373
|
}
|
|
318
374
|
|
|
319
375
|
.control-buttons {
|
|
@@ -340,7 +396,6 @@ onBeforeUnmount(() => {
|
|
|
340
396
|
align-items: center;
|
|
341
397
|
justify-content: center;
|
|
342
398
|
cursor: pointer;
|
|
343
|
-
transition: all 0.2s ease;
|
|
344
399
|
color: white;
|
|
345
400
|
|
|
346
401
|
&:hover {
|
|
@@ -37,6 +37,7 @@ export interface PlayerControlState {
|
|
|
37
37
|
isPlaying: Ref<boolean>;
|
|
38
38
|
currentFillMode: Ref<FillMode>;
|
|
39
39
|
isFullscreen: Ref<boolean>;
|
|
40
|
+
isLandscapeStyleMode: Ref<boolean>;
|
|
40
41
|
isPictureInPicture: Ref<boolean>;
|
|
41
42
|
currentVolume: Ref<number>;
|
|
42
43
|
|
|
@@ -45,8 +46,8 @@ export interface PlayerControlState {
|
|
|
45
46
|
pause: () => Promise<boolean>;
|
|
46
47
|
|
|
47
48
|
// Fullscreen control methods
|
|
48
|
-
requestFullscreen: () => Promise<
|
|
49
|
-
exitFullscreen: () => Promise<
|
|
49
|
+
requestFullscreen: () => Promise<FullscreenResult>;
|
|
50
|
+
exitFullscreen: () => Promise<FullscreenResult>;
|
|
50
51
|
|
|
51
52
|
// Picture-in-picture control methods
|
|
52
53
|
requestPictureInPicture: () => Promise<boolean>;
|
|
@@ -64,8 +65,10 @@ export interface PlayerControlState {
|
|
|
64
65
|
const isPlaying = ref(true);
|
|
65
66
|
const currentFillMode = ref<FillMode>(FillMode.CONTAIN);
|
|
66
67
|
const isFullscreen = ref(false);
|
|
68
|
+
const isLandscapeStyleMode = ref(false);
|
|
67
69
|
const isPictureInPicture = ref(false);
|
|
68
70
|
const currentVolume = ref(1.0); // Default volume is 1.0 (100%)
|
|
71
|
+
const roomEngine = useRoomEngine();
|
|
69
72
|
|
|
70
73
|
/**
|
|
71
74
|
* Player control state management hook
|
|
@@ -74,7 +77,6 @@ export function usePlayerControlState(): PlayerControlState {
|
|
|
74
77
|
// Dependency injection
|
|
75
78
|
const { localLiveStatus } = useLiveState();
|
|
76
79
|
const { canvas } = useLiveSeatState();
|
|
77
|
-
const roomEngine = useRoomEngine();
|
|
78
80
|
|
|
79
81
|
// Event listener management
|
|
80
82
|
const eventManager = new EventListenerManager();
|
|
@@ -211,7 +213,7 @@ export function usePlayerControlState(): PlayerControlState {
|
|
|
211
213
|
/**
|
|
212
214
|
* Fullscreen control methods
|
|
213
215
|
*/
|
|
214
|
-
const requestFullscreen = async (): Promise<
|
|
216
|
+
const requestFullscreen = async (): Promise<FullscreenResult> => {
|
|
215
217
|
return withErrorHandling(async () => {
|
|
216
218
|
const elements = DOMElementGetter.getAllElements();
|
|
217
219
|
const validation = DOMElementGetter.validateElements(elements);
|
|
@@ -248,12 +250,12 @@ export function usePlayerControlState(): PlayerControlState {
|
|
|
248
250
|
} else {
|
|
249
251
|
console.error('Fullscreen request failed:', result.error);
|
|
250
252
|
}
|
|
251
|
-
|
|
252
|
-
return result
|
|
253
|
-
}, 'Request fullscreen', false);
|
|
253
|
+
isLandscapeStyleMode.value = result.shouldRotateToLandscape;
|
|
254
|
+
return result;
|
|
255
|
+
}, 'Request fullscreen', { success: false, mode: FullscreenMode.CSS_SIMULATED, shouldRotateToLandscape: false });
|
|
254
256
|
};
|
|
255
257
|
|
|
256
|
-
const exitFullscreen = async (): Promise<
|
|
258
|
+
const exitFullscreen = async (): Promise<FullscreenResult> => {
|
|
257
259
|
return withErrorHandling(async () => {
|
|
258
260
|
const elements = DOMElementGetter.getAllElements();
|
|
259
261
|
if (!elements.view) {
|
|
@@ -291,9 +293,9 @@ export function usePlayerControlState(): PlayerControlState {
|
|
|
291
293
|
} else {
|
|
292
294
|
console.error('Fullscreen exit failed:', result.error);
|
|
293
295
|
}
|
|
294
|
-
|
|
295
|
-
return result
|
|
296
|
-
}, 'Exit fullscreen', false);
|
|
296
|
+
isLandscapeStyleMode.value = false;
|
|
297
|
+
return result;
|
|
298
|
+
}, 'Exit fullscreen', { success: false, mode: FullscreenMode.CSS_SIMULATED, shouldRotateToLandscape: false });
|
|
297
299
|
};
|
|
298
300
|
|
|
299
301
|
/**
|
|
@@ -394,7 +396,8 @@ export function usePlayerControlState(): PlayerControlState {
|
|
|
394
396
|
console.warn('Failed to unlock orientation during cleanup:', error);
|
|
395
397
|
});
|
|
396
398
|
}
|
|
397
|
-
|
|
399
|
+
|
|
400
|
+
isLandscapeStyleMode.value = false;
|
|
398
401
|
console.log('Fullscreen exit style cleanup completed');
|
|
399
402
|
} catch (error) {
|
|
400
403
|
console.error('Fullscreen exit style cleanup failed:', error);
|
|
@@ -419,7 +422,6 @@ export function usePlayerControlState(): PlayerControlState {
|
|
|
419
422
|
|
|
420
423
|
// Update state
|
|
421
424
|
isFullscreen.value = isCurrentlyFullscreen;
|
|
422
|
-
|
|
423
425
|
// If exiting fullscreen, need to cleanup styles
|
|
424
426
|
if (wasFullscreen && !isCurrentlyFullscreen) {
|
|
425
427
|
console.log('Detected passive fullscreen exit, executing style cleanup');
|
|
@@ -462,7 +464,7 @@ export function usePlayerControlState(): PlayerControlState {
|
|
|
462
464
|
const handleLeavePictureInPicture = () => {
|
|
463
465
|
console.log('Left picture-in-picture mode');
|
|
464
466
|
isPictureInPicture.value = false;
|
|
465
|
-
resume
|
|
467
|
+
setTimeout(resume, 300);
|
|
466
468
|
};
|
|
467
469
|
|
|
468
470
|
// Video playback state change handlers
|
|
@@ -580,6 +582,7 @@ export function usePlayerControlState(): PlayerControlState {
|
|
|
580
582
|
isPlaying,
|
|
581
583
|
currentFillMode,
|
|
582
584
|
isFullscreen,
|
|
585
|
+
isLandscapeStyleMode,
|
|
583
586
|
isPictureInPicture,
|
|
584
587
|
currentVolume,
|
|
585
588
|
|
|
@@ -46,9 +46,9 @@
|
|
|
46
46
|
/>
|
|
47
47
|
</div>
|
|
48
48
|
<Teleport to="body" v-if="!isFullscreen" :disabled="!isMobile">
|
|
49
|
-
<PlayerControl v-if="isShowPlayerControl" />
|
|
49
|
+
<PlayerControl :isLandscapeStyleMode="isLandscapeStyleMode" v-if="isShowPlayerControl" />
|
|
50
50
|
</Teleport>
|
|
51
|
-
<PlayerControl v-if="isShowPlayerControl && isFullscreen" />
|
|
51
|
+
<PlayerControl :isLandscapeStyleMode="isLandscapeStyleMode" v-if="isShowPlayerControl && isFullscreen" />
|
|
52
52
|
</div>
|
|
53
53
|
</template>
|
|
54
54
|
|
|
@@ -65,7 +65,7 @@ import type { SeatInfo, SeatUserInfo } from '../../types';
|
|
|
65
65
|
import { isMobile } from '../../utils';
|
|
66
66
|
import { usePlayerControlState } from './PlayerControl';
|
|
67
67
|
|
|
68
|
-
const { isFullscreen } = usePlayerControlState();
|
|
68
|
+
const { isFullscreen, isLandscapeStyleMode } = usePlayerControlState();
|
|
69
69
|
const { t } = useUIKit();
|
|
70
70
|
const { seatList, canvas, startPlayStream, stopPlayStream } = useLiveSeatState();
|
|
71
71
|
const { currentLive } = useLiveState();
|
|
@@ -304,7 +304,7 @@ watch(() => [canvas.value, seatList.value], () => {
|
|
|
304
304
|
fillMode.value = StreamFillMode.Fit;
|
|
305
305
|
}
|
|
306
306
|
handleStreamRegionSize();
|
|
307
|
-
});
|
|
307
|
+
}, { deep: true });
|
|
308
308
|
|
|
309
309
|
function handleStreamRegionSize() {
|
|
310
310
|
if (!liveCoreViewContainerRef.value) {
|