一、插入二进制图片
这个二进制图片我使用html2canvas生成的,网上有关于这个插件的教程很多0.0就不详细说明了,简单贴一下代码
利用html2canvas生成二进制图片,返回一个异步
// html2canvas 截取图片
convertToImage(container) {
// 高度
const arrDPI = this.js_getDPI() // 获取显示器DPI
let dpiX = 96
if (arrDPI.length > 0) {
dpiX = arrDPI[0]
}
// html2canvas配置项
const ops = {
dpi: dpiX,
useCORS: false,
allowTaint: true,
scale: 4 // 缩放比例越大,图片越清晰
}
return html2canvas(container, ops).then((canvas) => {
// 返回图片的二进制数据
// this.imgBlobData = canvas.toDataURL("image/png");
return canvas.toDataURL('image/png', 1.0)
})
},
// 获取屏幕dpi
js_getDPI() {
var arrDPI = []
if (window.screen.deviceXDPI !== undefined) {
arrDPI[0] = window.screen.deviceXDPI
arrDPI[1] = window.screen.deviceYDPI
} else {
var tmpNode = document.createElement('DIV')
tmpNode.style.cssText = 'width:1in;height:1in;position:absolute;left:0px;top:0px;z-index:99;visibility:hidden'
document.body.appendChild(tmpNode)
arrDPI[0] = parseInt(tmpNode.offsetWidth)
arrDPI[1] = parseInt(tmpNode.offsetHeight)
tmpNode.parentNode.removeChild(tmpNode)
}
return arrDPI
},
利用pptxgen,导出ppt时候插入二进制图片,有关于pptxgen的使用我在另一篇文章中有说0.0
所以这里直接上代码了
// 增加幻灯片
const slide = pres.addSlide()
// 截取二进制图片 记得用await,因为是异步的,否则会导致截图没截完,ppt就已经导出去了。
this.imgBlobData = await this.convertToImage(this.$refs.keyword.$children[0].$children[1].$el) // 这里参数是要截取的元素
// 插入图片
slide.addImage({ x: '52%', y: this.subContentY, h: 3.38, w: '43%', sizing: { type: 'contain' }, data: this.imgBlobData })
二、宽高确定的情况下,循环插入多张图片
有个任务要求是导出文章截图,文章截图的宽高是确定的,而且每张图片都一样,所以这个需求其实不算太难,只要算好图片宽高,然后对应设置x值就好了,最多再加一个换页
上代码,走你
// 模块五 内容图片
setModule5(pres, item, platform) {
const slide = pres.addSlide()
const list = this.contentImg.find(citem => citem.platform === platform) || {}
// list.contentList就是包含图片的数组,里面是图片的url
if (list.contentList) {
// 一页ppt放5张图片,超出换页
// 数组长度小于等于5的情况
if (list.contentList.length <= 5) {
list.contentList.forEach((litem, index) => {
// 循环,1.6是每张图片的实际宽度+左间距(1.5 + 0.1) 0.8是第一张图片的横坐标距离
slide.addImage({ x: index * 1.6 + 0.8, y: 1.3, w: 1.5, h: 3.3, path: litem, shadow: { type: 'outer' }})
})
} else {
// 数组长度大于5的情况
// 超出5张图片则换页,计算一下总页数 list.contentList.length - 1 是防止数组数量刚好为5的时候就换页了。。
const count = Math.floor((list.contentList.length - 1) / 5)
for (let i = 0; i <= count; i++) {
let tempSlide = slide
if (i > 0) {
// 如果不是第一页,则需要新建一张PPT页
tempSlide = pres.addSlide()
}
list.contentList.forEach((litem, index) => {
if (index >= i * 5 && index < (i * 5 + 5)) {
// 循环,1.6是每张图片的实际宽度+左间距(1.5 + 0.1) 0.8是第一张图片的横坐标距离
tempSlide.addImage({ x: (index % 5) * 1.6 + 0.8, y: 1.3, w: 1.5, h: 3.3, path: litem, shadow: { type: 'outer' }})
}
})
}
}
}
},
代码没什么好说的,了解一下思路就好了0.0循环确定图片位置,注意换行或者换页之后的位置就好了
三、宽高不确定的情况下,循环插入多张图片
工作中总是会遇到各种各样的需求,导出文章之后,又来了一个导出评论截图的功能。
由于评论截图是爬虫组与后端返回来的,在拿到图片之前,并不确定图片的宽高
但是在pptxgen.js中又必须确定了图片的宽高、位置等参数才能够插入图片,flex什么的以及图片自适应什么的我目前暂未发现。。
那怎么办呢?只好确定一个统一的高度或者宽度,自己硬着头皮,在图片加载完成之后获取图片本身宽高,再一张张地换算单位了。
直接上代码吧。。。
// 模块六 评论图片
async setModule6(pres, item, platform) {
const slide = pres.addSlide()
const list = this.commentImg.find(citem => citem.platform === platform) || {}
// list.commentList 就是包含图片的数组,里面是图片的url
if (!list.commentList) {
return false
}
const widthList = []
const heightList = []
// 图片实际宽度(在ppt上的宽度)限定 2.5 这里我是写死了图片宽度,然后去计算图片转换后的实际高度
const w = 2.5
// const hList = []
// 循环获取图片宽高,装进数组中
for (let index = 0; index < list.commentList.length; index++) {
// const i = Math.floor(Math.random() * 3)
const imgItem = list.commentList[index]
imgList.push(imgItem)
await this.loadImg(imgItem).then(obj => {
// 实际高度 = 实际宽度 / 图片原本宽度 * 图片原本高度
widthList.push(obj.width)
heightList.push(obj.height)
// hList.push(2.5 / obj.width * obj.height)
})
}
// 纵向排布 这里应该是我某个逻辑出了问题,导致纵向排布失败了,但其实大概思路其实都差不多的
// hList.forEach((item, index) => {
// // 计算数组0到当前索引实际总高度
// const totalH = this.getTotalH(hList.slice(0, index + 1))
// console.log(totalH);
// // 计算x的位置
// const x = Math.floor(totalH / 3.8) * 2.6 + 0.5
// console.log(x)
// let y = 1.3
// // 计算实际y距离,先判断0到上一个索引的高度
// if (index > 0) {
// const preTotalH = this.getTotalH(hList.slice(0, index))
// y = 1.3 + preTotalH % 3.8 + 0.1
// if (y > 3.8) {
// y = 1.3
// }
// }
// // 实际x距离,超出3个会换行,所以需要计算大于3的图片
// slide.addImage({ path: imgList[index], x, y, w, item })
// })
// 这是横向排布
// 取所有图片的最高高度来换行,以免图片重叠(实际上用纵向排布会更好一点,就是flex-direction: column 这样的效果,这样不会导致行与行之间的间距太大)
const maxH = Math.max.apply(null, heightList)
const maxHIndex = heightList.findIndex(item => item === maxH)
const maxImgH = w / widthList[maxHIndex] * maxH + 0.1
// 循环设置图片
widthList.forEach((item, index) => {
// 计算实际高度
const h = w / item * heightList[index]
// 计算实际y距离 因为超出3个就换行
const y = 1.3 + Math.floor(index / 3) * maxImgH
// 实际x距离,超出3个会换行,所以需要计算大于3的图片
let x = 0
if (index > 2) {
x = index % 3 * (w + 0.1) + 0.8
} else {
x = index * (w + 0.1) + 0.8
}
slide.addImage({ path: imgList[index], x, y, w, h })
})
},
// 获取总高度
// getTotalH(list) {
// const total = list.reduce((prev, cur) => {
// return prev + cur
// })
// return total
// },
// 在图片加载完成之后获取图片宽高
loadImg(url) {
return new Promise((resolve, reject) => {
const img = new Image()
img.src = url
img.onload = () => {
const obj = {
width: img.width,
height: img.height
}
resolve(obj)
}
img.onerror = reject
})
},
以上代码,重点是图片加载之后获取宽高,换算实际宽高比例,这样就知道了图片在ppt中的宽和高了。
然后注意循环时候图片换行之后的位置就好了
思路都是这样的,可以稍微参考,我当时写的还是比较乱的,假如有更好的选择可以交流一下,大家共同学习0.0
四、异步导出处理
由于导出ppt时候可能会涉及到异步操作(比如计算图片宽高、截图等操作),我在导出ppt的时候,就出现过“图片没算完,ppt就已经导出了”的现象,导致ppt一片空白,所以对导出ppt的方法我也做了一下处理
// 导出ppt
async downloadPPT() {
this.$notify.info('正在导出,请稍后')
const pres = new Pptxgen()
this.setModule1(pres)
this.setModule2(pres)
this.setModule3(pres)
// 项目中是有很多个页面的,但从第五个页面开始, 5 6 7、 8 9 10 、11 12 13,页面样式都是这样重复的,所以我进行了一个for循环,顺便踩了一下forEach的坑。(forEach里面只能执行同步方法,在for循环里面使用async 和await 这样的异步操作是不起作用的。)
for (let index = 0; index < this.settingList.length; index++) {
const item = this.settingList[index]
if (item.pageNo > 4 && item.disable) {
if ((item.pageNo - 4) % 3 === 1) {
this.setModule4(pres, item, index === 4 ? this.settingList[index - 1].title : '')
} else if ((item.pageNo - 4) % 3 === 2) {
this.setModule5(pres, item, this.settingList[index - 1].platform)
} else {
// 其实其他代码忽视就好了,重点是这里 用await处理一下就好了
await this.setModule6(pres, item, this.settingList[index - 2].platform)
}
}
}
console.log(pres)
pres.writeFile({ fileName: '报告' }).then(() => {
this.$notify.success('导出成功')
})
},