javascript - 为滚动视图的动态高度设置动画
问题描述
我有一个类似于下图的 UI:
标题部分(深蓝色部分)应该是固定的,而正文部分是可滚动的。
标题部分的高度将是动态的,因为可以从服务器检索到 4 - 5 个项目符号点。
我的任务是当用户开始向上滚动以阅读下面的内容时开始降低标题部分的高度,直到用户只能阅读标题中的标题。此外,当用户开始向下滚动到他/她可以看到标题Body的点时,我应该开始增加高度,以便他可以再次开始看到所有项目符号点。
此功能更像是在滚动条上隐藏/显示标题。但是,标题将始终可见。
我编写了以下编写的代码来实现相同的目的:
import React from 'react';
import {
View,
Text,
ScrollView,
TouchableOpacity,
Linking,
Image,
Animated,
} from 'react-native';
class App extends React.PureComponent<Props, States> {
constructor() {
super();
this.state = {
headerHeight: 0,
headerTitleHeight: 0,
};
this.scrollY = new Animated.Value(0);
}
render() {
const { navigation, pending, macroFeed } = this.props;
const { headerHeight, headerTitleHeight } = this.state;
const { themeDetails } = navigation.state.params;
const {
title, bullet1, bullet2, bullet3,
} = themeDetails;
let headerStyle = {};
if (headerHeight) {
headerStyle = {
height: this.scrollY.interpolate({
inputRange: [0, headerHeight - headerTitleHeight],
outputRange: [headerHeight, headerTitleHeight],
extrapolate: 'clamp',
}),
};
}
const sortedMacroFeed = _.sortBy(macroFeed, (o) => moment(o.date).format('YYYYMMDD')).reverse().slice(0, 5);
if (pending) {
return (
<View style={styles.maxFlex}>
<LoadingSpinner
size="large"
containerStyle={styles.loadingSpinner}
/>
</View>
);
}
return (
<View
style={styles.maxFlex}
>
<Animated.View
style={[styles.headerWrapper, headerStyle]}
onLayout={(event) => {
this.setState({
headerHeight: event.nativeEvent.layout.height,
});
}}
>
<View style={styles.macroBgWrapper}>
<Image source={themeDetails.imgUrl} style={styles.macroBg} />
<View style={styles.macroBgOverlay} />
</View>
<View
style={styles.header}
onLayout={(event) => {
this.setState({
headerTitleHeight: event.nativeEvent.layout.height,
});
}}
>
<TouchableOpacity onPress={() => navigation.goBack()}>
<View>
<Icon name="ios-arrow-back" size={32} style={styles.backIcon} />
</View>
</TouchableOpacity>
<View style={styles.titleWrap}>
<Text style={styles.headerTitle}>
{title}
</Text>
</View>
</View>
<View style={styles.bulletWrapper}>
{
!!bullet1 && (
<View style={styles.column}>
<View style={styles.row}>
<View style={styles.bullet}>
<Text style={styles.buttetListText}>
{'\u2022'}
{' '}
</Text>
</View>
<View style={styles.bulletText}>
<Text style={styles.buttetListText}>
{bullet1}
</Text>
</View>
</View>
</View>
)
}
{
!!bullet2 && (
<View style={styles.column}>
<View style={styles.row}>
<View style={styles.bullet}>
<Text style={styles.buttetListText}>
{'\u2022'}
{' '}
</Text>
</View>
<View style={styles.bulletText}>
<Text style={styles.buttetListText}>
{bullet2}
</Text>
</View>
</View>
</View>
)
}
{
!!bullet3 && (
<View style={styles.column}>
<View style={styles.row}>
<View style={styles.bullet}>
<Text style={styles.buttetListText}>
{'\u2022'}
{' '}
</Text>
</View>
<View style={styles.bulletText}>
<Text style={styles.buttetListText}>
{bullet3}
</Text>
</View>
</View>
</View>
)
}
{
!bullet1 && !bullet2 && !bullet3 && (
<View style={styles.noBulletWrapper}>
<Text style={styles.noBulletPoints}>
No description found.
</Text>
</View>
)
}
</View>
</Animated.View>
<ScrollView
style={styles.maxFlex}
showsVerticalScrollIndicator={false}
onScroll={Animated.event([
{ nativeEvent: { contentOffset: { y: this.scrollY } } },
])}
scrollEventThrottle={16}
>
<View style={[styles.section, styles.wrapGutter]}>
<Text style={styles.sectionTitle}>
Recent Headlines
</Text>
{
sortedMacroFeed.map((feed) => (
<View key={feed.id} style={styles.newsSection}>
<Text style={styles.newsHours}>
{moment(feed.date).fromNow()} | {feed.author}
</Text>
<Text style={styles.newsTitle}>
{feed.title}
<Text onPress={() => this.openWebView(feed.url)}>
<EvilIcons name="external-link" size={16} style={styles.externalLinkIcon} />
</Text>
</Text>
</View>
))
}
</View>
<View style={styles.section}>
<View style={[styles.wrapGutter, styles.sectionTitleWrap]}>
<Text style={styles.sectionTitle}>
Exposure
</Text>
<View style={styles.totalNavWrap}>
<Text style={styles.totalNav}>
$467M
</Text>
<Text style={styles.totalNavLabel}>
Total NaV (all portfolios)
</Text>
</View>
</View>
<Tabs
tabsData={TABS_DATA}
renderTabContent={this.renderTabContent}
tabName="title"
hideIfOneTab
/>
</View>
</ScrollView>
</View>
);
}
}
如果您观察到我正在尝试使用onLayout
.
此代码仅以一种方式起作用,即当我向上滚动时。标题部分的高度降低到只能看到标题并且项目符号点被隐藏的程度。但在那之后,我无法向下滚动。高度永久降低。
现在,如果我更改下面给出的代码:
let headerStyle = {};
if (headerHeight) {
headerStyle = {
height: this.scrollY.interpolate({
inputRange: [0, headerHeight - headerTitleHeight],
outputRange: [headerHeight, headerTitleHeight],
extrapolate: 'clamp',
}),
};
}
至
const headerStyle = {
height: this.scrollY.interpolate({
inputRange: [0, 240],
outputRange: [300, 60],
extrapolate: 'clamp',
}),
};
一切似乎都很好。基本上,如果我停止动态检索高度值并提供像 240 之类的静态值,一切似乎都正常。
但是,如果我接受动态高度,滚动动画就会停止。任何解决此问题的帮助将不胜感激。感谢期待。
解决方案
我发现了这个问题。所以,如果你观察这条线:
<Animated.View
style={[styles.headerWrapper, headerStyle]}
onLayout={(event) => {
this.setState({
headerHeight: event.nativeEvent.layout.height,
});
}}
>
onLayout 用于在 headerHeight 值在滚动时更改时立即设置状态。因此,用于减少和向下滚动内容的标题高度是不可能的。
我将代码更改为:
<Animated.View
style={[styles.headerWrapper, headerStyle]}
onLayout={(event) => {
if (!this.state.headerHeight) {
this.setState({
headerHeight: event.nativeEvent.layout.height,
});
}
}}
>
现在,一切正常。
推荐阅读
- javascript - vue如何在关注画布后做出动作
- label - 组织模式 LaTeX 导出和子树
- c# - #nullable disable - 仅适用于特定命名空间?
- amazon-web-services - AWS Cloudformation:如何检查 Cloudformation 模板中是否存在存储桶?
- angular - 如何显示商店的内容?
- filter - tcpdump 过滤掉 arp 和所有 stp 数据包
- javascript - 单击单选按钮后如何清除文本输入?
- c - 不使用标志变量的数字的素数?
- python-3.x - 无法为 python3 链接 urllib 和 web3.py
- schema.org - 如何为 LocalBusiness 指定多个区域服务(例如多个城市)?