This commit is contained in:
pipipi-pikachu 2020-12-24 18:13:52 +08:00
parent 1a87b3aeec
commit 57914f1e23
14 changed files with 163 additions and 195 deletions

View File

@ -18,40 +18,33 @@ export enum ElementAlignCommands {
HORIZONTAL = 'horizontal', HORIZONTAL = 'horizontal',
} }
export type ElementScaleHandler = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 export type OperateBorderLine = 'top' | 'bottom' | 'left' | 'right'
export type OperateBorderLineType = 't' | 'b' | 'l' | 'r' export enum OperateBorderLines {
T = 'top',
export enum OperateBorderLineTypes { B = 'bottom',
T = 't', L = 'left',
B = 'b', R = 'right',
L = 'l',
R = 'r',
} }
export type OperateResizablePointType = 't-l' | 't-c' | 't-r' | 'm-l' | 'm-r' | 'b-l' | 'b-c' | 'b-r' | 'any' export type OperateResizeHandler = 'left-top' |
'top' |
'right-top' |
'left' |
'right' |
'left-bottom' |
'bottom' |
'right-bottom'
export enum OperateResizablePointTypes { export enum OperateResizeHandlers {
TL = 't-l', LEFT_TOP = 'left-top',
TC = 't-c', TOP = 'top',
TR = 't-r', RIGHT_TOP = 'right-top',
ML = 'm-l', LEFT = 'left',
MR = 'm-r', RIGHT = 'right',
BL = 'b-l', LEFT_BOTTOM = 'left-bottom',
BC = 'b-c', BOTTOM = 'bottom',
BR = 'b-r', RIGHT_BOTTOM = 'right-bottom',
ANY = 'any',
}
export enum OperatePoints {
LEFT_TOP = 1,
TOP = 2,
RIGHT_TOP = 3,
LEFT = 4,
RIGHT = 5,
LEFT_BOTTOM = 6,
BOTTOM = 7,
RIGHT_BOTTOM = 8,
} }
export interface AlignmentLineAxis { export interface AlignmentLineAxis {

View File

@ -9,11 +9,11 @@
> >
<BorderLine v-for="line in borderLines" :key="line.type" :type="line.type" :style="line.style" /> <BorderLine v-for="line in borderLines" :key="line.type" :type="line.type" :style="line.style" />
<template v-if="!disableResizablePoint"> <template v-if="!disableResize">
<ResizablePoint <ResizeHandler
v-for="point in resizablePoints" v-for="point in resizeHandlers"
:key="point.type" :key="point.direction"
:type="point.type" :type="point.direction"
:style="point.style" :style="point.style"
@mousedown.stop="scaleMultiElement($event, { minX, maxX, minY, maxY }, point.direction)" @mousedown.stop="scaleMultiElement($event, { minX, maxX, minY, maxY }, point.direction)"
/> />
@ -27,15 +27,16 @@ import { useStore } from 'vuex'
import { State } from '@/store' import { State } from '@/store'
import { PPTElement, ElementTypes } from '@/types/slides' import { PPTElement, ElementTypes } from '@/types/slides'
import { getElementListRange } from '@/utils/element' import { getElementListRange } from '@/utils/element'
import { ElementScaleHandler, OperateResizablePointTypes, OperateBorderLineTypes, MultiSelectRange, OperatePoints } from '@/types/edit' import { OperateResizeHandler, MultiSelectRange } from '@/types/edit'
import useCommonOperate from '@/views/_common/_element/hooks/useCommonOperate'
import ResizablePoint from '@/views/_common/_operate/ResizablePoint.vue' import ResizeHandler from '@/views/_common/_operate/ResizeHandler.vue'
import BorderLine from '@/views/_common/_operate/BorderLine.vue' import BorderLine from '@/views/_common/_operate/BorderLine.vue'
export default defineComponent({ export default defineComponent({
name: 'multi-select-operate', name: 'multi-select-operate',
components: { components: {
ResizablePoint, ResizeHandler,
BorderLine, BorderLine,
}, },
props: { props: {
@ -44,7 +45,7 @@ export default defineComponent({
required: true, required: true,
}, },
scaleMultiElement: { scaleMultiElement: {
type: Function as PropType<(e: MouseEvent, range: MultiSelectRange, command: ElementScaleHandler) => void>, type: Function as PropType<(e: MouseEvent, range: MultiSelectRange, command: OperateResizeHandler) => void>,
required: true, required: true,
}, },
}, },
@ -63,30 +64,9 @@ export default defineComponent({
const width = computed(() => (range.maxX - range.minX) * canvasScale.value) const width = computed(() => (range.maxX - range.minX) * canvasScale.value)
const height = computed(() => (range.maxY - range.minY) * canvasScale.value) const height = computed(() => (range.maxY - range.minY) * canvasScale.value)
const { resizeHandlers, borderLines } = useCommonOperate(width, height)
const resizablePoints = computed(() => { const disableResize = computed(() => {
return [
{ type: OperateResizablePointTypes.TL, direction: OperatePoints.LEFT_TOP, style: {} },
{ type: OperateResizablePointTypes.TC, direction: OperatePoints.TOP, style: {left: width.value / 2 + 'px'} },
{ type: OperateResizablePointTypes.TR, direction: OperatePoints.RIGHT_TOP, style: {left: width.value + 'px'} },
{ type: OperateResizablePointTypes.ML, direction: OperatePoints.LEFT, style: {top: height.value / 2 + 'px'} },
{ type: OperateResizablePointTypes.MR, direction: OperatePoints.RIGHT, style: {left: width.value + 'px', top: height.value / 2 + 'px'} },
{ type: OperateResizablePointTypes.BL, direction: OperatePoints.LEFT_BOTTOM, style: {top: height.value + 'px'} },
{ type: OperateResizablePointTypes.BC, direction: OperatePoints.BOTTOM, style: {left: width.value / 2 + 'px', top: height.value + 'px'} },
{ type: OperateResizablePointTypes.BR, direction: OperatePoints.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 localActiveElementList.value.some(item => { return localActiveElementList.value.some(item => {
if( if(
(item.type === ElementTypes.IMAGE || item.type === ElementTypes.SHAPE) && (item.type === ElementTypes.IMAGE || item.type === ElementTypes.SHAPE) &&
@ -110,8 +90,8 @@ export default defineComponent({
...toRefs(range), ...toRefs(range),
canvasScale, canvasScale,
borderLines, borderLines,
disableResizablePoint, disableResize,
resizablePoints, resizeHandlers,
} }
}, },
}) })

View File

@ -2,7 +2,7 @@ import { computed, Ref } from 'vue'
import { useStore } from 'vuex' import { useStore } from 'vuex'
import { State, MutationTypes } from '@/store' import { State, MutationTypes } from '@/store'
import { ElementTypes, PPTElement, PPTImageElement, PPTLineElement, PPTShapeElement } from '@/types/slides' import { ElementTypes, PPTElement, PPTImageElement, PPTLineElement, PPTShapeElement } from '@/types/slides'
import { OperatePoints, ElementScaleHandler, AlignmentLineProps, MultiSelectRange } from '@/types/edit' import { OperateResizeHandlers, AlignmentLineProps, MultiSelectRange } from '@/types/edit'
import { VIEWPORT_SIZE, VIEWPORT_ASPECT_RATIO } from '@/configs/canvas' import { VIEWPORT_SIZE, VIEWPORT_ASPECT_RATIO } from '@/configs/canvas'
import { AlignLine, uniqAlignLines } from '@/utils/element' import { AlignLine, uniqAlignLines } from '@/utils/element'
import useHistorySnapshot from '@/hooks/useHistorySnapshot' import useHistorySnapshot from '@/hooks/useHistorySnapshot'
@ -68,16 +68,16 @@ const getRotateElementPoints = (element: RotateElementData, angle: number) => {
} }
// 获取元素某个操作点对角线上另一端的操作点坐标(例如:左上 <-> 右下) // 获取元素某个操作点对角线上另一端的操作点坐标(例如:左上 <-> 右下)
const getOppositePoint = (direction: number, points: ReturnType<typeof getRotateElementPoints>): { left: number; top: number } => { const getOppositePoint = (direction: string, points: ReturnType<typeof getRotateElementPoints>): { left: number; top: number } => {
const oppositeMap = { const oppositeMap = {
[OperatePoints.RIGHT_BOTTOM]: points.leftTopPoint, [OperateResizeHandlers.RIGHT_BOTTOM]: points.leftTopPoint,
[OperatePoints.LEFT_BOTTOM]: points.rightTopPoint, [OperateResizeHandlers.LEFT_BOTTOM]: points.rightTopPoint,
[OperatePoints.LEFT_TOP]: points.rightBottomPoint, [OperateResizeHandlers.LEFT_TOP]: points.rightBottomPoint,
[OperatePoints.RIGHT_TOP]: points.leftBottomPoint, [OperateResizeHandlers.RIGHT_TOP]: points.leftBottomPoint,
[OperatePoints.TOP]: points.bottomPoint, [OperateResizeHandlers.TOP]: points.bottomPoint,
[OperatePoints.BOTTOM]: points.topPoint, [OperateResizeHandlers.BOTTOM]: points.topPoint,
[OperatePoints.LEFT]: points.rightPoint, [OperateResizeHandlers.LEFT]: points.rightPoint,
[OperatePoints.RIGHT]: points.leftPoint, [OperateResizeHandlers.RIGHT]: points.leftPoint,
} }
return oppositeMap[direction] return oppositeMap[direction]
} }
@ -94,7 +94,7 @@ export default (
const { addHistorySnapshot } = useHistorySnapshot() const { addHistorySnapshot } = useHistorySnapshot()
const scaleElement = (e: MouseEvent, element: Exclude<PPTElement, PPTLineElement>, command: ElementScaleHandler) => { const scaleElement = (e: MouseEvent, element: Exclude<PPTElement, PPTLineElement>, command: OperateResizeHandlers) => {
let isMouseDown = true let isMouseDown = true
const elOriginLeft = element.left const elOriginLeft = element.left
@ -237,45 +237,45 @@ export default (
// 锁定宽高比例 // 锁定宽高比例
if(fixedRatio) { if(fixedRatio) {
if(command === OperatePoints.RIGHT_BOTTOM || command === OperatePoints.LEFT_TOP) revisedY = revisedX / aspectRatio if(command === OperateResizeHandlers.RIGHT_BOTTOM || command === OperateResizeHandlers.LEFT_TOP) revisedY = revisedX / aspectRatio
if(command === OperatePoints.LEFT_BOTTOM || command === OperatePoints.RIGHT_TOP) revisedY = -revisedX / aspectRatio if(command === OperateResizeHandlers.LEFT_BOTTOM || command === OperateResizeHandlers.RIGHT_TOP) revisedY = -revisedX / aspectRatio
} }
// 根据不同的操作点分别计算元素缩放后的大小和位置 // 根据不同的操作点分别计算元素缩放后的大小和位置
// 这里计算的位置是错误的,因为旋转后缩放实际上也改变了元素的位置,需要在后面进行矫正 // 这里计算的位置是错误的,因为旋转后缩放实际上也改变了元素的位置,需要在后面进行矫正
// 这里计算的大小是正确的,因为上面修正鼠标按下后移动的距离时其实已经进行过了矫正 // 这里计算的大小是正确的,因为上面修正鼠标按下后移动的距离时其实已经进行过了矫正
if(command === OperatePoints.RIGHT_BOTTOM) { if(command === OperateResizeHandlers.RIGHT_BOTTOM) {
width = getSizeWithinRange(elOriginWidth + revisedX) width = getSizeWithinRange(elOriginWidth + revisedX)
height = getSizeWithinRange(elOriginHeight + revisedY) height = getSizeWithinRange(elOriginHeight + revisedY)
} }
else if(command === OperatePoints.LEFT_BOTTOM) { else if(command === OperateResizeHandlers.LEFT_BOTTOM) {
width = getSizeWithinRange(elOriginWidth - revisedX) width = getSizeWithinRange(elOriginWidth - revisedX)
height = getSizeWithinRange(elOriginHeight + revisedY) height = getSizeWithinRange(elOriginHeight + revisedY)
left = elOriginLeft - (width - elOriginWidth) left = elOriginLeft - (width - elOriginWidth)
} }
else if(command === OperatePoints.LEFT_TOP) { else if(command === OperateResizeHandlers.LEFT_TOP) {
width = getSizeWithinRange(elOriginWidth - revisedX) width = getSizeWithinRange(elOriginWidth - revisedX)
height = getSizeWithinRange(elOriginHeight - revisedY) height = getSizeWithinRange(elOriginHeight - revisedY)
left = elOriginLeft - (width - elOriginWidth) left = elOriginLeft - (width - elOriginWidth)
top = elOriginTop - (height - elOriginHeight) top = elOriginTop - (height - elOriginHeight)
} }
else if(command === OperatePoints.RIGHT_TOP) { else if(command === OperateResizeHandlers.RIGHT_TOP) {
width = getSizeWithinRange(elOriginWidth + revisedX) width = getSizeWithinRange(elOriginWidth + revisedX)
height = getSizeWithinRange(elOriginHeight - revisedY) height = getSizeWithinRange(elOriginHeight - revisedY)
top = elOriginTop - (height - elOriginHeight) top = elOriginTop - (height - elOriginHeight)
} }
else if(command === OperatePoints.TOP) { else if(command === OperateResizeHandlers.TOP) {
height = getSizeWithinRange(elOriginHeight - revisedY) height = getSizeWithinRange(elOriginHeight - revisedY)
top = elOriginTop - (height - elOriginHeight) top = elOriginTop - (height - elOriginHeight)
} }
else if(command === OperatePoints.BOTTOM) { else if(command === OperateResizeHandlers.BOTTOM) {
height = getSizeWithinRange(elOriginHeight + revisedY) height = getSizeWithinRange(elOriginHeight + revisedY)
} }
else if(command === OperatePoints.LEFT) { else if(command === OperateResizeHandlers.LEFT) {
width = getSizeWithinRange(elOriginWidth - revisedX) width = getSizeWithinRange(elOriginWidth - revisedX)
left = elOriginLeft - (width - elOriginWidth) left = elOriginLeft - (width - elOriginWidth)
} }
else if(command === OperatePoints.RIGHT) { else if(command === OperateResizeHandlers.RIGHT) {
width = getSizeWithinRange(elOriginWidth + revisedX) width = getSizeWithinRange(elOriginWidth + revisedX)
} }
@ -298,11 +298,11 @@ export default (
let moveY = y / canvasScale.value let moveY = y / canvasScale.value
if(fixedRatio) { if(fixedRatio) {
if(command === OperatePoints.RIGHT_BOTTOM || command === OperatePoints.LEFT_TOP) moveY = moveX / aspectRatio if(command === OperateResizeHandlers.RIGHT_BOTTOM || command === OperateResizeHandlers.LEFT_TOP) moveY = moveX / aspectRatio
if(command === OperatePoints.LEFT_BOTTOM || command === OperatePoints.RIGHT_TOP) moveY = -moveX / aspectRatio if(command === OperateResizeHandlers.LEFT_BOTTOM || command === OperateResizeHandlers.RIGHT_TOP) moveY = -moveX / aspectRatio
} }
if(command === OperatePoints.RIGHT_BOTTOM) { if(command === OperateResizeHandlers.RIGHT_BOTTOM) {
const { offsetX, offsetY } = alignedAdsorption(elOriginLeft + elOriginWidth + moveX, elOriginTop + elOriginHeight + moveY) const { offsetX, offsetY } = alignedAdsorption(elOriginLeft + elOriginWidth + moveX, elOriginTop + elOriginHeight + moveY)
moveX = moveX - offsetX moveX = moveX - offsetX
moveY = moveY - offsetY moveY = moveY - offsetY
@ -313,7 +313,7 @@ export default (
width = getSizeWithinRange(elOriginWidth + moveX) width = getSizeWithinRange(elOriginWidth + moveX)
height = getSizeWithinRange(elOriginHeight + moveY) height = getSizeWithinRange(elOriginHeight + moveY)
} }
else if(command === OperatePoints.LEFT_BOTTOM) { else if(command === OperateResizeHandlers.LEFT_BOTTOM) {
const { offsetX, offsetY } = alignedAdsorption(elOriginLeft + moveX, elOriginTop + elOriginHeight + moveY) const { offsetX, offsetY } = alignedAdsorption(elOriginLeft + moveX, elOriginTop + elOriginHeight + moveY)
moveX = moveX - offsetX moveX = moveX - offsetX
moveY = moveY - offsetY moveY = moveY - offsetY
@ -325,7 +325,7 @@ export default (
height = getSizeWithinRange(elOriginHeight + moveY) height = getSizeWithinRange(elOriginHeight + moveY)
left = elOriginLeft - (width - elOriginWidth) left = elOriginLeft - (width - elOriginWidth)
} }
else if(command === OperatePoints.LEFT_TOP) { else if(command === OperateResizeHandlers.LEFT_TOP) {
const { offsetX, offsetY } = alignedAdsorption(elOriginLeft + moveX, elOriginTop + moveY) const { offsetX, offsetY } = alignedAdsorption(elOriginLeft + moveX, elOriginTop + moveY)
moveX = moveX - offsetX moveX = moveX - offsetX
moveY = moveY - offsetY moveY = moveY - offsetY
@ -338,7 +338,7 @@ export default (
left = elOriginLeft - (width - elOriginWidth) left = elOriginLeft - (width - elOriginWidth)
top = elOriginTop - (height - elOriginHeight) top = elOriginTop - (height - elOriginHeight)
} }
else if(command === OperatePoints.RIGHT_TOP) { else if(command === OperateResizeHandlers.RIGHT_TOP) {
const { offsetX, offsetY } = alignedAdsorption(elOriginLeft + elOriginWidth + moveX, elOriginTop + moveY) const { offsetX, offsetY } = alignedAdsorption(elOriginLeft + elOriginWidth + moveX, elOriginTop + moveY)
moveX = moveX - offsetX moveX = moveX - offsetX
moveY = moveY - offsetY moveY = moveY - offsetY
@ -350,24 +350,24 @@ export default (
height = getSizeWithinRange(elOriginHeight - moveY) height = getSizeWithinRange(elOriginHeight - moveY)
top = elOriginTop - (height - elOriginHeight) top = elOriginTop - (height - elOriginHeight)
} }
else if(command === OperatePoints.LEFT) { else if(command === OperateResizeHandlers.LEFT) {
const { offsetX } = alignedAdsorption(elOriginLeft + moveX, null) const { offsetX } = alignedAdsorption(elOriginLeft + moveX, null)
moveX = moveX - offsetX moveX = moveX - offsetX
width = getSizeWithinRange(elOriginWidth - moveX) width = getSizeWithinRange(elOriginWidth - moveX)
left = elOriginLeft - (width - elOriginWidth) left = elOriginLeft - (width - elOriginWidth)
} }
else if(command === OperatePoints.RIGHT) { else if(command === OperateResizeHandlers.RIGHT) {
const { offsetX } = alignedAdsorption(elOriginLeft + elOriginWidth + moveX, null) const { offsetX } = alignedAdsorption(elOriginLeft + elOriginWidth + moveX, null)
moveX = moveX - offsetX moveX = moveX - offsetX
width = getSizeWithinRange(elOriginWidth + moveX) width = getSizeWithinRange(elOriginWidth + moveX)
} }
else if(command === OperatePoints.TOP) { else if(command === OperateResizeHandlers.TOP) {
const { offsetY } = alignedAdsorption(null, elOriginTop + moveY) const { offsetY } = alignedAdsorption(null, elOriginTop + moveY)
moveY = moveY - offsetY moveY = moveY - offsetY
height = getSizeWithinRange(elOriginHeight - moveY) height = getSizeWithinRange(elOriginHeight - moveY)
top = elOriginTop - (height - elOriginHeight) top = elOriginTop - (height - elOriginHeight)
} }
else if(command === OperatePoints.BOTTOM) { else if(command === OperateResizeHandlers.BOTTOM) {
const { offsetY } = alignedAdsorption(null, elOriginTop + elOriginHeight + moveY) const { offsetY } = alignedAdsorption(null, elOriginTop + elOriginHeight + moveY)
moveY = moveY - offsetY moveY = moveY - offsetY
height = getSizeWithinRange(elOriginHeight + moveY) height = getSizeWithinRange(elOriginHeight + moveY)
@ -390,7 +390,7 @@ export default (
} }
} }
const scaleMultiElement = (e: MouseEvent, range: MultiSelectRange, command: ElementScaleHandler) => { const scaleMultiElement = (e: MouseEvent, range: MultiSelectRange, command: OperateResizeHandlers) => {
let isMouseDown = true let isMouseDown = true
const { minX, maxX, minY, maxY } = range const { minX, maxX, minY, maxY } = range
@ -415,8 +415,8 @@ export default (
// 锁定宽高比例 // 锁定宽高比例
if(ctrlOrShiftKeyActive.value) { if(ctrlOrShiftKeyActive.value) {
if(command === OperatePoints.RIGHT_BOTTOM || command === OperatePoints.LEFT_TOP) y = x / aspectRatio if(command === OperateResizeHandlers.RIGHT_BOTTOM || command === OperateResizeHandlers.LEFT_TOP) y = x / aspectRatio
if(command === OperatePoints.LEFT_BOTTOM || command === OperatePoints.RIGHT_TOP) y = -x / aspectRatio if(command === OperateResizeHandlers.LEFT_BOTTOM || command === OperateResizeHandlers.RIGHT_TOP) y = -x / aspectRatio
} }
// 获取鼠标缩放时当前所有激活元素的范围 // 获取鼠标缩放时当前所有激活元素的范围
@ -425,32 +425,32 @@ export default (
let currentMinY = minY let currentMinY = minY
let currentMaxY = maxY let currentMaxY = maxY
if(command === OperatePoints.RIGHT_BOTTOM) { if(command === OperateResizeHandlers.RIGHT_BOTTOM) {
currentMaxX = maxX + x currentMaxX = maxX + x
currentMaxY = maxY + y currentMaxY = maxY + y
} }
else if(command === OperatePoints.LEFT_BOTTOM) { else if(command === OperateResizeHandlers.LEFT_BOTTOM) {
currentMinX = minX + x currentMinX = minX + x
currentMaxY = maxY + y currentMaxY = maxY + y
} }
else if(command === OperatePoints.LEFT_TOP) { else if(command === OperateResizeHandlers.LEFT_TOP) {
currentMinX = minX + x currentMinX = minX + x
currentMinY = minY + y currentMinY = minY + y
} }
else if(command === OperatePoints.RIGHT_TOP) { else if(command === OperateResizeHandlers.RIGHT_TOP) {
currentMaxX = maxX + x currentMaxX = maxX + x
currentMinY = minY + y currentMinY = minY + y
} }
else if(command === OperatePoints.TOP) { else if(command === OperateResizeHandlers.TOP) {
currentMinY = minY + y currentMinY = minY + y
} }
else if(command === OperatePoints.BOTTOM) { else if(command === OperateResizeHandlers.BOTTOM) {
currentMaxY = maxY + y currentMaxY = maxY + y
} }
else if(command === OperatePoints.LEFT) { else if(command === OperateResizeHandlers.LEFT) {
currentMinX = minX + x currentMinX = minX + x
} }
else if(command === OperatePoints.RIGHT) { else if(command === OperateResizeHandlers.RIGHT) {
currentMaxX = maxX + x currentMaxX = maxX + x
} }

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="editor"> <div class="hamster-ppt-editor">
<EditorHeader class="layout-header" /> <EditorHeader class="layout-header" />
<div class="layout-content"> <div class="layout-content">
<Thumbnails class="layout-content-left" /> <Thumbnails class="layout-content-left" />
@ -41,7 +41,7 @@ export default defineComponent({
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.editor { .hamster-ppt-editor {
height: 100%; height: 100%;
} }
.layout-header { .layout-header {

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="slide-show"> <div class="hamster-ppt-slide-show">
slide-show slide-show
</div> </div>
</template> </template>

View File

@ -36,7 +36,7 @@ import useOrderElement from '@/hooks/useOrderElement'
import useAlignElementToCanvas from '@/hooks/useAlignElementToCanvas' import useAlignElementToCanvas from '@/hooks/useAlignElementToCanvas'
import useCopyAndPasteElement from '@/hooks/useCopyAndPasteElement' import useCopyAndPasteElement from '@/hooks/useCopyAndPasteElement'
import { ElementOrderCommands, ElementAlignCommands, ElementScaleHandler } from '@/types/edit' import { ElementOrderCommands, ElementAlignCommands, OperateResizeHandler } from '@/types/edit'
import ImageElement from './ImageElement/index.vue' import ImageElement from './ImageElement/index.vue'
import TextElement from './TextElement/index.vue' import TextElement from './TextElement/index.vue'
@ -81,7 +81,7 @@ export default defineComponent({
required: true, required: true,
}, },
scaleElement: { scaleElement: {
type: Function as PropType<(e: MouseEvent, element: Exclude<PPTElement, PPTLineElement>, command: ElementScaleHandler) => void>, type: Function as PropType<(e: MouseEvent, element: Exclude<PPTElement, PPTLineElement>, command: OperateResizeHandler) => void>,
required: true, required: true,
}, },
}, },

View File

@ -37,7 +37,7 @@
:outline="elementInfo.outline" :outline="elementInfo.outline"
/> />
<div class="img-wrapper" :style="{ clipPath: clipShape.style }"> <div class="image-content" :style="{ clipPath: clipShape.style }">
<img <img
:src="elementInfo.src" :src="elementInfo.src"
:draggable="false" :draggable="false"
@ -155,7 +155,7 @@ export default defineComponent({
height: 100%; height: 100%;
position: relative; position: relative;
.img-wrapper { .image-content {
width: 100%; width: 100%;
height: 100%; height: 100%;
overflow: hidden; overflow: hidden;

View File

@ -13,7 +13,7 @@
/> />
<div <div
class="top-img-wrapper" class="top-image-content"
:style="{ :style="{
...topImgWrapperPositionStyle, ...topImgWrapperPositionStyle,
clipPath: clipPath, clipPath: clipPath,
@ -456,7 +456,7 @@ export default defineComponent({
height: 100%; height: 100%;
} }
.top-img-wrapper { .top-image-content {
position: absolute; position: absolute;
overflow: hidden; overflow: hidden;

View File

@ -54,7 +54,7 @@
:createPath="clipShape.createPath" :createPath="clipShape.createPath"
/> />
<div class="img-wrapper" :style="{clipPath: clipShape.style}"> <div class="image-content" :style="{clipPath: clipShape.style}">
<img <img
:src="elementInfo.src" :src="elementInfo.src"
:draggable="false" :draggable="false"
@ -88,16 +88,16 @@
:style="line.style" :style="line.style"
/> />
<template v-if="!elementInfo.lock && (isActiveGroupElement || !isMultiSelect)"> <template v-if="!elementInfo.lock && (isActiveGroupElement || !isMultiSelect)">
<ResizablePoint <ResizeHandler
class="operate-resizable-point" class="operate-resize-handler"
v-for="point in resizablePoints" v-for="point in resizeHandlers"
:key="point.type" :key="point.direction"
:type="point.type" :type="point.direction"
:style="point.style" :style="point.style"
@mousedown.stop="scaleElement($event, elementInfo, point.direction)" @mousedown.stop="scaleElement($event, elementInfo, point.direction)"
/> />
<RotateHandler <RotateHandler
class="operate-rotate-handle" class="operate-rotate-handler"
:style="{left: scaleWidth / 2 + 'px'}" :style="{left: scaleWidth / 2 + 'px'}"
@mousedown.stop="rotateElement(elementInfo)" @mousedown.stop="rotateElement(elementInfo)"
/> />
@ -112,13 +112,13 @@
import { computed, defineComponent, ref, PropType } from 'vue' import { computed, defineComponent, ref, PropType } from 'vue'
import { PPTImageElement } from '@/types/slides' import { PPTImageElement } from '@/types/slides'
import { ElementScaleHandler } from '@/types/edit' import { OperateResizeHandler } from '@/types/edit'
import useCommonOperate from '@/views/_common/_element/hooks/useCommonOperate' import useCommonOperate from '@/views/_common/_element/hooks/useCommonOperate'
import { CLIPPATHS, ClipPathTypes } from '@/configs/imageClip' import { CLIPPATHS, ClipPathTypes } from '@/configs/imageClip'
import RotateHandler from '@/views/_common/_operate/RotateHandler.vue' import RotateHandler from '@/views/_common/_operate/RotateHandler.vue'
import ResizablePoint from '@/views/_common/_operate/ResizablePoint.vue' import ResizeHandler from '@/views/_common/_operate/ResizeHandler.vue'
import BorderLine from '@/views/_common/_operate/BorderLine.vue' import BorderLine from '@/views/_common/_operate/BorderLine.vue'
import AnimationIndex from '@/views/_common/_operate/AnimationIndex.vue' import AnimationIndex from '@/views/_common/_operate/AnimationIndex.vue'
@ -133,7 +133,7 @@ export default defineComponent({
name: 'editable-element-image', name: 'editable-element-image',
components: { components: {
RotateHandler, RotateHandler,
ResizablePoint, ResizeHandler,
BorderLine, BorderLine,
AnimationIndex, AnimationIndex,
ImageClip, ImageClip,
@ -179,7 +179,7 @@ export default defineComponent({
required: true, required: true,
}, },
scaleElement: { scaleElement: {
type: Function as PropType<(e: MouseEvent, element: PPTImageElement, command: ElementScaleHandler) => void>, type: Function as PropType<(e: MouseEvent, element: PPTImageElement, command: OperateResizeHandler) => void>,
required: true, required: true,
}, },
contextmenus: { contextmenus: {
@ -192,7 +192,7 @@ export default defineComponent({
const scaleWidth = computed(() => props.elementInfo.width * props.canvasScale) const scaleWidth = computed(() => props.elementInfo.width * props.canvasScale)
const scaleHeight = computed(() => props.elementInfo.height * props.canvasScale) const scaleHeight = computed(() => props.elementInfo.height * props.canvasScale)
const { resizablePoints, borderLines } = useCommonOperate(scaleWidth, scaleHeight) const { resizeHandlers, borderLines } = useCommonOperate(scaleWidth, scaleHeight)
const isCliping = computed(() => clipingImageElId.value === props.elementInfo.id) const isCliping = computed(() => clipingImageElId.value === props.elementInfo.id)
@ -278,7 +278,7 @@ export default defineComponent({
isCliping, isCliping,
imgPosition, imgPosition,
clipShape, clipShape,
resizablePoints, resizeHandlers,
borderLines, borderLines,
filter, filter,
flip, flip,
@ -313,7 +313,7 @@ export default defineComponent({
position: relative; position: relative;
cursor: move; cursor: move;
.img-wrapper { .image-content {
width: 100%; width: 100%;
height: 100%; height: 100%;
overflow: hidden; overflow: hidden;
@ -334,8 +334,8 @@ export default defineComponent({
&.active { &.active {
.operate-border-line, .operate-border-line,
.operate-resizable-point, .operate-resize-handler,
.operate-rotate-handle { .operate-rotate-handler {
display: block; display: block;
} }
} }
@ -345,8 +345,8 @@ export default defineComponent({
} }
.operate-border-line, .operate-border-line,
.operate-resizable-point, .operate-resize-handler,
.operate-rotate-handle { .operate-rotate-handler {
display: none; display: none;
} }
} }

View File

@ -49,15 +49,15 @@
@mousedown="handleSelectElement($event)" @mousedown="handleSelectElement($event)"
/> />
<template v-if="!elementInfo.lock && (isActiveGroupElement || !isMultiSelect)"> <template v-if="!elementInfo.lock && (isActiveGroupElement || !isMultiSelect)">
<ResizablePoint class="operate-resizable-point" <ResizeHandler class="operate-resize-handler"
v-for="point in resizablePoints" v-for="point in resizeHandlers"
:key="point.type" :key="point.direction"
:type="point.type" :type="point.direction"
:style="point.style" :style="point.style"
@mousedown.stop="scaleElement($event, elementInfo, point.direction)" @mousedown.stop="scaleElement($event, elementInfo, point.direction)"
/> />
<RotateHandler <RotateHandler
class="operate-rotate-handle" class="operate-rotate-handler"
:style="{ left: scaleWidth / 2 + 'px' }" :style="{ left: scaleWidth / 2 + 'px' }"
@mousedown.stop="rotateElement(elementInfo)" @mousedown.stop="rotateElement(elementInfo)"
/> />
@ -72,12 +72,12 @@
import { computed, defineComponent, PropType } from 'vue' import { computed, defineComponent, PropType } from 'vue'
import { PPTTextElement } from '@/types/slides' import { PPTTextElement } from '@/types/slides'
import { ElementScaleHandler } from '@/types/edit' import { OperateResizeHandler } from '@/types/edit'
import useCommonOperate from '@/views/_common/_element/hooks/useCommonOperate' import useCommonOperate from '@/views/_common/_element/hooks/useCommonOperate'
import ElementOutline from '@/views/_common/_element/ElementOutline.vue' import ElementOutline from '@/views/_common/_element/ElementOutline.vue'
import RotateHandler from '@/views/_common/_operate/RotateHandler.vue' import RotateHandler from '@/views/_common/_operate/RotateHandler.vue'
import ResizablePoint from '@/views/_common/_operate/ResizablePoint.vue' import ResizeHandler from '@/views/_common/_operate/ResizeHandler.vue'
import BorderLine from '@/views/_common/_operate/BorderLine.vue' import BorderLine from '@/views/_common/_operate/BorderLine.vue'
import AnimationIndex from '@/views/_common/_operate/AnimationIndex.vue' import AnimationIndex from '@/views/_common/_operate/AnimationIndex.vue'
@ -88,7 +88,7 @@ export default defineComponent({
components: { components: {
ElementOutline, ElementOutline,
RotateHandler, RotateHandler,
ResizablePoint, ResizeHandler,
BorderLine, BorderLine,
AnimationIndex, AnimationIndex,
}, },
@ -130,7 +130,7 @@ export default defineComponent({
required: true, required: true,
}, },
scaleElement: { scaleElement: {
type: Function as PropType<(e: MouseEvent, element: PPTTextElement, command: ElementScaleHandler) => void>, type: Function as PropType<(e: MouseEvent, element: PPTTextElement, command: OperateResizeHandler) => void>,
required: true, required: true,
}, },
contextmenus: { contextmenus: {
@ -141,7 +141,7 @@ export default defineComponent({
const scaleWidth = computed(() => props.elementInfo.width * props.canvasScale) const scaleWidth = computed(() => props.elementInfo.width * props.canvasScale)
const scaleHeight = computed(() => props.elementInfo.height * props.canvasScale) const scaleHeight = computed(() => props.elementInfo.height * props.canvasScale)
const { resizablePoints, borderLines } = useCommonOperate(scaleWidth, scaleHeight) const { resizeHandlers, borderLines } = useCommonOperate(scaleWidth, scaleHeight)
const handleSelectElement = (e: MouseEvent, canMove = true) => { const handleSelectElement = (e: MouseEvent, canMove = true) => {
if(props.elementInfo.lock) return if(props.elementInfo.lock) return
@ -155,7 +155,7 @@ export default defineComponent({
return { return {
scaleWidth, scaleWidth,
resizablePoints, resizeHandlers,
borderLines, borderLines,
handleSelectElement, handleSelectElement,
shadowStyle, shadowStyle,
@ -212,8 +212,8 @@ export default defineComponent({
&.active { &.active {
.operate-border-line, .operate-border-line,
.operate-resizable-point, .operate-resize-handler,
.operate-rotate-handle { .operate-rotate-handles {
display: block; display: block;
} }
} }
@ -223,8 +223,8 @@ export default defineComponent({
} }
.operate-border-line, .operate-border-line,
.operate-resizable-point, .operate-resize-handler,
.operate-rotate-handle { .operate-rotate-handles {
display: none; display: none;
} }
} }

View File

@ -1,33 +1,31 @@
import { computed, Ref } from 'vue' import { computed, Ref } from 'vue'
import { OperatePoints, OperateResizablePointTypes, OperateBorderLineTypes } from '@/types/edit' import { OperateResizeHandlers, OperateBorderLines } from '@/types/edit'
export default (scaleWidth: Ref<number>, scaleHeight: Ref<number>) => { export default (width: Ref<number>, height: Ref<number>) => {
const resizablePoints = computed(() => { const resizeHandlers = computed(() => {
return [ return [
{ type: OperateResizablePointTypes.TL, direction: OperatePoints.LEFT_TOP, style: {} }, { direction: OperateResizeHandlers.LEFT_TOP, style: {} },
{ type: OperateResizablePointTypes.TC, direction: OperatePoints.TOP, style: {left: scaleWidth.value / 2 + 'px'} }, { direction: OperateResizeHandlers.TOP, style: {left: width.value / 2 + 'px'} },
{ type: OperateResizablePointTypes.TR, direction: OperatePoints.RIGHT_TOP, style: {left: scaleWidth.value + 'px'} }, { direction: OperateResizeHandlers.RIGHT_TOP, style: {left: width.value + 'px'} },
{ type: OperateResizablePointTypes.ML, direction: OperatePoints.LEFT, style: {top: scaleHeight.value / 2 + 'px'} }, { direction: OperateResizeHandlers.LEFT, style: {top: height.value / 2 + 'px'} },
{ type: OperateResizablePointTypes.MR, direction: OperatePoints.RIGHT, style: {left: scaleWidth.value + 'px', top: scaleHeight.value / 2 + 'px'} }, { direction: OperateResizeHandlers.RIGHT, style: {left: width.value + 'px', top: height.value / 2 + 'px'} },
{ type: OperateResizablePointTypes.BL, direction: OperatePoints.LEFT_BOTTOM, style: {top: scaleHeight.value + 'px'} }, { direction: OperateResizeHandlers.LEFT_BOTTOM, style: {top: height.value + 'px'} },
{ type: OperateResizablePointTypes.BC, direction: OperatePoints.BOTTOM, style: {left: scaleWidth.value / 2 + 'px', top: scaleHeight.value + 'px'} }, { direction: OperateResizeHandlers.BOTTOM, style: {left: width.value / 2 + 'px', top: height.value + 'px'} },
{ type: OperateResizablePointTypes.BR, direction: OperatePoints.RIGHT_BOTTOM, style: {left: scaleWidth.value + 'px', top: scaleHeight.value + 'px'} }, { direction: OperateResizeHandlers.RIGHT_BOTTOM, style: {left: width.value + 'px', top: height.value + 'px'} },
] ]
}) })
const borderLines = computed(() => { const borderLines = computed(() => {
return [ return [
{ type: OperateBorderLineTypes.T, style: {width: scaleWidth.value + 'px'} }, { type: OperateBorderLines.T, style: {width: width.value + 'px'} },
{ type: OperateBorderLineTypes.B, style: {top: scaleHeight.value + 'px', width: scaleWidth.value + 'px'} }, { type: OperateBorderLines.B, style: {top: height.value + 'px', width: width.value + 'px'} },
{ type: OperateBorderLineTypes.L, style: {height: scaleHeight.value + 'px'} }, { type: OperateBorderLines.L, style: {height: height.value + 'px'} },
{ type: OperateBorderLineTypes.R, style: {left: scaleWidth.value + 'px', height: scaleHeight.value + 'px'} }, { type: OperateBorderLines.R, style: {left: width.value + 'px', height: height.value + 'px'} },
] ]
}) })
return { return {
scaleWidth, resizeHandlers,
scaleHeight,
resizablePoints,
borderLines, borderLines,
} }
} }

View File

@ -4,13 +4,13 @@
<script lang="ts"> <script lang="ts">
import { PropType } from 'vue' import { PropType } from 'vue'
import { OperateBorderLineType } from '@/types/edit' import { OperateBorderLine } from '@/types/edit'
export default { export default {
name: 'border-line', name: 'border-line',
props: { props: {
type: { type: {
type: String as PropType<OperateBorderLineType>, type: String as PropType<OperateBorderLine>,
required: true, required: true,
}, },
isWide: { isWide: {
@ -30,16 +30,16 @@ export default {
top: 0; top: 0;
border: 0 dashed $themeColor; border: 0 dashed $themeColor;
&.t { &.top {
border-top-width: 1px; border-top-width: 1px;
} }
&.b { &.bottom {
border-bottom-width: 1px; border-bottom-width: 1px;
} }
&.l { &.left {
border-left-width: 1px; border-left-width: 1px;
} }
&.r { &.right {
border-right-width: 1px; border-right-width: 1px;
} }
@ -51,25 +51,25 @@ export default {
cursor: move; cursor: move;
} }
&.t::before { &.top::before {
top: -8px; top: -8px;
left: -8px; left: -8px;
width: calc(100% + 16px); width: calc(100% + 16px);
height: 16px; height: 16px;
} }
&.b::before { &.bottom::before {
bottom: -8px; bottom: -8px;
left: -8px; left: -8px;
width: calc(100% + 16px); width: calc(100% + 16px);
height: 16px; height: 16px;
} }
&.l::before { &.left::before {
top: -8px; top: -8px;
left: -8px; left: -8px;
width: 16px; width: 16px;
height: calc(100% + 16px); height: calc(100% + 16px);
} }
&.r::before { &.right::before {
top: -8px; top: -8px;
right: -8px; right: -8px;
width: 16px; width: 16px;

View File

@ -1,16 +1,16 @@
<template> <template>
<div :class="['resizable-point', type]"></div> <div :class="['resize-handler', type]"></div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { PropType } from 'vue' import { PropType } from 'vue'
import { OperateResizablePointType } from '@/types/edit' import { OperateResizeHandler } from '@/types/edit'
export default { export default {
name: 'resizable-point', name: 'resize-handler',
props: { props: {
type: { type: {
type: String as PropType<OperateResizablePointType>, type: String as PropType<OperateResizeHandler>,
required: true, required: true,
}, },
}, },
@ -18,7 +18,7 @@ export default {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.resizable-point { .resize-handler {
position: absolute; position: absolute;
width: 10px; width: 10px;
height: 10px; height: 10px;
@ -29,32 +29,29 @@ export default {
background-color: #fff; background-color: #fff;
border-radius: 50%; border-radius: 50%;
&.t-l { &.left-top {
cursor: nwse-resize; cursor: nwse-resize;
} }
&.t-c { &.top {
cursor: ns-resize; cursor: ns-resize;
} }
&.t-r { &.right-top {
cursor: nesw-resize; cursor: nesw-resize;
} }
&.m-l { &.left {
cursor: ew-resize; cursor: ew-resize;
} }
&.m-r { &.right {
cursor: ew-resize; cursor: ew-resize;
} }
&.b-l { &.left-bottom {
cursor: nesw-resize; cursor: nesw-resize;
} }
&.b-c { &.bottom {
cursor: ns-resize; cursor: ns-resize;
} }
&.b-r { &.right-bottom {
cursor: nwse-resize; cursor: nwse-resize;
} }
&.any {
cursor: pointer;
}
} }
</style> </style>

View File

@ -1,17 +1,17 @@
<template> <template>
<div class="rotate-handle"> <div class="rotate-handler">
<div class="rotate-icon"><IconFont type="icon-rotate" /></div> <div class="rotate-icon"><IconFont type="icon-rotate" /></div>
</div> </div>
</template> </template>
<script> <script>
export default { export default {
name: 'rotate-handle', name: 'rotate-handler',
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.rotate-handle { .rotate-handler {
position: absolute; position: absolute;
top: -24px; top: -24px;
margin: -12px 0 0 -12px; margin: -12px 0 0 -12px;