我写了一个动画列表组件:
import React, {Component} from 'react';
import PropType from 'prop-types';
import * as Animatable from 'react-native-animatable-promise';
import {FlatList, View} from 'react-native';
import {observable, action, runInAction} from "mobx"
import {observer} from 'mobx-react';
import TransitionGroup, {FadeInOutTransition} from 'react-native-transitiongroup';
import arrayDiff from 'arraydiff';
class AnimatedListItem extends Component {
static propTypes = {
animationOut: PropType.string.isRequired,
animationIn: PropType.string.isRequired,
duration: PropType.number.isRequired
};
tempHeight = 0;
locked = false;
componentDidAppear(callback) {
this.componentWillEnter(callback);
}
componentDidEnter(callback) {
this.refs.Item[this.props.animationIn](this.props.duration).then(() => callback());
}
componentWillLeave(callback) {
this.locked = true;
this.refs.Item.stopAnimation();
this.refs.Item.transition({height: this.tempHeight, opacity: 1, scale: 1}, {height: 0, opacity: 0, scale: 0}, this.props.duration, 'ease').then(callback).catch(console.error);
//this.refs.Item[this.props.animationOut](this.props.duration).then(() => callback());
}
_onLayout(event) {
const {height} = event.nativeEvent.layout;
if (!this.locked) this.tempHeight = height;
}
render() {
return (<Animatable.View ref='Item' onLayout={this._onLayout.bind(this)}>
{this.props.children}
</Animatable.View>);
}
}
@observer
class AnimatedList extends Component {
static propTypes = {
data: PropType.array.isRequired,
renderItem: PropType.func.isRequired,
keyExtractor: PropType.func.isRequired,
inAnimation: PropType.string.isRequired,
outAnimation: PropType.string.isRequired,
style: PropType.any,
duration: PropType.number,
delay: PropType.number
};
@observable keyExtractor = this.props.keyExtractor;
@observable data = [];
@observable duration = this.props.duration || 1000;
@observable outAnimation = this.props.outAnimation;
@observable inAnimation = this.props.inAnimation;
_renderItem = this.props.renderItem;
delay = this.props.delay || 100;
queue;
actionIsRunning = false;
componentDidMount() {
this.setProps(this.props);
}
componentWillReceiveProps(props) {
this.setProps(props);
}
setProps(props) {
requestAnimationFrame(() => {
if (this.actionIsRunning) {
this.queue = props;
return;
}
this.queue = null;
this.actionIsRunning = true;
runInAction(() => {
this.duration = props.duration || 1000;
this.outAnimation = props.outAnimation;
this.inAnimation = props.inAnimation;
this.delay = props.delay;
this.keyExtractor = props.keyExtractor;
});
const differences = arrayDiff(this.data.toJS(), props.data.toJS(), (a, b) => {
const aKey = this.keyExtractor(a);
const bKey = this.keyExtractor(b);
return aKey === bKey;
});
console.log("ADIFF: " + JSON.stringify(differences));
this.applyDifferences(differences).then(() => {
console.log("NEWLIST: " + JSON.stringify(this.data));
this.actionIsRunning = false;
if (this.queue) this.setProps.bind(this)(this.queue);
}).catch(e => console.error(e));
});
}
async applyDifferences(differences) {
for (let diff of differences) {
if (!diff.type) continue;
await new Promise((resolve) => requestAnimationFrame(resolve));
if (diff.type === 'remove' && diff.index !== undefined && diff.howMany !== undefined) {
runInAction(() => {
this.data.splice(diff.index, diff.howMany);
});
}
if (diff.type === 'move' && diff.from !== undefined && diff.to !== undefined) {
runInAction(() => {
moveInThisArray(this.data, diff.from, diff.to);
});
}
if (diff.type === 'insert' && diff.index !== undefined && Array.isArray(diff.values)) {
const argArray = [diff.index, 0];
for (let value of diff.values) {
argArray.push(value);
}
runInAction(() => {
this.data.splice.apply(this.data, argArray);
});
}
await new Promise((resolve) => setTimeout(resolve, this.delay));
}
await new Promise((resolve) => setTimeout(resolve, this.duration));
}
/**
* @typedef {Object} FlatListRenderInfo
* @property {Object} item
* @property {Number} index
* @property {Object} separators
*/
/**
*
* @param {FlatListRenderInfo} info
*/
renderItem(info) {
//console.log(this.keyExtractor(info.item));
return (
<AnimatedListItem animationOut={this.outAnimation} animationIn={this.inAnimation} duration={this.duration}
key={this.keyExtractor(info.item)}>
{this._renderItem(info)}
</AnimatedListItem>);
}
render() {
return (<TransitionGroup>
{this.data.toJS().map((item, index) => {
return this.renderItem({
item: item,
index: index,
separators: {}
});
})}
</TransitionGroup>)
}
}
function moveInThisArray(arr, old_index, new_Index) {
if (new_Index >= arr.length) {
let k = new_Index - arr.length;
while ((k--) + 1) {
arr.push(undefined);
}
}
arr.splice(new_Index, 0, arr.splice(old_index, 1)[0]);
return arr;
}
export default AnimatedList;
我的大多数代码都按预期工作。但是我有一点问题,如果我在数组的开头添加一个元素,它将在我的列表末尾呈现。我想也许我对Array.map
的使用是错误的。数组没有问题,如果我将数组打印到控制台,它的顺序是正确的。我在上一个问题中已经注意到了这个问题,但忽略了它,因为在我的上一个项目中,它对于元素的顺序是重要的,但这一次很重要。是否有替代使用Array.map
来解决问题?或者我该怎么办?
答案 0 :(得分:0)
如果我理解正确,那么你希望你的数组将最后一项映射到第一项,对吧?如果是这样,那么在地图之前使用Array.prototype.reverse()应该可以解决问题。像这样......
render() {
return (<TransitionGroup>
{this.data.toJS()
.reverse()
.map((item, index) => {
return this.renderItem({
item: item,
index: index,
separators: {}
});
})}
</TransitionGroup>)
}