如何使用 ReactCSSTransitionGroup 为 React 中的元素高度设置动画?

IT技术 reactjs actualheight reactcsstransitiongroup
2021-05-24 16:47:42

我正在尝试使用 为元素高度设置动画ReactCSSTransitionGroup,所以这就是我想要的动画效果:

http://jsfiddle.net/cherrry/hgk4Lme9/

问题是我并不总是知道元素高度,所以我试图破解scrollHeightclientHeight或类似的东西,componentDidMount并尝试node.style.height在样式表中设置或添加规则

http://jsfiddle.net/cherrry/dz8uod7u/

离开动画看起来不错,但是当元素进入时,它会闪烁一点,并且缩放动画看起来很奇怪

应该是因为询问node.scrollHeight导致渲染立即发生,所以无论如何在动画开始之前获取相同的信息并注入css规则?还是我应该换个角度思考?

我对这个max-height解决方案不是很满意,因为当max-height不接近或小于时产生的动画速度会很奇怪height,而且我的组件的高度确实变化很大。

我可以想象最终的解决方案可能会有点混乱,但我认为将其放入 Mixin 将足以在任何地方重用它

4个回答

我遇到了同样的问题,最终编写了一个独立的组件来动画高度。

你可以在这里看到演示:https : //stanko.github.io/react-animate-height/

它更容易使用,整个库非常小(约 200 行)

<AnimateHeight
  duration={ 500 }
  height={ 'auto' }
>
  <h1>Your content goes here</h1>
  <p>Put as many React or HTML components here.</p>
</AnimateHeight>

很抱歉无耻的自我宣传,但我认为如果您有多个组件要制作动画,它可以为您节省大量时间。

干杯!

经过更多的实验,我想出了一个解决方案,使用低级 APIReactTransitionGroup而不是高级 APIReactCSSTransitionGroup

这是带有工作解决方案的 JSFiddle:http : //jsfiddle.net/cherrry/0wgp34cr/

在动画之前,它正在做 3 件事:

  1. 获取计算的高度、填充和边距
  2. 隐藏元素display: none并添加.anim-enter以将高度设置为 0
  3. 创建 css 规则 .anim-enter-active

要开始动画,它会做两件事:

  1. 取消隐藏元素
  2. 添加.anim-enter-active开始动画

JSFiddle 中的一些数字和类名是硬编码的,但是将“mixin”转换为 React 类作为替换应该很容易 ReactCSSTransitionGroup

如果您不想导入module或使用 qjuery,这里有一个使用 React ref 的模板(https://reactjs.org/docs/refs-and-the-dom.html

基本上你会得到它的高度,增长到那个高度,切换回自动。在返回的途中切换到它的高度,然后返回到 0。

class CollapsibleSectionBlock extends React.Component {
constructor(props) {
    super(props);
    this.state = {
        showContent: false,
        height: "0px",
        myRef: null,
    };
}

componentDidUpdate = (prevProps, prevState) => {
    if (prevState.height === "auto" && this.state.height !== "auto") {
        setTimeout(() => this.setState({ height: "0px" }), 1);
    }
}

setInnerRef = (ref) => this.setState({ myRef: ref });

toggleOpenClose = () => this.setState({
    showContent: !this.state.showContent,
    height: this.state.myRef.scrollHeight,
});

updateAfterTransition = () => {
    if (this.state.showContent) {
        this.setState({ height: "auto" });
    }
};

render() {
    const { title, children } = this.props;
    return (
        <div>
            <h2 onClick={() => this.toggleOpenClose()}>
                Example
            </h2>
            <div
                ref={this.setInnerRef}
                onTransitionEnd={() => this.updateAfterTransition()}
                style={{
                    height: this.state.height,
                    overflow: "hidden",
                    transition: "height 250ms linear 0s",
                }}
            >
                {children}
            </div>
        </div>
    );
}

}

一个非常简单和不错的方法是处理 scaleY 而不是高度。悬停应该设置属性 transform: scaleY(1) (最初 => transform: scaleY(0) 以隐藏它)并且动画应该以 transform 属性为目标。