feat: 添加图片导出

This commit is contained in:
pipipi-pikachu 2022-04-09 16:53:03 +08:00
parent 692ce11244
commit 24b00fbb20
3 changed files with 218 additions and 0 deletions

View File

@ -4,6 +4,7 @@ import { trim } from 'lodash'
import { saveAs } from 'file-saver'
import pptxgen from 'pptxgenjs'
import tinycolor from 'tinycolor2'
import { toPng, toJpeg } from 'html-to-image'
import { useSlidesStore } from '@/store'
import { getElementRange, getLineElementPath, getTableSubThemeColor } from '@/utils/element'
import { AST, toAST } from '@/utils/htmlParser'
@ -11,10 +12,41 @@ import { SvgPoints, toPoints } from '@/utils/svgPathParser'
import { svg2Base64 } from '@/utils/svg2Base64'
import { message } from 'ant-design-vue'
interface ExportImageConfig {
quality: number;
width: number;
fontEmbedCSS?: string;
}
export default () => {
const { slides } = storeToRefs(useSlidesStore())
const exporting = ref(false)
// 导出图片
const exportImage = (domRef: HTMLElement, format: string, quality: number, ignoreWebfont = true) => {
exporting.value = true
const toImage = format === 'png' ? toPng : toJpeg
setTimeout(() => {
if (!domRef) return
const config: ExportImageConfig = {
quality,
width: 1600,
}
if (ignoreWebfont) config.fontEmbedCSS = ''
toImage(domRef, config).then(dataUrl => {
exporting.value = false
saveAs(dataUrl, `pptist_slides.${format}`)
}).catch(() => {
exporting.value = false
message.error('导出图片失败')
})
}, 200)
}
// 导出JSON文件
const exportJSON = () => {
@ -576,6 +608,7 @@ export default () => {
return {
exporting,
exportImage,
exportJSON,
exportPPTX,
}

View File

@ -0,0 +1,169 @@
<template>
<div class="export-img-dialog">
<div class="thumbnails-view">
<div class="thumbnails" ref="imageThumbnailsRef">
<ThumbnailSlide
class="thumbnail"
v-for="slide in slides"
:key="slide.id"
:slide="slide"
:size="1600"
/>
</div>
</div>
<div class="configs">
<div class="row">
<div class="title">导出格式</div>
<RadioGroup
class="config-item"
v-model:value="format"
>
<RadioButton value="jpeg">JPEG</RadioButton>
<RadioButton value="png">PNG</RadioButton>
</RadioGroup>
</div>
<div class="row">
<div class="title">图片质量</div>
<Slider
class="config-item"
:min="0"
:max="1"
:step="0.1"
v-model:value="quality"
/>
</div>
<div class="row">
<div class="title">忽略在线字体</div>
<div class="config-item">
<Switch v-model:checked="ignoreWebfont" />
</div>
</div>
<div class="tip">
提示导出时默认会忽略在线字体若您在幻灯片中使用了在线字体且不希望导出图片中丢失相关样式可以选择关闭忽略在线字体选项但要注意这将会导致导出用时大幅度增加
</div>
<div class="btns">
<Button class="btn export" type="primary" @click="expImage()">导出图片</Button>
<Button class="btn close" @click="close()">关闭</Button>
</div>
</div>
<FullscreenSpin :loading="exporting" tip="正在导出..." />
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue'
import { storeToRefs } from 'pinia'
import { useSlidesStore } from '@/store'
import useExport from '@/hooks/useExport'
import ThumbnailSlide from '@/views/components/ThumbnailSlide/index.vue'
export default defineComponent({
name: 'export-img-dialog',
components: {
ThumbnailSlide,
},
setup(props, { emit }) {
const { slides } = storeToRefs(useSlidesStore())
const imageThumbnailsRef = ref<HTMLElement>()
const format = ref<'jpeg' | 'png'>('jpeg')
const quality = ref(1)
const ignoreWebfont = ref(true)
const close = () => emit('close')
const { exportImage, exporting } = useExport()
const expImage = () => {
if (!imageThumbnailsRef.value) return
exportImage(imageThumbnailsRef.value, format.value, quality.value, ignoreWebfont.value)
}
return {
imageThumbnailsRef,
slides,
format,
quality,
ignoreWebfont,
exporting,
expImage,
close,
}
},
})
</script>
<style lang="scss" scoped>
.export-img-dialog {
height: 500px;
display: flex;
justify-content: center;
position: relative;
overflow: hidden;
}
.thumbnails-view {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
&::after {
content: '';
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: #fff;
}
}
.configs {
width: 300px;
display: flex;
flex-direction: column;
justify-content: center;
z-index: 1;
.row {
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 25px;
}
.title {
width: 100px;
}
.config-item {
flex: 1;
}
.tip {
font-size: 12px;
color: #aaa;
line-height: 1.8;
}
.btns {
display: flex;
justify-content: center;
align-items: center;
margin-top: 40px;
.export {
flex: 1;
}
.close {
width: 100px;
margin-left: 10px;
}
}
}
</style>

View File

@ -7,6 +7,7 @@
<Menu>
<MenuItem @click="exportJSON()">导出 JSON</MenuItem>
<MenuItem @click="exportPPTX()">导出 PPTX</MenuItem>
<MenuItem @click="exportImgDialogVisible = true">导出图片</MenuItem>
</Menu>
</template>
</Dropdown>
@ -63,6 +64,17 @@
<HotkeyDoc />
</Drawer>
<Modal
v-model:visible="exportImgDialogVisible"
:footer="null"
centered
:closable="false"
:width="680"
destroyOnClose
>
<ExportImgDialog @close="exportImgDialogVisible = false"/>
</Modal>
<FullscreenSpin :loading="exporting" tip="正在导出..." />
</div>
</template>
@ -77,11 +89,13 @@ import useHistorySnapshot from '@/hooks/useHistorySnapshot'
import useExport from '@/hooks/useExport'
import HotkeyDoc from './HotkeyDoc.vue'
import ExportImgDialog from './ExportImgDialog.vue'
export default defineComponent({
name: 'editor-header',
components: {
HotkeyDoc,
ExportImgDialog,
},
setup() {
const mainStore = useMainStore()
@ -97,6 +111,7 @@ export default defineComponent({
}
const hotkeyDrawerVisible = ref(false)
const exportImgDialogVisible = ref(false)
const goIssues = () => {
window.open('https://github.com/pipipi-pikachu/PPTist/issues')
@ -107,6 +122,7 @@ export default defineComponent({
undo,
showGridLines,
hotkeyDrawerVisible,
exportImgDialogVisible,
exporting,
enterScreening,
enterScreeningFromStart,