Loading...
使用 CanvasRenderingContext2D 绘制 2D 图形。会在容器中创建一个 <canvas>
元素。
和 @antv/g
一样,也有以下两种使用方式。
安装 @antv/g-canvas
后可以从中获取渲染器:
import { Canvas } from '@antv/g';import { Renderer } from '@antv/g-canvas';const canvasRenderer = new Renderer();const canvas = new Canvas({container: 'container',width: 600,height: 500,renderer: canvasRenderer,});
<scriptsrc="https://unpkg.com/@antv/g-canvas/dist/index.umd.min.js"type="application/javascript">
从 G.Canvas2D
命名空间下可以获取渲染器:
const canvasRenderer = new window.G.Canvas2D.Renderer();
在创建渲染器时,可以传入一些初始化配置项,例如:
import { Renderer } from '@antv/g-canvas';const renderer = new Renderer({enableDirtyRectangleRendering: true,});
是否开启“脏矩形”渲染。开启后将大幅提升 Canvas2D 环境下的渲染性能。默认开启。
一种常见的交互是通过鼠标高亮某个图形。此时场景中仅有一小部分发生了改变,擦除画布中的全部图形再重绘就显得没有必要了。类比 React diff 算法能够找出真正变化的最小部分,“脏矩形”渲染能尽可能复用上一帧的渲染结果,仅绘制变更部分,特别适合 Canvas2D API。
下图展示了这个思路:
在以上求交与区域查询的过程中,我们可以复用剔除方案中的优化手段,例如加速结构。在实现中我们使用了 RBush。
显然当动态变化的对象数目太多时,该优化手段就失去了意义,试想经过一番计算合并后的“脏矩形”几乎等于整个画布,那还不如直接清空重绘所有对象。因此例如 Pixi.js 这样的 2D 游戏渲染引擎就不考虑内置。
但在可视化这类相对静态的场景下就显得有意义了,例如在触发拾取后只更新图表的局部,其余部分保持不变。
用于 debug,默认关闭,开启后画布会触发 CanvasEvent.DIRTY_RECTANGLE
事件并携带脏矩形信息,可用于后续可视化。
在该示例中,当鼠标划过各个圆时,能展示出当前需要被清除的脏矩形,当前帧仅会重绘该区域:
需要注意的是,脏矩形的坐标在 Canvas 坐标系下,如果想使用 HTML 绘制浮层,需要使用坐标系转换方法:
// display dirty rectangleconst $dirtyRectangle = document.createElement('div');$dirtyRectangle.style.cssText = `position: absolute;pointer-events: none;background: rgba(255, 0, 0, 0.5);`;$wrapper.appendChild($dirtyRectangle);canvas.addEventListener(CanvasEvent.DIRTY_RECTANGLE, (e) => {const { dirtyRect } = e.detail;const { x, y, width, height } = dirtyRect;const dpr = window.devicePixelRatio;// convert from canvas coords to viewport$dirtyRectangle.style.left = `${x / dpr}px`;$dirtyRectangle.style.top = `${y / dpr}px`;$dirtyRectangle.style.width = `${width / dpr}px`;$dirtyRectangle.style.height = `${height / dpr}px`;});
该渲染器内置了以下插件:
除了内置插件,还有以下可选插件。
使用 rough.js 的 Canvas 版本进行手绘风格的渲染。
我们提供了 g-plugin-rough-canvas-renderer 插件,注册后会替换掉 g-plugin-canvas-renderer 对于部分 2D 图形的渲染能力。
示例效果如下:
该渲染器依赖 CanvasRenderingContext2D 渲染能力,并不局限在浏览器端,因此也可以使用 node-canvas 进行服务端渲染。
在我们的集成测试中,会在 Node 端配合 node-canvas 渲染结果图片,与基准图片进行比对。其他服务端渲染场景也可以按照以下步骤进行:
Canvas
对象,通过 canvas 属性传入画布https://github.com/antvis/g/blob/next/integration/nodetests__/canvas/circle.spec.js
const { createCanvas } = require('canvas');const { Circle, Canvas } = require('@antv/g');const { Renderer } = require('@antv/g-canvas');// create a node-canvasconst nodeCanvas = createCanvas(200, 200);// create a renderer, unregister plugin relative to DOMconst renderer = new Renderer();const domInteractionPlugin = renderer.getPlugin('dom-interaction');renderer.unregisterPlugin(domInteractionPlugin);const canvas = new Canvas({width: 200,height: 200,canvas: nodeCanvas, // use node-canvasrenderer,});const circle = new Circle({style: {r: 10,fill: 'red',},});canvas.appendChild(circle);// output imageconst out = fs.createWriteStream(__dirname + RESULT_IMAGE);const stream = nodeCanvas.createPNGStream();stream.pipe(out);out.on('finish', () => {});
如果希望在 G 绘制之后使用 CanvasRenderingContext2D 继续绘制,可以在 CanvasEvent.AFTER_RENDER
时获取上下文,此时 G 已经完成了绘制,但由于在上下文中设置可 transform,在绘制前需要先清除,然后就可以按照 Canvas 原生坐标系进行绘制:
// 在 G 绘制完接着画canvas.addEventListener(CanvasEvent.AFTER_RENDER, () => {// 获取原生 Canvas2DContextconst context = canvas.getContextService().getContext();// 重置 transformcontext.resetTransform();// 绘制context.fillStyle = 'red';context.fillRect(200, 200, 100, 100);});