mirror of
https://github.com/pipipi-pikachu/PPTist.git
synced 2025-04-15 02:20:00 +08:00
update
This commit is contained in:
parent
6c65908a07
commit
ce9069d941
@ -1,12 +1,12 @@
|
||||
const DEFAULT_COLOR = '#41464b'
|
||||
|
||||
export enum ELEMENTS {
|
||||
text = '文本',
|
||||
image = '图片',
|
||||
shape = '形状',
|
||||
line = '线条',
|
||||
chart = '图表',
|
||||
table = '表格',
|
||||
export enum ElementTypes {
|
||||
TEXT = '文本',
|
||||
IMAGE = '图片',
|
||||
SHAPE = '形状',
|
||||
LINE = '线条',
|
||||
CHART = '图表',
|
||||
TABLE = '表格',
|
||||
}
|
||||
|
||||
export const DEFAULT_TEXT = {
|
||||
|
@ -3,7 +3,7 @@ import { Slide } from '@/types/slides'
|
||||
export const slides: Slide[] = [
|
||||
{
|
||||
id: 'xxx1',
|
||||
background: ['solid', '#323f4f'],
|
||||
background: ['solid', '#fff'],
|
||||
elements: [
|
||||
{
|
||||
elId: 'xxx1',
|
||||
|
@ -24,4 +24,8 @@ export enum MutationTypes {
|
||||
UNDO = 'undo',
|
||||
REDO = 'redo',
|
||||
SET_HISTORY_RECORD_LENGTH = 'setHistoryRecordLength',
|
||||
|
||||
// keyboard
|
||||
SET_CTRL_KEY_STATE = 'setCtrlKeyState',
|
||||
SET_SHIFT_KEY_STATE = 'setShiftKeyState',
|
||||
}
|
@ -8,6 +8,7 @@ export type Getters = {
|
||||
handleElement(state: State): PPTElement | null;
|
||||
canUndo(state: State): boolean;
|
||||
canRedo(state: State): boolean;
|
||||
ctrlOrShiftKeyActive(state: State): boolean;
|
||||
}
|
||||
|
||||
export const getters: Getters = {
|
||||
@ -45,4 +46,8 @@ export const getters: Getters = {
|
||||
canRedo(state) {
|
||||
return state.cursor < state.historyRecordLength - 1
|
||||
},
|
||||
|
||||
ctrlOrShiftKeyActive(state) {
|
||||
return state.ctrlKeyState || state.shiftKeyState
|
||||
},
|
||||
}
|
@ -36,6 +36,9 @@ export type Mutations = {
|
||||
[MutationTypes.UNDO](state: State): void;
|
||||
[MutationTypes.REDO](state: State): void;
|
||||
[MutationTypes.SET_HISTORY_RECORD_LENGTH](state: State, length: number): void;
|
||||
|
||||
[MutationTypes.SET_CTRL_KEY_STATE](state: State, isActive: boolean): void;
|
||||
[MutationTypes.SET_SHIFT_KEY_STATE](state: State, isActive: boolean): void;
|
||||
}
|
||||
|
||||
export const mutations: Mutations = {
|
||||
@ -143,4 +146,13 @@ export const mutations: Mutations = {
|
||||
[MutationTypes.SET_HISTORY_RECORD_LENGTH](state, length) {
|
||||
state.historyRecordLength = length
|
||||
},
|
||||
|
||||
// keyBoard
|
||||
|
||||
[MutationTypes.SET_CTRL_KEY_STATE](state, isActive) {
|
||||
state.ctrlKeyState = isActive
|
||||
},
|
||||
[MutationTypes.SET_SHIFT_KEY_STATE](state, isActive) {
|
||||
state.shiftKeyState = isActive
|
||||
},
|
||||
}
|
@ -15,6 +15,8 @@ export type State = {
|
||||
slideIndex: number;
|
||||
cursor: number;
|
||||
historyRecordLength: number;
|
||||
ctrlKeyState: boolean;
|
||||
shiftKeyState: boolean;
|
||||
}
|
||||
|
||||
export const state: State = {
|
||||
@ -30,4 +32,6 @@ export const state: State = {
|
||||
slideIndex: 0,
|
||||
cursor: -1,
|
||||
historyRecordLength: 0,
|
||||
ctrlKeyState: false,
|
||||
shiftKeyState: false,
|
||||
}
|
@ -1,5 +1,14 @@
|
||||
export type ElementType = 'text' | 'image' | 'shape' | 'line' | 'chart' | 'table'
|
||||
|
||||
export enum ElementTypes {
|
||||
TEXT = 'text',
|
||||
IMAGE = 'image',
|
||||
SHAPE = 'shape',
|
||||
LINE = 'line',
|
||||
CHART = 'chart',
|
||||
TABLE = 'table',
|
||||
}
|
||||
|
||||
export interface PPTElementBaseProps {
|
||||
elId: string;
|
||||
isLock: boolean;
|
||||
@ -41,7 +50,7 @@ export interface PPTImageElement extends PPTElementBaseProps, PPTElementSizeProp
|
||||
range: [[number, number], [number, number]];
|
||||
shape: 'rect' | 'roundRect' | 'ellipse' | 'triangle' | 'pentagon' | 'rhombus' | 'star';
|
||||
};
|
||||
flip?: { x?: number, y?: number };
|
||||
flip?: { x?: number; y?: number };
|
||||
shadow?: string;
|
||||
}
|
||||
|
||||
@ -70,7 +79,7 @@ export interface PPTChartElement extends PPTElementBaseProps, PPTElementSizeProp
|
||||
type: 'chart';
|
||||
chartType: string;
|
||||
theme: string;
|
||||
data: Object;
|
||||
data: string;
|
||||
}
|
||||
|
||||
export interface TableElementCell {
|
||||
|
132
src/views/Editor/Canvas/MultiSelectOperate.vue
Normal file
132
src/views/Editor/Canvas/MultiSelectOperate.vue
Normal file
@ -0,0 +1,132 @@
|
||||
<template>
|
||||
<div
|
||||
class="multi-select-operate"
|
||||
:style="{
|
||||
left: minX + 'px',
|
||||
top: minY + 'px',
|
||||
transform: `scale(${1 / canvasScale})`,
|
||||
}"
|
||||
>
|
||||
<BorderLine v-for="line in borderLines" :key="line.type" :type="line.type" :style="line.style" />
|
||||
|
||||
<template v-if="!disableResizablePoint">
|
||||
<ResizablePoint
|
||||
v-for="point in resizablePoints"
|
||||
:key="point.type"
|
||||
:type="point.type"
|
||||
:style="point.style"
|
||||
@mousedown.stop="scaleMultiElement($event, { minX, maxX, minY, maxY }, point.direction)"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, reactive, PropType, watch, toRefs, onMounted } from 'vue'
|
||||
import { OPERATE_KEYS } from '@/configs/element'
|
||||
import { PPTElement, ElementTypes } from '@/types/slides'
|
||||
import { getElementListRange } from './utils/elementRange'
|
||||
import { ElementScaleHandler, OperateResizablePointTypes, OperateBorderLineTypes } from '@/types/edit'
|
||||
|
||||
import ResizablePoint from '@/views/_common/_operate/ResizablePoint.vue'
|
||||
import BorderLine from '@/views/_common/_operate/BorderLine.vue'
|
||||
|
||||
interface Range {
|
||||
minX: number;
|
||||
maxX: number;
|
||||
minY: number;
|
||||
maxY: number;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'multi-select-operate',
|
||||
components: {
|
||||
ResizablePoint,
|
||||
BorderLine,
|
||||
},
|
||||
props: {
|
||||
canvasScale: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
activeElementList: {
|
||||
type: Array as PropType<PPTElement[]>,
|
||||
required: true,
|
||||
},
|
||||
scaleMultiElement: {
|
||||
type: Function as PropType<(e: MouseEvent, range: Range, command: ElementScaleHandler) => void>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const range = reactive({
|
||||
minX: 0,
|
||||
maxX: 0,
|
||||
minY: 0,
|
||||
maxY: 0,
|
||||
})
|
||||
|
||||
const width = computed(() => (range.maxX - range.minX) * props.canvasScale)
|
||||
const height = computed(() => (range.maxY - range.minY) * props.canvasScale)
|
||||
|
||||
const resizablePoints = computed(() => {
|
||||
return [
|
||||
{ type: OperateResizablePointTypes.TL, direction: OPERATE_KEYS.LEFT_TOP, style: {} },
|
||||
{ type: OperateResizablePointTypes.TC, direction: OPERATE_KEYS.TOP, style: {left: width.value / 2 + 'px'} },
|
||||
{ type: OperateResizablePointTypes.TR, direction: OPERATE_KEYS.RIGHT_TOP, style: {left: width.value + 'px'} },
|
||||
{ type: OperateResizablePointTypes.ML, direction: OPERATE_KEYS.LEFT, style: {top: height.value / 2 + 'px'} },
|
||||
{ type: OperateResizablePointTypes.MR, direction: OPERATE_KEYS.RIGHT, style: {left: width.value + 'px', top: height.value / 2 + 'px'} },
|
||||
{ type: OperateResizablePointTypes.BL, direction: OPERATE_KEYS.LEFT_BOTTOM, style: {top: height.value + 'px'} },
|
||||
{ type: OperateResizablePointTypes.BC, direction: OPERATE_KEYS.BOTTOM, style: {left: width.value / 2 + 'px', top: height.value + 'px'} },
|
||||
{ type: OperateResizablePointTypes.BR, direction: OPERATE_KEYS.RIGHT_BOTTOM, style: {left: width.value + 'px', top: height.value + 'px'} },
|
||||
]
|
||||
})
|
||||
|
||||
const borderLines = computed(() => {
|
||||
return [
|
||||
{ type: OperateBorderLineTypes.T, style: {width: width.value + 'px'} },
|
||||
{ type: OperateBorderLineTypes.B, style: {top: height.value + 'px', width: width.value + 'px'} },
|
||||
{ type: OperateBorderLineTypes.L, style: {height: height.value + 'px'} },
|
||||
{ type: OperateBorderLineTypes.R, style: {left: width.value + 'px', height: height.value + 'px'} },
|
||||
]
|
||||
})
|
||||
|
||||
const disableResizablePoint = computed(() => {
|
||||
return props.activeElementList.some(item => {
|
||||
if(
|
||||
(item.type === ElementTypes.IMAGE || item.type === ElementTypes.SHAPE) &&
|
||||
!item.rotate
|
||||
) return false
|
||||
return true
|
||||
})
|
||||
})
|
||||
|
||||
const setRange = () => {
|
||||
const { minX, maxX, minY, maxY } = getElementListRange(props.activeElementList)
|
||||
range.minX = minX
|
||||
range.maxX = maxX
|
||||
range.minY = minY
|
||||
range.maxY = maxY
|
||||
}
|
||||
|
||||
onMounted(setRange)
|
||||
watch(props.activeElementList, setRange)
|
||||
|
||||
return {
|
||||
...toRefs(range),
|
||||
borderLines,
|
||||
disableResizablePoint,
|
||||
resizablePoints,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.multi-select-operate {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 100;
|
||||
}
|
||||
</style>
|
@ -36,6 +36,13 @@
|
||||
:type="line.type" :axis="line.axis" :length="line.length"
|
||||
/>
|
||||
|
||||
<MultiSelectOperate
|
||||
v-if="activeElementIdList.length > 1"
|
||||
:activeElementList="activeElementList"
|
||||
:canvasScale="canvasScale"
|
||||
:scaleMultiElement="scaleMultiElement"
|
||||
/>
|
||||
|
||||
<EditableElement
|
||||
v-for="(element, index) in elementList"
|
||||
:key="element.elId"
|
||||
@ -63,13 +70,17 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, reactive, ref, watch } from 'vue'
|
||||
import { computed, defineComponent, onMounted, reactive, ref, watch } from 'vue'
|
||||
import { useStore } from 'vuex'
|
||||
import uniq from 'lodash/uniq'
|
||||
import { State } from '@/store/state'
|
||||
import { MutationTypes } from '@/store/constants'
|
||||
import { ContextmenuItem } from '@/components/Contextmenu/types'
|
||||
import { VIEWPORT_SIZE, VIEWPORT_ASPECT_RATIO } from '@/configs/canvas'
|
||||
import { getImageDataURL } from '@/utils/image'
|
||||
import { getElementRange } from './utils/elementRange'
|
||||
|
||||
import { PPTElement } from '@/types/slides'
|
||||
|
||||
import useDropImage from '@/hooks/useDropImage'
|
||||
import useSetViewportSize from './hooks/useSetViewportSize'
|
||||
@ -77,6 +88,7 @@ import useSetViewportSize from './hooks/useSetViewportSize'
|
||||
import EditableElement from '@/views/_common/_element/EditableElement.vue'
|
||||
import MouseSelection from './MouseSelection.vue'
|
||||
import SlideBackground from './SlideBackground.vue'
|
||||
import MultiSelectOperate from './MultiSelectOperate.vue'
|
||||
import AlignmentLine, { AlignmentLineProps } from './AlignmentLine.vue'
|
||||
|
||||
export default defineComponent({
|
||||
@ -85,22 +97,29 @@ export default defineComponent({
|
||||
EditableElement,
|
||||
MouseSelection,
|
||||
SlideBackground,
|
||||
MultiSelectOperate,
|
||||
AlignmentLine,
|
||||
},
|
||||
setup() {
|
||||
const store = useStore<State>()
|
||||
const elementList = computed(() => {
|
||||
const currentSlide = store.getters.currentSlide
|
||||
return currentSlide ? JSON.parse(JSON.stringify(currentSlide.elements)) : []
|
||||
})
|
||||
const activeElementIdList = computed(() => store.state.activeElementIdList)
|
||||
const handleElementId = computed(() => store.state.handleElementId)
|
||||
const activeGroupElementId = ref('')
|
||||
|
||||
const activeElementIdList = computed(() => store.state.activeElementIdList)
|
||||
const activeElementList = computed(() => store.getters.activeElementList)
|
||||
const handleElementId = computed(() => store.state.handleElementId)
|
||||
const ctrlOrShiftKeyActive = computed(() => store.getters.ctrlOrShiftKeyActive)
|
||||
|
||||
const activeGroupElementId = ref('')
|
||||
const viewportRef = ref<HTMLElement | null>(null)
|
||||
const isShowGridLines = ref(false)
|
||||
const alignmentLines = ref<AlignmentLineProps[]>([])
|
||||
|
||||
const currentSlide = computed(() => store.getters.currentSlide)
|
||||
const elementList = ref<PPTElement[]>([])
|
||||
const setLocalElementList = () => {
|
||||
elementList.value = currentSlide.value ? JSON.parse(JSON.stringify(currentSlide.value.elements)) : []
|
||||
}
|
||||
onMounted(setLocalElementList)
|
||||
watch(currentSlide, setLocalElementList)
|
||||
|
||||
const dropImageFile = useDropImage(viewportRef)
|
||||
watch(dropImageFile, () => {
|
||||
@ -180,6 +199,67 @@ export default defineComponent({
|
||||
document.onmouseup = null
|
||||
isMouseDown = false
|
||||
|
||||
// 计算当前页面中的每一个元素是否处在鼠标选择范围中(必须完全包裹)
|
||||
// 将选择范围中的元素添加为激活元素
|
||||
let inRangeElementList: PPTElement[] = []
|
||||
for(let i = 0; i < elementList.value.length; i++) {
|
||||
const element = elementList.value[i]
|
||||
const mouseSelectionLeft = mouseSelectionState.left
|
||||
const mouseSelectionTop = mouseSelectionState.top
|
||||
const mouseSelectionWidth = mouseSelectionState.width
|
||||
const mouseSelectionHeight = mouseSelectionState.height
|
||||
|
||||
const quadrant = mouseSelectionState.quadrant
|
||||
|
||||
const { minX, maxX, minY, maxY } = getElementRange(element)
|
||||
|
||||
let isInclude = false
|
||||
if(quadrant === 4) {
|
||||
isInclude = minX > mouseSelectionLeft &&
|
||||
maxX < mouseSelectionLeft + mouseSelectionWidth &&
|
||||
minY > mouseSelectionTop &&
|
||||
maxY < mouseSelectionTop + mouseSelectionHeight
|
||||
}
|
||||
else if(quadrant === 1) {
|
||||
isInclude = minX > (mouseSelectionLeft - mouseSelectionWidth) &&
|
||||
maxX < (mouseSelectionLeft - mouseSelectionWidth) + mouseSelectionWidth &&
|
||||
minY > (mouseSelectionTop - mouseSelectionHeight) &&
|
||||
maxY < (mouseSelectionTop - mouseSelectionHeight) + mouseSelectionHeight
|
||||
}
|
||||
else if(quadrant === 2) {
|
||||
isInclude = minX > mouseSelectionLeft &&
|
||||
maxX < mouseSelectionLeft + mouseSelectionWidth &&
|
||||
minY > (mouseSelectionTop - mouseSelectionHeight) &&
|
||||
maxY < (mouseSelectionTop - mouseSelectionHeight) + mouseSelectionHeight
|
||||
}
|
||||
else if(quadrant === 3) {
|
||||
isInclude = minX > (mouseSelectionLeft - mouseSelectionWidth) &&
|
||||
maxX < (mouseSelectionLeft - mouseSelectionWidth) + mouseSelectionWidth &&
|
||||
minY > mouseSelectionTop &&
|
||||
maxY < mouseSelectionTop + mouseSelectionHeight
|
||||
}
|
||||
|
||||
// 被锁定的元素除外
|
||||
if(isInclude && !element.isLock) inRangeElementList.push(element)
|
||||
}
|
||||
|
||||
// 对于组合元素成员,必须所有成员都在选择范围中才算被选中
|
||||
inRangeElementList = inRangeElementList.filter(inRangeElement => {
|
||||
if(inRangeElement.groupId) {
|
||||
const inRangeElementIdList = inRangeElementList.map(inRangeElement => inRangeElement.elId)
|
||||
const groupElementList = elementList.value.filter(element => element.groupId === inRangeElement.groupId)
|
||||
return groupElementList.every(groupElement => inRangeElementIdList.includes(groupElement.elId))
|
||||
}
|
||||
return true
|
||||
})
|
||||
const inRangeElementIdList = inRangeElementList.map(inRangeElement => inRangeElement.elId)
|
||||
|
||||
// 原本就存在激活元素(可能需要清空),或者本次选择了至少一个元素(可能需要选择),才会具体更新激活元素状态
|
||||
// 否则不做多余的激活元素状态更新(原本就没有激活元素,本次也没有选择任何元素,只是点击了一下空白区域,状态为:空 -> 空)
|
||||
if(activeElementIdList.value.length > 0 || inRangeElementIdList.length) {
|
||||
store.commit(MutationTypes.SET_ACTIVE_ELEMENT_ID_LIST, inRangeElementIdList)
|
||||
}
|
||||
|
||||
mouseSelectionState.isShow = false
|
||||
}
|
||||
}
|
||||
@ -187,7 +267,7 @@ export default defineComponent({
|
||||
const editorAreaFocus = computed(() => store.state.editorAreaFocus)
|
||||
|
||||
const handleClickBlankArea = (e: MouseEvent) => {
|
||||
updateMouseSelection(e)
|
||||
if(!ctrlOrShiftKeyActive.value) updateMouseSelection(e)
|
||||
if(!editorAreaFocus.value) store.commit(MutationTypes.SET_EDITORAREA_FOCUS, true)
|
||||
}
|
||||
|
||||
@ -195,8 +275,76 @@ export default defineComponent({
|
||||
if(editorAreaFocus.value) store.commit(MutationTypes.SET_EDITORAREA_FOCUS, false)
|
||||
}
|
||||
|
||||
const selectElement = () => {
|
||||
console.log('selectElement')
|
||||
const moveElement = (e: MouseEvent, element: PPTElement) => {
|
||||
console.log(e, element)
|
||||
}
|
||||
const selectElement = (e: MouseEvent, element: PPTElement, canMove = true) => {
|
||||
if(!editorAreaFocus.value) store.commit(MutationTypes.SET_EDITORAREA_FOCUS, true)
|
||||
|
||||
// 如果被点击的元素处于未激活状态,则将他设置为激活元素(单选),或者加入到激活元素中(多选)
|
||||
if(!activeElementIdList.value.includes(element.elId)) {
|
||||
let newActiveIdList: string[] = []
|
||||
|
||||
if(ctrlOrShiftKeyActive.value) {
|
||||
newActiveIdList = [...activeElementIdList.value, element.elId]
|
||||
}
|
||||
else newActiveIdList = [element.elId]
|
||||
|
||||
// 同时如果该元素是分组成员,需要将和他同组的元素一起激活
|
||||
if(element.groupId) {
|
||||
const groupMembersId: string[] = []
|
||||
elementList.value.forEach((el: PPTElement) => {
|
||||
if(el.groupId === element.groupId) groupMembersId.push(el.elId)
|
||||
})
|
||||
newActiveIdList = [...newActiveIdList, ...groupMembersId]
|
||||
}
|
||||
|
||||
store.commit(MutationTypes.SET_ACTIVE_ELEMENT_ID_LIST, uniq(newActiveIdList))
|
||||
store.commit(MutationTypes.SET_HANDLE_ELEMENT_ID, element.elId)
|
||||
}
|
||||
|
||||
// 如果被点击的元素已激活,且按下了多选按钮,则取消其激活状态(除非该元素或分组是最后的一个激活元素)
|
||||
else if(ctrlOrShiftKeyActive.value) {
|
||||
let newActiveIdList: string[] = []
|
||||
|
||||
// 同时如果该元素是分组成员,需要将和他同组的元素一起取消
|
||||
if(element.groupId) {
|
||||
const groupMembersId: string[] = []
|
||||
elementList.value.forEach((el: PPTElement) => {
|
||||
if(el.groupId === element.groupId) groupMembersId.push(el.elId)
|
||||
})
|
||||
newActiveIdList = activeElementIdList.value.filter(elId => !groupMembersId.includes(elId))
|
||||
}
|
||||
else {
|
||||
newActiveIdList = activeElementIdList.value.filter(elId => elId !== element.elId)
|
||||
}
|
||||
|
||||
if(newActiveIdList.length > 0) {
|
||||
store.commit(MutationTypes.SET_ACTIVE_ELEMENT_ID_LIST, newActiveIdList)
|
||||
}
|
||||
}
|
||||
|
||||
// 如果被点击的元素已激活,且没有按下多选按钮,且该元素不是当前操作元素,则将其设置为当前操作元素
|
||||
else if(handleElementId.value !== element.elId) {
|
||||
store.commit(MutationTypes.SET_HANDLE_ELEMENT_ID, element.elId)
|
||||
}
|
||||
|
||||
else if(activeGroupElementId.value !== element.elId && element.groupId) {
|
||||
const startPageX = e.pageX
|
||||
const startPageY = e.pageY
|
||||
|
||||
;(e.target as HTMLElement).onmouseup = (e: MouseEvent) => {
|
||||
const currentPageX = e.pageX
|
||||
const currentPageY = e.pageY
|
||||
|
||||
if(startPageX === currentPageX && startPageY === currentPageY) {
|
||||
activeGroupElementId.value = element.elId
|
||||
;(e.target as HTMLElement).onmouseup = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(canMove) moveElement(e, element)
|
||||
}
|
||||
const rotateElement = () => {
|
||||
console.log('rotateElement')
|
||||
@ -204,6 +352,9 @@ export default defineComponent({
|
||||
const scaleElement = () => {
|
||||
console.log('scaleElement')
|
||||
}
|
||||
const scaleMultiElement = () => {
|
||||
console.log('scaleMultiElement')
|
||||
}
|
||||
const orderElement = () => {
|
||||
console.log('orderElement')
|
||||
}
|
||||
@ -248,6 +399,7 @@ export default defineComponent({
|
||||
return {
|
||||
elementList,
|
||||
activeElementIdList,
|
||||
activeElementList,
|
||||
handleElementId,
|
||||
activeGroupElementId,
|
||||
canvasRef,
|
||||
@ -263,6 +415,7 @@ export default defineComponent({
|
||||
selectElement,
|
||||
rotateElement,
|
||||
scaleElement,
|
||||
scaleMultiElement,
|
||||
orderElement,
|
||||
combineElements,
|
||||
uncombineElements,
|
||||
|
@ -13,7 +13,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, onMounted, onUnmounted, ref } from 'vue'
|
||||
import { computed, defineComponent, onMounted, onUnmounted } from 'vue'
|
||||
import { useStore } from 'vuex'
|
||||
import { State } from '@/store/state'
|
||||
import { KEYCODE } from '@/configs/keyCode'
|
||||
@ -27,6 +27,7 @@ import Canvas from './Canvas/index.vue'
|
||||
import CanvasTool from './CanvasTool/index.vue'
|
||||
import Thumbnails from './Thumbnails/index.vue'
|
||||
import Toolbar from './Toolbar/index.vue'
|
||||
import { MutationTypes } from '@/store/constants'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'editor',
|
||||
@ -38,10 +39,11 @@ export default defineComponent({
|
||||
Toolbar,
|
||||
},
|
||||
setup() {
|
||||
const ctrlKeyDown = ref(false)
|
||||
const shiftKeyDown = ref(false)
|
||||
|
||||
const store = useStore<State>()
|
||||
|
||||
const ctrlKeyActive = computed(() => store.state.ctrlKeyState)
|
||||
const shiftKeyActive = computed(() => store.state.shiftKeyState)
|
||||
|
||||
const editorAreaFocus = computed(() => store.state.editorAreaFocus)
|
||||
const thumbnailsFocus = computed(() => store.state.thumbnailsFocus)
|
||||
const disableHotkeys = computed(() => store.state.disableHotkeys)
|
||||
@ -83,8 +85,8 @@ export default defineComponent({
|
||||
const keydownListener = (e: KeyboardEvent) => {
|
||||
const { keyCode, ctrlKey, shiftKey } = e
|
||||
|
||||
if(ctrlKey && !ctrlKeyDown.value) ctrlKeyDown.value = true
|
||||
if(shiftKey && !shiftKeyDown.value) shiftKeyDown.value = true
|
||||
if(ctrlKey && !ctrlKeyActive.value) store.commit(MutationTypes.SET_CTRL_KEY_STATE, true)
|
||||
if(shiftKey && !shiftKeyActive.value) store.commit(MutationTypes.SET_SHIFT_KEY_STATE, true)
|
||||
|
||||
if(!editorAreaFocus.value && !thumbnailsFocus.value) return
|
||||
|
||||
@ -147,8 +149,8 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
const keyupListener = () => {
|
||||
if(ctrlKeyDown.value) ctrlKeyDown.value = false
|
||||
if(shiftKeyDown.value) shiftKeyDown.value = false
|
||||
if(ctrlKeyActive.value) store.commit(MutationTypes.SET_CTRL_KEY_STATE, false)
|
||||
if(shiftKeyActive.value) store.commit(MutationTypes.SET_SHIFT_KEY_STATE, false)
|
||||
}
|
||||
|
||||
const pasteImageFile = (imageFile: File) => {
|
||||
|
@ -36,7 +36,7 @@
|
||||
<div
|
||||
class="operate"
|
||||
:class="{
|
||||
'show': isActive,
|
||||
'active': isActive,
|
||||
'multi-select': isMultiSelect && isActive,
|
||||
'selected': isHandleEl
|
||||
}"
|
||||
|
Loading…
x
Reference in New Issue
Block a user