【PPTist】插入形状、插入图片、插入图表

devtools/2025/1/14 13:41:44/

一、插入形状

插入形状有两种情况,一种是插入固定的形状,
在这里插入图片描述

一种是插入自定义的形状。
插入固定的形状时,跟上一篇文章 绘制文本框 是一样一样的,都是调用的 mainStore.setCreatingElement() 方法,只不多传的类型不一样。还有插入线条,也是类似的。

mainStore.setCreatingElement({type: 'shape',data: shape,
})

所以咱们那接下来主要看插入自定义形状时的代码执行流程

1、点击
<Popover trigger="click" v-model:value="shapeMenuVisible" style="height: 100%;" :offset="10"><template #content><PopoverMenuItem center @click="() => { drawCustomShape(); shapeMenuVisible = false }">自由绘制</PopoverMenuItem></template><IconDown class="arrow" />
</Popover>

src/views/Editor/CanvasTool/index.vue

// 绘制自定义任意多边形
const drawCustomShape = () => {mainStore.setCreatingCustomShapeState(true)shapePoolVisible.value = false
}

src/store/main.ts

setCreatingCustomShapeState(state: boolean) {this.creatingCustomShape = state
},

有了 creatingCustomShape,下面的组件就会显示

<ShapeCreateCanvasv-if="creatingCustomShape"@created="data => insertCustomShape(data)"
/>
2、mousedown

src/views/Editor/Canvas/ShapeCreateCanvas.vue
触发 created 方法

const addPoint = (e: MouseEvent) => {const { pageX, pageY } = getPoint(e)isMouseDown.value = trueif (closed.value) emit('created', getCreateData())else points.value.push([pageX, pageY])document.onmouseup = () => {isMouseDown.value = false}
}
3、created

src/views/Editor/Canvas/index.vue
插入任意多边形

// 插入自定义任意多边形
const insertCustomShape = (data: CreateCustomShapeData) => {const {start,end,path,viewBox,} = dataconst position = formatCreateSelection({ start, end })if (position) {const supplement: Partial<PPTShapeElement> = {}if (data.fill) supplement.fill = data.fillif (data.outline) supplement.outline = data.outline// 创建形状元素createShapeElement(position, { path, viewBox }, supplement)}// 清除 creatingCustomShapemainStore.setCreatingCustomShapeState(false)
}
4、mousemove

src/views/Editor/Canvas/ShapeCreateCanvas.vue
如果鼠标按下,添加 points,就会形成折线的效果。
可以看到只要起点和终点比较近就算闭合了,防止对不上

const updateMousePosition = (e: MouseEvent) => {// 如果鼠标按下,则添加点if (isMouseDown.value) {const { pageX, pageY } = getPoint(e, true)points.value.push([pageX, pageY])mousePosition.value = nullreturn}// 更新鼠标位置const { pageX, pageY } = getPoint(e)mousePosition.value = [pageX, pageY]// 判断是否闭合if (points.value.length >= 2) {const [firstPointX, firstPointY] = points.value[0]if (Math.abs(firstPointX - pageX) < 5 && Math.abs(firstPointY - pageY) < 5) {closed.value = true}else closed.value = false}else closed.value = false
}

根据鼠标位置 mousePosition 计算 path

const path = computed(() => {let d = ''for (let i = 0; i < points.value.length; i++) {const point = points.value[i]if (i === 0) d += `M ${point[0]} ${point[1]} `else d += `L ${point[0]} ${point[1]} `}if (points.value.length && mousePosition.value) {d += `L ${mousePosition.value[0]} ${mousePosition.value[1]}`}return d
})

模版中的 path 元素随之更新

<svg overflow="visible"><path:d="path" stroke="#d14424" :fill="closed ? 'rgba(226, 83, 77, 0.15)' : 'none'" stroke-width="2" ></path>
</svg>
5、取消绘制的按键绑定
const keydownListener = (e: KeyboardEvent) => {const key = e.key.toUpperCase()if (key === KEYS.ESC) close()if (key === KEYS.ENTER) create()
}
onMounted(() => {message.success('点击绘制任意形状,首尾闭合完成绘制,按 ESC 键或鼠标右键取消,按 ENTER 键提前完成', {duration: 0,})document.addEventListener('keydown', keydownListener)
})

以及鼠标右键也会取消绘制

@contextmenu.stop.prevent="close()"
const close = () => {mainStore.setCreatingCustomShapeState(false)
}

二、插入图片

src/views/Editor/CanvasTool/index.vue
插入图片也是一个自定义组件

<FileInput @change="files => insertImageElement(files)"><IconPicture class="handler-item" v-tooltip="'插入图片'" />
</FileInput>

这个组件里面实现上传功能的是 input 标签

<input class="input"type="file" name="upload" ref="inputRef" :accept="accept" @change="$event => handleChange($event)"
>

上传之后插入图片元素

const insertImageElement = (files: FileList) => {const imageFile = files[0]if (!imageFile) returngetImageDataURL(imageFile).then(dataURL => createImageElement(dataURL))
}

src/utils/image.ts

获取图片宽高的方法,相比大家都挺熟悉的

/*** 获取图片的原始宽高* @param src 图片地址*/
export const getImageSize = (src: string): Promise<ImageSize> => {return new Promise(resolve => {const img = document.createElement('img')img.src = srcimg.style.opacity = '0'document.body.appendChild(img)img.onload = () => {const imgWidth = img.clientWidthconst imgHeight = img.clientHeightimg.onload = nullimg.onerror = nulldocument.body.removeChild(img)resolve({ width: imgWidth, height: imgHeight })}img.onerror = () => {img.onload = nullimg.onerror = null}})
}

获取图片宽高之后,创建图片元素,通过 lefttop 将图片水平垂直居中
src/hooks/useCreateElement.ts

/*** 创建图片元素* @param src 图片地址*/
const createImageElement = (src: string) => {getImageSize(src).then(({ width, height }) => {const scale = height / widthif (scale < viewportRatio.value && width > VIEWPORT_SIZE) {width = VIEWPORT_SIZEheight = width * scale}else if (height > VIEWPORT_SIZE * viewportRatio.value) {height = VIEWPORT_SIZE * viewportRatio.valuewidth = height / scale}createElement({type: 'image',id: nanoid(10),src,width,height,left: (VIEWPORT_SIZE - width) / 2,top: (VIEWPORT_SIZE * viewportRatio.value - height) / 2,fixedRatio: true,rotate: 0,})})
}

复习一下创建元素的方法,会把元素放到当前幻灯片的元素列表中

// 创建(插入)一个元素并将其设置为被选中元素
const createElement = (element: PPTElement, callback?: () => void) => {// 添加元素到元素列表slidesStore.addElement(element)// 设置被选中元素列表mainStore.setActiveElementIdList([element.id])if (creatingElement.value) mainStore.setCreatingElement(null)setTimeout(() => {// 设置编辑器区域为聚焦状态mainStore.setEditorareaFocus(true)}, 0)if (callback) callback()// 添加历史快照addHistorySnapshot()
}

三、插入图表

插入图表的方法,其实也差不多,就是往当前的幻灯片里添加一个图表对象。不过这里就不讲前面怎么添加元素了,讲讲后面怎么展示元素吧。先来看一下图表元素的数据:

const newElement: PPTChartElement = {type: 'chart',id: nanoid(10),chartType: CHART_TYPES[type],left: 300,top: 81.25,width: 400,height: 400,rotate: 0,themeColor: [theme.value.themeColor],gridColor: theme.value.fontColor,data: {labels: ['类别1', '类别2', '类别3', '类别4', '类别5'],legends: ['系列1'],series: [[12, 19, 5, 2, 18],],},
}

是这个元素对元素列表进行循环的
src/views/Editor/Canvas/index.vue

<EditableElement v-for="(element, index) in elementList" :key="element.id":elementInfo="element":elementIndex="index + 1":isMultiSelect="activeElementIdList.length > 1":selectElement="selectElement":openLinkDialog="openLinkDialog"v-show="!hiddenElementIdList.includes(element.id)"
/>

src/views/Editor/Canvas/EditableElement.vue
这个组件中通过动态组件的方式控制显示哪个元素

<component:is="currentElementComponent":elementInfo="elementInfo":selectElement="selectElement":contextmenus="contextmenus"
></component>
const currentElementComponent = computed<unknown>(() => {const elementTypeMap = {[ElementTypes.IMAGE]: ImageElement,[ElementTypes.TEXT]: TextElement,[ElementTypes.SHAPE]: ShapeElement,[ElementTypes.LINE]: LineElement,[ElementTypes.CHART]: ChartElement,[ElementTypes.TABLE]: TableElement,[ElementTypes.LATEX]: LatexElement,[ElementTypes.VIDEO]: VideoElement,[ElementTypes.AUDIO]: AudioElement,}return elementTypeMap[props.elementInfo.type] || null
})

我们的目标就是 ChartElementsrc/views/components/element/ChartElement/index.vue
然后图表那一小块是这个:src/views/components/element/ChartElement/Chart.vue,图表是通过 chartist 库实现的

import { BarChart, LineChart, PieChart } from 'chartist'

http://www.ppmy.cn/devtools/150419.html

相关文章

PHP语言的学习路线

PHP语言的学习路线 PHP&#xff08;Hypertext Preprocessor&#xff09;是一种广泛使用的开源服务器端脚本语言&#xff0c;尤其适用于Web开发。由于其易学易用、功能强大&#xff0c;PHP成为了许多动态网站和Web应用程序开发的首选语言。随着Web3.0和云计算的兴起&#xff0c…

【数据仓库】— 5分钟浅谈数据仓库(适合新手)从理论到实践

大家好&#xff0c;我是摇光~ 对于刚进入大数据领域的萌新&#xff0c;且想要在数据分析岗、数据运维岗、数据工程师这些岗位立足&#xff0c;了解数据仓库是必要的&#xff0c;接下来我尽量用通俗易懂的语言让大家了解到数据仓库。 在当今大数据盛行的时代&#xff0c;数据仓…

支持selenium的chrome driver更新到131.0.6778.264

最近chrome释放新版本&#xff1a;131.0.6778.264 如果运行selenium自动化测试出现以下问题&#xff0c;是需要升级chromedriver才可以解决的。 selenium.common.exceptions.SessionNotCreatedException: Message: session not created: This version of ChromeDriver only s…

Ubuntu 20.04 安装Cuda 12.2版本踩坑记录

Ubuntu 20.04 安装Cuda 12.2版本踩坑记录 文章目录 Ubuntu 20.04 安装Cuda 12.2版本踩坑记录查看Ubuntu版本不成功的方式&#xff1a;使用deb安装卸载现有的 NVIDIA 驱动&#xff1a;安装符合要求的驱动版本&#xff1a; 成功的安装方式&#xff1a;使用runfile安装user账户nvc…

macOS 版本对应 Xcode 版本,以及 Xcode 历史版本下载

注&#xff1a;当前页面的所有Xcode下载链接均为苹果官方下载链接 &#xff0c;点击将直接转至苹果官网下载。❤️❤️❤️ Xcode官网&#xff1a;Xcode Releases | xcodereleases.com Xcode版本Xcode发布时间对应macOS版本macOS SDKsiOS SDKswatchOS SDKstvOS SDKs下载Xcode发…

提升租赁效率的租赁小程序全解析

内容概要 在如今快节奏的生活中&#xff0c;租赁小程序俨然成为了提升租赁效率的一把利器。无论是个人还是企业&#xff0c;都会因其便捷的功能而受益。简单来说&#xff0c;租赁小程序能让繁琐的租赁流程变得轻松、高效。在这里&#xff0c;我们将带您畅游租赁小程序的海洋&a…

例子:WeTextProcessing,如何查看现在已安装的这个模块的版本号呢?查看虚拟环境中模块的版本

要查看已安装的`WeTextProcessing`模块的版本号,您可以使用以下几种方法: 1.使用`pip`命令 在命令行中,您可以使用`pip`命令来查看已安装包的版本信息。运行以下命令: ```sh pip show WeTextProcessing ``` 这将显示`WeTextProcessing`的详细信息,包括版本号。 2.使用`c…

“AI智能服务平台系统,让生活更便捷、更智能

大家好&#xff0c;我是资深产品经理老王&#xff0c;今天咱们来聊聊一个让生活变得越来越方便的高科技产品——AI智能服务平台系统。这个系统可是现代服务业的一颗璀璨明珠&#xff0c;它究竟有哪些魅力呢&#xff1f;下面我就跟大家伙儿闲聊一下。 一、什么是AI智能服务平台系…