mirror of
https://github.com/pipipi-pikachu/PPTist.git
synced 2025-04-15 02:20:00 +08:00
添加形状元素的渲染
This commit is contained in:
parent
665e26c833
commit
8fe7b266f3
@ -109,7 +109,7 @@ export default () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const createShapeElement = (position: CommonElementPosition, svgCode: string) => {
|
const createShapeElement = (position: CommonElementPosition, path: string, viewBox: number) => {
|
||||||
const { left, top, width, height } = position
|
const { left, top, width, height } = position
|
||||||
createElement({
|
createElement({
|
||||||
...DEFAULT_SHAPE,
|
...DEFAULT_SHAPE,
|
||||||
@ -119,7 +119,8 @@ export default () => {
|
|||||||
top,
|
top,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
svgCode,
|
viewBox,
|
||||||
|
path,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,6 +88,21 @@ export const slides: Slide[] = [
|
|||||||
lock: false,
|
lock: false,
|
||||||
content: '<div>😀 😐 😶 😜 🔔 ⭐ ⚡ 🔥 👍 💡 🔰 🎀 🎁 🥇 🏅 🏆 🎈 🎉 💎 🚧 ⛔ 📢 ⌛ ⏰ 🕒 🧩 🎵 📎 🔒 🔑 ⛳ 📌 📍 💬 📅 📈 📋 📜 📁 📱 💻 💾 🌏 🚚 🚡 🚢💧 🌐 🧭 💰 💳 🛒</div>',
|
content: '<div>😀 😐 😶 😜 🔔 ⭐ ⚡ 🔥 👍 💡 🔰 🎀 🎁 🥇 🏅 🏆 🎈 🎉 💎 🚧 ⛔ 📢 ⌛ ⏰ 🕒 🧩 🎵 📎 🔒 🔑 ⛳ 📌 📍 💬 📅 📈 📋 📜 📁 📱 💻 💾 🌏 🚚 🚡 🚢💧 🌐 🧭 💰 💳 🛒</div>',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'xxx7',
|
||||||
|
type: 'shape',
|
||||||
|
left: 130,
|
||||||
|
top: 50,
|
||||||
|
width: 150,
|
||||||
|
height: 150,
|
||||||
|
rotate: 0,
|
||||||
|
fill: '#eebc29',
|
||||||
|
opacity: 0.9,
|
||||||
|
fixedRatio: false,
|
||||||
|
lock: false,
|
||||||
|
viewBox: 1024,
|
||||||
|
path: 'M721.35111111 475.59111111H302.64888889c-5.00622222 0-9.10222222 4.096-9.10222222 9.10222222v54.61333334c0 5.00622222 4.096 9.10222222 9.10222222 9.10222222h418.70222222c5.00622222 0 9.10222222-4.096 9.10222222-9.10222222v-54.61333334c0-5.00622222-4.096-9.10222222-9.10222222-9.10222222z M512 2.27555555C230.51377778 2.27555555 2.27555555 230.51377778 2.27555555 512s228.23822222 509.72444445 509.72444445 509.72444445 509.72444445-228.23822222 509.72444445-509.72444445S793.48622222 2.27555555 512 2.27555555z m0 932.97777778c-233.69955555 0-423.25333333-189.55377778-423.25333333-423.25333333s189.55377778-423.25333333 423.25333333-423.25333333 423.25333333 189.55377778 423.25333333 423.25333333-189.55377778 423.25333333-423.25333333 423.25333333z',
|
||||||
|
}
|
||||||
],
|
],
|
||||||
animations: [
|
animations: [
|
||||||
{
|
{
|
||||||
|
@ -70,7 +70,8 @@ export interface PPTShapeElement extends PPTElementBaseProps {
|
|||||||
type: 'shape';
|
type: 'shape';
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
svgCode: string;
|
viewBox: number;
|
||||||
|
path: string;
|
||||||
fixedRatio: boolean;
|
fixedRatio: boolean;
|
||||||
fill: string;
|
fill: string;
|
||||||
rotate?: number;
|
rotate?: number;
|
||||||
|
@ -30,6 +30,7 @@ import { ElementOrderCommands, ElementAlignCommands } from '@/types/edit'
|
|||||||
|
|
||||||
import ImageElement from '@/views/components/element/ImageElement/index.vue'
|
import ImageElement from '@/views/components/element/ImageElement/index.vue'
|
||||||
import TextElement from '@/views/components/element/TextElement/index.vue'
|
import TextElement from '@/views/components/element/TextElement/index.vue'
|
||||||
|
import ShapeElement from '@/views/components/element/ShapeElement/index.vue'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'editable-element',
|
name: 'editable-element',
|
||||||
@ -56,6 +57,7 @@ export default defineComponent({
|
|||||||
const elementTypeMap = {
|
const elementTypeMap = {
|
||||||
'image': ImageElement,
|
'image': ImageElement,
|
||||||
'text': TextElement,
|
'text': TextElement,
|
||||||
|
'shape': ShapeElement,
|
||||||
}
|
}
|
||||||
return elementTypeMap[props.elementInfo.type] || null
|
return elementTypeMap[props.elementInfo.type] || null
|
||||||
})
|
})
|
||||||
|
@ -11,15 +11,7 @@
|
|||||||
:clipPath="clipShape.style"
|
:clipPath="clipShape.style"
|
||||||
@clip="range => clip(range)"
|
@clip="range => clip(range)"
|
||||||
/>
|
/>
|
||||||
<div
|
<div class="image-element-operate" v-else>
|
||||||
class="image-element-operate"
|
|
||||||
v-else
|
|
||||||
:class="{
|
|
||||||
'selected': isSelected,
|
|
||||||
'multi-select': isMultiSelect && isSelected,
|
|
||||||
'active': isActive,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<BorderLine
|
<BorderLine
|
||||||
class="operate-border-line"
|
class="operate-border-line"
|
||||||
v-for="line in borderLines"
|
v-for="line in borderLines"
|
||||||
@ -72,14 +64,6 @@ export default defineComponent({
|
|||||||
type: Object as PropType<PPTImageElement>,
|
type: Object as PropType<PPTImageElement>,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
isSelected: {
|
|
||||||
type: Boolean,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
isActive: {
|
|
||||||
type: Boolean,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
isActiveGroupElement: {
|
isActiveGroupElement: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: true,
|
required: true,
|
||||||
@ -136,26 +120,4 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.image-element-operate {
|
|
||||||
&.selected {
|
|
||||||
.operate-border-line,
|
|
||||||
.operate-resize-handler,
|
|
||||||
.operate-rotate-handler {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.multi-select:not(.active) .operate-border-line {
|
|
||||||
border-color: rgba($color: $themeColor, $alpha: .3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.operate-border-line,
|
|
||||||
.operate-resize-handler,
|
|
||||||
.operate-rotate-handler {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
85
src/views/Editor/Canvas/Operate/ShapeElementOperate.vue
Normal file
85
src/views/Editor/Canvas/Operate/ShapeElementOperate.vue
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
<template>
|
||||||
|
<div class="text-element-operate">
|
||||||
|
<BorderLine
|
||||||
|
class="operate-border-line"
|
||||||
|
v-for="line in borderLines"
|
||||||
|
:key="line.type"
|
||||||
|
:type="line.type"
|
||||||
|
:style="line.style"
|
||||||
|
/>
|
||||||
|
<template v-if="!elementInfo.lock && (isActiveGroupElement || !isMultiSelect)">
|
||||||
|
<ResizeHandler
|
||||||
|
class="operate-resize-handler"
|
||||||
|
v-for="point in resizeHandlers"
|
||||||
|
:key="point.direction"
|
||||||
|
:type="point.direction"
|
||||||
|
:style="point.style"
|
||||||
|
@mousedown.stop="$event => scaleElement($event, elementInfo, point.direction)"
|
||||||
|
/>
|
||||||
|
<RotateHandler
|
||||||
|
class="operate-rotate-handler"
|
||||||
|
:style="{ left: scaleWidth / 2 + 'px' }"
|
||||||
|
@mousedown.stop="rotateElement(elementInfo)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { computed, defineComponent, PropType } from 'vue'
|
||||||
|
import { useStore } from 'vuex'
|
||||||
|
import { State } from '@/store'
|
||||||
|
|
||||||
|
import { PPTShapeElement } from '@/types/slides'
|
||||||
|
import { OperateResizeHandler } from '@/types/edit'
|
||||||
|
import useCommonOperate from '../hooks/useCommonOperate'
|
||||||
|
|
||||||
|
import RotateHandler from './RotateHandler.vue'
|
||||||
|
import ResizeHandler from './ResizeHandler.vue'
|
||||||
|
import BorderLine from './BorderLine.vue'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'text-element-operate',
|
||||||
|
components: {
|
||||||
|
RotateHandler,
|
||||||
|
ResizeHandler,
|
||||||
|
BorderLine,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
elementInfo: {
|
||||||
|
type: Object as PropType<PPTShapeElement>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
isActiveGroupElement: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
isMultiSelect: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
rotateElement: {
|
||||||
|
type: Function as PropType<(element: PPTShapeElement) => void>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
scaleElement: {
|
||||||
|
type: Function as PropType<(e: MouseEvent, element: PPTShapeElement, command: OperateResizeHandler) => void>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setup(props) {
|
||||||
|
const store = useStore<State>()
|
||||||
|
const canvasScale = computed(() => store.state.canvasScale)
|
||||||
|
|
||||||
|
const scaleWidth = computed(() => props.elementInfo.width * canvasScale.value)
|
||||||
|
const scaleHeight = computed(() => props.elementInfo.height * canvasScale.value)
|
||||||
|
const { resizeHandlers, borderLines } = useCommonOperate(scaleWidth, scaleHeight)
|
||||||
|
|
||||||
|
return {
|
||||||
|
scaleWidth,
|
||||||
|
resizeHandlers,
|
||||||
|
borderLines,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
@ -1,12 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div class="text-element-operate">
|
||||||
class="text-element-operate"
|
|
||||||
:class="{
|
|
||||||
'selected': isSelected,
|
|
||||||
'multi-select': isMultiSelect && isSelected,
|
|
||||||
'active': isActive,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<BorderLine
|
<BorderLine
|
||||||
class="operate-border-line"
|
class="operate-border-line"
|
||||||
v-for="line in borderLines"
|
v-for="line in borderLines"
|
||||||
@ -57,14 +50,6 @@ export default defineComponent({
|
|||||||
type: Object as PropType<PPTTextElement>,
|
type: Object as PropType<PPTTextElement>,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
isSelected: {
|
|
||||||
type: Boolean,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
isActive: {
|
|
||||||
type: Boolean,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
isActiveGroupElement: {
|
isActiveGroupElement: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: true,
|
required: true,
|
||||||
@ -98,26 +83,4 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.text-element-operate {
|
|
||||||
&.selected {
|
|
||||||
.operate-border-line,
|
|
||||||
.operate-resize-handler,
|
|
||||||
.operate-rotate-handler {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.multi-select:not(.active) .operate-border-line {
|
|
||||||
border-color: rgba($color: $themeColor, $alpha: .3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.operate-border-line,
|
|
||||||
.operate-resize-handler,
|
|
||||||
.operate-rotate-handler {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,21 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="operate"
|
class="operate"
|
||||||
|
:class="{ 'multi-select': isMultiSelect && !isActive }"
|
||||||
|
v-if="isSelected"
|
||||||
:style="{
|
:style="{
|
||||||
top: elementInfo.top * canvasScale + 'px',
|
top: elementInfo.top * canvasScale + 'px',
|
||||||
left: elementInfo.left * canvasScale + 'px',
|
left: elementInfo.left * canvasScale + 'px',
|
||||||
transform: `rotate(${elementInfo.rotate}deg)`,
|
transform: `rotate(${elementInfo.rotate}deg)`,
|
||||||
'transform-origin': `${elementInfo.width * canvasScale / 2}px ${elementInfo.height * canvasScale / 2}px`,
|
transformOrigin: `${elementInfo.width * canvasScale / 2}px ${elementInfo.height * canvasScale / 2}px`,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<component
|
<component
|
||||||
:is="currentOperateComponent"
|
:is="currentOperateComponent"
|
||||||
:elementInfo="elementInfo"
|
:elementInfo="elementInfo"
|
||||||
:isSelected="isSelected"
|
|
||||||
:isActive="isActive"
|
|
||||||
:isActiveGroupElement="isActiveGroupElement"
|
:isActiveGroupElement="isActiveGroupElement"
|
||||||
:isMultiSelect="isMultiSelect"
|
:isMultiSelect="isMultiSelect"
|
||||||
:animationIndex="elementIndexInAnimation"
|
|
||||||
:rotateElement="rotateElement"
|
:rotateElement="rotateElement"
|
||||||
:scaleElement="scaleElement"
|
:scaleElement="scaleElement"
|
||||||
></component>
|
></component>
|
||||||
@ -38,6 +37,7 @@ import { OperateResizeHandler } from '@/types/edit'
|
|||||||
|
|
||||||
import ImageElementOperate from './ImageElementOperate.vue'
|
import ImageElementOperate from './ImageElementOperate.vue'
|
||||||
import TextElementOperate from './TextElementOperate.vue'
|
import TextElementOperate from './TextElementOperate.vue'
|
||||||
|
import ShapeElementOperate from './ShapeElementOperate.vue'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'operate',
|
name: 'operate',
|
||||||
@ -81,6 +81,7 @@ export default defineComponent({
|
|||||||
const elementTypeMap = {
|
const elementTypeMap = {
|
||||||
'image': ImageElementOperate,
|
'image': ImageElementOperate,
|
||||||
'text': TextElementOperate,
|
'text': TextElementOperate,
|
||||||
|
'shape': ShapeElementOperate,
|
||||||
}
|
}
|
||||||
return elementTypeMap[props.elementInfo.type] || null
|
return elementTypeMap[props.elementInfo.type] || null
|
||||||
})
|
})
|
||||||
@ -105,6 +106,10 @@ export default defineComponent({
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
|
||||||
|
&.multi-select {
|
||||||
|
opacity: .3;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.animation-index {
|
.animation-index {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -16,6 +16,7 @@ import { PPTElement } from '@/types/slides'
|
|||||||
|
|
||||||
import BaseImageElement from '@/views/components/element/ImageElement/BaseImageElement.vue'
|
import BaseImageElement from '@/views/components/element/ImageElement/BaseImageElement.vue'
|
||||||
import BaseTextElement from '@/views/components/element/TextElement/BaseTextElement.vue'
|
import BaseTextElement from '@/views/components/element/TextElement/BaseTextElement.vue'
|
||||||
|
import BaseShapeElement from '@/views/components/element/ShapeElement/BaseShapeElement.vue'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'base-element',
|
name: 'base-element',
|
||||||
@ -34,6 +35,7 @@ export default defineComponent({
|
|||||||
const elementTypeMap = {
|
const elementTypeMap = {
|
||||||
'image': BaseImageElement,
|
'image': BaseImageElement,
|
||||||
'text': BaseTextElement,
|
'text': BaseTextElement,
|
||||||
|
'shape': BaseShapeElement,
|
||||||
}
|
}
|
||||||
return elementTypeMap[props.elementInfo.type] || null
|
return elementTypeMap[props.elementInfo.type] || null
|
||||||
})
|
})
|
||||||
|
@ -0,0 +1,92 @@
|
|||||||
|
<template>
|
||||||
|
<div class="base-element-shape"
|
||||||
|
:style="{
|
||||||
|
top: elementInfo.top + 'px',
|
||||||
|
left: elementInfo.left + 'px',
|
||||||
|
width: elementInfo.width + 'px',
|
||||||
|
transform: `rotate(${elementInfo.rotate}deg)`,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="element-content"
|
||||||
|
:style="{
|
||||||
|
opacity: elementInfo.opacity,
|
||||||
|
filter: shadowStyle ? `drop-shadow(${shadowStyle})` : '',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<SvgWrapper overflow="visible"
|
||||||
|
:width="elementInfo.width"
|
||||||
|
:height="elementInfo.height"
|
||||||
|
>
|
||||||
|
<g
|
||||||
|
:transform="`scale(${elementInfo.width / elementInfo.viewBox}, ${elementInfo.height / elementInfo.viewBox}) translate(0,0) matrix(1,0,0,1,0,0)`"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
vector-effect="non-scaling-stroke"
|
||||||
|
stroke-linecap="butt"
|
||||||
|
stroke-miterlimit="8"
|
||||||
|
stroke-linejoin=""
|
||||||
|
:d="elementInfo.path"
|
||||||
|
:fill="elementInfo.fill"
|
||||||
|
:stroke="outlineColor"
|
||||||
|
:stroke-width="outlineWidth"
|
||||||
|
:stroke-dasharray="outlineStyle === 'dashed' ? '10 5' : '0 0'"
|
||||||
|
></path>
|
||||||
|
</g>
|
||||||
|
</SvgWrapper>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { computed, defineComponent, PropType } from 'vue'
|
||||||
|
import { PPTShapeElement } from '@/types/slides'
|
||||||
|
import useElementOutline from '@/views/components/element/hooks/useElementOutline'
|
||||||
|
import useElementShadow from '@/views/components/element/hooks/useElementShadow'
|
||||||
|
|
||||||
|
import SvgWrapper from '@/components/SvgWrapper.vue'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'base-element-shape',
|
||||||
|
components: {
|
||||||
|
SvgWrapper,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
elementInfo: {
|
||||||
|
type: Object as PropType<PPTShapeElement>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setup(props) {
|
||||||
|
const outline = computed(() => props.elementInfo.outline)
|
||||||
|
const { outlineWidth, outlineStyle, outlineColor } = useElementOutline(outline)
|
||||||
|
|
||||||
|
const shadow = computed(() => props.elementInfo.shadow)
|
||||||
|
const { shadowStyle } = useElementShadow(shadow)
|
||||||
|
|
||||||
|
return {
|
||||||
|
shadowStyle,
|
||||||
|
outlineWidth,
|
||||||
|
outlineStyle,
|
||||||
|
outlineColor,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.base-element-shape {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.element-content {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
transform-origin: 0 0;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
116
src/views/components/element/ShapeElement/index.vue
Normal file
116
src/views/components/element/ShapeElement/index.vue
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
<template>
|
||||||
|
<div class="editable-element-shape"
|
||||||
|
:class="{ 'lock': elementInfo.lock }"
|
||||||
|
:style="{
|
||||||
|
top: elementInfo.top + 'px',
|
||||||
|
left: elementInfo.left + 'px',
|
||||||
|
width: elementInfo.width + 'px',
|
||||||
|
transform: `rotate(${elementInfo.rotate}deg)`,
|
||||||
|
}"
|
||||||
|
@mousedown="$event => handleSelectElement($event)"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="element-content"
|
||||||
|
v-contextmenu="contextmenus"
|
||||||
|
:style="{
|
||||||
|
opacity: elementInfo.opacity,
|
||||||
|
filter: shadowStyle ? `drop-shadow(${shadowStyle})` : '',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<SvgWrapper overflow="visible"
|
||||||
|
:width="elementInfo.width"
|
||||||
|
:height="elementInfo.height"
|
||||||
|
>
|
||||||
|
<g
|
||||||
|
:transform="`scale(${elementInfo.width / elementInfo.viewBox}, ${elementInfo.height / elementInfo.viewBox}) translate(0,0) matrix(1,0,0,1,0,0)`"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
vector-effect="non-scaling-stroke"
|
||||||
|
stroke-linecap="butt"
|
||||||
|
stroke-miterlimit="8"
|
||||||
|
stroke-linejoin=""
|
||||||
|
:d="elementInfo.path"
|
||||||
|
:fill="elementInfo.fill"
|
||||||
|
:stroke="outlineColor"
|
||||||
|
:stroke-width="outlineWidth"
|
||||||
|
:stroke-dasharray="outlineStyle === 'dashed' ? '10 5' : '0 0'"
|
||||||
|
></path>
|
||||||
|
</g>
|
||||||
|
</SvgWrapper>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { computed, defineComponent, PropType } from 'vue'
|
||||||
|
import { PPTShapeElement } from '@/types/slides'
|
||||||
|
import { ContextmenuItem } from '@/components/Contextmenu/types'
|
||||||
|
import useElementOutline from '@/views/components/element/hooks/useElementOutline'
|
||||||
|
import useElementShadow from '@/views/components/element/hooks/useElementShadow'
|
||||||
|
|
||||||
|
import SvgWrapper from '@/components/SvgWrapper.vue'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'editable-element-shape',
|
||||||
|
components: {
|
||||||
|
SvgWrapper,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
elementInfo: {
|
||||||
|
type: Object as PropType<PPTShapeElement>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
selectElement: {
|
||||||
|
type: Function as PropType<(e: MouseEvent, element: PPTShapeElement, canMove?: boolean) => void>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
contextmenus: {
|
||||||
|
type: Function as PropType<() => ContextmenuItem[]>,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setup(props) {
|
||||||
|
const handleSelectElement = (e: MouseEvent) => {
|
||||||
|
if(props.elementInfo.lock) return
|
||||||
|
e.stopPropagation()
|
||||||
|
|
||||||
|
props.selectElement(e, props.elementInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
const outline = computed(() => props.elementInfo.outline)
|
||||||
|
const { outlineWidth, outlineStyle, outlineColor } = useElementOutline(outline)
|
||||||
|
|
||||||
|
const shadow = computed(() => props.elementInfo.shadow)
|
||||||
|
const { shadowStyle } = useElementShadow(shadow)
|
||||||
|
|
||||||
|
return {
|
||||||
|
handleSelectElement,
|
||||||
|
shadowStyle,
|
||||||
|
outlineWidth,
|
||||||
|
outlineStyle,
|
||||||
|
outlineColor,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.editable-element-shape {
|
||||||
|
position: absolute;
|
||||||
|
cursor: move;
|
||||||
|
|
||||||
|
&.lock .element-content {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.element-content {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
transform-origin: 0 0;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
Loading…
x
Reference in New Issue
Block a user