Loading...
Use Skia to draw 2D graphics. Load Canvaskit in WASM format asynchronously at runtime, and wrap [WebGL2RenderingContext](https://developer .mozilla.org/en-US/Web/API/WebGL2RenderingContext) into SkSurface
, which in turn is drawn by the <canvas>
element on the page.
Skia offers more features than the Canvas2D API, such as text paragraph layout, Lottie animation, particle effects, and more. In addition to Chrome and Android, some cross-platform solutions such as Flutter, Weex weex) also use it as the underlying rendering engine.
As with @antv/g
, there are two ways to use it.
After installing @antv/g-canvaskit
you can get the renderer from.
import { Canvas } from '@antv/g';import { Renderer } from '@antv/g-canvaskit';const canvaskitRenderer = new Renderer();const canvas = new Canvas({container: 'container',width: 600,height: 500,renderer: canvaskitRenderer,});
<scriptsrc="https://unpkg.com/@antv/g-canvaskit/dist/index.umd.min.js"type="application/javascript">
The renderer is available from the G.Canvaskit
namespace under.
const canvasRenderer = new window.G.Canvaskit.Renderer();
The path to the WASM folder for CanvasKit. The default value is 'https://unpkg.com/canvaskit-wasm@0.34.1/bin/full/'
, which means that it is downloaded from a CDN.
In practice, we can copy the WASM to the server resource directory (e.g. with a build tool like webpack) instead of loading it from the CDN. In our case, the file is copied to the root directory (''/''), and the folder path can be specified via the wasmDir
configuration item.
const canvaskitRenderer = new CanvaskitRenderer({wasmDir: '/',});
It is worth noting that CanvasKit provides several versions of the WASM file.
'https://unpkg.com/canvaskit-wasm@0.34.1/bin/'
'https://unpkg.com/canvaskit-wasm@0.34.1/bin/full'
'https://unpkg.com/canvaskit-wasm@0.34.1/bin/profiling'
CanvasKit provides multi-line layout, decoration, omission, etc. in text and especially paragraphs compared to the familiar Canvas 2D API. The only problem is that the font file needs to be loaded at runtime.
For CJK (Chinese, Japanese, and Korean) fonts, if you use fonts that do not support them, the following effect will occur when rendering, as shown below from an ISSUE in Flutter 76248).
Therefore, Android uses NotoSansCJK font by default.
<family lang="zh-Hans"><font weight="400" style="normal" index="2">NotoSansCJK-Regular.ttc</font></family><family lang="zh-Hant zh-Bopo"><font weight="400" style="normal" index="3">NotoSansCJK-Regular.ttc</font></family><family lang=" ja ja-Latn"><font weight="400" style="normal" index="0">NotoSansCJK-Regular.ttc</font></family><family lang="ko ko-Latn "><font weight="400" style="normal" index="1">NotoSansCJK-Regular.ttc</font></family>
However, the complete NotoSansCJK is so large that in our actual development, if we only need Simplified Chinese, we can load only a subset of it (about 36MB):.
const canvaskitRenderer = new CanvaskitRenderer({wasmDir: '/',fonts: [{name: 'sans-serif',url: '/NotoSansCJKsc-VF.ttf',},],});
The renderer has the following plug-ins built in.
CanvasKit (full version) provides the following enhancements compared to the familiar Canvas 2D API.
The Lottie animation is created with the Bodymovin plugin for After Effects and exported to JSON format. JSON format. CanvasKit provides Skottie, a Lottie animation player.
In this example we show how to play a Lego animation.
First create the renderer and get the g-plugin-canvaskit-renderer plugin via getPlugin.
import { Renderer } from '@antv/g-canvaskit';const canvaskitRenderer = new Renderer({wasmDir: '/',});const plugin = canvaskitRenderer.getPlugin('canvaskit-renderer');
Then wait for the canvas initialization to complete, load the Lottie animation description file, and call playAnimation to start playing immediately when it's done.
(async () => {const cdn = 'https://storage.googleapis.com/skia-cdn/misc/';const [_, jsonstr] = await Promise.all([// wait for initialization of Canvascanvas.ready,// load Lottie description filefetch(cdn + 'lego_loader.json').then((response) => response.text()),]);const animation = plugin.playAnimation('sk_legos',jsonstr,[-50, 0, 350, 300],);})();
If you want to remove the animation, you can call.
animation.delete();
For example, particle effects such as fireworks, flames, etc. require generating and animating a large number of "particles", which are usually programmed in the GPU through the shader, e.g. interpolation calculations to change the position of each particle should be done in the GPU instead of the CPU.
CanvasKit provides a Skia-based programming language SkSL(Skia's shading language) implementation, which is syntactically very close to GLSL and is used in the shader to control particle generation and animation. and animation in the shader, which is a certain threshold for developers who have not been exposed to shader programming.
In this example, we have implemented some particle effects.
First create the renderer and get the g-plugin-canvaskit-renderer plugin via getPlugin.
import { Renderer } from '@antv/g-canvaskit';const canvaskitRenderer = new Renderer({wasmDir: '/',});const plugin = canvaskitRenderer.getPlugin('canvaskit-renderer');
Then call the plugin's createParticles to create the particle effect, transform the canvas to adjust the position of the particles in the callback function at each frame, and finally start the particle generation with .
const textParticles = plugin.createParticles(JSON.stringify(text), (canvas) => {canvas.translate(250, 250);});textParticles.start(Date.now() / 1000.0, true);
Finally, let's look at the key particle effect definitions.
MaxCount
Number of particlesDrawable
The type of particle, usually 'SkCircleDrawable'
, can be modified in sizeCode
SkSL code to control the life cycle of the particles, such as how the position and color should change in each frameBindings
const text = {MaxCount: 2000,Drawable: {Type: 'SkCircleDrawable',Radius: 1,},Code: ['void effectSpawn(inout Effect effect) {',' effect.rate = 1000;','}','','void spawn(inout Particle p) {',' p.lifetime = mix(1, 3, rand(p.seed));',' float a = radians(mix(250, 290, rand(p.seed)));',' float s = mix(10, 30, rand(p.seed));',' p.vel.x = cos(a) * s;',' p.vel.y = sin(a) * s;',' p.pos += text(rand(p.seed)).xy;','}','','void update(inout Particle p) {',' float4 startColor = float4(1, 0.196, 0.078, 1);',' float4 endColor = float4(1, 0.784, 0.078, 1);',' p.color = mix(startColor, endColor, p.age);','}','',],Bindings: [{Type: 'SkTextBinding',Name: 'text',Text: 'AntV',FontSize: 96,},],};
Compared to fillText in the Canvas2D API, CanvasKit provides the ability to draw along a specified path text along a specified path.
In this example, we can draw text along Path.
We can use the attribute to.
const alongPath = new Path({style: {d: 'M 0,40 C 5.5555555555555545...',},});const text = new Text({style: {fontFamily: 'sans-serif',fontSize: 22,fill: '#1890FF',text: 'abcdefghijklmn这是测试文字',alongPath,},});
Emoji cannot be supported by normal fonts.
const emoji = new Text({style: {fontFamily: 'sans-serif',fontSize: 30,fill: 'black',text: 'Emoji 🍕🍔🍟🥝🍱🕶🎩👩👩👦👩👩👧👧👩👩👦👩👩👧👧👩👩👦👩👩👧👧👩👩👦👩👩👧👧👩👩👦👩👩👧👧👩👩👦👩👩👧👧👩👩👦👩👩👧👧',},});
For example, NotoSansCJKsc-VF
will show the following effect.
In this example, we load fonts that support Emoji such as NotoColorEmoji, which is also used in Android and Chrome use.
const canvaskitRenderer = new CanvaskitRenderer({wasmDir: '/',fonts: [{name: 'Roboto',url: '/NotoSansCJKsc-VF.ttf',},{name: 'Noto Color Emoji',url: '/NotoColorEmoji.ttf',},],});
At this point it can be displayed normally, specifying two fonts in fontFamily
.
const emoji = new Text({style: {fontFamily: 'Roboto, Noto Color Emoji',},});
CanvasKit provides enhanced paragraph drawing capabilities.
The text-decoration property can be used in CSS to set the appearance of the text's modifier lines.
In this example, we use underscores.
const decoratedText = new Text({style: {fontFamily: 'sans-serif',fontSize: 22,fill: '#1890FF',text: 'abcdefghijklmnopqrstuvwxyz这是测试文本',wordWrap: true,wordWrapWidth: 100,decorationLine: 'underline',decorationColor: 'red',decorationStyle: 'wavy',decorationThickness: 1.5,},});
The following attributes are supported.
'none'
'underline'
'overline'
'line-through'
'solid'
'double'
'dotted'
'dashed'
'wavy'
In this example, using maxLines
and ellipsis
allows you to truncate and add ellipses after exceeding.
const text = new Text({style: {fontFamily: 'Roboto',fontSize: 22,fill: '#1890FF',text: 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz',wordWrap: true,wordWrapWidth: 100,maxLines: 3,ellipsis: '...',},});
Note that using certain fonts (e.g. Noto) can have the following strange effect.
The reason is that Skia will add a blank character after the ellipsis, and the missing character in some font files will show "tofu", the solution is as follows.
Using direction
you can specify the text direction from left to right or right to left, supporting 'ltr'
and 'rtl'
, the default is 'ltr'
. The following figure shows the effect of 'rtl'
.
The foreground and background colors of text can be specified using foregroundColor
and backgroundColor
.
Multiple shadows can be added to text in CSS using the text-shadow property.
We support specifying a set of shadows via the shadows
property, where each shadow supports the following configuration.
color
blurRadius
The default is 0. The larger the value, the larger the blur radius and the lighter the shadows.offset
Specify the offset of the shadow relative to the text.In this example, we specify two shadows.
const shadowedText = new Text({style: {shadows: [{color: 'black',blurRadius: 15,},{color: 'red',blurRadius: 5,offset: [10, 10],},],},});
Strut (meaning "pillar") sets the minimum line height relative to the baseline. Similar to the line-height property in CSS.
StrutStyle can be configured in SkParagraph, and a document with the same name is available in Flutter: https://api.flutter.dev/flutter/painting/StrutStyle-class.html
We will pass on the following attributes.
In this example we use this to control line height and line spacing.
decoratedText.style.strutStyle = {strutEnabled: false,fontFamilies: ['sans-serif'],fontSize: 22,heightMultiplier: 1,leading: 0,halfLeading: false,forceStrutHeight: false,};
The font-feature-settings property in CSS can be consulted to control the advanced printing features in OpenType fonts.
We provide control of the fontFeatures
property, which accepts an array of features. In this example, we use the Roboto font and turn on the small-cap feature (note the initial D).
const fontFeaturesText = new Text({style: {fontFamily: 'Roboto',fontSize: 22,fill: '#1890FF',text: 'Difficult waffles 0O 3.14',fontFeatures: [{name: 'smcp',value: 1,},{name: 'zero',value: 1,},],},});
Skia itself does not include Harfbuzz.
But CanvasKit packages it in by default.
https://skia.googlesource.com/skia/+/main/modules/canvaskit/CHANGELOG.md#0_4_0_2019_02_25
https://skia.googlesource.com/skia.git/+/4bd08c52c07d1f2ae313a54b45e5937b80fe2fa1
Text shaping with ShapedText object and SkCanvas.drawText. At compile time, one can choose between using Harfbuzz/ICU (default) or a primitive one (“primitive_shaper”) which just does line breaking. Using Harfbuzz/ICU substantially increases code size (4.3 MB to 6.4 MB).
CanvasKit draws via WebGL2RenderingContext and does a full redraw at each frame.