制作动画可折叠卡片组件,带有显示或隐藏的初始道具
Make animated collapsible card component, with initial props to show or hide
背景
使用 React Native,我能够制作可折叠的卡片组件。在图标上单击卡片会向上滑动以隐藏其内容,或展开以显示其内容。我认为设置默认值就像将扩展设置为 false 或 true 一样简单,但我认为这里的问题是当它被切换时会触发动画,从而改变卡片的高度。
例子
class CardCollapsible extends Component{
constructor(props){
super(props);
this.state = {
title: props.title,
expanded: true,
animation: new Animated.Value(),
iconExpand: "keyboard-arrow-down",
};
}
_setMaxHeight(event){
this.setState({
maxHeight : event.nativeEvent.layout.height
});
}
_setMinHeight(event){
this.setState({
minHeight : event.nativeEvent.layout.height
});
this.toggle = this.toggle.bind(this);
}
toggle(){
let initialValue = this.state.expanded? this.state.maxHeight + this.state.minHeight : this.state.minHeight,
finalValue = this.state.expanded? this.state.minHeight : this.state.maxHeight + this.state.minHeight;
this.setState({
expanded : !this.state.expanded
});
if (this.state.iconExpand === "keyboard-arrow-up") {
this.setState({
iconExpand : "keyboard-arrow-down"
})
} else {
this.setState({
iconExpand : "keyboard-arrow-up"
})
}
this.state.animation.setValue(initialValue);
Animated.spring( this.state.animation, {
toValue: finalValue
}
).start();
}
render(){
return (
<Animated.View style={[styles.container,{height: this.state.animation}]}>
<View style={styles.titleContainer} onLayout={this._setMinHeight.bind(this)}>
<CardTitle>{this.state.title}</CardTitle>
<TouchableHighlight
style={styles.button}
onPress={this.toggle}
underlayColor="#f1f1f1">
<Icon
name={this.state.iconExpand}
style={{ fontSize: 30 }}/>
</TouchableHighlight>
</View>
<Separator />
<View style={styles.card} onLayout={this._setMaxHeight.bind(this)}>
{this.props.children}
</View>
</Animated.View>
);
}
}
var styles = StyleSheet.create({
container: {
backgroundColor: '#fff',
margin:10,
overflow:'hidden'
},
titleContainer: {
flexDirection: 'row'
},
card: {
padding: 10
}
});
export { CardCollapsible };
打开
关闭
问题
我的目标是允许调用组件的人将组件的初始状态设置为展开或打开。但是当我尝试将展开状态更改为 false
时,它不会呈现关闭状态。
我将如何允许用户调用组件 select 无论它是在初始组件渲染时展开还是关闭?
为你做了一个全新的。简单又好用。
注意:此组件不需要 state
。更少的状态,更好的性能。
或许你可以在此基础上修改自己的风格=)
class Card extends Component {
anime = {
height: new Animated.Value(),
expanded: false,
contentHeight: 0,
}
constructor(props) {
super(props);
this._initContentHeight = this._initContentHeight.bind(this);
this.toggle = this.toggle.bind(this);
this.anime.expanded = props.expanded;
}
_initContentHeight(evt) {
if (this.anime.contentHeight>0) return;
this.anime.contentHeight = evt.nativeEvent.layout.height;
this.anime.height.setValue(this.anime.expanded ? this._getMaxValue() : this._getMinValue() );
}
_getMaxValue() { return this.anime.contentHeight };
_getMinValue() { return 0 };
toggle() {
Animated.timing(this.anime.height, {
toValue: this.anime.expanded ? this._getMinValue() : this._getMaxValue(),
duration: 300,
}).start();
this.anime.expanded = !this.anime.expanded;
}
render() {
return (
<View style={styles.titleContainer}>
<View style={styles.title}>
<TouchableHighlight underlayColor="transparent" onPress={this.toggle}>
<Text>{this.props.title}</Text>
</TouchableHighlight>
</View>
<Animated.View style={[styles.content, { height: this.anime.height }]} onLayout={this._initContentHeight}>
{this.props.children}
</Animated.View>
</View>
);
}
}
用法:
<Card title='Customized Card 1' expanded={false}>
<Text>Hello, this is first line.</Text>
<Text>Hello, this is second line.</Text>
<Text>Hello, this is third line.</Text>
</Card>
视觉结果:(只有第二张牌以expanded={true}
开头,其他牌以expanded={false}
开头)
扩展值 'true/false' 应从调用组件传递,您需要将此值用于 expand/collapse 您的组件。我看不到您在 render 方法中使用了 'expanded' 。你应该这样做:
{ this.props.expanded && <View style={styles.card} onLayout={this._setMaxHeight.bind(this)}>
{this.props.children}
</View> }
我按照一些博客上的步骤进行操作,然后我遇到了相同的情况。所以我根据这里其他人的线索进行了更改。我的 post 可能来晚了。但我希望能帮助别人。
import React from 'react';
import {
Text,
View,
TouchableOpacity,
Image,
Animated,
StyleSheet
} from 'react-native'
import styles from './styles' //put styles from another file
class PanelExpanding extends Component{
constructor(props) {
super(props)
this.state = {
expanded: false, //step 1
animation: new Animated.Value()
}
this.icons = {
'up': require('../../..assets/images/up.png'),
'down': require('../../../assets/images/down.png')
}
}
toggle(){
let finalValue = this.state.expanded? this.state.maxHeight + this.state.minHeight : this.state.minHeight,
initialValue = this.state.expanded? this.state.minHeight : this.state.maxHeight + this.state.minHeight;
//step 2, this needed, if we use !this.state.expanded i don't know how it wont changes at first
if(this.state.expanded === true){
return this.setState({ expanded : false })
} else {
return this.setState({ expanded : true })
}
this.state.animation.setValue(initialValue);
Animated.spring(
this.state.animation,
{ toValue: finalValue }
).start();
}
_setMinHeight(event){
this.setState({
minHeight : event.nativeEvent.layout.height
});
}
_setMaxHeight(event){
this.setState({
maxHeight : event.nativeEvent.layout.height
});
}
render(){
let icon = this.icons['down'],
textSwap = 'SHOWMORE'
if(this.state.expanded){
icon = this.icons['up'],
textSwap = 'SHOWLESS'
}
return (
<Animated.View
style={[styles.containerPanel,{height: this.state.animation}]} >
<View>
<View style={styles.titleContainer} onLayout={this._setMinHeight.bind(this)}>
<TouchableOpacity
style={styles.button}
onPress={this.toggle.bind(this)} >
<Text style={styles.textShow}>{textSwap}</Text>
<Image
style={styles.buttonImage}
source={icon} />
</TouchableOpacity>
</View>
//step 3, add this to initial when first render is false
{this.state.expanded && <View style={styles.body} onLayout={this._setMaxHeight.bind(this)}>
<Text>
{this.props.children}
</Text>
</View>}
</View>
</Animated.View>
)
}
}
export default PanelExpanding
// With hooks for rn
import React, { useState, useEffect } from 'react'
import {
Text,
View,
TouchableOpacity,
Image,
StyleSheet
} from 'react-native'
import styles from '../../assets/styles'
const usePanelExpanding = (props) => {
let icons = {
'up': require('../../images/formicons/icons/close.png'),
'down': require('../../images/formicons/icons/disclosure.png')
}
const [expanded, setExpanded] = useState(false);
const [iconShow, setIconShow] = useState('up');
const [textSwap, setTextSwap] = useState('Show more...');
useEffect(
() => {
expanded ? setIconShow('up') : setIconShow('down')
expanded ? setTextSwap('Show less') : setTextSwap('Show more ...')
},
[expanded]
);
const toggle = () =>{
setExpanded(!expanded)
}
const panel = (<View>
<View style={styles.titleContainer} >
<TouchableOpacity
style={styles.button}
onPress={toggle} >
<Image
style={styles.buttonImage}
source={icons[iconShow]} />
</TouchableOpacity>
</View>
{expanded && <View style={styles.mainPageFull} >
{props}
</View>
}
</View>)
return {
panel,
toggle
}
}
export default usePanelExpanding
// How to use it from a parent function
const expandingBioObject = usePanelExpanding(<Text>{profile.bio}</Text>)
<Card title="Biography" containerStyle={styles.CardContainerBio}>
{expandingBioObject.panel}
</Card>
使用react-native-collapsible-view。
您可以传递它 expanded
属性 ,它允许您完全控制其他组件的可折叠状态。
你也可以离开他 self-controlled 但传递 initExpanded
这允许你选择可折叠的初始状态。
背景
使用 React Native,我能够制作可折叠的卡片组件。在图标上单击卡片会向上滑动以隐藏其内容,或展开以显示其内容。我认为设置默认值就像将扩展设置为 false 或 true 一样简单,但我认为这里的问题是当它被切换时会触发动画,从而改变卡片的高度。
例子
class CardCollapsible extends Component{
constructor(props){
super(props);
this.state = {
title: props.title,
expanded: true,
animation: new Animated.Value(),
iconExpand: "keyboard-arrow-down",
};
}
_setMaxHeight(event){
this.setState({
maxHeight : event.nativeEvent.layout.height
});
}
_setMinHeight(event){
this.setState({
minHeight : event.nativeEvent.layout.height
});
this.toggle = this.toggle.bind(this);
}
toggle(){
let initialValue = this.state.expanded? this.state.maxHeight + this.state.minHeight : this.state.minHeight,
finalValue = this.state.expanded? this.state.minHeight : this.state.maxHeight + this.state.minHeight;
this.setState({
expanded : !this.state.expanded
});
if (this.state.iconExpand === "keyboard-arrow-up") {
this.setState({
iconExpand : "keyboard-arrow-down"
})
} else {
this.setState({
iconExpand : "keyboard-arrow-up"
})
}
this.state.animation.setValue(initialValue);
Animated.spring( this.state.animation, {
toValue: finalValue
}
).start();
}
render(){
return (
<Animated.View style={[styles.container,{height: this.state.animation}]}>
<View style={styles.titleContainer} onLayout={this._setMinHeight.bind(this)}>
<CardTitle>{this.state.title}</CardTitle>
<TouchableHighlight
style={styles.button}
onPress={this.toggle}
underlayColor="#f1f1f1">
<Icon
name={this.state.iconExpand}
style={{ fontSize: 30 }}/>
</TouchableHighlight>
</View>
<Separator />
<View style={styles.card} onLayout={this._setMaxHeight.bind(this)}>
{this.props.children}
</View>
</Animated.View>
);
}
}
var styles = StyleSheet.create({
container: {
backgroundColor: '#fff',
margin:10,
overflow:'hidden'
},
titleContainer: {
flexDirection: 'row'
},
card: {
padding: 10
}
});
export { CardCollapsible };
打开
关闭
问题
我的目标是允许调用组件的人将组件的初始状态设置为展开或打开。但是当我尝试将展开状态更改为 false
时,它不会呈现关闭状态。
我将如何允许用户调用组件 select 无论它是在初始组件渲染时展开还是关闭?
为你做了一个全新的。简单又好用。
注意:此组件不需要 state
。更少的状态,更好的性能。
或许你可以在此基础上修改自己的风格=)
class Card extends Component {
anime = {
height: new Animated.Value(),
expanded: false,
contentHeight: 0,
}
constructor(props) {
super(props);
this._initContentHeight = this._initContentHeight.bind(this);
this.toggle = this.toggle.bind(this);
this.anime.expanded = props.expanded;
}
_initContentHeight(evt) {
if (this.anime.contentHeight>0) return;
this.anime.contentHeight = evt.nativeEvent.layout.height;
this.anime.height.setValue(this.anime.expanded ? this._getMaxValue() : this._getMinValue() );
}
_getMaxValue() { return this.anime.contentHeight };
_getMinValue() { return 0 };
toggle() {
Animated.timing(this.anime.height, {
toValue: this.anime.expanded ? this._getMinValue() : this._getMaxValue(),
duration: 300,
}).start();
this.anime.expanded = !this.anime.expanded;
}
render() {
return (
<View style={styles.titleContainer}>
<View style={styles.title}>
<TouchableHighlight underlayColor="transparent" onPress={this.toggle}>
<Text>{this.props.title}</Text>
</TouchableHighlight>
</View>
<Animated.View style={[styles.content, { height: this.anime.height }]} onLayout={this._initContentHeight}>
{this.props.children}
</Animated.View>
</View>
);
}
}
用法:
<Card title='Customized Card 1' expanded={false}>
<Text>Hello, this is first line.</Text>
<Text>Hello, this is second line.</Text>
<Text>Hello, this is third line.</Text>
</Card>
视觉结果:(只有第二张牌以expanded={true}
开头,其他牌以expanded={false}
开头)
扩展值 'true/false' 应从调用组件传递,您需要将此值用于 expand/collapse 您的组件。我看不到您在 render 方法中使用了 'expanded' 。你应该这样做:
{ this.props.expanded && <View style={styles.card} onLayout={this._setMaxHeight.bind(this)}>
{this.props.children}
</View> }
我按照一些博客上的步骤进行操作,然后我遇到了相同的情况。所以我根据这里其他人的线索进行了更改。我的 post 可能来晚了。但我希望能帮助别人。
import React from 'react';
import {
Text,
View,
TouchableOpacity,
Image,
Animated,
StyleSheet
} from 'react-native'
import styles from './styles' //put styles from another file
class PanelExpanding extends Component{
constructor(props) {
super(props)
this.state = {
expanded: false, //step 1
animation: new Animated.Value()
}
this.icons = {
'up': require('../../..assets/images/up.png'),
'down': require('../../../assets/images/down.png')
}
}
toggle(){
let finalValue = this.state.expanded? this.state.maxHeight + this.state.minHeight : this.state.minHeight,
initialValue = this.state.expanded? this.state.minHeight : this.state.maxHeight + this.state.minHeight;
//step 2, this needed, if we use !this.state.expanded i don't know how it wont changes at first
if(this.state.expanded === true){
return this.setState({ expanded : false })
} else {
return this.setState({ expanded : true })
}
this.state.animation.setValue(initialValue);
Animated.spring(
this.state.animation,
{ toValue: finalValue }
).start();
}
_setMinHeight(event){
this.setState({
minHeight : event.nativeEvent.layout.height
});
}
_setMaxHeight(event){
this.setState({
maxHeight : event.nativeEvent.layout.height
});
}
render(){
let icon = this.icons['down'],
textSwap = 'SHOWMORE'
if(this.state.expanded){
icon = this.icons['up'],
textSwap = 'SHOWLESS'
}
return (
<Animated.View
style={[styles.containerPanel,{height: this.state.animation}]} >
<View>
<View style={styles.titleContainer} onLayout={this._setMinHeight.bind(this)}>
<TouchableOpacity
style={styles.button}
onPress={this.toggle.bind(this)} >
<Text style={styles.textShow}>{textSwap}</Text>
<Image
style={styles.buttonImage}
source={icon} />
</TouchableOpacity>
</View>
//step 3, add this to initial when first render is false
{this.state.expanded && <View style={styles.body} onLayout={this._setMaxHeight.bind(this)}>
<Text>
{this.props.children}
</Text>
</View>}
</View>
</Animated.View>
)
}
}
export default PanelExpanding
// With hooks for rn
import React, { useState, useEffect } from 'react'
import {
Text,
View,
TouchableOpacity,
Image,
StyleSheet
} from 'react-native'
import styles from '../../assets/styles'
const usePanelExpanding = (props) => {
let icons = {
'up': require('../../images/formicons/icons/close.png'),
'down': require('../../images/formicons/icons/disclosure.png')
}
const [expanded, setExpanded] = useState(false);
const [iconShow, setIconShow] = useState('up');
const [textSwap, setTextSwap] = useState('Show more...');
useEffect(
() => {
expanded ? setIconShow('up') : setIconShow('down')
expanded ? setTextSwap('Show less') : setTextSwap('Show more ...')
},
[expanded]
);
const toggle = () =>{
setExpanded(!expanded)
}
const panel = (<View>
<View style={styles.titleContainer} >
<TouchableOpacity
style={styles.button}
onPress={toggle} >
<Image
style={styles.buttonImage}
source={icons[iconShow]} />
</TouchableOpacity>
</View>
{expanded && <View style={styles.mainPageFull} >
{props}
</View>
}
</View>)
return {
panel,
toggle
}
}
export default usePanelExpanding
// How to use it from a parent function
const expandingBioObject = usePanelExpanding(<Text>{profile.bio}</Text>)
<Card title="Biography" containerStyle={styles.CardContainerBio}>
{expandingBioObject.panel}
</Card>
使用react-native-collapsible-view。
您可以传递它 expanded
属性 ,它允许您完全控制其他组件的可折叠状态。
你也可以离开他 self-controlled 但传递 initExpanded
这允许你选择可折叠的初始状态。