React Motion 中元素的动画退出(从 parent 组件接收这些元素)
Animating exit of an element in React Motion (which receives these elements from a parent component)
背景 我正在尝试为元素集合创建一个容器,每个元素都可以从集合中删除。当一个元素被移除时,我想为它的退出设置动画,我正在尝试使用 React Motion 来实现这一点。
这是上面的图表:
问题 我想到了使用 React Motion 的 TransitionMotion
组件,但在尝试编写一个需要传递给它的函数时遇到了困难。这是我的——不正确的——代码:
class Container extends Component {
state = {
elementStyles: {}
}
componentDidMount() {
this.getElementStyles();
}
componentDidUpdate() {
this.getElementStyles();
}
getElementStyles() {
if (!this.props.children.length || this.hasElementStyles()) return;
// this assumes all elements passed to the container are of equal dimensions
let firstChild = this.refs.scroller.firstChild;
let elementStyles = {
width: firstChild.offsetWidth,
height: firstChild.offsetHeight,
opacity: 1
};
this.setState({
elementStyles
});
}
hasElementStyles() {
return !isEmpty(this.state.elementStyles); // lodash to the rescue
}
willLeave() {
return { width: spring(0), height: spring(0), opacity: spring(0) }
}
getChildrenArray() {
return Children.toArray(this.props.children); // that's React's util function
}
getModifiedChild(element, props) {
if (!element) return;
return React.cloneElement(
element,
{style: props.style}
);
}
getInitialTransitionStyles() {
let elements = this.getChildrenArray();
let result = elements.map((element, index) => ({
key: element.key,
style: this.state.elementStyles
}));
return result;
}
render() {
if (this.hasElementStyles()) {
return (
<TransitionMotion
willLeave={this.willLeave}
styles={this.getInitialTransitionStyles()}
>
{ interpolatedStyles => {
let children = this.getChildrenArray();
return <div ref="scroller" className="container">
{ interpolatedStyles.map((style, index) => {
return this.getModifiedChild(children[index], style);
})
}
</div>
}}
</TransitionMotion>
);
} else {
return (
<div ref="scroller" className="container">
{ this.props.children }
</div>
);
}
}
}
请注意 TransitionMotion 组件中映射函数内的这一行:return this.getModifiedChild(children[index], style)
。这是错误的,因为一旦从集合中删除一个元素,this.props.children
就会发生变化,并且 children
数组的索引将不再对应于根据这些元素计算出的 styles
数组的索引children.
所以我需要的是一些聪明的方法来跟踪 props.children
在元素从集合中删除之前和之后(我想不出一个;我能想到的最好的with 在数组 return this.getModifiedChild(children.find(child => child.key === style.key), style);
上使用了 find
函数,但这会导致循环中有很多循环,我什至不敢想它),或者使用一些完全不同的方法,我是不知道。你能帮忙吗?
此解决方案的删除动画将已删除的项目(以及行中 "cards" 的其余部分)水平滑动到左侧卡片下方(使用其较低的 z-index)。
初始渲染设置了 标签并且卡片显示为一行。
每张卡片共享一个删除按钮方法,该方法设置数组元素的索引并启动切片数组的计时器。 setState 触发渲染,在计时器执行切片之前为卡片设置动画。
class CardRow extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
cardToRemove: 99,
};
}
findCard = (_id) => {
return this.myArray.find((_card) => {
return _card.id === _id;
});
};
removeCardClick = (evt) => {
const _card = this.findCard(evt.target.id);
setTimeout(() => { // wait for the render...
this.myArray.splice(this.myArray.indexOf(_card), 1);
this.setState({cardToRemove: 99});
}, 200);
this.setState({cardToRemove: this.myArray.indexOf(_card)});
};
render() {
let items = this.myArray.map((item, index) => {
let itemLeft = 200 * index; // card width 200px
if (this.state.cardToRemove <= index) {
itemLeft = 200 * (index - 1);
}
// .cardContainer {position: fixed}
return <div key={item.id}>
<Motion style={{left: spring(itemLeft)}}>
{({left}) =>
<div className="cardContainer" style={{left: left}}>
<div className="card">
<button className="Button" id={item.id} onClick={this.removeCardClick}>Del</button>
</div>
</div>
}
</Motion>
</div>;
});
items = items.reverse();
return <div>
{items}
</div>;
}
}
就是这样!谢谢,享受,
背景 我正在尝试为元素集合创建一个容器,每个元素都可以从集合中删除。当一个元素被移除时,我想为它的退出设置动画,我正在尝试使用 React Motion 来实现这一点。
这是上面的图表:
问题 我想到了使用 React Motion 的 TransitionMotion
组件,但在尝试编写一个需要传递给它的函数时遇到了困难。这是我的——不正确的——代码:
class Container extends Component {
state = {
elementStyles: {}
}
componentDidMount() {
this.getElementStyles();
}
componentDidUpdate() {
this.getElementStyles();
}
getElementStyles() {
if (!this.props.children.length || this.hasElementStyles()) return;
// this assumes all elements passed to the container are of equal dimensions
let firstChild = this.refs.scroller.firstChild;
let elementStyles = {
width: firstChild.offsetWidth,
height: firstChild.offsetHeight,
opacity: 1
};
this.setState({
elementStyles
});
}
hasElementStyles() {
return !isEmpty(this.state.elementStyles); // lodash to the rescue
}
willLeave() {
return { width: spring(0), height: spring(0), opacity: spring(0) }
}
getChildrenArray() {
return Children.toArray(this.props.children); // that's React's util function
}
getModifiedChild(element, props) {
if (!element) return;
return React.cloneElement(
element,
{style: props.style}
);
}
getInitialTransitionStyles() {
let elements = this.getChildrenArray();
let result = elements.map((element, index) => ({
key: element.key,
style: this.state.elementStyles
}));
return result;
}
render() {
if (this.hasElementStyles()) {
return (
<TransitionMotion
willLeave={this.willLeave}
styles={this.getInitialTransitionStyles()}
>
{ interpolatedStyles => {
let children = this.getChildrenArray();
return <div ref="scroller" className="container">
{ interpolatedStyles.map((style, index) => {
return this.getModifiedChild(children[index], style);
})
}
</div>
}}
</TransitionMotion>
);
} else {
return (
<div ref="scroller" className="container">
{ this.props.children }
</div>
);
}
}
}
请注意 TransitionMotion 组件中映射函数内的这一行:return this.getModifiedChild(children[index], style)
。这是错误的,因为一旦从集合中删除一个元素,this.props.children
就会发生变化,并且 children
数组的索引将不再对应于根据这些元素计算出的 styles
数组的索引children.
所以我需要的是一些聪明的方法来跟踪 props.children
在元素从集合中删除之前和之后(我想不出一个;我能想到的最好的with 在数组 return this.getModifiedChild(children.find(child => child.key === style.key), style);
上使用了 find
函数,但这会导致循环中有很多循环,我什至不敢想它),或者使用一些完全不同的方法,我是不知道。你能帮忙吗?
此解决方案的删除动画将已删除的项目(以及行中 "cards" 的其余部分)水平滑动到左侧卡片下方(使用其较低的 z-index)。
初始渲染设置了
每张卡片共享一个删除按钮方法,该方法设置数组元素的索引并启动切片数组的计时器。 setState 触发渲染,在计时器执行切片之前为卡片设置动画。
class CardRow extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
cardToRemove: 99,
};
}
findCard = (_id) => {
return this.myArray.find((_card) => {
return _card.id === _id;
});
};
removeCardClick = (evt) => {
const _card = this.findCard(evt.target.id);
setTimeout(() => { // wait for the render...
this.myArray.splice(this.myArray.indexOf(_card), 1);
this.setState({cardToRemove: 99});
}, 200);
this.setState({cardToRemove: this.myArray.indexOf(_card)});
};
render() {
let items = this.myArray.map((item, index) => {
let itemLeft = 200 * index; // card width 200px
if (this.state.cardToRemove <= index) {
itemLeft = 200 * (index - 1);
}
// .cardContainer {position: fixed}
return <div key={item.id}>
<Motion style={{left: spring(itemLeft)}}>
{({left}) =>
<div className="cardContainer" style={{left: left}}>
<div className="card">
<button className="Button" id={item.id} onClick={this.removeCardClick}>Del</button>
</div>
</div>
}
</Motion>
</div>;
});
items = items.reverse();
return <div>
{items}
</div>;
}
}
就是这样!谢谢,享受,