mirror of
https://gh-proxy.org/https://github.com/tiajinsha/JKVideo
synced 2026-07-08 23:43:52 +08:00
fix: BigVideoCard stale closure, visibility guard, mediaContainer positioning
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
View, Text, Image, TouchableOpacity, StyleSheet,
|
View, Text, Image, TouchableOpacity, StyleSheet,
|
||||||
Dimensions, Animated,
|
useWindowDimensions, Animated,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import Video from 'react-native-video';
|
import Video from 'react-native-video';
|
||||||
import { Ionicons } from '@expo/vector-icons';
|
import { Ionicons } from '@expo/vector-icons';
|
||||||
@@ -12,9 +12,6 @@ import { proxyImageUrl } from '../utils/imageUrl';
|
|||||||
import { formatCount, formatDuration } from '../utils/format';
|
import { formatCount, formatDuration } from '../utils/format';
|
||||||
import type { VideoItem } from '../services/types';
|
import type { VideoItem } from '../services/types';
|
||||||
|
|
||||||
const { width: SCREEN_W } = Dimensions.get('window');
|
|
||||||
const THUMB_H = SCREEN_W * 0.5625; // 16:9
|
|
||||||
|
|
||||||
const HEADERS = {
|
const HEADERS = {
|
||||||
Referer: 'https://www.bilibili.com',
|
Referer: 'https://www.bilibili.com',
|
||||||
'User-Agent':
|
'User-Agent':
|
||||||
@@ -28,11 +25,22 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function BigVideoCard({ item, isVisible, onPress }: Props) {
|
export function BigVideoCard({ item, isVisible, onPress }: Props) {
|
||||||
|
const { width: SCREEN_W } = useWindowDimensions();
|
||||||
|
const THUMB_H = SCREEN_W * 0.5625;
|
||||||
|
|
||||||
const [videoUrl, setVideoUrl] = useState<string | undefined>();
|
const [videoUrl, setVideoUrl] = useState<string | undefined>();
|
||||||
const [isDash, setIsDash] = useState(false);
|
const [isDash, setIsDash] = useState(false);
|
||||||
const [paused, setPaused] = useState(true);
|
const [paused, setPaused] = useState(true);
|
||||||
const thumbOpacity = useRef(new Animated.Value(1)).current;
|
const thumbOpacity = useRef(new Animated.Value(1)).current;
|
||||||
|
|
||||||
|
// Reset video state when the item changes
|
||||||
|
useEffect(() => {
|
||||||
|
setVideoUrl(undefined);
|
||||||
|
setIsDash(false);
|
||||||
|
setPaused(true);
|
||||||
|
thumbOpacity.setValue(1);
|
||||||
|
}, [item.bvid]);
|
||||||
|
|
||||||
// Fetch play URL when visible for the first time
|
// Fetch play URL when visible for the first time
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isVisible || videoUrl) return;
|
if (!isVisible || videoUrl) return;
|
||||||
@@ -64,7 +72,7 @@ export function BigVideoCard({ item, isVisible, onPress }: Props) {
|
|||||||
console.warn('BigVideoCard: failed to load play URL', e);
|
console.warn('BigVideoCard: failed to load play URL', e);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
}, [isVisible]);
|
}, [isVisible, item.bvid]);
|
||||||
|
|
||||||
// Pause/resume when visibility changes
|
// Pause/resume when visibility changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -77,6 +85,7 @@ export function BigVideoCard({ item, isVisible, onPress }: Props) {
|
|||||||
}, [isVisible, videoUrl]);
|
}, [isVisible, videoUrl]);
|
||||||
|
|
||||||
const handleVideoReady = () => {
|
const handleVideoReady = () => {
|
||||||
|
if (!isVisible) return;
|
||||||
setPaused(false);
|
setPaused(false);
|
||||||
Animated.timing(thumbOpacity, {
|
Animated.timing(thumbOpacity, {
|
||||||
toValue: 0,
|
toValue: 0,
|
||||||
@@ -88,12 +97,12 @@ export function BigVideoCard({ item, isVisible, onPress }: Props) {
|
|||||||
return (
|
return (
|
||||||
<TouchableOpacity style={styles.card} onPress={onPress} activeOpacity={0.9}>
|
<TouchableOpacity style={styles.card} onPress={onPress} activeOpacity={0.9}>
|
||||||
{/* Media area */}
|
{/* Media area */}
|
||||||
<View style={styles.mediaContainer}>
|
<View style={{ width: SCREEN_W - 8, height: THUMB_H, position: 'relative' }}>
|
||||||
{/* Thumbnail */}
|
{/* Thumbnail */}
|
||||||
<Animated.View style={[StyleSheet.absoluteFill, { opacity: thumbOpacity }]}>
|
<Animated.View style={[StyleSheet.absoluteFill, { opacity: thumbOpacity }]}>
|
||||||
<Image
|
<Image
|
||||||
source={{ uri: proxyImageUrl(item.pic) }}
|
source={{ uri: proxyImageUrl(item.pic) }}
|
||||||
style={styles.thumb}
|
style={{ width: SCREEN_W - 8, height: THUMB_H }}
|
||||||
resizeMode="cover"
|
resizeMode="cover"
|
||||||
/>
|
/>
|
||||||
</Animated.View>
|
</Animated.View>
|
||||||
@@ -143,14 +152,6 @@ const styles = StyleSheet.create({
|
|||||||
borderRadius: 6,
|
borderRadius: 6,
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
},
|
},
|
||||||
mediaContainer: {
|
|
||||||
width: SCREEN_W - 8,
|
|
||||||
height: THUMB_H,
|
|
||||||
},
|
|
||||||
thumb: {
|
|
||||||
width: SCREEN_W - 8,
|
|
||||||
height: THUMB_H,
|
|
||||||
},
|
|
||||||
durationBadge: {
|
durationBadge: {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
bottom: 4,
|
bottom: 4,
|
||||||
|
|||||||
Reference in New Issue
Block a user