mirror of
https://github.com/pipipi-pikachu/PPTist.git
synced 2025-04-15 02:20:00 +08:00
rename
This commit is contained in:
parent
fa1e7f13d3
commit
b715809a34
@ -1,14 +1,5 @@
|
|||||||
const DEFAULT_COLOR = '#41464b'
|
const DEFAULT_COLOR = '#41464b'
|
||||||
|
|
||||||
export enum ElementTypes {
|
|
||||||
TEXT = '文本',
|
|
||||||
IMAGE = '图片',
|
|
||||||
SHAPE = '形状',
|
|
||||||
LINE = '线条',
|
|
||||||
CHART = '图表',
|
|
||||||
TABLE = '表格',
|
|
||||||
}
|
|
||||||
|
|
||||||
export const DEFAULT_TEXT = {
|
export const DEFAULT_TEXT = {
|
||||||
left: 0,
|
left: 0,
|
||||||
top: 0,
|
top: 0,
|
||||||
@ -23,17 +14,17 @@ export const DEFAULT_TEXT = {
|
|||||||
export const DEFAULT_IMAGE = {
|
export const DEFAULT_IMAGE = {
|
||||||
left: 0,
|
left: 0,
|
||||||
top: 0,
|
top: 0,
|
||||||
lockRatio: true,
|
fixedRatio: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DEFAULT_SHAPE = {
|
export const DEFAULT_SHAPE = {
|
||||||
fill: DEFAULT_COLOR,
|
fill: DEFAULT_COLOR,
|
||||||
lockRatio: false,
|
fixedRatio: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DEFAULT_LINE = {
|
export const DEFAULT_LINE = {
|
||||||
style: 'solid',
|
style: 'solid',
|
||||||
marker: ['', ''],
|
points: ['', ''],
|
||||||
width: 4,
|
width: 4,
|
||||||
color: DEFAULT_COLOR,
|
color: DEFAULT_COLOR,
|
||||||
}
|
}
|
||||||
@ -48,8 +39,9 @@ export const DEFAULT_CHART = {
|
|||||||
export const DEFAULT_TABLE = {
|
export const DEFAULT_TABLE = {
|
||||||
left: 0,
|
left: 0,
|
||||||
top: 0,
|
top: 0,
|
||||||
isLock: false,
|
outline: {
|
||||||
borderStyle: 'solid',
|
width: 2,
|
||||||
borderWidth: 2,
|
style: 'solid',
|
||||||
borderColor: DEFAULT_COLOR,
|
color: DEFAULT_COLOR
|
||||||
|
},
|
||||||
}
|
}
|
@ -1,12 +1,12 @@
|
|||||||
export const LINES = [
|
export const LINES = [
|
||||||
{ path: 'M0,0 L20,20', style: 'solid', marker: ['', ''] },
|
{ path: 'M0,0 L20,20', style: 'solid', points: ['', ''] },
|
||||||
{ path: 'M0,0 L20,20', style: 'solid', marker: ['', 'arrow'] },
|
{ path: 'M0,0 L20,20', style: 'solid', points: ['', 'arrow'] },
|
||||||
{ path: 'M0,0 L20,20', style: 'solid', marker: ['arrow', 'arrow'] },
|
{ path: 'M0,0 L20,20', style: 'solid', points: ['arrow', 'arrow'] },
|
||||||
{ path: 'M0,0 L20,20', style: 'solid', marker: ['', 'cusp'] },
|
{ path: 'M0,0 L20,20', style: 'solid', points: ['', 'cusp'] },
|
||||||
{ path: 'M0,0 L20,20', style: 'solid', marker: ['cusp', 'cusp'] },
|
{ path: 'M0,0 L20,20', style: 'solid', points: ['cusp', 'cusp'] },
|
||||||
{ path: 'M0,0 L20,20', style: 'solid', marker: ['', 'dot'] },
|
{ path: 'M0,0 L20,20', style: 'solid', points: ['', 'dot'] },
|
||||||
{ path: 'M0,0 L20,20', style: 'solid', marker: ['dot', 'dot'] },
|
{ path: 'M0,0 L20,20', style: 'solid', points: ['dot', 'dot'] },
|
||||||
{ path: 'M0,0 L20,20', style: 'dashed', marker: ['', ''] },
|
{ path: 'M0,0 L20,20', style: 'dashed', points: ['', ''] },
|
||||||
{ path: 'M0,0 L20,20', style: 'dashed', marker: ['', 'arrow'] },
|
{ path: 'M0,0 L20,20', style: 'dashed', points: ['', 'arrow'] },
|
||||||
{ path: 'M0,0 L20,20', style: 'dashed', marker: ['arrow', 'arrow'] },
|
{ path: 'M0,0 L20,20', style: 'dashed', points: ['arrow', 'arrow'] },
|
||||||
]
|
]
|
@ -20,7 +20,7 @@ export default () => {
|
|||||||
|
|
||||||
const newElementList: PPTElement[] = JSON.parse(JSON.stringify(currentSlide.value.elements))
|
const newElementList: PPTElement[] = JSON.parse(JSON.stringify(currentSlide.value.elements))
|
||||||
for(const element of newElementList) {
|
for(const element of newElementList) {
|
||||||
if(!activeElementIdList.value.includes(element.elId)) continue
|
if(!activeElementIdList.value.includes(element.id)) continue
|
||||||
|
|
||||||
if(command === ElementAlignCommands.TOP) {
|
if(command === ElementAlignCommands.TOP) {
|
||||||
const offsetY = minY - 0
|
const offsetY = minY - 0
|
||||||
|
@ -22,16 +22,16 @@ export default () => {
|
|||||||
|
|
||||||
const combineElementList: PPTElement[] = []
|
const combineElementList: PPTElement[] = []
|
||||||
for(const element of newElementList) {
|
for(const element of newElementList) {
|
||||||
if(activeElementIdList.value.includes(element.elId)) {
|
if(activeElementIdList.value.includes(element.id)) {
|
||||||
element.groupId = groupId
|
element.groupId = groupId
|
||||||
combineElementList.push(element)
|
combineElementList.push(element)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 注意,组合元素的层级应该是连续的,所以需要获取该组元素中最顶层的元素,将组内其他成员从原位置移动到最顶层的元素的下面
|
// 注意,组合元素的层级应该是连续的,所以需要获取该组元素中最顶层的元素,将组内其他成员从原位置移动到最顶层的元素的下面
|
||||||
const combineElementMaxIndex = newElementList.findIndex(_element => _element.elId === combineElementList[combineElementList.length - 1].elId)
|
const combineElementMaxIndex = newElementList.findIndex(_element => _element.id === combineElementList[combineElementList.length - 1].id)
|
||||||
const combineElementIdList = combineElementList.map(_element => _element.elId)
|
const combineElementIdList = combineElementList.map(_element => _element.id)
|
||||||
newElementList = newElementList.filter(_element => !combineElementIdList.includes(_element.elId))
|
newElementList = newElementList.filter(_element => !combineElementIdList.includes(_element.id))
|
||||||
|
|
||||||
const insertIndex = combineElementMaxIndex - combineElementList.length + 1
|
const insertIndex = combineElementMaxIndex - combineElementList.length + 1
|
||||||
newElementList.splice(insertIndex, 0, ...combineElementList)
|
newElementList.splice(insertIndex, 0, ...combineElementList)
|
||||||
@ -48,7 +48,7 @@ export default () => {
|
|||||||
|
|
||||||
const newElementList: PPTElement[] = JSON.parse(JSON.stringify(currentSlide.value.elements))
|
const newElementList: PPTElement[] = JSON.parse(JSON.stringify(currentSlide.value.elements))
|
||||||
for(const element of newElementList) {
|
for(const element of newElementList) {
|
||||||
if(activeElementIdList.value.includes(element.elId) && element.groupId) delete element.groupId
|
if(activeElementIdList.value.includes(element.id) && element.groupId) delete element.groupId
|
||||||
}
|
}
|
||||||
store.commit(MutationTypes.UPDATE_SLIDE, { elements: newElementList })
|
store.commit(MutationTypes.UPDATE_SLIDE, { elements: newElementList })
|
||||||
addHistorySnapshot()
|
addHistorySnapshot()
|
||||||
|
@ -35,12 +35,12 @@ export default () => {
|
|||||||
|
|
||||||
const createElement = (element: PPTElement) => {
|
const createElement = (element: PPTElement) => {
|
||||||
store.commit(MutationTypes.ADD_ELEMENT, element)
|
store.commit(MutationTypes.ADD_ELEMENT, element)
|
||||||
store.commit(MutationTypes.SET_ACTIVE_ELEMENT_ID_LIST, [element.elId])
|
store.commit(MutationTypes.SET_ACTIVE_ELEMENT_ID_LIST, [element.id])
|
||||||
addHistorySnapshot()
|
addHistorySnapshot()
|
||||||
}
|
}
|
||||||
|
|
||||||
const createImageElement = (imgUrl: string) => {
|
const createImageElement = (src: string) => {
|
||||||
getImageSize(imgUrl).then(({ width, height }) => {
|
getImageSize(src).then(({ width, height }) => {
|
||||||
const scale = width / height
|
const scale = width / height
|
||||||
|
|
||||||
if(scale < VIEWPORT_ASPECT_RATIO && width > VIEWPORT_SIZE) {
|
if(scale < VIEWPORT_ASPECT_RATIO && width > VIEWPORT_SIZE) {
|
||||||
@ -55,8 +55,8 @@ export default () => {
|
|||||||
createElement({
|
createElement({
|
||||||
...DEFAULT_IMAGE,
|
...DEFAULT_IMAGE,
|
||||||
type: 'image',
|
type: 'image',
|
||||||
elId: createRandomCode(),
|
id: createRandomCode(),
|
||||||
imgUrl,
|
src,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
})
|
})
|
||||||
@ -67,7 +67,7 @@ export default () => {
|
|||||||
createElement({
|
createElement({
|
||||||
...DEFAULT_CHART,
|
...DEFAULT_CHART,
|
||||||
type: 'chart',
|
type: 'chart',
|
||||||
elId: createRandomCode(),
|
id: createRandomCode(),
|
||||||
chartType,
|
chartType,
|
||||||
data,
|
data,
|
||||||
})
|
})
|
||||||
@ -87,7 +87,7 @@ export default () => {
|
|||||||
createElement({
|
createElement({
|
||||||
...DEFAULT_TABLE,
|
...DEFAULT_TABLE,
|
||||||
type: 'table',
|
type: 'table',
|
||||||
elId: createRandomCode(),
|
id: createRandomCode(),
|
||||||
width: colCount * DEFAULT_CELL_WIDTH + DEFAULT_BORDER_WIDTH,
|
width: colCount * DEFAULT_CELL_WIDTH + DEFAULT_BORDER_WIDTH,
|
||||||
height: rowCount * DEFAULT_CELL_HEIGHT + DEFAULT_BORDER_WIDTH,
|
height: rowCount * DEFAULT_CELL_HEIGHT + DEFAULT_BORDER_WIDTH,
|
||||||
colSizes,
|
colSizes,
|
||||||
@ -101,7 +101,7 @@ export default () => {
|
|||||||
createElement({
|
createElement({
|
||||||
...DEFAULT_TEXT,
|
...DEFAULT_TEXT,
|
||||||
type: 'text',
|
type: 'text',
|
||||||
elId: createRandomCode(),
|
id: createRandomCode(),
|
||||||
left,
|
left,
|
||||||
top,
|
top,
|
||||||
width,
|
width,
|
||||||
@ -114,7 +114,7 @@ export default () => {
|
|||||||
createElement({
|
createElement({
|
||||||
...DEFAULT_SHAPE,
|
...DEFAULT_SHAPE,
|
||||||
type: 'shape',
|
type: 'shape',
|
||||||
elId: createRandomCode(),
|
id: createRandomCode(),
|
||||||
left,
|
left,
|
||||||
top,
|
top,
|
||||||
width,
|
width,
|
||||||
@ -123,17 +123,17 @@ export default () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const createLineElement = (position: LineElementPosition, marker: [string, string], lineType: string) => {
|
const createLineElement = (position: LineElementPosition, points: [string, string], lineType: string) => {
|
||||||
const { left, top, start, end } = position
|
const { left, top, start, end } = position
|
||||||
createElement({
|
createElement({
|
||||||
...DEFAULT_LINE,
|
...DEFAULT_LINE,
|
||||||
type: 'line',
|
type: 'line',
|
||||||
elId: createRandomCode(),
|
id: createRandomCode(),
|
||||||
left,
|
left,
|
||||||
top,
|
top,
|
||||||
start,
|
start,
|
||||||
end,
|
end,
|
||||||
marker,
|
points,
|
||||||
lineType,
|
lineType,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ export default () => {
|
|||||||
|
|
||||||
const deleteElement = () => {
|
const deleteElement = () => {
|
||||||
if(!activeElementIdList.value.length) return
|
if(!activeElementIdList.value.length) return
|
||||||
const newElementList = currentSlide.value.elements.filter(el => !activeElementIdList.value.includes(el.elId))
|
const newElementList = currentSlide.value.elements.filter(el => !activeElementIdList.value.includes(el.id))
|
||||||
store.commit(MutationTypes.SET_ACTIVE_ELEMENT_ID_LIST, [])
|
store.commit(MutationTypes.SET_ACTIVE_ELEMENT_ID_LIST, [])
|
||||||
store.commit(MutationTypes.UPDATE_SLIDE, { elements: newElementList })
|
store.commit(MutationTypes.UPDATE_SLIDE, { elements: newElementList })
|
||||||
addHistorySnapshot()
|
addHistorySnapshot()
|
||||||
|
@ -15,7 +15,7 @@ export default () => {
|
|||||||
const newElementList: PPTElement[] = JSON.parse(JSON.stringify(currentSlide.value.elements))
|
const newElementList: PPTElement[] = JSON.parse(JSON.stringify(currentSlide.value.elements))
|
||||||
|
|
||||||
for(const element of newElementList) {
|
for(const element of newElementList) {
|
||||||
if(activeElementIdList.value.includes(element.elId)) element.isLock = true
|
if(activeElementIdList.value.includes(element.id)) element.lock = true
|
||||||
}
|
}
|
||||||
store.commit(MutationTypes.UPDATE_SLIDE, { elements: newElementList })
|
store.commit(MutationTypes.UPDATE_SLIDE, { elements: newElementList })
|
||||||
addHistorySnapshot()
|
addHistorySnapshot()
|
||||||
@ -26,14 +26,14 @@ export default () => {
|
|||||||
|
|
||||||
if(handleElement.groupId) {
|
if(handleElement.groupId) {
|
||||||
for(const element of newElementList) {
|
for(const element of newElementList) {
|
||||||
if(element.groupId === handleElement.groupId) element.isLock = false
|
if(element.groupId === handleElement.groupId) element.lock = false
|
||||||
}
|
}
|
||||||
return newElementList
|
return newElementList
|
||||||
}
|
}
|
||||||
|
|
||||||
for(const element of newElementList) {
|
for(const element of newElementList) {
|
||||||
if(element.elId === handleElement.elId) {
|
if(element.id === handleElement.id) {
|
||||||
element.isLock = false
|
element.lock = false
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ export default () => {
|
|||||||
|
|
||||||
const moveElement = (command: string) => {
|
const moveElement = (command: string) => {
|
||||||
const newElementList = currentSlide.value.elements.map(el => {
|
const newElementList = currentSlide.value.elements.map(el => {
|
||||||
if(activeElementIdList.value.includes(el.elId)) {
|
if(activeElementIdList.value.includes(el.id)) {
|
||||||
let { left, top } = el
|
let { left, top } = el
|
||||||
switch(command) {
|
switch(command) {
|
||||||
case KEYS.LEFT:
|
case KEYS.LEFT:
|
||||||
|
@ -13,8 +13,8 @@ export default () => {
|
|||||||
|
|
||||||
// 获取组合元素层级范围(组合成员中的最大层级和最小层级)
|
// 获取组合元素层级范围(组合成员中的最大层级和最小层级)
|
||||||
const getCombineElementIndexRange = (elementList: PPTElement[], combineElementList: PPTElement[]) => {
|
const getCombineElementIndexRange = (elementList: PPTElement[], combineElementList: PPTElement[]) => {
|
||||||
const minIndex = elementList.findIndex(_element => _element.elId === combineElementList[0].elId)
|
const minIndex = elementList.findIndex(_element => _element.id === combineElementList[0].id)
|
||||||
const maxIndex = elementList.findIndex(_element => _element.elId === combineElementList[combineElementList.length - 1].elId)
|
const maxIndex = elementList.findIndex(_element => _element.id === combineElementList[combineElementList.length - 1].id)
|
||||||
return { minIndex, maxIndex }
|
return { minIndex, maxIndex }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,7 +53,7 @@ export default () => {
|
|||||||
else {
|
else {
|
||||||
|
|
||||||
// 元素在元素列表中的层级
|
// 元素在元素列表中的层级
|
||||||
const elementIndex = elementList.findIndex(item => item.elId === element.elId)
|
const elementIndex = elementList.findIndex(item => item.id === element.id)
|
||||||
|
|
||||||
// 无法移动(已经处在顶层)
|
// 无法移动(已经处在顶层)
|
||||||
if(elementIndex === elementList.length - 1) return null
|
if(elementIndex === elementList.length - 1) return null
|
||||||
@ -96,7 +96,7 @@ export default () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
else {
|
else {
|
||||||
const elementIndex = elementList.findIndex(item => item.elId === element.elId)
|
const elementIndex = elementList.findIndex(item => item.id === element.id)
|
||||||
if(elementIndex === 0) return null
|
if(elementIndex === 0) return null
|
||||||
const prevElement = copyOfElementList[elementIndex - 1]
|
const prevElement = copyOfElementList[elementIndex - 1]
|
||||||
const movedElement = copyOfElementList.splice(elementIndex, 1)[0]
|
const movedElement = copyOfElementList.splice(elementIndex, 1)[0]
|
||||||
@ -133,7 +133,7 @@ export default () => {
|
|||||||
else {
|
else {
|
||||||
|
|
||||||
// 元素在元素列表中的层级
|
// 元素在元素列表中的层级
|
||||||
const elementIndex = elementList.findIndex(item => item.elId === element.elId)
|
const elementIndex = elementList.findIndex(item => item.id === element.id)
|
||||||
|
|
||||||
// 无法移动(已经处在顶层)
|
// 无法移动(已经处在顶层)
|
||||||
if(elementIndex === elementList.length - 1) return null
|
if(elementIndex === elementList.length - 1) return null
|
||||||
@ -159,7 +159,7 @@ export default () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
else {
|
else {
|
||||||
const elementIndex = elementList.findIndex(item => item.elId === element.elId)
|
const elementIndex = elementList.findIndex(item => item.id === element.id)
|
||||||
if(elementIndex === 0) return null
|
if(elementIndex === 0) return null
|
||||||
copyOfElementList.splice(elementIndex, 1)
|
copyOfElementList.splice(elementIndex, 1)
|
||||||
copyOfElementList.unshift(element)
|
copyOfElementList.unshift(element)
|
||||||
|
@ -25,14 +25,14 @@ export default () => {
|
|||||||
if(groupId && !groupIdMap[groupId]) {
|
if(groupId && !groupIdMap[groupId]) {
|
||||||
groupIdMap[groupId] = createRandomCode()
|
groupIdMap[groupId] = createRandomCode()
|
||||||
}
|
}
|
||||||
elIdMap[element.elId] = createRandomCode()
|
elIdMap[element.id] = createRandomCode()
|
||||||
}
|
}
|
||||||
const currentSlideElementIdList = currentSlide.value.elements.map(el => el.elId)
|
const currentSlideElementIdList = currentSlide.value.elements.map(el => el.id)
|
||||||
|
|
||||||
for(const element of elements) {
|
for(const element of elements) {
|
||||||
const inCurrentSlide = currentSlideElementIdList.includes(element.elId)
|
const inCurrentSlide = currentSlideElementIdList.includes(element.id)
|
||||||
|
|
||||||
element.elId = elIdMap[element.elId]
|
element.id = elIdMap[element.id]
|
||||||
|
|
||||||
if(inCurrentSlide) {
|
if(inCurrentSlide) {
|
||||||
element.left = element.left + 10
|
element.left = element.left + 10
|
||||||
|
@ -8,8 +8,8 @@ export default () => {
|
|||||||
const currentSlide: Ref<Slide> = computed(() => store.getters.currentSlide)
|
const currentSlide: Ref<Slide> = computed(() => store.getters.currentSlide)
|
||||||
|
|
||||||
const selectAllElement = () => {
|
const selectAllElement = () => {
|
||||||
const unlockedElements = currentSlide.value.elements.filter(el => !el.isLock)
|
const unlockedElements = currentSlide.value.elements.filter(el => !el.lock)
|
||||||
const newActiveElementIdList = unlockedElements.map(el => el.elId)
|
const newActiveElementIdList = unlockedElements.map(el => el.id)
|
||||||
store.commit(MutationTypes.SET_ACTIVE_ELEMENT_ID_LIST, newActiveElementIdList)
|
store.commit(MutationTypes.SET_ACTIVE_ELEMENT_ID_LIST, newActiveElementIdList)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { Ref, computed } from 'vue'
|
import { Ref, computed } from 'vue'
|
||||||
|
import { SlideBackground } from '@/types/slides'
|
||||||
|
|
||||||
export default (background: Ref<[string, string] | undefined>) => {
|
export default (background: Ref<SlideBackground | undefined>) => {
|
||||||
const backgroundStyle = computed(() => {
|
const backgroundStyle = computed(() => {
|
||||||
if(!background.value) return { backgroundColor: '#fff' }
|
if(!background.value) return { backgroundColor: '#fff' }
|
||||||
|
|
||||||
const [type, value] = background.value
|
const { type, value } = background.value
|
||||||
if(type === 'solid') return { backgroundColor: value }
|
if(type === 'solid') return { backgroundColor: value }
|
||||||
else if(type === 'image') return { backgroundImage: `url(${value}` }
|
else if(type === 'image') return { backgroundImage: `url(${value}` }
|
||||||
|
|
||||||
|
@ -3,66 +3,71 @@ import { Slide } from '@/types/slides'
|
|||||||
export const slides: Slide[] = [
|
export const slides: Slide[] = [
|
||||||
{
|
{
|
||||||
id: 'xxx1',
|
id: 'xxx1',
|
||||||
background: ['solid', '#fff'],
|
background: {
|
||||||
|
type: 'solid',
|
||||||
|
value: '#fff',
|
||||||
|
},
|
||||||
elements: [
|
elements: [
|
||||||
{
|
{
|
||||||
elId: 'xxx1',
|
id: 'xxx1',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
left: 190,
|
left: 190,
|
||||||
top: 50,
|
top: 50,
|
||||||
width: 320,
|
width: 320,
|
||||||
height: 104,
|
height: 104,
|
||||||
rotate: 0,
|
rotate: 0,
|
||||||
borderStyle: 'solid',
|
fill: 'rgba(220, 220, 220, 0.8)',
|
||||||
borderWidth: 4,
|
shadow: {
|
||||||
borderColor: '#5b7d89',
|
h: 1,
|
||||||
fill: 'rgba(220,220,220,0.8)',
|
v: 1,
|
||||||
shadow: '1px 1px 3px rgba(10,10,10,.5)',
|
blur: 3,
|
||||||
|
color: 'rgba(10, 10, 10, .5)'
|
||||||
|
},
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
lineHeight: 1.5,
|
lock: false,
|
||||||
segmentSpacing: 10,
|
|
||||||
isLock: false,
|
|
||||||
content: '<div style=\'text-align: center;\'><span style=\'font-size: 28px;\'><span style=\'color: rgb(232, 107, 153); font-weight: bold;\'>一段测试文字</span>,字号固定为<span style=\'font-weight: bold; font-style: italic; text-decoration-line: underline;\'>28px</span></span></div>',
|
content: '<div style=\'text-align: center;\'><span style=\'font-size: 28px;\'><span style=\'color: rgb(232, 107, 153); font-weight: bold;\'>一段测试文字</span>,字号固定为<span style=\'font-weight: bold; font-style: italic; text-decoration-line: underline;\'>28px</span></span></div>',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
elId: 'xxx3',
|
id: 'xxx3',
|
||||||
type: 'image',
|
type: 'image',
|
||||||
left: 80,
|
left: 80,
|
||||||
top: 250,
|
top: 250,
|
||||||
width: 180,
|
width: 180,
|
||||||
height: 180,
|
height: 180,
|
||||||
rotate: 0,
|
rotate: 0,
|
||||||
borderStyle: 'solid',
|
outline: {
|
||||||
borderWidth: 4,
|
width: 4,
|
||||||
borderColor: 'rgba(10,10,10,1)',
|
style: 'solid',
|
||||||
filter: '',
|
color: '#333'
|
||||||
|
},
|
||||||
clip: {
|
clip: {
|
||||||
range: [[30, 0], [100, 70]],
|
range: [[30, 0], [100, 70]],
|
||||||
shape: 'ellipse'
|
shape: 'ellipse'
|
||||||
},
|
},
|
||||||
lockRatio: false,
|
fixedRatio: false,
|
||||||
isLock: false,
|
lock: false,
|
||||||
imgUrl: 'https://img.lessonplan.cn/IMG/Show/ppt/3ab74e91-c34f-499d-9711-166e423d4dd6/1573622467064v2-7aa3ce420052983d91c6d01b47a7441d_hd.jpg',
|
src: 'https://img.lessonplan.cn/IMG/Show/ppt/3ab74e91-c34f-499d-9711-166e423d4dd6/1573622467064v2-7aa3ce420052983d91c6d01b47a7441d_hd.jpg',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
elId: 'xxx2',
|
id: 'xxx2',
|
||||||
type: 'image',
|
type: 'image',
|
||||||
left: 750,
|
left: 750,
|
||||||
top: 320,
|
top: 320,
|
||||||
width: 150,
|
width: 150,
|
||||||
height: 150,
|
height: 150,
|
||||||
rotate: 0,
|
rotate: 0,
|
||||||
borderStyle: 'solid',
|
outline: {
|
||||||
borderWidth: 6,
|
width: 6,
|
||||||
borderColor: 'rgba(10,10,10,1)',
|
style: 'solid',
|
||||||
filter: '',
|
color: '#333'
|
||||||
|
},
|
||||||
clip: {
|
clip: {
|
||||||
range: [[0, 0], [100, 100]],
|
range: [[0, 0], [100, 100]],
|
||||||
shape: 'roundRect'
|
shape: 'roundRect'
|
||||||
},
|
},
|
||||||
lockRatio: true,
|
fixedRatio: true,
|
||||||
isLock: false,
|
lock: false,
|
||||||
imgUrl: 'https://img.lessonplan.cn/IMG/Show/ppt/3ab74e91-c34f-499d-9711-166e423d4dd6/62d9adb3-e7a6-4dc4-a352-095cffb49f08/b1be1a2f-f893-47d3-a8a3-eac7d04d395f/1596159381259v2-b2c69096d25ae16bf6ca09e30add3e65_hd.jpg',
|
src: 'https://img.lessonplan.cn/IMG/Show/ppt/3ab74e91-c34f-499d-9711-166e423d4dd6/62d9adb3-e7a6-4dc4-a352-095cffb49f08/b1be1a2f-f893-47d3-a8a3-eac7d04d395f/1596159381259v2-b2c69096d25ae16bf6ca09e30add3e65_hd.jpg',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -70,7 +75,7 @@ export const slides: Slide[] = [
|
|||||||
id: 'sajd172',
|
id: 'sajd172',
|
||||||
elements: [
|
elements: [
|
||||||
{
|
{
|
||||||
elId: 'yyx1',
|
id: 'yyx1',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
left: 590,
|
left: 590,
|
||||||
top: 90,
|
top: 90,
|
||||||
@ -78,9 +83,7 @@ export const slides: Slide[] = [
|
|||||||
height: 188,
|
height: 188,
|
||||||
rotate: 0,
|
rotate: 0,
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
lineHeight: 1.5,
|
lock: false,
|
||||||
segmentSpacing: 10,
|
|
||||||
isLock: false,
|
|
||||||
content: '<div>😀 😐 😶 😜 🔔 ⭐ ⚡ 🔥 👍 💡 🔰 🎀 🎁 🥇 🏅 🏆 🎈 🎉 💎 🚧 ⛔ 📢 ⌛ ⏰ 🕒 🧩 🎵 📎 🔒 🔑 ⛳ 📌 📍 💬 📅 📈 📋 📜 📁 📱 💻 💾 🌏 🚚 🚡 🚢💧 🌐 🧭 💰 💳 🛒</div>',
|
content: '<div>😀 😐 😶 😜 🔔 ⭐ ⚡ 🔥 👍 💡 🔰 🎀 🎁 🥇 🏅 🏆 🎈 🎉 💎 🚧 ⛔ 📢 ⌛ ⏰ 🕒 🧩 🎵 📎 🔒 🔑 ⛳ 📌 📍 💬 📅 📈 📋 📜 📁 📱 💻 💾 🌏 🚚 🚡 🚢💧 🌐 🧭 💰 💳 🛒</div>',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -13,20 +13,20 @@ export const getters: GetterTree<State, State> = {
|
|||||||
if(!animations) return null
|
if(!animations) return null
|
||||||
|
|
||||||
const els = currentSlide.elements
|
const els = currentSlide.elements
|
||||||
const elIds = els.map(el => el.elId)
|
const elIds = els.map(el => el.id)
|
||||||
return animations.filter(animation => elIds.includes(animation.elId))
|
return animations.filter(animation => elIds.includes(animation.elId))
|
||||||
},
|
},
|
||||||
|
|
||||||
activeElementList(state) {
|
activeElementList(state) {
|
||||||
const currentSlide = state.slides[state.slideIndex]
|
const currentSlide = state.slides[state.slideIndex]
|
||||||
if(!currentSlide || !currentSlide.elements) return []
|
if(!currentSlide || !currentSlide.elements) return []
|
||||||
return currentSlide.elements.filter(element => state.activeElementIdList.includes(element.elId))
|
return currentSlide.elements.filter(element => state.activeElementIdList.includes(element.id))
|
||||||
},
|
},
|
||||||
|
|
||||||
handleElement(state) {
|
handleElement(state) {
|
||||||
const currentSlide = state.slides[state.slideIndex]
|
const currentSlide = state.slides[state.slideIndex]
|
||||||
if(!currentSlide || !currentSlide.elements) return null
|
if(!currentSlide || !currentSlide.elements) return null
|
||||||
return currentSlide.elements.find(element => state.handleElementId === element.elId) || null
|
return currentSlide.elements.find(element => state.handleElementId === element.id) || null
|
||||||
},
|
},
|
||||||
|
|
||||||
canUndo(state) {
|
canUndo(state) {
|
||||||
|
@ -6,7 +6,7 @@ import { FONT_NAMES } from '@/configs/fontName'
|
|||||||
import { isSupportFontFamily } from '@/utils/fontFamily'
|
import { isSupportFontFamily } from '@/utils/fontFamily'
|
||||||
|
|
||||||
interface UpdateElementData {
|
interface UpdateElementData {
|
||||||
elId: string | string[];
|
id: string | string[];
|
||||||
props: Partial<PPTElement>;
|
props: Partial<PPTElement>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,13 +91,13 @@ export const mutations: MutationTree<State> = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
[MutationTypes.UPDATE_ELEMENT](state, data: UpdateElementData) {
|
[MutationTypes.UPDATE_ELEMENT](state, data: UpdateElementData) {
|
||||||
const { elId, props } = data
|
const { id, props } = data
|
||||||
const elIdList = typeof elId === 'string' ? [elId] : elId
|
const elIdList = typeof id === 'string' ? [id] : id
|
||||||
|
|
||||||
const slideIndex = state.slideIndex
|
const slideIndex = state.slideIndex
|
||||||
const slide = state.slides[slideIndex]
|
const slide = state.slides[slideIndex]
|
||||||
const elements = slide.elements.map(el => {
|
const elements = slide.elements.map(el => {
|
||||||
return elIdList.includes(el.elId) ? { ...el, ...props } : el
|
return elIdList.includes(el.id) ? { ...el, ...props } : el
|
||||||
})
|
})
|
||||||
state.slides[slideIndex].elements = (elements as PPTElement[])
|
state.slides[slideIndex].elements = (elements as PPTElement[])
|
||||||
},
|
},
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
export type ElementType = 'text' | 'image' | 'shape' | 'line' | 'chart' | 'table'
|
export interface PPTElementShadow {
|
||||||
|
h: number;
|
||||||
|
v: number;
|
||||||
|
blur: number;
|
||||||
|
color: string;
|
||||||
|
}
|
||||||
|
|
||||||
export enum ElementTypes {
|
export enum ElementTypes {
|
||||||
TEXT = 'text',
|
TEXT = 'text',
|
||||||
@ -10,58 +15,68 @@ export enum ElementTypes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface PPTElementBaseProps {
|
export interface PPTElementBaseProps {
|
||||||
elId: string;
|
id: string;
|
||||||
left: number;
|
left: number;
|
||||||
top: number;
|
top: number;
|
||||||
isLock?: boolean;
|
lock?: boolean;
|
||||||
groupId?: string;
|
groupId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PPTElementSizeProps {
|
export interface PPTElementOutline {
|
||||||
|
style?: 'dashed' | 'solid';
|
||||||
|
width?: number;
|
||||||
|
color?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PPTTextElement extends PPTElementBaseProps {
|
||||||
|
type: 'text';
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
}
|
|
||||||
|
|
||||||
export interface PPTElementBorderProps {
|
|
||||||
borderStyle?: string;
|
|
||||||
borderWidth?: number;
|
|
||||||
borderColor?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PPTTextElement extends PPTElementBaseProps, PPTElementSizeProps, PPTElementBorderProps {
|
|
||||||
type: 'text';
|
|
||||||
content: string;
|
content: string;
|
||||||
rotate?: number;
|
rotate?: number;
|
||||||
|
outline?: PPTElementOutline;
|
||||||
fill?: string;
|
fill?: string;
|
||||||
opacity?: number;
|
opacity?: number;
|
||||||
lineHeight?: number;
|
shadow?: PPTElementShadow;
|
||||||
segmentSpacing?: number;
|
|
||||||
letterSpacing?: number;
|
|
||||||
shadow?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PPTImageElement extends PPTElementBaseProps, PPTElementSizeProps, PPTElementBorderProps {
|
export interface ImageElementFilters {
|
||||||
|
'blur': string;
|
||||||
|
'brightness': string;
|
||||||
|
'contrast': string;
|
||||||
|
'grayscale': string;
|
||||||
|
'saturate': string;
|
||||||
|
'hue-rotate': string;
|
||||||
|
'opacity': string;
|
||||||
|
}
|
||||||
|
export interface PPTImageElement extends PPTElementBaseProps {
|
||||||
type: 'image';
|
type: 'image';
|
||||||
lockRatio: boolean;
|
width: number;
|
||||||
imgUrl: string;
|
height: number;
|
||||||
|
fixedRatio: boolean;
|
||||||
|
src: string;
|
||||||
rotate?: number;
|
rotate?: number;
|
||||||
filter?: string;
|
outline?: PPTElementOutline;
|
||||||
|
filters?: ImageElementFilters;
|
||||||
clip?: {
|
clip?: {
|
||||||
range: [[number, number], [number, number]];
|
range: [[number, number], [number, number]];
|
||||||
shape: 'rect' | 'roundRect' | 'ellipse' | 'triangle' | 'pentagon' | 'rhombus' | 'star';
|
shape: 'rect' | 'roundRect' | 'ellipse' | 'triangle' | 'pentagon' | 'rhombus' | 'star';
|
||||||
};
|
};
|
||||||
flip?: { x?: number; y?: number };
|
flip?: { x?: number; y?: number };
|
||||||
shadow?: string;
|
shadow?: PPTElementShadow;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PPTShapeElement extends PPTElementBaseProps, PPTElementSizeProps, PPTElementBorderProps {
|
export interface PPTShapeElement extends PPTElementBaseProps {
|
||||||
type: 'shape';
|
type: 'shape';
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
svgCode: string;
|
svgCode: string;
|
||||||
lockRatio: boolean;
|
fixedRatio: boolean;
|
||||||
fill: string;
|
fill: string;
|
||||||
rotate?: number;
|
rotate?: number;
|
||||||
|
outline?: PPTElementOutline;
|
||||||
opacity?: number;
|
opacity?: number;
|
||||||
shadow?: string;
|
shadow?: PPTElementShadow;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PPTLineElement extends PPTElementBaseProps {
|
export interface PPTLineElement extends PPTElementBaseProps {
|
||||||
@ -71,14 +86,17 @@ export interface PPTLineElement extends PPTElementBaseProps {
|
|||||||
width: number;
|
width: number;
|
||||||
style: string;
|
style: string;
|
||||||
color: string;
|
color: string;
|
||||||
marker: [string, string];
|
points: [string, string];
|
||||||
lineType: string;
|
lineType: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PPTChartElement extends PPTElementBaseProps, PPTElementSizeProps, PPTElementBorderProps {
|
export interface PPTChartElement extends PPTElementBaseProps {
|
||||||
type: 'chart';
|
type: 'chart';
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
chartType: string;
|
chartType: string;
|
||||||
data: string;
|
data: string;
|
||||||
|
outline?: PPTElementOutline;
|
||||||
theme?: string;
|
theme?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,8 +106,10 @@ export interface TableElementCell {
|
|||||||
content: string;
|
content: string;
|
||||||
bgColor: string;
|
bgColor: string;
|
||||||
}
|
}
|
||||||
export interface PPTTableElement extends PPTElementBaseProps, PPTElementSizeProps {
|
export interface PPTTableElement extends PPTElementBaseProps {
|
||||||
type: 'table';
|
type: 'table';
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
borderTheme?: string;
|
borderTheme?: string;
|
||||||
theme?: string;
|
theme?: string;
|
||||||
rowSizes: number[];
|
rowSizes: number[];
|
||||||
@ -97,12 +117,7 @@ export interface PPTTableElement extends PPTElementBaseProps, PPTElementSizeProp
|
|||||||
data: TableElementCell[][];
|
data: TableElementCell[][];
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PPTElement = PPTTextElement |
|
export type PPTElement = PPTTextElement | PPTImageElement | PPTShapeElement | PPTLineElement | PPTChartElement | PPTTableElement
|
||||||
PPTImageElement |
|
|
||||||
PPTShapeElement |
|
|
||||||
PPTLineElement |
|
|
||||||
PPTChartElement |
|
|
||||||
PPTTableElement
|
|
||||||
|
|
||||||
export interface PPTAnimation {
|
export interface PPTAnimation {
|
||||||
elId: string;
|
elId: string;
|
||||||
@ -110,9 +125,14 @@ export interface PPTAnimation {
|
|||||||
duration: number;
|
duration: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SlideBackground {
|
||||||
|
type: 'solid' | 'image';
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface Slide {
|
export interface Slide {
|
||||||
id: string;
|
id: string;
|
||||||
elements: PPTElement[];
|
elements: PPTElement[];
|
||||||
background?: [string, string];
|
background?: SlideBackground;
|
||||||
animations?: PPTAnimation[];
|
animations?: PPTAnimation[];
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import { PPTElement } from '@/types/slides'
|
import { ElementTypes, PPTElement } from '@/types/slides'
|
||||||
|
|
||||||
// 获取矩形旋转后在画布中的位置范围
|
// 获取矩形旋转后在画布中的位置范围
|
||||||
interface RotatedElementData {
|
interface RotatedElementData {
|
||||||
@ -46,7 +46,7 @@ export const getRectRotatedRange = (element: RotatedElementData) => {
|
|||||||
export const getElementRange = (element: PPTElement) => {
|
export const getElementRange = (element: PPTElement) => {
|
||||||
let minX, maxX, minY, maxY
|
let minX, maxX, minY, maxY
|
||||||
|
|
||||||
if(element.type === 'line') {
|
if(element.type === ElementTypes.LINE) {
|
||||||
minX = element.left
|
minX = element.left
|
||||||
maxX = element.left + Math.max(element.start[0], element.end[0])
|
maxX = element.left + Math.max(element.start[0], element.end[0])
|
||||||
minY = element.top
|
minY = element.top
|
||||||
|
@ -4,10 +4,10 @@ interface ImageSize {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取图片的原始宽高
|
// 获取图片的原始宽高
|
||||||
export const getImageSize = (imgUrl: string): Promise<ImageSize> => {
|
export const getImageSize = (src: string): Promise<ImageSize> => {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
const img = document.createElement('img')
|
const img = document.createElement('img')
|
||||||
img.src = imgUrl
|
img.src = src
|
||||||
img.style.opacity = '0'
|
img.style.opacity = '0'
|
||||||
document.body.appendChild(img)
|
document.body.appendChild(img)
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ export default defineComponent({
|
|||||||
const store = useStore<State>()
|
const store = useStore<State>()
|
||||||
const activeElementIdList = computed(() => store.state.activeElementIdList)
|
const activeElementIdList = computed(() => store.state.activeElementIdList)
|
||||||
const canvasScale = computed(() => store.state.canvasScale)
|
const canvasScale = computed(() => store.state.canvasScale)
|
||||||
const localActiveElementList = computed(() => props.elementList.filter(el => activeElementIdList.value.includes(el.elId)))
|
const localActiveElementList = computed(() => props.elementList.filter(el => activeElementIdList.value.includes(el.id)))
|
||||||
|
|
||||||
const range = reactive({
|
const range = reactive({
|
||||||
minX: 0,
|
minX: 0,
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
import { Ref, computed, defineComponent } from 'vue'
|
import { Ref, computed, defineComponent } from 'vue'
|
||||||
import { useStore } from 'vuex'
|
import { useStore } from 'vuex'
|
||||||
import { State } from '@/store'
|
import { State } from '@/store'
|
||||||
|
import { SlideBackground } from '@/types/slides'
|
||||||
import GridLines from './GridLines.vue'
|
import GridLines from './GridLines.vue'
|
||||||
import useSlideBackgroundStyle from '@/hooks/useSlideBackgroundStyle'
|
import useSlideBackgroundStyle from '@/hooks/useSlideBackgroundStyle'
|
||||||
|
|
||||||
@ -25,7 +26,7 @@ export default defineComponent({
|
|||||||
setup() {
|
setup() {
|
||||||
const store = useStore<State>()
|
const store = useStore<State>()
|
||||||
const showGridLines = computed(() => store.state.showGridLines)
|
const showGridLines = computed(() => store.state.showGridLines)
|
||||||
const background: Ref<[string, string] | undefined> = computed(() => store.getters.currentSlide.background)
|
const background: Ref<SlideBackground | undefined> = computed(() => store.getters.currentSlide.background)
|
||||||
|
|
||||||
const { backgroundStyle } = useSlideBackgroundStyle(background)
|
const { backgroundStyle } = useSlideBackgroundStyle(background)
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ export default (
|
|||||||
const { addHistorySnapshot } = useHistorySnapshot()
|
const { addHistorySnapshot } = useHistorySnapshot()
|
||||||
|
|
||||||
const dragElement = (e: MouseEvent, element: PPTElement) => {
|
const dragElement = (e: MouseEvent, element: PPTElement) => {
|
||||||
if(!activeElementIdList.value.includes(element.elId)) return
|
if(!activeElementIdList.value.includes(element.id)) return
|
||||||
let isMouseDown = true
|
let isMouseDown = true
|
||||||
|
|
||||||
// 可视范围宽高,用于边缘对齐吸附
|
// 可视范围宽高,用于边缘对齐吸附
|
||||||
@ -27,7 +27,7 @@ export default (
|
|||||||
const edgeHeight = VIEWPORT_SIZE * VIEWPORT_ASPECT_RATIO
|
const edgeHeight = VIEWPORT_SIZE * VIEWPORT_ASPECT_RATIO
|
||||||
|
|
||||||
const originElementList: PPTElement[] = JSON.parse(JSON.stringify(elementList.value))
|
const originElementList: PPTElement[] = JSON.parse(JSON.stringify(elementList.value))
|
||||||
const originActiveElementList = originElementList.filter(el => activeElementIdList.value.includes(el.elId))
|
const originActiveElementList = originElementList.filter(el => activeElementIdList.value.includes(el.id))
|
||||||
|
|
||||||
const sorptionRange = 3
|
const sorptionRange = 3
|
||||||
const elOriginLeft = element.left
|
const elOriginLeft = element.left
|
||||||
@ -40,7 +40,7 @@ export default (
|
|||||||
|
|
||||||
let isMisoperation: boolean | null = null
|
let isMisoperation: boolean | null = null
|
||||||
|
|
||||||
const isActiveGroupElement = element.elId === activeGroupElementId.value
|
const isActiveGroupElement = element.id === activeGroupElementId.value
|
||||||
|
|
||||||
// 收集对齐参考线
|
// 收集对齐参考线
|
||||||
// 包括页面内出被操作元素以外的所有元素在页面内水平和垂直方向的范围和中心位置、页面边界和水平和垂直的中心位置
|
// 包括页面内出被操作元素以外的所有元素在页面内水平和垂直方向的范围和中心位置、页面边界和水平和垂直的中心位置
|
||||||
@ -50,8 +50,8 @@ export default (
|
|||||||
// 元素在页面内水平和垂直方向的范围和中心位置(需要特殊计算线条和被旋转的元素)
|
// 元素在页面内水平和垂直方向的范围和中心位置(需要特殊计算线条和被旋转的元素)
|
||||||
for(const el of elementList.value) {
|
for(const el of elementList.value) {
|
||||||
if(el.type === ElementTypes.LINE) continue
|
if(el.type === ElementTypes.LINE) continue
|
||||||
if(isActiveGroupElement && el.elId === element.elId) continue
|
if(isActiveGroupElement && el.id === element.id) continue
|
||||||
if(!isActiveGroupElement && activeElementIdList.value.includes(el.elId)) continue
|
if(!isActiveGroupElement && activeElementIdList.value.includes(el.id)) continue
|
||||||
|
|
||||||
let left, top, width, height
|
let left, top, width, height
|
||||||
if('rotate' in el && el.rotate) {
|
if('rotate' in el && el.rotate) {
|
||||||
@ -145,7 +145,7 @@ export default (
|
|||||||
targetMinY = yRange[0]
|
targetMinY = yRange[0]
|
||||||
targetMaxY = yRange[1]
|
targetMaxY = yRange[1]
|
||||||
}
|
}
|
||||||
else if(element.type === 'line') {
|
else if(element.type === ElementTypes.LINE) {
|
||||||
targetMinX = targetLeft
|
targetMinX = targetLeft
|
||||||
targetMaxX = targetLeft + Math.max(element.start[0], element.end[0])
|
targetMaxX = targetLeft + Math.max(element.start[0], element.end[0])
|
||||||
targetMinY = targetTop
|
targetMinY = targetTop
|
||||||
@ -179,7 +179,7 @@ export default (
|
|||||||
rightValues.push(xRange[1])
|
rightValues.push(xRange[1])
|
||||||
bottomValues.push(yRange[1])
|
bottomValues.push(yRange[1])
|
||||||
}
|
}
|
||||||
else if(element.type === 'line') {
|
else if(element.type === ElementTypes.LINE) {
|
||||||
leftValues.push(left)
|
leftValues.push(left)
|
||||||
topValues.push(top)
|
topValues.push(top)
|
||||||
rightValues.push(left + Math.max(element.start[0], element.end[0]))
|
rightValues.push(left + Math.max(element.start[0], element.end[0]))
|
||||||
@ -265,19 +265,19 @@ export default (
|
|||||||
// 非多选,或者当前操作的元素时激活的组合元素
|
// 非多选,或者当前操作的元素时激活的组合元素
|
||||||
if(activeElementIdList.value.length === 1 || isActiveGroupElement) {
|
if(activeElementIdList.value.length === 1 || isActiveGroupElement) {
|
||||||
elementList.value = elementList.value.map(el => {
|
elementList.value = elementList.value.map(el => {
|
||||||
return el.elId === element.elId ? { ...el, left: targetLeft, top: targetTop } : el
|
return el.id === element.id ? { ...el, left: targetLeft, top: targetTop } : el
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改元素位置,如果需要修改位置的元素不是被操作的元素(例如多选下的操作)
|
// 修改元素位置,如果需要修改位置的元素不是被操作的元素(例如多选下的操作)
|
||||||
// 那么其他非操作元素要移动的位置通过操作元素的移动偏移量计算
|
// 那么其他非操作元素要移动的位置通过操作元素的移动偏移量计算
|
||||||
else {
|
else {
|
||||||
const handleElement = elementList.value.find(el => el.elId === element.elId)
|
const handleElement = elementList.value.find(el => el.id === element.id)
|
||||||
if(!handleElement) return
|
if(!handleElement) return
|
||||||
|
|
||||||
elementList.value = elementList.value.map(el => {
|
elementList.value = elementList.value.map(el => {
|
||||||
if(activeElementIdList.value.includes(el.elId)) {
|
if(activeElementIdList.value.includes(el.id)) {
|
||||||
if(el.elId === element.elId) {
|
if(el.id === element.id) {
|
||||||
return {
|
return {
|
||||||
...el,
|
...el,
|
||||||
left: targetLeft,
|
left: targetLeft,
|
||||||
|
@ -110,19 +110,19 @@ export default (elementList: Ref<PPTElement[]>, viewportRef: Ref<HTMLElement | n
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 被锁定的元素除外
|
// 被锁定的元素除外
|
||||||
if(isInclude && !element.isLock) inRangeElementList.push(element)
|
if(isInclude && !element.lock) inRangeElementList.push(element)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 对于组合元素成员,必须所有成员都在选择范围中才算被选中
|
// 对于组合元素成员,必须所有成员都在选择范围中才算被选中
|
||||||
inRangeElementList = inRangeElementList.filter(inRangeElement => {
|
inRangeElementList = inRangeElementList.filter(inRangeElement => {
|
||||||
if(inRangeElement.groupId) {
|
if(inRangeElement.groupId) {
|
||||||
const inRangeElementIdList = inRangeElementList.map(inRangeElement => inRangeElement.elId)
|
const inRangeElementIdList = inRangeElementList.map(inRangeElement => inRangeElement.id)
|
||||||
const groupElementList = elementList.value.filter(element => element.groupId === inRangeElement.groupId)
|
const groupElementList = elementList.value.filter(element => element.groupId === inRangeElement.groupId)
|
||||||
return groupElementList.every(groupElement => inRangeElementIdList.includes(groupElement.elId))
|
return groupElementList.every(groupElement => inRangeElementIdList.includes(groupElement.id))
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
const inRangeElementIdList = inRangeElementList.map(inRangeElement => inRangeElement.elId)
|
const inRangeElementIdList = inRangeElementList.map(inRangeElement => inRangeElement.id)
|
||||||
if(inRangeElementIdList.length) store.commit(MutationTypes.SET_ACTIVE_ELEMENT_ID_LIST, inRangeElementIdList)
|
if(inRangeElementIdList.length) store.commit(MutationTypes.SET_ACTIVE_ELEMENT_ID_LIST, inRangeElementIdList)
|
||||||
|
|
||||||
mouseSelectionState.isShow = false
|
mouseSelectionState.isShow = false
|
||||||
|
@ -59,7 +59,7 @@ export default (elementList: Ref<PPTElement[]>, viewportRef: Ref<HTMLElement | n
|
|||||||
else if( angle < 0 && Math.abs(angle + 180) <= sorptionRange ) angle -= (angle + 180)
|
else if( angle < 0 && Math.abs(angle + 180) <= sorptionRange ) angle -= (angle + 180)
|
||||||
|
|
||||||
// 修改元素角度
|
// 修改元素角度
|
||||||
elementList.value = elementList.value.map(el => element.elId === el.elId ? { ...el, rotate: angle } : el)
|
elementList.value = elementList.value.map(el => element.id === el.id ? { ...el, rotate: angle } : el)
|
||||||
}
|
}
|
||||||
|
|
||||||
document.onmouseup = () => {
|
document.onmouseup = () => {
|
||||||
|
@ -102,8 +102,8 @@ export default (
|
|||||||
const elOriginWidth = element.width
|
const elOriginWidth = element.width
|
||||||
const elOriginHeight = element.height
|
const elOriginHeight = element.height
|
||||||
|
|
||||||
const isLockRatio = ctrlOrShiftKeyActive.value || ('lockRatio' in element && element.lockRatio)
|
const fixedRatio = ctrlOrShiftKeyActive.value || ('fixedRatio' in element && element.fixedRatio)
|
||||||
const lockRatio = elOriginWidth / elOriginHeight
|
const aspectRatio = elOriginWidth / elOriginHeight
|
||||||
|
|
||||||
const elRotate = ('rotate' in element && element.rotate) ? element.rotate : 0
|
const elRotate = ('rotate' in element && element.rotate) ? element.rotate : 0
|
||||||
const rotateRadian = Math.PI * elRotate / 180
|
const rotateRadian = Math.PI * elRotate / 180
|
||||||
@ -133,13 +133,13 @@ export default (
|
|||||||
else {
|
else {
|
||||||
const edgeWidth = VIEWPORT_SIZE
|
const edgeWidth = VIEWPORT_SIZE
|
||||||
const edgeHeight = VIEWPORT_SIZE * VIEWPORT_ASPECT_RATIO
|
const edgeHeight = VIEWPORT_SIZE * VIEWPORT_ASPECT_RATIO
|
||||||
const isActiveGroupElement = element.elId === activeGroupElementId.value
|
const isActiveGroupElement = element.id === activeGroupElementId.value
|
||||||
|
|
||||||
for(const el of elementList.value) {
|
for(const el of elementList.value) {
|
||||||
if('rotate' in el && el.rotate) continue
|
if('rotate' in el && el.rotate) continue
|
||||||
if(el.type === ElementTypes.LINE) continue
|
if(el.type === ElementTypes.LINE) continue
|
||||||
if(isActiveGroupElement && el.elId === element.elId) continue
|
if(isActiveGroupElement && el.id === element.id) continue
|
||||||
if(!isActiveGroupElement && activeElementIdList.value.includes(el.elId)) continue
|
if(!isActiveGroupElement && activeElementIdList.value.includes(el.id)) continue
|
||||||
|
|
||||||
const left = el.left
|
const left = el.left
|
||||||
const top = el.top
|
const top = el.top
|
||||||
@ -236,9 +236,9 @@ export default (
|
|||||||
let revisedY = (Math.cos(rotateRadian) * y - Math.sin(rotateRadian) * x) / canvasScale.value
|
let revisedY = (Math.cos(rotateRadian) * y - Math.sin(rotateRadian) * x) / canvasScale.value
|
||||||
|
|
||||||
// 锁定宽高比例
|
// 锁定宽高比例
|
||||||
if(isLockRatio) {
|
if(fixedRatio) {
|
||||||
if(command === OperatePoints.RIGHT_BOTTOM || command === OperatePoints.LEFT_TOP) revisedY = revisedX / lockRatio
|
if(command === OperatePoints.RIGHT_BOTTOM || command === OperatePoints.LEFT_TOP) revisedY = revisedX / aspectRatio
|
||||||
if(command === OperatePoints.LEFT_BOTTOM || command === OperatePoints.RIGHT_TOP) revisedY = -revisedX / lockRatio
|
if(command === OperatePoints.LEFT_BOTTOM || command === OperatePoints.RIGHT_TOP) revisedY = -revisedX / aspectRatio
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据不同的操作点分别计算元素缩放后的大小和位置
|
// 根据不同的操作点分别计算元素缩放后的大小和位置
|
||||||
@ -297,18 +297,18 @@ export default (
|
|||||||
let moveX = x / canvasScale.value
|
let moveX = x / canvasScale.value
|
||||||
let moveY = y / canvasScale.value
|
let moveY = y / canvasScale.value
|
||||||
|
|
||||||
if(isLockRatio) {
|
if(fixedRatio) {
|
||||||
if(command === OperatePoints.RIGHT_BOTTOM || command === OperatePoints.LEFT_TOP) moveY = moveX / lockRatio
|
if(command === OperatePoints.RIGHT_BOTTOM || command === OperatePoints.LEFT_TOP) moveY = moveX / aspectRatio
|
||||||
if(command === OperatePoints.LEFT_BOTTOM || command === OperatePoints.RIGHT_TOP) moveY = -moveX / lockRatio
|
if(command === OperatePoints.LEFT_BOTTOM || command === OperatePoints.RIGHT_TOP) moveY = -moveX / aspectRatio
|
||||||
}
|
}
|
||||||
|
|
||||||
if(command === OperatePoints.RIGHT_BOTTOM) {
|
if(command === OperatePoints.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
|
||||||
if(isLockRatio) {
|
if(fixedRatio) {
|
||||||
if(offsetY) moveX = moveY * lockRatio
|
if(offsetY) moveX = moveY * aspectRatio
|
||||||
else moveY = moveX / lockRatio
|
else moveY = moveX / aspectRatio
|
||||||
}
|
}
|
||||||
width = getSizeWithinRange(elOriginWidth + moveX)
|
width = getSizeWithinRange(elOriginWidth + moveX)
|
||||||
height = getSizeWithinRange(elOriginHeight + moveY)
|
height = getSizeWithinRange(elOriginHeight + moveY)
|
||||||
@ -317,9 +317,9 @@ export default (
|
|||||||
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
|
||||||
if(isLockRatio) {
|
if(fixedRatio) {
|
||||||
if(offsetY) moveX = -moveY * lockRatio
|
if(offsetY) moveX = -moveY * aspectRatio
|
||||||
else moveY = -moveX / lockRatio
|
else moveY = -moveX / aspectRatio
|
||||||
}
|
}
|
||||||
width = getSizeWithinRange(elOriginWidth - moveX)
|
width = getSizeWithinRange(elOriginWidth - moveX)
|
||||||
height = getSizeWithinRange(elOriginHeight + moveY)
|
height = getSizeWithinRange(elOriginHeight + moveY)
|
||||||
@ -329,9 +329,9 @@ export default (
|
|||||||
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
|
||||||
if(isLockRatio) {
|
if(fixedRatio) {
|
||||||
if(offsetY) moveX = moveY * lockRatio
|
if(offsetY) moveX = moveY * aspectRatio
|
||||||
else moveY = moveX / lockRatio
|
else moveY = moveX / aspectRatio
|
||||||
}
|
}
|
||||||
width = getSizeWithinRange(elOriginWidth - moveX)
|
width = getSizeWithinRange(elOriginWidth - moveX)
|
||||||
height = getSizeWithinRange(elOriginHeight - moveY)
|
height = getSizeWithinRange(elOriginHeight - moveY)
|
||||||
@ -342,9 +342,9 @@ export default (
|
|||||||
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
|
||||||
if(isLockRatio) {
|
if(fixedRatio) {
|
||||||
if(offsetY) moveX = -moveY * lockRatio
|
if(offsetY) moveX = -moveY * aspectRatio
|
||||||
else moveY = -moveX / lockRatio
|
else moveY = -moveX / aspectRatio
|
||||||
}
|
}
|
||||||
width = getSizeWithinRange(elOriginWidth + moveX)
|
width = getSizeWithinRange(elOriginWidth + moveX)
|
||||||
height = getSizeWithinRange(elOriginHeight - moveY)
|
height = getSizeWithinRange(elOriginHeight - moveY)
|
||||||
@ -374,7 +374,7 @@ export default (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
elementList.value = elementList.value.map(el => element.elId === el.elId ? { ...el, left, top, width, height } : el)
|
elementList.value = elementList.value.map(el => element.id === el.id ? { ...el, left, top, width, height } : el)
|
||||||
}
|
}
|
||||||
|
|
||||||
document.onmouseup = e => {
|
document.onmouseup = e => {
|
||||||
@ -396,7 +396,7 @@ export default (
|
|||||||
const { minX, maxX, minY, maxY } = range
|
const { minX, maxX, minY, maxY } = range
|
||||||
const operateWidth = maxX - minX
|
const operateWidth = maxX - minX
|
||||||
const operateHeight = maxY - minY
|
const operateHeight = maxY - minY
|
||||||
const lockRatio = operateWidth / operateHeight
|
const aspectRatio = operateWidth / operateHeight
|
||||||
|
|
||||||
const startPageX = e.pageX
|
const startPageX = e.pageX
|
||||||
const startPageY = e.pageY
|
const startPageY = e.pageY
|
||||||
@ -415,8 +415,8 @@ export default (
|
|||||||
|
|
||||||
// 锁定宽高比例
|
// 锁定宽高比例
|
||||||
if(ctrlOrShiftKeyActive.value) {
|
if(ctrlOrShiftKeyActive.value) {
|
||||||
if(command === OperatePoints.RIGHT_BOTTOM || command === OperatePoints.LEFT_TOP) y = x / lockRatio
|
if(command === OperatePoints.RIGHT_BOTTOM || command === OperatePoints.LEFT_TOP) y = x / aspectRatio
|
||||||
if(command === OperatePoints.LEFT_BOTTOM || command === OperatePoints.RIGHT_TOP) y = -x / lockRatio
|
if(command === OperatePoints.LEFT_BOTTOM || command === OperatePoints.RIGHT_TOP) y = -x / aspectRatio
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取鼠标缩放时当前所有激活元素的范围
|
// 获取鼠标缩放时当前所有激活元素的范围
|
||||||
@ -468,8 +468,8 @@ export default (
|
|||||||
// 根据上面计算的比例,修改所有被激活元素的位置大小
|
// 根据上面计算的比例,修改所有被激活元素的位置大小
|
||||||
// 宽高通过乘以对应的比例得到,位置通过将被操作元素在所有元素整体中的相对位置乘以对应比例获得
|
// 宽高通过乘以对应的比例得到,位置通过将被操作元素在所有元素整体中的相对位置乘以对应比例获得
|
||||||
elementList.value = elementList.value.map(el => {
|
elementList.value = elementList.value.map(el => {
|
||||||
if((el.type === ElementTypes.IMAGE || el.type === ElementTypes.SHAPE) && activeElementIdList.value.includes(el.elId)) {
|
if((el.type === ElementTypes.IMAGE || el.type === ElementTypes.SHAPE) && activeElementIdList.value.includes(el.id)) {
|
||||||
const originElement = originElementList.find(originEl => originEl.elId === el.elId) as PPTImageElement | PPTShapeElement
|
const originElement = originElementList.find(originEl => originEl.id === el.id) as PPTImageElement | PPTShapeElement
|
||||||
return {
|
return {
|
||||||
...el,
|
...el,
|
||||||
width: originElement.width * widthScale,
|
width: originElement.width * widthScale,
|
||||||
|
@ -19,25 +19,25 @@ export default (
|
|||||||
if(!editorAreaFocus.value) store.commit(MutationTypes.SET_EDITORAREA_FOCUS, true)
|
if(!editorAreaFocus.value) store.commit(MutationTypes.SET_EDITORAREA_FOCUS, true)
|
||||||
|
|
||||||
// 如果被点击的元素处于未激活状态,则将他设置为激活元素(单选),或者加入到激活元素中(多选)
|
// 如果被点击的元素处于未激活状态,则将他设置为激活元素(单选),或者加入到激活元素中(多选)
|
||||||
if(!activeElementIdList.value.includes(element.elId)) {
|
if(!activeElementIdList.value.includes(element.id)) {
|
||||||
let newActiveIdList: string[] = []
|
let newActiveIdList: string[] = []
|
||||||
|
|
||||||
if(ctrlOrShiftKeyActive.value) {
|
if(ctrlOrShiftKeyActive.value) {
|
||||||
newActiveIdList = [...activeElementIdList.value, element.elId]
|
newActiveIdList = [...activeElementIdList.value, element.id]
|
||||||
}
|
}
|
||||||
else newActiveIdList = [element.elId]
|
else newActiveIdList = [element.id]
|
||||||
|
|
||||||
// 同时如果该元素是分组成员,需要将和他同组的元素一起激活
|
// 同时如果该元素是分组成员,需要将和他同组的元素一起激活
|
||||||
if(element.groupId) {
|
if(element.groupId) {
|
||||||
const groupMembersId: string[] = []
|
const groupMembersId: string[] = []
|
||||||
elementList.value.forEach((el: PPTElement) => {
|
elementList.value.forEach((el: PPTElement) => {
|
||||||
if(el.groupId === element.groupId) groupMembersId.push(el.elId)
|
if(el.groupId === element.groupId) groupMembersId.push(el.id)
|
||||||
})
|
})
|
||||||
newActiveIdList = [...newActiveIdList, ...groupMembersId]
|
newActiveIdList = [...newActiveIdList, ...groupMembersId]
|
||||||
}
|
}
|
||||||
|
|
||||||
store.commit(MutationTypes.SET_ACTIVE_ELEMENT_ID_LIST, uniq(newActiveIdList))
|
store.commit(MutationTypes.SET_ACTIVE_ELEMENT_ID_LIST, uniq(newActiveIdList))
|
||||||
store.commit(MutationTypes.SET_HANDLE_ELEMENT_ID, element.elId)
|
store.commit(MutationTypes.SET_HANDLE_ELEMENT_ID, element.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果被点击的元素已激活,且按下了多选按钮,则取消其激活状态(除非该元素或分组是最后的一个激活元素)
|
// 如果被点击的元素已激活,且按下了多选按钮,则取消其激活状态(除非该元素或分组是最后的一个激活元素)
|
||||||
@ -48,12 +48,12 @@ export default (
|
|||||||
if(element.groupId) {
|
if(element.groupId) {
|
||||||
const groupMembersId: string[] = []
|
const groupMembersId: string[] = []
|
||||||
elementList.value.forEach((el: PPTElement) => {
|
elementList.value.forEach((el: PPTElement) => {
|
||||||
if(el.groupId === element.groupId) groupMembersId.push(el.elId)
|
if(el.groupId === element.groupId) groupMembersId.push(el.id)
|
||||||
})
|
})
|
||||||
newActiveIdList = activeElementIdList.value.filter(elId => !groupMembersId.includes(elId))
|
newActiveIdList = activeElementIdList.value.filter(id => !groupMembersId.includes(id))
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
newActiveIdList = activeElementIdList.value.filter(elId => elId !== element.elId)
|
newActiveIdList = activeElementIdList.value.filter(id => id !== element.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
if(newActiveIdList.length > 0) {
|
if(newActiveIdList.length > 0) {
|
||||||
@ -62,11 +62,11 @@ export default (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 如果被点击的元素已激活,且没有按下多选按钮,且该元素不是当前操作元素,则将其设置为当前操作元素
|
// 如果被点击的元素已激活,且没有按下多选按钮,且该元素不是当前操作元素,则将其设置为当前操作元素
|
||||||
else if(handleElementId.value !== element.elId) {
|
else if(handleElementId.value !== element.id) {
|
||||||
store.commit(MutationTypes.SET_HANDLE_ELEMENT_ID, element.elId)
|
store.commit(MutationTypes.SET_HANDLE_ELEMENT_ID, element.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
else if(activeGroupElementId.value !== element.elId && element.groupId) {
|
else if(activeGroupElementId.value !== element.id && element.groupId) {
|
||||||
const startPageX = e.pageX
|
const startPageX = e.pageX
|
||||||
const startPageY = e.pageY
|
const startPageY = e.pageY
|
||||||
|
|
||||||
@ -75,7 +75,7 @@ export default (
|
|||||||
const currentPageY = e.pageY
|
const currentPageY = e.pageY
|
||||||
|
|
||||||
if(startPageX === currentPageX && startPageY === currentPageY) {
|
if(startPageX === currentPageX && startPageY === currentPageY) {
|
||||||
activeGroupElementId.value = element.elId
|
activeGroupElementId.value = element.id
|
||||||
;(e.target as HTMLElement).onmouseup = null
|
;(e.target as HTMLElement).onmouseup = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -85,8 +85,8 @@ export default (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const selectAllElement = () => {
|
const selectAllElement = () => {
|
||||||
const unlockedElements = elementList.value.filter(el => !el.isLock)
|
const unlockedElements = elementList.value.filter(el => !el.lock)
|
||||||
const newActiveElementIdList = unlockedElements.map(el => el.elId)
|
const newActiveElementIdList = unlockedElements.map(el => el.id)
|
||||||
store.commit(MutationTypes.SET_ACTIVE_ELEMENT_ID_LIST, newActiveElementIdList)
|
store.commit(MutationTypes.SET_ACTIVE_ELEMENT_ID_LIST, newActiveElementIdList)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,12 +42,12 @@
|
|||||||
|
|
||||||
<EditableElement
|
<EditableElement
|
||||||
v-for="(element, index) in elementList"
|
v-for="(element, index) in elementList"
|
||||||
:key="element.elId"
|
:key="element.id"
|
||||||
:elementInfo="element"
|
:elementInfo="element"
|
||||||
:elementIndex="index + 1"
|
:elementIndex="index + 1"
|
||||||
:isActive="activeElementIdList.includes(element.elId)"
|
:isActive="activeElementIdList.includes(element.id)"
|
||||||
:isHandleEl="element.elId === handleElementId"
|
:isHandleEl="element.id === handleElementId"
|
||||||
:isActiveGroupElement="activeGroupElementId === element.elId"
|
:isActiveGroupElement="activeGroupElementId === element.id"
|
||||||
:isMultiSelect="activeElementIdList.length > 1"
|
:isMultiSelect="activeElementIdList.length > 1"
|
||||||
:selectElement="selectElement"
|
:selectElement="selectElement"
|
||||||
:rotateElement="rotateElement"
|
:rotateElement="rotateElement"
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
<div class="background" :style="{ ...backgroundStyle }"></div>
|
<div class="background" :style="{ ...backgroundStyle }"></div>
|
||||||
<BaseElement
|
<BaseElement
|
||||||
v-for="(element, index) in slide.elements"
|
v-for="(element, index) in slide.elements"
|
||||||
:key="element.elId"
|
:key="element.id"
|
||||||
:elementInfo="element"
|
:elementInfo="element"
|
||||||
:elementIndex="index + 1"
|
:elementIndex="index + 1"
|
||||||
/>
|
/>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<div
|
<div
|
||||||
class="editable-element"
|
class="editable-element"
|
||||||
ref="elementRef"
|
ref="elementRef"
|
||||||
:id="'editable-element-' + elementInfo.elId"
|
:id="'editable-element-' + elementInfo.id"
|
||||||
:style="{ zIndex: elementIndex }"
|
:style="{ zIndex: elementIndex }"
|
||||||
>
|
>
|
||||||
<component
|
<component
|
||||||
@ -105,7 +105,7 @@ export default defineComponent({
|
|||||||
const { copyElement, cutElement } = useCopyAndPasteElement()
|
const { copyElement, cutElement } = useCopyAndPasteElement()
|
||||||
|
|
||||||
const contextmenus = (): ContextmenuItem[] => {
|
const contextmenus = (): ContextmenuItem[] => {
|
||||||
if(props.elementInfo.isLock) {
|
if(props.elementInfo.lock) {
|
||||||
return [{
|
return [{
|
||||||
text: '解锁',
|
text: '解锁',
|
||||||
icon: 'icon-unlock',
|
icon: 'icon-unlock',
|
||||||
|
@ -1,59 +0,0 @@
|
|||||||
<template>
|
|
||||||
<SvgWrapper
|
|
||||||
class="element-border"
|
|
||||||
overflow="visible"
|
|
||||||
:width="width"
|
|
||||||
:height="height"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
vector-effect="non-scaling-stroke"
|
|
||||||
stroke-linecap="butt"
|
|
||||||
stroke-miterlimit="8"
|
|
||||||
stroke-linejoin
|
|
||||||
fill="transparent"
|
|
||||||
:d="`M0,0 L${width},0 L${width},${height} L0,${height} Z`"
|
|
||||||
:stroke="borderColor"
|
|
||||||
:stroke-width="borderWidth"
|
|
||||||
:stroke-dasharray="borderStyle === 'dashed' ? '12 9' : '0 0'"
|
|
||||||
></path>
|
|
||||||
</SvgWrapper>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import SvgWrapper from '@/components/SvgWrapper.vue'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'element-border',
|
|
||||||
components: {
|
|
||||||
SvgWrapper,
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
width: {
|
|
||||||
type: Number,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
height: {
|
|
||||||
type: Number,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
borderColor: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
borderWidth: {
|
|
||||||
type: Number,
|
|
||||||
},
|
|
||||||
borderStyle: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
svg {
|
|
||||||
overflow: visible;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
69
src/views/_common/_element/ElementOutline.vue
Normal file
69
src/views/_common/_element/ElementOutline.vue
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
<template>
|
||||||
|
<SvgWrapper
|
||||||
|
class="element-outline"
|
||||||
|
overflow="visible"
|
||||||
|
:width="width"
|
||||||
|
:height="height"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
vector-effect="non-scaling-stroke"
|
||||||
|
stroke-linecap="butt"
|
||||||
|
stroke-miterlimit="8"
|
||||||
|
stroke-linejoin
|
||||||
|
fill="transparent"
|
||||||
|
: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'"
|
||||||
|
></path>
|
||||||
|
</SvgWrapper>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { PropType, defineComponent, toRef } from 'vue'
|
||||||
|
import { PPTElementOutline } from '@/types/slides'
|
||||||
|
import SvgWrapper from '@/components/SvgWrapper.vue'
|
||||||
|
import useElementOutline from '@/views/_common/_element/hooks/useElementOutline'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'element-outline',
|
||||||
|
components: {
|
||||||
|
SvgWrapper,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
width: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
outline: {
|
||||||
|
type: Object as PropType<PPTElementOutline>
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setup(props) {
|
||||||
|
const {
|
||||||
|
outlineWidth,
|
||||||
|
outlineStyle,
|
||||||
|
outlineColor,
|
||||||
|
} = useElementOutline(toRef(props, 'outline'))
|
||||||
|
|
||||||
|
return {
|
||||||
|
outlineWidth,
|
||||||
|
outlineStyle,
|
||||||
|
outlineColor,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
svg {
|
||||||
|
overflow: visible;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
</style>
|
@ -12,40 +12,34 @@
|
|||||||
<div
|
<div
|
||||||
class="element-content"
|
class="element-content"
|
||||||
:style="{
|
:style="{
|
||||||
filter: elementInfo.shadow ? `drop-shadow(${elementInfo.shadow})` : '',
|
filter: shadowStyle ? `drop-shadow(${shadowStyle})` : '',
|
||||||
transform: flip,
|
transform: flip,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<ImageRectBorder
|
<ImageRectOutline
|
||||||
v-if="clipShape.type === 'rect'"
|
v-if="clipShape.type === 'rect'"
|
||||||
:width="elementInfo.width"
|
:width="elementInfo.width"
|
||||||
:height="elementInfo.height"
|
:height="elementInfo.height"
|
||||||
:radius="clipShape.radius"
|
:radius="clipShape.radius"
|
||||||
:borderColor="elementInfo.borderColor"
|
:outline="elementInfo.outline"
|
||||||
:borderWidth="elementInfo.borderWidth"
|
|
||||||
:borderStyle="elementInfo.borderStyle"
|
|
||||||
/>
|
/>
|
||||||
<ImageEllipseBorder
|
<ImageEllipseOutline
|
||||||
v-else-if="clipShape.type === 'ellipse'"
|
v-else-if="clipShape.type === 'ellipse'"
|
||||||
:width="elementInfo.width"
|
:width="elementInfo.width"
|
||||||
:height="elementInfo.height"
|
:height="elementInfo.height"
|
||||||
:borderColor="elementInfo.borderColor"
|
:outline="elementInfo.outline"
|
||||||
:borderWidth="elementInfo.borderWidth"
|
|
||||||
:borderStyle="elementInfo.borderStyle"
|
|
||||||
/>
|
/>
|
||||||
<ImagePolygonBorder
|
<ImagePolygonOutline
|
||||||
v-else-if="clipShape.type === 'polygon'"
|
v-else-if="clipShape.type === 'polygon'"
|
||||||
:width="elementInfo.width"
|
:width="elementInfo.width"
|
||||||
:height="elementInfo.height"
|
:height="elementInfo.height"
|
||||||
:createPath="clipShape.createPath"
|
:createPath="clipShape.createPath"
|
||||||
:borderColor="elementInfo.borderColor"
|
:outline="elementInfo.outline"
|
||||||
:borderWidth="elementInfo.borderWidth"
|
|
||||||
:borderStyle="elementInfo.borderStyle"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="img-wrapper" :style="{ clipPath: clipShape.style }">
|
<div class="img-wrapper" :style="{ clipPath: clipShape.style }">
|
||||||
<img
|
<img
|
||||||
:src="elementInfo.imgUrl"
|
:src="elementInfo.src"
|
||||||
:draggable="false"
|
:draggable="false"
|
||||||
:style="{
|
:style="{
|
||||||
top: imgPosition.top,
|
top: imgPosition.top,
|
||||||
@ -67,16 +61,18 @@ import { computed, defineComponent, PropType } from 'vue'
|
|||||||
import { PPTImageElement } from '@/types/slides'
|
import { PPTImageElement } from '@/types/slides'
|
||||||
import { CLIPPATHS, ClipPathTypes } from '@/configs/imageClip'
|
import { CLIPPATHS, ClipPathTypes } from '@/configs/imageClip'
|
||||||
|
|
||||||
import ImageRectBorder from './ImageRectBorder.vue'
|
import ImageRectOutline from './ImageRectOutline.vue'
|
||||||
import ImageEllipseBorder from './ImageEllipseBorder.vue'
|
import ImageEllipseOutline from './ImageEllipseOutline.vue'
|
||||||
import ImagePolygonBorder from './ImagePolygonBorder.vue'
|
import ImagePolygonOutline from './ImagePolygonOutline.vue'
|
||||||
|
|
||||||
|
import useElementShadow from '@/views/_common/_element/hooks/useElementShadow'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'base-element-image',
|
name: 'base-element-image',
|
||||||
components: {
|
components: {
|
||||||
ImageRectBorder,
|
ImageRectOutline,
|
||||||
ImageEllipseBorder,
|
ImageEllipseOutline,
|
||||||
ImagePolygonBorder,
|
ImagePolygonOutline,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
elementInfo: {
|
elementInfo: {
|
||||||
@ -118,10 +114,10 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const filter = computed(() => {
|
const filter = computed(() => {
|
||||||
if(!props.elementInfo.filter) return ''
|
if(!props.elementInfo.filters) return ''
|
||||||
let filter = ''
|
let filter = ''
|
||||||
for(const key of Object.keys(props.elementInfo.filter)) {
|
for(const key of Object.keys(props.elementInfo.filters)) {
|
||||||
filter += `${key}(${props.elementInfo.filter[key]}) `
|
filter += `${key}(${props.elementInfo.filters[key]}) `
|
||||||
}
|
}
|
||||||
return filter
|
return filter
|
||||||
})
|
})
|
||||||
@ -135,11 +131,15 @@ export default defineComponent({
|
|||||||
return ''
|
return ''
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const shadow = computed(() => props.elementInfo.shadow)
|
||||||
|
const { shadowStyle } = useElementShadow(shadow)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
imgPosition,
|
imgPosition,
|
||||||
clipShape,
|
clipShape,
|
||||||
filter,
|
filter,
|
||||||
flip,
|
flip,
|
||||||
|
shadowStyle,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
class="bottom-img"
|
class="bottom-img"
|
||||||
:src="imgUrl"
|
:src="src"
|
||||||
:draggable="false"
|
:draggable="false"
|
||||||
alt=""
|
alt=""
|
||||||
:style="bottomImgPositionStyle"
|
:style="bottomImgPositionStyle"
|
||||||
@ -21,7 +21,7 @@
|
|||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
class="top-img"
|
class="top-img"
|
||||||
:src="imgUrl"
|
:src="src"
|
||||||
:draggable="false"
|
:draggable="false"
|
||||||
alt=""
|
alt=""
|
||||||
:style="topImgPositionStyle"
|
:style="topImgPositionStyle"
|
||||||
@ -78,7 +78,7 @@ export default defineComponent({
|
|||||||
SvgWrapper,
|
SvgWrapper,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
imgUrl: {
|
src: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
@ -1,66 +0,0 @@
|
|||||||
<template>
|
|
||||||
<SvgWrapper
|
|
||||||
class="image-ellipse-border"
|
|
||||||
overflow="visible"
|
|
||||||
:width="width"
|
|
||||||
:height="height"
|
|
||||||
>
|
|
||||||
<ellipse
|
|
||||||
vector-effect="non-scaling-stroke"
|
|
||||||
stroke-linecap="butt"
|
|
||||||
stroke-miterlimit="8"
|
|
||||||
stroke-linejoin
|
|
||||||
fill="transparent"
|
|
||||||
:cx="width / 2"
|
|
||||||
:cy="height / 2"
|
|
||||||
:rx="width / 2"
|
|
||||||
:ry="height / 2"
|
|
||||||
:stroke="borderColor"
|
|
||||||
:stroke-width="borderWidth"
|
|
||||||
:stroke-dasharray="borderStyle === 'dashed' ? '12 9' : '0 0'"
|
|
||||||
></ellipse>
|
|
||||||
</SvgWrapper>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import SvgWrapper from '@/components/SvgWrapper.vue'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'image-ellipse-border',
|
|
||||||
components: {
|
|
||||||
SvgWrapper,
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
width: {
|
|
||||||
type: Number,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
height: {
|
|
||||||
type: Number,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
borderColor: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
borderWidth: {
|
|
||||||
type: Number,
|
|
||||||
default: 0,
|
|
||||||
},
|
|
||||||
borderStyle: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
svg {
|
|
||||||
overflow: visible;
|
|
||||||
position: absolute;
|
|
||||||
z-index: 2;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -0,0 +1,73 @@
|
|||||||
|
<template>
|
||||||
|
<SvgWrapper
|
||||||
|
class="image-ellipse-outline"
|
||||||
|
overflow="visible"
|
||||||
|
:width="width"
|
||||||
|
:height="height"
|
||||||
|
>
|
||||||
|
<ellipse
|
||||||
|
vector-effect="non-scaling-stroke"
|
||||||
|
stroke-linecap="butt"
|
||||||
|
stroke-miterlimit="8"
|
||||||
|
stroke-linejoin
|
||||||
|
fill="transparent"
|
||||||
|
:cx="width / 2"
|
||||||
|
:cy="height / 2"
|
||||||
|
:rx="width / 2"
|
||||||
|
:ry="height / 2"
|
||||||
|
:stroke="outlineColor"
|
||||||
|
:stroke-width="outlineWidth"
|
||||||
|
:stroke-dasharray="outlineStyle === 'dashed' ? '12 9' : '0 0'"
|
||||||
|
></ellipse>
|
||||||
|
</SvgWrapper>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { PropType, defineComponent, toRef } from 'vue'
|
||||||
|
import { PPTElementOutline } from '@/types/slides'
|
||||||
|
import SvgWrapper from '@/components/SvgWrapper.vue'
|
||||||
|
import useElementOutline from '@/views/_common/_element/hooks/useElementOutline'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'image-ellipse-outline',
|
||||||
|
components: {
|
||||||
|
SvgWrapper,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
width: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
outline: {
|
||||||
|
type: Object as PropType<PPTElementOutline>
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setup(props) {
|
||||||
|
const {
|
||||||
|
outlineWidth,
|
||||||
|
outlineStyle,
|
||||||
|
outlineColor,
|
||||||
|
} = useElementOutline(toRef(props, 'outline'))
|
||||||
|
|
||||||
|
return {
|
||||||
|
outlineWidth,
|
||||||
|
outlineStyle,
|
||||||
|
outlineColor,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
svg {
|
||||||
|
overflow: visible;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 2;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,67 +0,0 @@
|
|||||||
<template>
|
|
||||||
<SvgWrapper
|
|
||||||
class="image-polygon-border"
|
|
||||||
overflow="visible"
|
|
||||||
:width="width"
|
|
||||||
:height="height"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
vector-effect="non-scaling-stroke"
|
|
||||||
stroke-linecap="butt"
|
|
||||||
stroke-miterlimit="8"
|
|
||||||
stroke-linejoin
|
|
||||||
fill="transparent"
|
|
||||||
:d="createPath(width, height)"
|
|
||||||
:stroke="borderColor"
|
|
||||||
:stroke-width="borderWidth"
|
|
||||||
:stroke-dasharray="borderStyle === 'dashed' ? '12 9' : '0 0'"
|
|
||||||
></path>
|
|
||||||
</SvgWrapper>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import SvgWrapper from '@/components/SvgWrapper.vue'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'image-polygon-border',
|
|
||||||
components: {
|
|
||||||
SvgWrapper,
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
width: {
|
|
||||||
type: Number,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
height: {
|
|
||||||
type: Number,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
borderColor: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
borderWidth: {
|
|
||||||
type: Number,
|
|
||||||
default: 0,
|
|
||||||
},
|
|
||||||
borderStyle: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
createPath: {
|
|
||||||
type: Function,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
svg {
|
|
||||||
overflow: visible;
|
|
||||||
position: absolute;
|
|
||||||
z-index: 2;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -0,0 +1,74 @@
|
|||||||
|
<template>
|
||||||
|
<SvgWrapper
|
||||||
|
class="image-polygon-outline"
|
||||||
|
overflow="visible"
|
||||||
|
:width="width"
|
||||||
|
:height="height"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
vector-effect="non-scaling-stroke"
|
||||||
|
stroke-linecap="butt"
|
||||||
|
stroke-miterlimit="8"
|
||||||
|
stroke-linejoin
|
||||||
|
fill="transparent"
|
||||||
|
:d="createPath(width, height)"
|
||||||
|
:stroke="outlineColor"
|
||||||
|
:stroke-width="outlineWidth"
|
||||||
|
:stroke-dasharray="outlineStyle === 'dashed' ? '12 9' : '0 0'"
|
||||||
|
></path>
|
||||||
|
</SvgWrapper>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { PropType, defineComponent, toRef } from 'vue'
|
||||||
|
import { PPTElementOutline } from '@/types/slides'
|
||||||
|
import SvgWrapper from '@/components/SvgWrapper.vue'
|
||||||
|
import useElementOutline from '@/views/_common/_element/hooks/useElementOutline'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'image-polygon-outline',
|
||||||
|
components: {
|
||||||
|
SvgWrapper,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
width: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
outline: {
|
||||||
|
type: Object as PropType<PPTElementOutline>
|
||||||
|
},
|
||||||
|
createPath: {
|
||||||
|
type: Function,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setup(props) {
|
||||||
|
const {
|
||||||
|
outlineWidth,
|
||||||
|
outlineStyle,
|
||||||
|
outlineColor,
|
||||||
|
} = useElementOutline(toRef(props, 'outline'))
|
||||||
|
|
||||||
|
return {
|
||||||
|
outlineWidth,
|
||||||
|
outlineStyle,
|
||||||
|
outlineColor,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
svg {
|
||||||
|
overflow: visible;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 2;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<SvgWrapper
|
<SvgWrapper
|
||||||
class="image-rect-border"
|
class="image-rect-outline"
|
||||||
overflow="visible"
|
overflow="visible"
|
||||||
:width="width"
|
:width="width"
|
||||||
:height="height"
|
:height="height"
|
||||||
@ -15,18 +15,21 @@
|
|||||||
:ry="radius"
|
:ry="radius"
|
||||||
:width="width"
|
:width="width"
|
||||||
:height="height"
|
:height="height"
|
||||||
:stroke="borderColor"
|
:stroke="outlineColor"
|
||||||
:stroke-width="borderWidth"
|
:stroke-width="outlineWidth"
|
||||||
:stroke-dasharray="borderStyle === 'dashed' ? '12 9' : '0 0'"
|
:stroke-dasharray="outlineStyle === 'dashed' ? '12 9' : '0 0'"
|
||||||
></rect>
|
></rect>
|
||||||
</SvgWrapper>
|
</SvgWrapper>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { PropType, defineComponent, toRef } from 'vue'
|
||||||
|
import { PPTElementOutline } from '@/types/slides'
|
||||||
import SvgWrapper from '@/components/SvgWrapper.vue'
|
import SvgWrapper from '@/components/SvgWrapper.vue'
|
||||||
|
import useElementOutline from '@/views/_common/_element/hooks/useElementOutline'
|
||||||
|
|
||||||
export default {
|
export default defineComponent({
|
||||||
name: 'image-rect-border',
|
name: 'image-rect-outline',
|
||||||
components: {
|
components: {
|
||||||
SvgWrapper,
|
SvgWrapper,
|
||||||
},
|
},
|
||||||
@ -39,24 +42,28 @@ export default {
|
|||||||
type: Number,
|
type: Number,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
borderColor: {
|
outline: {
|
||||||
type: String,
|
type: Object as PropType<PPTElementOutline>
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
borderWidth: {
|
|
||||||
type: Number,
|
|
||||||
default: 0,
|
|
||||||
},
|
|
||||||
borderStyle: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
},
|
||||||
radius: {
|
radius: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '0',
|
default: '0',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
setup(props) {
|
||||||
|
const {
|
||||||
|
outlineWidth,
|
||||||
|
outlineStyle,
|
||||||
|
outlineColor,
|
||||||
|
} = useElementOutline(toRef(props, 'outline'))
|
||||||
|
|
||||||
|
return {
|
||||||
|
outlineWidth,
|
||||||
|
outlineStyle,
|
||||||
|
outlineColor,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="editable-element image"
|
class="editable-element image"
|
||||||
:class="{ 'lock': elementInfo.isLock }"
|
:class="{ 'lock': elementInfo.lock }"
|
||||||
:style="{
|
:style="{
|
||||||
top: elementInfo.top + 'px',
|
top: elementInfo.top + 'px',
|
||||||
left: elementInfo.left + 'px',
|
left: elementInfo.left + 'px',
|
||||||
@ -13,7 +13,7 @@
|
|||||||
>
|
>
|
||||||
<ImageClip
|
<ImageClip
|
||||||
v-if="isCliping"
|
v-if="isCliping"
|
||||||
:imgUrl="elementInfo.imgUrl"
|
:src="elementInfo.src"
|
||||||
:clipData="elementInfo.clip"
|
:clipData="elementInfo.clip"
|
||||||
:canvasScale="canvasScale"
|
:canvasScale="canvasScale"
|
||||||
:width="elementInfo.width"
|
:width="elementInfo.width"
|
||||||
@ -29,40 +29,34 @@
|
|||||||
v-if="!isCliping"
|
v-if="!isCliping"
|
||||||
v-contextmenu="contextmenus"
|
v-contextmenu="contextmenus"
|
||||||
:style="{
|
:style="{
|
||||||
filter: elementInfo.shadow ? `drop-shadow(${elementInfo.shadow})` : '',
|
filter: shadowStyle ? `drop-shadow(${shadowStyle})` : '',
|
||||||
transform: flip,
|
transform: flip,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<ImageRectBorder
|
<ImageRectOutline
|
||||||
v-if="clipShape.type === 'rect'"
|
v-if="clipShape.type === 'rect'"
|
||||||
:width="elementInfo.width"
|
:width="elementInfo.width"
|
||||||
:height="elementInfo.height"
|
:height="elementInfo.height"
|
||||||
:radius="clipShape.radius"
|
:radius="clipShape.radius"
|
||||||
:borderColor="elementInfo.borderColor"
|
:outline="elementInfo.outline"
|
||||||
:borderWidth="elementInfo.borderWidth"
|
|
||||||
:borderStyle="elementInfo.borderStyle"
|
|
||||||
/>
|
/>
|
||||||
<ImageEllipseBorder
|
<ImageEllipseOutline
|
||||||
v-else-if="clipShape.type === 'ellipse'"
|
v-else-if="clipShape.type === 'ellipse'"
|
||||||
:width="elementInfo.width"
|
:width="elementInfo.width"
|
||||||
:height="elementInfo.height"
|
:height="elementInfo.height"
|
||||||
:borderColor="elementInfo.borderColor"
|
:outline="elementInfo.outline"
|
||||||
:borderWidth="elementInfo.borderWidth"
|
|
||||||
:borderStyle="elementInfo.borderStyle"
|
|
||||||
/>
|
/>
|
||||||
<ImagePolygonBorder
|
<ImagePolygonOutline
|
||||||
v-else-if="clipShape.type === 'polygon'"
|
v-else-if="clipShape.type === 'polygon'"
|
||||||
:width="elementInfo.width"
|
:width="elementInfo.width"
|
||||||
:height="elementInfo.height"
|
:height="elementInfo.height"
|
||||||
|
:outline="elementInfo.outline"
|
||||||
:createPath="clipShape.createPath"
|
:createPath="clipShape.createPath"
|
||||||
:borderColor="elementInfo.borderColor"
|
|
||||||
:borderWidth="elementInfo.borderWidth"
|
|
||||||
:borderStyle="elementInfo.borderStyle"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="img-wrapper" :style="{clipPath: clipShape.style}">
|
<div class="img-wrapper" :style="{clipPath: clipShape.style}">
|
||||||
<img
|
<img
|
||||||
:src="elementInfo.imgUrl"
|
:src="elementInfo.src"
|
||||||
:draggable="false"
|
:draggable="false"
|
||||||
:style="{
|
:style="{
|
||||||
top: imgPosition.top,
|
top: imgPosition.top,
|
||||||
@ -93,7 +87,7 @@
|
|||||||
:type="line.type"
|
:type="line.type"
|
||||||
:style="line.style"
|
:style="line.style"
|
||||||
/>
|
/>
|
||||||
<template v-if="!elementInfo.isLock && (isActiveGroupElement || !isMultiSelect)">
|
<template v-if="!elementInfo.lock && (isActiveGroupElement || !isMultiSelect)">
|
||||||
<ResizablePoint
|
<ResizablePoint
|
||||||
class="el-resizable-point"
|
class="el-resizable-point"
|
||||||
v-for="point in resizablePoints"
|
v-for="point in resizablePoints"
|
||||||
@ -119,7 +113,7 @@ import { computed, defineComponent, ref, PropType } from 'vue'
|
|||||||
|
|
||||||
import { PPTImageElement } from '@/types/slides'
|
import { PPTImageElement } from '@/types/slides'
|
||||||
import { ElementScaleHandler } from '@/types/edit'
|
import { ElementScaleHandler } from '@/types/edit'
|
||||||
import useCommonOperate from '@/views/_common/_element/useCommonOperate'
|
import useCommonOperate from '@/views/_common/_element/hooks/useCommonOperate'
|
||||||
|
|
||||||
import { CLIPPATHS, ClipPathTypes } from '@/configs/imageClip'
|
import { CLIPPATHS, ClipPathTypes } from '@/configs/imageClip'
|
||||||
|
|
||||||
@ -129,9 +123,11 @@ import BorderLine from '@/views/_common/_operate/BorderLine.vue'
|
|||||||
import AnimationIndex from '@/views/_common/_operate/AnimationIndex.vue'
|
import AnimationIndex from '@/views/_common/_operate/AnimationIndex.vue'
|
||||||
|
|
||||||
import ImageClip, { ClipedEmitData } from './ImageClipHandler.vue'
|
import ImageClip, { ClipedEmitData } from './ImageClipHandler.vue'
|
||||||
import ImageRectBorder from './ImageRectBorder.vue'
|
import ImageRectOutline from './ImageRectOutline.vue'
|
||||||
import ImageEllipseBorder from './ImageEllipseBorder.vue'
|
import ImageEllipseOutline from './ImageEllipseOutline.vue'
|
||||||
import ImagePolygonBorder from './ImagePolygonBorder.vue'
|
import ImagePolygonOutline from './ImagePolygonOutline.vue'
|
||||||
|
|
||||||
|
import useElementShadow from '@/views/_common/_element/hooks/useElementShadow'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'editable-element-image',
|
name: 'editable-element-image',
|
||||||
@ -141,9 +137,9 @@ export default defineComponent({
|
|||||||
BorderLine,
|
BorderLine,
|
||||||
AnimationIndex,
|
AnimationIndex,
|
||||||
ImageClip,
|
ImageClip,
|
||||||
ImageRectBorder,
|
ImageRectOutline,
|
||||||
ImageEllipseBorder,
|
ImageEllipseOutline,
|
||||||
ImagePolygonBorder,
|
ImagePolygonOutline,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
elementInfo: {
|
elementInfo: {
|
||||||
@ -198,7 +194,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
const { resizablePoints, borderLines } = useCommonOperate(scaleWidth, scaleHeight)
|
const { resizablePoints, borderLines } = useCommonOperate(scaleWidth, scaleHeight)
|
||||||
|
|
||||||
const isCliping = computed(() => clipingImageElId.value === props.elementInfo.elId)
|
const isCliping = computed(() => clipingImageElId.value === props.elementInfo.id)
|
||||||
|
|
||||||
const imgPosition = computed(() => {
|
const imgPosition = computed(() => {
|
||||||
if(!props.elementInfo || !props.elementInfo.clip) {
|
if(!props.elementInfo || !props.elementInfo.clip) {
|
||||||
@ -233,10 +229,10 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const filter = computed(() => {
|
const filter = computed(() => {
|
||||||
if(!props.elementInfo.filter) return ''
|
if(!props.elementInfo.filters) return ''
|
||||||
let filter = ''
|
let filter = ''
|
||||||
for(const key of Object.keys(props.elementInfo.filter)) {
|
for(const key of Object.keys(props.elementInfo.filters)) {
|
||||||
filter += `${key}(${props.elementInfo.filter[key]}) `
|
filter += `${key}(${props.elementInfo.filters[key]}) `
|
||||||
}
|
}
|
||||||
return filter
|
return filter
|
||||||
})
|
})
|
||||||
@ -250,8 +246,11 @@ export default defineComponent({
|
|||||||
return ''
|
return ''
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const shadow = computed(() => props.elementInfo.shadow)
|
||||||
|
const { shadowStyle } = useElementShadow(shadow)
|
||||||
|
|
||||||
const handleSelectElement = (e: MouseEvent) => {
|
const handleSelectElement = (e: MouseEvent) => {
|
||||||
if(isCliping.value || props.elementInfo.isLock) return
|
if(isCliping.value || props.elementInfo.lock) return
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
props.selectElement(e, props.elementInfo)
|
props.selectElement(e, props.elementInfo)
|
||||||
}
|
}
|
||||||
@ -283,6 +282,7 @@ export default defineComponent({
|
|||||||
borderLines,
|
borderLines,
|
||||||
filter,
|
filter,
|
||||||
flip,
|
flip,
|
||||||
|
shadowStyle,
|
||||||
handleSelectElement,
|
handleSelectElement,
|
||||||
clip,
|
clip,
|
||||||
}
|
}
|
||||||
|
@ -12,17 +12,13 @@
|
|||||||
:style="{
|
:style="{
|
||||||
backgroundColor: elementInfo.fill,
|
backgroundColor: elementInfo.fill,
|
||||||
opacity: elementInfo.opacity,
|
opacity: elementInfo.opacity,
|
||||||
textShadow: elementInfo.shadow,
|
textShadow: shadowStyle,
|
||||||
lineHeight: elementInfo.lineHeight,
|
|
||||||
letterSpacing: (elementInfo.letterSpacing || 0) + 'px',
|
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<ElementBorder
|
<ElementOutline
|
||||||
:width="elementInfo.width"
|
:width="elementInfo.width"
|
||||||
:height="elementInfo.height"
|
:height="elementInfo.height"
|
||||||
:borderColor="elementInfo.borderColor"
|
:outline="elementInfo.outline"
|
||||||
:borderWidth="elementInfo.borderWidth"
|
|
||||||
:borderStyle="elementInfo.borderStyle"
|
|
||||||
/>
|
/>
|
||||||
<div class="text-content"
|
<div class="text-content"
|
||||||
v-html="elementInfo.content"
|
v-html="elementInfo.content"
|
||||||
@ -32,14 +28,16 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, PropType } from 'vue'
|
import { defineComponent, PropType, computed } from 'vue'
|
||||||
import { PPTTextElement } from '@/types/slides'
|
import { PPTTextElement } from '@/types/slides'
|
||||||
import ElementBorder from '@/views/_common/_element/ElementBorder.vue'
|
import ElementOutline from '@/views/_common/_element/ElementOutline.vue'
|
||||||
|
|
||||||
|
import useElementShadow from '@/views/_common/_element/hooks/useElementShadow'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'base-element-text',
|
name: 'base-element-text',
|
||||||
components: {
|
components: {
|
||||||
ElementBorder,
|
ElementOutline,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
elementInfo: {
|
elementInfo: {
|
||||||
@ -47,6 +45,14 @@ export default defineComponent({
|
|||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
setup(props) {
|
||||||
|
const shadow = computed(() => props.elementInfo.shadow)
|
||||||
|
const { shadowStyle } = useElementShadow(shadow)
|
||||||
|
|
||||||
|
return {
|
||||||
|
shadowStyle,
|
||||||
|
}
|
||||||
|
},
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -58,6 +64,7 @@ export default defineComponent({
|
|||||||
.element-content {
|
.element-content {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
line-height: 1.5;
|
||||||
|
|
||||||
.text-content {
|
.text-content {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="editable-element text"
|
class="editable-element text"
|
||||||
:class="{ 'lock': elementInfo.isLock }"
|
:class="{ 'lock': elementInfo.lock }"
|
||||||
:style="{
|
:style="{
|
||||||
top: elementInfo.top + 'px',
|
top: elementInfo.top + 'px',
|
||||||
left: elementInfo.left + 'px',
|
left: elementInfo.left + 'px',
|
||||||
@ -14,22 +14,18 @@
|
|||||||
:style="{
|
:style="{
|
||||||
backgroundColor: elementInfo.fill,
|
backgroundColor: elementInfo.fill,
|
||||||
opacity: elementInfo.opacity,
|
opacity: elementInfo.opacity,
|
||||||
textShadow: elementInfo.shadow,
|
textShadow: shadowStyle,
|
||||||
lineHeight: elementInfo.lineHeight,
|
|
||||||
letterSpacing: (elementInfo.letterSpacing || 0) + 'px',
|
|
||||||
}"
|
}"
|
||||||
v-contextmenu="contextmenus"
|
v-contextmenu="contextmenus"
|
||||||
>
|
>
|
||||||
<ElementBorder
|
<ElementOutline
|
||||||
:width="elementInfo.width"
|
:width="elementInfo.width"
|
||||||
:height="elementInfo.height"
|
:height="elementInfo.height"
|
||||||
:borderColor="elementInfo.borderColor"
|
:outline="elementInfo.outline"
|
||||||
:borderWidth="elementInfo.borderWidth"
|
|
||||||
:borderStyle="elementInfo.borderStyle"
|
|
||||||
/>
|
/>
|
||||||
<div class="text-content"
|
<div class="text-content"
|
||||||
v-html="elementInfo.content"
|
v-html="elementInfo.content"
|
||||||
:contenteditable="isActive && !elementInfo.isLock"
|
:contenteditable="isActive && !elementInfo.lock"
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -52,7 +48,7 @@
|
|||||||
:isWide="true"
|
:isWide="true"
|
||||||
@mousedown="handleSelectElement($event)"
|
@mousedown="handleSelectElement($event)"
|
||||||
/>
|
/>
|
||||||
<template v-if="!elementInfo.isLock && (isActiveGroupElement || !isMultiSelect)">
|
<template v-if="!elementInfo.lock && (isActiveGroupElement || !isMultiSelect)">
|
||||||
<ResizablePoint class="el-resizable-point"
|
<ResizablePoint class="el-resizable-point"
|
||||||
v-for="point in resizablePoints"
|
v-for="point in resizablePoints"
|
||||||
:key="point.type"
|
:key="point.type"
|
||||||
@ -77,18 +73,20 @@ import { computed, defineComponent, PropType } from 'vue'
|
|||||||
|
|
||||||
import { PPTTextElement } from '@/types/slides'
|
import { PPTTextElement } from '@/types/slides'
|
||||||
import { ElementScaleHandler } from '@/types/edit'
|
import { ElementScaleHandler } from '@/types/edit'
|
||||||
import useCommonOperate from '@/views/_common/_element/useCommonOperate'
|
import useCommonOperate from '@/views/_common/_element/hooks/useCommonOperate'
|
||||||
|
|
||||||
import ElementBorder from '@/views/_common/_element/ElementBorder.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 ResizablePoint from '@/views/_common/_operate/ResizablePoint.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'
|
||||||
|
|
||||||
|
import useElementShadow from '@/views/_common/_element/hooks/useElementShadow'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'editable-element-text',
|
name: 'editable-element-text',
|
||||||
components: {
|
components: {
|
||||||
ElementBorder,
|
ElementOutline,
|
||||||
RotateHandler,
|
RotateHandler,
|
||||||
ResizablePoint,
|
ResizablePoint,
|
||||||
BorderLine,
|
BorderLine,
|
||||||
@ -146,17 +144,21 @@ export default defineComponent({
|
|||||||
const { resizablePoints, borderLines } = useCommonOperate(scaleWidth, scaleHeight)
|
const { resizablePoints, borderLines } = useCommonOperate(scaleWidth, scaleHeight)
|
||||||
|
|
||||||
const handleSelectElement = (e: MouseEvent, canMove = true) => {
|
const handleSelectElement = (e: MouseEvent, canMove = true) => {
|
||||||
if(props.elementInfo.isLock) return
|
if(props.elementInfo.lock) return
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
|
|
||||||
props.selectElement(e, props.elementInfo, canMove)
|
props.selectElement(e, props.elementInfo, canMove)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const shadow = computed(() => props.elementInfo.shadow)
|
||||||
|
const { shadowStyle } = useElementShadow(shadow)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
scaleWidth,
|
scaleWidth,
|
||||||
resizablePoints,
|
resizablePoints,
|
||||||
borderLines,
|
borderLines,
|
||||||
handleSelectElement,
|
handleSelectElement,
|
||||||
|
shadowStyle,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -182,6 +184,7 @@ export default defineComponent({
|
|||||||
.element-content {
|
.element-content {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
line-height: 1.5;
|
||||||
|
|
||||||
.text-content {
|
.text-content {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
14
src/views/_common/_element/hooks/useElementOutline.ts
Normal file
14
src/views/_common/_element/hooks/useElementOutline.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
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 : '#41464b')
|
||||||
|
|
||||||
|
return {
|
||||||
|
outlineWidth,
|
||||||
|
outlineStyle,
|
||||||
|
outlineColor,
|
||||||
|
}
|
||||||
|
}
|
14
src/views/_common/_element/hooks/useElementShadow.ts
Normal file
14
src/views/_common/_element/hooks/useElementShadow.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { Ref } from 'vue'
|
||||||
|
import { PPTElementShadow } from '@/types/slides'
|
||||||
|
|
||||||
|
export default (shadow: Ref<PPTElementShadow | undefined>) => {
|
||||||
|
let shadowStyle = ''
|
||||||
|
if(shadow.value) {
|
||||||
|
const { h, v, blur, color } = shadow.value
|
||||||
|
shadowStyle = `${h} ${v} ${blur} ${color}`
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
shadowStyle,
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user