2017-10-05 46 views
0

陣營本地陣圖插入我寫了一個動畫列表組件:在開始

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

你有沒有嘗試使用unshift而不是push? –

+0

因爲它不應該在每一次開始時都推動。開始只是一個例子。也可能在第二個位置等。 –

回答

0

如果我理解正確,那麼你想讓你的數組映射最後一項到第一個,對嗎?如果是這樣,那麼在地圖之前使用Array.prototype.reverse()應該做的伎倆。像這樣...

render() { 
    return (<TransitionGroup> 
     {this.data.toJS() 
      .reverse() 
      .map((item, index) => { 
      return this.renderItem({ 
       item: item, 
       index: index, 
       separators: {} 
      }); 
     })} 
    </TransitionGroup>) 
} 
+0

不,也許我的問題有點混亂。當我將**作爲例子**將一個項目推到頂部時,它將不會在頂部呈現,直到我刷新洞組件。當我將物品推到中間或其他地方時也會發生這種情況。它將最新的物品渲染到底部,但它應該使物品按照正確的順序渲染,而不會渲染所有內容。 –

+1

啊!是的,我不明白這是一個問題。哈!讓我仔細看看。 –