From 72fa220cf37a2ad04e68b16e5f419c4830e979a8 Mon Sep 17 00:00:00 2001 From: pipipi-pikachu Date: Sat, 30 Apr 2022 15:39:50 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E7=94=BB=E5=B8=83?= =?UTF-8?q?=E6=8B=96=E6=8B=BD=E7=A7=BB=E5=8A=A8=E3=80=81=E7=94=BB=E5=B8=83?= =?UTF-8?q?=E7=BC=A9=E6=94=BE=E6=AF=94=E4=BE=8B=E9=A2=84=E7=BD=AE=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/configs/hotkey.ts | 3 +- src/hooks/useGlobalHotkey.ts | 8 +-- src/hooks/useScaleCanvas.ts | 31 ++++++++--- src/store/keyboard.ts | 5 ++ src/store/main.ts | 6 +++ .../Editor/Canvas/hooks/useViewportSize.ts | 37 ++++++++++++- src/views/Editor/Canvas/index.vue | 16 ++++-- src/views/Editor/CanvasTool/index.vue | 54 +++++++++++++++---- 8 files changed, 135 insertions(+), 25 deletions(-) diff --git a/src/configs/hotkey.ts b/src/configs/hotkey.ts index d964e6e3..1c420b4c 100644 --- a/src/configs/hotkey.ts +++ b/src/configs/hotkey.ts @@ -52,10 +52,11 @@ export const HOTKEY_DOC = [ type: '幻灯片编辑', children: [ { label: '新建幻灯片', value: 'Enter' }, + { label: '移动画布', value: 'Space + 鼠标拖拽' }, { label: '缩放画布', value: 'Ctrl + 鼠标滚动' }, { label: '放大画布', value: 'Ctrl + =' }, { label: '缩小画布', value: 'Ctrl + -' }, - { label: '缩放画布到合适大小', value: 'Ctrl + 0' }, + { label: '使画布适应当前屏幕', value: 'Ctrl + 0' }, { label: '编辑上一页', value: '↑ / ← / 鼠标上滚' }, { label: '编辑下一页', value: '↓ / → / 鼠标下滚' }, ], diff --git a/src/hooks/useGlobalHotkey.ts b/src/hooks/useGlobalHotkey.ts index 83e1d5dc..1dac5518 100644 --- a/src/hooks/useGlobalHotkey.ts +++ b/src/hooks/useGlobalHotkey.ts @@ -28,7 +28,7 @@ export default () => { thumbnailsFocus, } = storeToRefs(mainStore) const { currentSlide } = storeToRefs(useSlidesStore()) - const { ctrlKeyState, shiftKeyState } = storeToRefs(keyboardStore) + const { ctrlKeyState, shiftKeyState, spaceKeyState } = storeToRefs(keyboardStore) const { updateSlideIndex, @@ -49,7 +49,7 @@ export default () => { const { orderElement } = useOrderElement() const { redo, undo } = useHistorySnapshot() const { enterScreening } = useScreening() - const { scaleCanvas, setCanvasPercentage } = useScaleCanvas() + const { scaleCanvas, resetCanvas } = useScaleCanvas() const copy = () => { if (activeElementIdList.value.length) copyElement() @@ -127,6 +127,7 @@ export default () => { if (ctrlOrMetaKeyActive && !ctrlKeyState.value) keyboardStore.setCtrlKeyState(true) if (shiftKey && !shiftKeyState.value) keyboardStore.setShiftKeyState(true) + if (!disableHotkeys.value && key === KEYS.SPACE) keyboardStore.setSpaceKeyState(true) if (ctrlOrMetaKeyActive && key === KEYS.F) { e.preventDefault() @@ -234,7 +235,7 @@ export default () => { if (key === KEYS.DIGIT_0) { if (disableHotkeys.value) return e.preventDefault() - setCanvasPercentage(90) + resetCanvas() } if (key === KEYS.TAB) { if (disableHotkeys.value) return @@ -246,6 +247,7 @@ export default () => { const keyupListener = () => { if (ctrlKeyState.value) keyboardStore.setCtrlKeyState(false) if (shiftKeyState.value) keyboardStore.setShiftKeyState(false) + if (spaceKeyState.value) keyboardStore.setSpaceKeyState(false) } onMounted(() => { diff --git a/src/hooks/useScaleCanvas.ts b/src/hooks/useScaleCanvas.ts index 53d63fbe..28156abe 100644 --- a/src/hooks/useScaleCanvas.ts +++ b/src/hooks/useScaleCanvas.ts @@ -1,9 +1,12 @@ +import { computed } from 'vue' import { storeToRefs } from 'pinia' import { useMainStore } from '@/store' export default () => { const mainStore = useMainStore() - const { canvasPercentage } = storeToRefs(mainStore) + const { canvasPercentage, canvasScale, canvasDragged } = storeToRefs(mainStore) + + const canvasScalePercentage = computed(() => Math.round(canvasScale.value * 100) + '%') /** * 缩放画布百分比 @@ -12,8 +15,8 @@ export default () => { const scaleCanvas = (command: '+' | '-') => { let percentage = canvasPercentage.value const step = 5 - const max = 120 - const min = 60 + const max = 200 + const min = 30 if (command === '+' && percentage <= max) percentage += step if (command === '-' && percentage >= min) percentage -= step @@ -21,15 +24,27 @@ export default () => { } /** - * 设置画笔百分比 - * @param percentage 百分比(小数形式,如0.8) + * 设置画布缩放比例 + * 但不是直接设置该值,而是通过设置画布可视区域百分比来动态计算 + * @param value 目标画布缩放比例 */ - const setCanvasPercentage = (percentage: number) => { + const setCanvasScalePercentage = (value: number) => { + const percentage = Math.round(value / canvasScale.value * canvasPercentage.value) / 100 mainStore.setCanvasPercentage(percentage) } - + + /** + * 重置画布尺寸和位置 + */ + const resetCanvas = () => { + mainStore.setCanvasPercentage(90) + if (canvasDragged) mainStore.setCanvasDragged(false) + } + return { + canvasScalePercentage, + setCanvasScalePercentage, scaleCanvas, - setCanvasPercentage, + resetCanvas, } } \ No newline at end of file diff --git a/src/store/keyboard.ts b/src/store/keyboard.ts index fcbe8258..795c6589 100644 --- a/src/store/keyboard.ts +++ b/src/store/keyboard.ts @@ -3,12 +3,14 @@ import { defineStore } from 'pinia' export interface KeyboardState { ctrlKeyState: boolean; shiftKeyState: boolean; + spaceKeyState: boolean; } export const useKeyboardStore = defineStore('keyboard', { state: (): KeyboardState => ({ ctrlKeyState: false, // ctrl键按下状态 shiftKeyState: false, // shift键按下状态 + spaceKeyState: false, // space键按下状态 }), getters: { @@ -24,5 +26,8 @@ export const useKeyboardStore = defineStore('keyboard', { setShiftKeyState(active: boolean) { this.shiftKeyState = active }, + setSpaceKeyState(active: boolean) { + this.spaceKeyState = active + }, }, }) \ No newline at end of file diff --git a/src/store/main.ts b/src/store/main.ts index 99a300b6..6a2af761 100644 --- a/src/store/main.ts +++ b/src/store/main.ts @@ -13,6 +13,7 @@ export interface MainState { activeGroupElementId: string; canvasPercentage: number; canvasScale: number; + canvasDragged: boolean; thumbnailsFocus: boolean; editorAreaFocus: boolean; disableHotkeys: boolean; @@ -35,6 +36,7 @@ export const useMainStore = defineStore('main', { activeGroupElementId: '', // 组合元素成员中,被选中可独立操作的元素ID canvasPercentage: 90, // 画布可视区域百分比 canvasScale: 1, // 画布缩放比例(基于宽度1000px) + canvasDragged: false, // 画布被拖拽移动 thumbnailsFocus: false, // 左侧导航缩略图区域聚焦 editorAreaFocus: false, // 编辑区域聚焦 disableHotkeys: false, // 禁用快捷键 @@ -90,6 +92,10 @@ export const useMainStore = defineStore('main', { this.canvasScale = scale }, + setCanvasDragged(isDragged: boolean) { + this.canvasDragged = isDragged + }, + setThumbnailsFocus(isFocus: boolean) { this.thumbnailsFocus = isFocus }, diff --git a/src/views/Editor/Canvas/hooks/useViewportSize.ts b/src/views/Editor/Canvas/hooks/useViewportSize.ts index 4f424874..cf4ae280 100644 --- a/src/views/Editor/Canvas/hooks/useViewportSize.ts +++ b/src/views/Editor/Canvas/hooks/useViewportSize.ts @@ -8,7 +8,7 @@ export default (canvasRef: Ref) => { const viewportTop = ref(0) const mainStore = useMainStore() - const { canvasPercentage } = storeToRefs(mainStore) + const { canvasPercentage, canvasDragged } = storeToRefs(mainStore) const { viewportRatio } = storeToRefs(useSlidesStore()) // 计算画布可视区域的位置 @@ -34,6 +34,11 @@ export default (canvasRef: Ref) => { // 可视区域缩放或比例变化时,更新可视区域的位置 watch([canvasPercentage, viewportRatio], setViewportPosition) + // 画布拖拽状态改变(复原)时,更新可视区域的位置 + watch(canvasDragged, () => { + if (!canvasDragged.value) setViewportPosition() + }) + // 画布可视区域位置和大小的样式 const viewportStyles = computed(() => ({ width: VIEWPORT_SIZE, @@ -52,7 +57,37 @@ export default (canvasRef: Ref) => { if (canvasRef.value) resizeObserver.unobserve(canvasRef.value) }) + // 拖拽画布 + const dragViewport = (e: MouseEvent) => { + let isMouseDown = true + + const startPageX = e.pageX + const startPageY = e.pageY + + const originLeft = viewportLeft.value + const originTop = viewportTop.value + + document.onmousemove = e => { + if (!isMouseDown) return + + const currentPageX = e.pageX + const currentPageY = e.pageY + + viewportLeft.value = originLeft + (currentPageX - startPageX) + viewportTop.value = originTop + (currentPageY - startPageY) + } + + document.onmouseup = () => { + isMouseDown = false + document.onmousemove = null + document.onmouseup = null + + mainStore.setCanvasDragged(true) + } + } + return { viewportStyles, + dragViewport, } } \ No newline at end of file diff --git a/src/views/Editor/Canvas/index.vue b/src/views/Editor/Canvas/index.vue index 4b4229f6..414ec25f 100644 --- a/src/views/Editor/Canvas/index.vue +++ b/src/views/Editor/Canvas/index.vue @@ -74,6 +74,8 @@ +
+ () const alignmentLines = ref([]) @@ -167,7 +169,7 @@ export default defineComponent({ watchEffect(setLocalElementList) const canvasRef = ref() - const { viewportStyles } = useViewportSize(canvasRef) + const { dragViewport, viewportStyles } = useViewportSize(canvasRef) useDropImageOrText(canvasRef) @@ -188,7 +190,10 @@ export default defineComponent({ // 点击画布的空白区域:清空焦点元素、设置画布焦点、清除文字选区 const handleClickBlankArea = (e: MouseEvent) => { mainStore.setActiveElementIdList([]) - if (!ctrlOrShiftKeyActive.value) updateMouseSelection(e) + + if (!spaceKeyState.value) updateMouseSelection(e) + else dragViewport(e) + if (!editorAreaFocus.value) mainStore.setEditorareaFocus(true) removeAllRanges() } @@ -273,6 +278,7 @@ export default defineComponent({ creatingElement, alignmentLines, linkDialogVisible, + spaceKeyState, openLinkDialog, handleClickBlankArea, removeEditorAreaFocus, @@ -297,6 +303,10 @@ export default defineComponent({ background-color: $lightGray; position: relative; } +.drag-mask { + cursor: grab; + @include absolute-0(); +} .viewport-wrapper { position: absolute; box-shadow: 0 0 15px 0 rgba(0, 0, 0, 0.1); diff --git a/src/views/Editor/CanvasTool/index.vue b/src/views/Editor/CanvasTool/index.vue index 02b41cba..0b5e408f 100644 --- a/src/views/Editor/CanvasTool/index.vue +++ b/src/views/Editor/CanvasTool/index.vue @@ -72,10 +72,22 @@
- {{canvasScalePercentage}} + + + {{canvasScalePercentage}} + - - + +
@@ -95,7 +107,7 @@