index.vue 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. <template>
  2. <view class="tui-swipeout-wrap" :style="{ backgroundColor: backgroundColor }">
  3. <view class="tui-swipeout-item" :class="[isShowBtn ? 'swipe-action-show' : '']"
  4. :style="{ transform: 'translate(' + position.pageX + 'px,0)' }">
  5. <view class="tui-swipeout-content" @touchstart="handlerTouchstart"
  6. @touchmove="handlerTouchmove" @touchend="handlerTouchend" @mousedown="handlerTouchstart"
  7. @mousemove="handlerTouchmove" @mouseup="handlerTouchend">
  8. <slot name="content"></slot>
  9. </view>
  10. <view class="tui-swipeout-button-right-group" v-if="actions.length > 0" @touchend.stop="loop">
  11. <view class="tui-swipeout-button-right-item" v-for="(item, index) in actions" :key="index"
  12. :style="{ backgroundColor: item.background || '#f7f7f7', color: item.color, width: item.width + 'px' }"
  13. :data-index="index" @tap="handlerButton">
  14. <image :src="item.icon" v-if="item.icon"
  15. :style="{ width: px(item.imgWidth), height: px(item.imgHeight) }"></image>
  16. <text :style="{ fontSize: px(item.fontsize) }">{{ item.name }}</text>
  17. </view>
  18. </view>
  19. <!--actions长度设置为0,可直接传按钮进来-->
  20. <view class="tui-swipeout-button-right-group" @touchend.stop="loop" @tap="handlerParentButton"
  21. v-if="actions.length === 0" :style="{ width: operateWidth + 'px', right: '-' + operateWidth + 'px' }">
  22. <slot name="button"></slot>
  23. </view>
  24. </view>
  25. <view v-if="isShowBtn && showMask" class="swipe-action_mask" @tap.stop="closeButtonGroup"
  26. @touchstart.stop.prevent="closeButtonGroup"></view>
  27. </view>
  28. </template>
  29. <script>
  30. export default {
  31. name: 'tuiSwipeAction',
  32. emits: ['click'],
  33. props: {
  34. // name: '删除',
  35. // color: '#fff',
  36. // fontsize: 32,//单位rpx
  37. // width: 80, //单位px
  38. // icon: 'like.png',//此处为图片地址
  39. // background: '#ed3f14'
  40. actions: {
  41. type: Array,
  42. default () {
  43. return [];
  44. }
  45. },
  46. //点击按钮时是否自动关闭
  47. closable: {
  48. type: Boolean,
  49. default: true
  50. },
  51. //设为false,可以滑动多行不关闭菜单
  52. showMask: {
  53. type: Boolean,
  54. default: true
  55. },
  56. operateWidth: {
  57. type: Number,
  58. default: 80
  59. },
  60. params: {
  61. type: Object,
  62. default () {
  63. return {};
  64. }
  65. },
  66. //禁止滑动
  67. forbid: {
  68. type: Boolean,
  69. default: false
  70. },
  71. //手动开关
  72. open: {
  73. type: Boolean,
  74. default: false
  75. },
  76. //背景色
  77. backgroundColor: {
  78. type: String,
  79. default: '#fff'
  80. }
  81. },
  82. watch: {
  83. actions(newValue, oldValue) {
  84. this.updateButtonSize();
  85. },
  86. open(newValue) {
  87. this.manualSwitch(newValue);
  88. }
  89. },
  90. data() {
  91. return {
  92. //start position
  93. tStart: {
  94. pageX: 0,
  95. pageY: 0
  96. },
  97. //限制滑动距离
  98. limitMove: 0,
  99. //move position
  100. position: {
  101. pageX: 0,
  102. pageY: 0
  103. },
  104. isShowBtn: false,
  105. move: false
  106. };
  107. },
  108. mounted() {
  109. this.updateButtonSize();
  110. },
  111. methods: {
  112. swipeDirection(x1, x2, y1, y2) {
  113. return Math.abs(x1 - x2) >= Math.abs(y1 - y2) ? (x1 - x2 > 0 ? 'Left' : 'Right') : y1 - y2 > 0 ? 'Up' :
  114. 'Down';
  115. },
  116. //阻止事件冒泡
  117. loop() {
  118. },
  119. updateButtonSize() {
  120. const actions = this.actions;
  121. if (actions.length > 0) {
  122. const query = uni.createSelectorQuery().in(this);
  123. let limitMovePosition = 0;
  124. actions.forEach(item => {
  125. limitMovePosition += item.width || 0;
  126. });
  127. this.limitMove = limitMovePosition;
  128. } else {
  129. this.limitMove = this.operateWidth;
  130. }
  131. },
  132. handlerTouchstart(event) {
  133. if (this.forbid) return;
  134. let touches = event.touches
  135. if (touches && touches.length > 1) return;
  136. this.move = true;
  137. touches = touches ? event.touches[0] : {};
  138. if (!touches || (touches.pageX === undefined && touches.pageY === undefined)) {
  139. touches = {
  140. pageX: event.pageX,
  141. pageY: event.pageY
  142. };
  143. }
  144. const tStart = this.tStart;
  145. if (touches) {
  146. for (let i in tStart) {
  147. if (touches[i]) {
  148. tStart[i] = touches[i];
  149. }
  150. }
  151. }
  152. },
  153. swipper(touches) {
  154. const start = this.tStart;
  155. const spacing = {
  156. pageX: touches.pageX - start.pageX,
  157. pageY: touches.pageY - start.pageY
  158. };
  159. if (this.limitMove < Math.abs(spacing.pageX)) {
  160. spacing.pageX = -this.limitMove;
  161. }
  162. this.position = spacing;
  163. },
  164. handlerTouchmove(event) {
  165. if (this.forbid || !this.move) return;
  166. const start = this.tStart;
  167. let touches = event.touches ? event.touches[0] : {};
  168. if (!touches || (touches.pageX === undefined && touches.pageY === undefined)) {
  169. touches = {
  170. pageX: event.pageX,
  171. pageY: event.pageY
  172. };
  173. }
  174. if (touches) {
  175. const direction = this.swipeDirection(start.pageX, touches.pageX, start.pageY, touches.pageY);
  176. if (direction === 'Left' && Math.abs(this.position.pageX) !== this.limitMove) {
  177. this.swipper(touches);
  178. }
  179. }
  180. },
  181. handlerTouchend(event) {
  182. if (this.forbid || !this.move) return;
  183. this.move = false;
  184. const start = this.tStart;
  185. let touches = event.changedTouches ? event.changedTouches[0] : {};
  186. if (!touches || (touches.pageX === undefined && touches.pageY === undefined)) {
  187. touches = {
  188. pageX: event.pageX,
  189. pageY: event.pageY
  190. };
  191. }
  192. if (touches) {
  193. const direction = this.swipeDirection(start.pageX, touches.pageX, start.pageY, touches.pageY);
  194. const spacing = {
  195. pageX: touches.pageX - start.pageX,
  196. pageY: touches.pageY - start.pageY
  197. };
  198. if (Math.abs(spacing.pageX) >= 40 && direction === 'Left') {
  199. spacing.pageX = spacing.pageX < 0 ? -this.limitMove : this.limitMove;
  200. this.isShowBtn = true;
  201. } else {
  202. spacing.pageX = 0;
  203. }
  204. if (spacing.pageX== 0) {
  205. this.isShowBtn = false;
  206. }
  207. this.position = spacing;
  208. }
  209. },
  210. handlerButton(event) {
  211. if (this.closable) {
  212. this.closeButtonGroup();
  213. }
  214. const dataset = event.currentTarget.dataset;
  215. this.$emit('click', {
  216. index: Number(dataset.index),
  217. item: this.params
  218. });
  219. },
  220. closeButtonGroup() {
  221. this.position = {
  222. pageX: 0,
  223. pageY: 0
  224. };
  225. this.isShowBtn = false;
  226. },
  227. //控制自定义按钮菜单
  228. handlerParentButton(event) {
  229. if (this.closable) {
  230. this.closeButtonGroup();
  231. }
  232. },
  233. manualSwitch(isOpen) {
  234. let x = 0;
  235. if (isOpen) {
  236. if (this.actions.length === 0) {
  237. x = this.operateWidth;
  238. } else {
  239. let width = 0;
  240. this.actions.forEach(item => {
  241. width += item.width;
  242. });
  243. x = width;
  244. }
  245. }
  246. this.position = {
  247. pageX: -x,
  248. pageY: 0
  249. };
  250. },
  251. px(num) {
  252. return uni.upx2px(num) + 'px';
  253. }
  254. }
  255. };
  256. </script>
  257. <style scoped>
  258. .tui-swipeout-wrap {
  259. position: relative;
  260. overflow: hidden;
  261. }
  262. .swipe-action-show {
  263. position: relative;
  264. z-index: 998;
  265. }
  266. .tui-swipeout-item {
  267. width: 100%;
  268. /* padding: 15px 20px; */
  269. box-sizing: border-box;
  270. transition: transform 0.2s ease;
  271. font-size: 14px;
  272. cursor: pointer;
  273. }
  274. .tui-swipeout-content {
  275. white-space: nowrap;
  276. overflow: hidden;
  277. }
  278. .tui-swipeout-button-right-group {
  279. position: absolute;
  280. right: -100%;
  281. top: 0;
  282. height: 100%;
  283. z-index: 1;
  284. width: 100%;
  285. }
  286. .tui-swipeout-button-right-item {
  287. height: 100%;
  288. float: left;
  289. white-space: nowrap;
  290. box-sizing: border-box;
  291. display: flex;
  292. align-items: center;
  293. justify-content: center;
  294. text-align: center;
  295. }
  296. .swipe-action_mask {
  297. display: block;
  298. opacity: 0;
  299. position: fixed;
  300. z-index: 997;
  301. top: 0;
  302. left: 0;
  303. width: 100%;
  304. height: 100%;
  305. }
  306. </style>