网站建设方案案例,建设银行黑龙江省分行官方网站,视频制作平台,注册域名的官方网站#x1f4c5; 我们继续 50 个小项目挑战#xff01;—— DragNDrop 组件 仓库地址#xff1a;https://gitee.com/hhm-hhm/50days50projects.git 构建一个支持拖拽交互的图片拖放组件。该组件允许用户将一张图片从一个容器拖动并释放到另一个“空位”中#xff0c;并带有视… 我们继续 50 个小项目挑战—— DragNDrop 组件仓库地址https://gitee.com/hhm-hhm/50days50projects.git构建一个支持拖拽交互的图片拖放组件。该组件允许用户将一张图片从一个容器拖动并释放到另一个“空位”中并带有视觉反馈如悬停高亮、背景变化等。 组件目标创建多个“空位”容器默认展示一张可拖动的图片支持拖拽交互并投放到任意空位投放后更新对应位置的图片状态拖拽过程中提供视觉反馈如悬停样式使用 TailwindCSS快速构建现代 UI 界面 DragNDrop.tsx组件实现import React, { useState } from react const DragNDrop: React.FC () { const [filledIndex, setFilledIndex] useStatenumber(0) const [isHovered, setIsHovered] useStateboolean[](Array(5).fill(false)) const imageUrls [ https://picsum.photos/id/10/150/150, https://picsum.photos/id/11/150/150, https://picsum.photos/id/12/150/150, https://picsum.photos/id/13/150/150, https://picsum.photos/id/14/150/150, ] const empties Array.from({ length: 5 }, (_, i) i) // 拖拽开始设置被拖拽的元素标识这里用 filledIndex const dragStart (e: React.DragEventHTMLDivElement) { e.dataTransfer.setData(text/plain, filledIndex.toString()) e.dataTransfer.effectAllowed move } const dragEnd () { // 可选添加拖拽结束效果如重置样式 } const dragOver (e: React.DragEventHTMLDivElement) { e.preventDefault() // 必须阻止默认行为才能触发 drop } const dragEnter (index: number) { setIsHovered((prev) { const newState [...prev] newState[index] true return newState }) } const dragLeave (index: number) { setIsHovered((prev) { const newState [...prev] newState[index] false return newState }) } const dragDrop (index: number, e: React.DragEventHTMLDivElement) { e.preventDefault() const draggedIndexStr e.dataTransfer.getData(text/plain) const draggedIndex parseInt(draggedIndexStr, 10) if (!isNaN(draggedIndex) draggedIndex ! index) { setFilledIndex(index) } // 清除所有 hover 状态 setIsHovered(Array(5).fill(false)) } return ( div classNameflex h-screen items-center justify-center overflow-hidden bg-gray-900 {empties.map((_, index) ( div key{index} className{m-2 h-36 w-36 border-4 border-black bg-white ${ isHovered[index] ? border-dashed border-black bg-gray-800 : }} onDragOver{dragOver} onDragEnter{() dragEnter(index)} onDragLeave{() dragLeave(index)} onDrop{(e) dragDrop(index, e)} {index filledIndex ( div classNameh-full w-full cursor-move bg-cover transition-all duration-200 ease-in-out style{{ backgroundImage: url(${imageUrls[index]}) }} draggable onDragStart{dragStart} onDragEnd{dragEnd} / )} /div ))} div classNamefixed right-20 bottom-5 text-2xl text-red-500CSDNHao_Harrision/div /div ) } export default DragNDrop 转换说明功能Vue 3 (Composition API)React TS响应式状态const filledIndex ref(0)const isHovered ref([...])const [filledIndex, setFilledIndex] useState(0)const [isHovered, setIsHovered] useState([...])列表渲染v-for(empty, index) in empties{empties.map((_, index) div key{index}...)}动态 class:class[isHovered[index] border-dashed ...]使用模板字符串或条件表达式className{... ${isHovered[index] ? border-dashed bg-gray-800 : }}事件绑定dragenterdragEnter(index)onDragEnter{() dragEnter(index)}事件对象类型自动推导显式标注e: React.DragEventHTMLDivElement阻止默认行为dragover.prevente.preventDefault()必须在onDragOver中手动调用拖拽数据传递无显式设置逻辑隐含必须通过e.dataTransfer.setData(text/plain, value)传递获取拖拽数据无e.dataTransfer.getData(text/plain)内联样式:style{ backgroundImage: url(...) }style{{ backgroundImage: \url(${url}) }}⚠️ 常见差异与注意事项1.HTML5 拖拽在 React 中必须显式处理Vue 的.prevent修饰符自动阻止默认行为React 中必须手动调用e.preventDefault()在onDragOver和onDrop中否则drop事件不会触发。2.状态更新不可变性Vue 可直接修改isHovered.value[index] trueReact 必须使用不可变更新setIsHovered(prev { const newState [...prev]; newState[index] true; return newState; });3.事件处理器传参方式不同VuedragenterdragEnter(index)直接传参React需用箭头函数包裹onDragEnter{() dragEnter(index)}。4.draggable 属性Vuedraggabletrue字符串Reactdraggable布尔属性写成div draggable /即可但写draggable{true}也合法5.CSS 过渡效果Vue 使用style scoped定义[draggabletrue]React 中建议方式一在全局 CSS 中定义如index.css方式二推荐直接用 Tailwind 类实现过渡classNametransition-all duration-200 ease-in-out✅ 最佳实践建议场景推荐做法拖拽标识传递使用dataTransfer.setData(text/plain, id)传递唯一标识防止无效移动在drop中判断if (from ! to)再更新状态hover 状态管理使用数组记录每个格子的悬停状态确保精准控制图片 URL 管理将imageUrls作为常量或 props避免硬编码无障碍与 UX添加cursor-move、user-select: none提升体验 TailwindCSS 样式重点讲解 TailwindCSS 样式说明类名作用h-screen,items-center,justify-center全屏高度 内容居中布局overflow-hidden防止内容溢出bg-sky-500设置背景颜色为浅蓝色h-36,w-36设置每个容器的宽高为 369remm-2设置外边距为 20.5remborder-4,border-black黑色边框bg-white/bg-gray-800默认和悬停状态下的背景颜色border-dashed悬停时边框变为虚线cursor-pointer设置图片区域为可点击bg-cover图片背景自适应填充transition添加拖拽过程中的平滑过渡动画 路由组件 常量定义router/index.tsx中children数组中添加子路由{ path: /, element: App /, children: [ ... { path: /DragNDrop, lazy: () import(/projects/DragNDrop.tsx).then((mod) ({ Component: mod.default, })), }, ], },constants/index.tsx 添加组件预览常量import demo21Img from /assets/pic-demo/demo-21.png 省略部分.... export const projectList: ProjectItem[] [ 省略部分.... { id: 21, title: Drag-and-drop Occupation, image: demo21Img, link: DragNDrop, }, 小结进一步扩展的功能推荐✅ 支持多张图片同时拖动✅ 支持图片预览拖拽不立即改变原图位置✅ 拖拽时高亮目标容器边界✅ 支持触摸设备拖拽交互移动端适配✅ 封装为可复用组件支持 props 传入图片列表 明日预告 我们将完成DrawApp组件创建一个画板具有调节画笔粗细的功能并且能够一键清除画板上的内容。原文链接https://blog.csdn.net/qq_44808710/article/details/149103822每天造一个轮子码力暴涨不是梦