uni-swipe-action.vue 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. <template>
  2. <view class="uni-swipe-action">
  3. <view :class="{'uni-swipe-action--show':isShowBtn}" :style="{'transform':transformX,'-webkit-transform':transformX}" class="uni-swipe-action__container" @touchstart="touchStart" @touchmove="touchMove" @touchend="touchEnd" @touchcancel="touchEnd" @click="bindClickCont">
  4. <view class="uni-swipe-action__content">
  5. <slot />
  6. </view>
  7. <view :id="elId" class="uni-swipe-action__btn-group">
  8. <div v-for="(item,index) in options" :key="index" :style="{backgroundColor: item.style && item.style.backgroundColor ? item.style.backgroundColor : '#C7C6CD',color: item.style && item.style.color ? item.style.color : '#FFFFFF',fontSize: item.style && item.style.fontSize ? item.style.fontSize : '28upx'}" class="uni-swipe-action--btn" @click="bindClickBtn(item,index)">
  9. {{ item.text }}
  10. </div>
  11. </view>
  12. </view>
  13. <view v-if="isShowBtn" class="uni-swipe-action__mask" @click="close" @touchmove.stop.prevent="close" />
  14. </view>
  15. </template>
  16. <script>
  17. export default {
  18. name: 'UniSwipeAction',
  19. props: {
  20. isDrag: {
  21. type: Boolean,
  22. default: false
  23. },
  24. isOpened: {
  25. type: Boolean,
  26. default: false
  27. },
  28. disabled: {
  29. type: Boolean,
  30. default: false
  31. },
  32. autoClose: {
  33. type: Boolean,
  34. default: true
  35. },
  36. options: {
  37. type: Array,
  38. default () {
  39. return []
  40. }
  41. }
  42. },
  43. data() {
  44. /**
  45. * TODO 兼容新旧编译器
  46. * 新编译器(自定义组件模式)下必须使用固定数值,否则部分平台下会获取不到节点。
  47. * 随机数值是在旧编译器下使用的,旧编译器模式已经不推荐使用,后续直接废掉随机数值的写法。
  48. */
  49. const elId = this.__call_hook ? 'uni_swipe_action' : `Uni_${Math.ceil(Math.random() * 10e5).toString(36)}`
  50. return {
  51. elId: elId,
  52. isShowBtn: false,
  53. transformX: 'translateX(0px)'
  54. }
  55. },
  56. watch: {
  57. isOpened(newValue, oldValue) {
  58. this.isShowBtn = !!newValue
  59. this.endMove()
  60. }
  61. },
  62. created() {
  63. this.direction = ''
  64. this.startX = 0
  65. this.startY = 0
  66. this.btnGroupWidth = 0
  67. this.isMoving = false
  68. this.startTime = 0
  69. },
  70. // #ifdef H5
  71. mounted() {
  72. this.getSize()
  73. },
  74. // #endif
  75. // #ifndef H5
  76. onReady() {
  77. this.getSize()
  78. },
  79. // #endif
  80. methods: {
  81. getSize() {
  82. uni.createSelectorQuery().in(this).select(`#${this.elId}`).boundingClientRect().exec((ret) => {
  83. this.btnGroupWidth = ret[0].width
  84. })
  85. if (this.isOpened) {
  86. this.isShowBtn = true
  87. this.endMove()
  88. }
  89. },
  90. bindClickBtn(item, index) {
  91. this.$emit('click', {
  92. text: item.text,
  93. style: item.style,
  94. index: index
  95. })
  96. },
  97. bindClickCont(e) {
  98. if (this.isShowBtn && this.autoClose) {
  99. this.isShowBtn = false
  100. this.endMove()
  101. }
  102. },
  103. touchStart(event) {
  104. this.startTime = event.timeStamp
  105. this.startX = event.touches[0].pageX
  106. this.startY = event.touches[0].pageY
  107. },
  108. touchMove(event) {
  109. console.log(JSON.stringify(event))
  110. if (this.direction === 'Y' || this.disabled) {
  111. console.log('disabled');
  112. return
  113. }
  114. var moveY = event.touches[0].pageY - this.startY
  115. var moveX = event.touches[0].pageX - this.startX
  116. if (!this.isMoving && Math.abs(moveY) > Math.abs(moveX)) { // 纵向滑动
  117. this.direction = 'Y'
  118. return
  119. }
  120. this.direction = moveX > 0 ? 'right' : 'left'
  121. this.isMoving = true
  122. if (this.isDrag) {
  123. let movedLength = this.isShowBtn ? -this.btnGroupWidth : 0
  124. if (movedLength + moveX >= 0) {
  125. this.transformX = `translateX(0px)`
  126. return
  127. }
  128. if (-movedLength - moveX >= this.btnGroupWidth) {
  129. this.transformX = `translateX(${-this.btnGroupWidth}px)`
  130. return
  131. }
  132. if (movedLength - moveX > 0) {
  133. this.transformX = `translateX(${moveX}px)`
  134. } else {
  135. if (moveX >= -this.btnGroupWidth) {
  136. this.transformX = `translateX(${moveX - this.btnGroupWidth}px)`
  137. }
  138. }
  139. }
  140. },
  141. touchEnd(event) {
  142. this.isMoving = false
  143. if (this.direction !== 'right' && this.direction !== 'left') {
  144. this.direction = ''
  145. return
  146. }
  147. if (this.isDrag) {
  148. let movedLength = Math.abs(Number(this.transformX.slice(11, -3)))
  149. let movedTime = event.timeStamp - this.startTime
  150. this.isShowBtn = movedLength >= this.btnGroupWidth / 2
  151. if (movedTime > 50 && movedTime < 300 && movedLength > 20) { // 在这个时间里面,且滑动了一定的距离
  152. if (this.direction === 'right') {
  153. this.isShowBtn = false
  154. } else {
  155. this.isShowBtn = true
  156. }
  157. }
  158. } else {
  159. if (this.direction === 'right') {
  160. this.isShowBtn = false
  161. } else {
  162. this.isShowBtn = true
  163. }
  164. }
  165. this.endMove()
  166. },
  167. endMove() {
  168. if (this.direction === 'Y' || this.disabled) {
  169. this.direction = ''
  170. return
  171. }
  172. if (this.isShowBtn) {
  173. this.transformX = `translateX(${-this.btnGroupWidth}px)`
  174. this.$emit('opened')
  175. } else {
  176. this.transformX = 'translateX(0px)'
  177. this.$emit('closed')
  178. }
  179. this.direction = ''
  180. },
  181. close() {
  182. this.isShowBtn = false
  183. this.endMove()
  184. }
  185. }
  186. }
  187. </script>
  188. <style>
  189. @charset "UTF-8";
  190. .uni-swipe-action {
  191. width: 100%;
  192. overflow: hidden
  193. }
  194. .uni-swipe-action__container {
  195. position: relative;
  196. background-color: #fff;
  197. width: 200%;
  198. display: flex;
  199. flex-direction: row;
  200. flex-wrap: wrap;
  201. transition: transform 350ms cubic-bezier(.165, .84, .44, 1)
  202. }
  203. .uni-swipe-action__content {
  204. width: 50%
  205. }
  206. .uni-swipe-action__btn-group {
  207. display: flex;
  208. flex-direction: row
  209. }
  210. .uni-swipe-action--show {
  211. position: relative;
  212. z-index: 1000
  213. }
  214. .uni-swipe-action--btn {
  215. padding: 0 32upx;
  216. color: #fff;
  217. background-color: #c7c6cd;
  218. font-size: 28upx;
  219. display: inline-flex;
  220. text-align: center;
  221. flex-direction: row;
  222. align-items: center
  223. }
  224. .uni-swipe-action__mask {
  225. display: block;
  226. opacity: 0;
  227. position: fixed;
  228. z-index: 999;
  229. top: 0;
  230. left: 0;
  231. width: 100%;
  232. height: 100%
  233. }
  234. </style>