将指针事件重新触发到下层(用于 interact.js 的不规则形状拖动)
Re-firing pointer events onto lower layer (for irregular-shaped dragging with interact.js)
我需要手动 construct/fire mousedown 事件以任何相关事件处理程序可以处理的方式(在直接 JS、jQuery 或 interact.js 中),只是作为一个自然的 mousedown 事件。然而,该事件似乎并没有像我期望的那样触发任何事情。
我正在尝试使用 interact.js 库使一些形状不规则的图像可拖动。图像只是带有透明部分的矩形 img 元素。在每个元素上,我定义了 2 个 interact.js 事件侦听器:
- 检查点击是否在图像区域内,如果不在则禁用拖动(触发 "down" 事件)
- 处理拖动(触发 "drag" 事件)
但是,如果img元素重叠,用户点击了顶部元素的透明区域,但点击了下方元素的填充区域,则下方元素应该是拖动的目标。在尝试了几件事之后(见下文),我确定了以下解决方案:在我禁用第 1 步中的拖动的同时重新排序元素的 z 索引,然后在所有较低元素上重新触发 mousedown 事件.我正在使用 "native" 事件类型(不是 jQuery 或 interact.js),希望它能真正复制原始的 mousedown 事件。
// code to re-assign "zIndex"s
function demote(element, interactible, event){
// block dragging on element
interactible.draggable(false);
// get all images lower than the target
var z = element.css("zIndex");
var images = $("img").filter(function() {
return Number($(this).css("zIndex")) < z;
});
// push the target to the back
element.css("zIndex",1);
// re-process all lower events
$(images).each( function () {
// move element up
$(this).css("zIndex",Number($(this).css("zIndex"))+1);
// re-fire event if element began below current target
elem = document.getElementById($(this).attr('id'));
// Create the event.
e = new MouseEvent("mousedown", {clientX: event.pageX, clientY: event.pageY});
var cancelled = !elem.dispatchEvent(e);
});
}
不幸的是,这不起作用,因为 mousedown 事件没有向任何处理程序注册。为什么?
我在这个 JSFiddle 中拥有所有(相关)代码:https://jsfiddle.net/tfollo/xr27938a/10/
请注意,此 JSFiddle 似乎不像在普通浏览器中那样流畅地运行 window,但我认为它展示了预期的功能。
我尝试过的其他事情:
很多人提出了不同的方案来处理类似的问题(例如,将事件转发到较低层,使用指针事件:none,等等),但是 none 似乎可以触发interact.js 处理程序并在右侧元素上开始拖动交互。我也尝试使用 interaction.start(由 interact.js 提供),但它似乎有问题——该主题至少有一个未解决的问题,当我尝试在我选择的目标上开始新的拖动交互时我在库的代码中发现了很多错误。
我不反对重新审视这些解决方案本身,但我也很想知道为什么手动触发 mousedown 事件不起作用。
您是否尝试过在更高级别上设置 css 属性 pointer-events: none
(也可以通过 javascript 设置)?
想法是监听父元素上的 down
事件并手动选择拖动目标。此外,我没有使用 z-index 来选择要拖动的图像,因为 z-index 不适用于 position:static
。而不是我只是优先考虑图像,这完全取决于你。 https://jsfiddle.net/yevt/6wb5oxx3/3/
var dragTarget;
function dragMoveListener (event) {
var target = event.target,
// keep the dragged position in the data-x/data-y attributes
x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx,
y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;
// translate the element
target.style.webkitTransform =
target.style.transform =
'translate(' + x + 'px, ' + y + 'px)';
// update the posiion attributes
target.setAttribute('data-x', x);
target.setAttribute('data-y', y);
}
function setDragTarget(event) {
//choose element to drag
dragTarget = $('#parent').find('img')
.filter(function(i, el) {
var clickCandicateRect = el.getBoundingClientRect();
return insideBoundingRect(event.x, event.y, clickCandicateRect);
}).sort(byPriority).get(0);
}
function insideBoundingRect(x, y, rect) {
return (rect.left <= x) && (rect.top <= y) && (rect.right >= x) && (rect.bottom >= y);
}
function byPriority (a, b) {
return $(b).data('priority') - $(a).data('priority');
}
function startDrag(event) {
var interaction = event.interaction;
var target = dragTarget;
if (!interaction.interacting()) {
interaction.start({ name: 'drag' }, event.interactable, target);
}
}
//Give priority as you wish
$('#face1').data('priority', 2);
$('#face2').data('priority', 1);
interact('#parent').draggable({
//use manualStart to determine which element to drag
manualStart: true,
onmove: dragMoveListener,
restrict: {
restriction: "parent",
endOnly: true,
elementRect: { top: 0, left: 0, bottom: 1, right: 1 }
},
})
.on('down', setDragTarget)
.on('move', startDrag);
我需要手动 construct/fire mousedown 事件以任何相关事件处理程序可以处理的方式(在直接 JS、jQuery 或 interact.js 中),只是作为一个自然的 mousedown 事件。然而,该事件似乎并没有像我期望的那样触发任何事情。
我正在尝试使用 interact.js 库使一些形状不规则的图像可拖动。图像只是带有透明部分的矩形 img 元素。在每个元素上,我定义了 2 个 interact.js 事件侦听器:
- 检查点击是否在图像区域内,如果不在则禁用拖动(触发 "down" 事件)
- 处理拖动(触发 "drag" 事件)
但是,如果img元素重叠,用户点击了顶部元素的透明区域,但点击了下方元素的填充区域,则下方元素应该是拖动的目标。在尝试了几件事之后(见下文),我确定了以下解决方案:在我禁用第 1 步中的拖动的同时重新排序元素的 z 索引,然后在所有较低元素上重新触发 mousedown 事件.我正在使用 "native" 事件类型(不是 jQuery 或 interact.js),希望它能真正复制原始的 mousedown 事件。
// code to re-assign "zIndex"s
function demote(element, interactible, event){
// block dragging on element
interactible.draggable(false);
// get all images lower than the target
var z = element.css("zIndex");
var images = $("img").filter(function() {
return Number($(this).css("zIndex")) < z;
});
// push the target to the back
element.css("zIndex",1);
// re-process all lower events
$(images).each( function () {
// move element up
$(this).css("zIndex",Number($(this).css("zIndex"))+1);
// re-fire event if element began below current target
elem = document.getElementById($(this).attr('id'));
// Create the event.
e = new MouseEvent("mousedown", {clientX: event.pageX, clientY: event.pageY});
var cancelled = !elem.dispatchEvent(e);
});
}
不幸的是,这不起作用,因为 mousedown 事件没有向任何处理程序注册。为什么?
我在这个 JSFiddle 中拥有所有(相关)代码:https://jsfiddle.net/tfollo/xr27938a/10/ 请注意,此 JSFiddle 似乎不像在普通浏览器中那样流畅地运行 window,但我认为它展示了预期的功能。
我尝试过的其他事情:
很多人提出了不同的方案来处理类似的问题(例如,将事件转发到较低层,使用指针事件:none,等等),但是 none 似乎可以触发interact.js 处理程序并在右侧元素上开始拖动交互。我也尝试使用 interaction.start(由 interact.js 提供),但它似乎有问题——该主题至少有一个未解决的问题,当我尝试在我选择的目标上开始新的拖动交互时我在库的代码中发现了很多错误。
我不反对重新审视这些解决方案本身,但我也很想知道为什么手动触发 mousedown 事件不起作用。
您是否尝试过在更高级别上设置 css 属性 pointer-events: none
(也可以通过 javascript 设置)?
想法是监听父元素上的 down
事件并手动选择拖动目标。此外,我没有使用 z-index 来选择要拖动的图像,因为 z-index 不适用于 position:static
。而不是我只是优先考虑图像,这完全取决于你。 https://jsfiddle.net/yevt/6wb5oxx3/3/
var dragTarget;
function dragMoveListener (event) {
var target = event.target,
// keep the dragged position in the data-x/data-y attributes
x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx,
y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;
// translate the element
target.style.webkitTransform =
target.style.transform =
'translate(' + x + 'px, ' + y + 'px)';
// update the posiion attributes
target.setAttribute('data-x', x);
target.setAttribute('data-y', y);
}
function setDragTarget(event) {
//choose element to drag
dragTarget = $('#parent').find('img')
.filter(function(i, el) {
var clickCandicateRect = el.getBoundingClientRect();
return insideBoundingRect(event.x, event.y, clickCandicateRect);
}).sort(byPriority).get(0);
}
function insideBoundingRect(x, y, rect) {
return (rect.left <= x) && (rect.top <= y) && (rect.right >= x) && (rect.bottom >= y);
}
function byPriority (a, b) {
return $(b).data('priority') - $(a).data('priority');
}
function startDrag(event) {
var interaction = event.interaction;
var target = dragTarget;
if (!interaction.interacting()) {
interaction.start({ name: 'drag' }, event.interactable, target);
}
}
//Give priority as you wish
$('#face1').data('priority', 2);
$('#face2').data('priority', 1);
interact('#parent').draggable({
//use manualStart to determine which element to drag
manualStart: true,
onmove: dragMoveListener,
restrict: {
restriction: "parent",
endOnly: true,
elementRect: { top: 0, left: 0, bottom: 1, right: 1 }
},
})
.on('down', setDragTarget)
.on('move', startDrag);