前端的快乐,有一半来自写出好玩的交互。另一半,来自抓一只 奶龙 到手上。
没错,今天我们要用 HTML5 Drag & Drop API,做一个能把奶龙从一个盒子拖到另一个盒子的小游戏。
如果你会了这个,不仅能轻松玩转拖拽,还能用在 文件上传、任务排序、小游戏 这些实用场景里。
1. 先上效果,骗你点进来
看吧,奶龙被你抓来抓去的感觉,是不是很爽?
不过放心,它不会告你,它只是个可爱的小图片罢了。
2. 为什么要学拖拽?
-
比点击有趣
拖拽是一种“你看我就懂”的交互,不用解释,用户自然会玩。 -
场景多到飞起
- Google Drive 文件拖拽上传
- Trello、Jira 拖拽任务排序
- 把奶龙从一个窝拖到另一个窝(今日主线)
-
提升用户体验
iPad 当年火得一塌糊涂,不就是因为“戳一戳、拖一拖”傻瓜又好玩嘛。
3. HTML5 拖拽原理
HTML5 自带了拖拽 API,只要在元素上加一句话:
<div draggable="true"></div>
它就像被赋予了“灵魂”一样,能被你拽走了。
默认情况下,图片和选中文本也是可拖拽的。
4. 拖拽的流程像谈恋爱
整个拖拽过程,其实就像一场短暂的恋爱:
dragstart→ 初见倾心:我想抓你drag→ 热恋期:我带你飞dragend→ 分手快乐:拜拜了您嘞dragenter→ 进入新恋情:你来了dragover→ 在对方面前晃悠:想不想在一起?(这里必须e.preventDefault(),否则没戏)dragleave→ 走了:算了算了drop→ 领证:你就是我的了
5. 为什么拖不动奶龙?因为少了 e.preventDefault()
这里是个很多人会踩的坑:
如果你在 dragover 事件里没有调用 e.preventDefault(),奶龙是 放不下去 的,drop 事件根本不会触发。
原因:
- 浏览器默认不让你随便把东西“丢”到别的元素里
- 必须明确告诉它:“我允许”,才会放下去
- 而这个“允许”的暗号,就是
e.preventDefault()
function dragOver(e) {
e.preventDefault(); // 允许奶龙在这里落脚
}
记住:
- 不写 →
drop根本没戏 - 写了 → 奶龙就乖乖躺到新窝里
6. 奶龙抓捕现场
6.1 HTML 结构
<div class="empty">
<div class="fill" draggable="true"></div>
</div>
<div class="empty"></div>
<div class="empty"></div>
<div class="empty"></div>
<div class="empty"></div>
6.2 CSS 打造奶龙的窝
body {
background-color: steelblue;
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
margin: 0;
}
.empty {
height: 150px;
width: 150px;
margin: 10px;
border: 3px solid #000;
background: white;
}
.fill {
background-image: url('你的奶龙图片链接');
background-size: cover;
width: 145px;
height: 145px;
cursor: pointer;
}
.hold {
border: 5px solid #ccc;
}
.hovered {
background-color: #333;
border-color: white;
border-style: dashed;
}
/* 小屏幕适配 */
@media (max-width: 800px) {
body {
flex-direction: column;
}
}
6.3 JavaScript:奶龙的搬家逻辑
const fill = document.querySelector('.fill');
const empties = document.querySelectorAll('.empty');
// 抓住奶龙
function dragStart(e) {
if (!e.target.classList.contains('fill')) return;
fill.classList.add('hold');
setTimeout(() => fill.className = 'invisible', 0);
}
// 松开奶龙
function dragEnd() {
fill.className = 'fill';
}
// 奶龙走到新窝门口
function dragEnter(e) {
e.preventDefault();
this.classList.add('hovered');
}
// 在新窝门口晃悠(必须允许)
function dragOver(e) {
e.preventDefault(); // 核心,允许 drop
}
// 离开新窝
function dragLeave() {
this.className = 'empty';
}
// 放下奶龙
function drop() {
this.className = 'empty';
this.append(fill);
}
// 绑定事件
fill.addEventListener('dragstart', dragStart);
fill.addEventListener('dragend', dragEnd);
empties.forEach(empty => {
empty.addEventListener('dragover', dragOver);
empty.addEventListener('dragenter', dragEnter);
empty.addEventListener('dragleave', dragLeave);
empty.addEventListener('drop', drop);
});
7. 小技巧
preventDefault()必须写
不写 → 拖拽白费力
写了 → drop 顺利执行- 样式反馈很重要
在dragenter、dragleave里加样式,让用户知道这里是“合法窝”。 - 多端适配
用@media让手机和平板下变成垂直排列,拖拽体验更好。
8. 总结
HTML5 拖拽 API 的流程很简单:
- 元素加
draggable="true" - 监听拖拽源 + 目标事件
- 在
dragover阻止默认行为 - 样式反馈,让交互更直观