Files
2025-06-10 10:50:25 +08:00

190 lines
4.8 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<!-- 小窗播放容器 -->
<view
v-if="visible"
class="mini-player"
:style="{
left: position.x + 'px',
top: position.y + 'px',
width: isFullScreen ? '100%' : videoWidth + 'rpx',
height: isFullScreen ? '100vh' : videoHeight + 'rpx',
}"
@touchstart="handleTouchStart"
@touchmove="handleTouchMove"
@touchend="handleTouchEnd"
@touchmove.stop.prevent
>
<!-- 视频组件 -->
<video
:src="videoUrl"
:controls="true"
:show-progress="isFullScreen"
:style="{
width: '100%',
height: '100%',
}"
id="myVideo"
ref="videoRef"
@play="onPlay"
@pause="onPause"
@loadedmetadata="onLoadedMetadata"
></video>
<!-- 控制栏 -->
<view class="controls">
<!-- <text @click="togglePlay">{{ isPlaying ? '暂停' : '播放' }}</text>
<text @click="toggleFullScreen">{{ isFullScreen ? '退出全屏' : '全屏' }}</text> -->
<text @click="close">关闭</text>
</view>
</view>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue';
import { nextTick } from 'vue';
const videoRef = ref(null);
const visible = ref(false);
const isPlaying = ref(false);
const isFullScreen = ref(false);
const videoUrl = ref('');
const position = reactive({ x: 20, y: 100 });
const videoContext = ref(null);
const startPos = reactive({ x: 0, y: 0 });
const moving = ref(false);
const videoWidth = ref(0);
const videoHeight = ref(0);
// 初始化视频上下文
onMounted(() => {
videoContext.value = uni.createVideoContext('myVideo');
});
// 触摸开始
const handleTouchStart = (e) => {
if (isFullScreen.value) return;
startPos.x = e.touches[0].clientX - position.x;
startPos.y = e.touches[0].clientY - position.y;
moving.value = true;
};
// 触摸移动
const handleTouchMove = (e) => {
if (!moving.value || isFullScreen.value) return;
const newX = e.touches[0].clientX - startPos.x;
const newY = e.touches[0].clientY - startPos.y;
// 边界检测
const maxX = window.innerWidth - videoWidth.value / 2; // 300rpx换算后的值
const maxY = window.innerHeight - videoHeight.value / 2;
position.x = Math.max(0, Math.min(newX, maxX));
position.y = Math.max(0, Math.min(newY, maxY));
};
// 触摸结束
const handleTouchEnd = () => {
moving.value = false;
};
// 播放状态变化
const onPlay = () => {
isPlaying.value = true;
};
const onPause = () => {
isPlaying.value = false;
};
// 切换播放状态
const togglePlay = () => {
isPlaying.value ? videoContext.value.pause() : videoContext.value.play();
};
// 切换全屏
const toggleFullScreen = () => {
isFullScreen.value = !isFullScreen.value;
if (!isFullScreen.value) {
// 退出全屏时恢复原位
position.x = 20;
position.y = 100;
}
};
// 关闭播放器
const close = () => {
visible.value = false;
videoContext.value.stop();
isPlaying.value = false;
isFullScreen.value = false;
};
// 打开播放器方法
const open = (url) => {
videoUrl.value = url;
visible.value = true;
nextTick(() => {
videoContext.value.play();
});
};
const onLoadedMetadata = (e) => {
const video = e.detail;
const width = video.width;
const height = video.height;
const ratio = width / height;
// 设置宽度:横屏宽 300竖屏宽 180可自定义
if (ratio >= 1) {
videoWidth.value = 300; // 横屏
videoHeight.value = 300 * (height / width); // 保持比例
} else {
videoWidth.value = 180; // 竖屏
videoHeight.value = 180 * (height / width); // 保持比例
}
// console.log(`宽高: ${width}x${height}`);
// console.log(`比例: ${ratio.toFixed(2)} (${getRatioName(ratio)})`);
};
function getRatioName(ratio) {
const rounded = Math.round(ratio * 100) / 100;
if (Math.abs(rounded - 16 / 9) < 0.01) return '16:9';
if (Math.abs(rounded - 4 / 3) < 0.01) return '4:3';
if (Math.abs(rounded - 1) < 0.01) return '1:1';
return `${rounded.toFixed(2)}:1`;
}
// 暴露方法
defineExpose({ open });
</script>
<style scoped>
.mini-player {
position: fixed;
z-index: 999;
background: #000;
border-radius: 8rpx;
overflow: hidden;
box-shadow: 0 0 20rpx rgba(0, 0, 0, 0.3);
}
.controls {
position: absolute;
top: 0;
right: 0;
/* background: rgba(0, 0, 0, 0.7); */
padding: 10rpx;
display: flex;
justify-content: space-around;
}
.controls text {
color: #fff;
font-size: 24rpx;
padding: 8rpx 16rpx;
background: rgba(255, 255, 255, 0.2);
border-radius: 4rpx;
}
</style>