Angular(Ionic/Cordova) iOS端SVG转图片/PDF节点丢失问题求方案
iOS端SVG转图片/PDF问题的解决方案(Angular/Ionic/Cordova)
问题根源
- iOS WebKit内核(Safari及Cordova WebView)对
foreignObject的Canvas渲染支持有限,异步加载的Base64图片常未完成就触发绘制 - SVG中引用的本地字体在Canvas绘制时可能未加载完毕
- Cordova iOS WebView存在资源加载时序问题,导致图片渲染不完整
方案一:确保资源加载完成后再绘制Canvas
步骤1:预加载所有Base64图片
遍历SVG字符串中的Base64图片资源,创建Image对象预加载,确保所有图片加载完成后再执行绘制。
async preloadSvgImages(svgString: string): Promise<void> { const imgRegex = /background-image: url\("data:image\/png;base64,([^&]+)"\)/g; let match; const promises: Promise<void>[] = []; while ((match = imgRegex.exec(svgString)) !== null) { const base64Data = match[1].replace(';', ''); // 修复原始SVG中的多余分号 const img = new Image(); promises.push(new Promise((resolve, reject) => { img.onload = () => resolve(); img.onerror = reject; img.src = `data:image/png;base64,${base64Data}`; })); } await Promise.all(promises); }
步骤2:修复字体引用问题
将SVG中@font-face的本地字体替换为Base64嵌入,避免iOS WebKit无法加载本地字体:
@font-face { font-family: mdzRoboto; src: url(data:font/truetype;base64,【你的字体Base64内容】) format("truetype"); font-weight: 400; font-style: normal; }
步骤3:延迟Canvas绘制
预加载完成后,用requestAnimationFrame确保浏览器完成资源渲染再绘制:
async svgToImage(svgString: string): Promise<string> { await this.preloadSvgImages(svgString); return new Promise((resolve) => { requestAnimationFrame(async () => { const svgBlob = new Blob([svgString], { type: 'image/svg+xml' }); const svgUrl = URL.createObjectURL(svgBlob); const img = new Image(); img.onload = () => { const canvas = document.createElement('canvas'); canvas.width = img.width; canvas.height = img.height; const ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0); const imgDataUrl = canvas.toDataURL('image/png'); URL.revokeObjectURL(svgUrl); resolve(imgDataUrl); }; img.src = svgUrl; }); }); }
方案二:绕过Canvas,直接转Blob导出
转图片并保存到本地
async svgToImageBlob(svgString: string): Promise<Blob> { const svgBlob = new Blob([svgString], { type: 'image/svg+xml' }); const svgUrl = URL.createObjectURL(svgBlob); return new Promise((resolve, reject) => { const img = new Image(); img.onload = () => { const canvas = document.createElement('canvas'); canvas.width = img.width; canvas.height = img.height; const ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0); canvas.toBlob((blob) => { URL.revokeObjectURL(svgUrl); resolve(blob); }, 'image/png'); }; img.onerror = reject; img.src = svgUrl; }); } // 配合Cordova File插件保存 async saveImageToLocal(blob: Blob, fileName: string) { const fileEntry = await this.file.writeFile(this.file.dataDirectory, fileName, blob, { replace: true }); return fileEntry.nativeURL; }
转PDF(使用jsPDF)
先安装依赖:npm install jspdf
import jsPDF from 'jspdf'; async svgToPdf(svgString: string): Promise<Blob> { const svgBlob = new Blob([svgString], { type: 'image/svg+xml' }); const svgUrl = URL.createObjectURL(svgBlob); return new Promise((resolve, reject) => { const img = new Image(); img.onload = () => { const pdf = new jsPDF('p', 'px', [img.width, img.height]); pdf.addImage(img, 'PNG', 0, 0, img.width, img.height); const pdfBlob = pdf.output('blob'); URL.revokeObjectURL(svgUrl); resolve(pdfBlob); }; img.onerror = reject; img.src = svgUrl; }); }
关键注意事项
- 确保SVG中
foreignObject的xmlns属性为http://www.w3.org/1999/xhtml - 移除SVG中
-webkit-transform,替换为SVG原生transform属性 - Cordova项目需确保
Content-Security-Policy允许data URI加载(默认允许,自定义CSP需检查)
内容的提问来源于stack exchange,提问作者VinceSanto
相关产品推荐
相关产品推荐

