【救护车APP源码】【scikit中svm源码】【短链生产源码】拖拽制图源码_图形拖拽编程原理

时间:2024-12-23 01:20:00 编辑:尺码助手源码 来源:主力净额指标源码

1.react组件可视化拖拽页面搭建,拖拽图形拖拽源码生成,你有什么想法?
2.深入解析React DnD拖拽原理,轻松掌握拖放技巧!制图
3.可拖拽、源码原理缩放、编程旋转组件网格效果及使用方法
4.2022年末了,拖拽图形拖拽react拖拽组件和最牛的制图救护车APP源码代码调试技巧一起学!
5.vue3功能最强的源码原理拖拽库-dnd起步教程
6.SortableJS原理分析(源码)

拖拽制图源码_图形拖拽编程原理

react组件可视化拖拽页面搭建,源码生成,你有什么想法?

       本文主要回顾了后台系统中所见即所得大盘编辑器的开发过程。拖拽功能是编程实现该编辑器的关键,涉及元素的拖拽图形拖拽拖动和放置,以及相关事件的制图监听与处理。本文选择了React DnD库来实现这一功能,源码原理因为它提供了对拖拽底层的编程封装,简化了实现过程。拖拽图形拖拽

       在React DnD中,制图有四个核心概念:backend、源码原理monitor、drag和drop。backend负责处理PC端和移动端事件的差异,monitor监控整个拖动事件的状态,drag代表可拖动元素,而drop则代表可以放置元素。在使用这些概念时,通过useDrag和useDrop函数生成对应的ref,实现拖动和放置功能。

       对于拖动和放置的细节,React DnD提供了丰富的功能,如动画、虚拟列表、移动端兼容等。re-resizable库则专门用于实现拉伸功能,通过设置value和onChange,组件可以轻松实现拉伸。宽高的辅助吸附可以通过设置grid来实现步长,或使用document.elementFromPoint方法迭代找到最近的子元素。

       为了实现一个拖拽系统,可以将整个系统分为四个部分:拖拽源容器、拖拽源组件、画布容器和画布组件。使用JS数组维护画布区域,React的Render模式使数据结构改变时自动更新画布内容。在处理缺失数据时,可以采用时间序列和空结果数组的方法进行补零处理。

       此外,还存在一些未完善之处,如拖拽交互的动效、布局的灵活性、组件库的建设等。为了进一步提高系统的成熟度,需要对这些方面进行优化和改进。

深入解析React DnD拖拽原理,轻松掌握拖放技巧!scikit中svm源码

       深入解析React DnD拖拽原理,轻松掌握拖放技巧!

       业务中会有一些需要实现拖拽的场景,尤其是偏视觉方向以及移动端较多。拖拽在一定程度上能让交互更加便捷,能大大提升用户体验。以业务中心子产品配置功能为例,产品模块通过拖拽来调整顺序,的确会更加方便一些。

       React DnD 是一组 React 实用程序,可帮助您构建复杂的拖放界面,同时保持组件分离。它非常适合 Trello 和 Storify 等应用程序,在应用程序的不同部分之间拖动可以传输数据,组件会根据拖放事件更改其外观和应用程序状态。React-DnD 特点包括:

       安装 react-dnd, react-dnd-html5-backend

       将需要拖拽的组件使用DndProvider进行包裹

       看下Container组件,主要是管理数据,并渲染Card列表。

       Card组件

       至此一个简单的拖拽排序列表就实现了,实现的效果类似于React DnD官网的这个示例: react-dnd.github.io/rea...,接下来我们来看看实现原理。

       主要代码代码目录结构

       核心代码主要分三个部分:

       dnd-core向backend提供数据的更新方法

       backend在拖拽时更新dnd-core中的数据

       dnd-core通过react-dnd更新业务组件

       实现原理: dnd-core向backend提供数据的更新方法,backend在拖拽时更新dnd-core中的数据,dnd-core通过react-dnd更新业务组件。

       DndProvider

       先看一下源码

       生成了一个manager,并将其放到DndContext.Provider中

       就是使用 React 的createContext创建的上下文容器组件。

       接下来看下这个manager,主要是用来控制拖拽行为,通过Provider让子节点也可以访问。我们看下创建manager的getDndContextValue方法:

       getDndContextValue方法又调用了createSingletonDndContext方法,并传入了backend、context、options、debugMode这几个属性,然后通过dnd-core中的createDragDropManager来创建manager。

       DragDropManager

       看下createDragDropManager.js中的主要代码

       使用了redux的createStore创建了store,并创建了monitor和manager实例

       通过backendFactory创建backend后端实例并安装到manager总实例

       DragDropManagerImpl的主要代码

       handleRefCountChange方法,在构造函数里通过store进行订阅

       在第一次使用useDrop或useDrag时会执行setup方法初始化backend

       在拖拽源和放置源都被卸载时则会执行teardown销毁backend

       createDragDropActions方法

       绑定一些action

       manager包含了之前生成的 monitor、store、backend,manager 创建完成,表示此时我们有了一个 store 来管理拖拽中的数据,有了 monitor 来监听数据和控制行为,能通过 manager 进行注册,可以通过 backend 将 DOM 事件转换为 action。接下来便可以注册拖拽源和放置源了。

       useDrag方法返回了一个包含3个元素的数组,CollectedProps、ConnectDragSource、ConnectDragPreview

       monitor是短链生产源码从前面Provider中的manager中获取的

       主要看下connector

       connector获取了manager.getBackend后端的数据。

       useRegisteredDragSource方法会对拖动源进行注册,会保存拖动源实例,并记录注册的数量。

       useDrop

       返回了一个包含2个元素的数组,CollectedProps、ConnectDropTarget

       monitor和connector的获取都和useDrag类似

       HTML5Backend

       使用了HTML5 拖放 API

       了解下HTML拖拽事件:一个简单拖拽操作过程,会依次触发拖拽事件:dragstart -> drag -> dragenter -> dragover (-> dragleave) -> drop -> dragend。

       drag事件会在dragstar触发后持续触发,直至drop

       dragleave事件会在拖拽元素离开一个可释放目标时触发

       HTML5Backend是React DnD 主要支持的后端,使用HTML5 拖放 API,它会截取拖动的 DOM 节点并将其用作开箱即用的“拖动预览”。React DnD 中以可插入的方式实现 HTML5 拖放支持,可以根据触摸事件、鼠标事件或其他完全不同的事件编写不同的实现,这种可插入的实现在 React DnD 中称为后端。官网提供了HTML5Backend和TouchBackend,分别用来支持web端和移动端。

       后端担任与 React 的合成事件系统类似的角色:它们抽象出浏览器差异并处理原生DOM 事件。尽管有相似之处,但 React DnD 后端并不依赖于 React 或其合成事件系统。在后台,后端所做的就是将 DOM 事件转换为 React DnD 可以处理的内部 Redux 操作。

       HTML5Backend 在初始化的时候在 window 对象上绑定拖拽事件的监听函数,拖拽事件触发时执行对应action,更新 store 中的数据,完成由 Dom 事件到数据的转变。

       TouchBackend

       HTML5 后端不支持触摸事件,因此它不适用于平板电脑和移动设备

       可以使用react-dnd-touch-backend来支持触摸设备

       ToucheBackend主要是为了支持移动端,也支持web端,在web端可以使用 mousedown、mousemove、mouseup,在移动端则使用 touchstart、touchmove、touchend。

       React-DnD 采用了分层设计,react-dnd充当接入层,dnd-core实现拖拽接口、定义拖拽行为、管理数据流向,backend将DOM事件通过redux action转换为数据。

       使用可插入的方式引入backend,使拖拽的实现可扩展且更加灵活。

       使用了单向数据流,在拖拽时不用处理中间状态,不用额外对DOM事件进行处理,只需专注于数据的变化。 React-DnD对backend的实现方式、数据的管理方式,以及整体的分销系统源码商城设计都值得借鉴。

可拖拽、缩放、旋转组件网格效果及使用方法

       本文介绍基于 vue3.x + CompositionAPI + typescript + vite 的可拖拽、缩放、旋转组件的实现及使用方法。

       在线示例和源码地址提供给读者自行查看。

       在上一篇文章中,我们探讨了实现细节,并留有两个待解问题。为了帮助读者快速上手,我们主要分享网格拖拽、缩放比的实现以及 es-drager 组件的具体使用。

       在移动操作时,我们通过校验 onMousemove 事件来实现网格功能。在网格缩放功能中,我们新增代码,确保在开启网格时,鼠标始终在小圆点按下的位置。实现这一功能的关键在于计算缩放大小,我们通过调用 calcGridResize 函数获取缩放后的尺寸。

       接下来,我们介绍如何使用 es-drager 组件。首先,通过安装依赖并全局注册组件,然后在组件中直接使用。在浏览器中,我们可以通过 HTML 标签导入 es-drager,并使用全局变量 ESDrager。

       es-drager 提供了丰富的事件,使用户能够实现更细粒度的操作。通过 snapToGrid 属性控制是否开启网格,gridX 和 gridY 分别表示网格的横纵大小。scaleRatio 属性用于设置缩放比例,确保在父标签或画布放大或缩小时,拖拽操作的精准性。

       此外,我们还可以使用方位按键来控制元素的移动。开启网格后,移动将遵循网格规则,否则每次移动为 1 像素。对于不需要使用方向键移动的情况,可以禁用 disabledKeyEvent。

       对于需要在组件中插入 echarts 图表的场景,我们同样支持。只需确保将 chart 元素的宽高设置为 %,并在监听 es-drager 的 resize 事件后调用 echarts 的 resize 方法,实现图表的自适应缩放。

       最后,我们提供了一个详细的 API 概览,包括 Drager 属性、源码求补码电路事件和插槽,帮助读者更全面地理解组件的使用方法。

年末了,react拖拽组件和最牛的代码调试技巧一起学!

        年末了,react 拖拽组件和最牛的代码调试技巧一起学!前言

       最近刷到了利用 H5drag、dropapi 进行拖拽组件实现的代码示例,于是想学习一下业界知名的一些拖拽组件。于是想从学习成本较低的react-sortable-hoc开始看起。那么对于一个学习者而言,我们应该如何地去优雅地学习第三方库呢?

       当然是「调试」啦。

       首先第一步,我们随便创建一个 react 项目,并且按照react-sortable-hoc的最简单的案例编写后准备调试。

       比如说我们想看看SortableHandler里面的具体实现,我们给它打个断点,并且创建一个 vscode debug 配置:

       按F5开启调试后我们进入SortableHandler中,看到的却是经过打包后的产物:

       这显然非常不利于去读懂代码。那么我们该如何将它变成我们能看得懂的源码呢?答案就是sourcemap!

       sourcemap 就是用于表示打包后代码和源码的映射关系。因此我们只需要开启 sourcemap 就可以进行 debug 的源码的映射。

       我们将react-sortable-hoc项目 clone 下来(这里只拉取一层 commit、一个 master 分支):

       我们可以发现整个项目是使用rollup进行打包的,我们只需要配置一下 sourcemap 开启:

       然后执行npm run build,将打包好的 dist 文件夹替换至node_modules/react-sortable-hoc/dist目录下。接着在我们测试项目中将其引入路径改为:

       然后我们再来运行一下 debug 试试看:

       瞧!这是不是非常熟悉呢?利用调试我们可以随时随地打断点,知道变量的运行时,读起源码来是不是非常轻松呢?

       注有的小伙伴可能会发现在调试的时候,打开的源码文件是只读模式,这是为什么呢?

       我们可以在 vscode 左侧的CALL STACK中找到当前文件映射到的目录。

       如果是node_modules/react-sortable-hoc/src/.../xxx.js,就证明你映射到的只是node_modules中的路径,是无法更改的。

       这时候,你可以点击该文件对应的.js.map文件,将其中的../src/xxx.js路径改成你克隆下来的react-sortable-hoc的路径。这样的话,映射到的目录就是你本地的文件,就可以编辑啦!!~

       我们修改过node_modules下的文件但又不想被覆盖,可以使用patch-package这个包。 npx patch-package react-sortable-hoc 可以生成一个 diff 文件,上传至 GitHub 上,别人 clone 后只需要运行npx patch-package即可将 diff 覆盖到node_modules下

       源码阅读组件的初始化

       我们首先来梳理一下示例代码的组件嵌套:

       SortableContainer >> SortableElement >> SortableHandler

       我们先从组件的初始化入手,从外到内一层一层解析:

       SortableContainer

       可以发现SortableContainer来初始化的时候,获取了各种 dom 结构以及绑定好了事件。

       除此之外,它 new 了一个Manager作为总的拖拽管理中心。其主要功能如下:「注册并储存可拖拽的子节点」,「记录当前激活节点的 index」,「根据 index 进行 sort」:

       最后,它渲染函数是这样的:

       即通过Provider将全局 Manager 对象传递给了子组件。

       SortableElement

       我们可以看到,其实SortableElement的初始化只是将自身节点以及一些属性信息注册到了全局Manager对象中。

       SortableHandle

       SortableHandle的代码就更简单了,只是在自身 dom 上添加了一个sortableHandle的标识,用于判断用户当前点击的节点是否是SortableHandle。这部分逻辑我们在下面就可以看到~

       事件触发

       了解了各个组件的初始化流程之后,我们可以开始调试拖拽的整个过程的实现逻辑了~

       首先我们要知道,所有的事件都是注册在SortableContainer中的,因此我们只需要对其进行调试即可。

       拖拽触发事件顺序如下图:

       下面让我们来看一下各种事件的逻辑吧:

       handleStart

       在handleStart的这个回调函数中,我们可以发现它主要做了一下事情:

       handlePress

       注意看,这个函数有一个比较关键的思想:就是利用克隆节点来模拟正在拖拽的节点。计算并记录好所需要的图形指标并且赋值到新节点上,并且设置position:fixed。

       最后在绑定上move事件的监听----handleSortMove.

       handleSortMove

       函数本身很简洁,首先是updateHelperPosition。

       updateHelperPosition

       updateHelperPosition的代码经过清理后,核心就在于对克隆元素设置translate,来模拟拖拽的过程。

       其次就是最重要的animateNodes函数了。

       这里包含了拖拽排序最核心的节点移动逻辑。核心思想如下:

       遍历所有sortableElement,如果是当前激活节点,则把原有节点透明化。(因为有克隆节点了);如果不是,则判断激活节点的坐标以及当前遍历元素的坐标的大小,依此来进行translate3d的动画。

       handleSortEnd

       最后,当拖拽结束后,触发handleSortEnd。主要逻辑是做一些善后处理,清理各种事件监听器,全局 Manager 的变化,本身被拖拽元素恢复透明度等。。

       总结

       到这里,整个react-sortable-hoc实现的大致思想就全部介绍完毕啦。它并没有利用 h5 的dragapi,而是利用mousemove、touchmove之类的事件实现 h5 和移动端的兼容。利用 css3 的动画来实现 sort 效果。

       但实现过程中也有一些缺点。

       比如reactDom.findDomNodeapi,react 并不推荐使用它来去获取 dom,可以换成ref。

       比如只能在react类组件中使用。

       其他

       觉得封装的比较好的工具函数用于学习记录:

       2. 获取当前元素距离窗口的偏移值(也可以使用elm.getBoundingClientRect())

       3.移动数组内元素

       4. 过滤对象某些属性

vue3功能最强的拖拽库-dnd起步教程

       拖拽功能在Vue开发中不可或缺,Vue3中使用功能强大的dnd库,相较于vue-draggable,dnd在扩展性方面更胜一筹,推荐使用dnd库进行拖拽功能开发。

       为了简化学习dnd的步骤,我们可以按照以下上手思路进行。首先,直接从dnd的示例代码出发,理解dnd的运行流程,这通常只需很少的代码量,实际操作一遍就能深刻理解dnd的功能。接下来,对照dnd的官方示例,根据自身业务需求,下载官方示例代码并进行改造,以适应具体的业务场景。使用此方法,能够提高业务实现的效率,同时加快学习上手速度。当然,如果觉得有用的话,支持一下也是鼓励写作的动力。

       下面是一个简单的dnd流程示例,用于实现将一个拖拽源移动到容器内的功能:

       首先,创建可以被拖拽的元素(Drag)。

       其次,监控拖拽源的状态变化,例如在拖拽过程中,查看拖拽源的实时位置或状态信息。

       然后,创建接受拖拽源的容器(Drop),并实时监听容器内的情况,例如更新拖拽源在容器内的位置,以此实现拖拽功能的完整过程。

       若要更深入理解dnd的运作流程,可以对照实现过程中的代码细节回顾整个流程。要实现特定业务逻辑,可查阅dnd官方文档中提供的示例代码库,找到与自己业务需求相似的例子,通过下载并修改官方源代码,以适应实际项目需求。这种方法能够快速上手dnd,并熟练掌握其应用技巧。

       综上所述,dnd库提供了一种高效、灵活的拖拽功能解决方案,在Vue3中使用dnd不仅可以简化开发过程,还能提高代码的可维护性和可扩展性。在实际开发中,灵活运用dnd的特性与方法,可以帮助开发者更快地构建出高质量的拖拽功能,满足各种应用需求。

SortableJS原理分析(源码)

       前言

       SortableJS是基于H5拖拽API实现的一个轻量级JS拖拽排序库,它适用于以下一些场景:

       容器项目拖动排序:容器列表内的子项目,通过拖动进行位置调换,且具有动画效果;

       容器间的项目移动:将一个容器列表中的子项目,拖动到另一个容器列表中(移动/克隆)。

       不论是容器内元素顺序排序,或是两个容器内的元素进行移动,本质上是在通过操作DOM来实现。

       下面我们先熟悉一下SortableJS基本使用。

示例

       1、HTML结构:

<divclass="row"><divid="leftContainer"class="list-groupcol-6"><divclass="list-group-item">Item1</div><divclass="list-group-item">Item2</div><divclass="list-group-item">Item3</div><divclass="list-group-item">Item4</div><divclass="list-group-item">Item5</div><divclass="list-group-item">Item6</div></div><divid="rightContainer"class="list-groupcol-6"><divclass="list-group-itemtinted">Item1</div><divclass="list-group-itemtinted">Item2</div><divclass="list-group-itemtinted">Item3</div><divclass="list-group-itemtinted">Item4</div><divclass="list-group-itemtinted">Item5</div><divclass="list-group-itemtinted">Item6</div></div></div>

       2、为容器实例化:

newSortable(leftContainer,{ group:{ name:'group',pull:'clone',put:true},});newSortable(rightContainer,{ group:'group',});

       现在,就可以在容器内进行排序拖动,或者拖动左侧容器元素,添加到右侧容器中。

思路分析

       在看源码之前,还是需要对H5拖拽用法有一定了解,如果不熟悉,直接去看源码很容易就放弃。

       若你对H5拖拽API比较熟悉,就可以根据SortableJS的视图呈现效果,想出个大概思路。

       拖拽,首先要搞清楚两个词汇对象:

       拖动元素:作为拖拽元素被拖起(下文叫dragEl);

       目标元素:作为拖拽元素即将被放置时的参照物(下文叫target);

       在SortableJS中,拖拽离不开以下几个事件:

       dragstart:作为拖拽元素,按下鼠标开始拖动元素时触发(拖拽周期只触发一次);

       dragend:作为拖拽元素,当鼠标松开拖放元素时触发(拖拽周期只触发一次);

       dragover:作为拖拽元素,当拖动元素进行移动,会持续触发,需要在这里取消默认事件,否则元素无法被拖动(松开时元素的预览幽灵图又回去了);

       drop:作为目标元素,当鼠标松开拖放元素时触发(拖拽周期只触发一次);

       下面我们一起去分析SortableJS具体实现。

源码实例构造函数

       从上面的示例使用上得知,SortableJS是一个构造函数,接收容器元素和配置项:

constexpando='Sortable'+(newDate).getTime();functionSortable(el,options){ this.el=el;//rootelementthis.options=options=Object.assign({ },options);el[expando]=this;constdefaults={ group:null,sort:true,//默认容器可以排序animation:0,removeCloneOnHide:true,//将一个容器元素拖动至另一个容器后,默认setData:function(dataTransfer,dragEl){ dataTransfer.setData('Text',dragEl.textContent);}};//参数合并for(varnameindefaults){ !(nameinoptions)&&(options[name]=defaults[name]);}//规范group_prepareGroup(options);//绑定原型方法为私有方法for(varfninthis){ if(fn.charAt(0)==='_'&&typeofthis[fn]==='function'){ this[fn]=this[fn].bind(this);}}//绑定指针触摸事件,类似mousedownon(el,'pointerdown',this._prepareDragStart);on(el,'dragover',this);on(el,'dragenter',this);}

       初始化示例做了以下几件事件:

       将传入的参数与提供的默认参数进行合并;

       规范传入的group格式;

       将原型上的方法绑定在实例对象上,便于使用;

       绑定pointerdown、dragover、dragenter事件,其中pointerdown可以看作是dragstart事件,做了一些拖拽前的准备工作。

       group用于两个容器元素的相互拖拽场景,规范group核心代码如下:

function_prepareGroup(options){ functiontoFn(value,pull){ returnfunction(to,from){ letsameGroup=to.options.group.name&&from.options.group.name&&to.options.group.name===from.options.group.name;if(value==null&&(pull||sameGroup)){ returntrue;}elseif(value==null||value===false){ returnfalse;}elseif(pull&&value==='clone'){ returnvalue;}else{ returnvalue===true;}};}letgroup={ };letoriginalGroup=options.group;if(!originalGroup||typeoforiginalGroup!='object'){ originalGroup={ name:originalGroup};}group.name=originalGroup.name;group.checkPull=toFn(originalGroup.pull,true);group.checkPut=toFn(originalGroup.put);options.group=group;}_prepareDragStart拖动前的准备工作

       当鼠标按下触发pointerdown事件时,会保存拖动元素的信息,提供后续使用,并且注册dragstart事件:

letoldIndex,newIndex;letdragEl=null;//拖拽元素letrootEl=null;//容器元素letparentEl=null;//拖拽元素的父节点letnextEl=null;//拖拽元素下一个元素letactiveGroup=null;//options.groupSortable.prototype={ _prepareDragStart(evt){ lettarget=evt.target,el=this.el,options=this.options;oldIndex=index(target);rootEl=el;dragEl=target;parentEl=dragEl.parentNode;nextEl=dragEl.nextSibling;activeGroup=options.group;dragEl.draggable=true;//设置元素拖拽属性on(dragEl,'dragend',this);on(rootEl,'dragstart',this._onDragStart);on(document,'mouseup',this._onDrop);},}

       on就是addEventListener,index方法用于获取元素在父容器内的索引:

functionon(el,event,fn){ el.addEventListener(event,fn);}functionoff(el,event,fn){ el.removeEventListener(event,fn);}functionindex(el){ if(!el||!el.parentNode)return-1;letindex=0;//返回元素节点之前的兄弟元素节点(不包括文本节点、注释节点)while(el=el.previousElementSibling){ if(el!==Sortable.clone)index++;}returnindex;}

       _onDragStart用于处理dragstart事件逻辑,_onDrop用于处理拖拽结束逻辑,比如这里执行了dragEl.draggable=true;,那么在mouseup鼠标松开后需将draggable=false。

       这里有趣的一点是dragend事件,它的处理函数绑定的是this即Sortable实例本身,我们都知道实例对象是一个对象,怎么能作为函数使用呢?

       其实addEventListener第二参数可以是函数,也可以是对象,当为对象时,需要提有一个handleEvent方法来处理事件:

Sortable.prototype={ handleEvent:function(evt){ switch(evt.type){ case'dragend':this._onDrop(evt);break;case'dragover':evt.stopPropagation();evt.preventDefault();break;case'dragenter':if(dragEl){ this._onDragOver(evt);}break;}},}

       到这里,整个拖拽流程功能函数都暴露在了眼前:

       _onDragStart处理dragstart拖拽开始工作;

       _onDragOver处理拖拽移动到别的元素时工作;

       _onDrop处理鼠标拖动结束的收尾工作。

dragstart

       这里做了两件事情:

       clone一个dragEl元素副本,用于两个容器项目移动时使用;

       触发外部传入的clone和dragstart事件;

letcloneEl=null,cloneHidden=null;//clone元素_onDragStart(evt){ letdataTransfer=evt.dataTransfer;letoptions=this.options;cloneEl=clone(dragEl);cloneEl.removeAttribute("id");cloneEl.draggable=false;//设置拖拽数据if(dataTransfer){ dataTransfer.effectAllowed='move';options.setData&&options.setData.call(this,dataTransfer,dragEl);}Sortable.active=this;Sortable.clone=cloneEl;_dispatchEvent({ sortable:this,name:'clone'});_dispatchEvent({ sortable:this,name:'start',originalEvent:evt});},functionclone(el){ returnel.cloneNode(true);}

       _dispatchEvent会通过newwindow.CustomEvent构造一个事件对象,将拖拽元素的信息添加到自定义事件对象上,传递给外部的注册事件函数,大体代码如下:

functiondispatchEvent(...params){ //sortable没有传,就根据rootEl获取sortable。sortable=(sortable||(rootEl&&rootEl[expando]));if(!sortable)return;letevt,options=sortable.options,onName='on'+name.charAt(0).toUpperCase()+name.substr(1);//自定义事件,拿到事件对象,满足外部用户传入的事件正常使用if(window.CustomEvent){ evt=newCustomEvent(name,{ bubbles:true,cancelable:true});}else{ evt=document.createEvent('Event');evt.initEvent(name,true,true);}evt.to=toEl||rootEl;evt.from=fromEl||rootEl;evt.item=targetEl||rootEl;evt.clone=cloneEl;evt.oldIndex=oldIndex;evt.newIndex=newIndex;//执行外部传入的事件if(options[onName]){ options[onName].call(sortable,evt);}}

       可见,拖拽的核心逻辑不在dragstart中,下面我们去看dragenter的处理函数_onDragOver。

dragenter

       SortableJS的核心逻辑在_onDragOver中,拿容器内项目排序为例:当拖动dragEl元素,移动到另一个元素上时,会发生两者的位置交换,可见,Sort的逻辑在这里。

       首先,在实例化对象时绑定了dragover和dragenter事件,并且通过handleEvent将事件逻辑交由_onDragOver来处理:

on(el,'dragover',this);on(el,'dragenter',this);handleEvent:function(evt){ switch(evt.type){ case'dragover':evt.stopPropagation();evt.preventDefault();break;case'dragenter':if(dragEl){ this._onDragOver(evt);}break;}},

       在_onDragOver中,需要注意一点是:假如有两个容器,那就有两个newSortable实例对象,isOwner将为false,这是就需要判断拖动容器的activeGroup.pull(是否允许被移动)和group.put(是否允许添加拖动过来的元素)。

newSortable(leftContainer,{ group:{ name:'group',pull:'clone',put:true},});newSortable(rightContainer,{ group:'group',});0

       上面的核心在于下面这一行代码:

newSortable(leftContainer,{ group:{ name:'group',pull:'clone',put:true},});newSortable(rightContainer,{ group:'group',});1

       如果拖拽元素的位置小于目标元素的位置,说明是从上往下拖动,那么将dragEl移动到target.nextSibling之前;

       如果拖拽元素的位置大于目标元素的位置,说明是从下往上拖动,那么只需将dragEl移动到target之前即可;

       整个移动过程均采用DOM操作insertBefore来实现。

       另外如果是两个容器的场景(isOwner=false),并且拖动元素的容器activeGroup.pull=clone,需要将dragstart创建的clone元素渲染到容器中:

newSortable(leftContainer,{ group:{ name:'group',pull:'clone',put:true},});newSortable(rightContainer,{ group:'group',});2drop

       drop主要做一些收尾工作,如将dragEl.draggable=false,移除绑定的mouseup、dragstart、dragend事件,触发用户传入的sort、end事件等。

       不过注意,虽然起名叫drop,触发的事件确是dragend。

newSortable(leftContainer,{ group:{ name:'group',pull:'clone',put:true},});newSortable(rightContainer,{ group:'group',});3动画

       如果想在拖动排序中有一定的animation动画效果,可以配置动画属性,属性值是动画持续时长:

newSortable(leftContainer,{ group:{ name:'group',pull:'clone',put:true},});newSortable(rightContainer,{ group:'group',});4

       动画的时机也是在dragenter中,大致的思路如下:

       1、记录:记录容器子项位置信息

       在操作DOM移动dragEl之前,记录容器内所有子项的位置;

       进行DOM操作进行位置交换,DOM操作本身没有动画;

       这时再去记录一次移动后的容器内所有子项的位置;

       2、执行:有了上面几步的操作,接下来就可以根据移动前后的位置进行动画操作

       通过translate先让元素立刻回到移动前的位置;

       此时给元素自身设置过度效果transform;

       这时候就可以通过translate让元素回到移动之后的位置。

       大致实现如下:

newSortable(leftContainer,{ group:{ name:'group',pull:'clone',put:true},});newSortable(rightContainer,{ group:'group',});5最后

       本文以探索SortableJS拖拽思路为主线,去了解业界开源拖拽库的设计与思路。感谢阅读。

原文:/post/

可拖拽、缩放、旋转组件之 - 多元素组合与拆分功能

       介绍

       基于 vue3.x + CompositionAPI + typescript + vite 的可拖拽、缩放、旋转的组件

       在线示例

       源码地址

       这节主要来分享如何使用es-drager,根据现有功能实现多个元素组合与拆分功能

       es-drager的更新

       es-drager 的1.x版本支持移动端啦

       另外最近还在使用es-drager开发一个低代码编辑器(还未成型),也算是一个es-drager的综合使用案例吧,老铁们可以先到 编辑器案例 中查看

       本章内容使用svg绘制网格

       在开始讲组合之前,先来介绍一下如何使用svg画一个指定大小的网格。前面的demo都是使用css的方式,感觉还是不太灵活,有一定的局限性

       这里直接抽离成了一个 vue 组件

       可以看到,如果不加属性的话,整个网格组件还是挺简单的

       使用时直接包裹在画布元素里即可,当然我们也可以传入指定网格的大小

       元素组合与拆分选中区域

       组合前,我们需要选中需要组合的元素,类似下图这样的效果

       注意:由于这个onMouseDown是画布触发时调用的, 因此e.target 获取的是画布元素

       有了这个组件,该如何使用呢?

       先上使用代码,后面有详细解释

       步骤解析

       移动选中的元素

       移动多个区域选中的元素,类似下面的效果

       要计算每个元素的移动距离,就需要es-drager提供的一些事件了

       上面多了 areaSelected 记录是否是区域选择状态,那么在什么情况它的值才是true呢?

       这时我们就要监听 Area 组件的 up 事件了

       只有区域选中了元素,areaSelected才能是true,然后点击其它区域是设置为false

       组合与拆分

       完成上面的工作后,我们来看看如何将多个元素组合成一个,为了方便渲染我们先封装一个Group组件

       Group 组件

       这个组件的功能就是循环显示所有组合的元素

       下面分别解释这两个函数

       最后

       本节只是对多个元素的组合与拆分的简单实现,对于组合后的旋转与缩放我想在后面的文章中介绍。

       最后来看看在drawio中元素组合与拆分的效果

       drawio在实现组合后缩放会有一点小问题,大家看下图

       当然我们的目标是尽可能实现理想的组合后的缩放与旋转

搜索关键词:暗夜者源码搭配