DragRow.vue 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. <script setup lang="ts">
  2. import { ref, onMounted, onUnmounted, watch } from 'vue';
  3. const props = defineProps({
  4. topPercent: {
  5. type: Number,
  6. default: 50,
  7. },
  8. sliderWidth: {
  9. type: Number,
  10. default: 10,
  11. },
  12. width: {
  13. type: String,
  14. default: '100%',
  15. },
  16. height: {
  17. type: String,
  18. default: '100%',
  19. },
  20. sliderColor: {
  21. type: String,
  22. default: '#989da285',
  23. },
  24. sliderBgColor: {
  25. type: String,
  26. default: '#f5f7f9',
  27. },
  28. sliderHoverColor: {
  29. type: String,
  30. default: '#989DA290',
  31. },
  32. sliderBgHoverColor: {
  33. type: String,
  34. default: '#2e33380d',
  35. },
  36. });
  37. const emit = defineEmits(['isDragging', 'dragging', 'resizeRow']);
  38. const top = ref(props.topPercent);
  39. const isDragging = ref(false);
  40. let containerHeight = 0;
  41. const dragerRef = ref();
  42. let time = Date.now();
  43. onMounted(() => {
  44. containerHeight = dragerRef.value.offsetHeight;
  45. });
  46. watch(
  47. () => props.topPercent,
  48. (val) => {
  49. top.value = val;
  50. }
  51. );
  52. onUnmounted(() => {
  53. document.onmousemove = null;
  54. document.onmouseup = null;
  55. document.ontouchmove = null;
  56. document.ontouchend = null;
  57. });
  58. const mobileDragRow = (e) => {
  59. document.body.style.overflow = 'hidden';
  60. document.body.style.overscrollBehaviorY = 'contain';
  61. e.stopPropagation();
  62. let oldPos = e.changedTouches[0].clientY;
  63. let oldPosPercent = top.value;
  64. let newPos = 0;
  65. let newPosPercent = 0;
  66. isDragging.value = true;
  67. emit('isDragging', isDragging.value);
  68. document.ontouchmove = sliderDrag;
  69. document.ontouchend = cancelSliderDrag;
  70. function sliderDrag(e) {
  71. if (time && Date.now() - time < 40) return;
  72. time = Date.now();
  73. e.stopPropagation();
  74. newPos = e.changedTouches[0].clientY;
  75. const movingDistancePercent = parseFloat(
  76. (((oldPos - newPos) / containerHeight) * 100).toFixed(3)
  77. );
  78. newPosPercent = oldPosPercent - movingDistancePercent;
  79. if (newPosPercent <= 0) {
  80. top.value = 0;
  81. } else if (newPosPercent >= 100) {
  82. top.value = 100;
  83. } else {
  84. top.value = newPosPercent;
  85. }
  86. emit('dragging', top.value);
  87. }
  88. function cancelSliderDrag() {
  89. isDragging.value = false;
  90. emit('isDragging', isDragging.value);
  91. document.body.style.overflow = '';
  92. document.body.style.overscrollBehaviorY = '';
  93. document.ontouchmove = null;
  94. document.ontouchend = null;
  95. }
  96. };
  97. const dragRow = (e) => {
  98. e.preventDefault();
  99. e.stopPropagation();
  100. let oldPos = e.clientY;
  101. let oldPosPercent = top.value;
  102. let newPos = 0;
  103. let newPosPercent = 0;
  104. isDragging.value = true;
  105. emit('isDragging', isDragging.value);
  106. document.onmousemove = sliderDrag;
  107. document.onmouseup = cancelSliderDrag;
  108. function sliderDrag(e) {
  109. if (time && Date.now() - time < 40) return;
  110. time = Date.now();
  111. e.stopPropagation();
  112. newPos = e.clientY;
  113. const movingDistancePercent = parseFloat(
  114. (((oldPos - newPos) / containerHeight) * 100).toFixed(3)
  115. );
  116. newPosPercent = oldPosPercent - movingDistancePercent;
  117. if (newPosPercent <= 0) {
  118. top.value = 0;
  119. } else if (newPosPercent >= 100) {
  120. top.value = 100;
  121. } else {
  122. top.value = newPosPercent;
  123. }
  124. emit('dragging', top.value);
  125. }
  126. function cancelSliderDrag() {
  127. isDragging.value = false;
  128. emit('isDragging', isDragging.value);
  129. document.onmouseup = null;
  130. document.onmousemove = null;
  131. }
  132. };
  133. </script>
  134. <template>
  135. <div class="drager_row" ref="dragerRef" :style="{ width: width, height: height }">
  136. <div class="drager_top" :style="{ height: top + '%' }">
  137. <div>
  138. <slot name="top"></slot>
  139. </div>
  140. </div>
  141. <div
  142. class="slider_row"
  143. @touchstart.passive="mobileDragRow"
  144. @mousedown="dragRow"
  145. :style="{
  146. height: sliderWidth + 'px',
  147. marginTop: -sliderWidth / 2 + 'px',
  148. marginBottom: -sliderWidth / 2 + 'px',
  149. }"
  150. ></div>
  151. <div class="drager_bottom" :style="`height: calc(${100 - top}% - 10px);`">
  152. <div>
  153. <slot name="bottom"></slot>
  154. </div>
  155. </div>
  156. </div>
  157. </template>
  158. <style scoped lang="less">
  159. .drager_row {
  160. overflow: hidden;
  161. box-sizing: border-box;
  162. display: flex;
  163. flex-direction: column;
  164. }
  165. .drager_row * {
  166. box-sizing: border-box;
  167. //transition: height 0.1s ease;
  168. }
  169. .drager_row > div {
  170. width: 100%;
  171. }
  172. .drager_top {
  173. margin-bottom: 5px;
  174. }
  175. .drager_top > div {
  176. height: 100%;
  177. overflow: hidden;
  178. }
  179. .drager_bottom {
  180. margin-top: 5px;
  181. }
  182. .drager_bottom > div {
  183. height: 100%;
  184. overflow: hidden;
  185. }
  186. .drager_row > .slider_row {
  187. transition: background 0.2s;
  188. position: relative;
  189. z-index: 1;
  190. cursor: row-resize;
  191. //background: v-bind('sliderBgColor');
  192. }
  193. .drager_row > .slider_row:before {
  194. transition: background-color 0.2s;
  195. position: absolute;
  196. left: 50%;
  197. top: 45%;
  198. transform: translateX(-50%);
  199. content: '';
  200. display: block;
  201. height: 2px;
  202. width: 24%;
  203. min-width: 30px;
  204. max-width: 70px;
  205. background-color: v-bind('sliderColor');
  206. }
  207. .drager_row > .slider_row:hover:before,
  208. .drager_row > .slider_row:hover:after,
  209. .drager_row > .slider_row:active:before,
  210. .drager_row > .slider_row:active:after {
  211. background-color: v-bind('sliderHoverColor');
  212. }
  213. .drager_row > .slider_row:hover,
  214. .drager_row > .slider_row:active {
  215. background: v-bind('sliderBgHoverColor');
  216. }
  217. </style>