我已经创建了一个 codepen 我想要实现的目标。
我正在使用几个水平可拖动项目创建时间线概述。除了
jquery
和 jquery-ui
之外,我还使用 jquery-ui-touch-punch
来实现移动设备上的所有手势。在桌面上一切正常,但在移动设备上却不行。
不幸的是,
$(".timeLineItemContainer").draggable({ ... });
极具侵略性,它还吞噬了我的垂直滚动行为,导致当我触摸其中一个项目时,我无法在移动设备上向下滚动。
不知何故,我需要根据滚动操作的方向启用或禁用项目的拖动功能。当我禁用 event.preventDefault();
中的
jquery-ui-touch-punch.js
时,它会向下滚动,然后
click
和
horizontal move
无法正常工作(移动设备上的水平滑动指的是 History.goback,我想阻止它我的手势需要它)。总而言之:我想要的只是防止默认行为,垂直滚动除外。
对于如何实现这一目标有什么想法吗?或者还有其他想法/方法来实现这一目标吗?
HTML(只是多个项目的简单列表)
<div class="timeLineItemContainer">
<div class="subItemMiddleContainer">
<div class="timeLineHeader">
<div>
<span>A TITLE</span>
</div>
<div>
<div>Some other text text</div>
</div>
</div>
</div>
<div class="subItemBottomContainer">
<ul>
<li class="static">
<div class="timeLineRight timeLineInfo">
<span class="info">Thu 12 March</span>
</div>
</li>
<li class="static">
<div class="timeLineRight timeLineInfo">
<span class="label">some additional text</span>
<span class="info time strikethrough">bla</span>
<div class="info time attention">
<span>yup</span>
</div>
</div>
</li>
<li class="toggable closed">
<div class="timeLineRight timeLineInfo">
<span class="label">Text</span>
<span class="info">content</span>
</div>
</li>
<li class="toggable closed">
<div class="timeLineRight timeLineInfo">
<span class="label">title</span>
<span class="info">value</span>
</div>
</li>
<li class="toggable closed">
<div class="timeLineRight timeLineInfo">
<span class="label">Another title</span>
<span class="info">another value</span>
</div>
</li>
<li class="static">
<div class="timeLineRight timeLineInfo">
<span class="label">always visible</span>
<span class="info time strikethrough">just because</span>
<div class="info attention">
<span>attention!</span>
</div>
</div>
</li>
</ul>
</div>
</div>
<div class="timeLineItemContainer">
<div class="subItemMiddleContainer">
<div class="timeLineLeft">
<div class="timeLinePipe"></div>
</div>
<div class="timeLineHeader">
<div>
<span>Some other text</span>
</div>
<div>
<div>Ola!</div>
</div>
</div>
</div>
<div class="subItemBottomContainer">
<ul>
<li class="static">
<div class="timeLineRight timeLineInfo">
<span class="info">A date</span>
</div>
</li>
<li class="toggable closed">
<div class="timeLineRight timeLineInfo">
<span class="label">label</span>
<span class="info">value</span>
</div>
</li>
<li class="toggable closed">
<div class="timeLineRight timeLineInfo">
<span class="label">anotehr label</span>
<span class="info time">different value</span>
</div>
</li>
</ul>
</div>
</div>
<div class="timeLineItemContainer">
<div class="subItemMiddleContainer">
<div class="timeLineHeader">
<div>
<span>A TITLE</span>
</div>
<div>
<div>Some other text text</div>
</div>
</div>
</div>
<div class="subItemBottomContainer">
<ul>
<li class="static">
<div class="timeLineRight timeLineInfo">
<span class="info">Thu 12 March</span>
</div>
</li>
<li class="static">
<div class="timeLineRight timeLineInfo">
<span class="label">some additional text</span>
<span class="info time strikethrough">bla</span>
<div class="info time attention">
<span>yup</span>
</div>
</div>
</li>
<li class="toggable closed">
<div class="timeLineRight timeLineInfo">
<span class="label">Text</span>
<span class="info">content</span>
</div>
</li>
<li class="toggable closed">
<div class="timeLineRight timeLineInfo">
<span class="label">title</span>
<span class="info">value</span>
</div>
</li>
<li class="toggable closed">
<div class="timeLineRight timeLineInfo">
<span class="label">Another title</span>
<span class="info">another value</span>
</div>
</li>
<li class="static">
<div class="timeLineRight timeLineInfo">
<span class="label">always visible</span>
<span class="info time strikethrough">just because</span>
<div class="info attention">
<span>attention!</span>
</div>
</div>
</li>
</ul>
</div>
</div>
<div class="timeLineItemContainer">
<div class="subItemMiddleContainer">
<div class="timeLineLeft">
<div class="timeLinePipe"></div>
</div>
<div class="timeLineHeader">
<div>
<span>Some other text</span>
</div>
<div>
<div>Ola!</div>
</div>
</div>
</div>
<div class="subItemBottomContainer">
<ul>
<li class="static">
<div class="timeLineRight timeLineInfo">
<span class="info">A date</span>
</div>
</li>
<li class="toggable closed">
<div class="timeLineRight timeLineInfo">
<span class="label">label</span>
<span class="info">value</span>
</div>
</li>
<li class="toggable closed">
<div class="timeLineRight timeLineInfo">
<span class="label">anotehr label</span>
<span class="info time">different value</span>
</div>
</li>
</ul>
</div>
</div>
Javascript
var sw = screen.width;
var thresholdPercentageSwipeTimeLineItem = 15;
var thresholdPercentageSwipeDetailScreen = 10;
$(".timeLineItemContainer").draggable({
scroll: false,
axis: "x",
drag: function (event, ui) {
if (ui.position.left < 0) {
ui.position.left = 0;
}
else {
if (calculateOffsetPercentage(ui.position.left) > thresholdPercentageSwipeTimeLineItem) {
return false;
};
}
},
stop: function (event, ui) {
$(this).animate({
left: 0,
}, {
duration: 200,
queue: false
});
}
}).click(function () {
var nextObjs = $(this).toggleClass("visible").find(".toggable");
$.each(nextObjs, function () {
$(this).stop().animate({
height: "toggle",
queue: false
}).toggleClass("closed");
});
});
function calculateOffsetPercentage(screenValue) {
return (100 / (sw / screenValue));
}
draggable
自从您上次发表这篇文章以来可能已经发生了变化。在我的测试中,即使在完全删除
jquery-ui-touch-punch.js
之后,我发现拖动事件仍然被 jQuery UI
draggable
消耗。我不想深入研究 jQuery UI 代码并分叉库,所以我设计了一个相当简单的替代方案。在我的场景中,我有一个收件箱列表,您可以将其项目向左或向右拖动以显示操作按钮。整个收件箱项目必须是可拖动的——因此不能选择使用拖动手柄。
如果触摸是在
draggable
元素内启动的,
jQuery 的
draggable
会阻止触摸屏上的垂直滚动。因此,如果屏幕上充满了可拖动的收件箱项目,那么用户就会陷入困境——无法向上或向下滚动。对我有用的解决方案是测量光标垂直位置的任何变化,并使用
window.scrollBy
手动滚动窗口相同的量:
var firstY = null;
var lastY = null;
var currentY = null;
var vertScroll = false;
var initAdjustment = 0;
// record the initial position of the cursor on start of the touch
jqDraggableItem.on("touchstart", function(event) {
lastY = currentY = firstY = event.originalEvent.touches[0].pageY;
});
// fires whenever the cursor moves
jqDraggableItem.on("touchmove", function(event) {
currentY = event.originalEvent.touches[0].pageY;
var adjustment = lastY-currentY;
// Mimic native vertical scrolling where scrolling only starts after the
// cursor has moved up or down from its original position by ~30 pixels.
if (vertScroll == false && Math.abs(currentY-firstY) > 30) {
vertScroll = true;
initAdjustment = currentY-firstY;
}
// only apply the adjustment if the user has met the threshold for vertical scrolling
if (vertScroll == true) {
window.scrollBy(0,adjustment + initAdjustment);
lastY = currentY + adjustment;
}
});
// when the user lifts their finger, they will again need to meet the
// threshold before vertical scrolling starts.
jqDraggableItem.on("touchend", function(event) {
vertScroll = false;
});
这将非常模仿触摸设备上的本机滚动。
乍一看,似乎
$(".timeLineItemContainer").draggable({ ... });
正在消耗所有事件,但实际上是
jquery-ui-touch-punch.js
阻止了一切。
jquery-ui-touch-punch.js
的工作方式是阻止所有触摸事件结果执行默认行为。因此,我稍微调整了
jquery-ui-touch-punch.js
以获得正确的行为。
event.preventDefault()
函数中删除了
simulateMouseEvent()
。
mouseProto._touchStart
事件中
tsx = event.originalEvent.touches[0].clientX;
tsy = event.originalEvent.touches[0].clientY;
mouseProto._touchMove
事件中添加了一个条件来检查我是否在水平或垂直方向滚动。如果是水平方向,我将执行
event.preventDefault()
。为此,您可以使用:
// Higher then 1 means a (more) horizontal direction
// Lower then 1 means a (more) vertical direction
// 1 is an exact 45 degrees swipe, which will be handled as a vertical swipe
if ((Math.abs(CurrentX - StartX)) / Math.abs(CurrentY - StartY) > 1) {
event.preventDefault();
}
mouseProto._touchStart
和
mouseProto._touchMove
函数的最终结果如下所示:
var tsx = 0,
tsy = 0,
currentx = 0,
currenty = 0;
mouseProto._touchStart = function (event) {
var self = this;
tsx = event.originalEvent.touches[0].clientX;
tsy = event.originalEvent.touches[0].clientY;
// Ignore the event if another widget is already being handled
if (touchHandled || !self._mouseCapture(event.originalEvent.changedTouches[0])) {
return;
}
// Set the flag to prevent other widgets from inheriting the touch event
touchHandled = true;
// Track movement to determine if interaction was a click
self._touchMoved = false;
//// Simulate the mouseover event
simulateMouseEvent(event, 'mouseover');
//// Simulate the mousemove event
simulateMouseEvent(event, 'mousemove');
// Simulate the mousedown event
simulateMouseEvent(event, 'mousedown');
};
mouseProto._touchMove = function (event) {
// Ignore event if not handled
if (!touchHandled) {
return;
}
// Higher then 1 means a (more) horizontal direction
// Lower then 1 means a (more) vertical direction
// 1 is an exact 45 degrees swipe, which will be handled as a vertical swipe
if ((Math.abs(event.originalEvent.changedTouches[0].clientX - tsx)) / Math.abs(event.originalEvent.changedTouches[0].clientY - tsy) > 1) {
event.preventDefault();
}
// Interaction was not a click
this._touchMoved = true;
// Simulate the mousemove event
simulateMouseEvent(event, 'mousemove');
};