HTML5 DnD - 为什么重影仅在拖放元素后才按预期工作?
HTML5 DnD - Why is ghost image only working as expected AFTER dragging & dropping an element?
我正在尝试获取带有嵌套 img
的 p
元素,以便在拖动时显示为重影图像。
我不确定如何调试它,但我 注意到一旦图像被缓存或被拖放到页面上的某处,它就会按预期工作。我在这里做了一个 MWE:
首次加载页面时拖动笑脸并显示错误行为 - 表情符号 不会 在拖动过程中显示。悲伤的脸被拖拽、释放,然后重新拖拽,这导致了预期的行为——表情符号 确实 显示为幽灵图像的一部分。所有图像都是如此。
我试过的:
我认为这可能是页面元素加载方式的问题,所以我将 javascript 移到了正文的底部(试图确保在脚本运行之前加载所有元素)。这不能解决问题。
MWE代码:
我得到了表情符号 from here,但我猜你机器上的任何 png 都可以重现这个。
index.php:
<html>
<head>
<link rel="stylesheet" type="text/css" href="stylesheet.css">
</head>
<body>
<h2>Order these items</h2>
<div id="main_wrapper">
<?php
$json = json_decode(file_get_contents("image_set.json"), true);
echo '<div id="home_container" ondrop="drop(event, this)" ondragover="allowDrop(event)">';
$i = 0;
foreach($json as $k => $v) {
echo '<p class="drag_item" draggable="true" ondragstart="drag(event)" id="drag'.$i.'"><img draggable="false" src="/images/'.$v['fn'].'" width=200 height=200>'.$v['text'].'</p>';
$i++;
}
echo '</div>';
?>
<div id="buffer" style="min-height:100px; width:100%;"></div>
<div id="dropzone_wrapper">
<?php
for($i = 0; $i < count($json); $i++) {
echo '<div class="dropzone" id="dropzone'.$i.'" ondrop="drop(event, this)" ondragover="allowDrop(event)"></div>';
if($i < count($json)-1){echo '<';}
}
?>
</div>
<div id="msg"></div>
</div>
<script>
function allowDrop(ev) {
ev.preventDefault();
}
function drag(ev) {
var dataList = ev.dataTransfer.items;
dataList.add(ev.target.id, "text/plain");
}
function drop(ev, el) {
ev.preventDefault();
var data = ev.dataTransfer.getData("text");
var element_to_drop = document.getElementById(data);
let droppable = true;
// If the dropzone already contains something (not text due to
// spaces in markup being counted as text), don't allow
// another drop to occur.
if (el.childNodes.length > 0) {
el.childNodes.forEach(function(obj) {
if(obj.nodeName != '#text') {
droppable = false;
}
});
}
if(droppable)
el.appendChild(document.getElementById(data));
}
function reset() {
// Put all drag items back into the home container
let home = document.getElementById('home_container');
let cards = document.querySelectorAll('.drag_item');
for(var i = 0; i < cards.length; i++) {
home.appendChild(cards[i]);
}
}
</script>
</body>
</html>
image_set.json:
{
"happy": {
"fn":"happy.png",
"text":"A happy face"
},
"sad": {
"fn":"sad.png",
"text":"A sad face"
},
"angry": {
"fn":"angry.png",
"text":"An angry face"
},
"confused": {
"fn":"confused.png",
"text":"A confused face"
},
"sleepy": {
"fn":"sleepy.png",
"text":"A sleepy face"
}
}
stylesheet.css:
* {
box-sizing:border-box;
padding:0px;
margin:0px;
font-family:sans-serif;
font-weight:100;
}
body {
padding:20px;
}
h2 {
padding:20px 0;
font-size:4em;
}
p.drag_item {
text-align:center;
transition:0.5s;
width:200px;
height:200px;
}
.drag_item:hover {
cursor:move;
}
#home_container, #dropzone_wrapper {
min-height:200px;
width:100%;
display:flex;
flex-direction:row;
justify-content:space-around;
margin:20px 0;
align-items:center;
}
#dropzone_wrapper {
font-size:3em;
}
#dropzone_wrapper p {
font-size:initial;
}
#home_container {
border:1px solid black;
border-radius:8px;
background-color:#e5e5e5;
}
#home_container p {
width:200px;
font-size:16px;
}
#msg {
display:block;
font-size:2.5em;
}
.dropzone {
min-height:200px;
width:200px;
border:1px dashed black;
background-color:#00a8bd;
}
我做了一些研究来找出问题所在。这对我来说有点困难,因为 Firefox 是唯一一个在第一次加载页面和第一次拖动时不显示重影图像的浏览器。我打开 Network
选项卡,发现只有在第一次拖动时才请求图像(我不太明白,因为图像已完全加载)。
无论如何,我终于成功了,方法是将可拖动元素更改为图像而不是段落。
index.php:
<div id="main_wrapper">
<?php
$json = json_decode(file_get_contents("image_set.json"), true);
echo '<div id="home_container" ondrop="drop(event, this)" ondragover="allowDrop(event)">';
$i = 0;
foreach($json as $k => $v) {
echo '<p class="drag_item"><img ondragstart="drag(event)" id="drag'.$i.'" draggable="true" src="images/'.$v['fn'].'" width=200 height=200>'.$v['text'].'</p>';
$i++;
}
echo '</div>';
?>
<div id="buffer" style="min-height:100px; width:100%;"></div>
<div id="dropzone_wrapper">
<?php
for($i = 0; $i < count($json); $i++) {
echo '<div class="dropzone" id="dropzone'.$i.'" ondrop="drop(event, this)" ondragover="allowDrop(event)"></div>';
if($i < count($json)-1){echo '<';}
}
?>
</div>
<div id="msg"></div>
</div>
JS:
function allowDrop(ev) {
ev.preventDefault();
}
function drag(ev) {
// get the cursor position relative to the element
var x = (ev.pageX - ev.target.offsetLeft) + document.body.scrollLeft;
var y = (ev.pageY - ev.target.offsetTop) + document.body.scrollTop;
ev.dataTransfer.setData("text", ev.target.id);
// set the parent element (the paragraph) as the custom ghost image and set the position of the ghost image (x, y)
ev.dataTransfer.setDragImage(ev.target.parentElement, x, y);
}
function drop(ev, el) {
ev.preventDefault();
var data = ev.dataTransfer.getData("text");
var element_to_drop = document.getElementById(data);
let droppable = true;
if (el.childNodes.length > 0) {
el.childNodes.forEach(function(obj) {
if(obj.nodeName != '#text') {
droppable = false;
}
});
}
if(droppable)
el.appendChild(document.getElementById(data).parentElement);
}
function reset() {
let home = document.getElementById('home_container');
let cards = document.querySelectorAll('.drag_item');
for(var i = 0; i < cards.length; i++) {
home.appendChild(cards[i]);
}
}
This works pretty well in: Chrome, Edge, IE 11
NOTE: This only works perfectly in Firefox (the paragraph text only appears in this browser)
我正在尝试获取带有嵌套 img
的 p
元素,以便在拖动时显示为重影图像。
我不确定如何调试它,但我 注意到一旦图像被缓存或被拖放到页面上的某处,它就会按预期工作。我在这里做了一个 MWE:
首次加载页面时拖动笑脸并显示错误行为 - 表情符号 不会 在拖动过程中显示。悲伤的脸被拖拽、释放,然后重新拖拽,这导致了预期的行为——表情符号 确实 显示为幽灵图像的一部分。所有图像都是如此。
我试过的: 我认为这可能是页面元素加载方式的问题,所以我将 javascript 移到了正文的底部(试图确保在脚本运行之前加载所有元素)。这不能解决问题。
MWE代码: 我得到了表情符号 from here,但我猜你机器上的任何 png 都可以重现这个。
index.php:
<html>
<head>
<link rel="stylesheet" type="text/css" href="stylesheet.css">
</head>
<body>
<h2>Order these items</h2>
<div id="main_wrapper">
<?php
$json = json_decode(file_get_contents("image_set.json"), true);
echo '<div id="home_container" ondrop="drop(event, this)" ondragover="allowDrop(event)">';
$i = 0;
foreach($json as $k => $v) {
echo '<p class="drag_item" draggable="true" ondragstart="drag(event)" id="drag'.$i.'"><img draggable="false" src="/images/'.$v['fn'].'" width=200 height=200>'.$v['text'].'</p>';
$i++;
}
echo '</div>';
?>
<div id="buffer" style="min-height:100px; width:100%;"></div>
<div id="dropzone_wrapper">
<?php
for($i = 0; $i < count($json); $i++) {
echo '<div class="dropzone" id="dropzone'.$i.'" ondrop="drop(event, this)" ondragover="allowDrop(event)"></div>';
if($i < count($json)-1){echo '<';}
}
?>
</div>
<div id="msg"></div>
</div>
<script>
function allowDrop(ev) {
ev.preventDefault();
}
function drag(ev) {
var dataList = ev.dataTransfer.items;
dataList.add(ev.target.id, "text/plain");
}
function drop(ev, el) {
ev.preventDefault();
var data = ev.dataTransfer.getData("text");
var element_to_drop = document.getElementById(data);
let droppable = true;
// If the dropzone already contains something (not text due to
// spaces in markup being counted as text), don't allow
// another drop to occur.
if (el.childNodes.length > 0) {
el.childNodes.forEach(function(obj) {
if(obj.nodeName != '#text') {
droppable = false;
}
});
}
if(droppable)
el.appendChild(document.getElementById(data));
}
function reset() {
// Put all drag items back into the home container
let home = document.getElementById('home_container');
let cards = document.querySelectorAll('.drag_item');
for(var i = 0; i < cards.length; i++) {
home.appendChild(cards[i]);
}
}
</script>
</body>
</html>
image_set.json:
{
"happy": {
"fn":"happy.png",
"text":"A happy face"
},
"sad": {
"fn":"sad.png",
"text":"A sad face"
},
"angry": {
"fn":"angry.png",
"text":"An angry face"
},
"confused": {
"fn":"confused.png",
"text":"A confused face"
},
"sleepy": {
"fn":"sleepy.png",
"text":"A sleepy face"
}
}
stylesheet.css:
* {
box-sizing:border-box;
padding:0px;
margin:0px;
font-family:sans-serif;
font-weight:100;
}
body {
padding:20px;
}
h2 {
padding:20px 0;
font-size:4em;
}
p.drag_item {
text-align:center;
transition:0.5s;
width:200px;
height:200px;
}
.drag_item:hover {
cursor:move;
}
#home_container, #dropzone_wrapper {
min-height:200px;
width:100%;
display:flex;
flex-direction:row;
justify-content:space-around;
margin:20px 0;
align-items:center;
}
#dropzone_wrapper {
font-size:3em;
}
#dropzone_wrapper p {
font-size:initial;
}
#home_container {
border:1px solid black;
border-radius:8px;
background-color:#e5e5e5;
}
#home_container p {
width:200px;
font-size:16px;
}
#msg {
display:block;
font-size:2.5em;
}
.dropzone {
min-height:200px;
width:200px;
border:1px dashed black;
background-color:#00a8bd;
}
我做了一些研究来找出问题所在。这对我来说有点困难,因为 Firefox 是唯一一个在第一次加载页面和第一次拖动时不显示重影图像的浏览器。我打开 Network
选项卡,发现只有在第一次拖动时才请求图像(我不太明白,因为图像已完全加载)。
无论如何,我终于成功了,方法是将可拖动元素更改为图像而不是段落。
index.php:
<div id="main_wrapper">
<?php
$json = json_decode(file_get_contents("image_set.json"), true);
echo '<div id="home_container" ondrop="drop(event, this)" ondragover="allowDrop(event)">';
$i = 0;
foreach($json as $k => $v) {
echo '<p class="drag_item"><img ondragstart="drag(event)" id="drag'.$i.'" draggable="true" src="images/'.$v['fn'].'" width=200 height=200>'.$v['text'].'</p>';
$i++;
}
echo '</div>';
?>
<div id="buffer" style="min-height:100px; width:100%;"></div>
<div id="dropzone_wrapper">
<?php
for($i = 0; $i < count($json); $i++) {
echo '<div class="dropzone" id="dropzone'.$i.'" ondrop="drop(event, this)" ondragover="allowDrop(event)"></div>';
if($i < count($json)-1){echo '<';}
}
?>
</div>
<div id="msg"></div>
</div>
JS:
function allowDrop(ev) {
ev.preventDefault();
}
function drag(ev) {
// get the cursor position relative to the element
var x = (ev.pageX - ev.target.offsetLeft) + document.body.scrollLeft;
var y = (ev.pageY - ev.target.offsetTop) + document.body.scrollTop;
ev.dataTransfer.setData("text", ev.target.id);
// set the parent element (the paragraph) as the custom ghost image and set the position of the ghost image (x, y)
ev.dataTransfer.setDragImage(ev.target.parentElement, x, y);
}
function drop(ev, el) {
ev.preventDefault();
var data = ev.dataTransfer.getData("text");
var element_to_drop = document.getElementById(data);
let droppable = true;
if (el.childNodes.length > 0) {
el.childNodes.forEach(function(obj) {
if(obj.nodeName != '#text') {
droppable = false;
}
});
}
if(droppable)
el.appendChild(document.getElementById(data).parentElement);
}
function reset() {
let home = document.getElementById('home_container');
let cards = document.querySelectorAll('.drag_item');
for(var i = 0; i < cards.length; i++) {
home.appendChild(cards[i]);
}
}
This works pretty well in: Chrome, Edge, IE 11
NOTE: This only works perfectly in Firefox (the paragraph text only appears in this browser)