【问题标题】:Horizontal dragging is blocking the default vertical scroll of the page水平拖动正在阻止页面的默认垂直滚动
【发布时间】:2015-06-24 10:30:43
【问题描述】:

我已经创建了一个 codepen 我想要实现的目标。

我正在创建一个包含几个水平可拖动项目的时间线概览。在jqueryjquery-ui 旁边,我还使用jquery-ui-touch-punch 来实现移动设备上的所有手势。在桌面上一切正常,但在移动设备上却不行。

不幸的是,$(".timeLineItemContainer").draggable({ ... }); 极具攻击性,它还吞噬了我的垂直滚动行为,导致当我触摸其中一项时,我无法在移动设备上向下滚动页面。 p>

不知何故,我需要启用或禁用项目的拖动功能,具体取决于滚动操作的方向。当我在jquery-ui-touch-punch.js 中禁用event.preventDefault(); 时,它会向下滚动,只有clickhorizontal move 不能正常工作(移动设备上的水平滑动指的是history.goback,我想阻止它作为我的手势需要它)。

总结一下:我想要的只是阻止默认行为,除了垂直滚动。

关于如何实现这一目标的任何想法?或者有没有其他想法/方法来实现这一点?

Here's a link for a mobile version

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));
}

【问题讨论】:

    标签: javascript jquery html mobile


    【解决方案1】:

    成功了!

    乍一看,$(".timeLineItemContainer").draggable({ ... }); 似乎正在消耗所有事件,但实际上是 jquery-ui-touch-punch.js 阻止了一切。

    jquery-ui-touch-punch.js 的工作方式是阻止所有触摸事件结果执行默认行为。因此,我稍微调整了jquery-ui-touch-punch.js 以获得正确的行为。

    1. 我从 simulateMouseEvent() 函数中删除了 event.preventDefault()

    2. 我将触摸的开始存储在mouseProto._touchStart 事件中

      tsx = event.originalEvent.touches[0].clientX; tsy = event.originalEvent.touches[0].clientY;

    3. 我在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._touchStartmouseProto._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');
    };
    

    【讨论】:

      【解决方案2】:

      我尝试了 OPs 解决方案,但我认为 jQuery UI draggable 可能在您上次发布此帖子后发生了变化。在我的测试中,即使在完全删除 jquery-ui-touch-punch.js 之后,我发现拖动事件仍然被 jQuery UI draggable 消耗。我不想深入研究 jQuery UI 代码并 fork 库,所以我设计了一个相当简单的替代方案。

      在我的场景中,我有一个收件箱列表,您可以将其项目拖动到左侧或右侧以显示操作按钮。整个收件箱项目必须是可拖动的——因此不能选择使用拖动手柄。

      如果触摸是在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;
      });
      

      这将非常模仿触摸设备上的原生滚动。

      【讨论】:

      • 能否请您为此创建一个 jsfiddle,谢谢!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-15
      • 2010-10-18
      • 1970-01-01
      • 1970-01-01
      • 2021-11-13
      相关资源
      最近更新 更多