1 | <template>
|
2 | <x-cell
|
3 | v-clickoutside:touchstart="swipeMove"
|
4 | @click.native="swipeMove()"
|
5 | @touchstart.native="startDrag"
|
6 | @touchmove.native="onDrag"
|
7 | @touchend.native="endDrag"
|
8 | class="mint-cell-swipe"
|
9 | :title="title"
|
10 | :icon="icon"
|
11 | :label="label"
|
12 | :to="to"
|
13 | :is-link="isLink"
|
14 | ref="cell"
|
15 | :value="value">
|
16 | <div
|
17 | slot="right"
|
18 | class="mint-cell-swipe-buttongroup"
|
19 | ref="right">
|
20 | <a
|
21 | class="mint-cell-swipe-button"
|
22 | v-for="btn in right"
|
23 | :style="btn.style"
|
24 | @click.prevent.stop="btn.handler && btn.handler(), swipeMove()"
|
25 | v-html="btn.content"></a>
|
26 | </div>
|
27 | <div
|
28 | slot="left"
|
29 | class="mint-cell-swipe-buttongroup"
|
30 | ref="left">
|
31 | <a
|
32 | class="mint-cell-swipe-button"
|
33 | v-for="btn in left"
|
34 | :style="btn.style"
|
35 | @click.prevent.stop="btn.handler && btn.handler(), swipeMove()"
|
36 | v-html="btn.content"></a>
|
37 | </div>
|
38 | <slot></slot>
|
39 | <span
|
40 | v-if="$slots.title"
|
41 | slot="title">
|
42 | <slot name="title"></slot>
|
43 | </span>
|
44 | <span
|
45 | v-if="$slots.icon"
|
46 | slot="icon">
|
47 | <slot name="icon"></slot>
|
48 | </span>
|
49 | </x-cell>
|
50 | </template>
|
51 |
|
52 | <script>
|
53 | import { once } from 'mint-ui/src/utils/dom';
|
54 | import XCell from 'mint-ui/packages/cell/index.js';
|
55 | import Clickoutside from 'mint-ui/src/utils/clickoutside';
|
56 | if (process.env.NODE_ENV === 'component') {
|
57 | require('mint-ui/packages/cell/style.css');
|
58 | }
|
59 |
|
60 | /**
|
61 | * mt-cell-swipe
|
62 | * @desc 类似 iOS 滑动 Cell 的效果
|
63 | * @module components/cell-swipe
|
64 | *
|
65 | * @example
|
66 | * <mt-cell-swipe
|
67 | * :left=[
|
68 | * {
|
69 | * content: 'text',
|
70 | * style: {color: 'white', backgroundColor: 'red'},
|
71 | * handler(e) => console.log(123)
|
72 | * }
|
73 | * ]
|
74 | * :right=[{ content: 'allowed HTML' }]>
|
75 | * swipe me
|
76 | * </mt-cell-swipe>
|
77 | */
|
78 | export default {
|
79 | name: 'mt-cell-swipe',
|
80 |
|
81 | components: { XCell },
|
82 |
|
83 | directives: { Clickoutside },
|
84 |
|
85 | props: {
|
86 | to: String,
|
87 | left: Array,
|
88 | right: Array,
|
89 | icon: String,
|
90 | title: String,
|
91 | label: String,
|
92 | isLink: Boolean,
|
93 | value: {}
|
94 | },
|
95 |
|
96 | data() {
|
97 | return {
|
98 | start: { x: 0, y: 0 }
|
99 | };
|
100 | },
|
101 |
|
102 | mounted() {
|
103 | this.wrap = this.$refs.cell.$el.querySelector('.mint-cell-wrapper');
|
104 | this.leftElm = this.$refs.left;
|
105 | this.rightElm = this.$refs.right;
|
106 | this.leftWrapElm = this.leftElm.parentNode;
|
107 | this.rightWrapElm = this.rightElm.parentNode;
|
108 | this.leftWidth = this.leftElm.getBoundingClientRect().width;
|
109 | this.rightWidth = this.rightElm.getBoundingClientRect().width;
|
110 |
|
111 | this.leftDefaultTransform = this.translate3d(-this.leftWidth - 1);
|
112 | this.rightDefaultTransform = this.translate3d(this.rightWidth);
|
113 |
|
114 | this.rightWrapElm.style.webkitTransform = this.rightDefaultTransform;
|
115 | this.leftWrapElm.style.webkitTransform = this.leftDefaultTransform;
|
116 | },
|
117 |
|
118 | methods: {
|
119 | resetSwipeStatus() {
|
120 | this.swiping = false;
|
121 | this.opened = true;
|
122 | this.offsetLeft = 0;
|
123 | },
|
124 |
|
125 | translate3d(offset) {
|
126 | return `translate3d(${offset}px, 0, 0)`;
|
127 | },
|
128 |
|
129 | setAnimations(val) {
|
130 | this.wrap.style.transitionDuration = val;
|
131 | this.rightWrapElm.style.transitionDuration = val;
|
132 | this.leftWrapElm.style.transitionDuration = val;
|
133 | },
|
134 |
|
135 | swipeMove(offset = 0) {
|
136 | this.wrap.style.webkitTransform = this.translate3d(offset);
|
137 | this.rightWrapElm.style.webkitTransform = this.translate3d(this.rightWidth + offset);
|
138 | this.leftWrapElm.style.webkitTransform = this.translate3d(-this.leftWidth + offset);
|
139 | offset && (this.swiping = true);
|
140 | },
|
141 |
|
142 | swipeLeaveTransition(direction) {
|
143 | setTimeout(() => {
|
144 | this.swipeLeave = true;
|
145 |
|
146 |
|
147 | if (direction > 0 && -this.offsetLeft > this.rightWidth * 0.4) {
|
148 | this.swipeMove(-this.rightWidth);
|
149 | this.resetSwipeStatus();
|
150 | return;
|
151 |
|
152 | } else if (direction < 0 && this.offsetLeft > this.leftWidth * 0.4) {
|
153 | this.swipeMove(this.leftWidth);
|
154 | this.resetSwipeStatus();
|
155 | return;
|
156 | }
|
157 |
|
158 | this.swipeMove(0);
|
159 | once(this.wrap, 'webkitTransitionEnd', _ => {
|
160 | this.wrap.style.webkitTransform = '';
|
161 | this.rightWrapElm.style.webkitTransform = this.rightDefaultTransform;
|
162 | this.leftWrapElm.style.webkitTransform = this.leftDefaultTransform;
|
163 | this.swipeLeave = false;
|
164 | this.swiping = false;
|
165 | });
|
166 | }, 0);
|
167 | },
|
168 |
|
169 | startDrag(evt) {
|
170 | evt = evt.changedTouches ? evt.changedTouches[0] : evt;
|
171 | this.dragging = true;
|
172 | this.start.x = evt.pageX;
|
173 | this.start.y = evt.pageY;
|
174 | this.direction = '';
|
175 | },
|
176 |
|
177 | onDrag(evt) {
|
178 | if (this.opened) {
|
179 | if (!this.swiping) {
|
180 | this.swipeMove(0);
|
181 | this.setAnimations('');
|
182 | }
|
183 | this.opened = false;
|
184 | return;
|
185 | }
|
186 | if (!this.dragging) return;
|
187 |
|
188 | let swiping;
|
189 | const e = evt.changedTouches ? evt.changedTouches[0] : evt;
|
190 | const offsetTop = e.pageY - this.start.y;
|
191 | const offsetLeft = this.offsetLeft = e.pageX - this.start.x;
|
192 |
|
193 | const y = Math.abs(offsetTop);
|
194 | const x = Math.abs(offsetLeft);
|
195 |
|
196 | this.setAnimations('0ms');
|
197 |
|
198 | if (this.direction === '') {
|
199 | this.direction = x > y ? 'horizonal' : 'vertical';
|
200 | }
|
201 |
|
202 | if (this.direction === 'horizonal') {
|
203 | evt.preventDefault();
|
204 | evt.stopPropagation();
|
205 |
|
206 | swiping = !(x < 5 || (x >= 5 && y >= x * 1.73));
|
207 | if (!swiping) return;
|
208 |
|
209 | if ((offsetLeft < 0 && -offsetLeft > this.rightWidth) ||
|
210 | (offsetLeft > 0 && offsetLeft > this.leftWidth) ||
|
211 | (offsetLeft > 0 && !this.leftWidth) ||
|
212 | (offsetLeft < 0 && !this.rightWidth)) {
|
213 | } else {
|
214 | this.swipeMove(offsetLeft);
|
215 | }
|
216 | }
|
217 | },
|
218 |
|
219 | endDrag() {
|
220 | this.direction = '';
|
221 | this.setAnimations('');
|
222 | if (!this.swiping) return;
|
223 | this.swipeLeaveTransition(this.offsetLeft > 0 ? -1 : 1);
|
224 | }
|
225 | }
|
226 | };
|
227 | </script>
|
228 |
|
229 | <style lang="css">
|
230 | @import "../../../src/style/var.css";
|
231 |
|
232 | @component-namespace mint {
|
233 | @component cell-swipe {
|
234 | @descendent buttongroup {
|
235 | height: 100%;
|
236 | }
|
237 |
|
238 | @descendent button {
|
239 | height: 100%;
|
240 | display: inline-block;
|
241 | padding: 0 10px;
|
242 | line-height: 48px;
|
243 | }
|
244 |
|
245 | .mint-cell-wrapper {
|
246 | position: relative;
|
247 | }
|
248 |
|
249 | .mint-cell-wrapper,
|
250 | .mint-cell-left,
|
251 | .mint-cell-right {
|
252 | transition: transform 150ms ease-in-out;
|
253 | }
|
254 | }
|
255 | }
|
256 | </style>
|