Reactjs:等待动画完成

Reactjs: Wait for animation to finish

在我的 React 应用程序中,用户可以输入保存在服务器上的数据。数据立即保存,用户无需按任何 "save" 按钮。我想显示一个简短的动画(类似于https://codepen.io/Sixclones/pen/VBdeXL) whenever I'm sending data to the server. After doing some research, I figured out that I should use react-transition-group (http://reactcommunity.org/react-transition-group/css-transition)。

我做了一个组件savingIcon:

const SavingIcon: React.SFC<SavingIconProps> = ({
    className,
    saving,
}) => {

    return (
        <div className={ classNames(className, "saving-icon") }>
            <CSSTransition 
                timeout={ 200 }
                classNames="saving"
                in={ saving }
            >

                <div className="saving-balls">
                    <div className="saving-balls__item" />
                    <div className="saving-balls__item" />
                    <div className="saving-balls__item" />
                </div>
            </CSSTransition>
        </div>
    );
}

每当保存内容时,saving 设置为 true,我想显示动画。

saving-icon.scss里面我放了下面的CSS(我尝试了几种方法,因此可能会有一些不必要的css):

@keyframes bouncing {

    0% {
        transform: translate3d(0, 10px, 0) scale(1.2, 0.85);
    }

    100% {
        transform: translate3d(0, -20px, 0) scale(0.9, 1.1);
    }
}

div.saving-icon {
    position: sticky;
    top: 15vh;
    display: flex;
    flex-direction: column;
    justify-content: end;
    height: 84vh;

    $anim-drt: 0.4s;
    $anim-ease: cubic-bezier(.6, .05, .15, .95);

    .saving-enter {

        div.saving-balls{

            &__item {
                background-color: $success-color;
                transition: background-color 20ms;  

            }
        }
    }

    .saving-enter-active {       

        div.saving-balls{

            &__item {
                background-color: $active-color;
                transition: background-color 20ms;  

            }

            &:nth-child(1) {
                animation: bouncing $anim-drt alternate infinite $anim-ease;
                transition: animation 1000ms;  
            }

            &:nth-child(2) {
                animation: bouncing $anim-drt $anim-drt/4 alternate infinite 
                    $anim-ease backwards;
                transition: animation 1000ms;  
            }

            &:nth-child(3) {
                animation: bouncing $anim-drt $anim-drt/2 alternate infinite 
                    $anim-ease backwards;
                transition: animation 1000ms;  
            }
        }
    }

    .saving-exit {
        div.saving-balls{

            &__item {
                background-color: $active-color;
                transition: background-color 20ms;  

            }

            &:nth-child(1) {
                animation: bouncing $anim-drt alternate infinite $anim-ease;
                transition: animation 1000ms;  
            }

            &:nth-child(2) {
                animation: bouncing $anim-drt $anim-drt/4 alternate infinite 
                    $anim-ease backwards;
                transition: animation 1000ms;  
            }

            &:nth-child(3) {
                animation: bouncing $anim-drt $anim-drt/2 alternate infinite 
                    $anim-ease backwards;
                transition: animation 1000ms;  
            }
        }
    }

    .saving-exit-active {
        div.saving-balls{

            &__item {
                background-color: $success-color;
                transition: background-color 20ms;  
                transition: animation 1000ms;  
            }


        }     
    }

    div.saving-balls {
        width: 3em;
        height: 10vh;

        z-index: 5;

        display: flex;
        justify-content: space-between;
        align-items: center;

        &__item {
            width: 0.7em;
            height: 0.7em;
            border-radius: 50%;
            background: $success-color;
        }
    }

}

我的问题如下: 通常保存东西只需要很短的时间;没有足够的时间实际 运行 动画一次。我的球瞬间变成红色,return 变成绿色(活动和成功颜色是某种类型或红色和绿色)。我想让动画 运行 持续更长的时间,大约 2 秒。我尝试了一些使用效果、状态和超时的 hack,但效果不佳,我宁愿使用正确的解决方案而不是一些肮脏的 hack。

我对 css 过渡和动画不太熟悉,对 react-transition-group 也不熟悉。我希望有一些现有的简单方法来播放动画一定的最短时间(如果连接较弱,动画应该显示直到数据被保存)。

作为替代方案,我接受关于如何告诉用户他的输入已保存而不是页面一角的动画的不同建议。目前,用户输入的所有内容都已保存,但他可能不会注意到发生了这种情况并搜索 "save" 按钮。

对于有类似问题的任何人:我通过使用 useState 解决了这个问题,并且只更改 saving 的状态值(如果它确实发生了变化)。我没有使用过渡组。

工作保存组件:

const SavingIcon: React.SFC<SavingIconProps> = ({
    className,
    saving,
}) => {

    const [ isSaving, setIsSaving ] = useState(false);

    useEffect(() => {
        if (isSaving !== saving) setIsSaving(saving);
    }, [ saving ]);

    return (
        <div className={ classNames(className, "saving-icon", {
            "saving-active": isSaving
        }) }>
            <div className="saving-balls">
                <div className="saving-balls__item" />
                <div className="saving-balls__item" />
                <div className="saving-balls__item" />
            </div>
        </div>
    );
}

scss:

@keyframes bouncing {

    0% {
        transform: translate3d(0, 10px, 0) scale(1.2, 0.85);
    }

    100% {
        transform: translate3d(0, -20px, 0) scale(0.9, 1.1);
    }
}

div.saving-icon {
    position: fixed;
    top: 86px;
    right: 1.3vw;

    $anim-drt: 0.4s;  /* duration */
    $anim-ease: cubic-bezier(.6, .05, .15, .95);

    &.saving-active {

        div.saving-balls {

            div.saving-balls__item {
                background-color: $active-color;
                transition: background-color 20ms;

                &:nth-child(1) {
                    animation: bouncing $anim-drt alternate infinite $anim-ease;
                }

                &:nth-child(2) {
                    animation: bouncing $anim-drt $anim-drt/4 alternate infinite
                        $anim-ease backwards;
                }

                &:nth-child(3) {
                    animation: bouncing $anim-drt $anim-drt/2 alternate infinite
                        $anim-ease backwards;
                }
            }
        }
    }

    div.saving-balls {
        width: 3em;
        height: 30px;

        z-index: 5;

        display: flex;
        justify-content: space-between;
        align-items: center;

        &__item {
            width: 0.7em;
            height: 0.7em;
            border-radius: 50%;
            background: $success-color;
        }
    }

}