快捷鍵操作的需求
元素選擇 前提都是在元素被選中的情況下
- 拷貝圖層 - ?C / Ctrl+C : 新建當前選擇的元素的一個數(shù)據(jù)結(jié)構(gòu)
- 粘貼圖層 - ?V / Ctrl+V : 將新建的元素添加到 components 數(shù)組中
- 刪除圖層 - Backspace / Delete : 在 components 數(shù)組中刪除選擇的元素
- 取消選中 - ESC : currentElement 設置為空
元素移動
- 上下左右移動一像素 - ↑ ↓ → ← : 更新選中元素 props 的 top/left 值
- 上下左右移動十像素 - Shift + ↑ ↓ → ←:更新選中元素 props 的 top/left 值
撤銷/重做
- 撤銷 - ?Z / Ctrl+Z
- 重做 - ??Z / Ctrl+Shift+Z
好用的按鍵響應庫 - HotKeys.js
之前我們在InlineEdit.vue
組件中,使用到了useKeyPress
幫助了我們對esc和enter進行了綁定:
const useKeyPress = (key: string, cb: () => any) => {
const trigger = (event: KeyboardEvent) => {
if (event.key === key) {
cb()
}
}
onMounted(() => {
document.addEventListener('keydown', trigger)
})
onUnmounted(() => {
document.removeEventListener('keydown', trigger)
})
}
但是現(xiàn)在我們的需求復雜了很多,因為我們有很多的組合鍵,所以我們需要好用的第三方庫來完成對應的工作:
項目地址:https://github.com/jaywcjlove/hotkeys
演示地址:https://wangchujiang.com/hotkeys/
完成對應的編碼
// useHotKey.ts
import hotkeys, { KeyHandler } from 'hotkeys-js'
import { onMounted, onUnmounted } from 'vue'
const useHotKey = (keys: string, callback: KeyHandler) => {
onMounted(() => {
hotkeys(keys, callback)
})
onUnmounted(() => {
hotkeys.unbind(keys, callback)
})
}
export default useHotKey
對于快捷鍵的操作,是獨立于整個應用,為了更好的交互,而單獨添加的,看起來相對獨立的功能,所以可以把整個模塊稱之為系統(tǒng)的插件。
創(chuàng)建plugins文件夾:
// plugins/hotKey.ts
import useHotKey from '../hooks/useHotKey'
export default function initHotKeys() {
useHotKey('ctrl+c, command+c', () => {
alert('ctrl+c, command+c')
})
useHotKey('ctrl+v, command+v', () => {
alert('ctrl+v, command+v')
})
}
// 在Editor.vue中的setup函數(shù)中進行調(diào)用,測試效果
setup(){
initHotKeys()
}
完成ctrl+c快捷鍵的操作:
// plugins/hotKey.ts
import { useStore } from "vuex";
import { computed } from "vue";
import { GlobalDataProps } from "../store";
useHotKey("ctrl+c, command+c", () => {
const store = useStore<GlobalDataProps>();
const currentId = computed(() => store.state.editor.currentElement);
store.commit("copyComponent", currentId.value);
}
// store/editor.ts mutations中的
copyComponent(state, id) {
// 這里是在geeters定義了一個getElement,引入store,就可以在mutations中拿到getters中的屬性了
const currentComponent = store.getters.getElement(id)
if (currentComponent) {
state.copiedComponent = currentComponent;
message.success('已拷貝當前圖層', 1);
}
},
// 在editoreProps中添加copiedComponent
interface EditorProps {
// 供中間編輯器渲染的數(shù)組
components: ComponentData[];
// 當前編輯的是哪個元素,uuid
currentElement: string;
// 當然最后保存的時候還有有一些項目信息,這里并沒有寫出,等做到的時候再補充
page: PageData;
// 當前被復制的組件
copiedComponent?: ComponentData;
}
完成ctrl+v快捷鍵的操作:
// plugins/hotKey.ts
useHotKey('ctrl+v, command+v', () => {
store.commit('pasteCopiedComponent')
})
// store/editors.ts中mutations中的
pasteCopiedComponent(){
if (state.copiedComponent) {
state.components.push(state.copiedComponent)
message.success('已黏貼當前圖層', 1);
}
}
這里如果直接進行push操作,就會發(fā)現(xiàn)復制出來的元素也粘貼出來的元素是一摸一樣的,最終就會導致在移動復制的元素的時候,被復制的元素也會被同樣移動。所以我們要對pasteCopiedComponent
進行稍微的改造,拷貝一下數(shù)據(jù):
// store/editors.ts中mutations中的
pasteCopiedComponent: setDirtyWrapper((state) => {
if (state.copiedComponent) {
// 使用lodash中的cloneDeep進行拷貝,可以喪失響應式
const clone = cloneDeep(state.copiedComponent);
clone.id = uuidv4();
clone.layerName = clone.layerName + '副本';
state.components.push(clone);
message.success('已黏貼當前圖層', 1);
}
}),
// 刪除圖層:
deleteComponent: setDirtyWrapper((state, id) => {
const currentComponent = state.components.find(
(component) => component.id === id
);
if (currentComponent) {
state.components = state.components.filter(
(component) => component.id !== id
);
message.success("刪除當前圖層成功", 1);
}
}),
添加移動元素的快捷鍵:文章來源:http://www.zghlxwxcb.cn/news/detail-817219.html
// store/editors.ts
moveComponent(
state,
data: { direction: MoveDirection; amount: number; id: string }
) {
const currentComponent = state.components.find(
(component) => component.id === data.id
);
if (currentComponent) {
// 獲取舊的值
const oldTop = parseInt(currentComponent.props.top || "0");
const oldLeft = parseInt(currentComponent.props.left || "0");
const { direction, amount } = data;
switch (direction) {
case "Up": {
const newValue = oldTop - amount + "px";
store.commit("updateComponent", {
key: "top",
value: newValue,
id: data.id,
});
break;
}
case "Down": {
const newValue = oldTop + amount + "px";
store.commit("updateComponent", {
key: "top",
value: newValue,
id: data.id,
});
break;
}
case "Left": {
const newValue = oldLeft - amount + "px";
store.commit("updateComponent", {
key: "left",
value: newValue,
id: data.id,
});
break;
}
case "Right": {
const newValue = oldLeft + amount + "px";
store.commit("updateComponent", {
key: "left",
value: newValue,
id: data.id,
});
break;
}
default:
break;
}
}
},
// hotKey.ts
function initHotKeys(){
...
useHotKey('down', () => {
store.commit('moveComponent', { direction: 'Down', amount: 1, id: currentId.value})
})
useHotKey('left', () => {
store.commit('moveComponent', { direction: 'Left', amount: 1, id: currentId.value})
})
useHotKey('right', () => {
store.commit('moveComponent', { direction: 'Right', amount: 1, id: currentId.value})
})
useHotKey('shift+up', () => {
store.commit('moveComponent', { direction: 'Up', amount: 10, id: currentId.value})
})
useHotKey('shift+down', () => {
store.commit('moveComponent', { direction: 'Down', amount: 10, id: currentId.value})
})
useHotKey('shift+left', () => {
store.commit('moveComponent', { direction: 'Left', amount: 10, id: currentId.value})
})
useHotKey('shift+right', () => {
store.commit('moveComponent', { direction: 'Right', amount: 10, id: currentId.value})
})
}
實現(xiàn)之后發(fā)現(xiàn)會有一個問題:如果滾動條在下面的話,滾動條會跟著一起移動。
滾動條跟隨元素一起移動,這個行為是瀏覽器默認的一個行為,所以我們可以使用e.preventDefault
來阻止瀏覽器默認行為,在useKotKey回調(diào)函數(shù)里面,第一個參數(shù)就是事件對象,我們可以在這里進行添加,其他事件也是如此。但是如果要是都是寫同樣的邏輯,是比較繁瑣的,有什么辦法能夠完成無數(shù)個這樣的邏輯呢?我們可以給回調(diào)函數(shù)在來一套包裝,返回一個新的function
,這就是高階函數(shù),高階函數(shù)在處理多次邏輯重復的回調(diào)中比較適用:文章來源地址http://www.zghlxwxcb.cn/news/detail-817219.html
const wrap = (callback: KeyHandler) => {
const wrapperFn = (e: KeyboardEvent, event: HotkeysEvent) => {
e.preventDefault()
callback(e, event)
}
return wrapperFn
}
useHotKey('up', wrap(() => {
store.commit('moveComponent', { direction: 'Up', amount: 1, id: currentId.value })
}))
useHotKey('down', wrap(() => {
store.commit('moveComponent', { direction: 'Down', amount: 1, id: currentId.value})
}))
到了這里,關于web架構(gòu)師編輯器內(nèi)容-快捷鍵操作的實現(xiàn)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!