Loading...
In G2, there is no concept of charts, instead marks are used as the basic visual component units. Any chart can be composed of one or more marks.
Marks are the most fundamental graphic units in the G2 drawing framework and are one of the core components that constitute complex charts. They have diverse characteristics and powerful expressiveness, serving as "atomic" components for building charts that can be flexibly used. Through proper combination, arrangement, and customization of marks, users can not only meet conventional chart drawing needs but also create highly personalized and complex visualization works. This freedom gives marks a wide range of application space in data visualization, including but not limited to basic graphics such as scatter plots, bar charts, pie charts, and more structured or innovative hybrid charts. This characteristic enables G2 to demonstrate great advantages in drawing flexibility and customization capabilities.
As mentioned above, by adding Point marks for scatter plots and Link marks for connection graphs to a chart, we can obtain a point-line connection graph with annotations.
import { Chart } from '@antv/g2';const chart = new Chart({container: 'container',height: 180,});chart.options({type: 'view',data: {type: 'fetch',value: 'https://assets.antv.antgroup.com/g2/penguins.json',transform: [{type: 'map',callback: (d) => ({...d,body_mass_g: +d.body_mass_g,}),},],},children: [// point mark{type: 'point',encode: { x: 'body_mass_g', y: 'species' },style: { stroke: '#000' },tooltip: { items: [{ channel: 'x' }] },},// link mark{type: 'link',encode: { x: 'body_mass_g', y: 'species' },transform: [{ type: 'groupY', x: 'min', x1: 'max' }],style: { stroke: '#000' },tooltip: false,},// point mark for drawing median line{type: 'point',encode: { y: 'species', x: 'body_mass_g', shape: 'line', size: 12 },transform: [{ type: 'groupY', x: 'median' }],style: { stroke: 'red' },tooltip: { items: [{ channel: 'x' }] },},],});chart.render();
Based on the data dimensions that marks can represent, they can be categorized as:
The freedom of marks is related to the visual channel size that data can be mapped to the graphics. From this perspective:
The representation form of marks is related to the visual channel color that data can be mapped to the graphics. From this perspective:
point
shape of point marks, the rect
shape of interval marks, etc. The color channel is generally represented in the mark's fill color fill
.hollow
shape of interval marks, etc. The color channel is generally represented in the mark's line color stroke
.fill
.Currently, G2 supports the following built-in marks:
type | Description | Properties | Examples |
---|---|---|---|
area | Use area fill to show data trends, suitable for stacking | area | |
box | Basic box plot, showing data distribution and outliers | box | |
boxplot | Box plot with aggregation calculation, auto-calculates quartiles | boxplot | |
cell | Divides space into blocks for visualization, used for calendar and heatmaps | cell | |
chord | Chord diagram showing relationship strength between entities | chord | |
density | Kernel density estimation, commonly used for violin plots | density | |
gauge | Gauge chart showing progress indicators | gauge | |
heatmap | 2D density distribution using color encoding for data density | heatmap | |
image | Renders images at specified positions | image | |
interval | Basic bar/column chart, can generate pie charts through coordinate transformation | interval | |
line | Line chart supporting smooth curves and step lines | line | |
lineX | Vertical auxiliary line, commonly used for marking specific values | lineX | |
lineY | Horizontal auxiliary line, commonly used for marking thresholds | lineY | |
link | Arrow mark showing relationships between nodes | link | |
liquid | Liquid chart showing percentage progress | liquid | |
point | Scatter plot encoding multi-dimensional data through size/color | point | |
polygon | Polygon mark, often used with layout algorithms | polygon | |
range | Rectangle area mark for highlighting specific intervals | range | |
rangeX | Vertical direction area mark | rangeX | |
rangeY | Horizontal direction area mark | rangeY | |
rect | Basic rectangle mark for histograms/treemaps | rect | |
shape | Fully customizable graphic mark | shape | |
text | Data label mark supporting rich text formatting | text | |
vector | Vector field mark showing direction/strength dual-dimensional data | vector | |
wordCloud | Word cloud encoding word frequency through text size | wordCloud |
Each graphic mark is an independent entity, with the mark type specified through the type
property. Marks are the core atomic units of the G2 visualization system and are leaf nodes in the view tree. As "first-class citizens" of G2, their core components include the following concepts:
Data Related
Graphic Generation
Visual Appearance
Interactive Dynamics
Chart Components
Extension Control
({type: 'mark',data: [],encode: {},scale: {},transform: [],coordinate: {},style: {},viewStyle: {},animate: {},state: {},label: {},title: {},axis: {},legend: {},tooltip: {},scrollbar: {},slider: {},interaction: {},theme: {},});
Marks can be declared as top-level types as follows:
({type: 'interval',encode: {x: 'name',y: 'value',},});
They can also be placed inside a View to add multiple marks to the view:
({type: 'view',children: [{ type: 'line' }, { type: 'point' }],});
Marks in G2 have many features, including templating, stackability, composability, etc. Proper use of these features allows for quick definition and use of various graphic styles, combining multiple marks to display richer graphic effects. These flexible and highly customizable features enable marks to meet multi-level needs from basic charts to complex visualizations.
Each built-in mark is a graphic template that generates a series of data-driven graphics, where each graphic corresponds to one or more data items. For example, in the scatter plot below, there is only one Point mark, and this mark generates multiple circles, each circle corresponding to one data item.
import { Chart } from '@antv/g2';const chart = new Chart({container: 'container',});chart.options({type: 'point',data: {type: 'fetch',value:'https://gw.alipayobjects.com/os/basement_prod/6b4aa721-b039-49b9-99d8-540b3f87d339.json',},encode: { x: 'height', y: 'weight', color: 'gender' },});chart.render();
In the line chart below, one line corresponds to multiple data items.
import { Chart } from '@antv/g2';const chart = new Chart({container: 'container',});chart.options({type: 'line',width: 900,autoFit: true,data: {type: 'fetch',value:'https://gw.alipayobjects.com/os/bmw-prod/551d80c6-a6be-4f3c-a82a-abd739e12977.csv',},encode: { x: 'date', y: 'close' },});chart.render();
G2's marks are stackable, in other words: multiple marks can be added to one view to enrich chart display effects.
The following example adds both line and point marks to the chart:
import { Chart } from '@antv/g2';const chart = new Chart({container: 'container',});chart.options({type: 'view',data: [{ year: '1991', value: 3 },{ year: '1992', value: 4 },{ year: '1993', value: 3.5 },{ year: '1994', value: 5 },{ year: '1995', value: 4.9 },{ year: '1996', value: 6 },{ year: '1997', value: 7 },{ year: '1998', value: 9 },{ year: '1999', value: 13 },],children: [{type: 'line',encode: { x: 'year', y: 'value' },},{type: 'point',encode: { x: 'year', y: 'value' },tooltip: false, // If you don't want to show tooltip for a specific mark, you can disable it individually},],});chart.render();
Of course, we can also combine more marks to draw a complex interval curve area chart with graphic meaning.
import { Chart } from '@antv/g2';const chart = new Chart({container: 'container',});chart.options({type: 'view',data: {type: 'fetch',value: 'https://assets.antv.antgroup.com/g2/range-spline-area.json',transform: [{type: 'map',callback: ([x, low, high, v2, v3]) => ({x,low,high,v2,v3,}),},],},scale: { x: { type: 'linear', tickCount: 10 } },axis: { y: { title: false } },children: [{type: 'area',encode: { x: 'x', y: ['low', 'high'], shape: 'smooth' },style: { fillOpacity: 0.65, fill: '#64b5f6', lineWidth: 1 },},{type: 'point',encode: { x: 'x', y: 'v2', size: 2, shape: 'point' },tooltip: { items: ['v2'] },},{type: 'line',encode: { x: 'x', y: 'v3', color: '#FF6B3B', shape: 'smooth' },},],});chart.render();
Marks in G2 can be composed into one mark through a mechanism and then used, for example, the point line chart above:
import { Chart } from '@antv/g2';// Define a composite markfunction PointLine({ encode, data } = {}) {return [{ type: 'line', data, encode },{ type: 'point', data, encode },];}const data = [{ year: '1991', value: 3 },{ year: '1992', value: 4 },{ year: '1993', value: 3.5 },{ year: '1994', value: 5 },{ year: '1995', value: 4.9 },{ year: '1996', value: 6 },{ year: '1997', value: 7 },{ year: '1998', value: 9 },{ year: '1999', value: 13 },];const chart = new Chart({container: 'container',});// Use the composite mark in Optionschart.mark(PointLine).data(data).encode('x', 'year').encode('y', 'value');// Use the composite mark in Specchart.options({type: PointLine,data,encode: { x: 'year', y: 'value' },});chart.render();
The composability feature of marks provides a simple yet powerful way to extend the capabilities of G2. G2 also uses this mechanism to implement some rather complex marks, such as Sankey diagrams: using two Polygon marks for composition.
import { Chart } from '@antv/g2';const chart = new Chart({container: 'container',width: 900,height: 600,});// Sankey markchart.options({type: 'sankey',layout: { nodeAlign: 'center', nodePadding: 0.03 },data: {type: 'fetch',value: 'https://assets.antv.antgroup.com/g2/energy.json',transform: [{type: 'custom',callback: (data) => ({links: data,}),},],},style: {labelSpacing: 3,labelFontWeight: 'bold',nodeStrokeWidth: 1.2,linkFillOpacity: 0.4,},});chart.render();
G2 marks support multiple transforms, allowing flexible adjustment of the geometric shape, style, or spatial layout of marks to achieve rich visual presentation effects. These transforms can not only be used for basic graphic transformations such as grouping, stacking, and binning, but can also be combined with data-driven dynamic adjustments to adapt to complex visualization scenario requirements. Through simple configuration, users can achieve intuitive mapping between data and visual elements in charts, improving chart expressiveness and readability.
Below is a color-categorized histogram after binX and stackY transforms.
import { Chart } from '@antv/g2';const chart = new Chart({container: 'container',});chart.options({type: 'rect',autoFit: true,data: {type: 'fetch',value: 'https://assets.antv.antgroup.com/g2/athletes.json',},encode: { x: 'weight', color: 'sex' },transform: [{ type: 'binX', y: 'count' },{ type: 'stackY', orderBy: 'series' },],style: { inset: 0.5 },});chart.render();
By configuring multiple transforms, we can obtain complex charts with specific representation forms. Below is an aggregated normalized stacked bar chart obtained after multiple transforms including normalizeY and stackY.
import { Chart } from '@antv/g2';const chart = new Chart({container: 'container',});chart.options({type: 'interval',autoFit: true,data: {type: 'fetch',value:'https://gw.alipayobjects.com/os/bmw-prod/87b2ff47-2a33-4509-869c-dae4cdd81163.csv',transform: [{ type: 'filter', callback: (d) => d.year === 2000 }],},encode: { x: 'age', y: 'people', color: 'sex' },transform: [{ type: 'groupX', y: 'sum' },{ type: 'stackY' },{ type: 'normalizeY' },],scale: { color: { type: 'ordinal', range: ['#ca8861', '#675193'] } },coordinate: { transform: [{ type: 'transpose' }] },axis: { y: { labelFormatter: '.0%' } },labels: [{ text: 'people', position: 'inside', fill: 'white' }],tooltip: { items: [{ channel: 'y', valueFormatter: '.0%' }] },});chart.render();
Annotations are graphic elements used to explain and emphasize areas or information that need focused attention in visualization charts. In G2 5.0, dedicated annotation components are not provided separately. Instead, annotation functionality is achieved through flexible mark configuration. In other words, annotations are actually a form of mark expression, where some marks (such as Text, Image, etc.) can be used in annotation scenarios. This design approach unifies the usage logic of marks and annotations, giving users greater freedom and flexibility to easily meet various annotation needs.
Since annotations are also a type of mark, they can also perform transforms. For example, the Select transform below.
The Select mark transform provides the ability to select data from a group of graphics based on specified channels and selectors. For example, in the following example, the country with the largest GDP in each continent is annotated.
import { Chart } from '@antv/g2';const chart = new Chart({container: 'container',});chart.options({type: 'view',data: {type: 'fetch',value:'https://gw.alipayobjects.com/os/bmw-prod/1ecf85d2-8279-46a1-898d-d2e1814617f9.json',},children: [{type: 'point',encode: { x: 'GDP', y: 'LifeExpectancy', color: 'Continent' },},{type: 'text',encode: {text: 'Country',x: 'GDP',y: 'LifeExpectancy',series: 'Continent',},// Group graphics by series, which is Continent// Select through x channel, select the maximum, which is the largest GDPtransform: [{ type: 'select', channel: 'x', selector: 'max' }],style: { textAlign: 'end' },},],});chart.render();
For simple text marks that don't require grouping, you can use Data Labels, otherwise consider the above approach.
In grammar of graphics, the core of annotations lies in accurate positioning to appropriate locations for effective communication of key information. In G2, annotation positioning supports the following three methods:
Data-driven positioning: Based on data values, bind annotations to specific chart data points or data ranges. This method can dynamically adapt to data changes, for example, when data updates or animation interactions occur, annotation positions will adjust accordingly.
Absolute positioning: Place annotations at specific positions on the canvas through fixed pixel coordinates, with no direct relationship to data. This method is suitable for adding titles, descriptions, or other annotation content unrelated to data logic.
Relative positioning: Define annotation positions through percentage or relative position parameters with reference to coordinate systems or graphic areas. This method is suitable for providing flexible layout when emphasizing or annotating areas of the overall chart.
In G2, you can specify data-driven positioning through data
. For example, in the following example where we want to annotate the safe daily intake of sugar and fat, it can be implemented as follows.
import { Chart } from '@antv/g2';const chart = new Chart({container: 'container',});chart.options({type: 'view',autoFit: true,children: [{type: 'point',data: [{ x: 95, y: 95, z: 13.8, name: 'BE', country: 'Belgium' },{ x: 86.5, y: 102.9, z: 14.7, name: 'DE', country: 'Germany' },{ x: 80.8, y: 91.5, z: 15.8, name: 'FI', country: 'Finland' },{ x: 80.4, y: 102.5, z: 12, name: 'NL', country: 'Netherlands' },{ x: 80.3, y: 86.1, z: 11.8, name: 'SE', country: 'Sweden' },{ x: 78.4, y: 70.1, z: 16.6, name: 'ES', country: 'Spain' },{ x: 74.2, y: 68.5, z: 14.5, name: 'FR', country: 'France' },{ x: 73.5, y: 83.1, z: 10, name: 'NO', country: 'Norway' },{ x: 71, y: 93.2, z: 24.7, name: 'UK', country: 'United Kingdom' },{ x: 69.2, y: 57.6, z: 10.4, name: 'IT', country: 'Italy' },{ x: 68.6, y: 20, z: 16, name: 'RU', country: 'Russia' },{ x: 65.5, y: 126.4, z: 35.3, name: 'US', country: 'United States' },{ x: 65.4, y: 50.8, z: 28.5, name: 'HU', country: 'Hungary' },{ x: 63.4, y: 51.8, z: 15.4, name: 'PT', country: 'Portugal' },{ x: 64, y: 82.9, z: 31.3, name: 'NZ', country: 'New Zealand' },],encode: { x: 'x', y: 'y', size: 'z', shape: 'point' },scale: {x: { nice: true },y: { nice: true, domainMax: 165, zero: true },size: { range: [10, 40] },},style: { stroke: '#1890ff', fillOpacity: 0.3, fill: '#1890ff' },legend: false,labels: [{ text: 'name', position: 'inside', fill: '#1890ff', stroke: '#fff' },],},{type: 'lineY',data: [50],style: { stroke: '#000', strokeOpacity: 0.45, lineDash: [3, 3] },labels: [{text: 'Safe sugar intake 50g/day',position: 'right',textBaseline: 'bottom',fill: '#000',fillOpacity: 0.45,background: true,backgroundFill: '#000',backgroundOpacity: 0.15,},],},{type: 'lineX',data: [65],style: { stroke: '#000', strokeOpacity: 0.45, lineDash: [3, 3] },labels: [{text: 'Safe fat intake 65g/day',position: 'top-left',textBaseline: 'bottom',fill: '#000',fillOpacity: 0.45,background: true,backgroundFill: '#000',backgroundOpacity: 0.15,},],},],});chart.render();
In addition to data-driven positioning, G2 also provides non-data-driven positioning methods. By specifying x and y properties through style
, x and y have the following two types, corresponding to absolute positioning and relative positioning of annotations.
import { Chart } from '@antv/g2';const chart = new Chart({container: 'container',});chart.options({type: 'view',children: [{type: 'interval',data: [{ genre: 'Sports', sold: 275 },{ genre: 'Strategy', sold: 115 },{ genre: 'Action', sold: 120 },{ genre: 'Shooter', sold: 350 },{ genre: 'Other', sold: 150 },],encode: { y: 'sold', color: 'genre' },transform: [{ type: 'stackY' }],coordinate: { type: 'theta', innerRadius: 0.5 },},{type: 'text',style: {x: 290, // Configure specific pixel coordinatesy: 200,text: 'hello',textAlign: 'center',fontSize: 60,textBaseline: 'middle',},},],});chart.render();
import { Chart } from '@antv/g2';const chart = new Chart({container: 'container',});chart.options({type: 'view',children: [{type: 'interval',data: [{ genre: 'Sports', sold: 275 },{ genre: 'Strategy', sold: 115 },{ genre: 'Action', sold: 120 },{ genre: 'Shooter', sold: 350 },{ genre: 'Other', sold: 150 },],encode: { y: 'sold', color: 'genre' },transform: [{ type: 'stackY' }],coordinate: { type: 'theta', innerRadius: 0.5 },},{type: 'text',style: {x: '50%', // Configure percentage coordinatesy: '50%',text: 'hello',textAlign: 'center',fontSize: 60,textBaseline: 'middle',},},],});chart.render();
Each mark can customize shapes, and shapes determine the final presentation form of marks. Customizing shapes mainly involves three steps:
First, let's see how to define a shape component. A shape component is a function that takes the graphic style style and context context, and returns a render function render. Where style is the processed options specified through mark.style
, and context contains the document for creating graphics with @antv/g.
The returned render function takes the graphic control points P, mapped values value, and default values defaults, and returns @antv/g graphics. Where P is an array of canvas coordinates, value is the value processed through mark.encode
, and defaults is the value specified in the theme's theme.mark.shape
. A shape component definition looks roughly like this:
function ShapeTriangle(style, context) {const { document } = context;return (P, value, defaults) => {return document.createElement('rect', {//...});};}
Next is registering the shape, which is completed by calling G2.register('shape.${mark}.${shape}', Shape)
. Where mark is the name of the mark, shape is the name of the shape, and Shape is the defined shape component. For example, registering a triangle shape for the Interval mark:
import { register } from '@antv/g2';register('shape.interval.triangle', ShapeTriangle);
Finally, using the shape can be done through mark.encode
or mark.style
.
({type: 'interval',encode: { shape: 'triangle' },// orstyle: { shape: 'triangle' },});
Below is a complete example showing how to customize shapes.
import { register, Chart } from '@antv/g2';// Define graphic componentfunction ShapeTriangle(style, context) {const { document } = context;return (P, value, defaults) => {const { color: defaultColor } = defaults;const [p0, p1, p2, p3] = P;const pm = [(p0[0] + p1[0]) / 2, p0[1]];const { color = defaultColor } = value;return document.createElement('polygon', {style: {...style,fill: color,points: [pm, p2, p3],},});};}// Register the triangleregister('shape.interval.triangle', ShapeTriangle);// Initialize chartconst chart = new Chart({container: 'container',});chart.options({type: 'interval',data: [{ genre: 'Sports', sold: 275 },{ genre: 'Strategy', sold: 115 },{ genre: 'Action', sold: 120 },{ genre: 'Shooter', sold: 350 },{ genre: 'Other', sold: 150 },],encode: {x: 'genre',y: 'sold',color: 'genre',shape: 'triangle', // Use this shape},});chart.render();