feat: add danmaku overlay + time tracking to NativeVideoPlayer

This commit is contained in:
Developer
2026-03-10 22:50:48 +08:00
parent d1b66541d2
commit 85ceec8b58

View File

@@ -1,17 +1,16 @@
import React, { useState, useRef, useEffect, useCallback } from 'react';
import {
View, StyleSheet, Dimensions, TouchableOpacity, TouchableWithoutFeedback,
Text, Modal, Image, PanResponder,
View, StyleSheet, TouchableOpacity, TouchableWithoutFeedback,
Text, Modal, Image, PanResponder, useWindowDimensions,
} from 'react-native';
import Video, { VideoRef } from 'react-native-video';
import { LinearGradient } from 'expo-linear-gradient';
import { Ionicons } from '@expo/vector-icons';
import type { PlayUrlResponse, VideoShotData } from '../services/types';
import type { PlayUrlResponse, VideoShotData, DanmakuItem } from '../services/types';
import { buildDashMpdUri } from '../utils/dash';
import { getHeatmap, getVideoShot } from '../services/bilibili';
import { DanmakuOverlay } from './DanmakuOverlay';
const { width: SCREEN_W } = Dimensions.get('window');
const VIDEO_H = SCREEN_W * 0.5625;
const BAR_H = 3;
const BALL = 12;
const BALL_ACTIVE = 16;
@@ -80,12 +79,19 @@ interface Props {
style?: object;
bvid?: string;
cid?: number;
danmakus?: DanmakuItem[];
isFullscreen?: boolean;
onTimeUpdate?: (t: number) => void;
initialTime?: number;
}
export function NativeVideoPlayer({
playData, qualities, currentQn, onQualityChange, onFullscreen, onMiniPlayer, style,
bvid, cid,
bvid, cid, danmakus, isFullscreen, onTimeUpdate, initialTime,
}: Props) {
const { width: SCREEN_W, height: SCREEN_H } = useWindowDimensions();
const VIDEO_H = SCREEN_W * 0.5625;
const [resolvedUrl, setResolvedUrl] = useState<string | undefined>();
const isDash = !!playData?.dash;
@@ -103,11 +109,12 @@ export function NativeVideoPlayer({
const isSeekingRef = useRef(false);
const [touchX, setTouchX] = useState<number | null>(null);
const barOffsetX = useRef(0);
const barWidthRef = useRef(SCREEN_W);
const barWidthRef = useRef(300);
const trackRef = useRef<View>(null);
const [heatSegments, setHeatSegments] = useState<number[]>([]);
const [shots, setShots] = useState<VideoShotData | null>(null);
const [showDanmaku, setShowDanmaku] = useState(true);
const videoRef = useRef<VideoRef>(null);
const currentDesc = qualities.find(q => q.qn === currentQn)?.desc ?? String(currentQn || 'HD');
@@ -244,7 +251,7 @@ export function NativeVideoPlayer({
return (
<TouchableWithoutFeedback onPress={handleTap}>
<View style={[styles.container, style]}>
<View style={[styles.container, { width: SCREEN_W, height: VIDEO_H }, style]}>
{resolvedUrl ? (
<Video
key={resolvedUrl}
@@ -260,12 +267,28 @@ export function NativeVideoPlayer({
onProgress={({ currentTime: ct, seekableDuration: dur }) => {
setCurrentTime(ct);
if (dur > 0) setDuration(dur);
onTimeUpdate?.(ct);
}}
onLoad={() => {
if (initialTime && initialTime > 0) {
videoRef.current?.seek(initialTime);
}
}}
/>
) : (
<View style={styles.placeholder} />
)}
{isFullscreen && !!danmakus?.length && (
<DanmakuOverlay
danmakus={danmakus}
currentTime={currentTime}
screenWidth={SCREEN_W}
screenHeight={SCREEN_H}
visible={showDanmaku}
/>
)}
{showControls && (
<>
{/* Top bar */}
@@ -336,6 +359,11 @@ export function NativeVideoPlayer({
<TouchableOpacity style={styles.ctrlBtn} onPress={() => setShowQuality(true)}>
<Text style={styles.qualityText}>{currentDesc}</Text>
</TouchableOpacity>
{isFullscreen && (
<TouchableOpacity style={styles.ctrlBtn} onPress={() => setShowDanmaku(v => !v)}>
<Ionicons name={showDanmaku ? 'chatbubbles' : 'chatbubbles-outline'} size={16} color="#fff" />
</TouchableOpacity>
)}
<TouchableOpacity style={styles.ctrlBtn} onPress={onFullscreen}>
<Ionicons name="expand" size={16} color="#fff" />
</TouchableOpacity>
@@ -370,7 +398,7 @@ export function NativeVideoPlayer({
}
const styles = StyleSheet.create({
container: { width: SCREEN_W, height: VIDEO_H, backgroundColor: '#000' },
container: { backgroundColor: '#000' },
placeholder: { ...StyleSheet.absoluteFillObject, backgroundColor: '#000' },
topBar: {
position: 'absolute', top: 0, left: 0, right: 0, height: 56,