2017-10-09 3 views
3

J'ai mis en place mon propre accordéon sensible dans React, et je n'arrive pas à l'obtenir pour animer l'ouverture d'un pli.Pourquoi mon animation d'accordéon React ne fonctionne-t-elle pas?

Ceci est particulièrement étrange parce que je peux obtenir l'icône avant le titre pour animer haut et bas, et, autre que l'icône étant un pseudo-élément, je ne peux pas voir la différence entre les deux.

JS:

class Accordion extends React.Component { 
    constructor(props) { 
    super(props); 
    this.state = { 
     active: -1 
    }; 
    } 
    /*** 
    * Selects the given fold, and deselects if it has been clicked again by setting "active to -1" 
    * */ 
    selectFold = foldNum => { 
    const current = this.state.active === foldNum ? -1 : foldNum; 
    this.setState(() => ({ active: current })); 
    }; 

    render() { 
    return (
     <div className="accordion"> 
     {this.props.contents.map((content, i) => { 
      return (
      <Fold 
       key={`${i}-${content.title}`} 
       content={content} 
       handle={() => this.selectFold(i)} 
       active={i === this.state.active} 
      /> 
     ); 
     })} 
     </div> 
    ); 
    } 
} 

class Fold extends React.Component { 
    render() { 
    return (
     <div className="fold"> 
     <button 
      className={`fold_trigger ${this.props.active ? "open" : ""}`} 
      onClick={this.props.handle} 
     > 
      {this.props.content.title} 
     </button> 
      <div 
      key="content" 
      className={`fold_content ${this.props.active ? "open" : ""}`} 
      > 
      {this.props.active ? this.props.content.inner : null} 
      </div> 
     </div> 
    ); 
    } 
} 

CSS:

$line-color: rgba(34, 36, 38, 0.35); 

.accordion { 
    width: 100%; 
    padding: 1rem 2rem; 
    display: flex; 
    flex-direction: column; 
    border-radius: 10%; 
    overflow-y: auto; 
} 

.fold { 
    .fold_trigger { 
    &:before { 
     font-family: FontAwesome; 
     content: "\f107"; 
     display: block; 
     float: left; 
     padding-right: 1rem; 
     transition: transform 400ms; 
     transform-origin: 20%; 
     color: $line-color; 
    } 

    text-align: start; 
    width: 100%; 
    padding: 1rem; 
    border: none; 
    outline: none; 
    background: none; 
    cursor: pointer; 
    border-bottom: 1px solid $line-color; 

    &.open { 
     &:before { 
     transform: rotateZ(-180deg); 
     } 
    } 
    } 

    .fold_content { 
    display: none; 
    max-height: 0; 
    opacity: 0; 
    transition: max-height 400ms linear; 

    &.open { 
     display: block; 
     max-height: 400px; 
     opacity: 1; 
    } 
    } 
    border-bottom: 1px solid $line-color; 
} 

Voici le CodePen: https://codepen.io/renzyq19/pen/bovZKj

+1

Évitez d'animer la propriété 'display'. https://codepen.io/solbreslin/pen/QqmRLN - ne fonctionne pas parfaitement mais vous pouvez voir la transition en hauteur en supprimant 'display' – sol

Répondre

1

Je ne ne rendre le contenu si vous voulez une transition en douceur. Cela rendra l'animation d'une diapositive particulièrement difficile.


je changerais ceci:

{this.props.active ? this.props.content.inner : null} 

à ceci:

{this.props.content.inner} 

et utiliser cette scss:

.fold_content { 
    max-height: 0; 
    overflow: hidden; 
    transition: max-height 400ms ease; 

    &.open { 
    max-height: 400px; 
    } 
} 

Essayez l'extrait ci-dessous ou consultez la fourchette CodePen Demo.

class Accordion extends React.Component { 
 
    constructor(props) { 
 
    super(props); 
 
    this.state = { 
 
     active: -1 
 
    }; 
 
    } 
 
    /*** 
 
    * Selects the given fold, and deselects if it has been clicked again by setting "active to -1" 
 
    * */ 
 
    selectFold = foldNum => { 
 
    const current = this.state.active === foldNum ? -1 : foldNum; 
 
    this.setState(() => ({ active: current })); 
 
    }; 
 

 
    render() { 
 
    return (
 
     <div className="accordion"> 
 
     {this.props.contents.map((content, i) => { 
 
      return (
 
      <Fold 
 
       key={`${i}-${content.title}`} 
 
       content={content} 
 
       handle={() => this.selectFold(i)} 
 
       active={i === this.state.active} 
 
      /> 
 
     ); 
 
     })} 
 
     </div> 
 
    ); 
 
    } 
 
} 
 

 
class Fold extends React.Component { 
 
    render() { 
 
    return (
 
     <div className="fold"> 
 
     <button 
 
      className={`fold_trigger ${this.props.active ? "open" : ""}`} 
 
      onClick={this.props.handle} 
 
     > 
 
      {this.props.content.title} 
 
     </button> 
 
      <div 
 
      key="content" 
 
      className={`fold_content ${this.props.active ? "open" : ""}`} 
 
      > 
 
      {this.props.content.inner} 
 
      </div> 
 
     </div> 
 
    ); 
 
    } 
 
} 
 

 
const pictures = [ 
 
    "http://unsplash.it/200", 
 
    "http://unsplash.it/200", 
 
    "http://unsplash.it/200", 
 
]; 
 
var test = (title, text, imageURLs) => { 
 
    const images= 
 
    <div className='test-images' > 
 
     {imageURLs.map((url,i) => <img key={i} src={url} />)} 
 
    </div>; 
 

 
    const inner = 
 
    <div className='test-content' > 
 
     <p>{text} </p> 
 
     {images} 
 
    </div>; 
 
    
 
    return {title, inner}; 
 
}; 
 

 
const testData = [ 
 
    test('Title', 'Content',pictures), 
 
    test('Title', 'Content',pictures), 
 
    test('Title', 'Content',pictures), 
 
    test('Title', 'Content',pictures), 
 
    test('Title', 'Content',pictures), 
 
]; 
 

 
ReactDOM.render(<Accordion contents={testData} />, document.getElementById('root'));
.accordion { 
 
    width: 100%; 
 
    padding: 1rem 2rem; 
 
    display: flex; 
 
    flex-direction: column; 
 
    border-radius: 10%; 
 
    overflow-y: auto; 
 
} 
 

 
.fold { 
 
    border-bottom: 1px solid rgba(34, 36, 38, 0.35); 
 
} 
 

 
.fold .fold_trigger { 
 
    text-align: start; 
 
    width: 100%; 
 
    padding: 1rem; 
 
    border: none; 
 
    outline: none; 
 
    background: none; 
 
    cursor: pointer; 
 
    border-bottom: 1px solid rgba(34, 36, 38, 0.35); 
 
} 
 

 
.fold .fold_trigger:before { 
 
    font-family: FontAwesome; 
 
    content: "\f107"; 
 
    display: block; 
 
    float: left; 
 
    padding-right: 1rem; 
 
    transition: transform 400ms; 
 
    transform-origin: 20%; 
 
    color: rgba(34, 36, 38, 0.35); 
 
} 
 

 
.fold .fold_trigger.open:before { 
 
    transform: rotateZ(-180deg); 
 
} 
 

 
.fold .fold_content { 
 
    max-height: 0; 
 
    overflow: hidden; 
 
    transition: max-height 400ms ease; 
 
} 
 

 
.fold .fold_content.open { 
 
    max-height: 400px; 
 
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.4.0/css/font-awesome.min.css" rel="stylesheet" /> 
 

 
<div id='root'></div> 
 

 
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script> 
 
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

Note:

J'utilisé ease au lieu de linear sur la transition parce que je pense qu'il est plus agréable effet. Mais c'est juste un goût personnel. linear fonctionnera aussi bien.

De plus, vous pouvez continuer à rendre le contenu sous conditions. Une animation de diapositive est possible, mais une diapositive ne peut pas être facilement réalisée. Il y a quelques transition libraries que vous pourriez également explorer.

Cependant, je pense qu'il est plus facile d'utiliser l'état juste pour les classes conditionnelles (comme vous le faites avec la classe open). Je pense que rendre le contenu conditionnel au DOM rend votre vie difficile si vous essayez de faire des animations CSS.

+0

Merci beaucoup! Je remarque que vous avez également supprimé 'display' du CSS, comme suggéré par @ovokuro. Cela brise-t-il simplement les transitions? – renzyq19

+0

Pas de problème :) Je suis content que ça a aidé. –