|
|
@@ -0,0 +1,195 @@
|
|
|
+import { MovableArea, MovableView, View, ScrollView, Text, Image } from '@tarojs/components'
|
|
|
+import './move_list.scss'
|
|
|
+import { useEffect, useRef, useState } from 'react';
|
|
|
+import { ColorType } from '@/context/themes/color';
|
|
|
+import Taro from '@tarojs/taro';
|
|
|
+import { IconDrag } from '@/components/basic/Icons';
|
|
|
+import { rpxToPx } from '@/utils/tools';
|
|
|
+
|
|
|
+export default function MoveList(props: { array: any,components:any, itemHeight: number, color?: string, update: Function }) {
|
|
|
+ const [list, setList] = useState(props.array)
|
|
|
+ const [components,setComponents] = useState(props.components)
|
|
|
+ const [movaleViewY, setMovaleViewY] = useState(0)
|
|
|
+ const [dragElement, setDragElement] = useState(null)
|
|
|
+ const [lastTarget, setLastTarget] = useState(null)
|
|
|
+ const [startOffsetY, setStartOffsetY] = useState(0)
|
|
|
+ const [startPageY, setStartPageY] = useState(0)
|
|
|
+ const [dragIndex, setDragIndex] = useState(-1)
|
|
|
+ const [scrollThreshold, setScrollThreshold] = useState(0.5)
|
|
|
+ const upperThreshold = 100
|
|
|
+ const lowThreshold = 100
|
|
|
+ const [canScroll, setCanScroll] = useState(true)
|
|
|
+ const [changedIndex, setChangedIndex] = useState(-1)
|
|
|
+ const [hiddenContent, setHiddenContent] = useState(false)
|
|
|
+
|
|
|
+ const [scrollTop, setScrollTop] = useState(0)
|
|
|
+ const [contentY, setContentY] = useState(0)
|
|
|
+ const [contentHeight, setContentHeight] = useState(0)
|
|
|
+ const ref = useRef(null)
|
|
|
+
|
|
|
+ useEffect(() => {
|
|
|
+ if (process.env.TARO_ENV == 'weapp') {
|
|
|
+ const query = Taro.createSelectorQuery();
|
|
|
+ query.select('#myScrollView').boundingClientRect();
|
|
|
+ query.exec((res) => {
|
|
|
+ setContentY(res[0].top)
|
|
|
+ setContentHeight(res[0].height)
|
|
|
+ })
|
|
|
+ }
|
|
|
+ else {
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }, [])
|
|
|
+
|
|
|
+ function longPress(e, index) {
|
|
|
+ setChangedIndex(index)
|
|
|
+ setStartOffsetY(e.target.offsetTop)
|
|
|
+ setStartPageY(e.touches[0].pageY)
|
|
|
+ setDragIndex(index)
|
|
|
+ setDragElement(components[index])
|
|
|
+ setMovaleViewY(e.target.offsetTop)
|
|
|
+ setCanScroll(false)
|
|
|
+ }
|
|
|
+
|
|
|
+ function touchMove(e) {
|
|
|
+ if (dragElement) {
|
|
|
+ let clientY = e.touches[0].clientY;
|
|
|
+ pageScroll(clientY);
|
|
|
+ let pageY = e.touches[0].pageY;
|
|
|
+ let targetMoveDistance = pageY - startPageY
|
|
|
+ let movaleViewY2 = startOffsetY + targetMoveDistance
|
|
|
+
|
|
|
+ let targetIndex = computeFutureIndex(targetMoveDistance, dragIndex)
|
|
|
+ if (targetIndex !== false && targetIndex != changedIndex) {
|
|
|
+ var temps = swapListItems(list, targetIndex, changedIndex)
|
|
|
+ setList(temps)
|
|
|
+
|
|
|
+ var temps2 = swapListItems(components, targetIndex, changedIndex)
|
|
|
+ setComponents(temps2)
|
|
|
+ setChangedIndex(targetIndex)
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ if (targetIndex === false && targetIndex != changedIndex) {
|
|
|
+ var temps = swapListItems(list, dragIndex, changedIndex)
|
|
|
+ setList(temps)
|
|
|
+
|
|
|
+ var temps2 = swapListItems(components, dragIndex, changedIndex)
|
|
|
+ setComponents(temps2)
|
|
|
+ setChangedIndex(dragIndex)
|
|
|
+ }
|
|
|
+
|
|
|
+ setMovaleViewY(movaleViewY2)
|
|
|
+ setLastTarget(targetIndex as any)
|
|
|
+ setHiddenContent(true)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ function pageScroll(clientY) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ function swapListItems<T>(list: T[], index1: number, index2: number): T[] {
|
|
|
+ const newList = [...list];
|
|
|
+ const temp = newList[index1];
|
|
|
+ newList[index1] = newList[index2];
|
|
|
+ newList[index2] = temp;
|
|
|
+
|
|
|
+ return newList;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ function touchEnd(e) {
|
|
|
+ if (dragElement) {
|
|
|
+ setDragElement(null)
|
|
|
+ setLastTarget(null)
|
|
|
+ setDragIndex(-1)
|
|
|
+
|
|
|
+ }
|
|
|
+ setChangedIndex(-1)
|
|
|
+ setCanScroll(true)
|
|
|
+ setHiddenContent(false)
|
|
|
+ setMovaleViewY(-100)
|
|
|
+
|
|
|
+ props.update(list)
|
|
|
+ }
|
|
|
+
|
|
|
+ function computeFutureIndex(targetMoveDistance, dragElementIndex) {
|
|
|
+ let willInsertAfter = getSwapDirection(targetMoveDistance);
|
|
|
+ if (willInsertAfter !== false) {
|
|
|
+ /** 偏移索引 */
|
|
|
+ let offsetElementIndex = dragElementIndex + willInsertAfter;
|
|
|
+ /** 移动步数 */
|
|
|
+ let step = targetMoveDistance / props.itemHeight;
|
|
|
+ /** 步数补偿,当只有移动距离超过单项 _scrollThreshold 时才算有效 */
|
|
|
+ if (step <= -1) {
|
|
|
+ step += scrollThreshold;
|
|
|
+ } else if (step >= 1) {
|
|
|
+ step -= scrollThreshold;
|
|
|
+ }
|
|
|
+ /** 目标索引 */
|
|
|
+ let futureIndex = parseInt(step) + offsetElementIndex;
|
|
|
+
|
|
|
+ // 避免越界
|
|
|
+ if (futureIndex < 0) {
|
|
|
+ futureIndex = 0;
|
|
|
+ } else if (futureIndex > components.length - 1) {
|
|
|
+ futureIndex = components.length - 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ return futureIndex;
|
|
|
+ } else {
|
|
|
+ return willInsertAfter;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ function getSwapDirection(targetMoveDistance) {
|
|
|
+ if (Math.abs(targetMoveDistance) < props.itemHeight / 2) {
|
|
|
+ // 轻轻拂动,滑动距离小于1/2单项高度
|
|
|
+ return false;
|
|
|
+ } else if (targetMoveDistance >= props.itemHeight / 2) {
|
|
|
+ // console.log('[_getSwapDirection] 👇👇👇');
|
|
|
+ return 1; // 下滑
|
|
|
+ } else if (targetMoveDistance <= props.itemHeight / -2) {
|
|
|
+ // console.log('[_getSwapDirection] 👆👆👆');
|
|
|
+ return -1; // 上滑
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return <View id="myScrollView" ref={ref} style={{ height: '100%', overflow: canScroll ? 'scroll' : 'hidden' }} catchMove>
|
|
|
+ <MovableArea style={{ height: components.length * props.itemHeight, width: rpxToPx(700) }}>
|
|
|
+ <View>
|
|
|
+ {
|
|
|
+ components.map((item, index) => {
|
|
|
+ return <View key={index} style={{ opacity: changedIndex == index && hiddenContent ? 0 : 1, height: props.itemHeight, width: rpxToPx(700) }}
|
|
|
+ onLongPress={(e) => longPress(e, index)}
|
|
|
+ onTouchMove={(e) => touchMove(e)}
|
|
|
+ onTouchEnd={(e) => touchEnd(e)}
|
|
|
+ >
|
|
|
+ {
|
|
|
+ item
|
|
|
+ }
|
|
|
+ </View>
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ <MovableView style={{ height: changedIndex >= 0 ? props.itemHeight : 0, width: rpxToPx(700) }}
|
|
|
+ direction='vertical'
|
|
|
+ disabled
|
|
|
+ animation={false}
|
|
|
+ y={movaleViewY}
|
|
|
+ >
|
|
|
+ <View className='drag_item' style={{ backgroundColor: props.color }}>
|
|
|
+ {
|
|
|
+ dragIndex >= 0 && <View className='modal_sel_item' style={{ height: props.itemHeight, border: 'none' }}>
|
|
|
+ {
|
|
|
+ components[dragIndex]
|
|
|
+ }
|
|
|
+ </View>
|
|
|
+ }
|
|
|
+ </View>
|
|
|
+ </MovableView>
|
|
|
+ </View>
|
|
|
+ </MovableArea>
|
|
|
+ </View>
|
|
|
+}
|