logo

G

  • Tutorials
  • API
  • Examples
  • Plugins
  • Productsantv logo arrow
  • 6.1.26
  • Getting Started
  • Section I - Defining the Scenario
  • Section II - Using the renderer
  • Section III - Adding some interaction
  • Diving Deeper
    • Scene Graph
    • 创造一个“太阳系”
    • 使用相机
    • 使用插件
    • 实现一个简单的动画
    • GPGPU 初体验
    • 进入 3D 世界
    • 使用 Box2D 物理引擎
    • 使用 matter.js 物理引擎
    • 使用 Yoga 布局引擎
    • Takeover D3's rendering
    • Takeover Observable Plot's rendering
    • Choose a renderer
    • Rendering on demand
  • Advanced Topics
    • 性能优化
    • 自定义图形
    • 理解事件传播路径
    • Using react-g
    • Exporting the contents of the canvas
    • Using lite version

Takeover D3's rendering

Previous
使用 Yoga 布局引擎
Next
Takeover Observable Plot's rendering

Resource

Ant Design
Galacea Effects
Umi-React Application Framework
Dumi-Component doc generator
ahooks-React Hooks Library

Community

Ant Financial Experience Tech
seeconfSEE Conf-Experience Tech Conference

Help

GitHub
StackOverflow

more productsMore Productions

Ant DesignAnt Design-Enterprise UI design language
yuqueYuque-Knowledge creation and Sharing tool
EggEgg-Enterprise-class Node development framework
kitchenKitchen-Sketch Tool set
GalaceanGalacean-Interactive solution
xtechLiven Experience technology
© Copyright 2025 Ant Group Co., Ltd..备案号:京ICP备15032932号-38

Loading...

The G API is as consistent as possible with the DOM API, so some of the DOM API-oriented libraries in the Web ecosystem can be accessed at very low cost, for example using the Hammer.js gesture library, using the Interact.js drag-and-drop library. For them, G's event API and the DOM Events API are identical:

import Hammer from 'hammerjs';
// Give Hammer.js the Circle as a DOM element directly
const hammer = new Hammer(circle);
hammer.on('press', (e) => {
console.log("You're pressing me!");
console.log(e.target); // circle
});

Similarly, for D3, it is perfectly possible to take over its internal default SVG (which is also part of the DOM API) rendering, done using Canvas or WebGL, while retaining its data-driven capabilities.

In the following example, we use several tutorial examples from Fullstack D3 to switch the rendering API with a "one-line" code change while retaining most of the D3 style code We'll use a few examples from Fullstack D3 to implement D3 data processing + G rendering, while keeping most of the D3 style code. You can switch between Canvas, WebGL and SVG rendering at runtime.

Some stylized rendering plugins can also be used directly, such as g-plugin-rough-canvas-renderer for hand-drawn styling of the bar chart above.

sketchy barchart with D3

See: https://observablehq.com/@xiaoiver/d3-rough-barchart

It's worth mentioning that I first saw this idea in Sprite.js, but at the time it was not quite finished with the DOM API, so some of the D3 APIs (e.g. join) didn't work properly.

In theory, this could also solve the performance problems of other SVG-based drawing libraries, but there are some "limitations" to this solution.

One line of code modification

Example bar chart from Fullstack D3

The "one line of code" is indeed a bit of a headline, after all the steps to create the G canvas and renderer cannot be missing.

const canvasRenderer = new CanvasRenderer();
const canvas = new Canvas({
container: 'container',
width: 600,
height: 500,
renderer: canvasRenderer,
});

Now we don't need D3 to create <svg>, we just need to give D3 the root node of the G scene graph, the size of the canvas is already specified at creation time.

// Before the change: D3 uses the DOM API to create `<svg>`
const wrapper = d3
.select('#wrapper')
.append('svg')
.attr('width', dimensions.width)
.attr('height', dimensions.height);
// After the change: Give the root node of the G scene graph to D3
const wrapper = d3.select(canvas.document.documentElement);

That's all that's been changed, and you can now use the D3 syntax in full. For example, creating a <g> and setting the style, G will make D3 think it is still manipulating the DOM API.

const bounds = wrapper
.append('g')
.style(
'transform',
`translate(${dimensions.margin.left}px, ${dimensions.margin.top}px)`,
);

Or use D3's event mechanism to add some event interaction, such as modifying the column color in response to a mouse event.

binGroups
.on('mouseenter', function (e) {
d3.select(e.target).attr('fill', 'red');
})
.on('mouseleave', function (e) {
d3.select(e.target).attr('fill', 'cornflowerblue');
});
Example

Using g-plugin-css-selector

When using D3 and third-party extensions, it is often necessary to use CSS selectors, for example d3-annotation would use the following syntax.

var group = selection.select('g.annotations');

In order to get CSS selectors like g.annotations to work properly, the g-plugin-css-selector plugin is required, registered as follows.

import { Plugin } from '@antv/g-plugin-css-select';
renderer.registerPlugin(new Plugin());

In addition, CSS style sheets are often used in D3 projects, such as this example, which uses d3-annotation to set the stroke color.

.annotation path {
stroke: var(--accent-color);
}

We can use the G-like DOM API's element query method querySelectorAll.

const paths = canvas.document.querySelectorAll('.annotation path');
paths.forEach(() => {});

Or continue using D3's selector syntax.

svg.select('.annotation path').style('stroke', 'purple');

Implementation principle

Before explaining the limitations of the program, it is necessary to understand the rationale behind it. We will expand on the following.

  • What kind of class libraries can be accessed seamlessly?
  • The completion of DOM API implementation in G.
  • Other gains

What kind of class libraries can be accessed seamlessly?

First of all, not all DOM API-based libraries are as "seamlessly accessible" to G as Hammer.js, interact.js, [D3](https://github. com/d3/d3). Or rather, the libraries that are suitable for access have in common that they do not assume that they are in a real browser DOM environment.

Let's take D3 as an example, d3-selection does not use window.document directly when creating a DOM element, but takes it from the element's [ownerDocument](/en/api/ builtin-objects/node#ownerdocument) attribute of the element, so as long as the G graphic also has that attribute of the same name, it will work without calling the browser's real DOM API.

// @see https://github.com/d3/d3-selection/blob/main/src/creator.js#L6
// get document
var document = this.ownerDocument;
// create element with document
document.createElement(name);

These libraries have the added benefit of being suitable for running test cases on the node side with jsdom (D3's [practice](https://github.com/d3/d3-selection/blob/main/test /jsdom.js)).

Therefore, if X6 wants to access G in this way in the future, it needs to make sure that there is no usage like window.document as well.

Finally G needs to avoid the assumption of a "browser real DOM environment" in its own internal implementation, e.g. when creating canvas, so that it can run in a WebWorker or even an applet environment.

The completion of DOM API implementation in G

With the appropriate access to the class library, whether it works properly depends on the extent to which the G for DOM API is implemented. Still using D3 as an example, before inserting an element into a document it will use compareDocumentPosition to compare positions. If G does not implement this API, the runtime will report an error.

The core API of G is actually a lightweight version of jsdom. Why "lightweight"? Because many functions such as HTML parsing and non-inline CSS styles have been omitted.

The current G implementation of the DOM API is as follows.

  • Node & Element API Disguise the G graphic as a real DOM element
  • Event System Provide complete event binding and propagation process
  • Web Animations API Provides command-based animation capabilities
  • CustomElementRegistry The graph is created by name, so D3 code like wrapper.append('g') actually creates a Group of G
  • MutationObserver For monitoring structural and attribute changes between elements.
  • The style property is calculated, so code like el.style('font-size', '1em') in D3 that contains relative units will work.

Other Gains

D3's ecosystem is very large, and seamless access means that many capabilities are available out of the box.

In this example, we use d3-shape and [d3-transition](https://github.com/d3/ d3-transition) to implement the shape animation.

This leads us to think about another question, whether G's animation capabilities should also be pluggable. In the above example, G only needs to provide rendering capabilities, and the animation of the d attribute of path is left entirely to D3.

Limitations

innerHTML

We have selectively implemented most of the DOM API, which means that APIs such as innerHTML are dropped.

el.innerHTML = '<div></div>';

So selection.html() in D3 does not work for now.

If we want to implement this feature, G needs to think about "mixed" rendering. Currently only one renderer can be selected to render all graphics at the same time, and blended rendering requires HTML to co-exist with graphics rendered using Canvas / WebGL. Considering the order of rendering and interaction between these hybrid contents is not an easy task.

It is worth mentioning that the new Google Docs, from the official sample document, contains two SVG and one Canvas on the first page. The main part (mainly text) is drawn with Canvas / WebGL and supports text selection, while the image (top right corner, inside the document) is drawn with SVG.

External Style Sheets

Currently, G does not support external style sheets either, so external style sheets in D3 applications will not work.

However, inline usage is valid, such as the following usage in the example.

const barText = binGroups
.filter(yAccessor)
.append('text')
.attr('x', (d) => xScale(d.x0) + (xScale(d.x1) - xScale(d.x0)) / 2)
.attr('y', (d) => yScale(yAccessor(d)) - 5)
.text(yAccessor)
.attr('fill', 'darkgrey')
.style('text-anchor', 'middle')
.style('font-size', '12px')
.style('font-family', 'sans-serif');

foreignObject

The foreignObject in SVG allows for embedded HTML, which, like innerHTML, is not supported at this time.