perf: 代码优化&注释

This commit is contained in:
pipipi-pikachu 2021-02-11 14:13:49 +08:00
parent 1d25d85621
commit d4a8d41394
23 changed files with 307 additions and 226 deletions

View File

@ -54,6 +54,10 @@ export interface ImageElementFilters {
'hue-rotate'?: string;
'opacity'?: string;
}
export interface ImageElementClip {
range: [[number, number], [number, number]];
shape: string;
}
export interface PPTImageElement {
type: 'image';
id: string;
@ -68,10 +72,7 @@ export interface PPTImageElement {
rotate?: number;
outline?: PPTElementOutline;
filters?: ImageElementFilters;
clip?: {
range: [[number, number], [number, number]];
shape: 'rect' | 'roundRect' | 'ellipse' | 'triangle' | 'pentagon' | 'rhombus' | 'star';
};
clip?: ImageElementClip;
flip?: ImageOrShapeFlip;
shadow?: PPTElementShadow;
}

View File

@ -13,7 +13,7 @@
transform: `scale(${scale})`,
}"
>
<div class="background" :style="{ ...backgroundStyle }"></div>
<div class="background" :style="backgroundStyle"></div>
<ThumbnailElement
v-for="(element, index) in slide.elements"
:key="element.id"

View File

@ -101,6 +101,7 @@ export default defineComponent({
onMounted(renderChart)
//
const updateTheme = () => {
if (!chartRef.value) return
@ -116,15 +117,19 @@ export default defineComponent({
}
chartRef.value.style.setProperty(`--theme-color-${i + 1}`, tinycolor(_hsla).toRgbString())
}
}
watch(() => props.themeColor, updateTheme)
onMounted(updateTheme)
//
const updateGridColor = () => {
if (!chartRef.value) return
if (props.gridColor) chartRef.value.style.setProperty(`--grid-color`, props.gridColor)
}
watch([
() => props.themeColor,
() => props.gridColor,
], updateTheme)
onMounted(updateTheme)
watch(() => props.gridColor, updateGridColor)
onMounted(updateGridColor)
return {
slideScale,

View File

@ -15,7 +15,7 @@
:d="`M0,0 L${width},0 L${width},${height} L0,${height} Z`"
:stroke="outlineColor"
:stroke-width="outlineWidth"
:stroke-dasharray="outlineStyle === 'dashed' ? '12 9' : '0 0'"
:stroke-dasharray="outlineStyle === 'dashed' ? '10 6' : '0 0'"
></path>
</SvgWrapper>
</template>

View File

@ -16,26 +16,7 @@
transform: flipStyle,
}"
>
<ImageRectOutline
v-if="clipShape.type === 'rect'"
:width="elementInfo.width"
:height="elementInfo.height"
:radius="clipShape.radius"
:outline="elementInfo.outline"
/>
<ImageEllipseOutline
v-else-if="clipShape.type === 'ellipse'"
:width="elementInfo.width"
:height="elementInfo.height"
:outline="elementInfo.outline"
/>
<ImagePolygonOutline
v-else-if="clipShape.type === 'polygon'"
:width="elementInfo.width"
:height="elementInfo.height"
:createPath="clipShape.createPath"
:outline="elementInfo.outline"
/>
<ImageOutline :elementInfo="elementInfo" />
<div class="image-content" :style="{ clipPath: clipShape.style }">
<img
@ -57,23 +38,18 @@
<script lang="ts">
import { computed, defineComponent, PropType } from 'vue'
import { PPTImageElement } from '@/types/slides'
import { CLIPPATHS, ClipPathTypes } from '@/configs/imageClip'
import ImageRectOutline from './ImageRectOutline.vue'
import ImageEllipseOutline from './ImageEllipseOutline.vue'
import ImagePolygonOutline from './ImagePolygonOutline.vue'
import useElementShadow from '@/views/components/element/hooks/useElementShadow'
import useElementFlip from '@/views/components/element/hooks/useElementFlip'
import useClipImage from './useClipImage'
import useFilter from './useFilter'
import ImageOutline from './ImageOutline/index.vue'
export default defineComponent({
name: 'base-element-image',
components: {
ImageRectOutline,
ImageEllipseOutline,
ImagePolygonOutline,
ImageOutline,
},
props: {
elementInfo: {
@ -82,59 +58,24 @@ export default defineComponent({
},
},
setup(props) {
const imgPosition = computed(() => {
if (!props.elementInfo || !props.elementInfo.clip) {
return {
top: '0',
left: '0',
width: '100%',
height: '100%',
}
}
const [start, end] = props.elementInfo.clip.range
const widthScale = (end[0] - start[0]) / 100
const heightScale = (end[1] - start[1]) / 100
const left = start[0] / widthScale
const top = start[1] / heightScale
return {
left: -left + '%',
top: -top + '%',
width: 100 / widthScale + '%',
height: 100 / heightScale + '%',
}
})
const clipShape = computed(() => {
if (!props.elementInfo || !props.elementInfo.clip) return CLIPPATHS.rect
const shape = props.elementInfo.clip.shape || ClipPathTypes.RECT
return CLIPPATHS[shape]
})
const filter = computed(() => {
if (!props.elementInfo.filters) return ''
let filter = ''
for (const key of Object.keys(props.elementInfo.filters)) {
filter += `${key}(${props.elementInfo.filters[key]}) `
}
return filter
})
const shadow = computed(() => props.elementInfo.shadow)
const { shadowStyle } = useElementShadow(shadow)
const flip = computed(() => props.elementInfo.flip)
const { flipStyle } = useElementFlip(flip)
const clip = computed(() => props.elementInfo.clip)
const { clipShape, imgPosition } = useClipImage(clip)
const filters = computed(() => props.elementInfo.filters)
const { filter } = useFilter(filters)
return {
imgPosition,
clipShape,
filter,
flipStyle,
shadowStyle,
clipShape,
}
},
})

View File

@ -95,12 +95,6 @@ export default defineComponent({
const canvasScale = computed(() => store.state.canvasScale)
const ctrlOrShiftKeyActive = computed<boolean>(() => store.getters.ctrlOrShiftKeyActive)
const topImgWrapperPosition = reactive({
top: 0,
left: 0,
width: 0,
height: 0,
})
const clipWrapperPositionStyle = reactive({
top: '0',
left: '0',
@ -108,6 +102,7 @@ export default defineComponent({
const isSettingClipRange = ref(false)
const currentRange = ref<ImageClipDataRange | null>(null)
//
const getClipDataTransformInfo = () => {
const [start, end] = props.clipData ? props.clipData.range : [[0, 0], [100, 100]]
@ -118,7 +113,8 @@ export default defineComponent({
return { widthScale, heightScale, left, top }
}
//
const imgPosition = computed(() => {
const { widthScale, heightScale, left, top } = getClipDataTransformInfo()
return {
@ -129,6 +125,7 @@ export default defineComponent({
}
})
//
const bottomImgPositionStyle = computed(() => {
return {
top: imgPosition.value.top + '%',
@ -138,6 +135,15 @@ export default defineComponent({
}
})
//
const topImgWrapperPosition = reactive({
top: 0,
left: 0,
width: 0,
height: 0,
})
//
const topImgWrapperPositionStyle = computed(() => {
return {
top: topImgWrapperPosition.top + '%',
@ -147,6 +153,7 @@ export default defineComponent({
}
})
//
const topImgPositionStyle = computed(() => {
const bottomWidth = imgPosition.value.width
const bottomHeight = imgPosition.value.height
@ -164,6 +171,7 @@ export default defineComponent({
}
})
//
const initClipPosition = () => {
const { left, top } = getClipDataTransformInfo()
topImgWrapperPosition.left = left
@ -175,6 +183,7 @@ export default defineComponent({
clipWrapperPositionStyle.left = -left + '%'
}
//
const handleClip = () => {
if (isSettingClipRange.value) return
@ -199,6 +208,7 @@ export default defineComponent({
emit('clip', clipedEmitData)
}
//
const keyboardListener = (e: KeyboardEvent) => {
const key = e.key.toUpperCase()
if (key === KEYS.ENTER) handleClip()
@ -212,7 +222,8 @@ export default defineComponent({
document.removeEventListener('keydown', keyboardListener)
})
const getRange = () => {
//
const updateRange = () => {
const retPosition = {
left: parseInt(topImgPositionStyle.value.left),
top: parseInt(topImgPositionStyle.value.top),
@ -235,6 +246,7 @@ export default defineComponent({
currentRange.value = [start, end]
}
//
const moveClipRange = (e: MouseEvent) => {
isSettingClipRange.value = true
let isMouseDown = true
@ -261,7 +273,6 @@ export default defineComponent({
let targetLeft = originPositopn.left + moveX
let targetTop = originPositopn.top + moveY
//
if (targetLeft < 0) targetLeft = 0
else if (targetLeft + originPositopn.width > bottomPosition.width) {
targetLeft = bottomPosition.width - originPositopn.width
@ -280,7 +291,7 @@ export default defineComponent({
document.onmousemove = null
document.onmouseup = null
getRange()
updateRange()
setTimeout(() => {
isSettingClipRange.value = false
@ -288,6 +299,7 @@ export default defineComponent({
}
}
//
const scaleClipRange = (e: MouseEvent, type: ScaleClipRangeType) => {
isSettingClipRange.value = true
let isMouseDown = true
@ -323,7 +335,6 @@ export default defineComponent({
let targetLeft, targetTop, targetWidth, targetHeight
//
if (type === 't-l') {
if (originPositopn.left + moveX < 0) {
moveX = -originPositopn.left
@ -408,7 +419,7 @@ export default defineComponent({
document.onmousemove = null
document.onmouseup = null
getRange()
updateRange()
setTimeout(() => isSettingClipRange.value = false, 0)
}

View File

@ -18,7 +18,7 @@
:ry="height / 2"
:stroke="outlineColor"
:stroke-width="outlineWidth"
:stroke-dasharray="outlineStyle === 'dashed' ? '12 9' : '0 0'"
:stroke-dasharray="outlineStyle === 'dashed' ? '10 6' : '0 0'"
></ellipse>
</SvgWrapper>
</template>

View File

@ -15,7 +15,7 @@
:d="createPath(width, height)"
:stroke="outlineColor"
:stroke-width="outlineWidth"
:stroke-dasharray="outlineStyle === 'dashed' ? '12 9' : '0 0'"
:stroke-dasharray="outlineStyle === 'dashed' ? '10 6' : '0 0'"
></path>
</SvgWrapper>
</template>

View File

@ -18,7 +18,7 @@
:height="height"
:stroke="outlineColor"
:stroke-width="outlineWidth"
:stroke-dasharray="outlineStyle === 'dashed' ? '12 9' : '0 0'"
:stroke-dasharray="outlineStyle === 'dashed' ? '10 6' : '0 0'"
></rect>
</SvgWrapper>
</template>

View File

@ -0,0 +1,57 @@
<template>
<div class="image-outline">
<ImageRectOutline
v-if="clipShape.type === 'rect'"
:width="elementInfo.width"
:height="elementInfo.height"
:radius="clipShape.radius"
:outline="elementInfo.outline"
/>
<ImageEllipseOutline
v-else-if="clipShape.type === 'ellipse'"
:width="elementInfo.width"
:height="elementInfo.height"
:outline="elementInfo.outline"
/>
<ImagePolygonOutline
v-else-if="clipShape.type === 'polygon'"
:width="elementInfo.width"
:height="elementInfo.height"
:outline="elementInfo.outline"
:createPath="clipShape.createPath"
/>
</div>
</template>
<script lang="ts">
import { computed, defineComponent, PropType } from 'vue'
import { PPTImageElement } from '@/types/slides'
import useClipImage from '../useClipImage'
import ImageRectOutline from './ImageRectOutline.vue'
import ImageEllipseOutline from './ImageEllipseOutline.vue'
import ImagePolygonOutline from './ImagePolygonOutline.vue'
export default defineComponent({
name: 'image-outline',
components: {
ImageRectOutline,
ImageEllipseOutline,
ImagePolygonOutline,
},
props: {
elementInfo: {
type: Object as PropType<PPTImageElement>,
required: true,
},
},
setup(props) {
const clip = computed(() => props.elementInfo.clip)
const { clipShape } = useClipImage(clip)
return {
clipShape,
}
},
})
</script>

View File

@ -20,7 +20,7 @@
:top="elementInfo.top"
:left="elementInfo.left"
:clipPath="clipShape.style"
@clip="range => clip(range)"
@clip="range => handleClip(range)"
/>
<div
class="element-content"
@ -31,28 +31,9 @@
transform: flipStyle,
}"
>
<ImageRectOutline
v-if="clipShape.type === 'rect'"
:width="elementInfo.width"
:height="elementInfo.height"
:radius="clipShape.radius"
:outline="elementInfo.outline"
/>
<ImageEllipseOutline
v-else-if="clipShape.type === 'ellipse'"
:width="elementInfo.width"
:height="elementInfo.height"
:outline="elementInfo.outline"
/>
<ImagePolygonOutline
v-else-if="clipShape.type === 'polygon'"
:width="elementInfo.width"
:height="elementInfo.height"
:outline="elementInfo.outline"
:createPath="clipShape.createPath"
/>
<ImageOutline :elementInfo="elementInfo" />
<div class="image-content" :style="{clipPath: clipShape.style}">
<div class="image-content" :style="{ clipPath: clipShape.style }">
<img
:src="elementInfo.src"
:draggable="false"
@ -74,23 +55,20 @@
import { computed, defineComponent, PropType } from 'vue'
import { MutationTypes, useStore } from '@/store'
import { PPTImageElement } from '@/types/slides'
import { ImageClipedEmitData } from '@/types/edit'
import { ContextmenuItem } from '@/components/Contextmenu/types'
import { CLIPPATHS, ClipPathTypes } from '@/configs/imageClip'
import useElementShadow from '@/views/components/element/hooks/useElementShadow'
import useElementFlip from '@/views/components/element/hooks/useElementFlip'
import useClipImage from './useClipImage'
import useFilter from './useFilter'
import ImageRectOutline from './ImageRectOutline.vue'
import ImageEllipseOutline from './ImageEllipseOutline.vue'
import ImagePolygonOutline from './ImagePolygonOutline.vue'
import ImageOutline from './ImageOutline/index.vue'
import ImageClipHandler from './ImageClipHandler.vue'
import { ImageClipedEmitData } from '@/types/edit'
export default defineComponent({
name: 'editable-element-image',
components: {
ImageRectOutline,
ImageEllipseOutline,
ImagePolygonOutline,
ImageOutline,
ImageClipHandler,
},
props: {
@ -117,53 +95,19 @@ export default defineComponent({
const flip = computed(() => props.elementInfo.flip)
const { flipStyle } = useElementFlip(flip)
const clip = computed(() => props.elementInfo.clip)
const { clipShape, imgPosition } = useClipImage(clip)
const filters = computed(() => props.elementInfo.filters)
const { filter } = useFilter(filters)
const handleSelectElement = (e: MouseEvent) => {
if (props.elementInfo.lock) return
e.stopPropagation()
props.selectElement(e, props.elementInfo)
}
const clipShape = computed(() => {
if (!props.elementInfo || !props.elementInfo.clip) return CLIPPATHS.rect
const shape = props.elementInfo.clip.shape || ClipPathTypes.RECT
return CLIPPATHS[shape]
})
const imgPosition = computed(() => {
if (!props.elementInfo || !props.elementInfo.clip) {
return {
top: '0',
left: '0',
width: '100%',
height: '100%',
}
}
const [start, end] = props.elementInfo.clip.range
const widthScale = (end[0] - start[0]) / 100
const heightScale = (end[1] - start[1]) / 100
const left = start[0] / widthScale
const top = start[1] / heightScale
return {
left: -left + '%',
top: -top + '%',
width: 100 / widthScale + '%',
height: 100 / heightScale + '%',
}
})
const filter = computed(() => {
if (!props.elementInfo.filters) return ''
let filter = ''
for (const key of Object.keys(props.elementInfo.filters)) {
filter += `${key}(${props.elementInfo.filters[key]}) `
}
return filter
})
const clip = (data: ImageClipedEmitData) => {
const handleClip = (data: ImageClipedEmitData) => {
store.commit(MutationTypes.SET_CLIPING_IMAGE_ELEMENT_ID, '')
if (!data) return
@ -183,7 +127,7 @@ export default defineComponent({
return {
isCliping,
clip,
handleClip,
clipingImageElementId,
shadowStyle,
handleSelectElement,

View File

@ -0,0 +1,42 @@
import { computed, Ref } from 'vue'
import { CLIPPATHS, ClipPathTypes } from '@/configs/imageClip'
import { ImageElementClip } from '@/types/slides'
export default (clip: Ref<ImageElementClip | undefined>) => {
const clipShape = computed(() => {
if (!clip.value) return CLIPPATHS.rect
const shape = clip.value.shape || ClipPathTypes.RECT
return CLIPPATHS[shape]
})
const imgPosition = computed(() => {
if (!clip.value) {
return {
top: '0',
left: '0',
width: '100%',
height: '100%',
}
}
const [start, end] = clip.value.range
const widthScale = (end[0] - start[0]) / 100
const heightScale = (end[1] - start[1]) / 100
const left = start[0] / widthScale
const top = start[1] / heightScale
return {
left: -left + '%',
top: -top + '%',
width: 100 / widthScale + '%',
height: 100 / heightScale + '%',
}
})
return {
clipShape,
imgPosition,
}
}

View File

@ -0,0 +1,17 @@
import { computed, Ref } from 'vue'
import { ImageElementFilters } from '@/types/slides'
export default (filters: Ref<ImageElementFilters | undefined>) => {
const filter = computed(() => {
if (!filters.value) return ''
let filter = ''
for (const key of Object.keys(filters.value)) {
filter += `${key}(${filters.value[key]}) `
}
return filter
})
return {
filter,
}
}

View File

@ -82,6 +82,7 @@ export default defineComponent({
})
const lineDashArray = computed(() => props.elementInfo.style === 'dashed' ? '10, 5' : '0, 0')
const path = computed(() => {
const start = props.elementInfo.start.join(',')
const end = props.elementInfo.end.join(',')

View File

@ -106,7 +106,8 @@ export default defineComponent({
return height < 24 ? 24 : height
})
const lineDashArray = computed(() => props.elementInfo.style === 'dashed' ? '10, 5' : '0, 0')
const lineDashArray = computed(() => props.elementInfo.style === 'dashed' ? '10 6' : '0 0')
const path = computed(() => {
const start = props.elementInfo.start.join(',')
const end = props.elementInfo.end.join(',')

View File

@ -46,7 +46,7 @@
:fill="elementInfo.gradient ? `url(#editabel-gradient-${elementInfo.id})` : elementInfo.fill"
:stroke="outlineColor"
:stroke-width="outlineWidth"
:stroke-dasharray="outlineStyle === 'dashed' ? '10 5' : '0 0'"
:stroke-dasharray="outlineStyle === 'dashed' ? '10 6' : '0 0'"
></path>
</g>
</SvgWrapper>

View File

@ -30,6 +30,8 @@ export default defineComponent({
const text = ref('')
const isFocus = ref(false)
// v-modal
//
watch(() => props.modelValue, () => {
if (isFocus.value) return
text.value = props.modelValue
@ -42,6 +44,7 @@ export default defineComponent({
emit('update:modelValue', text)
}
//
const handleFocus = () => {
isFocus.value = true
@ -58,11 +61,13 @@ export default defineComponent({
}
}
//
const handleBlur = () => {
isFocus.value = false
if (textareaRef.value) textareaRef.value.onpaste = null
}
//
onUnmounted(() => {
if (textareaRef.value) textareaRef.value.onpaste = null
})

View File

@ -113,7 +113,21 @@ export default defineComponent({
setup(props, { emit }) {
const store = useStore()
const canvasScale = computed(() => store.state.canvasScale)
const isStartSelect = ref(false)
const startCell = ref<number[]>([])
const endCell = ref<number[]>([])
const tableCells = computed<TableCell[][]>({
get() {
return props.data
},
set(newData) {
emit('change', newData)
},
})
//
const subThemeColor = ref(['', ''])
watch(() => props.theme, () => {
if (props.theme) {
@ -127,15 +141,7 @@ export default defineComponent({
}
}, { immediate: true })
const tableCells = computed<TableCell[][]>({
get() {
return props.data
},
set(newData) {
emit('change', newData)
},
})
//
const colSizeList = ref<number[]>([])
const totalWidth = computed(() => colSizeList.value.reduce((a, b) => a + b))
watch([
@ -145,10 +151,8 @@ export default defineComponent({
colSizeList.value = props.colWidths.map(item => item * props.width)
}, { immediate: true })
const isStartSelect = ref(false)
const startCell = ref<number[]>([])
const endCell = ref<number[]>([])
//
//
const removeSelectedCells = () => {
startCell.value = []
endCell.value = []
@ -158,6 +162,7 @@ export default defineComponent({
if (!props.editable) removeSelectedCells()
})
//
const dragLinePosition = computed(() => {
const dragLinePosition: number[] = []
for (let i = 1; i < colSizeList.value.length + 1; i++) {
@ -167,6 +172,7 @@ export default defineComponent({
return dragLinePosition
})
//
const hideCells = computed(() => {
const hideCells = []
@ -188,6 +194,7 @@ export default defineComponent({
return hideCells
})
//
const selectedCells = computed(() => {
if (!startCell.value.length) return []
const [startX, startY] = startCell.value
@ -217,11 +224,13 @@ export default defineComponent({
emit('changeSelectedCells', selectedCells.value)
})
//
const activedCell = computed(() => {
if (selectedCells.value.length > 1) return null
return selectedCells.value[0]
})
//
const selectedRange = computed(() => {
if (!startCell.value.length) return null
const [startX, startY] = startCell.value
@ -242,6 +251,7 @@ export default defineComponent({
}
})
//
const handleMouseup = () => isStartSelect.value = false
const handleCellMousedown = (e: MouseEvent, rowIndex: number, colIndex: number) => {
@ -264,20 +274,24 @@ export default defineComponent({
document.removeEventListener('mouseup', handleMouseup)
})
//
const isHideCell = (rowIndex: number, colIndex: number) => hideCells.value.includes(`${rowIndex}_${colIndex}`)
//
const selectCol = (index: number) => {
const maxRow = tableCells.value.length - 1
startCell.value = [0, index]
endCell.value = [maxRow, index]
}
//
const selectRow = (index: number) => {
const maxCol = tableCells.value[index].length - 1
startCell.value = [index, 0]
endCell.value = [index, maxCol]
}
//
const selectAll = () => {
const maxRow = tableCells.value.length - 1
const maxCol = tableCells.value[maxRow].length - 1
@ -285,6 +299,7 @@ export default defineComponent({
endCell.value = [maxRow, maxCol]
}
//
const deleteRow = (rowIndex: number) => {
const _tableCells: TableCell[][] = JSON.parse(JSON.stringify(tableCells.value))
@ -307,6 +322,7 @@ export default defineComponent({
tableCells.value = _tableCells
}
//
const deleteCol = (colIndex: number) => {
const _tableCells: TableCell[][] = JSON.parse(JSON.stringify(tableCells.value))
@ -332,6 +348,7 @@ export default defineComponent({
emit('changeColWidths', colSizeList.value)
}
//
const insertRow = (rowIndex: number) => {
const _tableCells: TableCell[][] = JSON.parse(JSON.stringify(tableCells.value))
@ -349,6 +366,7 @@ export default defineComponent({
tableCells.value = _tableCells
}
//
const insertCol = (colIndex: number) => {
tableCells.value = tableCells.value.map(item => {
const cell = {
@ -364,6 +382,7 @@ export default defineComponent({
emit('changeColWidths', colSizeList.value)
}
//
const mergeCells = () => {
const [startX, startY] = startCell.value
const [endX, endY] = endCell.value
@ -382,6 +401,7 @@ export default defineComponent({
removeSelectedCells()
}
//
const splitCells = (rowIndex: number, colIndex: number) => {
const _tableCells: TableCell[][] = JSON.parse(JSON.stringify(tableCells.value))
_tableCells[rowIndex][colIndex].rowspan = 1
@ -391,6 +411,7 @@ export default defineComponent({
removeSelectedCells()
}
//
const handleMousedownColHandler = (e: MouseEvent, colIndex: number) => {
removeSelectedCells()
let isMouseDown = true
@ -417,6 +438,7 @@ export default defineComponent({
}
}
//
const clearSelectedCellText = () => {
const _tableCells: TableCell[][] = JSON.parse(JSON.stringify(tableCells.value))
@ -430,6 +452,10 @@ export default defineComponent({
tableCells.value = _tableCells
}
//
//
//
//
const tabActiveCell = () => {
const getNextCell = (i: number, j: number): [number, number] | null => {
if (!tableCells.value[i]) return null
@ -450,12 +476,14 @@ export default defineComponent({
}
else startCell.value = nextCell
//
nextTick(() => {
const textRef = document.querySelector('.cell-text.active') as HTMLInputElement
if (textRef) textRef.focus()
})
}
//
const keydownListener = (e: KeyboardEvent) => {
if (!props.editable || !selectedCells.value.length) return
@ -498,6 +526,7 @@ export default defineComponent({
document.removeEventListener('keydown', keydownListener)
})
//
const getTextStyle = (style?: TableCellStyle) => {
if (!style) return {}
const {
@ -524,10 +553,12 @@ export default defineComponent({
}
}
//
const handleInput = debounce(function() {
emit('change', tableCells.value)
}, 300, { trailing: true })
//
const getEffectiveTableCells = () => {
const effectiveTableCells = []
@ -543,6 +574,7 @@ export default defineComponent({
return effectiveTableCells
}
// /1
const checkCanDeleteRowOrCol = () => {
const effectiveTableCells = getEffectiveTableCells()
const canDeleteRow = effectiveTableCells.length > 1
@ -551,6 +583,9 @@ export default defineComponent({
return { canDeleteRow, canDeleteCol }
}
//
//
//
const checkCanMergeOrSplit = (rowIndex: number, colIndex: number) => {
const isMultiSelected = selectedCells.value.length > 1
const targetCell = tableCells.value[rowIndex][colIndex]
@ -717,7 +752,7 @@ table {
position: absolute;
top: 0;
left: 0;
background-color: rgba($color: $themeColor, $alpha: .3);
background-color: rgba($color: #666, $alpha: .4);
}
}

View File

@ -69,6 +69,9 @@ export default defineComponent({
setup(props) {
const store = useStore()
const canvasScale = computed(() => store.state.canvasScale)
const handleElementId = computed(() => store.state.handleElementId)
const elementRef = ref<HTMLElement>()
const { addHistorySnapshot } = useHistorySnapshot()
@ -78,8 +81,9 @@ export default defineComponent({
props.selectElement(e, props.elementInfo)
}
//
const editable = ref(false)
const handleElementId = computed(() => store.state.handleElementId)
watch(handleElementId, () => {
if (handleElementId.value !== props.elementInfo.id) editable.value = false
@ -88,9 +92,13 @@ export default defineComponent({
watch(editable, () => {
store.commit(MutationTypes.SET_DISABLE_HOTKEYS_STATE, editable.value)
})
const elementRef = ref<HTMLElement>()
const startEdit = () => {
if (!props.elementInfo.lock) editable.value = true
}
// vuex
//
const isScaling = ref(false)
const realHeightCache = ref(-1)
@ -139,6 +147,7 @@ export default defineComponent({
if (elementRef.value) resizeObserver.unobserve(elementRef.value)
})
//
const updateTableCells = (data: TableCell[][]) => {
store.commit(MutationTypes.UPDATE_ELEMENT, {
id: props.elementInfo.id,
@ -146,6 +155,8 @@ export default defineComponent({
})
addHistorySnapshot()
}
//
const updateColWidths = (widths: number[]) => {
const width = widths.reduce((a, b) => a + b)
const colWidths = widths.map(item => item / width)
@ -157,14 +168,11 @@ export default defineComponent({
addHistorySnapshot()
}
//
const updateSelectedCells = (cells: string[]) => {
nextTick(() => emitter.emit(EmitterEvents.UPDATE_TABLE_SELECTED_CELL, cells))
}
const startEdit = () => {
if (!props.elementInfo.lock) editable.value = true
}
return {
elementRef,
canvasScale,

View File

@ -86,6 +86,23 @@ export default defineComponent({
const isScaling = ref(false)
const realHeightCache = ref(-1)
const editorViewRef = ref<HTMLElement>()
let editorView: EditorView
const shadow = computed(() => props.elementInfo.shadow)
const { shadowStyle } = useElementShadow(shadow)
const handleElementId = computed(() => store.state.handleElementId)
const handleSelectElement = (e: MouseEvent, canMove = true) => {
if (props.elementInfo.lock) return
e.stopPropagation()
props.selectElement(e, props.elementInfo, canMove)
}
// vuex
//
const scaleElementStateListener = (state: boolean) => {
isScaling.value = state
@ -127,10 +144,11 @@ export default defineComponent({
onUnmounted(() => {
if (elementRef.value) resizeObserver.unobserve(elementRef.value)
})
const editorViewRef = ref<HTMLElement>()
let editorView: EditorView
//
//
// vuex
//
const handleFocus = () => {
store.commit(MutationTypes.SET_DISABLE_HOTKEYS_STATE, true)
}
@ -155,6 +173,7 @@ export default defineComponent({
handleClick()
}
// DOM
const textContent = computed(() => props.elementInfo.content)
watch(textContent, () => {
if (!editorView) return
@ -162,11 +181,13 @@ export default defineComponent({
editorView.dom.innerHTML = textContent.value
})
// /
const editable = computed(() => !props.elementInfo.lock)
watch(editable, () => {
editorView.setProps({ editable: () => editable.value })
})
// Prosemirror
onMounted(() => {
editorView = initProsemirrorEditor((editorViewRef.value as Element), textContent.value, {
handleDOMEvents: {
@ -181,19 +202,9 @@ export default defineComponent({
onUnmounted(() => {
editorView && editorView.destroy()
})
const handleSelectElement = (e: MouseEvent, canMove = true) => {
if (props.elementInfo.lock) return
e.stopPropagation()
props.selectElement(e, props.elementInfo, canMove)
}
const shadow = computed(() => props.elementInfo.shadow)
const { shadowStyle } = useElementShadow(shadow)
const handleElementId = computed(() => store.state.handleElementId)
//
//
const execCommand = (payload: CommandPayload | CommandPayload[]) => {
if (handleElementId.value !== props.elementInfo.id) return

View File

@ -1,18 +1,20 @@
import { ref, Ref, watchEffect } from 'vue'
import { computed, Ref } from 'vue'
import { ImageOrShapeFlip } from '@/types/slides'
// 计算元素的翻转样式
export default (flip: Ref<ImageOrShapeFlip | undefined>) => {
const flipStyle = ref('')
watchEffect(() => {
const flipStyle = computed(() => {
if (flip.value) {
let style = ''
const { x, y } = flip.value
if (x && y) flipStyle.value = `rotateX(${x}deg) rotateY(${y}deg)`
else if (x) flipStyle.value = `rotateX(${x}deg)`
else if (y) flipStyle.value = `rotateY(${y}deg)`
else flipStyle.value = ''
if (x && y) style = `rotateX(${x}deg) rotateY(${y}deg)`
else if (x) style = `rotateX(${x}deg)`
else if (y) style = `rotateY(${y}deg)`
return style
}
else flipStyle.value = ''
return ''
})
return {

View File

@ -1,10 +1,11 @@
import { computed, Ref } from 'vue'
import { PPTElementOutline } from '@/types/slides'
// 计算边框相关属性值,主要是对默认值的处理
export default (outline: Ref<PPTElementOutline | undefined>) => {
const outlineWidth = computed(() => (outline.value && outline.value.width !== undefined) ? outline.value.width : 0)
const outlineStyle = computed(() => (outline.value && outline.value.style !== undefined) ? outline.value.style : 'solid')
const outlineColor = computed(() => (outline.value && outline.value.color !== undefined) ? outline.value.color : '#d14424')
const outlineWidth = computed(() => outline.value?.width ?? 0)
const outlineStyle = computed(() => outline.value?.style || 'solid')
const outlineColor = computed(() => outline.value?.color || '#d14424')
return {
outlineWidth,

View File

@ -1,15 +1,14 @@
import { ref, Ref, watchEffect } from 'vue'
import { computed, Ref } from 'vue'
import { PPTElementShadow } from '@/types/slides'
// 计算元素的阴影样式
export default (shadow: Ref<PPTElementShadow | undefined>) => {
const shadowStyle = ref('')
watchEffect(() => {
const shadowStyle = computed(() => {
if (shadow.value) {
const { h, v, blur, color } = shadow.value
shadowStyle.value = `${h}px ${v}px ${blur}px ${color}`
return `${h}px ${v}px ${blur}px ${color}`
}
else shadowStyle.value = ''
return ''
})
return {