目录
前言初始时渲染时开始拖拽拖拽中拖拽结束拖拽删除参考链接前言
之前负责的一个需求,让在RN端做仿微信朋友圈的图片删除和排序,由于经验和时间限制,就跟PM协商改为点击删除,由此欠下一个技术栈,今天是来还债的。本文基于transform实现,而非定位。
源码
初始时
将数据源转化为如下格式
const dataArray = props.dataArray.map((item, index)=>{const newData = {}const translateX = (index % COLUMN_NUM) * this.itemW;const translateY = parseInt((index/COLUMN_NUM)) * this.itemW;newData.data = item; // 原始内容newData.originIndex = index; // 初始下标newData.originX = translateX; // 当前项基于父试图的相对x值newData.originY = translateY; // 当前项基于父试图的相对y值// 当前项的偏移量,初始时都是按照正常的布局来,都不需要偏移,故都为0newData.transAnimated = new Animated.ValueXY({x: 0,y: 0,});return newData});this.setState({dataArray });
渲染时
新数据源属性做如下分配
{this.state.dataArray.map((item, index) => (<Draggable ref={ref => this.refDrag.set(index, ref)} // 收集每项的ref引用pan={this.state.dataArray[index].transAnimated} // 创建的动画给到每一项key={`${item.data}`} destination={{// 删除区域x: 0,y: ScreenH-40,}} onMoveStart={() => this.handleMoveStart(item.originIndex)} // 此处不要使用map的index,因为排序后数组的数组没有变onMoveEnd={this.handleMoveEnd} onMove={this.handleMove}onArriveDestination={() => this.handleArriveDestination(item.data)} >{this.renderItem(item.data)}</Draggable>))}
开始拖拽
将如下属性赋值给this.touchCurItem
handleMoveStart = (move) => {this.touchCurItem = {ref: this.refDrag.get(move), // 获取当前拖拽的item引用index: move, // 当前拖拽item的下标moveToIndex: 0, // 将要拖拽到的位置,初始为0即可}
拖拽中
不断计算所有item(除拖拽项)将要到达的位置,然后用动画移动
handleMove = (e, gestureState) => {if (!this.touchCurItem) return;// 偏移量let translateX = gestureState.dx;let translateY = gestureState.dy;// 让拖拽项跟着手指移动this.state.dataArray[this.touchCurItem.index].transAnimated.setOffset({x: translateX,y: translateY});let moveXNum = translateX/this.itemWlet moveYNum = translateY/this.itemH;// 向四个方向移动一半位置就触发排序if (moveXNum > 0) {moveXNum = parseInt(moveXNum+0.5)} else if (moveXNum < 0) {moveXNum = parseInt(moveXNum-0.5)}if (moveYNum > 0) {moveYNum = parseInt(moveYNum+0.5)} else if (moveYNum < 0) {moveYNum = parseInt(moveYNum-0.5)}let moveToIndex = this.touchCurItem.index + moveXNum + moveYNum * COLUMN_NUM;// 边缘线制if (moveToIndex > this.state.dataArray.length-1) {moveToIndex = this.state.dataArray.length-1} else if (moveToIndex < 0) {moveToIndex = 0;}if (this.touchCurItem.moveToIndex == moveToIndex ) return;this.touchCurItem.moveToIndex = moveToIndexthis.state.dataArray.forEach((item,index)=> {let nextItem = nullif (index > this.touchCurItem.index && index <= moveToIndex) {// 向右拖拽nextItem = this.state.dataArray[index-1]} else if (index >= moveToIndex && index < this.touchCurItem.index) {// 向左拖拽nextItem = this.state.dataArray[index+1]} else if (index != this.touchCurItem.index &&(item.transAnimated.x._value != item.originX || item.transAnimated.y._value != item.originY)) {// 处理不松开时左右滑动nextItem = this.state.dataArray[index]} if (!nextItem) return;Animated.timing(item.transAnimated,{// 还记得在初始时的构造的数据源么,curItem和nextItem偏移量的差值就是当前想要到达的位置toValue: {x: parseInt(nextItem.originX+0.5 - item.originX),y: parseInt(nextItem.originY+0.5 - item.originY)},duration: 300,easing: Easing.out(Easing.quad),useNativeDriver: false,}).start()})}
拖拽结束
handleMoveEnd = () => {const curIndex = this.touchCurItem.index;const moveToIndex = this.touchCurItem.moveToIndex;const curItem = this.state.dataArray[this.touchCurItem.index];const moveToItem = this.state.dataArray[moveToIndex];let translateX = 0;let translateY = 0;// 判断移动是否达到移动标准,若是:动画方式返回原位置,否:动画偏移到新位置if (curIndex != moveToIndex) {translateX = parseInt(moveToItem.originX - curItem.originX+0.5);translateY = parseInt(moveToItem.originY - curItem.originY+0.5); }Animated.timing(curItem.transAnimated,{toValue: {x: translateX,y: translateY},duration: 300,easing: Easing.out(Easing.quad),useNativeDriver: false,}).start()this.touchCurItem = null;this.changePosition(curIndex, moveToIndex);}// 每次排序结束,按照新的排序整理dataArraychangePosition(startIndex,endIndex) {let isCommon = true; // 向右边移if (startIndex > endIndex) {// 向左移isCommon = false let tempIndex = startIndexstartIndex = endIndexendIndex = tempIndex}const newDataArray = [...this.state.dataArray].map((item,index)=>{let newIndex = null;if (isCommon) {if (endIndex > index && index >= startIndex) {newIndex = index+1} else if (endIndex == index) {newIndex = startIndex}} else {if (endIndex >= index && index > startIndex) {newIndex = index-1} else if (startIndex == index) {newIndex = endIndex}}if (newIndex != null) {const newItem = {...this.state.dataArray[newIndex]}newItem.originX = item.originXnewItem.originY = item.originYnewItem.originIndex = item.originIndex;newItem.transAnimated = new Animated.ValueXY({// 每次排序过后都将偏移量给清空,准备下次排序x: 0,y: 0,})item = newItem}return item})this.setState({dataArray: newDataArray});}
拖拽删除
这个比较简单了,通过通知的方式来传送消息,没啥可说的,有兴趣直接看源码吧