当用户滑动 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>
假设 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>