当用户滑动 L 或 R 时重新排序堆栈中的 <img>

Reorder <img>s in a stack when user swipes L or R

假设 6 个绝对定位的 HTML 元素堆叠在另一个之上,A-F,“A”在顶部。

<body data-swipe-threshold="100">
<div><img alt = "F"/></div>
<div><img alt = "E"/></div>
<div><img alt = "D"/></div>
<div><img alt = "C"/></div>
<div><img alt = "B"/></div>
<div><img alt = "A"/></div>
</body>

对于启用触摸的屏幕,我希望用户能够在“A”上方向右滑动 div/image 以将其放在堆栈底部(露出“B”)。下一次向右滑动会显示“C”等。相反,向左滑动会将牌组底部的“卡片”带到顶部,在那里它变得可见。当“F”在顶部时,我会允许“F”通过向右滑动来显示“A”,并且还允许“A”通过向左滑动来显示“F”。我意识到我会操纵 zIndex,但我似乎无法 capture/process 以这种方式堆叠元素的滑动事件。 “swiped-events.js”是我正在使用的脚本。我的代码在这里:https://jsfiddle.net/okrcmLw5/ 但即使我在“fiddle”之外进行测试,该演示似乎也忽略了我的滑动。谢谢!

swiped-events.js 在用户滑动时创建一个事件。它在看到 touchstart 事件的元素上执行此操作,并在整个文档中查找 touchstart 事件。

将事件侦听器放在 deck 元素上会收到 'swipe' 事件,但事件的目标似乎是包含的 img 元素。所以我们不能使用 event.target 来找到整个刷过的 div,但是我们要移动的是整个刷过的 div。

如果我们不尝试使用 z-index 而是使用 JS prepend 和 appendChild 函数,我们可以将顶部元素移动到底部,反之亦然,而不必担心 div的child个元素(目前只有一个,但没有理由不能有几个,比如一个标题)被刷了。

这是执行此操作的 JS:

  document.getElementById('deck').addEventListener('swiped-left', function () { deck.appendChild(deck.firstElementChild); });
  document.getElementById('deck').addEventListener('swiped-right', function () { deck.prepend(deck.lastElementChild); });

这是一个片段。它包括来自 github.com/john-doherty/swiped-event 的所有 js 代码,以防获得 moved/deleted。在触摸设备上测试的代码片段 - iPad 和 IOS14.2 - 以及 Edge 开发工具 'emulator'.

/*!
 * swiped-events.js - v@version@
 * Pure JavaScript swipe events
 * https://github.com/john-doherty/swiped-events
 * @inspiration https://whosebug.com/questions/16348031/disable-scrolling-when-touch-moving-certain-element
 * @author John Doherty <www.johndoherty.info>
 * @license MIT
 */

function init() {

    'use strict';
    
    // patch CustomEvent to allow constructor creation (IE/Chrome)
    if (typeof window.CustomEvent !== 'function') {

        window.CustomEvent = function (event, params) {

            params = params || { bubbles: false, cancelable: false, detail: undefined };

            var evt = document.createEvent('CustomEvent');
            evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
            return evt;
        };

        window.CustomEvent.prototype = window.Event.prototype;
    }

    document.addEventListener('touchstart', handleTouchStart, false);
    document.addEventListener('touchmove', handleTouchMove, false);
    document.addEventListener('touchend', handleTouchEnd, false);

    var xDown = null;
    var yDown = null;
    var xDiff = null;
    var yDiff = null;
    var timeDown = null;
    var startEl = null;

    /**
     * Fires swiped event if swipe detected on touchend
     * @param {object} e - browser event object
     * @returns {void}
     */
    function handleTouchEnd(e) {

        // if the user released on a different target, cancel!
        // !!THIS NEVER HAPPENS AT LEAST ON SAFARI IPAD IOS 14.2, IF RELEASE OUTSIDE THE START ELEMENT IT GIVES THE TARGET AS THE START ELEMENT
        if (startEl !== e.target) { return;}

        var swipeThreshold = parseInt(getNearestAttribute(startEl, 'data-swipe-threshold', '20'), 10); // default 20px
        var swipeTimeout = parseInt(getNearestAttribute(startEl, 'data-swipe-timeout', '500'), 10);    // default 500ms
        var timeDiff = Date.now() - timeDown;
        var eventType = '';
        var changedTouches = e.changedTouches || e.touches || [];

        if (Math.abs(xDiff) > Math.abs(yDiff)) { // most significant
            if (Math.abs(xDiff) > swipeThreshold && timeDiff < swipeTimeout) {
                if (xDiff > 0) {
                    eventType = 'swiped-left';
                }
                else {
                    eventType = 'swiped-right';
                }
            }
        }
        else if (Math.abs(yDiff) > swipeThreshold && timeDiff < swipeTimeout) {
            if (yDiff > 0) {
                eventType = 'swiped-up';
            }
            else {
                eventType = 'swiped-down';
            }
        }

        if (eventType !== '') {

            var eventData = {
                dir: eventType.replace(/swiped-/, ''),
                xStart: parseInt(xDown, 10),
                xEnd: parseInt((changedTouches[0] || {}).clientX || -1, 10),
                yStart: parseInt(yDown, 10),
                yEnd: parseInt((changedTouches[0] || {}).clientY || -1, 10)
            };

            // fire `swiped` event event on the element that started the swipe
            startEl.dispatchEvent(new CustomEvent('swiped', { bubbles: true, cancelable: true, detail: eventData }));
            // fire `swiped-dir` event on the element that started the swipe
            startEl.dispatchEvent(new CustomEvent(eventType, { bubbles: true, cancelable: true, detail: eventData }));
        }

        // reset values
        xDown = null;
        yDown = null;
        timeDown = null;
    }

    /**
     * Records current location on touchstart event
     * @param {object} e - browser event object
     * @returns {void}
     */
    function handleTouchStart(e) {    
        
        // if the element has data-swipe-ignore="true" we stop listening for swipe events
        if (e.target.getAttribute('data-swipe-ignore') === 'true') return;

        startEl = e.target;

        timeDown = Date.now();
        xDown = e.touches[0].clientX;
        yDown = e.touches[0].clientY;
        xDiff = 0;
        yDiff = 0;
    }

    /**
     * Records location diff in px on touchmove event
     * @param {object} e - browser event object
     * @returns {void}
     */
    function handleTouchMove(e) {
        
        if (!xDown || !yDown) return;

        var xUp = e.touches[0].clientX;
        var yUp = e.touches[0].clientY;

        xDiff = xDown - xUp;
        yDiff = yDown - yUp;
    }

    /**
     * Gets attribute off HTML element or nearest parent
     * @param {object} el - HTML element to retrieve attribute from
     * @param {string} attributeName - name of the attribute
     * @param {any} defaultValue - default value to return if no match found
     * @returns {any} attribute value or defaultValue
     */
    function getNearestAttribute(el, attributeName, defaultValue) {

        // walk up the dom tree looking for data-action and data-trigger
        while (el && el !== document.documentElement) {

            var attributeValue = el.getAttribute(attributeName);

            if (attributeValue) {
                return attributeValue;
            }

            el = el.parentNode;
        }

        return defaultValue;
    }

}
document.getElementById('deck').addEventListener('swiped-left', function () { deck.appendChild(deck.firstElementChild); });
  document.getElementById('deck').addEventListener('swiped-right', function () { deck.prepend(deck.lastElementChild); });
  init();
  body { overflow: hidden; padding: 100px; }
    #deck {
                position: relative;
    }
    #deck > div {
                position: absolute;
                top: 0; 
                left: 0;
    }
<div id="deck" data-swipe-threshold="100">
  <div><img src="http://dummyimage.com/250x250/000/fff&text=F" /></div>
  <div><img src="http://dummyimage.com/250x250/000/fff&text=E" /></div>
  <div><img src="http://dummyimage.com/250x250/000/fff&text=D" /></div>
  <div><img src="http://dummyimage.com/250x250/000/fff&text=C" /></div>
  <div><img src="http://dummyimage.com/250x250/000/fff&text=B" /></div>
  <div><img src="http://dummyimage.com/250x250/000/fff&text=A" /></div>
</div>