在Vue流行的今天,许多应用中的特效和画板功能需要将SVG转换为图片以便用户下载。最近,在我负责的一个项目中,由于使用了自定义字体,遇到了一个问题:SVG无法以正确的方式转换为图片下载,导致缺失了自定义样式。

为解决这一问题,我最初采用了将SVG渲染到Canvas中的方案。通过利用Canvas的 toDataURL() 方法,可以获取到转换后的图片数据。随后,通过创建一个隐藏的 <a> 标签并设置其 href 属性为图片数据的URL,触发下载操作。以下是一个使用Vue 3的示例代码:

<template>
    <!-- 假设你的SVG内容在这里 -->
    <svg ref="svgElement" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
      <!-- SVG内容,包含自定义字体 -->
      <text font-family="abc" font-size="22" x="20" y="20">自定义字体</text>
    <button @click="downloadSvgAsImage">下载SVG为图片</button>
</template>
<script setup>
  import {
  } from 'vue';
  const svgElement = ref(null);
  // SVG 转图片并下载的函数
  function downloadSvgAsImage() {
    if (!svgElement.value) {
      return;
    const serializer = new XMLSerializer();
    const svgStr = serializer.serializeToString(svgElement.value);
    const blob = new Blob([svgStr], {
      type: 'image/svg+xml;charset=utf-8'
    const url = URL.createObjectURL(blob);
    const img = new Image();
    img.onload = function() {
      // 创建一个canvas元素
      const canvas = document.createElement('canvas');
      canvas.width = img.width;
      canvas.height = img.height;
      const ctx = canvas.getContext('2d');
      ctx.drawImage(img, 0, 0, img.width, img.height);
      // 将canvas转换为图片
      const dataUrl = canvas.toDataURL('image/png');
      // 创建一个隐藏的a标签用于下载
      const a = document.createElement('a');
      a.href = dataUrl;
      a.download = 'custom-font-svg.png';
      a.style.display = 'none';
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
      URL.revokeObjectURL(url);
    img.src = url;
</script>
<style scoped>
  /* 在这里定义你的自定义字体 */
  /* 推荐一个免费的字体库,部分收费:https://font.chinaz.com/ */
  @font-face {
    font-family: 'abc';
    src: url('@/assets/fonts/abc.ttf');
    font-weight: normal;
    font-style: normal;
</style>

在上述情况中,当SVG中包含text标签并应用了自定义字体时,发现导出的图片并未正确呈现这些自定义样式。为了解决这个问题,曾尝试使用第三方库,如html2canvas或dom-to-image,期望通过截图的方式来实现。然而,令人遗憾的是,这种方法仍然未能有效解决问题。

经过我的深入剖析,问题的根源在于资源文件未能正确加载,因此需要确保在SVG的 defs 标签中对其进行定义。然而,令人遗憾的是,尽管我尝试了多次,却始终未能成功解决这个问题。网络上提供的案例虽然为我提供了一些思路,但并未能实际解决面临的问题。

最后,我灵光一闪,如果我们尝试以编程的方式直接绘制字体,而不是依赖外部资源文件,是否能够绕过这个难题呢?于是,我尝试编写了以下代码来实现这一想法:采用canvas的2D画笔实现

<template>
    <!-- 假设你的SVG内容在这里 -->
    <svg ref="svgElement" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
      <!-- SVG内容,包含自定义字体 -->
      <text font-family="abc" font-size="22" x="20" y="20">自定义字体</text>
    <button @click="downloadSvgAsImage">下载SVG为图片</button>
</template>
<script setup>
  import {
  } from 'vue';
  const svgElement = ref(null);
  // SVG 转图片并下载的函数
  function downloadSvgAsImage() {
    if (!svgElement.value) {
      return;
    // 提取所有text标签信息,根据自己实际情况修改
    const texts = document.getElementsByTagName("text");
    const serializer = new XMLSerializer();
    let svgStr = serializer.serializeToString(svgElement.value);
    //删除掉文字,解决自定义字体
    svgStr = svgStr.replaceAll(/<text(?:\s+[^>]+)?>([\s\S]*?)<\/text>/g, '');
    const blob = new Blob([svgStr], {
      type: 'image/svg+xml;charset=utf-8'
    const url = URL.createObjectURL(blob);
    const img = new Image();
    img.onload = function() {
      // 创建一个canvas元素
      const canvas = document.createElement('canvas');
      canvas.width = img.width;
      canvas.height = img.height;
      const ctx = canvas.getContext('2d');
      ctx.drawImage(img, 0, 0, img.width, img.height);
      // 关键代码
      for (let i = 0; i < texts.length; i++) {
        const text = texts[i];
        // 重新绘制文本,这次在画布上
        ctx.font = `${text.getAttribute('font-size')}px ${text.getAttribute('font-family')}`;
        //ctx.fillStyle = 'black';// 颜色
        // 如果是渐变颜色,创建一个线性渐变对象,svg对应defs下的linearGradient
        //const lg = ctx.createLinearGradient(0, 0, img.width, 0);
        // 添加颜色停止点
        //lg.addColorStop(0, 'rgba(0, 0, 0,1)'); // 在%位置
        //lg.addColorStop(1, 'rgba(255, 0, 0,1)'); // 在%位置
        //ctx.fillStyle = lg;
        ctx.fillText(text.textContent, text.getAttribute('x'), text.getAttribute('y'));
      // 将canvas转换为图片
      const dataUrl = canvas.toDataURL('image/png');
      // 创建一个隐藏的a标签用于下载
      const a = document.createElement('a');
      a.href = dataUrl;
      a.download = 'custom-font-svg.png';
      a.style.display = 'none';
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
      URL.revokeObjectURL(url);
    img.src = url;
</script>
<style scoped>
  /* 在这里定义你的自定义字体 */
  /* 推荐一个免费的字体库,部分收费:https://font.chinaz.com/ */
  @font-face {
    font-family: 'abc';
    src: url('@/assets/fonts/abc.ttf');
    font-weight: normal;
    font-style: normal;
</style>

当然,这个解决方案提供了一个最原始且核心的实现方式,但我们可以进一步对代码进行优化以提升效率和用户体验。例如,在导出文件时,可以利用 file-saver 这一第三方库来简化文件保存的操作,并确保文件的正确性和完整性。通过整合 file-saver 库,我们可以更高效地处理文件导出过程,提升整体功能的稳定性和可靠性。

svg 2Font 将 SVG 换为TTF / EOT / WOFF / WOFF2 / SVG 格式。 注意: svg -> svg font-> ttf ttf-> EOT ttf-> WOFF ttf-> WOFF2 ttf-> svg , , Unicode字符平面映射 始末字符值 U + 0000-U + FFFF 基本多文种平面 基本多语言平面,简称BMP U + 10000-U + 1FFFF 多文种补充平面 补充多语言平面,简称SMP U + 20000-U + 2FFFF 表意文字补充平面 表意补充平面,简称SIP 单个 svg 文件,与 svg font文件不一样, svg 字体 是特殊处理的图标集合,像文字一样组合好后,会有Unicode代码点,一般用工具处理获得 下载 SVG 图标https://www.iconfont.cn/ 创建 SVG fontshttps://icomoon.io/ 参考链接: 1. SVG fonts( SVG 字体 )https://cloud.tencent.com/deve...
今天来说一下将 svg 图片 化为 字体 图片 的办法,很简单,不啰嗦直接讲方法。一、 方法 11.讲 svg 图片 字体 图片 网站 icoMoon : https://icomoon.io 进入网站后点击右上角 icoMoom App 按钮进入后,它提供了一些 图片 ,可以点击选中,如果不想可以点击左上角 import Icons 按钮,上传你做好的 svg 图片 。然后:下载好后的文件:需要解压可以看看例