ffx*_*sam 73 animation css-animations greensock reactjs react-motion
这个简单的东西应该很容易实现,但是我把头发拉出来是多么复杂.
我想做的就是动画安装和卸载React组件,就是这样.这是我到目前为止所尝试的以及为什么每个解决方案都不起作用:
ReactCSSTransitionGroup - 我根本不使用CSS类,它是所有JS样式,所以这不起作用.ReactTransitionGroup - 这个较低级别的API很棒,但它要求你在动画完成时使用回调,所以只使用CSS过渡在这里不起作用.总有动画库,这导致下一点:TransitionMotion对于我需要的东西来说却非常混乱和过于复杂.left: -10000px)但我宁愿不去那条路线.我认为它很hacky,我希望卸载我的组件,以便清理它们并且不会使DOM混乱.我想要一些易于实现的东西.在mount上,动画一组样式; 在卸载时,为相同(或另一组)样式设置动画.完成.它还必须在多个平台上具有高性能.
我在这里碰到了一堵砖墙.如果我遗漏了一些东西并且有一个简单的方法可以做到这一点,请告诉我.
Pra*_*avi 89
这有点长,但我已经使用了所有本机事件和方法来实现这个动画.不ReactCSSTransitionGroup,ReactTransitionGroup等等
我用过的东西
onTransitionEnd 事件这是如何工作的
mounted)和默认样式(opacity: 0)挂载元素componentDidMount(componentWillReceiveProps进一步更新opacity: 1)使用超时更改样式()(使其为异步).opacity: 0),onTransitionEnd从DOM中卸载元素.继续循环.
仔细阅读代码,你就明白了.如果需要澄清,请发表评论.
希望这可以帮助.
class App extends React.Component{
constructor(props) {
super(props)
this.transitionEnd = this.transitionEnd.bind(this)
this.mountStyle = this.mountStyle.bind(this)
this.unMountStyle = this.unMountStyle.bind(this)
this.state ={ //base css
show: true,
style :{
fontSize: 60,
opacity: 0,
transition: 'all 2s ease',
}
}
}
componentWillReceiveProps(newProps) { // check for the mounted props
if(!newProps.mounted)
return this.unMountStyle() // call outro animation when mounted prop is false
this.setState({ // remount the node when the mounted prop is true
show: true
})
setTimeout(this.mountStyle, 10) // call the into animation
}
unMountStyle() { // css for unmount animation
this.setState({
style: {
fontSize: 60,
opacity: 0,
transition: 'all 1s ease',
}
})
}
mountStyle() { // css for mount animation
this.setState({
style: {
fontSize: 60,
opacity: 1,
transition: 'all 1s ease',
}
})
}
componentDidMount(){
setTimeout(this.mountStyle, 10) // call the into animation
}
transitionEnd(){
if(!this.props.mounted){ // remove the node on transition end when the mounted prop is false
this.setState({
show: false
})
}
}
render() {
return this.state.show && <h1 style={this.state.style} onTransitionEnd={this.transitionEnd}>Hello</h1>
}
}
class Parent extends React.Component{
constructor(props){
super(props)
this.buttonClick = this.buttonClick.bind(this)
this.state = {
showChild: true,
}
}
buttonClick(){
this.setState({
showChild: !this.state.showChild
})
}
render(){
return <div>
<App onTransitionEnd={this.transitionEnd} mounted={this.state.showChild}/>
<button onClick={this.buttonClick}>{this.state.showChild ? 'Unmount': 'Mount'}</button>
</div>
}
}
ReactDOM.render(<Parent />, document.getElementById('app'))Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.2/react-with-addons.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>Run Code Online (Sandbox Code Playgroud)
ffx*_*sam 14
利用从Pranesh的答案中获得的知识,我想出了一个可配置和可重用的替代解决方案:
const AnimatedMount = ({ unmountedStyle, mountedStyle }) => {
return (Wrapped) => class extends Component {
constructor(props) {
super(props);
this.state = {
style: unmountedStyle,
};
}
componentWillEnter(callback) {
this.onTransitionEnd = callback;
setTimeout(() => {
this.setState({
style: mountedStyle,
});
}, 20);
}
componentWillLeave(callback) {
this.onTransitionEnd = callback;
this.setState({
style: unmountedStyle,
});
}
render() {
return <div
style={this.state.style}
onTransitionEnd={this.onTransitionEnd}
>
<Wrapped { ...this.props } />
</div>
}
}
};
Run Code Online (Sandbox Code Playgroud)
用法:
import React, { PureComponent } from 'react';
class Thing extends PureComponent {
render() {
return <div>
Test!
</div>
}
}
export default AnimatedMount({
unmountedStyle: {
opacity: 0,
transform: 'translate3d(-100px, 0, 0)',
transition: 'opacity 250ms ease-out, transform 250ms ease-out',
},
mountedStyle: {
opacity: 1,
transform: 'translate3d(0, 0, 0)',
transition: 'opacity 1.5s ease-out, transform 1.5s ease-out',
},
})(Thing);
Run Code Online (Sandbox Code Playgroud)
最后,在另一个组件的render方法中:
return <div>
<ReactTransitionGroup>
<Thing />
</ReactTransitionGroup>
</div>
Run Code Online (Sandbox Code Playgroud)
我在工作期间反驳了这个问题,看起来很简单,它实际上并不在React中.在正常情况下,您可以呈现如下内容:
this.state.show ? {childen} : null;
Run Code Online (Sandbox Code Playgroud)
作为this.state.show改变,孩子们立即安装/卸载.
我采取的一种方法是创建一个包装器组件Animate并使用它
<Animate show={this.state.show}>
{childen}
</Animate>
Run Code Online (Sandbox Code Playgroud)
现在作为this.state.show变化,我们可以感知道具变化,getDerivedStateFromProps(componentWillReceiveProps)并创建中间渲染阶段来执行动画.

我们从安装或卸载儿童时开始使用静态舞台.
一旦我们检测到show标志更改,我们进入准备阶段,我们计算必要的属性,如height和width从ReactDOM.findDOMNode.getBoundingClientRect().
然后输入Animate State我们可以使用css过渡将高度,宽度和不透明度从0更改为计算值(如果卸载则更改为0).
在过渡结束时,我们使用onTransitionEndapi改回
Static舞台.
关于阶段如何顺利转移的更多细节,但这可能是整体想法:)
如果有兴趣的话,我创建了一个React库https://github.com/MingruiZhang/react-animate-mount来分享我的解决方案.任何反馈欢迎:)
这是基于此帖子的使用新的hooks API(带有TypeScript)的解决方案,用于延迟组件的卸载阶段:
function useDelayUnmount(isMounted: boolean, delayTime: number) {
const [ shouldRender, setShouldRender ] = useState(false);
useEffect(() => {
let timeoutId: number;
if (isMounted && !shouldRender) {
setShouldRender(true);
}
else if(!isMounted && shouldRender) {
timeoutId = setTimeout(
() => setShouldRender(false),
delayTime
);
}
return () => clearTimeout(timeoutId);
}, [isMounted, delayTime, shouldRender]);
return shouldRender;
}
Run Code Online (Sandbox Code Playgroud)
用法:
const Parent: React.FC = () => {
const [ isMounted, setIsMounted ] = useState(true);
const shouldRenderChild = useDelayUnmount(isMounted, 500);
const mountedStyle = {opacity: 1, transition: "opacity 500ms ease-in"};
const unmountedStyle = {opacity: 0, transition: "opacity 500ms ease-in"};
const handleToggleClicked = () => {
setIsMounted(!isMounted);
}
return (
<>
{shouldRenderChild &&
<Child style={isMounted ? mountedStyle : unmountedStyle} />}
<button onClick={handleToggleClicked}>Click me!</button>
</>
);
}
Run Code Online (Sandbox Code Playgroud)
CodeSandbox链接。
小智 7
我认为使用Transitionfromreact-transition-group可能是跟踪安装/卸载的最简单方法。它非常灵活。我正在使用一些类来展示它的易用性,但是您绝对可以使用addEndListenerprop连接自己的 JS 动画——我也很幸运地使用了 GSAP。
沙盒:https : //codesandbox.io/s/k9xl9mkx2o
这是我的代码。
import React, { useState } from "react";
import ReactDOM from "react-dom";
import { Transition } from "react-transition-group";
import styled from "styled-components";
const H1 = styled.h1`
transition: 0.2s;
/* Hidden init state */
opacity: 0;
transform: translateY(-10px);
&.enter,
&.entered {
/* Animate in state */
opacity: 1;
transform: translateY(0px);
}
&.exit,
&.exited {
/* Animate out state */
opacity: 0;
transform: translateY(-10px);
}
`;
const App = () => {
const [show, changeShow] = useState(false);
const onClick = () => {
changeShow(prev => {
return !prev;
});
};
return (
<div>
<button onClick={onClick}>{show ? "Hide" : "Show"}</button>
<Transition mountOnEnter unmountOnExit timeout={200} in={show}>
{state => {
let className = state;
return <H1 className={className}>Animate me</H1>;
}}
</Transition>
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Run Code Online (Sandbox Code Playgroud)
从 npm 安装 framer-motion。
import { motion, AnimatePresence } from "framer-motion"
export const MyComponent = ({ isVisible }) => (
<AnimatePresence>
{isVisible && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
/>
)}
</AnimatePresence>
)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
43858 次 |
| 最近记录: |