Skip to content

Commit

Permalink
feat: 完成快捷键绑定模块
Browse files Browse the repository at this point in the history
  • Loading branch information
wangrongding committed May 3, 2024
1 parent 4aae5be commit 3da9a33
Show file tree
Hide file tree
Showing 5 changed files with 256 additions and 36 deletions.
85 changes: 69 additions & 16 deletions src/components/player/CanvasPlayer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import Logo from '~/assets/icons/icon-github.svg'
import { usePlayerStore, type ElementItem } from '~/stores/player'
import emitter, { BusEvent } from '~/utils/eventBus'
import { initFabricControlCustomStyle } from './customFabricControl'
import { nanoid } from 'nanoid'
import { registerHotkey } from './operate'
defineProps<{
msg: string
Expand All @@ -17,7 +17,7 @@ const container = ref<HTMLElement | null>(null)
let videoRef: HTMLVideoElement
let canvasRef: HTMLCanvasElement
const playerStore = usePlayerStore()
const { togglePlay, addElement, removeElement } = playerStore
const { togglePlay, addElement, removeElement, setFocusElements } = playerStore
const { playStatus, currentTime, duration, elementList } = storeToRefs(playerStore)
const menuShow = ref(false)
const contextMenuPosition = ref({ x: 0, y: 0 })
Expand Down Expand Up @@ -47,6 +47,7 @@ emitter.on(BusEvent.ElementDelete, onDelete)
emitter.on(BusEvent.ElementAdd, onAdd)
emitter.on(BusEvent.ElementAlign, setElementAlign)
emitter.on(BusEvent.ElementLayer, setElementLayer)
emitter.on(BusEvent.ElementSelectAll, selectAll)
emitter.on(BusEvent.CanvasFullScreen, toggleCanvasFullScreen)
emitter.on(BusEvent.CanvasExportCurrentFrame, onExportCurrentFrame)
emitter.on(BusEvent.VideoSkip, (time: number) => (videoRef.currentTime += time))
Expand Down Expand Up @@ -76,17 +77,17 @@ function initCanvas() {
// 初始化 Canvas 事件
function initCanvasEvent() {
// 画布鼠标按下事件
canvas.on('mouse:down', canvasOnMouseDown)
// 选中元素时
canvas.on('selection:created', onElementSelected)
canvas.on('selection:updated', onElementSelected)
// 取消选中元素时
canvas.on('selection:cleared', onElementDeselected)
// 目标移动中
canvas.on('object:moving', (e) => {
if (!e.target) return
e.target.opacity = 0.5
})
canvas.on('object:moving', onElementMoving)
// 目标修改后
canvas.on('object:modified', (e) => {
if (!e.target) return
e.target.opacity = 1
})
canvas.on('object:modified', onElementModified)
}
// 绘制元素
Expand All @@ -100,14 +101,16 @@ async function drawElements() {
}
// 删除元素
function onDelete(obj: ElementItem) {
const { element, id } = toRaw(obj)
const activeObject = element instanceof fabric.Object ? element : null
function onDelete(obj: ElementItem | void) {
const targetElement: ElementItem | null = obj ? toRaw(obj) : canvas.getActiveObject()
if (!targetElement) return
const { elementId } = targetElement
const activeObject = targetElement instanceof fabric.Object ? targetElement : null
if (!activeObject) return
canvas.remove(activeObject)
canvas.requestRenderAll()
if (menuShow.value) menuShow.value = false
removeElement(id)
elementId && removeElement(elementId)
}
// 添加元素
Expand All @@ -126,6 +129,9 @@ function onAdd({ type, value }: { type: string; value: string }) {
case 'text':
addText(value)
break
case 'rect':
addRect()
break
default:
break
}
Expand All @@ -148,6 +154,19 @@ function addSVG(url: string) {
})
}
// 绘制矩形
function addRect() {
const rectElement = new fabric.Rect({
left: canvas.width! / 2,
top: canvas.height! / 2,
fill: 'red',
width: 100,
height: 100,
})
canvas.add(rectElement)
addElement('svg', rectElement)
}
// 添加文字
function addText(val: string, options?: fabric.ITextboxOptions) {
const textElement = new fabric.Textbox(val, {
Expand Down Expand Up @@ -201,8 +220,8 @@ async function drawVideo(url: string) {
canvas.add(videoElement)
continuouslyRepaint()
duration.value = videoRef.duration
canvas.setActiveObject(videoElement)
addElement('video', videoElement)
canvas.setActiveObject(videoElement)
videoRef.addEventListener('timeupdate', () => {
currentTime.value = videoRef.currentTime
Expand All @@ -227,8 +246,8 @@ function addImage(url: string) {
angle: 0,
})
canvas.add(imgElement)
canvas.setActiveObject(imgElement)
addElement('image', imgElement)
canvas.setActiveObject(imgElement)
})
}
Expand Down Expand Up @@ -405,6 +424,7 @@ function setElementLayer(type: 'up' | 'down' | 'top' | 'bottom') {
menuShow.value = false
}
// 切换画布全屏
function toggleCanvasFullScreen(fullscreen?: boolean) {
if (fullscreen === undefined) {
if (document.fullscreenElement) {
Expand Down Expand Up @@ -552,9 +572,42 @@ function canvasOnMouseDown(e: fabric.IEvent<MouseEvent>) {
}
}
// 全选
function selectAll() {
canvas.discardActiveObject()
const sel = new fabric.ActiveSelection(canvas.getObjects(), {
canvas: canvas,
})
canvas.setActiveObject(sel)
canvas.requestRenderAll()
}
// 元素选中时
function onElementSelected(e: fabric.IEvent<MouseEvent>) {
setFocusElements(e.selected, undefined)
}
// 元素取消选中时
function onElementDeselected(e: fabric.IEvent<MouseEvent>) {
setFocusElements(undefined, e.deselected)
}
// 元素移动时
function onElementMoving(e: fabric.IEvent<MouseEvent>) {
if (!e.target) return
e.target.opacity = 0.5
}
// 元素修改后
function onElementModified(e: fabric.IEvent<MouseEvent>) {
if (!e.target) return
e.target.opacity = 1
}
onMounted((): void => {
// 初始化画布
initCanvas()
// 注册快捷键
registerHotkey()
// oncopy事件禁用复制;
document.oncopy = onCopy
Expand Down
4 changes: 2 additions & 2 deletions src/components/player/customFabricControl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ export function initFabricControlCustomStyle() {
borderColor: '#fff', // 控制边框颜色
cornerColor: '#fff', // 控制控件颜色
cornerStrokeColor: '#fff', // 控制控件边框颜色
cornerSize: 10, // 控制控件大小
cornerSize: 14, // 控制控件大小
cornerStyle: 'circle', // 控制控件形状
transparentCorners: false // 控制控件是否透明
transparentCorners: false, // 控制控件是否透明
})
}
160 changes: 160 additions & 0 deletions src/components/player/operate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import emitter, { BusEvent } from '~/utils/eventBus'

const keyCodeMap: Record<string, string> = {
'8': 'Backspace',
'27': 'Esc',
'37': 'left',
'38': 'top',
'39': 'right',
'40': 'bottom',
'46': 'Delete',

'48': '0',
'49': '1',
'50': '2',
'51': '3',
'52': '4',
'53': '5',
'54': '6',
'55': '7',
'56': '8',
'57': '9',

'65': 'A',
'66': 'B',
'67': 'C',
'68': 'D',
'69': 'E',
'70': 'F',
'71': 'G',
'72': 'H',
'73': 'I',
'74': 'J',
'75': 'K',
'76': 'L',
'77': 'M',
'78': 'N',
'79': 'O',
'80': 'P',
'81': 'Q',
'82': 'R',
'83': 'S',
'84': 'T',
'85': 'U',
'86': 'V',
'87': 'W',
'88': 'X',
'89': 'Y',
'90': 'Z',

'187': '+',
'188': ',',
'189': '-',
'190': '.',
'191': '/',
'219': '[',
'221': ']',
}
const keyEventMap: Record<
string,
(event: KeyboardEvent & { readonly target: HTMLElement }) => void
> = {
// 全选
'cmd&A': (event) => {
event.preventDefault()
emitter.emit(BusEvent.ElementSelectAll)
},
// 剪切
'cmd&X': (event) => {
event.preventDefault()
},
// 复制
'cmd&C': (event) => {
event.preventDefault()
emitter.emit(BusEvent.ElementCopy)
},
// 粘贴
'cmd&V': (event) => {
event.preventDefault()
emitter.emit(BusEvent.ElementPaste)
},
// 撤销 undo
'cmd&Z': (event) => {
event.preventDefault()
},
// 重做 redo
'cmd&shift&Z': (event) => {
event.preventDefault()
},
// 重做 redo
'cmd&Y': (event) => {
event.preventDefault()
},
// 保存
'cmd&S': (event) => {
event.preventDefault()
},
// 锁定/取消锁定
'cmd&L': (event) => {
if (event.shiftKey) return
event.preventDefault()
},
// 上移一个图层
'cmd&]': (event) => {},
// 上移到顶层
'cmd&alt&]': (event) => {},
// 下移一个图层
'cmd&[': (event) => {},
// 下移到底层
'cmd&alt&[': (event) => {},
// 退格键删除
Backspace: (event) => {
event.preventDefault()
emitter.emit(BusEvent.ElementDelete)
},
// 删除键删除
Delete: (event) => {
event.preventDefault()
emitter.emit(BusEvent.ElementDelete)
},
// 元素恢复实际大小
'shift&0': (event) => {},
// 元素左右翻转
'shift&H': (event) => {},
// 元素上下翻转
'shift&V': (event) => {},
// 元素向左移动
left: (event) => {},
// 元素向右移动
right: (event) => {},
// 元素向上移动
top: (event) => {},
// 元素向下移动
bottom: (event) => {},
}
// 找到对应的热键方法并执行
const findHotKeyFun = (event: KeyboardEvent & { optionKey?: boolean }) => {
const keys: string[] = []
if (event.metaKey || event.ctrlKey) {
keys.push('cmd')
}
if (event.shiftKey) {
keys.push('shift')
}
if (event.altKey || event.optionKey) {
keys.push('alt')
}
keys.push(keyCodeMap[event.keyCode])
const handler = keyEventMap[keys.join('&')]
handler?.(event as KeyboardEvent & { target: HTMLElement })
}

// 快捷键绑定
export function registerHotkey() {
window.addEventListener('keydown', findHotKeyFun, false)
}

// 快捷键解绑
export function unregisterHotkey() {
window.removeEventListener('keydown', findHotKeyFun, false)
}
Loading

0 comments on commit 3da9a33

Please sign in to comment.