<template>
<view style="flex: auto">
<!-- :style="{ width: `${videoWidth}rpx`, height: `${videoHeight}rpx` }" -->
<video
id="video"
src="https://ygg-all.oss-cn-zhangjiakou.aliyuncs.com/app/video/xuanchuan.mp4"
class="relative"
:style="{ width: `${videoWidth}rpx`, height: `${videoHeight}rpx` }"
:controls="false"
:enable-play-gesture="true"
:autoplay="false"
:vslide-gesture-in-fullscreen="false"
:advanced="advanced"
@controlstoggle="onControlstoggle"
@loadedmetadata="loadedmetadata"
@fullscreenchange="onFullscreenChange"
@play="isPlay = true"
@pause="isPlay = false"
@ended="isPlay = false"
@timeupdate="onTimeUpdate"
@longpress="setPlaybackRate(2)"
@touchend="setPlaybackRate(1)"
@fullscreenclick="test"
>
<view class="controls-head" :style="{ transform: `translateY(${showControls ? 0 : '-100%'})`, position: isFullScreen ? 'fixed' : 'absolute' }">
<text class="controls-head-title" @longpress="onClick">标题</text>
<view class="controls-head-capsules" @tap="showMenu = !showMenu">
<text @longpress="onClick" class="controls-head-capsules-text" style="color: white">章节</text>
<text class="controls-head-capsules-icon"></text>
</view>
</view>
<view class="controls-bar" @tap.stop.prevent :style="{ transform: `translateY(${showControls ? 0 : '100%'})`, position: isFullScreen ? 'fixed' : 'absolute' }">
<text class="controls-bar-play" @tap="onPlay">{{ isPlay ? '' : '' }}</text>
<text class="controls-bar-current-time">{{ formatTime(videoCurrentTime) }}</text>
<view style="flex: 1" @tap.stop.prevent>
<slider
:value="videoCurrentTime"
:min="0"
:max="videoDuration"
:step="1"
:block-size="10"
@changing="handerSilderChanging($event.detail.value)"
@change="handerSilderChange($event.detail.value)"
/>
</view>
<text class="controls-bar-duration">{{ formatTime(videoDuration) }}</text>
<view class="controls-bar-rate" @tap="showRates = !showRates">
<text class="controls-bar-rate-number">{{ currentRate.toFixed(1) }}</text>
<text class="controls-bar-rate-text">倍速</text>
</view>
<text class="controls-bar-fullscreen" @tap.stop="onFullScreen()">{{ isFullScreen ? '' : '' }}</text>
</view>
<view
class="controls-bar-rate-list"
:style="{
transform: `translateX(${showControls && showRates ? 0 : '200%'})`,
opacity: showControls && showRates ? 1 : 0,
position: isFullScreen ? 'fixed' : 'absolute'
}"
>
<text class="controls-bar-rate-list-number" v-for="(item, index) in rates" :key="index" :class="{ avtion: item === currentRate }" @tap="setPlaybackRate(item)">
{{ item.toFixed(1) }}
</text>
</view>
<view
class="menu-list"
:style="{
transform: `translateX(${showControls && showMenu ? 0 : '200%'})`,
opacity: showControls && showMenu ? 1 : 0,
position: isFullScreen ? 'fixed' : 'absolute'
}"
>
<list :show-scrollbar="false">
<cell v-for="i in 10" :key="i">
<view class="menu-list-item">
<text class="menu-list-item-text">知识点{{ i }}:xxxx</text>
<!--  -->
<text class="menu-list-item-icon"></text>
</view>
</cell>
</list>
</view>
</video>
</view>
</template>
<script setup>
import { ref, onBeforeMount, watch } from 'vue';
import { onReady } from '@dcloudio/uni-app';
const advanced = ref([
{
key: 'auto_convert',
value: 1,
type: 'format'
}
]);
const video = ref(null);
const videoWidth = ref(750);
const videoHeight = ref(421);
const videoDuration = ref(0);
const videoCurrentTime = ref(0);
const isFullScreen = ref(false);
const isPlay = ref(false);
const rates = ref([0.5, 1, 1.5, 2]);
const currentRate = ref(1);
const showRates = ref(false);
const showControls = ref(false);
const showMenu = ref(false);
const changing = ref(false);
const handerSilderChanging = (v) => {
changing.value = true;
videoCurrentTime.value = v;
};
const handerSilderChange = (v) => {
changing.value = false;
video.value.seek(v);
};
const onControlstoggle = ({ detail }) => {
showControls.value = detail.show;
};
const onClick = (e) => {
console.log(e);
};
const setPlaybackRate = (rate) => {
video.value.playbackRate(rate);
currentRate.value = rate;
showRates.value = false;
};
const loadedmetadata = ({ detail }) => {
console.log(detail);
const { width, height, duration } = detail;
videoWidth.value = width;
videoHeight.value = height;
videoDuration.value = duration;
};
const onPlay = () => {
if (isPlay.value) {
video.value.pause();
} else {
video.value.play();
}
};
const onFullscreenChange = (e) => {
const { fullScreen } = e.detail;
isFullScreen.value = fullScreen;
};
const onFullScreen = (direction = 'horizontal') => {
if (isFullScreen.value) {
video.value.exitFullScreen();
} else {
video.value.requestFullScreen();
}
};
const onTimeUpdate = ({ detail }) => {
const { currentTime, duration } = detail;
if (changing.value) return;
videoCurrentTime.value = currentTime;
videoDuration.value = duration;
};
const test = (e) => {
console.log(e);
// const { screenWidth, screenHeight } = e.detail;
// videoWidth.value = screenWidth;
// videoHeight.value = screenHeight;
};
const formatTime = (seconds) => {
if (isNaN(seconds) || seconds < 0) return '00:00';
seconds = Math.floor(seconds);
const h = Math.floor(seconds / 3600);
const m = Math.floor((seconds % 3600) / 60);
const s = seconds % 60;
if (h > 0) {
// 大于 1 小时 → 00:00:00
return `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`;
} else {
// 小于 1 小时 → 00:00
return `${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`;
}
};
watch(
() => ({
controls: showControls.value,
fullscreen: isFullScreen.value
}),
(newVal, oldVal) => {
showRates.value = false;
showMenu.value = false;
}
);
onBeforeMount(() => {
// #ifdef APP-PLUS-NVUE
const domModule = uni.requireNativePlugin('dom');
domModule.addRule('fontFace', {
fontFamily: 'iconfont',
src: "url('/static/font/iconfont.ttf')"
});
// #endif
});
onReady(() => {
video.value = uni.createVideoContext('video');
});
</script>
<style scoped lang="scss">
$themeColor: #2150ec;
.controls-head {
position: absolute;
top: 0;
left: 0;
right: 0;
flex-direction: row;
justify-content: space-between;
padding: 10rpx 30rpx;
background-image: linear-gradient(to bottom, black, transparent);
transition-property: transform, background-color;
transition-duration: 0.3s;
transition-timing-function: linear;
&-title {
color: white;
font-size: 14px;
}
&-capsules {
flex-direction: row;
align-items: center;
justify-content: center;
&-text {
color: white;
font-size: 12px;
}
&-icon {
font-family: iconfont;
font-size: 30rpx;
background-color: $themeColor;
border-radius: 999px;
margin-left: 4px;
text-align: center;
width: 36rpx;
height: 36rpx;
line-height: 36rpx;
}
}
}
.controls-bar {
position: absolute;
bottom: 0;
left: 0;
right: 0;
flex-direction: row;
justify-content: space-between;
align-items: center;
padding: 10rpx 30rpx;
background-image: linear-gradient(to top, black, transparent);
transition-property: transform, background-color;
transition-duration: 0.3s;
transition-timing-function: linear;
&-play {
font-family: iconfont;
font-size: 50rpx;
color: $themeColor;
}
&-current-time,
&-duration {
color: white;
font-size: 14px;
padding: 0 20rpx;
}
&-rate {
flex-direction: row;
background-color: $themeColor;
border-radius: 999px;
padding: 2px;
margin-right: 20rpx;
position: relative;
&-list {
position: absolute;
bottom: 70rpx;
right: 90rpx;
background-color: rgba(255, 255, 255, 0.5);
width: 100rpx;
border-radius: 10rpx;
transition-property: transform, opacity;
transition-duration: 0.3s;
transition-timing-function: linear;
// opacity: 0;
// transform: translateY(150rpx);
&-number {
text-align: center;
border-radius: 10rpx;
&.avtion {
background-color: #2150ec;
}
}
}
&-number {
width: 36rpx;
height: 36rpx;
border-radius: 999px;
background-color: black;
color: $themeColor;
text-align: center;
line-height: 36rpx;
font-size: 12px;
}
&-text {
color: white;
font-size: 12px;
line-height: 36rpx;
padding: 0 8rpx;
}
}
&-fullscreen {
font-family: iconfont;
font-size: 24rpx;
width: 36rpx;
height: 36rpx;
border-radius: 999px;
background-color: $themeColor;
text-align: center;
line-height: 36rpx;
}
}
.menu-list {
position: absolute;
right: 30rpx;
top: 60rpx;
bottom: 70rpx;
background-color: #333333;
border-radius: 20rpx;
flex-direction: column;
padding: 10rpx 20rpx;
transition-property: transform, opacity;
transition-duration: 0.3s;
transition-timing-function: linear;
// transform: translateX(200%);
// opacity: 0;
&-item {
margin-bottom: 10rpx;
flex-direction: row;
justify-content: space-between;
align-items: center;
&-text {
color: white;
font-size: 12px;
}
&-icon {
margin-left: 30rpx;
font-family: iconfont;
color: $themeColor;
font-size: 12px;
}
}
}
</style> - 发布:2025-11-11 09:40
- 更新:2025-11-11 09:40
- 阅读:15
产品分类: uniapp/App
PC开发环境操作系统: Windows
PC开发环境操作系统版本号: Windows11
HBuilderX类型: 正式
HBuilderX版本号: 4.85
手机系统: iOS
手机系统版本号: iOS 16
手机厂商: 苹果
手机机型: iPhoneX
页面类型: nvue
vue版本: vue3
打包方式: 云端
项目创建方式: HBuilderX
示例代码:
操作步骤:
<template>
<view style="flex: auto">
<!-- :style="{ width: `${videoWidth}rpx`, height: `${videoHeight}rpx` }" -->
<video
id="video"
src="https://ygg-all.oss-cn-zhangjiakou.aliyuncs.com/app/video/xuanchuan.mp4"
class="relative"
:style="{ width: `${videoWidth}rpx`, height: `${videoHeight}rpx` }"
:controls="false"
:enable-play-gesture="true"
:autoplay="false"
:vslide-gesture-in-fullscreen="false"
:advanced="advanced"
@controlstoggle="onControlstoggle"
@loadedmetadata="loadedmetadata"
@fullscreenchange="onFullscreenChange"
@play="isPlay = true"
@pause="isPlay = false"
@ended="isPlay = false"
@timeupdate="onTimeUpdate"
@longpress="setPlaybackRate(2)"
@touchend="setPlaybackRate(1)"
@fullscreenclick="test"
>
<view class="controls-head" :style="{ transform: `translateY(${showControls ? 0 : '-100%'})`, position: isFullScreen ? 'fixed' : 'absolute' }">
<text class="controls-head-title" @longpress="onClick">标题</text>
<view class="controls-head-capsules" @tap="showMenu = !showMenu">
<text @longpress="onClick" class="controls-head-capsules-text" style="color: white">章节</text>
<text class="controls-head-capsules-icon"></text>
</view>
</view>
<view class="controls-bar" @tap.stop.prevent :style="{ transform: `translateY(${showControls ? 0 : '100%'})`, position: isFullScreen ? 'fixed' : 'absolute' }">
<text class="controls-bar-play" @tap="onPlay">{{ isPlay ? '' : '' }}</text>
<text class="controls-bar-current-time">{{ formatTime(videoCurrentTime) }}</text>
<view style="flex: 1" @tap.stop.prevent>
<slider
:value="videoCurrentTime"
:min="0"
:max="videoDuration"
:step="1"
:block-size="10"
@changing="handerSilderChanging($event.detail.value)"
@change="handerSilderChange($event.detail.value)"
/>
</view>
<text class="controls-bar-duration">{{ formatTime(videoDuration) }}</text>
<view class="controls-bar-rate" @tap="showRates = !showRates">
<text class="controls-bar-rate-number">{{ currentRate.toFixed(1) }}</text>
<text class="controls-bar-rate-text">倍速</text>
</view>
<text class="controls-bar-fullscreen" @tap.stop="onFullScreen()">{{ isFullScreen ? '' : '' }}</text>
</view>
<view
class="controls-bar-rate-list"
:style="{
transform: `translateX(${showControls && showRates ? 0 : '200%'})`,
opacity: showControls && showRates ? 1 : 0,
position: isFullScreen ? 'fixed' : 'absolute'
}"
>
<text class="controls-bar-rate-list-number" v-for="(item, index) in rates" :key="index" :class="{ avtion: item === currentRate }" @tap="setPlaybackRate(item)">
{{ item.toFixed(1) }}
</text>
</view>
<view
class="menu-list"
:style="{
transform: `translateX(${showControls && showMenu ? 0 : '200%'})`,
opacity: showControls && showMenu ? 1 : 0,
position: isFullScreen ? 'fixed' : 'absolute'
}"
>
<list :show-scrollbar="false">
<cell v-for="i in 10" :key="i">
<view class="menu-list-item">
<text class="menu-list-item-text">知识点{{ i }}:xxxx</text>
<!--  -->
<text class="menu-list-item-icon"></text>
</view>
</cell>
</list>
</view>
</video>
</view>
</template>
<script setup>
import { ref, onBeforeMount, watch } from 'vue';
import { onReady } from '@dcloudio/uni-app';
const advanced = ref([
{
key: 'auto_convert',
value: 1,
type: 'format'
}
]);
const video = ref(null);
const videoWidth = ref(750);
const videoHeight = ref(421);
const videoDuration = ref(0);
const videoCurrentTime = ref(0);
const isFullScreen = ref(false);
const isPlay = ref(false);
const rates = ref([0.5, 1, 1.5, 2]);
const currentRate = ref(1);
const showRates = ref(false);
const showControls = ref(false);
const showMenu = ref(false);
const changing = ref(false);
const handerSilderChanging = (v) => {
changing.value = true;
videoCurrentTime.value = v;
};
const handerSilderChange = (v) => {
changing.value = false;
video.value.seek(v);
};
const onControlstoggle = ({ detail }) => {
showControls.value = detail.show;
};
const onClick = (e) => {
console.log(e);
};
const setPlaybackRate = (rate) => {
video.value.playbackRate(rate);
currentRate.value = rate;
showRates.value = false;
};
const loadedmetadata = ({ detail }) => {
console.log(detail);
const { width, height, duration } = detail;
videoWidth.value = width;
videoHeight.value = height;
videoDuration.value = duration;
};
const onPlay = () => {
if (isPlay.value) {
video.value.pause();
} else {
video.value.play();
}
};
const onFullscreenChange = (e) => {
const { fullScreen } = e.detail;
isFullScreen.value = fullScreen;
};
const onFullScreen = (direction = 'horizontal') => {
if (isFullScreen.value) {
video.value.exitFullScreen();
} else {
video.value.requestFullScreen();
}
};
const onTimeUpdate = ({ detail }) => {
const { currentTime, duration } = detail;
if (changing.value) return;
videoCurrentTime.value = currentTime;
videoDuration.value = duration;
};
const test = (e) => {
console.log(e);
// const { screenWidth, screenHeight } = e.detail;
// videoWidth.value = screenWidth;
// videoHeight.value = screenHeight;
};
const formatTime = (seconds) => {
if (isNaN(seconds) || seconds < 0) return '00:00';
seconds = Math.floor(seconds);
const h = Math.floor(seconds / 3600);
const m = Math.floor((seconds % 3600) / 60);
const s = seconds % 60;
if (h > 0) {
// 大于 1 小时 → 00:00:00
return `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`;
} else {
// 小于 1 小时 → 00:00
return `${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`;
}
};
watch(
() => ({
controls: showControls.value,
fullscreen: isFullScreen.value
}),
(newVal, oldVal) => {
showRates.value = false;
showMenu.value = false;
}
);
onBeforeMount(() => {
// #ifdef APP-PLUS-NVUE
const domModule = uni.requireNativePlugin('dom');
domModule.addRule('fontFace', {
fontFamily: 'iconfont',
src: "url('/static/font/iconfont.ttf')"
});
// #endif
});
onReady(() => {
video.value = uni.createVideoContext('video');
});
</script>
<style scoped lang="scss">
$themeColor: #2150ec;
.controls-head {
position: absolute;
top: 0;
left: 0;
right: 0;
flex-direction: row;
justify-content: space-between;
padding: 10rpx 30rpx;
background-image: linear-gradient(to bottom, black, transparent);
transition-property: transform, background-color;
transition-duration: 0.3s;
transition-timing-function: linear;
&-title {
color: white;
font-size: 14px;
}
&-capsules {
flex-direction: row;
align-items: center;
justify-content: center;
&-text {
color: white;
font-size: 12px;
}
&-icon {
font-family: iconfont;
font-size: 30rpx;
background-color: $themeColor;
border-radius: 999px;
margin-left: 4px;
text-align: center;
width: 36rpx;
height: 36rpx;
line-height: 36rpx;
}
}
}
.controls-bar {
position: absolute;
bottom: 0;
left: 0;
right: 0;
flex-direction: row;
justify-content: space-between;
align-items: center;
padding: 10rpx 30rpx;
background-image: linear-gradient(to top, black, transparent);
transition-property: transform, background-color;
transition-duration: 0.3s;
transition-timing-function: linear;
&-play {
font-family: iconfont;
font-size: 50rpx;
color: $themeColor;
}
&-current-time,
&-duration {
color: white;
font-size: 14px;
padding: 0 20rpx;
}
&-rate {
flex-direction: row;
background-color: $themeColor;
border-radius: 999px;
padding: 2px;
margin-right: 20rpx;
position: relative;
&-list {
position: absolute;
bottom: 70rpx;
right: 90rpx;
background-color: rgba(255, 255, 255, 0.5);
width: 100rpx;
border-radius: 10rpx;
transition-property: transform, opacity;
transition-duration: 0.3s;
transition-timing-function: linear;
// opacity: 0;
// transform: translateY(150rpx);
&-number {
text-align: center;
border-radius: 10rpx;
&.avtion {
background-color: #2150ec;
}
}
}
&-number {
width: 36rpx;
height: 36rpx;
border-radius: 999px;
background-color: black;
color: $themeColor;
text-align: center;
line-height: 36rpx;
font-size: 12px;
}
&-text {
color: white;
font-size: 12px;
line-height: 36rpx;
padding: 0 8rpx;
}
}
&-fullscreen {
font-family: iconfont;
font-size: 24rpx;
width: 36rpx;
height: 36rpx;
border-radius: 999px;
background-color: $themeColor;
text-align: center;
line-height: 36rpx;
}
}
.menu-list {
position: absolute;
right: 30rpx;
top: 60rpx;
bottom: 70rpx;
background-color: #333333;
border-radius: 20rpx;
flex-direction: column;
padding: 10rpx 20rpx;
transition-property: transform, opacity;
transition-duration: 0.3s;
transition-timing-function: linear;
// transform: translateX(200%);
// opacity: 0;
&-item {
margin-bottom: 10rpx;
flex-direction: row;
justify-content: space-between;
align-items: center;
&-text {
color: white;
font-size: 12px;
}
&-icon {
margin-left: 30rpx;
font-family: iconfont;
color: $themeColor;
font-size: 12px;
}
}
}
</style>
<template>
<view style="flex: auto">
<!-- :style="{ width: `${videoWidth}rpx`, height: `${videoHeight}rpx` }" -->
<video
id="video"
src="https://ygg-all.oss-cn-zhangjiakou.aliyuncs.com/app/video/xuanchuan.mp4"
class="relative"
:style="{ width: `${videoWidth}rpx`, height: `${videoHeight}rpx` }"
:controls="false"
:enable-play-gesture="true"
:autoplay="false"
:vslide-gesture-in-fullscreen="false"
:advanced="advanced"
@controlstoggle="onControlstoggle"
@loadedmetadata="loadedmetadata"
@fullscreenchange="onFullscreenChange"
@play="isPlay = true"
@pause="isPlay = false"
@ended="isPlay = false"
@timeupdate="onTimeUpdate"
@longpress="setPlaybackRate(2)"
@touchend="setPlaybackRate(1)"
@fullscreenclick="test"
>
<view class="controls-head" :style="{ transform: `translateY(${showControls ? 0 : '-100%'})`, position: isFullScreen ? 'fixed' : 'absolute' }">
<text class="controls-head-title" @longpress="onClick">标题</text>
<view class="controls-head-capsules" @tap="showMenu = !showMenu">
<text @longpress="onClick" class="controls-head-capsules-text" style="color: white">章节</text>
<text class="controls-head-capsules-icon"></text>
</view>
</view>
<view class="controls-bar" @tap.stop.prevent :style="{ transform: `translateY(${showControls ? 0 : '100%'})`, position: isFullScreen ? 'fixed' : 'absolute' }">
<text class="controls-bar-play" @tap="onPlay">{{ isPlay ? '' : '' }}</text>
<text class="controls-bar-current-time">{{ formatTime(videoCurrentTime) }}</text>
<view style="flex: 1" @tap.stop.prevent>
<slider
:value="videoCurrentTime"
:min="0"
:max="videoDuration"
:step="1"
:block-size="10"
@changing="handerSilderChanging($event.detail.value)"
@change="handerSilderChange($event.detail.value)"
/>
</view>
<text class="controls-bar-duration">{{ formatTime(videoDuration) }}</text>
<view class="controls-bar-rate" @tap="showRates = !showRates">
<text class="controls-bar-rate-number">{{ currentRate.toFixed(1) }}</text>
<text class="controls-bar-rate-text">倍速</text>
</view>
<text class="controls-bar-fullscreen" @tap.stop="onFullScreen()">{{ isFullScreen ? '' : '' }}</text>
</view>
<view
class="controls-bar-rate-list"
:style="{
transform: `translateX(${showControls && showRates ? 0 : '200%'})`,
opacity: showControls && showRates ? 1 : 0,
position: isFullScreen ? 'fixed' : 'absolute'
}"
>
<text class="controls-bar-rate-list-number" v-for="(item, index) in rates" :key="index" :class="{ avtion: item === currentRate }" @tap="setPlaybackRate(item)">
{{ item.toFixed(1) }}
</text>
</view>
<view
class="menu-list"
:style="{
transform: `translateX(${showControls && showMenu ? 0 : '200%'})`,
opacity: showControls && showMenu ? 1 : 0,
position: isFullScreen ? 'fixed' : 'absolute'
}"
>
<list :show-scrollbar="false">
<cell v-for="i in 10" :key="i">
<view class="menu-list-item">
<text class="menu-list-item-text">知识点{{ i }}:xxxx</text>
<!--  -->
<text class="menu-list-item-icon"></text>
</view>
</cell>
</list>
</view>
</video>
</view>
</template>
<script setup>
import { ref, onBeforeMount, watch } from 'vue';
import { onReady } from '@dcloudio/uni-app';
const advanced = ref([
{
key: 'auto_convert',
value: 1,
type: 'format'
}
]);
const video = ref(null);
const videoWidth = ref(750);
const videoHeight = ref(421);
const videoDuration = ref(0);
const videoCurrentTime = ref(0);
const isFullScreen = ref(false);
const isPlay = ref(false);
const rates = ref([0.5, 1, 1.5, 2]);
const currentRate = ref(1);
const showRates = ref(false);
const showControls = ref(false);
const showMenu = ref(false);
const changing = ref(false);
const handerSilderChanging = (v) => {
changing.value = true;
videoCurrentTime.value = v;
};
const handerSilderChange = (v) => {
changing.value = false;
video.value.seek(v);
};
const onControlstoggle = ({ detail }) => {
showControls.value = detail.show;
};
const onClick = (e) => {
console.log(e);
};
const setPlaybackRate = (rate) => {
video.value.playbackRate(rate);
currentRate.value = rate;
showRates.value = false;
};
const loadedmetadata = ({ detail }) => {
console.log(detail);
const { width, height, duration } = detail;
videoWidth.value = width;
videoHeight.value = height;
videoDuration.value = duration;
};
const onPlay = () => {
if (isPlay.value) {
video.value.pause();
} else {
video.value.play();
}
};
const onFullscreenChange = (e) => {
const { fullScreen } = e.detail;
isFullScreen.value = fullScreen;
};
const onFullScreen = (direction = 'horizontal') => {
if (isFullScreen.value) {
video.value.exitFullScreen();
} else {
video.value.requestFullScreen();
}
};
const onTimeUpdate = ({ detail }) => {
const { currentTime, duration } = detail;
if (changing.value) return;
videoCurrentTime.value = currentTime;
videoDuration.value = duration;
};
const test = (e) => {
console.log(e);
// const { screenWidth, screenHeight } = e.detail;
// videoWidth.value = screenWidth;
// videoHeight.value = screenHeight;
};
const formatTime = (seconds) => {
if (isNaN(seconds) || seconds < 0) return '00:00';
seconds = Math.floor(seconds);
const h = Math.floor(seconds / 3600);
const m = Math.floor((seconds % 3600) / 60);
const s = seconds % 60;
if (h > 0) {
// 大于 1 小时 → 00:00:00
return `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`;
} else {
// 小于 1 小时 → 00:00
return `${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`;
}
};
watch(
() => ({
controls: showControls.value,
fullscreen: isFullScreen.value
}),
(newVal, oldVal) => {
showRates.value = false;
showMenu.value = false;
}
);
onBeforeMount(() => {
// #ifdef APP-PLUS-NVUE
const domModule = uni.requireNativePlugin('dom');
domModule.addRule('fontFace', {
fontFamily: 'iconfont',
src: "url('/static/font/iconfont.ttf')"
});
// #endif
});
onReady(() => {
video.value = uni.createVideoContext('video');
});
</script>
<style scoped lang="scss">
$themeColor: #2150ec;
.controls-head {
position: absolute;
top: 0;
left: 0;
right: 0;
flex-direction: row;
justify-content: space-between;
padding: 10rpx 30rpx;
background-image: linear-gradient(to bottom, black, transparent);
transition-property: transform, background-color;
transition-duration: 0.3s;
transition-timing-function: linear;
&-title {
color: white;
font-size: 14px;
}
&-capsules {
flex-direction: row;
align-items: center;
justify-content: center;
&-text {
color: white;
font-size: 12px;
}
&-icon {
font-family: iconfont;
font-size: 30rpx;
background-color: $themeColor;
border-radius: 999px;
margin-left: 4px;
text-align: center;
width: 36rpx;
height: 36rpx;
line-height: 36rpx;
}
}
}
.controls-bar {
position: absolute;
bottom: 0;
left: 0;
right: 0;
flex-direction: row;
justify-content: space-between;
align-items: center;
padding: 10rpx 30rpx;
background-image: linear-gradient(to top, black, transparent);
transition-property: transform, background-color;
transition-duration: 0.3s;
transition-timing-function: linear;
&-play {
font-family: iconfont;
font-size: 50rpx;
color: $themeColor;
}
&-current-time,
&-duration {
color: white;
font-size: 14px;
padding: 0 20rpx;
}
&-rate {
flex-direction: row;
background-color: $themeColor;
border-radius: 999px;
padding: 2px;
margin-right: 20rpx;
position: relative;
&-list {
position: absolute;
bottom: 70rpx;
right: 90rpx;
background-color: rgba(255, 255, 255, 0.5);
width: 100rpx;
border-radius: 10rpx;
transition-property: transform, opacity;
transition-duration: 0.3s;
transition-timing-function: linear;
// opacity: 0;
// transform: translateY(150rpx);
&-number {
text-align: center;
border-radius: 10rpx;
&.avtion {
background-color: #2150ec;
}
}
}
&-number {
width: 36rpx;
height: 36rpx;
border-radius: 999px;
background-color: black;
color: $themeColor;
text-align: center;
line-height: 36rpx;
font-size: 12px;
}
&-text {
color: white;
font-size: 12px;
line-height: 36rpx;
padding: 0 8rpx;
}
}
&-fullscreen {
font-family: iconfont;
font-size: 24rpx;
width: 36rpx;
height: 36rpx;
border-radius: 999px;
background-color: $themeColor;
text-align: center;
line-height: 36rpx;
}
}
.menu-list {
position: absolute;
right: 30rpx;
top: 60rpx;
bottom: 70rpx;
background-color: #333333;
border-radius: 20rpx;
flex-direction: column;
padding: 10rpx 20rpx;
transition-property: transform, opacity;
transition-duration: 0.3s;
transition-timing-function: linear;
// transform: translateX(200%);
// opacity: 0;
&-item {
margin-bottom: 10rpx;
flex-direction: row;
justify-content: space-between;
align-items: center;
&-text {
color: white;
font-size: 12px;
}
&-icon {
margin-left: 30rpx;
font-family: iconfont;
color: $themeColor;
font-size: 12px;
}
}
}
</style> 预期结果:
@longpress事件 全屏的时候也生效,能实现长按倍数
在自定义ui,使用slider 组件当进度条 iOS 触摸乱跳触碰就直接到最大值了
@longpress事件 全屏的时候也生效,能实现长按倍数
在自定义ui,使用slider 组件当进度条 iOS 触摸乱跳触碰就直接到最大值了
实际结果:
@loadedmetadata事件不触发
@longpress事件 全屏无效
自定义ui,slider组件触摸乱跳
@loadedmetadata事件不触发
@longpress事件 全屏无效
自定义ui,slider组件触摸乱跳
bug描述:
video 组件
@loadedmetadata事件不触发
@longpress事件 全屏无效
自定义ui,slider组件触摸乱跳
0 个回复