Commit 67b129aef0b73dcb1a988466dbdf72e23c1f62e7
Committed by
小小鲁班
1 parent
f002c516
feat: support drag&drop element from the left panel to the canvas; !#: 支持从左侧元素列表中拖拽元素至中间画布
Showing
4 changed files
with
173 additions
and
61 deletions
front-end/h5/src/components/core/editor/drag-mixin.js
0 → 100644
| 1 | +/* | |
| 2 | + * @Author: ly525 | |
| 3 | + * @Date: 2020-05-17 17:21:04 | |
| 4 | + * @LastEditors: ly525 | |
| 5 | + * @LastEditTime: 2020-05-24 18:09:23 | |
| 6 | + * @FilePath: /luban-h5/front-end/h5/src/components/core/editor/drag-mixin.js | |
| 7 | + * @Github: https://github.com/ly525/luban-h5 | |
| 8 | + * @Copyright 2018 - 2019 luban-h5. All Rights Reserved | |
| 9 | + * @Description: | |
| 10 | + * 组件拖拽至画布功能 | |
| 11 | + * 其中部分代码参考自:https://github.com/hakubox/haku-form-design,已经征得作者同意,目的是后续考虑做 tab 之类的嵌套容器 | |
| 12 | + */ | |
| 13 | + | |
| 14 | +let dragDom = null | |
| 15 | + | |
| 16 | +let dragConfig = { | |
| 17 | + isPreDrag: false, // 准备拖拽 | |
| 18 | + isDrag: false, // 正式拖拽 | |
| 19 | + origin: { | |
| 20 | + clientY: 0, // 鼠标按下时候时候值 | |
| 21 | + clientX: 0, | |
| 22 | + layerX: 0, // 鼠标.x 相对于元素左上角.left 的偏移 | |
| 23 | + layerY: 0 // 鼠标.y 相对于元素左上角.top 的偏移 | |
| 24 | + } | |
| 25 | +} | |
| 26 | + | |
| 27 | +class Drag { | |
| 28 | + constructor (options) { | |
| 29 | + this.mousedown = options.mousedown | |
| 30 | + this.mousemove = options.mousemove | |
| 31 | + this.mouseup = options.mouseup | |
| 32 | + | |
| 33 | + this._mousedown = this._mousedown.bind(this) | |
| 34 | + this._mousemove = this._mousemove.bind(this) | |
| 35 | + this._mouseup = this._mouseup.bind(this) | |
| 36 | + } | |
| 37 | + | |
| 38 | + start (e) { | |
| 39 | + this._mousedown(e) | |
| 40 | + } | |
| 41 | + | |
| 42 | + _mousedown (e) { | |
| 43 | + this.mousedown(e) | |
| 44 | + this.toggleListener('add') | |
| 45 | + } | |
| 46 | + | |
| 47 | + _mousemove (e) { | |
| 48 | + console.log('mousemove') | |
| 49 | + this.mousemove(e) | |
| 50 | + } | |
| 51 | + | |
| 52 | + _mouseup (e) { | |
| 53 | + console.log('mouseup') | |
| 54 | + this.mouseup(e) | |
| 55 | + this.toggleListener('remove') | |
| 56 | + } | |
| 57 | + | |
| 58 | + toggleListener (action) { | |
| 59 | + document[`${action}EventListener`]('mousemove', this._mousemove) | |
| 60 | + document[`${action}EventListener`]('mouseup', this._mouseup) | |
| 61 | + } | |
| 62 | +} | |
| 63 | + | |
| 64 | +export default { | |
| 65 | + data () { | |
| 66 | + return { | |
| 67 | + | |
| 68 | + } | |
| 69 | + }, | |
| 70 | + methods: { | |
| 71 | + /** | |
| 72 | + * | |
| 73 | + * @param {*} element | |
| 74 | + * @param {*} e | |
| 75 | + */ | |
| 76 | + handleDragStartFromMixin (element, e) { | |
| 77 | + // https://developer.mozilla.org/zh-CN/docs/Web/API/event.button | |
| 78 | + // 0 为 左键点击. | |
| 79 | + if (e.button !== 0) return | |
| 80 | + if (dragDom) { | |
| 81 | + document.body.removeChild(dragDom) | |
| 82 | + dragDom = null | |
| 83 | + } | |
| 84 | + this.dragElement = element | |
| 85 | + dragDom = e.target.cloneNode(true) | |
| 86 | + document.body.appendChild(dragDom) | |
| 87 | + | |
| 88 | + new Drag({ | |
| 89 | + mousedown: this.mousedown, | |
| 90 | + mousemove: this.mousemove, | |
| 91 | + mouseup: this.mouseup | |
| 92 | + }).start(e) | |
| 93 | + }, | |
| 94 | + /** | |
| 95 | + * | |
| 96 | + * @param {*} e | |
| 97 | + */ | |
| 98 | + mousedown (e) { | |
| 99 | + // 鼠标.x 相对于元素左上角 的偏移 | |
| 100 | + const { layerX, layerY } = e | |
| 101 | + dragConfig.origin.layerX = layerX | |
| 102 | + dragConfig.origin.layerY = layerY | |
| 103 | + dragConfig.origin.clientX = e.clientX | |
| 104 | + dragConfig.origin.clientY = e.clientY | |
| 105 | + | |
| 106 | + dragDom.style.position = 'absolute' | |
| 107 | + dragDom.style.left = e.clientX - layerX + 'px' | |
| 108 | + dragDom.style.top = e.clientY - layerY + 'px' | |
| 109 | + dragDom.classList.add('dragging-dom-ele', 'hidden') | |
| 110 | + | |
| 111 | + dragConfig.isPreDrag = true | |
| 112 | + }, | |
| 113 | + /** 组件拖拽中 */ | |
| 114 | + mousemove (e) { | |
| 115 | + dragDom.classList.remove('hidden') | |
| 116 | + const { layerX, layerY } = dragConfig.origin | |
| 117 | + dragDom.style.left = e.clientX - layerX + 'px' | |
| 118 | + dragDom.style.top = e.clientY - layerY + 'px' | |
| 119 | + }, | |
| 120 | + mouseup (e) { | |
| 121 | + const { layerX, layerY } = dragConfig.origin | |
| 122 | + document.body.removeChild(dragDom) | |
| 123 | + dragDom = null | |
| 124 | + | |
| 125 | + const canvasWrapper = document.querySelector('.canvas-wrapper') | |
| 126 | + const position = canvasWrapper.getBoundingClientRect() | |
| 127 | + this.dragElement && this.clone({ | |
| 128 | + ...this.dragElement, | |
| 129 | + customStyle: { | |
| 130 | + left: e.clientX - layerX - position.left, | |
| 131 | + top: e.clientY - layerY - position.top | |
| 132 | + } | |
| 133 | + }) | |
| 134 | + } | |
| 135 | + }, | |
| 136 | + updated () { | |
| 137 | + console.log('updated') | |
| 138 | + } | |
| 139 | +} | ... | ... |
front-end/h5/src/components/core/editor/index.js
| ... | ... | @@ -22,26 +22,7 @@ import LangSelect from '@/components/common/header/LangSelect.vue' |
| 22 | 22 | import Feedback from '@/components/common/feedback/index' |
| 23 | 23 | import AdjustLineV from '@/components/core/support/adjust-line/vertical' |
| 24 | 24 | |
| 25 | -// const sidebarMenus = [ | |
| 26 | -// { | |
| 27 | -// i18nLabel: 'editor.sidebar.components', | |
| 28 | -// label: '组件列表', | |
| 29 | -// value: 'pluginList', | |
| 30 | -// antIcon: 'bars' | |
| 31 | -// }, | |
| 32 | -// { | |
| 33 | -// i18nLabel: 'editor.sidebar.pages', | |
| 34 | -// label: '页面管理', | |
| 35 | -// value: 'pageManagement', | |
| 36 | -// antIcon: 'snippets' | |
| 37 | -// }, | |
| 38 | -// { | |
| 39 | -// i18nLabel: 'editor.sidebar.templates', | |
| 40 | -// label: '免费模板', | |
| 41 | -// value: 'freeTemplate', | |
| 42 | -// antIcon: 'appstore' | |
| 43 | -// } | |
| 44 | -// ] | |
| 25 | +import DragMixin from './drag-mixin' | |
| 45 | 26 | |
| 46 | 27 | const fixedTools = [ |
| 47 | 28 | { |
| ... | ... | @@ -103,6 +84,7 @@ const fixedTools = [ |
| 103 | 84 | ] |
| 104 | 85 | |
| 105 | 86 | export default { |
| 87 | + mixins: [DragMixin], | |
| 106 | 88 | name: 'Editor', |
| 107 | 89 | components: { |
| 108 | 90 | LogoOfHeader, |
| ... | ... | @@ -184,7 +166,11 @@ export default { |
| 184 | 166 | <strong>{ this.$t('editor.tip.click') }</strong>{ this.$t('editor.tip.click') } |
| 185 | 167 | </i18n> |
| 186 | 168 | </div> |
| 187 | - <RenderShortcutsPanel pluginsList={this.pluginsList} handleClickShortcut={this.clone} /> | |
| 169 | + <RenderShortcutsPanel | |
| 170 | + pluginsList={this.pluginsList} | |
| 171 | + handleClickShortcut={this.clone} | |
| 172 | + handleDragStart={this.handleDragStartFromMixin} | |
| 173 | + /> | |
| 188 | 174 | </a-tab-pane> |
| 189 | 175 | <a-tab-pane key='page-manager' tab={this.$t('editor.sidebar.pages')}> |
| 190 | 176 | <RenderPageManager |
| ... | ... | @@ -202,46 +188,6 @@ export default { |
| 202 | 188 | </a-tab-pane> |
| 203 | 189 | </a-tabs> |
| 204 | 190 | ) |
| 205 | - // switch (this.activeMenuKey) { | |
| 206 | - // case sidebarMenus[0].value: | |
| 207 | - // return ( | |
| 208 | - // <a-tabs | |
| 209 | - // style="height: 100%;" | |
| 210 | - // tabBarGutter={10} | |
| 211 | - // > | |
| 212 | - // <a-tab-pane key="plugin-list" tab={this.$t('editor.sidebar.components')}> | |
| 213 | - // <RenderShortcutsPanel pluginsList={this.pluginsList} handleClickShortcut={this.clone} /> | |
| 214 | - // </a-tab-pane> | |
| 215 | - // <a-tab-pane key='page-manager' tab={this.$t('editor.sidebar.pages')}> | |
| 216 | - // <RenderPageManager | |
| 217 | - // pages={this.pages} | |
| 218 | - // editingPage={this.editingPage} | |
| 219 | - // onSelectMenuItem={(menuKey) => { | |
| 220 | - // this.pageManager({ type: menuKey }) | |
| 221 | - // }} | |
| 222 | - // onEditTitle={({ pageIndexForEditingTitle, newTitle }) => { | |
| 223 | - // this.pageManager({ type: 'editTitle', value: { pageIndexForEditingTitle, newTitle } }) | |
| 224 | - // this.saveWork({ isSaveCover: false }) | |
| 225 | - // }} | |
| 226 | - // onSelectPage={(pageIndex) => { this.setEditingPage(pageIndex) }} | |
| 227 | - // /> | |
| 228 | - // </a-tab-pane> | |
| 229 | - // </a-tabs> | |
| 230 | - // ) | |
| 231 | - // case sidebarMenus[1].value: | |
| 232 | - // return ( | |
| 233 | - // <RenderPageManager | |
| 234 | - // pages={this.pages} | |
| 235 | - // editingPage={this.editingPage} | |
| 236 | - // onSelectMenuItem={(menuKey) => { | |
| 237 | - // this.pageManager({ type: menuKey }) | |
| 238 | - // }} | |
| 239 | - // onSelectPage={(pageIndex) => { this.setEditingPage(pageIndex) }} | |
| 240 | - // /> | |
| 241 | - // ) | |
| 242 | - // default: | |
| 243 | - // return null | |
| 244 | - // } | |
| 245 | 191 | } |
| 246 | 192 | }, |
| 247 | 193 | mounted () { | ... | ... |
front-end/h5/src/components/core/editor/shortcuts-panel/index.js
| ... | ... | @@ -11,6 +11,10 @@ export default { |
| 11 | 11 | }, |
| 12 | 12 | handleClickShortcut: { |
| 13 | 13 | type: Function |
| 14 | + }, | |
| 15 | + handleDragStart: { | |
| 16 | + type: Function, | |
| 17 | + default: (e) => {} | |
| 14 | 18 | } |
| 15 | 19 | }, |
| 16 | 20 | data: () => ({ |
| ... | ... | @@ -97,6 +101,7 @@ export default { |
| 97 | 101 | <a-col span={12} style={{ marginTop: '10px' }}> |
| 98 | 102 | <ShortcutButton |
| 99 | 103 | clickFn={this.onClickShortcut.bind(this, plugin)} |
| 104 | + mousedownFn={this.handleDragStart.bind(this, plugin)} | |
| 100 | 105 | // title={plugin.title} |
| 101 | 106 | title={plugin.i18nTitle[this.currentLang] || plugin.title} |
| 102 | 107 | faIcon={plugin.icon} | ... | ... |
front-end/h5/src/components/core/styles/index.scss
| ... | ... | @@ -52,6 +52,28 @@ |
| 52 | 52 | } |
| 53 | 53 | } |
| 54 | 54 | |
| 55 | +.hidden { | |
| 56 | + display: none !important; | |
| 57 | +} | |
| 58 | + | |
| 59 | +// 拖拽出来的控件 | |
| 60 | +.dragging-dom-ele { | |
| 61 | + position: fixed !important; | |
| 62 | + z-index: 100; | |
| 63 | + | |
| 64 | + display: inline-block; | |
| 65 | + box-sizing: border-box; | |
| 66 | + | |
| 67 | + height: 30px; | |
| 68 | + width: 130px; | |
| 69 | + border-radius: 4px; | |
| 70 | + border: 1px solid #CCC; | |
| 71 | + | |
| 72 | + cursor: grabbing; | |
| 73 | + user-select: none; | |
| 74 | + opacity: 0.6; | |
| 75 | +} | |
| 76 | + | |
| 55 | 77 | .ant-tabs-nav .ant-tabs-tab { |
| 56 | 78 | padding: 12px 0 !important; |
| 57 | 79 | } | ... | ... |