Cross fade
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
import PropTypes from "prop-types";
|
||||
import React, { PureComponent, Children } from "react";
|
||||
import { View, Animated, StyleSheet } from "react-native";
|
||||
|
||||
export default class FadeView extends PureComponent {
|
||||
static defaultProps = {
|
||||
animationDuration: 225,
|
||||
|
||||
active: false,
|
||||
removeHiddenSubviews: true,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
animationDuration: PropTypes.number,
|
||||
|
||||
active: PropTypes.bool,
|
||||
removeHiddenSubviews: PropTypes.bool,
|
||||
|
||||
children: PropTypes.oneOfType([
|
||||
PropTypes.arrayOf(PropTypes.node),
|
||||
PropTypes.node,
|
||||
]),
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.renderChild = this.renderChild.bind(this);
|
||||
this.mounted = false;
|
||||
|
||||
let { active } = this.props;
|
||||
|
||||
this.state = {
|
||||
progress: new Animated.Value(Number(active)),
|
||||
animating: false,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.mounted = true;
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.mounted = false;
|
||||
}
|
||||
|
||||
componentWillReceiveProps({ active, animationDuration }) {
|
||||
let { progress } = this.state;
|
||||
|
||||
if (active ^ this.props.active) {
|
||||
this.setState({ animating: true });
|
||||
|
||||
Animated.timing(progress, {
|
||||
toValue: Number(active),
|
||||
duration: animationDuration,
|
||||
useNativeDriver: true,
|
||||
}).start(() => {
|
||||
if (this.mounted) {
|
||||
this.setState({ animating: false });
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderChild(child, index) {
|
||||
let { active, removeHiddenSubviews } = this.props;
|
||||
let { animating, progress } = this.state;
|
||||
|
||||
let opacity = progress.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: index ? [0, 1] : [1, 0],
|
||||
});
|
||||
|
||||
let hidden = active ^ !!index;
|
||||
|
||||
if (removeHiddenSubviews) {
|
||||
if (!animating && hidden) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
let pointerEvents = hidden ? "none" : "box-none";
|
||||
|
||||
return (
|
||||
<Animated.View
|
||||
style={[styles.container, { opacity }]}
|
||||
pointerEvents={pointerEvents}
|
||||
>
|
||||
{child}
|
||||
</Animated.View>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
let { children, ...props } = this.props;
|
||||
|
||||
return (
|
||||
<View {...props}>{Children.map(children, this.renderChild)}</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
// ...StyleSheet.absoluteFillObject,
|
||||
flexDirection: "row",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
},
|
||||
});
|
||||
@@ -2,6 +2,7 @@ import React, { useState } from "react";
|
||||
import { View } from "react-native";
|
||||
|
||||
import { Appbar } from "react-native-paper";
|
||||
import FadeView from "../../../components/Common/CrossFadeView";
|
||||
import { bookmarkChapterAction } from "../../../redux/novel/novel.actions";
|
||||
|
||||
const ChapterAppbar = ({
|
||||
@@ -16,19 +17,20 @@ const ChapterAppbar = ({
|
||||
}) => {
|
||||
const [bookmarked, setBookmarked] = useState(bookmark);
|
||||
|
||||
if (hide) {
|
||||
return null;
|
||||
} else {
|
||||
return (
|
||||
return (
|
||||
<FadeView
|
||||
style={{
|
||||
flex: 1,
|
||||
position: "absolute",
|
||||
width: "100%",
|
||||
top: 0,
|
||||
zIndex: 1,
|
||||
}}
|
||||
active={hide}
|
||||
animationDuration={150}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
position: "absolute",
|
||||
flex: 1,
|
||||
backgroundColor: `${theme.colorPrimary}E6`,
|
||||
width: "100%",
|
||||
top: 0,
|
||||
zIndex: 1,
|
||||
}}
|
||||
style={{ flex: 1, backgroundColor: `${theme.colorPrimary}E6` }}
|
||||
>
|
||||
<Appbar.Header
|
||||
style={{ backgroundColor: "transparent", elevation: 0 }}
|
||||
@@ -58,8 +60,8 @@ const ChapterAppbar = ({
|
||||
/>
|
||||
</Appbar.Header>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
</FadeView>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChapterAppbar;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from "react";
|
||||
import { Pressable, StyleSheet, View } from "react-native";
|
||||
import { IconButton } from "react-native-paper";
|
||||
import FadeView from "../../../components/Common/CrossFadeView";
|
||||
|
||||
const ChapterFooter = ({
|
||||
hide,
|
||||
@@ -22,10 +23,8 @@ const ChapterFooter = ({
|
||||
radius: 50,
|
||||
};
|
||||
|
||||
if (hide) {
|
||||
return null;
|
||||
} else {
|
||||
return (
|
||||
return (
|
||||
<FadeView active={hide} animationDuration={150}>
|
||||
<View
|
||||
style={{
|
||||
position: "absolute",
|
||||
@@ -109,8 +108,8 @@ const ChapterFooter = ({
|
||||
/>
|
||||
</Pressable>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
</FadeView>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChapterFooter;
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Dimensions, View, Text, StyleSheet } from "react-native";
|
||||
|
||||
import Slider from "@react-native-community/slider";
|
||||
import { getDeviceOrientation } from "../../../services/utils/helpers";
|
||||
import FadeView from "../../../components/Common/CrossFadeView";
|
||||
|
||||
const VerticalScrollbar = ({
|
||||
theme,
|
||||
@@ -22,84 +23,95 @@ const VerticalScrollbar = ({
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
if (hide) {
|
||||
return null;
|
||||
} else if (getDeviceOrientation() === "potrait") {
|
||||
if (getDeviceOrientation() === "potrait") {
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
styles.verticalSliderContainer,
|
||||
{ backgroundColor: `${theme.colorPrimary}E6` },
|
||||
]}
|
||||
<FadeView
|
||||
style={{
|
||||
position: "absolute",
|
||||
zIndex: 2,
|
||||
right: -(Dimensions.get("window").width / 2) + 40,
|
||||
bottom: 300,
|
||||
}}
|
||||
active={hide}
|
||||
animationDuration={150}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
color: theme.textColorPrimary,
|
||||
marginLeft: 16,
|
||||
transform: [{ rotate: "-90deg" }],
|
||||
}}
|
||||
<View
|
||||
style={[
|
||||
styles.verticalSliderContainer,
|
||||
{ backgroundColor: `${theme.colorPrimary}E6` },
|
||||
]}
|
||||
>
|
||||
{scrollPercentage}
|
||||
</Text>
|
||||
<Slider
|
||||
style={{
|
||||
flex: 1,
|
||||
height: 40,
|
||||
}}
|
||||
minimumValue={0}
|
||||
maximumValue={100}
|
||||
step={1}
|
||||
value={scrollPercentage}
|
||||
onSlidingComplete={onSlidingComplete}
|
||||
thumbTintColor={theme.colorAccent}
|
||||
minimumTrackTintColor={theme.colorAccent}
|
||||
maximumTrackTintColor={theme.textColorPrimary}
|
||||
/>
|
||||
<Text
|
||||
style={{
|
||||
color: theme.textColorPrimary,
|
||||
marginRight: 16,
|
||||
transform: [{ rotate: "-90deg" }],
|
||||
}}
|
||||
>
|
||||
100
|
||||
</Text>
|
||||
</View>
|
||||
<Text
|
||||
style={{
|
||||
color: theme.textColorPrimary,
|
||||
marginLeft: 16,
|
||||
transform: [{ rotate: "-90deg" }],
|
||||
}}
|
||||
>
|
||||
{scrollPercentage}
|
||||
</Text>
|
||||
<Slider
|
||||
style={{
|
||||
flex: 1,
|
||||
height: 40,
|
||||
}}
|
||||
minimumValue={0}
|
||||
maximumValue={100}
|
||||
step={1}
|
||||
value={scrollPercentage}
|
||||
onSlidingComplete={onSlidingComplete}
|
||||
thumbTintColor={theme.colorAccent}
|
||||
minimumTrackTintColor={theme.colorAccent}
|
||||
maximumTrackTintColor={theme.textColorPrimary}
|
||||
/>
|
||||
<Text
|
||||
style={{
|
||||
color: theme.textColorPrimary,
|
||||
marginRight: 16,
|
||||
transform: [{ rotate: "-90deg" }],
|
||||
}}
|
||||
>
|
||||
100
|
||||
</Text>
|
||||
</View>
|
||||
</FadeView>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<View style={styles.horizontalSliderContainer}>
|
||||
<Text
|
||||
style={{
|
||||
color: "#FFFFFF",
|
||||
marginLeft: 16,
|
||||
}}
|
||||
>
|
||||
{scrollPercentage}
|
||||
</Text>
|
||||
<Slider
|
||||
style={{
|
||||
flex: 1,
|
||||
height: 40,
|
||||
}}
|
||||
minimumValue={0}
|
||||
maximumValue={100}
|
||||
step={1}
|
||||
value={scrollPercentage}
|
||||
onSlidingComplete={onSlidingComplete}
|
||||
thumbTintColor={theme.colorAccent}
|
||||
minimumTrackTintColor={theme.colorAccent}
|
||||
maximumTrackTintColor={theme.textColorPrimary}
|
||||
/>
|
||||
<Text
|
||||
style={{
|
||||
color: "#FFFFFF",
|
||||
marginRight: 16,
|
||||
}}
|
||||
>
|
||||
100
|
||||
</Text>
|
||||
</View>
|
||||
<FadeView active={hide} animationDuration={150}>
|
||||
<View style={styles.horizontalSliderContainer}>
|
||||
<Text
|
||||
style={{
|
||||
color: "#FFFFFF",
|
||||
marginLeft: 16,
|
||||
}}
|
||||
>
|
||||
{scrollPercentage}
|
||||
</Text>
|
||||
<Slider
|
||||
style={{
|
||||
flex: 1,
|
||||
height: 40,
|
||||
}}
|
||||
minimumValue={0}
|
||||
maximumValue={100}
|
||||
step={1}
|
||||
value={scrollPercentage}
|
||||
onSlidingComplete={onSlidingComplete}
|
||||
thumbTintColor={theme.colorAccent}
|
||||
minimumTrackTintColor={theme.colorAccent}
|
||||
maximumTrackTintColor={theme.textColorPrimary}
|
||||
/>
|
||||
<Text
|
||||
style={{
|
||||
color: "#FFFFFF",
|
||||
marginRight: 16,
|
||||
}}
|
||||
>
|
||||
100
|
||||
</Text>
|
||||
</View>
|
||||
</FadeView>
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -108,11 +120,6 @@ export default VerticalScrollbar;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
verticalSliderContainer: {
|
||||
position: "absolute",
|
||||
zIndex: 2,
|
||||
right: -(Dimensions.get("window").width / 2) + 40,
|
||||
bottom: 300,
|
||||
backgroundColor: "rgba(0,0,0,0.7)",
|
||||
flexDirection: "row",
|
||||
justifyContent: "flex-end",
|
||||
alignItems: "center",
|
||||
@@ -124,7 +131,6 @@ const styles = StyleSheet.create({
|
||||
position: "absolute",
|
||||
zIndex: 2,
|
||||
bottom: 100,
|
||||
backgroundColor: "rgba(0,0,0,0.7)",
|
||||
flexDirection: "row",
|
||||
justifyContent: "flex-end",
|
||||
alignItems: "center",
|
||||
|
||||
+15
-10
@@ -44,6 +44,7 @@ import JumpToChapterModal from "./components/JumpToChapterModal";
|
||||
import { Actionbar } from "../../components/Actionbar/Actionbar";
|
||||
import EditInfoModal from "./components/EditInfoModal";
|
||||
import { setCustomNovelCover } from "../../database/queries/NovelQueries";
|
||||
import FadeView from "../../components/Common/CrossFadeView";
|
||||
|
||||
const Novel = ({ route, navigation }) => {
|
||||
const item = route.params;
|
||||
@@ -198,26 +199,29 @@ const Novel = ({ route, navigation }) => {
|
||||
{ backgroundColor: theme.colorPrimaryDark },
|
||||
]}
|
||||
>
|
||||
{selected.length > 0 ? (
|
||||
<FadeView
|
||||
style={{
|
||||
position: "absolute",
|
||||
zIndex: 1,
|
||||
width: Dimensions.get("window").width,
|
||||
elevation: 2,
|
||||
}}
|
||||
active={selected.length == 0}
|
||||
animationDuration={150}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: theme.colorPrimary,
|
||||
position: "absolute",
|
||||
zIndex: 1,
|
||||
width: Dimensions.get("window").width,
|
||||
paddingTop: StatusBar.currentHeight,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
paddingTop: StatusBar.currentHeight,
|
||||
paddingBottom: 8,
|
||||
elevation: 2,
|
||||
}}
|
||||
>
|
||||
<Appbar.Action
|
||||
icon="close"
|
||||
color={theme.textColorPrimary}
|
||||
onPress={() => {
|
||||
setSelected([]);
|
||||
}}
|
||||
onPress={() => setSelected([])}
|
||||
/>
|
||||
<Appbar.Content
|
||||
title={selected.length}
|
||||
@@ -232,7 +236,8 @@ const Novel = ({ route, navigation }) => {
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
) : (
|
||||
</FadeView>
|
||||
{selected.length == 0 && (
|
||||
<View
|
||||
style={{
|
||||
position: "absolute",
|
||||
|
||||
Reference in New Issue
Block a user