下面我将为你介绍几种主流的实现方法,从最简单到最灵活,并提供完整的代码示例。

bootstrap 网页模块可移动
(图片来源网络,侵删)

核心思路

无论使用哪种方法,基本思路都是一致的:

  1. HTML 结构: 创建一个可拖拽的容器,通常使用 Bootstrap 的 card 组件,因为它有标题栏、内容区和阴影,非常适合作为拖拽模块。
  2. CSS 样式: 为拖拽模块添加基本样式,并定义拖拽过程中的视觉反馈(如改变鼠标指针、阴影等)。
  3. JavaScript 逻辑: 监听鼠标事件(或触摸事件),当用户在模块标题栏上按下鼠标时,开始拖拽;移动鼠标时,更新模块的位置;松开鼠标时,结束拖拽。

使用原生 JavaScript (最轻量,适合学习原理)

这种方法不依赖任何第三方库,代码量稍多,但能让你完全理解拖拽的实现原理。

实现步骤

  1. HTML: 创建一个 card 作为可拖拽模块,并给它一个唯一的 idcard-header 将是我们拖拽的“把手”。
  2. CSS: 添加一些自定义 CSS 来处理拖拽时的样式,比如改变鼠标光标和提升层级。
  3. JavaScript: 编写拖拽逻辑。

完整代码示例

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">Bootstrap 可移动模块 (原生 JS)</title>
    <!-- 1. 引入 Bootstrap CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
    <style>
        body {
            padding: 50px; /* 给页面一些内边距 */
        }
        /* 可拖拽模块的容器,用于定位 */
        .draggable-container {
            position: relative;
            width: 300px;
            height: 200px;
        }
        /* 可拖拽模块本身 */
        .draggable-card {
            position: absolute; /* 关键:脱离文档流,以便通过 top/left 定位 */
            width: 100%;
            cursor: move; /* 默认光标 */
            user-select: none; /* 防止拖拽时选中文本 */
        }
        /* 拖拽中的样式 */
        .draggable-card.dragging {
            opacity: 0.8; /* 半透明 */
            box-shadow: 0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23); /* 增强阴影 */
            z-index: 1000; /* 提升到最上层 */
            cursor: grabbing; /* 抓取光标 */
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>可拖拽的 Bootstrap 模块</h1>
        <p>拖动下面的卡片标题栏来移动它。</p>
        <!-- 可拖拽模块 -->
        <div class="draggable-container">
            <div id="myCard" class="card draggable-card">
                <div class="card-header bg-primary text-white" style="cursor: grab;">
                    <strong>拖拽我!</strong>
                </div>
                <div class="card-body">
                    <p class="card-text">这是一个使用原生 JavaScript 实现的可拖拽 Bootstrap 卡片,你可以在页面内自由移动它。</p>
                </div>
            </div>
        </div>
    </div>
    <!-- 2. 引入 Bootstrap JS (可选,这里我们主要用原生JS) -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
    <script>
        document.addEventListener('DOMContentLoaded', () => {
            const card = document.getElementById('myCard');
            const header = card.querySelector('.card-header');
            let isDragging = false;
            let currentX;
            let currentY;
            let initialX;
            let initialY;
            let xOffset = 0;
            let yOffset = 0;
            // 鼠标按下事件
            header.addEventListener('mousedown', dragStart);
            // 鼠标移动事件
            document.addEventListener('mousemove', drag);
            // 鼠标松开事件
            document.addEventListener('mouseup', dragEnd);
            // 触摸事件支持 (移动端)
            header.addEventListener('touchstart', dragStart);
            document.addEventListener('touchmove', drag);
            document.addEventListener('touchend', dragEnd);
            function dragStart(e) {
                if (e.type === "touchstart") {
                    initialX = e.touches[0].clientX - xOffset;
                    initialY = e.touches[0].clientY - yOffset;
                } else {
                    initialX = e.clientX - xOffset;
                    initialY = e.clientY - yOffset;
                }
                if (e.target === header) {
                    isDragging = true;
                    card.classList.add('dragging');
                }
            }
            function drag(e) {
                if (isDragging) {
                    e.preventDefault();
                    if (e.type === "touchmove") {
                        currentX = e.touches[0].clientX - initialX;
                        currentY = e.touches[0].clientY - initialY;
                    } else {
                        currentX = e.clientX - initialX;
                        currentY = e.clientY - initialY;
                    }
                    xOffset = currentX;
                    yOffset = currentY;
                    setTranslate(currentX, currentY, card);
                }
            }
            function dragEnd(e) {
                initialX = currentX;
                initialY = currentY;
                isDragging = false;
                card.classList.remove('dragging');
            }
            function setTranslate(xPos, yPos, el) {
                el.style.transform = `translate(${xPos}px, ${yPos}px)`;
            }
        });
    </script>
</body>
</html>

使用 jQuery UI (经典、简单)

如果你的项目已经使用了 jQuery,jQuery UI 提供了非常简单易用的 draggable 组件。

实现步骤

  1. HTML: 与原生 JS 方法的 HTML 结构类似,但不需要复杂的 CSS。
  2. 引入库: 引入 jQuery、jQuery UI 的 CSS 和 JS 文件。
  3. JavaScript: 只需一行代码即可让元素可拖拽。

完整代码示例

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">Bootstrap 可移动模块 (jQuery UI)</title>
    <!-- 1. 引入 Bootstrap CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
    <!-- 2. 引入 jQuery UI CSS -->
    <link rel="stylesheet" href="https://code.jquery.com/ui/1.13.2/themes/base/jquery-ui.css">
    <style>
        body {
            padding: 50px;
        }
        /* 只需要一个容器来限制拖拽范围 */
        .draggable-container {
            width: 300px;
            height: 200px;
            border: 1px dashed #ccc; /* 可视化边界 */
        }
        /* jQuery UI 会自动添加 'ui-draggable' 类 */
        .ui-draggable-dragging {
            z-index: 1000; /* 确保拖拽时在最上层 */
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>可拖拽的 Bootstrap 模块 (jQuery UI)</h1>
        <p>拖动下面的卡片标题栏来移动它。</p>
        <!-- 可拖拽模块 -->
        <div class="draggable-container">
            <div id="myCard" class="card">
                <div class="card-header bg-success text-white">
                    <strong>jQuery UI 拖拽我!</strong>
                </div>
                <div class="card-body">
                    <p class="card-text">这是一个使用 jQuery UI 实现的可拖拽 Bootstrap 卡片,非常简单!</p>
                </div>
            </div>
        </div>
    </div>
    <!-- 3. 引入 jQuery -->
    <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
    <!-- 4. 引入 jQuery UI JS -->
    <script src="https://code.jquery.com/ui/1.13.2/jquery-ui.js"></script>
    <!-- 5. 引入 Bootstrap JS (可选) -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
    <script>
        $(document).ready(function() {
            // 只需这一行!
            $("#myCard").draggable({ handle: ".card-header" });
            // handle: ".card-header" 表示只有点击这个区域才能拖拽,非常实用!
        });
    </script>
</body>
</html>

使用现代库 interact.js (功能强大,性能好)

interact.js 是一个专注于拖拽、缩放、旋转的轻量级现代库,不依赖 jQuery,性能优秀,API 设计也很优雅。

bootstrap 网页模块可移动
(图片来源网络,侵删)

实现步骤

  1. HTML: 结构与前两种方法类似。
  2. 引入库: 通过 CDN 引入 interact.js
  3. JavaScript: 使用 interact API 来配置拖拽行为。

完整代码示例

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">Bootstrap 可移动模块 (interact.js)</title>
    <!-- 1. 引入 Bootstrap CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
    <style>
        body {
            padding: 50px;
        }
        .draggable-container {
            width: 300px;
            height: 200px;
            border: 1px dashed #ccc;
        }
        /* interact.js 会自动添加 'interact-dragging' 类 */
        .interact-dragging {
            opacity: 0.8;
            box-shadow: 0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23);
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>可拖拽的 Bootstrap 模块 (interact.js)</h1>
        <p>拖动下面的卡片标题栏来移动它。</p>
        <!-- 可拖拽模块 -->
        <div class="draggable-container">
            <div id="myCard" class="card">
                <div class="card-header bg-info text-white">
                    <strong>interact.js 拖拽我!</strong>
                </div>
                <div class="card-body">
                    <p class="card-text">这是一个使用 interact.js 实现的可拖拽 Bootstrap 卡片,功能强大且现代!</p>
                </div>
            </div>
        </div>
    </div>
    <!-- 2. 引入 interact.js -->
    <script src="https://cdn.jsdelivr.net/npm/interactjs/dist/interact.min.js"></script>
    <!-- 3. 引入 Bootstrap JS (可选) -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
    <script>
        document.addEventListener('DOMContentLoaded', () => {
            interact('#myCard')
                .draggable({
                    // 指定拖拽手柄
                    allowFrom: '.card-header',
                    // 限制在父元素内拖拽
                    modifiers: [
                        interact.modifiers.restrictRect({
                            restriction: 'parent',
                            endOnly: true
                        })
                    ],
                    // 拖拽时的监听器
                    listeners: {
                        // 每次移动时调用
                        move: dragMoveListener,
                        // 开始拖拽时调用
                        start(event) {
                            event.target.classList.add('interact-dragging');
                        },
                        // 结束拖拽时调用
                        end(event) {
                            event.target.classList.remove('interact-dragging');
                        }
                    }
            });
            function dragMoveListener(event) {
                const target = event.target;
                // 保持 transform 的值,并更新 x 和 y
                const x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx;
                const y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;
                // 使用 transform 进行移动,性能更好
                target.style.transform = `translate(${x}px, ${y}px)`;
                // 保存最新的位置
                target.setAttribute('data-x', x);
                target.setAttribute('data-y', y);
            }
        });
    </script>
</body>
</html>

总结与对比

方法 优点 缺点 适用场景
原生 JavaScript - 无依赖,加载快
- 可控性最高,完全理解原理
- 代码量多,需要处理兼容性
- 事件处理逻辑较复杂
- 学习和教学
- 对性能和体积有极致要求的项目
- 需要高度定制化拖拽行为
jQuery UI - 非常简单,一行代码搞定
- 文档丰富,社区支持好
- 依赖 jQuery,增加项目体积
- 相对老旧,不符合现代前端趋势
- 已经在使用 jQuery 的老项目
- 需要快速实现简单拖拽功能
interact.js - 轻量,不依赖其他库
- API 现代,功能强大(拖拽、缩放、旋转)
- 性能好,支持触摸事件
- 专为交互设计
- 相比 jQuery UI 需要学习新的 API - 现代前端项目(Vue, React, Angular 等)
- 需要复杂交互功能(如拼图、看板)
- 对移动端支持要求高的项目

推荐选择:

  • 新项目或对性能有要求:首选 interact.js
  • 快速原型或已有 jQuery 项目:可以使用 jQuery UI
  • 学习和理解原理:从 原生 JavaScript 开始是最好的选择。
bootstrap 网页模块可移动
(图片来源网络,侵删)