|
70 | 70 |
|
71 | 71 | targetMoveDistance, |
72 | 72 |
|
| 73 | + // For positioning ghost absolutely |
| 74 | + ghostRelativeParent, |
| 75 | + ghostRelativeParentInitialScroll = [], // (left, top) |
| 76 | + |
73 | 77 |
|
74 | 78 | forRepaintDummy, |
75 | 79 | realDragElRect, // dragEl rect after current animation |
|
96 | 100 | Edge = !!navigator.userAgent.match(/Edge/i), |
97 | 101 | FireFox = !!navigator.userAgent.match(/firefox/i), |
98 | 102 | Safari = !!(navigator.userAgent.match(/safari/i) && !navigator.userAgent.match(/chrome/i) && !navigator.userAgent.match(/android/i)), |
| 103 | + IOS = !!(navigator.userAgent.match(/iP(ad|od|hone)/i)), |
| 104 | + |
| 105 | + PositionGhostAbsolutely = IOS, |
99 | 106 |
|
100 | 107 | CSSFloatProperty = Edge || IE11OrLess ? 'cssFloat' : 'float', |
101 | 108 |
|
|
353 | 360 | // emulate drag over during autoscroll (fallback), emulating native DnD behaviour |
354 | 361 | if (isFallback && this.layer === 0) { |
355 | 362 | Sortable.active._emulateDragOver(true); |
| 363 | + Sortable.active._onTouchMove(touchEvt, true); |
356 | 364 | } |
357 | 365 | var scrollOffsetY = autoScrolls[this.layer].vy ? autoScrolls[this.layer].vy * speed : 0; |
358 | 366 | var scrollOffsetX = autoScrolls[this.layer].vx ? autoScrolls[this.layer].vx * speed : 0; |
|
948 | 956 | } |
949 | 957 | }, |
950 | 958 |
|
951 | | - _emulateDragOver: function (bypassLastTouchCheck) { |
| 959 | + _emulateDragOver: function (forAutoScroll) { |
952 | 960 | if (touchEvt) { |
953 | | - if (this._lastX === touchEvt.clientX && this._lastY === touchEvt.clientY && !bypassLastTouchCheck) { |
| 961 | + if (this._lastX === touchEvt.clientX && this._lastY === touchEvt.clientY && !forAutoScroll) { |
954 | 962 | return; |
955 | 963 | } |
956 | 964 | this._lastX = touchEvt.clientX; |
|
995 | 1003 | }, |
996 | 1004 |
|
997 | 1005 |
|
998 | | - _onTouchMove: function (/**TouchEvent*/evt) { |
| 1006 | + _onTouchMove: function (/**TouchEvent*/evt, forAutoScroll) { |
999 | 1007 | if (tapEvt) { |
1000 | 1008 | var options = this.options, |
1001 | 1009 | fallbackTolerance = options.fallbackTolerance, |
|
1004 | 1012 | matrix = ghostEl && _matrix(ghostEl), |
1005 | 1013 | scaleX = ghostEl && matrix && matrix.a, |
1006 | 1014 | scaleY = ghostEl && matrix && matrix.d, |
1007 | | - dx = ((touch.clientX - tapEvt.clientX) + fallbackOffset.x) / (scaleX ? scaleX : 1), |
1008 | | - dy = ((touch.clientY - tapEvt.clientY) + fallbackOffset.y) / (scaleY ? scaleY : 1), |
| 1015 | + relativeScrollOffset = PositionGhostAbsolutely && ghostRelativeParent && _getRelativeScrollOffset(ghostRelativeParent), |
| 1016 | + dx = ((touch.clientX - tapEvt.clientX) |
| 1017 | + + fallbackOffset.x) / (scaleX || 1) |
| 1018 | + + (relativeScrollOffset ? (relativeScrollOffset[0] - ghostRelativeParentInitialScroll[0]) : 0) / (scaleX || 1), |
| 1019 | + dy = ((touch.clientY - tapEvt.clientY) |
| 1020 | + + fallbackOffset.y) / (scaleY || 1) |
| 1021 | + + (relativeScrollOffset ? (relativeScrollOffset[1] - ghostRelativeParentInitialScroll[1]) : 0) / (scaleY || 1), |
1009 | 1022 | translate3d = evt.touches ? 'translate3d(' + dx + 'px,' + dy + 'px,0)' : 'translate(' + dx + 'px,' + dy + 'px)'; |
1010 | 1023 |
|
1011 | | - |
1012 | 1024 | // only set the status to dragging, when we are actually dragging |
1013 | 1025 | if (!Sortable.active && !awaitingDragStarted) { |
1014 | 1026 | if (fallbackTolerance && |
|
1019 | 1031 | this._onDragStart(evt, true); |
1020 | 1032 | } |
1021 | 1033 |
|
1022 | | - this._handleAutoScroll(touch, true); |
1023 | | - |
| 1034 | + !forAutoScroll && this._handleAutoScroll(touch, true); |
1024 | 1035 |
|
1025 | 1036 | moved = true; |
1026 | 1037 | touchEvt = touch; |
1027 | 1038 |
|
1028 | | - |
1029 | 1039 | _css(ghostEl, 'webkitTransform', translate3d); |
1030 | 1040 | _css(ghostEl, 'mozTransform', translate3d); |
1031 | 1041 | _css(ghostEl, 'msTransform', translate3d); |
|
1036 | 1046 | }, |
1037 | 1047 |
|
1038 | 1048 | _appendGhost: function () { |
| 1049 | + // Bug if using scale(): https://stackoverflow.com/questions/2637058 |
| 1050 | + // Not being adjusted for |
1039 | 1051 | if (!ghostEl) { |
1040 | | - var rect = _getRect(dragEl, this.options.fallbackOnBody ? document.body : rootEl, true), |
| 1052 | + var container = this.options.fallbackOnBody ? document.body : rootEl, |
| 1053 | + rect = _getRect(dragEl, true, container, !PositionGhostAbsolutely), |
1041 | 1054 | css = _css(dragEl), |
1042 | 1055 | options = this.options; |
1043 | 1056 |
|
| 1057 | + // Position absolutely |
| 1058 | + if (PositionGhostAbsolutely) { |
| 1059 | + // Get relatively positioned parent |
| 1060 | + ghostRelativeParent = container; |
| 1061 | + |
| 1062 | + while ( |
| 1063 | + _css(ghostRelativeParent, 'position') === 'static' && |
| 1064 | + _css(ghostRelativeParent, 'transform') === 'none' && |
| 1065 | + ghostRelativeParent !== document |
| 1066 | + ) { |
| 1067 | + ghostRelativeParent = ghostRelativeParent.parentNode; |
| 1068 | + } |
| 1069 | + |
| 1070 | + if (ghostRelativeParent !== document) { |
| 1071 | + var ghostRelativeParentRect = _getRect(ghostRelativeParent, true); |
| 1072 | + |
| 1073 | + rect.top -= ghostRelativeParentRect.top; |
| 1074 | + rect.left -= ghostRelativeParentRect.left; |
| 1075 | + } |
| 1076 | + |
| 1077 | + if (ghostRelativeParent !== document.body && ghostRelativeParent !== document.documentElement) { |
| 1078 | + if (ghostRelativeParent === document) ghostRelativeParent = _getWindowScrollingElement(); |
| 1079 | + |
| 1080 | + rect.top += ghostRelativeParent.scrollTop; |
| 1081 | + rect.left += ghostRelativeParent.scrollLeft; |
| 1082 | + } else { |
| 1083 | + ghostRelativeParent = _getWindowScrollingElement(); |
| 1084 | + } |
| 1085 | + ghostRelativeParentInitialScroll = _getRelativeScrollOffset(ghostRelativeParent); |
| 1086 | + } |
| 1087 | + |
| 1088 | + |
1044 | 1089 | ghostEl = dragEl.cloneNode(true); |
1045 | 1090 |
|
1046 | 1091 | _toggleClass(ghostEl, options.ghostClass, false); |
|
1054 | 1099 | _css(ghostEl, 'width', rect.width); |
1055 | 1100 | _css(ghostEl, 'height', rect.height); |
1056 | 1101 | _css(ghostEl, 'opacity', '0.8'); |
1057 | | - _css(ghostEl, 'position', 'fixed'); |
| 1102 | + _css(ghostEl, 'position', (PositionGhostAbsolutely ? 'absolute' : 'fixed')); |
1058 | 1103 | _css(ghostEl, 'zIndex', '100000'); |
1059 | 1104 | _css(ghostEl, 'pointerEvents', 'none'); |
1060 | 1105 |
|
1061 | | - options.fallbackOnBody && document.body.appendChild(ghostEl) || rootEl.appendChild(ghostEl); |
| 1106 | + container.appendChild(ghostEl); |
1062 | 1107 | } |
1063 | 1108 | }, |
1064 | 1109 |
|
|
2245 | 2290 | * @param {[Boolean]} adjustForTransform Whether the rect should compensate for parent's transform |
2246 | 2291 | * @return {Object} The boundingClientRect of el |
2247 | 2292 | */ |
2248 | | - function _getRect(el, container, adjustForTransform) { |
| 2293 | + function _getRect(el, adjustForTransform, container, adjustForFixed) { |
2249 | 2294 | if (!el.getBoundingClientRect && el !== win) return; |
2250 | 2295 |
|
2251 | 2296 | var elRect, |
|
2273 | 2318 | width = window.innerWidth; |
2274 | 2319 | } |
2275 | 2320 |
|
2276 | | - if (adjustForTransform && el !== win) { |
| 2321 | + if (adjustForFixed && el !== win) { |
2277 | 2322 | // Adjust for translate() |
2278 | 2323 | container = container || el.parentNode; |
2279 | 2324 |
|
|
2295 | 2340 | /* jshint boss:true */ |
2296 | 2341 | } while (container = container.parentNode); |
2297 | 2342 | } |
| 2343 | + } |
2298 | 2344 |
|
| 2345 | + if (adjustForTransform && el !== win) { |
2299 | 2346 | // Adjust for scale() |
2300 | | - var matrix = _matrix(el), |
| 2347 | + var matrix = _matrix(container || el), |
2301 | 2348 | scaleX = matrix && matrix.a, |
2302 | 2349 | scaleY = matrix && matrix.d; |
2303 | 2350 |
|
|
2328 | 2375 | * Checks if a side of an element is scrolled past a side of it's parents |
2329 | 2376 | * @param {HTMLElement} el The element who's side being scrolled out of view is in question |
2330 | 2377 | * @param {String} side Side of the element in question ('top', 'left', 'right', 'bottom') |
2331 | | - * @return {int} Amount the element is overflowing over the specified side of it's parent scroll element |
2332 | 2378 | * @return {HTMLElement} The parent scroll element that the el's side is scrolled past, or null if there is no such element |
2333 | 2379 | */ |
2334 | 2380 | function _isScrolledPast(el, side) { |
|
2356 | 2402 | return false; |
2357 | 2403 | } |
2358 | 2404 |
|
| 2405 | + /** |
| 2406 | + * Returns the scroll offset of the given element, added with all the scroll offsets of parent elements. |
| 2407 | + * The value is returned in real pixels. |
| 2408 | + * @param {HTMLElement} el |
| 2409 | + * @return {Array} Offsets in the format of [left, top] |
| 2410 | + */ |
| 2411 | + function _getRelativeScrollOffset(el) { |
| 2412 | + var offsetLeft = 0, |
| 2413 | + offsetTop = 0, |
| 2414 | + winScroller = _getWindowScrollingElement(); |
| 2415 | + |
| 2416 | + if (el) { |
| 2417 | + do { |
| 2418 | + var matrix = _matrix(el), |
| 2419 | + scaleX = matrix.a, |
| 2420 | + scaleY = matrix.d; |
| 2421 | + |
| 2422 | + offsetLeft += el.scrollLeft * scaleX; |
| 2423 | + offsetTop += el.scrollTop * scaleY; |
| 2424 | + } while (el !== winScroller && (el = el.parentNode)); |
| 2425 | + } |
| 2426 | + |
| 2427 | + return [offsetLeft, offsetTop]; |
| 2428 | + } |
| 2429 | + |
2359 | 2430 | // Fixed #973: |
2360 | 2431 | _on(document, 'touchmove', function(evt) { |
2361 | 2432 | if ((Sortable.active || awaitingDragStarted) && evt.cancelable) { |
|
0 commit comments