由于v-html属于同步指令,无法绑定异步函数,直接绑定异步函数无法渲染异步生成的字符串节点
应用场景:需要从接口获取到文本内容展示到窗口,遍历循环多条的文本内容有匹配的文案需要根据规则转换成table排版并转换成图片展示到页面
解决方案与思路:
转换图片需要用到canvas ,由于是不去确定的内容条数,无法再页面直接写死canvas,只写死一个canvas 在页面上所有出现需要转换的页面都用同一个canvas 也不合适,会出现冲突,所以考虑的是动态创建canvas 转换完成后再从DOM树移除,因为生成canvas 节点属于异步操作,需要canvas onload后才可操作api所以这里就涉及到需要异步操作的地方;
这里我偷懒,使用第三方插件html2canvas
下载依赖:npm -i html2canvas -S
以上思路可以生成我们想要的字符串节点,但是无法通过v-html渲染,因为操作canvas 的时候是异步的,直接v-html绑定异步函数只会展示异步函数本身并非返回值
解决方案:既然无法通过v-html渲染,那就直接通过event去挂载,在遍历的消息列表上绑定事件并把event 传递给处理函数,处理函数处理完成返回字符串节点后转换成真实dom后再appendchild 到消息节点上,绑定事件理论只需要自动触发一次,所以可以写一个自动触发trigger指令实现,只触发一次可以使用 v-once
具体实现:
html:
<template>
<span
v-if="con.type === 'txt'"
style="white-space: pre-wrap"
>
<span
v-if="con.data.indexOf('历史参考:') !== -1"
v-trigger
v-once
@click.stop="(event) => { createImg(con.data,event) }">
</span>
<span v-else v-html="con.data"></span>
</span>
</template>
<style lang="scss">
/ table样式定制
.tablewrap{
position: fixed;
left: -9999px;
top: -9999px;
.res-table{
...
}
</style>
dom操作 domHandle.js
import { formatDates } from '@/utils/tools'
import html2canvas from "html2canvas"
// 转换真实dom
function toHtml (str) {
let parser = new DOMParser()
let doc = parser.parseFromString(str, 'text/html')
return doc.body.firstChild
}
/**
* html 转 canvas 图片
*/
function canvasToSvg (domstr) {
return new Promise((resolve, reject) => {
var dom = toHtml(domstr)
// 创建的dom 必须渲染到dom树 否则无法渲染style样式
document.querySelector('body').appendChild(dom)
var ops = {
width: dom.offsetWidth,
height: dom.offsetHeight,
useCORS: true,
allowTaint: false
}
html2canvas(dom, ops).then((canvas) => {
let imgData = canvas.toDataURL('image/png')
// 生成玩图片路径 移除dom
document.querySelector('body').removeChild(dom)
resolve(imgData)
})
})
}
// 过滤历史参考消息添加节点 ev当前节点
export function filterText (text, ev) {
const resId = Date.now()
// 创建dom
if (text.indexOf('历史参考:\n') !== -1) {
// 新版表格展示板
let tableText = text.split('历史参考:\n')[1].split('\n--------------------')[0]
let tableList = tableText.split('\n')
tableList = tableList.map(str => {
return str.split(';')
})
let dom = `<div id="${resId}" class="tablewrap"><table class="res-table">`
tableList.forEach((item, index) => {
dom += `
<tr class="${index == 0 ? 'th' : ''}">
${(() => {
let otd = ``
item.forEach((tdD, ind) => {
let isTime = (ind == 0 && !isNaN(tdD * 1))
let isIssue = ind == 1
let isTm = ind == 3
let clList = isIssue ? 'black' : isTm ? 'red' : 'blue'
otd += `<td class="${clList}">${(() => {
if (/(\d\,)+/.test(tdD)) {
let coded = ``
// 特殊处理
tdD.split(',').forEach(tm => {
coded += `<span class="code">${tm}</span>`
})
return coded
} else {
let classList = isTime ? 'red' : { '大': 'red', '双': 'red' }[tdD]
return `<span class="${classList}">${
// 第一列时间处理
isTime ? formatDates(new Date(tdD * 1), 'hh:mm') : tdD
}</span>`
}
})()
}</td>`
})
return otd
})()
}
</tr>
`
})
dom += `</table></div>`
// html 转 canvas 生成图片
let src_ = await canvasToSvg(dom)
text = `<span>${text.replace(tableText, `<img style="width: 100%;" src="${src_}" />`)}</span>`
// 节点字符串生成完成 把节点插入回消息容器
ev.target.appendChild(toHtml(text))
// 如果是同步操作直接使用以下操作
// text = text.replace(tableText, dom)
// return text
}
}
script:
import { filterText } from './domHandle'
import { ImagePreview } from 'vant';
export default {
...
directives:{
// 自定义一个默认触发指令 用于生成开奖结果图片
trigger:{
inserted(el,binging){
el.click()
}
}
},
methods: {
// 历史参考结果处理成图片 / 点击图片预览
createImg(text,event) {
if (event.target.childNodes.length) {
// 图片已经生成过 跳出点击事件
return
}
if (event.target.getAttribute('src')) {
// 执行图片预览
ImagePreview({
images: [event.target.getAttribute('src')],
closeable: true
})
return
}
filterText(text, event)
},
},
...
}
示例图: