如何架设内部网站,查排名网站,仿站怎么做,企业咨询管理有限公司最近碰到了个需求#xff0c;大概就是要通过可视化拖拽的方式配置一个冰柜#xff0c;需要把预设好的冰柜内部架子模板一个个拖到冰箱内。一开始的想法是用鼠标事件#xff08;mousedown、mouseup等#xff09;那一套去实现#xff0c;能实现但是过程过于复杂#xff0c;… 最近碰到了个需求大概就是要通过可视化拖拽的方式配置一个冰柜需要把预设好的冰柜内部架子模板一个个拖到冰箱内。一开始的想法是用鼠标事件mousedown、mouseup等那一套去实现能实现但是过程过于复杂需要控制的状态太多了。其实 Web Api 为 html 元素拖拽量身定制了一套 HTML 拖放API用这个方法实现一些简单的拖拽功能简直不要太简单。为此写了这篇文章下面将详细介绍 HTML 拖放 API 的核心知识点 文档
一、被拖拽元素和放置被拖拽元素的元素 通常我们所了解的拖放是按住鼠标左键不放然后移动鼠标把一个页面元素从某个位置移动到另一个位置然后松开鼠标左键至此完成了整个拖放过程。在这个过程中我们需要先重点关注两个东西一个是被拖拽元素另一个是 放置被拖拽元素的元素。 1.1 被拖拽元素 我们得先有个概念页面上显示的元素默认并不都是可以被拖拽的除了图片、被选中的文字、链接所以如果当前元素默认不可被拖拽那么就得先把它设置为可拖拽的。ps可拖拽元素被拖拽时会有一个半透明的快照跟着鼠标移动。 将 HTML 元素的 draggable 属性设置为 true, 元素就可以变为可拖拽元素。效果如下图。
div idbox draggabletruedraggable box/div1.2 可放置被拖拽元素的元素 所有的元素区域默认是不支持放置被拖拽元素的直观的表现是当被拖拽元素经过不可放置区域时鼠标的样式是一个禁止放置的一个图标圆圈带一个斜杠所以需要将目标元素设置为一个可放置区域 默认情况下是这样
div idbox draggabletruedraggable box/div
div iddroppable放置区域/div设置为放置区域需要给元素绑定一个事件 dragover 且要 阻止默认行为
div idbox draggabletruedraggable box/div
div iddroppable放置区域/div
scriptlet dropDom document.getElementById(droppable)dropDom.addEventListener(dragover, (e) {e.preventDefault()})
/scriptReact: 放置区域需要绑定onDragOver事件且要 阻止默认行为 – 其他事件一样加on
div draggabletruedraggable box/div
div onDragOver{(e) {e.preventDefault()}}放置区域/div设置为可放置区域后鼠标样式也变了不再是禁止图标,而是一个加号图标图标可以设置下面会讲解 然而你会发现被拖放元素并没有真正的被放置到放置区域这是必然的放置操作需要开发者自行定义以上的设置只是是为了向用户表明这个区域是允许放东西的那么至于怎么放需要开发者自行决定。
二、拖拽过程触发的一些事件 这一小节将带你了解整个拖放过程的其他细节比如拖拽过程中会触发哪些事件 2.1 被拖放目标触发的事件
给被拖放目标元素绑定三个事件 dragstart、drag、dragend。
div idbox draggabletruedraggable box/div
div iddroppable放置区域/div
scriptlet dragDom document.getElementById(box)dragDom.addEventListener(dragstart, (e) {console.log(开始拖动);})dragDom.addEventListener(drag, (e) {console.log(拖动中);})dragDom.addEventListener(dragend, (e) {console.log(结束拖动);})
/script
React
div draggabletrueonDragStart{(e) {console.log(开始拖动, e);}}onDrag{(e) {console.log(拖动中, e);}}onDragEnd{(e) {console.log(结束拖动, e);}}draggable box/div
div onDragOver{(e) {e.preventDefault()}}放置区域/div
开始拖动触发 dragstart 拖动过程中鼠标不松开触发drag松开鼠标或者按下 Esc 键触发 dragend。
2.2 被拖拽元素在放置区域内会触发的事件
先给放置目标元素绑定四个事件
div idbox draggabletruedraggable box/div
div iddroppable放置区域/div
scriptlet dropDom document.getElementById(droppable)dropDom.addEventListener(dragenter, (e) {console.log(进入到了放置区域~);})dropDom.addEventListener(dragover, (e) {e.preventDefault()console.log(在放置区域内拖拽中~);})dropDom.addEventListener(dragleave, (e) {console.log(离开了放置区域~);})dropDom.addEventListener(drop, (e) {console.log(在放置区域内放下了被拖拽元素~)})
/script拖拽元素进入放置区域内时触发 dragenter 事件在放置区域内移动被拖放鼠标不松开元素触发 dragover 事件被拖放元素离开放置区域触发 dragleave 事件在放置区域内松开鼠标触发 drop 事件。
三、实现真正意义上的元素拖放 通过上面触发的事件我们可以知道用户真正在放置区域释放鼠标的时候只有 drop 事件能够监听到。所以开发者需要在这个事件里做真正的放置操作放置什么由开发者决定可以是被拖拽元素也可以是自定义的一些内容。 放置被拖拽元素
div idbox draggabletruedraggable box/div
div iddroppable放置区域/div
scriptlet dropDom document.getElementById(droppable)dropDom.addEventListener(dragover, (e) {e.preventDefault()console.log(在放置区域内拖拽中~);})dropDom.addEventListener(drop, (e) {console.log(在放置区域内放下了被拖拽元素~)e.target.appendChild(document.getElementById(box))})
/script放置自定义内容
dropDom.addEventListener(drop, (e) {console.log(在放置区域内放下了被拖拽元素~)let customCOntent p自定义内容/pe.target.innerHTML e.target.innerHTML customCOntent
})四、dataTransfer 对象
4.1 从被拖放元素向可放置元素传递数据 dataTransfer 对象提供了一个setData()方法它接受两个参数第一个参数是传递数据的类型(一般是标准的MIME类型)第二个数据是数据值。dataTransfer 还提供了 getData() 的方法用于获取传递的数据它接受一个参数参数值为 setData 对应的第一个参数。 传递一个简单的字符串数据
div idbox draggabletruedraggable box/div
div iddroppable放置区域/div
scriptlet dropDom document.getElementById(droppable)let dragDom document.getElementById(box)dragDom.addEventListener(dragstart, (e) {e.dataTransfer.setData(text/plain, 自定义数据)})dropDom.addEventListener(dragover, (e) {e.preventDefault()})dropDom.addEventListener(drop, (e) {let data e.dataTransfer.getData(text/plain)console.log(你传递的数据为, data);})
/script⚡注意只能在 dragstart 事件中设置数据在其他地方设置无效。且只能在 drop 事件中获取设置的数据其他事件中获取不到。 案例根据传递的数据放置不同的内容。
div idbox draggabletruedraggable box/div
div iddroppable放置区域/div
scriptlet dropDom document.getElementById(droppable)let dragDom document.getElementById(box)dropDom.addEventListener(dragover, (e) {e.preventDefault()})dropDom.addEventListener(drop, (e) {let num e.dataTransfer.getData(num)console.log(num);if(num 5)e.target.innerHTML e.target.innerHTML p传递的数字大于5/pelse if(num 5) e.target.innerHTML e.target.innerHTML p传递的数字等于5/pelsee.target.innerHTML e.target.innerHTML p传递的数字小于5/p})dragDom.addEventListener(dragstart, (e) {let num Math.floor(Math.random() * 10) 1;e.dataTransfer.setData(num, num)})
/script4.2 自定义拖拽过程中跟随鼠标移动的内容 默认情况下元素被拖拽时会有一个半透明的元素快照跟随着鼠标移动。通过 dataTransfer 提供的 setDragImage(elemnt, xOffset, yOffset) 方法是可以自定义跟随内容。接受三个参数 elemnt 可以是 dom 节点或者一个图片对象xOffset, yOffset 是相对于鼠标的偏移量。 语法 dataTransfer.setDragImage(img, xOffset, yOffset); img | Element 用于拖曳反馈图像的图像 Element 元素。
如果 Element 是一个 img 元素则将拖动位图设置为该元素的图像保持大小否则将拖动数位图设置为从给定元素所生成的图片
xOffset 使用 long 指示相对于图片的横向偏移量
yOffset 使用 long 指示相对于图片的纵向偏移量
解析
发生拖动时从拖动目标 (dragstart事件触发的元素) 生成半透明图像并在拖动过程中跟随鼠标指针。这个图片是自动创建的你不需要自己去创建它。然而如果想要设置为自定义图像那么 DataTransfer.setDragImage() 方法就能派上用场。图像通常是一个 image 元素但也可以是canvas 或任何其他图像元素。该方法的 x 和 y 坐标是图像应该相对于鼠标指针出现的偏移量。 坐标指定鼠标指针相对于图片的偏移量。例如要使图像居中请使用图像宽度和高度的一半。通常在 dragstart 事件处理程序中调用此方法。
实际用例 setDragImage 的第一个参数接受的是一个Element参数这样的话普通的html元素、image元素、canvas都可以传递 1、设置为一个图片
scriptimport Tag from ../../style/imgs/attributeTag/路径.png; //已经存在的图片let dragDom document.getElementById(box)dragDom.addEventListener(dragstart, (e) {let img new Image()// 创建一个图像并且使用它作为拖动图像// 请注意 改变 example.gif 为一个已经存在的图片// 或者一个还没有创建出来的图片那么浏览器将会使用默认的拖动图片// 译者注默认的拖动图片与拖动对象没有联系。一般是一个小型文件图标// 例如// mg.src Tag //或// mg.src data:image/png;base64,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;img.src example.gife.dataTransfer.setDragImage(img, 10, 10)})
/script2、以官网例子为例把canvas作为参数传递我首先尝试的是这种方式发现并不能生效。官方的例子没有运行成功
function dragWithCustomImage(event) {var canvas document.createElementNS(http://www.w3.org/1999/xhtml,canvas);canvas.width canvas.height 50;var ctx canvas.getContext(2d);ctx.lineWidth 4;ctx.moveTo(0, 0);ctx.lineTo(50, 50);ctx.moveTo(0, 50);ctx.lineTo(50, 0);ctx.stroke();var dt event.dataTransfer;dt.setData(text/plain, Data to Drag);dt.setDragImage(canvas, 25, 25);
}3、根据案例我接着使用·HtmlDivElement·作为参数传递,创建了·DIV元素·此时也·没有生效·。 export function drawDragImage(dataTransfer: DataTransfer, context: string) { const drawItem: API.EquipmentInfo JSON.parse(context); const div document.createElement(‘div’); div.style.height itemObj.height ‘px’; div.style.width itemObj.width ‘px’; div.style.border ‘1px solid #000’; const span document.createElement(‘span’); span.innerText ‘2222’; div.appendChild(span); dataTransfer.setDragImage(div, drawItem.width / 2, drawItem.height / 2); } 4、然后我改进了canvas把canvas转化为图片第一次拖拽的时候因为image加载元素是异步导致了没有生效如图1第二以后拖拽的时候可以生效如图二。 const imageContent canvas.toDataURL(image/jpeg, 1);const image new Image();image.src imageContent;image.onload () {console.log(image2 load);};dataTransfer.setDragImage(image, drawItem.width / 2, drawItem.height / 2);5、最后我尝试了使用官网的方法同样因为image加载图片是异步的而拖拽事件是同步发生的导致了第一次执行失败。
function dragstart_handler(ev) {console.log(dragStart);// 设置拖动的格式和数据。使用事件目标的 id 作为数据ev.dataTransfer.setData(text/plain, ev.target.id);// 创建一个图像并且使用它作为拖动图像// 请注意改变 example.gif 为一个已经存在的图片// 或者一个还没有创建出来的图片那么浏览器将会使用默认的拖动图片// 译者注默认的拖动图片与拖动对象没有联系。一般是一个小型文件图标var img new Image();img.src example.gif;ev.dataTransfer.setDragImage(img, 10, 10);
}解决方案
在尝试了不同方式设置拖拽反馈图像总结了一些解决方案
以html页面的元素为模版动态生成内容然后设置Element元素参数可以设置DIV元素的z-index(使用z-index必须使用position:relative | absolute)–(尝试使用过css1、position:absolute 定位出浏览器可视界面 2、 display:none无用)隐藏在实际页面之下这样可以动态生成要拖拽的元素并和生成的fabric的group保持一致。 完美的解决了问题 。 js
export function drawDragImage(dataTransfer: DataTransfer, context: string) {const drawItem: API.EquipmentInfo JSON.parse(context);const dragElement document.getElementById(dragItem);const idElement dragElement?.getElementsByClassName(dragItemId)[0];const nameElement dragElement?.getElementsByClassName(dragItemName)[0];if (idElement) {idElement.innerHTML drawItem.id;}if (nameElement) {nameElement.innerHTML drawItem.typeName || ;}if (dragElement) {dragElement.style.height drawItem.height px;dragElement.style.width drawItem.width px;dragElement.style.border 1px solid #000;dragElement.style.background #fff;}if (dragElement) {dataTransfer.setDragImage(dragElement, drawItem.width / 2, drawItem.height / 2);}}React import {useRef} from react;import { Modal, Space, Input, Tree, Button, Badge } from antd;const mouseStyle useRefany(null);divdraggabletrueonDragStart{(e) {//mouseStyle.currente.dataTransfer.setDragImage(mouseStyle.current, 10, 10);}}移动位置/div//
//absolute top-[10%] z-[1] h-10 css使用了tailwindcss
//npm install -D tailwindcss
//https://tailwindcss.com/docs/installation 文档地址div classNameabsolute top-[10%] z-[1] h-10 ref{mouseStyle}Badge count{5}div className border border-[#444444] leading-6 h-6 w-152023.09.22初级会计资格考试/div/Badge
/div4.3 设置放置前的反馈图标 dataTransfer 提供了一个 dropEffect属性设置放置前的反馈图标它有四种取值 none move copy link 在 dragover 中设置 dropEffect 的值
dropDom.addEventListener(dragover, (e) {e.preventDefault()e.dataTransfer.dropEffect link // none || move || copy || link
})值为 none 或者经过不可放置区域显示禁止放置图标值为 move 时 值为 copy 时 值为 link 时
4.4 拖动文件上传 通过 dataTransfer 的 files 属性可以获取用户拖拽的文件信息 拖拽系统文件到放置区域并打印拖拽的文件信息
dropDom.addEventListener(drop, (e) {e.preventDefault()// 上传的文件列表let fileList e.dataTransfer.filesfor (let i 0; i fileList.length; i) {const file fileList[i];console.log(文件名 file.name);console.log(文件大小 file.size);// 后续操作 比如调接口上传文件}
})4.5 清除 setData() 的值 dataTransfer 提供了 clearData() 清除 setData 设置的值传参数则删除指定类型的值不传则全部清除。 dropDom.addEventListener(drop, (e) {console.log(e.dataTransfer.getData(text/plain));console.log(e.dataTransfer.getData(text/html));
})dragDom.addEventListener(dragstart, (e) {e.dataTransfer.setData(text/plain, 自定义数据)e.dataTransfer.setData(text/html, 自定义数据2)e.dataTransfer.clearData(text/html)
})4.6 查看设置了哪些类型的值 dataTransfer 提供了 types 属性查看 setData 设置了哪些类型的值。 dropDom.addEventListener(drop, (e) {console.log(e.dataTransfer.types);
})
dragDom.addEventListener(dragstart, (e) {e.dataTransfer.setData(text/plain, 自定义数据)e.dataTransfer.setData(text/html, 自定义数据2)
})4.7 effectAllowed 属性取值会影响到 dropEffect 的取值效果。 effectAllowed 用于限制 dropEffect 只能设置哪些值 effectAllowed 的取值有 none - 此项表示 dropEffect 设置任何值都是禁止效果 copy - dropEffect 可以设置为 copy copyLink - dropEffect 可以设置为 copy 和 link copyMove - dropEffect 可以设置为 copy 和 Move link - dropEffect 可以设置为 link linkMove - dropEffect 可以设置为 link 和 Move move - dropEffect 可以设置为 Move all - dropEffect 可以设置为所有合法值 uninitialized - 等同 all 效果
dropDom.addEventListener(dragover, (e) {e.preventDefault()e.dataTransfer.dropEffect move
})
dragDom.addEventListener(dragstart, (e) {e.dataTransfer.effectAllowed none
})上面即使 dropEffect 设置为 move 但是 effectAllowed 的值为 none所有还是禁止放置的反馈图标。
五、总结
实现一个拖拽功能时先定义好被拖拽元素和放置区域元素。所有的放置操作都是在 drop 事件中完成。放置前的反馈效果可以根据你传递的数据来设置 dropEffect 显示不同的效果。被拖拽元素也可以是放置区域放置区域也可以是被拖拽元素两者没有明确的界限。功能自定义按需求开发