Make animated collapsible card component, with initial props to show or hide

前端 未结 5 1731
面向向阳花
面向向阳花 2021-01-14 03:12

Background

Using React Native I was able to make collapsible card component. On Icon click the card slides up hiding its content, or expands showing

相关标签:
5条回答
  • 2021-01-14 03:25

    Made a brand new one for you. Simple and works fine.

    Note: no state required for this component. fewer state, better performance.

    Maybe you could modify your own style on top of this =)

    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>
            );
        }
    }
    

    Usage:

    <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>
    

    Visual result: (only second card start with expanded={true}, others with expanded={false})

    0 讨论(0)
  • 2021-01-14 03:26

    The expanded value 'true/false' should be passed from the calling component and you need to use this value to expand/collapse your component. I can't see your have used 'expanded' inside render method. You should do like this:

     { this.props.expanded && <View style={styles.card} onLayout={this._setMaxHeight.bind(this)}>
                {this.props.children}
              </View> }
    
    0 讨论(0)
  • 2021-01-14 03:36

    use react-native-collapsible-view. you can pass it expanded property which allows you to fully control the collapsible state by other components.
    you can also leave him self-controlled but pass initExpanded which allows you to choose what is the initial state of the collapsible.

    0 讨论(0)
  • 2021-01-14 03:39

    i follow step on some blog then i have same condition with it. so i made a changes after clue from others here. my post maybe is to late. but i wish will be help others.

    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
    
    0 讨论(0)
  • 2021-01-14 03:40
    // 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>
    
    0 讨论(0)
提交回复
热议问题