如何将Canvas转为SVG

70 阅读1分钟

之前做了个关系图谱的需求,因为导出png等图片放大会失真,所以希望导出为矢量图。但是G6自带的导出图谱功能再Canvas模式下无法导出为SVG,而SVG模式又有很多特性不支持,所以就想办法能不能把Canvas导出为SVG了。

本代码使用了一个多年的老库,叫做canvas-to-svg

该库模拟了canvas并且暴露了一些canvas的api,最后可以导出为svg

需要注意的是,部分canvas的api并不支持,比如 setTransform,resetTransform等,如果你需要用到这两个api,可以将其拆分为translate和rotate等

直接上代码:

import CanvasToSVG from 'canvas-to-svg';
function addTextWaterMaskToCanvas(
  context: CanvasRenderingContext2D,
  textWaterMask: string,
) {
  context.font = '16px Microsoft YaHei';
  context.fillStyle = '#F2F3F5';
  const { width, height } = context.canvas;
  const textMaskCountHorizontal = Math.max(1, width / 150);
  const textMaskCountVertical = Math.max(1, height / 150);
  const fwidth = width / textMaskCountHorizontal;
  const fheight = height / textMaskCountVertical;
  for (let hh = 0; hh < height; hh += fheight) {
    for (let ww = 0; ww < width; ww += fwidth) {
      context.translate(ww, hh);
      context.rotate(-Math.atan(height / width));
      context.fillText(textWaterMask, -fwidth / 2, fheight / 2);
      context.rotate(Math.atan(height / width));
      context.translate(-ww, -hh);
    }
  }
}

export function downloadFullImageWithWaterMaskCreator(
  graphInstance: IGraph | null,
  textWaterMask: string,
) {
  return function (
    name?: string,
    type?: DataUrlType,
    imageConfig?: {
      backgroundColor?: string;
      padding?: number | number[];
    },
    callback?: (success: boolean) => void,
  ) {
    graphInstance?.toFullDataURL(
      res => {
        const image = new Image();
        image.src = res;
        image.onload = function () {
          try {
            // const canvas = document.createElement('canvas');
            // canvas.width = image.width;
            // canvas.height = image.height;
            // const context = canvas.getContext('2d');
            const context = new CanvasToSVG(image.width, image.height);
            if (context && name) {
              context.rect(0, 0, image.width, image.height);
              context.drawImage(image, 0, 0, image.width, image.height);

              addTextWaterMaskToCanvas(context, textWaterMask);

              // downloadCanvasImage(canvas, name, type);

              const svg = context.getSerializedSvg(true);
              const svgData = URL.createObjectURL(
                new Blob([svg], { type: 'image/svg' }),
              );
              const downloadLink = document.createElement('a');
              downloadLink.download = `${name}.svg`;
              downloadLink.href = svgData;
              downloadLink.click();

              callback?.(true);
            }
          } catch {
            callback?.(false);
          }
        };

        image.onerror = function () {
          callback?.(false);
        };
      },
      type,
      imageConfig,
    );
  };
}