Skip to content

Commit

Permalink
#1448: Position ghost absolutely on IOS
Browse files Browse the repository at this point in the history
  • Loading branch information
owen-m1 authored Mar 9, 2019
1 parent 452436a commit e6b496d
Showing 1 changed file with 87 additions and 16 deletions.
103 changes: 87 additions & 16 deletions Sortable.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@

targetMoveDistance,

// For positioning ghost absolutely
ghostRelativeParent,
ghostRelativeParentInitialScroll = [], // (left, top)


forRepaintDummy,
realDragElRect, // dragEl rect after current animation
Expand All @@ -96,6 +100,9 @@
Edge = !!navigator.userAgent.match(/Edge/i),
FireFox = !!navigator.userAgent.match(/firefox/i),
Safari = !!(navigator.userAgent.match(/safari/i) && !navigator.userAgent.match(/chrome/i) && !navigator.userAgent.match(/android/i)),
IOS = !!(navigator.userAgent.match(/iP(ad|od|hone)/i)),

PositionGhostAbsolutely = IOS,

CSSFloatProperty = Edge || IE11OrLess ? 'cssFloat' : 'float',

Expand Down Expand Up @@ -353,6 +360,7 @@
// emulate drag over during autoscroll (fallback), emulating native DnD behaviour
if (isFallback && this.layer === 0) {
Sortable.active._emulateDragOver(true);
Sortable.active._onTouchMove(touchEvt, true);
}
var scrollOffsetY = autoScrolls[this.layer].vy ? autoScrolls[this.layer].vy * speed : 0;
var scrollOffsetX = autoScrolls[this.layer].vx ? autoScrolls[this.layer].vx * speed : 0;
Expand Down Expand Up @@ -948,9 +956,9 @@
}
},

_emulateDragOver: function (bypassLastTouchCheck) {
_emulateDragOver: function (forAutoScroll) {
if (touchEvt) {
if (this._lastX === touchEvt.clientX && this._lastY === touchEvt.clientY && !bypassLastTouchCheck) {
if (this._lastX === touchEvt.clientX && this._lastY === touchEvt.clientY && !forAutoScroll) {
return;
}
this._lastX = touchEvt.clientX;
Expand Down Expand Up @@ -995,7 +1003,7 @@
},


_onTouchMove: function (/**TouchEvent*/evt) {
_onTouchMove: function (/**TouchEvent*/evt, forAutoScroll) {
if (tapEvt) {
var options = this.options,
fallbackTolerance = options.fallbackTolerance,
Expand All @@ -1004,11 +1012,15 @@
matrix = ghostEl && _matrix(ghostEl),
scaleX = ghostEl && matrix && matrix.a,
scaleY = ghostEl && matrix && matrix.d,
dx = ((touch.clientX - tapEvt.clientX) + fallbackOffset.x) / (scaleX ? scaleX : 1),
dy = ((touch.clientY - tapEvt.clientY) + fallbackOffset.y) / (scaleY ? scaleY : 1),
relativeScrollOffset = PositionGhostAbsolutely && ghostRelativeParent && _getRelativeScrollOffset(ghostRelativeParent),
dx = ((touch.clientX - tapEvt.clientX)
+ fallbackOffset.x) / (scaleX || 1)
+ (relativeScrollOffset ? (relativeScrollOffset[0] - ghostRelativeParentInitialScroll[0]) : 0) / (scaleX || 1),
dy = ((touch.clientY - tapEvt.clientY)
+ fallbackOffset.y) / (scaleY || 1)
+ (relativeScrollOffset ? (relativeScrollOffset[1] - ghostRelativeParentInitialScroll[1]) : 0) / (scaleY || 1),
translate3d = evt.touches ? 'translate3d(' + dx + 'px,' + dy + 'px,0)' : 'translate(' + dx + 'px,' + dy + 'px)';


// only set the status to dragging, when we are actually dragging
if (!Sortable.active && !awaitingDragStarted) {
if (fallbackTolerance &&
Expand All @@ -1019,13 +1031,11 @@
this._onDragStart(evt, true);
}

this._handleAutoScroll(touch, true);

!forAutoScroll && this._handleAutoScroll(touch, true);

moved = true;
touchEvt = touch;


_css(ghostEl, 'webkitTransform', translate3d);
_css(ghostEl, 'mozTransform', translate3d);
_css(ghostEl, 'msTransform', translate3d);
Expand All @@ -1036,11 +1046,46 @@
},

_appendGhost: function () {
// Bug if using scale(): https://stackoverflow.com/questions/2637058
// Not being adjusted for
if (!ghostEl) {
var rect = _getRect(dragEl, this.options.fallbackOnBody ? document.body : rootEl, true),
var container = this.options.fallbackOnBody ? document.body : rootEl,
rect = _getRect(dragEl, true, container, !PositionGhostAbsolutely),
css = _css(dragEl),
options = this.options;

// Position absolutely
if (PositionGhostAbsolutely) {
// Get relatively positioned parent
ghostRelativeParent = container;

while (
_css(ghostRelativeParent, 'position') === 'static' &&
_css(ghostRelativeParent, 'transform') === 'none' &&
ghostRelativeParent !== document
) {
ghostRelativeParent = ghostRelativeParent.parentNode;
}

if (ghostRelativeParent !== document) {
var ghostRelativeParentRect = _getRect(ghostRelativeParent, true);

rect.top -= ghostRelativeParentRect.top;
rect.left -= ghostRelativeParentRect.left;
}

if (ghostRelativeParent !== document.body && ghostRelativeParent !== document.documentElement) {
if (ghostRelativeParent === document) ghostRelativeParent = _getWindowScrollingElement();

rect.top += ghostRelativeParent.scrollTop;
rect.left += ghostRelativeParent.scrollLeft;
} else {
ghostRelativeParent = _getWindowScrollingElement();
}
ghostRelativeParentInitialScroll = _getRelativeScrollOffset(ghostRelativeParent);
}


ghostEl = dragEl.cloneNode(true);

_toggleClass(ghostEl, options.ghostClass, false);
Expand All @@ -1054,11 +1099,11 @@
_css(ghostEl, 'width', rect.width);
_css(ghostEl, 'height', rect.height);
_css(ghostEl, 'opacity', '0.8');
_css(ghostEl, 'position', 'fixed');
_css(ghostEl, 'position', (PositionGhostAbsolutely ? 'absolute' : 'fixed'));
_css(ghostEl, 'zIndex', '100000');
_css(ghostEl, 'pointerEvents', 'none');

options.fallbackOnBody && document.body.appendChild(ghostEl) || rootEl.appendChild(ghostEl);
container.appendChild(ghostEl);
}
},

Expand Down Expand Up @@ -2245,7 +2290,7 @@
* @param {[Boolean]} adjustForTransform Whether the rect should compensate for parent's transform
* @return {Object} The boundingClientRect of el
*/
function _getRect(el, container, adjustForTransform) {
function _getRect(el, adjustForTransform, container, adjustForFixed) {
if (!el.getBoundingClientRect && el !== win) return;

var elRect,
Expand Down Expand Up @@ -2273,7 +2318,7 @@
width = window.innerWidth;
}

if (adjustForTransform && el !== win) {
if (adjustForFixed && el !== win) {
// Adjust for translate()
container = container || el.parentNode;

Expand All @@ -2295,9 +2340,11 @@
/* jshint boss:true */
} while (container = container.parentNode);
}
}

if (adjustForTransform && el !== win) {
// Adjust for scale()
var matrix = _matrix(el),
var matrix = _matrix(container || el),
scaleX = matrix && matrix.a,
scaleY = matrix && matrix.d;

Expand Down Expand Up @@ -2328,7 +2375,6 @@
* Checks if a side of an element is scrolled past a side of it's parents
* @param {HTMLElement} el The element who's side being scrolled out of view is in question
* @param {String} side Side of the element in question ('top', 'left', 'right', 'bottom')
* @return {int} Amount the element is overflowing over the specified side of it's parent scroll element
* @return {HTMLElement} The parent scroll element that the el's side is scrolled past, or null if there is no such element
*/
function _isScrolledPast(el, side) {
Expand Down Expand Up @@ -2356,6 +2402,31 @@
return false;
}

/**
* Returns the scroll offset of the given element, added with all the scroll offsets of parent elements.
* The value is returned in real pixels.
* @param {HTMLElement} el
* @return {Array} Offsets in the format of [left, top]
*/
function _getRelativeScrollOffset(el) {
var offsetLeft = 0,
offsetTop = 0,
winScroller = _getWindowScrollingElement();

if (el) {
do {
var matrix = _matrix(el),
scaleX = matrix.a,
scaleY = matrix.d;

offsetLeft += el.scrollLeft * scaleX;
offsetTop += el.scrollTop * scaleY;
} while (el !== winScroller && (el = el.parentNode));
}

return [offsetLeft, offsetTop];
}

// Fixed #973:
_on(document, 'touchmove', function(evt) {
if ((Sortable.active || awaitingDragStarted) && evt.cancelable) {
Expand Down

0 comments on commit e6b496d

Please sign in to comment.