mirror of
https://gh-proxy.org/https://github.com/tiajinsha/JKVideo
synced 2026-07-07 23:18:38 +08:00
feat: 直播小窗播放(PiP)功能
- 新增 store/liveStore.ts:Zustand store,存储小窗直播状态(roomId/title/cover/hlsUrl) - 新增 components/LiveMiniPlayer.tsx:可拖动悬浮迷你直播播放器 - 原生端实际播放 HLS 流(react-native-video,有声) - Web 端降级展示封面图 + LIVE 徽标 - LIVE 红点徽标 + 标题条底部 - 关闭按钮(×)和点击进入直播间 - HLS 出错时自动关闭(onError → clearLive) - 视频 MiniPlayer 激活时自动上移避免重叠 - _layout.tsx:全局挂载 <LiveMiniPlayer /> - live/[roomId].tsx:顶部栏添加小窗按钮(browsers-outline) - 仅直播中且有流地址时显示,离线时占位保持 title 居中 - 点击后 setLive + router.back(),返回首页继续看直播 - 进入同房间时自动 clearLive() 避免双播
This commit is contained in:
@@ -8,6 +8,7 @@ import { useDownloadStore } from '../store/downloadStore';
|
||||
import { useSettingsStore } from '../store/settingsStore';
|
||||
import { useTheme } from '../utils/theme';
|
||||
import { MiniPlayer } from '../components/MiniPlayer';
|
||||
import { LiveMiniPlayer } from '../components/LiveMiniPlayer';
|
||||
import * as Sentry from '@sentry/react-native';
|
||||
import { ErrorBoundary } from '@sentry/react-native';
|
||||
|
||||
@@ -80,6 +81,7 @@ function RootLayout() {
|
||||
</Stack>
|
||||
</ErrorBoundary>
|
||||
<MiniPlayer />
|
||||
<LiveMiniPlayer />
|
||||
</View>
|
||||
</SafeAreaProvider>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState } from "react";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import {
|
||||
View,
|
||||
Text,
|
||||
@@ -18,6 +18,7 @@ import DanmakuList from "../../components/DanmakuList";
|
||||
import { formatCount } from "../../utils/format";
|
||||
import { proxyImageUrl } from "../../utils/imageUrl";
|
||||
import { useTheme } from "../../utils/theme";
|
||||
import { useLiveStore } from "../../store/liveStore";
|
||||
|
||||
type Tab = "intro" | "danmaku";
|
||||
|
||||
@@ -36,6 +37,15 @@ export default function LiveDetailScreen() {
|
||||
const qualities = stream?.qualities ?? [];
|
||||
const currentQn = stream?.qn ?? 0;
|
||||
|
||||
const { roomId: miniRoomId, setLive, clearLive } = useLiveStore();
|
||||
|
||||
// 进入该直播间时,若小窗正在播放同一房间,清除小窗避免双播
|
||||
useEffect(() => {
|
||||
if (miniRoomId === id) {
|
||||
clearLive();
|
||||
}
|
||||
}, [id, miniRoomId]);
|
||||
|
||||
const actualRoomId = room?.roomid ?? id;
|
||||
const { danmakus, giftCounts } = useLiveDanmaku(isLive ? actualRoomId : 0);
|
||||
|
||||
@@ -49,6 +59,19 @@ export default function LiveDetailScreen() {
|
||||
<Text style={[styles.topTitle, { color: theme.text }]} numberOfLines={1}>
|
||||
{room?.title ?? "直播间"}
|
||||
</Text>
|
||||
{isLive && hlsUrl ? (
|
||||
<TouchableOpacity
|
||||
style={styles.pipBtn}
|
||||
onPress={() => {
|
||||
setLive(id, room?.title ?? '', room?.keyframe ?? '', hlsUrl);
|
||||
router.back();
|
||||
}}
|
||||
>
|
||||
<Ionicons name="browsers-outline" size={22} color={theme.text} />
|
||||
</TouchableOpacity>
|
||||
) : (
|
||||
<View style={styles.pipBtn} />
|
||||
)}
|
||||
</View>
|
||||
|
||||
{/* Player */}
|
||||
@@ -177,6 +200,7 @@ const styles = StyleSheet.create({
|
||||
borderBottomWidth: StyleSheet.hairlineWidth,
|
||||
},
|
||||
backBtn: { padding: 4 },
|
||||
pipBtn: { padding: 4, width: 32, alignItems: 'center' },
|
||||
topTitle: {
|
||||
flex: 1,
|
||||
fontSize: 15,
|
||||
|
||||
Reference in New Issue
Block a user