MoveOrderList.tsx 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. import { MovableArea, MovableView, View, ScrollView, Text, Image } from '@tarojs/components'
  2. import './MoveOrderList.scss'
  3. import { useEffect, useRef, useState } from 'react';
  4. import { ColorType } from '@/context/themes/color';
  5. import Taro from '@tarojs/taro';
  6. import { IconDrag } from '@/components/basic/Icons';
  7. export default function Component(props: { array: any, itemHeight: number, color?: string,update:Function }) {
  8. const [list, setList] = useState(props.array)
  9. const [movaleViewY, setMovaleViewY] = useState(0)
  10. const [dragElement, setDragElement] = useState(null)
  11. const [lastTarget, setLastTarget] = useState(null)
  12. const [startOffsetY, setStartOffsetY] = useState(0)
  13. const [startPageY, setStartPageY] = useState(0)
  14. const [dragIndex, setDragIndex] = useState(-1)
  15. const [scrollThreshold, setScrollThreshold] = useState(0.5)
  16. const upperThreshold = 100
  17. const lowThreshold = 100
  18. const [duration, setDuration] = useState(1000)
  19. const [canScroll, setCanScroll] = useState(true)
  20. const [changedIndex, setChangedIndex] = useState(-1)
  21. const [hiddenContent, setHiddenContent] = useState(false)
  22. const [scrollTop, setScrollTop] = useState(0)
  23. const [contentY, setContentY] = useState(0)
  24. const [contentHeight, setContentHeight] = useState(0)
  25. const ref = useRef(null)
  26. useEffect(() => {
  27. const query = Taro.createSelectorQuery();
  28. query.select('#myScrollView').boundingClientRect();
  29. query.exec((res) => {
  30. setContentY(res[0].top)
  31. setContentHeight(res[0].height)
  32. })
  33. }, [])
  34. function longPress(e, index) {
  35. setChangedIndex(index)
  36. setStartOffsetY(e.target.offsetTop)
  37. setStartPageY(e.touches[0].pageY)
  38. setDragIndex(index)
  39. setDragElement(list[index])
  40. setMovaleViewY(e.target.offsetTop)
  41. setCanScroll(false)
  42. }
  43. function touchMove(e) {
  44. if (dragElement) {
  45. let clientY = e.touches[0].clientY;
  46. pageScroll(clientY);
  47. let pageY = e.touches[0].pageY;
  48. let targetMoveDistance = pageY - startPageY
  49. let movaleViewY2 = startOffsetY + targetMoveDistance
  50. let targetIndex = computeFutureIndex(targetMoveDistance, dragIndex)
  51. if (targetIndex !== false && targetIndex != changedIndex) {
  52. var temps = swapListItems(list, targetIndex, changedIndex)
  53. setList(temps)
  54. setChangedIndex(targetIndex)
  55. }
  56. if (targetIndex === false && targetIndex != changedIndex) {
  57. var temps = swapListItems(list, dragIndex, changedIndex)
  58. setList(temps)
  59. setChangedIndex(dragIndex)
  60. }
  61. setMovaleViewY(movaleViewY2)
  62. setLastTarget(targetIndex as any)
  63. setHiddenContent(true)
  64. }
  65. }
  66. function pageScroll(clientY) {
  67. return
  68. // console.log(clientY,contentY,contentHeight)
  69. if ((clientY - contentY) + upperThreshold >= contentHeight) {
  70. // var scroll = ref.current
  71. // debugger
  72. // (ref.current as any).scrollTo({scrollTop:100})
  73. // setScrollTop((clientY - contentY) + props.itemHeight)
  74. // setScrollTop(2200)
  75. // setCanScroll(true)
  76. Taro.pageScrollTo({
  77. selector: '#myScrollView',
  78. scrollTop: (clientY - contentY) + props.itemHeight,
  79. duration: 200
  80. })
  81. }
  82. else if ((clientY - contentY) - lowThreshold <= 0) {
  83. setScrollTop((clientY - contentY) - props.itemHeight)
  84. // setCanScroll(true)
  85. }
  86. else {
  87. // setCanScroll(true)
  88. }
  89. }
  90. function swapListItems<T>(list: T[], index1: number, index2: number): T[] {
  91. const newList = [...list];
  92. const temp = newList[index1];
  93. newList[index1] = newList[index2];
  94. newList[index2] = temp;
  95. return newList;
  96. }
  97. function touchEnd(e) {
  98. if (dragElement) {
  99. // let pageY = e.changedTouches[0].pageY
  100. // let targetMoveDistance = pageY - startPageY;
  101. // let dragElementIndex = dragIndex;
  102. // const futrueIndex = computeFutureIndex(targetMoveDistance, dragElementIndex)
  103. // if (futrueIndex !== false) {
  104. // var temps = list
  105. // temps.splice(futrueIndex, 0, temps.splice(dragIndex, 1)[0])
  106. // setList(temps)
  107. // }
  108. setDragElement(null)
  109. setLastTarget(null)
  110. setDragIndex(-1)
  111. }
  112. setChangedIndex(-1)
  113. setCanScroll(true)
  114. setHiddenContent(false)
  115. setMovaleViewY(-100)
  116. props.update(list)
  117. }
  118. function computeFutureIndex(targetMoveDistance, dragElementIndex) {
  119. let willInsertAfter = getSwapDirection(targetMoveDistance);
  120. if (willInsertAfter !== false) {
  121. /** 偏移索引 */
  122. let offsetElementIndex = dragElementIndex + willInsertAfter;
  123. /** 移动步数 */
  124. let step = targetMoveDistance / props.itemHeight;
  125. /** 步数补偿,当只有移动距离超过单项 _scrollThreshold 时才算有效 */
  126. if (step <= -1) {
  127. step += scrollThreshold;
  128. } else if (step >= 1) {
  129. step -= scrollThreshold;
  130. }
  131. /** 目标索引 */
  132. let futureIndex = parseInt(step) + offsetElementIndex;
  133. // 避免越界
  134. if (futureIndex < 0) {
  135. futureIndex = 0;
  136. } else if (futureIndex > list.length - 1) {
  137. futureIndex = list.length - 1;
  138. }
  139. return futureIndex;
  140. } else {
  141. return willInsertAfter;
  142. }
  143. }
  144. function getSwapDirection(targetMoveDistance) {
  145. if (Math.abs(targetMoveDistance) < props.itemHeight / 2) {
  146. // 轻轻拂动,滑动距离小于1/2单项高度
  147. return false;
  148. } else if (targetMoveDistance >= props.itemHeight / 2) {
  149. // console.log('[_getSwapDirection] 👇👇👇');
  150. return 1; // 下滑
  151. } else if (targetMoveDistance <= props.itemHeight / -2) {
  152. // console.log('[_getSwapDirection] 👆👆👆');
  153. return -1; // 上滑
  154. }
  155. }
  156. console.log(list)
  157. return <View id="myScrollView" ref={ref} style={{height:'100%', overflow: canScroll?'scroll':'hidden' }} catchMove>
  158. <MovableArea style={{ height: list.length * props.itemHeight, width: '100vw' }}>
  159. {list.map((item, index) => {
  160. return <View style={{ opacity: changedIndex == index && hiddenContent ? 0 : 1, height: props.itemHeight, width: '100vw' }}
  161. onLongPress={(e) => longPress(e, index)}
  162. onTouchMove={(e) => touchMove(e)}
  163. onTouchEnd={(e) => touchEnd(e)}
  164. >
  165. <View className='modal_order_item' style={{ height: 40}}>
  166. <Text style={{ color: props.color }}>{item.name}</Text>
  167. <IconDrag width={17} height={12} />
  168. {/* <Image src={require('@assets/images/menu.png')} style={{width:20,height:20}}/> */}
  169. </View>
  170. </View>
  171. })}
  172. <MovableView style={{ height: changedIndex>=0?props.itemHeight:0,width:'100vw' }}
  173. direction='vertical'
  174. disabled
  175. animation={false}
  176. y={movaleViewY}
  177. >
  178. <View className='drag_item' style={{ backgroundColor: props.color }}>
  179. {
  180. dragIndex >= 0 && <View className='modal_sel_item' style={{ height: 40, border:'none' }}>
  181. <Text style={{ color: 'black',fontWeight:'bold' }}>{list[changedIndex].name}</Text>
  182. <IconDrag width={17} height={12} />
  183. </View>
  184. }
  185. </View>
  186. </MovableView>
  187. </MovableArea>
  188. </View>
  189. }