@antv/g-image-exporter
v1.0.20
Published
A image exporter for G using DOM API
Downloads
1,026
Maintainers
Readme
g-plugin-image-exporter
一些图表库提供了保存内容到图片的功能,下图来自 Highcharts:
为此我们提供了 g-image-exporter
,它支持选定画布区域,导出指定格式的 dataURL 或保存成图片等功能,示例。其中部分功能依赖 DOM API,对于非浏览器运行环境,请参考 画布的特殊运行平台适配。例如下载功能需要通过 document.createElement('a')
实现,非浏览器环境需要自行传入 document
对象。
配置项
创建时可以指定以下配置项,其中 canvas
为必填项,将画布传入:
import { ImageExporter } from '@antv/g-image-exporter';
const exporter = new ImageExporter({
canvas, // 传入画布
defaultFilename: 'my-default-filename',
});
defaultFilename
在调用 downloadImage 保存并下载图片时,如果没有指定文件名,将使用该配置项的值作为默认文件名。
API
toCanvas
该方法用以将指定区域的画布内容绘制到额外的 HTMLCanvasElement 中,随后可以根据需要进一步加工,例如添加背景色、水印等。
完整方法签名如下,该方法为异步:
toCanvas(options: Partial<CanvasOptions> = {}): Promise<HTMLCanvasElement>;
interface CanvasOptions {
clippingRegion: Rectangle;
beforeDrawImage: (context: CanvasRenderingContext2D) => void;
afterDrawImage: (context: CanvasRenderingContext2D) => void;
}
各配置项含义如下:
- clippingRegion 画布裁剪区域,用矩形表示
- beforeDrawImage 在绘制画布内容前调用,适合绘制背景颜色
- afterDrawImage 在绘制画布内容后调用,适合绘制水印
- ignoreElements 在导出 HTML 内容时,如何判断容器内一个 HTMLElement 是否被忽略
在该示例中,我们添加了背景色和水印,通过传入的 CanvasRenderingContext2D 可以调用 Canvas2D API 进行绘制:
import { Rectangle } from '@antv/g';
const canvas = await exporter.toCanvas({
// 忽略 stats.js lil-gui 等在容器内添加的 DOM 元素
ignoreElements: (element) => {
return [gui.domElement, stats.dom].indexOf(element) > -1;
},
// 指定导出画布区域
clippingRegion: new Rectangle(
clippingRegionX,
clippingRegionY,
clippingRegionWidth,
clippingRegionHeight,
),
beforeDrawImage: (context) => {
// 绘制背景色
context.fillStyle = backgroundColor;
context.fillRect(0, 0, clippingRegionWidth, clippingRegionHeight);
},
afterDrawImage: (context) => {
// 绘制水印
context.font = '24px Times New Roman';
context.fillStyle = '#FFC82C';
context.fillText('AntV', 20, 20);
},
});
注意裁剪区域使用的是 Rectangle
而非 Rect 图形。它的构造函数中包含 x/y/width/height
四个参数。它相对于视口坐标系下,即对于一个 400 x 400 的画布,裁剪的最大宽高就是 400。
在导出 HTML 时,默认会导出容器内的全部 HTMLElement,但有时有些元素并不是我们想导出的,此时可以使用 ignoreElements: (element: Element): boolean;
方法进行过滤。例如该示例中容器内还有 stats.js 和 lil-gui 添加的 DOM 元素,我们并不希望导出,此时可以:
ignoreElements: (element) => {
return [gui.domElement, stats.dom].indexOf(element) > -1;
},
toSVGDataURL
有时我们想导出矢量图。不同于 toCanvas 对于所有渲染器都支持,只有 g-svg 渲染器支持生成 SVG 类型的 dataURL,如果选择了其他渲染器,将返回 Promise<undefined>
。
方法签名如下:
toSVGDataURL(): Promise<string>;
内部使用 XMLSerializer 实现,将 SVGElement 序列化成 XML 字符串。
downloadImage
触发浏览器下载行为,可以将 导出的 dataURL 传入并指定保存的文件名。
完整方法签名如下:
downloadImage(options: DownloadImageOptions): void;
interface DownloadImageOptions {
dataURL: string;
name?: string;
}
在该示例中,点击按钮立即开始下载图片,如果选择了 image/png
格式,最终保存成 my-file.png
文件:
const canvas = await exporter.toCanvas();
const dataURL = canvas.toDataURL();
// 触发下载
exporter.downloadImage({
dataURL,
name: 'my-file',
});
下载行为是通过使用 document
创建 HTMLAnchorElement 并触发它的默认点击行为实现的。
导出 dataURL
通过 toCanvas 我们得到了包含画布内容的 HTMLCanvasElement,使用其原生方法 toDataURL 就可以得到 dataURL:
const canvas = await exporter.toCanvas();
const dataURL = canvas.toDataURL(); // data:...
在 toDataURL 方法中可以指定图片格式,默认为 image/png
,以及图片质量,详见参数。
导出 ImageData
HTMLCanvasElement 同样提供了 getImageData 方法用于获取指定区域的像素数据:
const canvas = await exporter.toCanvas();
const imageData = canvas.getImageData(50, 50, 100, 100); // ImageData { width: 100, height: 100, data: Uint8ClampedArray[40000] }
导出 PDF
如果我们还想在前端根据图片生成 PDF,可以参考:https://github.com/parallax/jsPDF
注意事项
导出图片的物理尺寸
导出图片的物理尺寸已经包含了 resolution,即对于指定了宽高 400 x 400 的画布,如果当前环境的 devicePixelRatio 为 2,将生成 800 x 800 的图片。
可以导出 HTML 吗?
可以,如果画布中包含 HTML,目前不同的渲染器实现如下:
- 导出 SVG,其中天然包含 foreignObject
- 导出其他图片格式,内部使用 html2canvas 实现
在该示例中,左上角 Tooltip 就是一个 HTML。
为何 toCanvas 为异步方法?
HTMLCanvasElement 的原生方法 toDataURL 的确是一个同步方法。
但由于 WebGL / Canvaskit 使用双缓冲机制,拥有绘制 Buffer 和展示 Buffer。好处是相比每一帧都拷贝绘制 Buffer 的内容到展示 Buffer,直接交换效率更高。因此在创建 WebGL 上下文时我们关闭了 preserveDrawingBuffer,但需要确保调用 toDataURL 时渲染没有被清除(调用 gl.clear()
),这会导致该行为变成异步,等待下一次渲染 tick 时才能获取内容。
另外在导出 HTML 内容时,使用 html2canvas 提供的导出方法同样也是一个异步操作。
如何导出画布视口之外的图形?
我们提供的导出方法都只针对画布视口范围,即使是裁剪也是相对视口坐标系下。因此如果想导出视口之外的图形,可以使用相机 API 在不改变场景结构的前提下改变视口范围,例如通过 setZoom 进行缩放,让视口内容纳更多图形。
toDataURL polyfill
HTMLCanvasElement 的原生方法 toDataURL 有可能在某些古早浏览器上不支持,此时可以使用 polyfill: https://stackoverflow.com/a/47148969