资讯详情

深入 Draggable Examples 示例沙箱:项目结构、构建流程与拖拽交互源码实战

📅 2026/9/21 14:08:09 | 华诺云谱 👁 阅读
深入 Draggable Examples 示例沙箱:项目结构、构建流程与拖拽交互源码实战
深入 Draggable Examples 示例沙箱项目结构、构建流程与拖拽交互源码实战【免费下载链接】draggableThe JavaScript Drag Drop library your grandparents warned you about.项目地址: https://gitcode.com/gh_mirrors/dr/draggable本文以官方示例站点Examples为线索系统讲解 Draggable 拖拽库配套示例工程的整体架构从本地开发环境的搭建、Nunjucks 视图与 SCSS 样式组织到各类拖拽/排序/放置/插件示例的源码实现与构建工具链。读完本文你将掌握如何在examples/目录中定位、运行、改造甚至新增一个拖拽示例并理解每个示例如何与 Draggable 核心库Sortable、Droppable、Swappable、Plugins对接。Examples 站点是什么一本可运行的拖拽问答手册Draggable 仓库中的examples/目录是一套完整的示例沙箱站点其定位正如 examples/README.md 开篇所述辅助想要用 Draggable 构建 Web 应用的开发者同时也面向想为库本身做贡献的开发者。这套沙箱要回答的核心问题是Draggable 的常见实现疑问并为任何能想到的拖放问题提供解决方案。一个非常典型的例子是触摸事件的处理Draggable 在触摸场景下需要在 DOM 中复制source节点即 Mirror 机制这会带来布局维护上的摩擦——拖动过程中如何保证页面布局不乱答案是巧妙的 CSS。仓库中就有多个示例专门演示这一布局问题的解法例如examples/src/content/下的示例普遍通过.Block--isDraggable等类与镜像约束配置协同处理。从内容组织上看示例按业务类型被划分为五大类Draggable、Droppable、Sortable、Swappable和Plugins外加一个落地页Home。每个示例都遵循一个.html视图 一个.scss样式 一个index.js逻辑的固定形态是研究 Draggable 各模块 API 的绝佳入口。本地开发三步跑起示例站点examples/README.md 给出了极简的本地开发流程在仓库根目录执行即可克隆仓库git clone https://gitcode.com/gh_mirrors/dr/draggable.git安装依赖并启动yarn yarn start打开浏览器访问本地站点默认地址为http://localhost:3000实际地址以控制台输出的为准可能因端口占用而变化。这一步背后实际触发了 examples/package.json 中定义的start脚本start: NODE_OPTIONS--openssl-legacy-provider gulp start $npm_package_config_tools其中$npm_package_config_tools来自同文件的config.tools字段config: { tools: --require babel/register --gulpfile tools }也就是说start本质上是用babel/register预加载 ES 模块语法并让 gulp 从tools/目录读取任务定义gulpfile 就是 examples/tools/index.js。项目使用 gulp 4 作为构建编排工具任务被拆分在 examples/tools/tasks/index.js 中暴露了四个任务startServer来自 examples/tools/server.js、scripts、styles和views。除了startexamples/package.json 还提供了完整的生命周期脚本值得逐一了解脚本作用clean用rimraf删除dist/与bundle-report.html保证构建产物干净views单独编译 Nunjucks 视图scripts单独用 webpack 打包 JSstyles单独编译 SCSSstart启动 browsersync 开发服务器并开启文件监听build先clean再执行完整 gulp buildbuild:prod以NODE_ENVproduction执行生产构建lint:js/prettier:jsESLint 检查 / 自动修复 JSlint:scss/prettier:scssstylelint 检查 / 格式化 SCSS依赖方面运行时依赖只有core-jsPolyfill和threads样式属性管理库来自beefchimi/threads的 v1.0.0-beta8开发依赖则覆盖了gulp、browser-sync、webpack、nunjucks、gulp-nunjucks、gulp-sass、gulp-postcss、autoprefixer、cssnano、stylelint、eslint等一整套现代前端构建工具链。代码风格与 Shopify 一致的 ESLint / Stylelint 约束如果你要为本仓库贡献代码必须遵循 examples/package.json 中配置的 Shopify 官方规范JSeslint-plugin-shopifyv35配合eslint-plugin-prettier强制格式化风格执行yarn lint:js时要求--max-warnings 0即零警告放行SCSSstylelint-config-shopifyv7.4可用yarn lint:scss校验、yarn prettier:scss自动修复。正如文档所说如果只是本地把玩而不打算提交代码直接关闭编辑器里的 linter 即可不影响示例的运行与调试。项目结构co-located 的组件化组织examples/采用co-located component structure组件就近存放的目录组织方式即一个组件相关的所有文件都放在同一个文件夹里。日常开发绝大多数时间只需要关注两个目录src/components和src/content。根目录下的各种配置文件gulp、webpack、postcss、babel 等在未改动编译逻辑时基本可以忽略。建议先通读 examples/package.json 熟悉可用脚本见上文表格。下面按文档脉络逐目录拆解。src/viewsNunjucks 顶层页面模板src/views存放顶层页面模板模板语言为Nunjucksnunjucks。整个站点只有一个基础布局模板 examples/src/views/templates/document.html其余所有页面视图都通过{% extends templates/document.html %}继承它。该模板定义了head、Sidebar、Main等插槽block例如!DOCTYPE html html langen>{% extends templates/document.html %} {% import components/Document/Head.html as Head %} {% import components/Sidebar/Sidebar.html as Sidebar %} {% import components/PageHeader/PageHeader.html as PageHeader %} {% import content/Sortable/SimpleList/SimpleList.html as SimpleList %} {% set ViewAttr { id: SimpleList, parent: Sortable, child: Simple list, subheading: Sort elements in a single collection, maintaining order for all but the element being dragged. } %} {% block PageId %}{{ ViewAttr.id }}{% endblock %} {% block head %}{{ Head.render(ViewAttr) }}{% endblock %} {% block sidebar %}{{ Sidebar.render(ViewAttr, DataPages) }}{% endblock %} {% block main %} {{ PageHeader.render(ViewAttr) }} {{ SimpleList.render(ViewAttr.id) }} {% endblock %}注意这里的DataPages变量它来自 examples/src/views/data-pages.json。在 examples/tools/tasks/views.js 中该 JSON 会被gulp-data注入到每个视图的渲染上下文中用于生成侧边栏导航等页面清单信息。编译完成后所有 HTML 还会经过gulp-htmlmin压缩开启minifyJS、removeComments、collapseWhitespace输出到dist/。src/styles全局样式与 Threads 主题src/styles存放全局样式入口清单是 examples/src/styles/examples-app.scss。文档建议除非要新增 import否则不必改动这里的文件。这个工程使用Threads来管理样式属性设计 tokenThreads 主题值集中在 examples/src/styles/examples-theme/ 目录即原文档所述styles/themes/examples在当前仓库中的实际位置为src/styles/examples-theme/。该目录下是一批 SCSS 部分文件_color.scss、_spacing.scss、_type-scale.scss、_border.scss、_breakpoint.scss、_cursor.scss、_duration.scss、_easing.scss、_font-stack.scss、_layout-length.scss、_z-index.scss最终由examples-theme.scss汇总导出。整个站点的色彩、间距、排版、动效时长、层级等设计变量都集中于此供各组件样式消费。此外examples/src/styles/utils/ 下还有global/全局动画、布局、排版工具与shared/共享 functions/layout/typography/animation两套工具类。样式构建由 examples/tools/tasks/styles.js 负责经 gulp-sass、gulp-postcss、autoprefixer 处理生产模式下还会用 cssnano 压缩。src/scripts全局脚本与示例初始化入口与src/styles对称src/scripts存放全局脚本入口为 examples/src/scripts/examples-app.js。该入口文件做了三件事初始化 Google AnalyticsAnalytics组件跟踪 IDUA-107063633-1初始化移动端导航MobileNav组件遍历src/content/index.js导出的所有示例模块并逐个调用import Content from ../content; // ... for (const Example in Content) { if (Content.hasOwnProperty(Example)) { Content[Example](); } }示例清单见 examples/src/content/index.js共 12 个示例 1 个 Home 落地页Home、DragEvents、UniqueDropzone、SimpleList、Transformed、MultipleContainers、Flexbox、Floated、GridLayout、PluginsCollidable、PluginsSnappable、PluginsSwapAnimation、PluginsSortAnimation。每个示例模块都默认导出一个初始化函数通常带守卫当页面容器不存在时提前return false由examples-app.js统一执行。JS 的打包由 examples/tools/tasks/scripts.js 调用 webpack配置见 examples/tools/webpack.config.js完成所有 JS 均生成 source map因此可以直接在浏览器中追踪错误与console.log输出。src/content示例内容组件按类型分组src/content存放所有内容组件即与具体示例强相关的代码。分组规则与 Draggable 的模块体系一一对应Draggable/DragEvents基础拖拽事件演示Droppable/UniqueDropzone放置dropzone示例Sortable/SimpleList、Sortable/Transformed、Sortable/MultipleContainers排序示例Swappable/Flexbox、Swappable/Floated、Swappable/GridLayout交换示例Plugins/Collidable、Plugins/Snappable、Plugins/SwapAnimation、Plugins/SortAnimation插件示例Home落地页。每个示例固定由三个文件组成.html视图定义示例的 DOM 结构.scss样式示例专属样式index.js示例逻辑在这里初始化 Draggable或其派生模块并编写交互逻辑。每个index.js默认导出的函数会被 examples/src/scripts/examples-app.js 统一 import 并调用无需手工逐个注册。src/components共享组件与 Nunjucks 宏src/components存放跨示例复用的共享组件例如Block——被大量示例用作可拖拽元素。按原文档说明该目录还曾包含一个src/root子目录用于存放需要原样复制到dist/根目录的文件如manifest.json、.htaccess通常无需改动。共享组件的组织形态很灵活纯样式组件只有.scss文件标记直接写在内容组件里样式值拆分如果某组件有专属的样式值但需要被其他组件共享会拆出独立的props.scss如 examples/src/components/Block/props.scss变体类拆分变体variant样式集中放在variants.scss如 examples/src/components/Block/variants.scss带模板的组件用 Nunjucks 宏定义可复用标记例如 examples/src/components/Block/Block.html 导出的render(heading, options)宏带逻辑的组件拥有自己的 JS例如 examples/src/components/Plate/index.js负责管理 Plate 组件在拖拽事件中的 transform 变换。以 Block 组件为例视图中的典型用法是{{ Block.render(one, {index: 1, draggable: true}) }}这条语句会渲染出一个完整的 Block 标记以字符串one作为其标题Heading并根据options追加Block--item1和Block--isDraggable两个类。展开 examples/src/components/Block/Block.html 的宏实现可以看到其类名拼接逻辑{% macro render(heading, options {}) %} {% set classes [Block] %} {% if options.type and (options.type Shell or options.type Hollow or options.type Stripes) %} {% set classes (classes.push(Block--type options.type), classes) %} {% endif %} {% set classes (classes.push(Block--item options.index) if options.index, classes) %} {% set classes (classes.push(Block--isDraggable) if options.draggable, classes) %} ... {{ openingTag | safe }} div classBlockContent {% if heading.length 1 %} h3 classHeading Heading--size2 text-no-select{{ heading }}/h3 {% endif %} ... /div {{ closingTag | safe }} {% endmacro %}Block 支持typeShell/Hollow/Stripes、index、draggable、classes等选项并通过options.draggable决定是否在元素上添加titleClick to drag提示及拖拽纹理图案这正是触摸场景下复制source节点方案在标记层面的配合。tools构建脚本目录tools集中了全部构建脚本一般情况下无需进入除非你想改变编译方式。它的组成包括examples/tools/index.jsgulpfile 入口汇总startServer、scripts、styles、views四个任务examples/tools/tasks/views.jsNunjucks 编译 gulp-data注入 htmlminexamples/tools/tasks/scripts.jswebpack 打包examples/tools/tasks/styles.jsSCSS 编译examples/tools/server.js 与 examples/tools/watch.jsbrowsersync 服务器与文件监听examples/tools/webpack.config.js 与 examples/tools/webpack.plugins.jswebpack 配置。运行服务器与文件监听所见即所得的开发体验执行yarn start会启动一个browsersync服务器来预览站点。运行期间对src/下任意文件的新增或修改都会触发对应任务重编译并输出到dist/随后浏览器自动刷新页面。两个细节值得注意SCSS 热注入样式改动不会触发整页刷新新样式会被直接注入当前页面browsersync 的 CSS 注入机制核心库联动监听文件监听器同时关注 Draggable 核心库的构建产物目录原文档写作draggable/lib这意味着你可以一边运行示例服务器一边修改核心库源码——核心库脚本重编译后示例页面同样会自动重载。对插件、传感器等底层实现进行调试时这条链路极其高效。示例源码实战从三个典型示例看 Draggable 的用法Sortable单容器排序SimpleListexamples/src/content/Sortable/SimpleList/index.js 是入门级示例演示如何把一组元素变成可排序列表import {Sortable} from shopify/draggable; export default function SimpleList() { const containerSelector #SimpleList .StackedList; const containers document.querySelectorAll(containerSelector); if (containers.length 0) { return false; } const sortable new Sortable(containers, { draggable: .StackedListItem--isDraggable, mirror: { appendTo: containerSelector, constrainDimensions: true, }, }); return sortable; }要点解析函数先做容器存在性守卫containers.length 0时直接返回避免在非本示例页面上报错——这是所有示例统一遵循的约定new Sortable(containers, {...})接收一个容器集合draggable指定可拖拽子元素的选择器mirror.appendTo把镜像节点挂到容器内部mirror.constrainDimensions让镜像保持原尺寸。这正是文档开头提到的触摸事件需要复制 source 节点在 API 层的体现Mirror 机制是 Draggable 对触摸/指针拖拽的核心抽象相关实现见 src/Draggable/Plugins/Mirror。Droppable限定目标放置区UniqueDropzoneexamples/src/content/Droppable/UniqueDropzone/index.js 展示了如何通过事件系统实现只能放回同源区域的约束逻辑import {Droppable} from shopify/draggable; export default function UniqueDropzone() { const containers document.querySelectorAll(#UniqueDropzone .BlockLayout); // ...守卫省略 const droppable new Droppable(containers, { draggable: .Block--isDraggable, dropzone: .BlockWrapper--isDropzone, mirror: {constrainDimensions: true}, }); let droppableOrigin; droppable.on(drag:start, (evt) { droppableOrigin evt.originalSource.parentNode.dataset.dropzone; }); droppable.on(droppable:dropped, (evt) { if (droppableOrigin ! evt.dropzone.dataset.dropzone) { evt.cancel(); } }); return droppable; }Droppable通过dropzone指定放置区并暴露drag:start、droppable:dropped等事件。此示例在drag:start时记录元素来源的data-dropzone在droppable:dropped时通过evt.cancel()阻止跨区放置——这是事件驱动行为控制的教科书式用法也印证了根 README.md 中通过 hook 进 Draggable 事件生命周期扩展拖拽行为的特性。Plugins碰撞检测Collidableexamples/src/content/Plugins/Collidable/index.js 演示了插件的注册与事件订阅import {Droppable, Plugins} from shopify/draggable; const droppable new Droppable(containers, { draggable: .Block--isDraggable, dropzone: .BlockWrapper--isDropzone, collidables: .CollidableObstacle, mirror: {appendTo: containerSelector, constrainDimensions: true}, plugins: [Plugins.Collidable], }); droppable.on(collidable:in, ({collidingElement}) { if (collidingElement.classList.contains(wallClass)) { walls.forEach((wall) wall.classList.add(isColliding)); } else { collidingElement.classList.add(isColliding); } }); droppable.on(collidable:out, ({collidingElement}) { // 对称地移除 isColliding 类 });通过plugins: [Plugins.Collidable]注入插件并用collidables声明障碍物随后订阅collidable:in/collidable:out事件在碰撞进入/离开时切换高亮类。这展示了 Draggable 用插件扩展拖拽能力的架构思路相关插件实现见 src/Plugins/Collidable。贡献新示例让社区覆盖更多拖拽场景原文档鼓励开发者把遇到的、尚未被覆盖的场景沉淀为新的 Example并给出了三条建议归入正确的分组按Draggable/Droppable/Sortable/Swappable/Plugins五大类放置遵循代码风格与站点设计遵守 ESLint/stylelint 配置尽量与现有站点视觉保持一致复用已有组件优先基于src/components中现成的组件如Block、Plate来组装示例确有需要时也可以自行设计并构建新组件。在动手前建议先通读 examples/README.md、examples/package.json 与 CONTRIBUTING.md了解构建脚本与协作流程。新增一个示例的标准路径是在src/content对应分组下新建目录 → 编写xxx.html/xxx.scss/index.js→ 在 examples/src/content/index.js 中注册 → 在src/views下新增继承document.html的视图 → 在 examples/src/views/data-pages.json 中登记页面元数据 → 启动yarn start验证。总结从示例沙箱到源码级理解Draggable Examples 不只是一个演示页面集合它是一套完整的、可运行、可扩展的工程模板开发体验yarn yarn start一键启动 browsersyncSCSS 热注入、核心库联动监听让调试链路极短工程组织co-located 组件结构 Nunjucks 模板 Threads 样式 token gulp/webpack 构建链各司其职学习价值每个示例都是对应模块src/Draggable、src/Sortable、src/Droppable、src/Swappable、src/Plugins的活文档——从Sortable的 mirror 配置、Droppable的事件取消到Collidable插件的碰撞事件都能在此找到可直接复制的最小实现。无论你是要基于 Draggable 构建生产级拖拽应用还是想深入源码贡献功能这套示例沙箱都是最快的起点。【免费下载链接】draggableThe JavaScript Drag Drop library your grandparents warned you about.项目地址: https://gitcode.com/gh_mirrors/dr/draggable创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。