feat: fade and slide in controls (#964)

This commit is contained in:
Fredrik Burmester
2025-08-20 21:59:09 +02:00
committed by GitHub
parent f2219a1daa
commit 55ce7d8cec
5 changed files with 133 additions and 84 deletions

View File

@@ -117,7 +117,6 @@ export const BottomControls: FC<BottomControlsProps> = ({
style={{
flexDirection: "column",
alignSelf: "flex-end",
opacity: showControls ? 1 : 0,
}}
pointerEvents={showControls ? "box-none" : "none"}
>
@@ -164,9 +163,6 @@ export const BottomControls: FC<BottomControlsProps> = ({
</View>
<View
className={"flex flex-col-reverse rounded-lg items-center my-2"}
style={{
opacity: showControls ? 1 : 0,
}}
pointerEvents={showControls ? "box-none" : "none"}
>
<View className={"flex flex-col w-full shrink"}>

View File

@@ -55,7 +55,6 @@ export const CenterControls: FC<CenterControlsProps> = ({
transform: [{ rotate: "270deg" }],
left: 0,
bottom: 30,
opacity: showControls ? 1 : 0,
}}
>
<BrightnessSlider />
@@ -68,7 +67,6 @@ export const CenterControls: FC<CenterControlsProps> = ({
position: "relative",
justifyContent: "center",
alignItems: "center",
opacity: showControls ? 1 : 0,
}}
>
<Ionicons
@@ -101,9 +99,6 @@ export const CenterControls: FC<CenterControlsProps> = ({
name={isPlaying ? "pause" : "play"}
size={ICON_SIZES.CENTER}
color='white'
style={{
opacity: showControls ? 1 : 0,
}}
/>
) : (
<Loader size={"large"} />
@@ -118,7 +113,6 @@ export const CenterControls: FC<CenterControlsProps> = ({
position: "relative",
justifyContent: "center",
alignItems: "center",
opacity: showControls ? 1 : 0,
}}
>
<Ionicons

View File

@@ -13,10 +13,13 @@ import {
useState,
} from "react";
import { useWindowDimensions } from "react-native";
import {
import Animated, {
Easing,
type SharedValue,
useAnimatedReaction,
useAnimatedStyle,
useSharedValue,
withTiming,
} from "react-native-reanimated";
import ContinueWatchingOverlay from "@/components/video-player/controls/ContinueWatchingOverlay";
import { useCreditSkipper } from "@/hooks/useCreditSkipper";
@@ -130,10 +133,61 @@ export const Controls: FC<Props> = ({
const min = useSharedValue(0);
const max = useSharedValue(item.RunTimeTicks || 0);
// Animation values for controls
const controlsOpacity = useSharedValue(showControls ? 1 : 0);
const headerTranslateY = useSharedValue(showControls ? 0 : -50);
const bottomTranslateY = useSharedValue(showControls ? 0 : 50);
useEffect(() => {
prefetchAllTrickplayImages();
}, [prefetchAllTrickplayImages]);
// Animate controls visibility
useEffect(() => {
const animationConfig = {
duration: 300,
easing: Easing.out(Easing.quad),
};
controlsOpacity.value = withTiming(showControls ? 1 : 0, animationConfig);
headerTranslateY.value = withTiming(
showControls ? 0 : -10,
animationConfig,
);
bottomTranslateY.value = withTiming(showControls ? 0 : 10, animationConfig);
}, [showControls, controlsOpacity, headerTranslateY, bottomTranslateY]);
// Create animated styles
const headerAnimatedStyle = useAnimatedStyle(() => ({
opacity: controlsOpacity.value,
transform: [{ translateY: headerTranslateY.value }],
position: "absolute" as const,
top: 0,
left: 0,
right: 0,
zIndex: 10,
}));
const centerAnimatedStyle = useAnimatedStyle(() => ({
opacity: controlsOpacity.value,
position: "absolute" as const,
top: 0,
left: 0,
right: 0,
bottom: 0,
zIndex: 5,
}));
const bottomAnimatedStyle = useAnimatedStyle(() => ({
opacity: controlsOpacity.value,
transform: [{ translateY: bottomTranslateY.value }],
position: "absolute" as const,
bottom: 0,
left: 0,
right: 0,
zIndex: 10,
}));
// Initialize progress values
useEffect(() => {
if (item) {
@@ -435,68 +489,83 @@ export const Controls: FC<Props> = ({
showControls={showControls}
onToggleControls={toggleControls}
/>
<HeaderControls
item={item}
showControls={showControls}
offline={offline}
mediaSource={mediaSource}
startPictureInPicture={startPictureInPicture}
switchOnEpisodeMode={switchOnEpisodeMode}
goToPreviousItem={goToPreviousItem}
goToNextItem={goToNextItem}
previousItem={previousItem}
nextItem={nextItem}
getAudioTracks={getAudioTracks}
getSubtitleTracks={getSubtitleTracks}
setAudioTrack={setAudioTrack}
setSubtitleTrack={setSubtitleTrack}
setSubtitleURL={setSubtitleURL}
aspectRatio={aspectRatio}
scaleFactor={scaleFactor}
setAspectRatio={setAspectRatio}
setScaleFactor={setScaleFactor}
setVideoAspectRatio={setVideoAspectRatio}
setVideoScaleFactor={setVideoScaleFactor}
/>
<CenterControls
showControls={showControls}
isPlaying={isPlaying}
isBuffering={isBuffering}
showAudioSlider={showAudioSlider}
setShowAudioSlider={setShowAudioSlider}
togglePlay={togglePlay}
handleSkipBackward={handleSkipBackward}
handleSkipForward={handleSkipForward}
/>
<BottomControls
item={item}
showControls={showControls}
isSliding={isSliding}
showRemoteBubble={showRemoteBubble}
currentTime={currentTime}
remainingTime={remainingTime}
isVlc={isVlc}
showSkipButton={showSkipButton}
showSkipCreditButton={showSkipCreditButton}
skipIntro={skipIntro}
skipCredit={skipCredit}
nextItem={nextItem}
handleNextEpisodeAutoPlay={handleNextEpisodeAutoPlay}
handleNextEpisodeManual={handleNextEpisodeManual}
handleControlsInteraction={handleControlsInteraction}
min={min}
max={max}
effectiveProgress={effectiveProgress}
cacheProgress={cacheProgress}
handleSliderStart={handleSliderStart}
handleSliderComplete={handleSliderComplete}
handleSliderChange={handleSliderChange}
handleTouchStart={handleTouchStart}
handleTouchEnd={handleTouchEnd}
trickPlayUrl={trickPlayUrl}
trickplayInfo={trickplayInfo}
time={isSliding || showRemoteBubble ? time : remoteTime}
/>
<Animated.View
style={headerAnimatedStyle}
pointerEvents={showControls ? "auto" : "none"}
>
<HeaderControls
item={item}
showControls={showControls}
offline={offline}
mediaSource={mediaSource}
startPictureInPicture={startPictureInPicture}
switchOnEpisodeMode={switchOnEpisodeMode}
goToPreviousItem={goToPreviousItem}
goToNextItem={goToNextItem}
previousItem={previousItem}
nextItem={nextItem}
getAudioTracks={getAudioTracks}
getSubtitleTracks={getSubtitleTracks}
setAudioTrack={setAudioTrack}
setSubtitleTrack={setSubtitleTrack}
setSubtitleURL={setSubtitleURL}
aspectRatio={aspectRatio}
scaleFactor={scaleFactor}
setAspectRatio={setAspectRatio}
setScaleFactor={setScaleFactor}
setVideoAspectRatio={setVideoAspectRatio}
setVideoScaleFactor={setVideoScaleFactor}
/>
</Animated.View>
<Animated.View
style={centerAnimatedStyle}
pointerEvents={showControls ? "box-none" : "none"}
>
<CenterControls
showControls={showControls}
isPlaying={isPlaying}
isBuffering={isBuffering}
showAudioSlider={showAudioSlider}
setShowAudioSlider={setShowAudioSlider}
togglePlay={togglePlay}
handleSkipBackward={handleSkipBackward}
handleSkipForward={handleSkipForward}
/>
</Animated.View>
<Animated.View
style={bottomAnimatedStyle}
pointerEvents={showControls ? "auto" : "none"}
>
<BottomControls
item={item}
showControls={showControls}
isSliding={isSliding}
showRemoteBubble={showRemoteBubble}
currentTime={currentTime}
remainingTime={remainingTime}
isVlc={isVlc}
showSkipButton={showSkipButton}
showSkipCreditButton={showSkipCreditButton}
skipIntro={skipIntro}
skipCredit={skipCredit}
nextItem={nextItem}
handleNextEpisodeAutoPlay={handleNextEpisodeAutoPlay}
handleNextEpisodeManual={handleNextEpisodeManual}
handleControlsInteraction={handleControlsInteraction}
min={min}
max={max}
effectiveProgress={effectiveProgress}
cacheProgress={cacheProgress}
handleSliderStart={handleSliderStart}
handleSliderComplete={handleSliderComplete}
handleSliderChange={handleSliderChange}
handleTouchStart={handleTouchStart}
handleTouchEnd={handleTouchEnd}
trickPlayUrl={trickPlayUrl}
trickplayInfo={trickplayInfo}
time={isSliding || showRemoteBubble ? time : remoteTime}
/>
</Animated.View>
</>
)}
{settings.maxAutoPlayEpisodeCount.value !== -1 && (

View File

@@ -106,7 +106,6 @@ export const HeaderControls: FC<HeaderControlsProps> = ({
width: settings?.safeAreaInControlsEnabled
? screenWidth - insets.left - insets.right
: screenWidth,
opacity: showControls ? 1 : 0,
},
]}
pointerEvents={showControls ? "auto" : "none"}
@@ -138,7 +137,6 @@ export const HeaderControls: FC<HeaderControlsProps> = ({
name='picture-in-picture'
size={ICON_SIZES.HEADER}
color='white'
style={{ opacity: showControls ? 1 : 0 }}
/>
</TouchableOpacity>
)}