Commit d039dcbafbf0e160b175f7fea52a27ae7bb4cf44

Authored by shihongzhi
Committed by GitHub
1 parent a9a71231

feat: 添加快捷键+更完整的辅助线提示 (#148) add shortcuts for editor and more guidelines

* fix scrollbar show when h-line&v-line visible

* remove unused text code

* add hotkey to menu tools

* 除了left和top,其他四条线(midx,right,midy,bottom)也提示辅助线

* change width and height when point move situation

* rename argument and optimize some experience

* Fix typo

thanks @shihongzhi
front-end/h5/package.json
@@ -23,6 +23,7 @@ @@ -23,6 +23,7 @@
23 "core-js": "^2.6.5", 23 "core-js": "^2.6.5",
24 "element-ui": "^2.13.0", 24 "element-ui": "^2.13.0",
25 "font-awesome": "4.7.0", 25 "font-awesome": "4.7.0",
  26 + "hotkeys-js": "^3.7.6",
26 "html2canvas": "^1.0.0-rc.3", 27 "html2canvas": "^1.0.0-rc.3",
27 "proxy-agent": "^3.1.0", 28 "proxy-agent": "^3.1.0",
28 "qrcode": "^1.4.1", 29 "qrcode": "^1.4.1",
front-end/h5/src/components/core/editor/canvas/edit.js
@@ -23,73 +23,97 @@ export default { @@ -23,73 +23,97 @@ export default {
23 // TODO #!zh: 优化代码 23 // TODO #!zh: 优化代码
24 // generate vertical line 24 // generate vertical line
25 drawVLine (newLeft) { 25 drawVLine (newLeft) {
26 - // this.editingElement.commonStyle.left = newLeft  
27 - this.setElementPosition({ left: newLeft })  
28 this.vLines = [{ left: newLeft }] 26 this.vLines = [{ left: newLeft }]
29 }, 27 },
  28 + clearVLine () {
  29 + this.vLines = []
  30 + },
30 // generate horizontal line 31 // generate horizontal line
31 drawHLine (newTop) { 32 drawHLine (newTop) {
32 - // this.editingElement.commonStyle.top = newTop  
33 - this.setElementPosition({ top: newTop })  
34 this.hLines = [{ top: newTop }] 33 this.hLines = [{ top: newTop }]
35 }, 34 },
36 - calcX (newLeft) { 35 + clearHLine () {
  36 + this.hLines = []
  37 + },
  38 + calcVHLine (isPointMove) {
37 const uuid = this.editingElement.uuid 39 const uuid = this.editingElement.uuid
38 - let xCoords = []  
39 - this.elements.filter(e => e.uuid !== uuid).forEach(e => { 40 + const referElements = this.elements.filter(e => e.uuid !== uuid)
  41 + let referElementsXCoords = []
  42 + let referElementsYCoords = []
  43 + referElements.forEach(e => {
40 const width = e.commonStyle.width 44 const width = e.commonStyle.width
41 const left = e.commonStyle.left 45 const left = e.commonStyle.left
42 - xCoords = [  
43 - ...xCoords, 46 + const height = e.commonStyle.height
  47 + const top = e.commonStyle.top
  48 +
  49 + referElementsXCoords = [
  50 + ...referElementsXCoords,
44 left, 51 left,
45 left + (width / 2), 52 left + (width / 2),
46 left + width 53 left + width
47 ] 54 ]
48 - })  
49 -  
50 - xCoords.some(x => {  
51 - if (Math.abs(newLeft - x) <= 5) {  
52 - this.drawVLine(x)  
53 - return true  
54 - } else {  
55 - this.vLines = []  
56 - }  
57 - })  
58 - },  
59 - calcY (newTop) {  
60 - const uuid = this.editingElement.uuid  
61 - let yCoords = []  
62 - this.elements.filter(e => e.uuid !== uuid).forEach(e => {  
63 - const height = e.commonStyle.height  
64 - const top = e.commonStyle.top  
65 - yCoords = [  
66 - ...yCoords, 55 + referElementsYCoords = [
  56 + ...referElementsYCoords,
67 top, 57 top,
68 top + (height / 2), 58 top + (height / 2),
69 top + height 59 top + height
70 ] 60 ]
71 }) 61 })
72 62
73 - yCoords.some(y => {  
74 - if (Math.abs(newTop - y) <= 5) {  
75 - this.drawHLine(y)  
76 - return true  
77 - } else {  
78 - this.hLines = []  
79 - } 63 + // e代表 editingElement
  64 + const eleft = this.editingElement.commonStyle.left
  65 + const etop = this.editingElement.commonStyle.top
  66 + const ewidth = this.editingElement.commonStyle.width
  67 + const eheight = this.editingElement.commonStyle.height
  68 + const exCoords = [eleft + ewidth, eleft + (ewidth / 2), eleft]
  69 + const eyCoords = [etop + eheight, etop + (eheight / 2), etop]
  70 + let hasVLine = false
  71 + let hasHLine = false
  72 + exCoords.forEach(eX => {
  73 + referElementsXCoords.forEach(referX => {
  74 + let offset = referX - eX
  75 + if (Math.abs(offset) <= 5) {
  76 + if (isPointMove) {
  77 + this.setElementPosition({ width: ewidth + offset })
  78 + } else {
  79 + this.setElementPosition({ left: eleft + offset })
  80 + }
  81 + this.drawVLine(referX)
  82 + hasVLine = true
  83 + }
  84 + })
  85 + })
  86 + eyCoords.forEach(eY => {
  87 + referElementsYCoords.forEach(referY => {
  88 + let offset = referY - eY
  89 + if (Math.abs(offset) <= 5) {
  90 + if (isPointMove) {
  91 + this.setElementPosition({ height: eheight + offset })
  92 + } else {
  93 + this.setElementPosition({ top: etop + offset })
  94 + }
  95 + this.drawHLine(referY)
  96 + hasHLine = true
  97 + }
  98 + })
80 }) 99 })
  100 + if (!hasVLine) {
  101 + this.clearVLine()
  102 + }
  103 + if (!hasHLine) {
  104 + this.clearHLine()
  105 + }
81 }, 106 },
82 /** 107 /**
83 * #!zh: 在元素移动过程中,计算和生成辅助线 108 * #!zh: 在元素移动过程中,计算和生成辅助线
84 */ 109 */
85 handleElementMove (pos) { 110 handleElementMove (pos) {
86 this.setElementPosition(pos) 111 this.setElementPosition(pos)
87 - this.calcX(pos.left)  
88 - this.calcY(pos.top) 112 + this.calcVHLine(false)
89 }, 113 },
90 - handlePointMove ({ top, left }) {  
91 - this.calcX(left)  
92 - this.calcY(top) 114 + handlePointMove (pos) {
  115 + this.setElementPosition(pos)
  116 + this.calcVHLine(true)
93 }, 117 },
94 bindContextMenu (e) { 118 bindContextMenu (e) {
95 // 优化右击菜单的显示,去除冗余的无效逻辑 119 // 优化右击菜单的显示,去除冗余的无效逻辑
@@ -185,14 +209,16 @@ export default { @@ -185,14 +209,16 @@ export default {
185 this.setEditingElement(element) 209 this.setEditingElement(element)
186 }} 210 }}
187 // TODO 矩形四周的点叫什么?暂时叫 Point 吧 211 // TODO 矩形四周的点叫什么?暂时叫 Point 吧
188 - handlePointMoveProp={pos => {  
189 - this.setElementPosition(pos)  
190 - }} 212 + handlePointMoveProp={this.handlePointMove}
191 handleElementMoveProp={this.handleElementMove} 213 handleElementMoveProp={this.handleElementMove}
192 handleElementMouseUpProp={() => { 214 handleElementMouseUpProp={() => {
  215 + this.clearHLine()
  216 + this.clearVLine()
193 this.recordElementRect() 217 this.recordElementRect()
194 }} 218 }}
195 handlePointMouseUpProp={() => { 219 handlePointMouseUpProp={() => {
  220 + this.clearHLine()
  221 + this.clearVLine()
196 this.recordElementRect() 222 this.recordElementRect()
197 }} 223 }}
198 nativeOnContextmenu={e => { 224 nativeOnContextmenu={e => {
front-end/h5/src/components/core/editor/index.js
1 import { mapState, mapActions } from 'vuex' 1 import { mapState, mapActions } from 'vuex'
  2 +import hotkeys from 'hotkeys-js'
2 import undoRedoHistory from '../../../store/plugins/undo-redo/History' 3 import undoRedoHistory from '../../../store/plugins/undo-redo/History'
3 4
4 import '../styles/index.scss' 5 import '../styles/index.scss'
@@ -44,58 +45,47 @@ import Feedback from &#39;@/components/common/feedback/index&#39; @@ -44,58 +45,47 @@ import Feedback from &#39;@/components/common/feedback/index&#39;
44 const fixedTools = [ 45 const fixedTools = [
45 { 46 {
46 i18nTooltip: 'editor.fixedTool.undo', 47 i18nTooltip: 'editor.fixedTool.undo',
47 - 'tooltip': '撤消', // TODO 支持快捷键  
48 - 'text': '撤消',  
49 'icon': 'mail-reply', 48 'icon': 'mail-reply',
50 - 'action': () => undoRedoHistory.undo() 49 + 'action': () => undoRedoHistory.undo(),
  50 + 'hotkey': 'ctrl&z,⌘&z'
51 }, 51 },
52 { 52 {
53 i18nTooltip: 'editor.fixedTool.redo', 53 i18nTooltip: 'editor.fixedTool.redo',
54 - 'tooltip': '恢复',  
55 - 'text': '恢复',  
56 'icon': 'mail-forward', 54 'icon': 'mail-forward',
57 - 'action': () => undoRedoHistory.redo() 55 + 'action': () => undoRedoHistory.redo(),
  56 + 'hotkey': 'ctrl&y,⌘&u'
58 }, 57 },
59 { 58 {
60 i18nTooltip: 'editor.fixedTool.preview', 59 i18nTooltip: 'editor.fixedTool.preview',
61 - 'tooltip': '刷新预览',  
62 - 'text': '刷新预览',  
63 'icon': 'eye', 60 'icon': 'eye',
64 'action': function () { this.previewVisible = true } 61 'action': function () { this.previewVisible = true }
65 }, 62 },
66 { 63 {
67 i18nTooltip: 'editor.fixedTool.copyCurrentPage', 64 i18nTooltip: 'editor.fixedTool.copyCurrentPage',
68 - 'tooltip': '复制当前页',  
69 - 'text': '复制当前页',  
70 'icon': 'copy', 65 'icon': 'copy',
71 - 'action': function () { this.pageManager({ type: 'copy' }) } 66 + 'action': function () { this.pageManager({ type: 'copy' }) },
  67 + 'hotkey': 'ctrl&c,⌘&c'
72 }, 68 },
73 { 69 {
74 i18nTooltip: 'editor.fixedTool.importPSD', 70 i18nTooltip: 'editor.fixedTool.importPSD',
75 - 'tooltip': '导入PSD',  
76 - 'text': 'Ps',  
77 'icon': '', 71 'icon': '',
78 'action': '', 72 'action': '',
79 'disabled': true 73 'disabled': true
80 }, 74 },
81 { 75 {
82 i18nTooltip: 'editor.fixedTool.zoomOut', 76 i18nTooltip: 'editor.fixedTool.zoomOut',
83 - 'tooltip': '放大画布',  
84 - 'text': '放大画布',  
85 'icon': 'plus', 77 'icon': 'plus',
86 - 'action': function () { this.scaleRate += 0.25 } 78 + 'action': function () { this.scaleRate += 0.25 },
  79 + 'hotkey': 'ctrl&=,⌘&='
87 }, 80 },
88 { 81 {
89 i18nTooltip: 'editor.fixedTool.zoomIn', 82 i18nTooltip: 'editor.fixedTool.zoomIn',
90 - 'tooltip': '缩小画布',  
91 - 'text': '缩小画布',  
92 'icon': 'minus', 83 'icon': 'minus',
93 - 'action': function () { this.scaleRate -= 0.25 } 84 + 'action': function () { this.scaleRate -= 0.25 },
  85 + 'hotkey': 'ctrl&-,⌘&-'
94 }, 86 },
95 { 87 {
96 i18nTooltip: 'editor.fixedTool.issues', 88 i18nTooltip: 'editor.fixedTool.issues',
97 - 'tooltip': 'issues',  
98 - 'text': '常见问题',  
99 'icon': 'question', 89 'icon': 'question',
100 'action': function () { window.open('https://github.com/ly525/luban-h5/issues/110') } 90 'action': function () { window.open('https://github.com/ly525/luban-h5/issues/110') }
101 } 91 }
@@ -237,6 +227,15 @@ export default { @@ -237,6 +227,15 @@ export default {
237 // } 227 // }
238 } 228 }
239 }, 229 },
  230 + mounted () {
  231 + fixedTools.map(tool => {
  232 + tool.hotkey && hotkeys(tool.hotkey, { splitKey: '&' }, (event, handler) => {
  233 + event.preventDefault()
  234 + event.stopPropagation()
  235 + tool.action && tool.action.call(this)
  236 + })
  237 + })
  238 + },
240 render (h) { 239 render (h) {
241 return ( 240 return (
242 <a-layout id="luban-editor-layout" style={{ height: '100vh' }}> 241 <a-layout id="luban-editor-layout" style={{ height: '100vh' }}>
@@ -340,10 +339,9 @@ export default { @@ -340,10 +339,9 @@ export default {
340 <a-button-group style={{ display: 'flex', flexDirection: 'column' }}> 339 <a-button-group style={{ display: 'flex', flexDirection: 'column' }}>
341 { 340 {
342 fixedTools.map(tool => ( 341 fixedTools.map(tool => (
343 - // <a-tooltip effect="dark" placement="left" title={tool.tooltip}>  
344 <a-tooltip effect="dark" placement="left" title={this.$t(tool.i18nTooltip)}> 342 <a-tooltip effect="dark" placement="left" title={this.$t(tool.i18nTooltip)}>
345 <a-button block class="transparent-bg" type="link" size="small" style={{ height: '40px', color: '#000' }} onClick={() => tool.action && tool.action.call(this) } disabled={!!tool.disabled}> 343 <a-button block class="transparent-bg" type="link" size="small" style={{ height: '40px', color: '#000' }} onClick={() => tool.action && tool.action.call(this) } disabled={!!tool.disabled}>
346 - { tool.icon ? <i class={['shortcut-icon', 'fa', `fa-${tool.icon}`]} aria-hidden='true'/> : tool.text } 344 + { tool.icon ? <i class={['shortcut-icon', 'fa', `fa-${tool.icon}`]} aria-hidden='true'/> : this.$t(tool.i18nTooltip) }
347 </a-button> 345 </a-button>
348 { tool.icon === 'minus' && <div style={{ fontSize: '12px', textAlign: 'center' }}>{this.scaleRate * 100}%</div> } 346 { tool.icon === 'minus' && <div style={{ fontSize: '12px', textAlign: 'center' }}>{this.scaleRate * 100}%</div> }
349 </a-tooltip> 347 </a-tooltip>
front-end/h5/yarn.lock
@@ -5071,6 +5071,11 @@ hosted-git-info@^2.1.4: @@ -5071,6 +5071,11 @@ hosted-git-info@^2.1.4:
5071 resolved "https://registry.npm.taobao.org/hosted-git-info/download/hosted-git-info-2.8.5.tgz#759cfcf2c4d156ade59b0b2dfabddc42a6b9c70c" 5071 resolved "https://registry.npm.taobao.org/hosted-git-info/download/hosted-git-info-2.8.5.tgz#759cfcf2c4d156ade59b0b2dfabddc42a6b9c70c"
5072 integrity sha1-dZz88sTRVq3lmwst+r3cQqa5xww= 5072 integrity sha1-dZz88sTRVq3lmwst+r3cQqa5xww=
5073 5073
  5074 +hotkeys-js@^3.7.6:
  5075 + version "3.7.6"
  5076 + resolved "https://registry.npm.taobao.org/hotkeys-js/download/hotkeys-js-3.7.6.tgz#b90ae3453a7be2f2b2bed6ee55cb96443944c77b"
  5077 + integrity sha1-uQrjRTp74vKyvtbuVcuWRDlEx3s=
  5078 +
5074 hpack.js@^2.1.6: 5079 hpack.js@^2.1.6:
5075 version "2.1.6" 5080 version "2.1.6"
5076 resolved "https://registry.npm.taobao.org/hpack.js/download/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" 5081 resolved "https://registry.npm.taobao.org/hpack.js/download/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2"