1cb93a386Sopenharmony_ci 2cb93a386Sopenharmony_ci--- 3cb93a386Sopenharmony_cititle: "CanvasKit - Quickstart" 4cb93a386Sopenharmony_cilinkTitle: "CanvasKit - Quickstart" 5cb93a386Sopenharmony_ci 6cb93a386Sopenharmony_ci--- 7cb93a386Sopenharmony_ci 8cb93a386Sopenharmony_ci 9cb93a386Sopenharmony_ciCanvasKit is a wasm module that uses Skia to draw to canvas elements a more advance feature set than the canvas API. 10cb93a386Sopenharmony_ci 11cb93a386Sopenharmony_ciMinimal application 12cb93a386Sopenharmony_ci------------------- 13cb93a386Sopenharmony_ci 14cb93a386Sopenharmony_ciThis example is a minimal Canvaskit application that draws a rounded rect for one frame. 15cb93a386Sopenharmony_ciIt pulls the wasm binary from unpkg.com but you can also build and host it yourself. 16cb93a386Sopenharmony_ci 17cb93a386Sopenharmony_ci<!--?prettify?--> 18cb93a386Sopenharmony_ci``` js 19cb93a386Sopenharmony_ci<canvas id=foo width=300 height=300></canvas> 20cb93a386Sopenharmony_ci 21cb93a386Sopenharmony_ci<script type="text/javascript" 22cb93a386Sopenharmony_ci src="https://unpkg.com/canvaskit-wasm@0.19.0/bin/canvaskit.js"></script> 23cb93a386Sopenharmony_ci<script type="text/javascript"> 24cb93a386Sopenharmony_ci const ckLoaded = CanvasKitInit({ 25cb93a386Sopenharmony_ci locateFile: (file) => 'https://unpkg.com/canvaskit-wasm@0.19.0/bin/'+file}); 26cb93a386Sopenharmony_ci ckLoaded.then((CanvasKit) => { 27cb93a386Sopenharmony_ci const surface = CanvasKit.MakeCanvasSurface('foo'); 28cb93a386Sopenharmony_ci 29cb93a386Sopenharmony_ci const paint = new CanvasKit.Paint(); 30cb93a386Sopenharmony_ci paint.setColor(CanvasKit.Color4f(0.9, 0, 0, 1.0)); 31cb93a386Sopenharmony_ci paint.setStyle(CanvasKit.PaintStyle.Stroke); 32cb93a386Sopenharmony_ci paint.setAntiAlias(true); 33cb93a386Sopenharmony_ci const rr = CanvasKit.RRectXY(CanvasKit.LTRBRect(10, 60, 210, 260), 25, 15); 34cb93a386Sopenharmony_ci 35cb93a386Sopenharmony_ci function draw(canvas) { 36cb93a386Sopenharmony_ci canvas.clear(CanvasKit.WHITE); 37cb93a386Sopenharmony_ci canvas.drawRRect(rr, paint); 38cb93a386Sopenharmony_ci } 39cb93a386Sopenharmony_ci surface.drawOnce(draw); 40cb93a386Sopenharmony_ci }); 41cb93a386Sopenharmony_ci</script> 42cb93a386Sopenharmony_ci``` 43cb93a386Sopenharmony_ci 44cb93a386Sopenharmony_ci<canvas id=foo width=300 height=300></canvas> 45cb93a386Sopenharmony_ci 46cb93a386Sopenharmony_ci<script type="text/javascript" 47cb93a386Sopenharmony_ci src="https://unpkg.com/canvaskit-wasm@0.19.0/bin/canvaskit.js"></script> 48cb93a386Sopenharmony_ci<script type="text/javascript"> 49cb93a386Sopenharmony_ci const ckLoaded = CanvasKitInit({ 50cb93a386Sopenharmony_ci locateFile: (file) => 'https://unpkg.com/canvaskit-wasm@0.19.0/bin/'+file}); 51cb93a386Sopenharmony_ci ckLoaded.then((CanvasKit) => { 52cb93a386Sopenharmony_ci const surface = CanvasKit.MakeCanvasSurface('foo'); 53cb93a386Sopenharmony_ci 54cb93a386Sopenharmony_ci const paint = new CanvasKit.Paint(); 55cb93a386Sopenharmony_ci paint.setColor(CanvasKit.Color4f(0.9, 0, 0, 1.0)); 56cb93a386Sopenharmony_ci paint.setStyle(CanvasKit.PaintStyle.Stroke); 57cb93a386Sopenharmony_ci paint.setAntiAlias(true); 58cb93a386Sopenharmony_ci const rr = CanvasKit.RRectXY(CanvasKit.LTRBRect(10, 60, 210, 260), 25, 15); 59cb93a386Sopenharmony_ci 60cb93a386Sopenharmony_ci function draw(canvas) { 61cb93a386Sopenharmony_ci canvas.clear(CanvasKit.WHITE); 62cb93a386Sopenharmony_ci canvas.drawRRect(rr, paint); 63cb93a386Sopenharmony_ci } 64cb93a386Sopenharmony_ci surface.drawOnce(draw); 65cb93a386Sopenharmony_ci }); 66cb93a386Sopenharmony_ci</script> 67cb93a386Sopenharmony_ci 68cb93a386Sopenharmony_ciLet's break it down into parts and explain what they are doing: 69cb93a386Sopenharmony_ci 70cb93a386Sopenharmony_ci`<canvas id=foo width=300 height=300></canvas>` Creates the canvas to which CanvasKit will draw. 71cb93a386Sopenharmony_ciThis element is where we control the width and height of the drawing buffer, while it's css style 72cb93a386Sopenharmony_ciwould control any scaling applied after drawing to those pixels. Despite using a canvas element, 73cb93a386Sopenharmony_ciCanvasKit isn't calling the HTML canvas's own draw methods. It is using this canvas element to 74cb93a386Sopenharmony_ciget a WebGL2 context and performing most of the drawing work in C++ code compiled to WebAssembly, 75cb93a386Sopenharmony_cithen sending commands to the GPU at the end of each frame. 76cb93a386Sopenharmony_ci 77cb93a386Sopenharmony_ci<!--?prettify?--> 78cb93a386Sopenharmony_ci``` html 79cb93a386Sopenharmony_ci<script type="text/javascript" 80cb93a386Sopenharmony_ci src="https://unpkg.com/canvaskit-wasm@0.19.0/bin/canvaskit.js"></script> 81cb93a386Sopenharmony_ci``` 82cb93a386Sopenharmony_ciand 83cb93a386Sopenharmony_ci 84cb93a386Sopenharmony_ci<!--?prettify?--> 85cb93a386Sopenharmony_ci``` js 86cb93a386Sopenharmony_ciconst ckLoaded = CanvasKitInit({ 87cb93a386Sopenharmony_ci locateFile: (file) => 'https://unpkg.com/canvaskit-wasm@0.19.0/bin/'+file}); 88cb93a386Sopenharmony_cickLoaded.then((CanvasKit) => { 89cb93a386Sopenharmony_ci``` 90cb93a386Sopenharmony_ciare loading the canvaskit helper js and wasm binary respectively. CanvasKitInit accepts a function 91cb93a386Sopenharmony_cifor allowing you to alter the path where it will try to find `canvaskit.wasm` and returns a promise 92cb93a386Sopenharmony_cithat resolves with the loaded module, which we typically name `CanvasKit`. 93cb93a386Sopenharmony_ci 94cb93a386Sopenharmony_ci<!--?prettify?--> 95cb93a386Sopenharmony_ci``` js 96cb93a386Sopenharmony_ciconst surface = CanvasKit.MakeCanvasSurface('foo'); 97cb93a386Sopenharmony_ci``` 98cb93a386Sopenharmony_ciCreates a Surface associated with the HTML canvas element above. 99cb93a386Sopenharmony_ciHardware acceleration is the default behavior, but can be overridden by calling 100cb93a386Sopenharmony_ci`MakeSWCanvasSurface` instead. `MakeCanvasSurface` is also where alternative color spaces or gl 101cb93a386Sopenharmony_ciattrtributes can be specified. 102cb93a386Sopenharmony_ci 103cb93a386Sopenharmony_ci<!--?prettify?--> 104cb93a386Sopenharmony_ci``` js 105cb93a386Sopenharmony_ciconst paint = new CanvasKit.Paint(); 106cb93a386Sopenharmony_cipaint.setColor(CanvasKit.Color4f(0.9, 0, 0, 1.0)); 107cb93a386Sopenharmony_cipaint.setStyle(CanvasKit.PaintStyle.Stroke); 108cb93a386Sopenharmony_cipaint.setAntiAlias(true); 109cb93a386Sopenharmony_ciconst rr = CanvasKit.RRectXY(CanvasKit.LTRBRect(10, 60, 210, 260), 25, 15); 110cb93a386Sopenharmony_ci``` 111cb93a386Sopenharmony_ciCreates a paint, a description of how to fill or stroke rects, paths, text and other geometry in 112cb93a386Sopenharmony_cicanvaskit. `rr` is a rounded rect, with corners having a radius of 25 in the x axis, and 15 pixels 113cb93a386Sopenharmony_ciin the y axis. 114cb93a386Sopenharmony_ci 115cb93a386Sopenharmony_ci<!--?prettify?--> 116cb93a386Sopenharmony_ci``` js 117cb93a386Sopenharmony_cifunction draw(canvas) { 118cb93a386Sopenharmony_ci canvas.clear(CanvasKit.WHITE); 119cb93a386Sopenharmony_ci canvas.drawRRect(rr, paint); 120cb93a386Sopenharmony_ci} 121cb93a386Sopenharmony_ci``` 122cb93a386Sopenharmony_ciDefines a function that will draw our frame. The function is provided a Canvas object on which we 123cb93a386Sopenharmony_cimake draw calls. One to clear the entire canvas, and one to draw the rounded rect with the 124cb93a386Sopenharmony_cipaint from above. 125cb93a386Sopenharmony_ci 126cb93a386Sopenharmony_ciWe also delete the paint object. CanvasKit objects created with `new` or methods prefixed with 127cb93a386Sopenharmony_ci`make` must be deleted for the wasm memory to be released. Javascript's GC will not take care of 128cb93a386Sopenharmony_ciit automatically. `rr` is just an array, wasn't created with `new` and doesn't point to any WASM 129cb93a386Sopenharmony_cimemory, so we don't have to call delete on it. 130cb93a386Sopenharmony_ci 131cb93a386Sopenharmony_ci<!--?prettify?--> 132cb93a386Sopenharmony_ci``` js 133cb93a386Sopenharmony_cisurface.drawOnce(draw); 134cb93a386Sopenharmony_cipaint.delete() 135cb93a386Sopenharmony_ci``` 136cb93a386Sopenharmony_ciHand the drawing function to `surface.drawOnce` which makes the calls and flushes the surface. 137cb93a386Sopenharmony_ciUpon flushing, Skia will batch and send WebGL commands, making visible changes appear onscreen. 138cb93a386Sopenharmony_ciThis example draws once and disposes of the surface. As promised, it is is a minimal 139cb93a386Sopenharmony_ciapplication. 140cb93a386Sopenharmony_ci 141cb93a386Sopenharmony_ciBasic Draw Loop 142cb93a386Sopenharmony_ci--------------- 143cb93a386Sopenharmony_ci 144cb93a386Sopenharmony_ciWhat if we need to redraw to our canvas every frame? This example 145cb93a386Sopenharmony_cibounces a rounded rect around like a 90s screensaver. 146cb93a386Sopenharmony_ci 147cb93a386Sopenharmony_ci<!--?prettify?--> 148cb93a386Sopenharmony_ci``` js 149cb93a386Sopenharmony_cickLoaded.then((CanvasKit) => { 150cb93a386Sopenharmony_ci const surface = CanvasKit.MakeCanvasSurface('foo2'); 151cb93a386Sopenharmony_ci 152cb93a386Sopenharmony_ci const paint = new CanvasKit.Paint(); 153cb93a386Sopenharmony_ci paint.setColor(CanvasKit.Color4f(0.9, 0, 0, 1.0)); 154cb93a386Sopenharmony_ci paint.setStyle(CanvasKit.PaintStyle.Stroke); 155cb93a386Sopenharmony_ci paint.setAntiAlias(true); 156cb93a386Sopenharmony_ci // const rr = CanvasKit.RRectXY(CanvasKit.LTRBRect(10, 60, 210, 260), 25, 15); 157cb93a386Sopenharmony_ci const w = 100; // size of rect 158cb93a386Sopenharmony_ci const h = 60; 159cb93a386Sopenharmony_ci let x = 10; // initial position of top left corner. 160cb93a386Sopenharmony_ci let y = 60; 161cb93a386Sopenharmony_ci let dirX = 1; // box is always moving at a constant speed in one of the four diagonal directions 162cb93a386Sopenharmony_ci let dirY = 1; 163cb93a386Sopenharmony_ci 164cb93a386Sopenharmony_ci function drawFrame(canvas) { 165cb93a386Sopenharmony_ci // boundary check 166cb93a386Sopenharmony_ci if (x < 0 || x+w > 300) { 167cb93a386Sopenharmony_ci dirX *= -1; // reverse x direction when hitting side walls 168cb93a386Sopenharmony_ci } 169cb93a386Sopenharmony_ci if (y < 0 || y+h > 300) { 170cb93a386Sopenharmony_ci dirY *= -1; // reverse y direction when hitting top and bottom walls 171cb93a386Sopenharmony_ci } 172cb93a386Sopenharmony_ci // move 173cb93a386Sopenharmony_ci x += dirX; 174cb93a386Sopenharmony_ci y += dirY; 175cb93a386Sopenharmony_ci 176cb93a386Sopenharmony_ci canvas.clear(CanvasKit.WHITE); 177cb93a386Sopenharmony_ci const rr = CanvasKit.RRectXY(CanvasKit.LTRBRect(x, y, x+w, y+h), 25, 15); 178cb93a386Sopenharmony_ci canvas.drawRRect(rr, paint); 179cb93a386Sopenharmony_ci surface.requestAnimationFrame(drawFrame); 180cb93a386Sopenharmony_ci } 181cb93a386Sopenharmony_ci surface.requestAnimationFrame(drawFrame); 182cb93a386Sopenharmony_ci}); 183cb93a386Sopenharmony_ci``` 184cb93a386Sopenharmony_ci 185cb93a386Sopenharmony_ci<canvas id=foo2 width=300 height=300></canvas> 186cb93a386Sopenharmony_ci 187cb93a386Sopenharmony_ci<script type="text/javascript"> 188cb93a386Sopenharmony_ci ckLoaded.then((CanvasKit) => { 189cb93a386Sopenharmony_ci const surface = CanvasKit.MakeCanvasSurface('foo2'); 190cb93a386Sopenharmony_ci 191cb93a386Sopenharmony_ci const paint = new CanvasKit.Paint(); 192cb93a386Sopenharmony_ci paint.setColor(CanvasKit.Color4f(0.9, 0, 0, 1.0)); 193cb93a386Sopenharmony_ci paint.setStyle(CanvasKit.PaintStyle.Stroke); 194cb93a386Sopenharmony_ci paint.setAntiAlias(true); 195cb93a386Sopenharmony_ci // const rr = CanvasKit.RRectXY(CanvasKit.LTRBRect(10, 60, 210, 260), 25, 15); 196cb93a386Sopenharmony_ci const w = 100; // size of rect 197cb93a386Sopenharmony_ci const h = 60; 198cb93a386Sopenharmony_ci let x = 10; // initial position of top left corner. 199cb93a386Sopenharmony_ci let y = 60; 200cb93a386Sopenharmony_ci // The box is always moving at a constant speed in one of the four diagonal directions 201cb93a386Sopenharmony_ci let dirX = 1; 202cb93a386Sopenharmony_ci let dirY = 1; 203cb93a386Sopenharmony_ci 204cb93a386Sopenharmony_ci function drawFrame(canvas) { 205cb93a386Sopenharmony_ci // boundary check 206cb93a386Sopenharmony_ci if (x < 0 || x+w > 300) { 207cb93a386Sopenharmony_ci dirX *= -1; // reverse x direction when hitting side walls 208cb93a386Sopenharmony_ci } 209cb93a386Sopenharmony_ci if (y < 0 || y+h > 300) { 210cb93a386Sopenharmony_ci dirY *= -1; // reverse y direction when hitting top and bottom walls 211cb93a386Sopenharmony_ci } 212cb93a386Sopenharmony_ci // move 213cb93a386Sopenharmony_ci x += dirX; 214cb93a386Sopenharmony_ci y += dirY; 215cb93a386Sopenharmony_ci 216cb93a386Sopenharmony_ci canvas.clear(CanvasKit.WHITE); 217cb93a386Sopenharmony_ci const rr = CanvasKit.RRectXY(CanvasKit.LTRBRect(x, y, x+w, y+h), 25, 15); 218cb93a386Sopenharmony_ci canvas.drawRRect(rr, paint); 219cb93a386Sopenharmony_ci surface.requestAnimationFrame(drawFrame); 220cb93a386Sopenharmony_ci } 221cb93a386Sopenharmony_ci surface.requestAnimationFrame(drawFrame); 222cb93a386Sopenharmony_ci }); 223cb93a386Sopenharmony_ci</script> 224cb93a386Sopenharmony_ci 225cb93a386Sopenharmony_ciThe main difference here is that we define a function to be called before each frame is drawn and 226cb93a386Sopenharmony_cipass it to `surface.requestAnimationFrame(drawFrame);` That callback is handed a `canvas` and 227cb93a386Sopenharmony_ciflushing is taken care of. 228cb93a386Sopenharmony_ci 229cb93a386Sopenharmony_ci<!--?prettify?--> 230cb93a386Sopenharmony_ci``` js 231cb93a386Sopenharmony_cifunction drawFrame(canvas) { 232cb93a386Sopenharmony_ci canvas.clear(CanvasKit.WHITE); 233cb93a386Sopenharmony_ci // code to update and draw the frame goes here 234cb93a386Sopenharmony_ci surface.requestAnimationFrame(drawFrame); 235cb93a386Sopenharmony_ci} 236cb93a386Sopenharmony_cisurface.requestAnimationFrame(drawFrame); 237cb93a386Sopenharmony_ci``` 238cb93a386Sopenharmony_ci 239cb93a386Sopenharmony_ciCreates a function to serve as our main drawing loop. Each time a frame is about to be rendered 240cb93a386Sopenharmony_ci(the browser will typically target 60fps), our function is called, we clear the canvas with white, 241cb93a386Sopenharmony_ciredraw the round rect, and call `surface.requestAnimationFrame(drawFrame)` registering the 242cb93a386Sopenharmony_cifunction to be called again before the next frame. 243cb93a386Sopenharmony_ci 244cb93a386Sopenharmony_ci`surface.requestAnimationFrame(drawFrame)` combines window.requestAnimationFrame with 245cb93a386Sopenharmony_ci`surface.flush()` and should be used in all the same ways. If your application would only make 246cb93a386Sopenharmony_civisible changes as a result of mouse events, 247cb93a386Sopenharmony_cidon't call `surface.requestAnimationFrame` at the end of your drawFrame function. Call it only 248cb93a386Sopenharmony_ciafter handling mouse input. 249cb93a386Sopenharmony_ci 250cb93a386Sopenharmony_ciText Shaping 251cb93a386Sopenharmony_ci------------ 252cb93a386Sopenharmony_ci 253cb93a386Sopenharmony_ciOne of the biggest features that CanvasKit offers over the HTML Canvas API is paragraph shaping. 254cb93a386Sopenharmony_ciTo use text your applicatoin, supply a font file and use Promise.all to run your code when both 255cb93a386Sopenharmony_ciCanvasKit and the font file are ready. 256cb93a386Sopenharmony_ci 257cb93a386Sopenharmony_ci<!--?prettify?--> 258cb93a386Sopenharmony_ci``` js 259cb93a386Sopenharmony_ciconst loadFont = fetch('https://storage.googleapis.com/skia-cdn/misc/Roboto-Regular.ttf') 260cb93a386Sopenharmony_ci .then((response) => response.arrayBuffer()); 261cb93a386Sopenharmony_ci 262cb93a386Sopenharmony_ciPromise.all([ckLoaded, loadFont]).then(([CanvasKit, robotoData]) => { 263cb93a386Sopenharmony_ci const surface = CanvasKit.MakeCanvasSurface('foo3'); 264cb93a386Sopenharmony_ci const canvas = surface.getCanvas(); 265cb93a386Sopenharmony_ci canvas.clear(CanvasKit.Color4f(0.9, 0.9, 0.9, 1.0)); 266cb93a386Sopenharmony_ci 267cb93a386Sopenharmony_ci const fontMgr = CanvasKit.FontMgr.FromData([robotoData]); 268cb93a386Sopenharmony_ci const paraStyle = new CanvasKit.ParagraphStyle({ 269cb93a386Sopenharmony_ci textStyle: { 270cb93a386Sopenharmony_ci color: CanvasKit.BLACK, 271cb93a386Sopenharmony_ci fontFamilies: ['Roboto'], 272cb93a386Sopenharmony_ci fontSize: 28, 273cb93a386Sopenharmony_ci }, 274cb93a386Sopenharmony_ci textAlign: CanvasKit.TextAlign.Left, 275cb93a386Sopenharmony_ci }); 276cb93a386Sopenharmony_ci const text = 'Any sufficiently entrenched technology is indistinguishable from Javascript'; 277cb93a386Sopenharmony_ci const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr); 278cb93a386Sopenharmony_ci builder.addText(text); 279cb93a386Sopenharmony_ci const paragraph = builder.build(); 280cb93a386Sopenharmony_ci paragraph.layout(290); // width in pixels to use when wrapping text 281cb93a386Sopenharmony_ci canvas.drawParagraph(paragraph, 10, 10); 282cb93a386Sopenharmony_ci surface.flush(); 283cb93a386Sopenharmony_ci}); 284cb93a386Sopenharmony_ci``` 285cb93a386Sopenharmony_ci 286cb93a386Sopenharmony_ci<canvas id=foo3 width=300 height=300></canvas> 287cb93a386Sopenharmony_ci 288cb93a386Sopenharmony_ci<script type="text/javascript"> 289cb93a386Sopenharmony_ciconst loadFont = fetch('https://storage.googleapis.com/skia-cdn/misc/Roboto-Regular.ttf') 290cb93a386Sopenharmony_ci .then((response) => response.arrayBuffer()); 291cb93a386Sopenharmony_ci 292cb93a386Sopenharmony_ciPromise.all([ckLoaded, loadFont]).then(([CanvasKit, robotoData]) => { 293cb93a386Sopenharmony_ci const surface = CanvasKit.MakeCanvasSurface('foo3'); 294cb93a386Sopenharmony_ci const canvas = surface.getCanvas(); 295cb93a386Sopenharmony_ci canvas.clear(CanvasKit.Color4f(0.9, 0.9, 0.9, 1.0)); 296cb93a386Sopenharmony_ci 297cb93a386Sopenharmony_ci const fontMgr = CanvasKit.FontMgr.FromData([robotoData]); 298cb93a386Sopenharmony_ci const paraStyle = new CanvasKit.ParagraphStyle({ 299cb93a386Sopenharmony_ci textStyle: { 300cb93a386Sopenharmony_ci color: CanvasKit.BLACK, 301cb93a386Sopenharmony_ci fontFamilies: ['Roboto'], 302cb93a386Sopenharmony_ci fontSize: 28, 303cb93a386Sopenharmony_ci }, 304cb93a386Sopenharmony_ci textAlign: CanvasKit.TextAlign.Left, 305cb93a386Sopenharmony_ci }); 306cb93a386Sopenharmony_ci const text = 'Any sufficiently entrenched technology is indistinguishable from Javascript'; 307cb93a386Sopenharmony_ci const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr); 308cb93a386Sopenharmony_ci builder.addText(text); 309cb93a386Sopenharmony_ci const paragraph = builder.build(); 310cb93a386Sopenharmony_ci paragraph.layout(290); // width in pixels to use when wrapping text 311cb93a386Sopenharmony_ci canvas.drawParagraph(paragraph, 10, 10); 312cb93a386Sopenharmony_ci surface.flush(); 313cb93a386Sopenharmony_ci}); 314cb93a386Sopenharmony_ci</script> 315cb93a386Sopenharmony_ci 316cb93a386Sopenharmony_ci<!--?prettify?--> 317cb93a386Sopenharmony_ci``` js 318cb93a386Sopenharmony_ciconst fontMgr = CanvasKit.FontMgr.FromData([robotoData]); 319cb93a386Sopenharmony_ci``` 320cb93a386Sopenharmony_ciCreates an object that provides fonts by name to various text facilities in CanvasKit. You could 321cb93a386Sopenharmony_ciload more than one font in this statement if needed. 322cb93a386Sopenharmony_ci 323cb93a386Sopenharmony_ci<!--?prettify?--> 324cb93a386Sopenharmony_ci``` js 325cb93a386Sopenharmony_ciconst paraStyle = new CanvasKit.ParagraphStyle({ 326cb93a386Sopenharmony_ci textStyle: { 327cb93a386Sopenharmony_ci color: CanvasKit.BLACK, 328cb93a386Sopenharmony_ci fontFamilies: ['Roboto'], 329cb93a386Sopenharmony_ci fontSize: 28, 330cb93a386Sopenharmony_ci }, 331cb93a386Sopenharmony_ci textAlign: CanvasKit.TextAlign.Left, 332cb93a386Sopenharmony_ci}); 333cb93a386Sopenharmony_ci``` 334cb93a386Sopenharmony_ciSpecifies the style of the text. The font's name, Roboto, will be used to fetch it from the font 335cb93a386Sopenharmony_cimanager. You can specify either (color) or (foregroundColor and backgroundColor) in order to have 336cb93a386Sopenharmony_cia highlight. For the full documentation of the API, check out the Typescript definitions in the 337cb93a386Sopenharmony_ci`types/` subfolder of the npm package or in the 338cb93a386Sopenharmony_ci[Skia repo](https://github.com/google/skia/tree/main/modules/canvaskit/npm_build/types). 339cb93a386Sopenharmony_ci 340cb93a386Sopenharmony_ci<!--?prettify?--> 341cb93a386Sopenharmony_ci``` js 342cb93a386Sopenharmony_ciconst builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr); 343cb93a386Sopenharmony_cibuilder.addText(text); 344cb93a386Sopenharmony_ciconst paragraph = builder.build(); 345cb93a386Sopenharmony_ci``` 346cb93a386Sopenharmony_ciNext, we create a `ParagraphBuilder` with a style, add some text, and finalize it with `build()`. 347cb93a386Sopenharmony_ciAlternatively, we could use multiple `TextStyle`s in one paragraph with 348cb93a386Sopenharmony_ci 349cb93a386Sopenharmony_ci<!--?prettify?--> 350cb93a386Sopenharmony_ci``` js 351cb93a386Sopenharmony_ciconst builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr); 352cb93a386Sopenharmony_cibuilder.addText(text1); 353cb93a386Sopenharmony_ciconst boldTextStyle = CanvasKit.TextStyle({ 354cb93a386Sopenharmony_ci color: CanvasKit.BLACK, 355cb93a386Sopenharmony_ci fontFamilies: ['Roboto'], 356cb93a386Sopenharmony_ci fontSize: 28, 357cb93a386Sopenharmony_ci fontStyle: {'weight': CanvasKit.FontWeight.Bold}, 358cb93a386Sopenharmony_ci}) 359cb93a386Sopenharmony_cibuilder.pushStyle(boldTextStyle); 360cb93a386Sopenharmony_cibuilder.addText(text2); 361cb93a386Sopenharmony_cibuilder.pop(); 362cb93a386Sopenharmony_cibuilder.addText(text3); 363cb93a386Sopenharmony_ciconst paragraph = builder.build(); 364cb93a386Sopenharmony_ci``` 365cb93a386Sopenharmony_ciFinally, we *layout* the paragraph, meaning wrap the text to a particular width, and draw it to 366cb93a386Sopenharmony_cithe canvas with 367cb93a386Sopenharmony_ci 368cb93a386Sopenharmony_ci<!--?prettify?--> 369cb93a386Sopenharmony_ci``` js 370cb93a386Sopenharmony_ciparagraph.layout(290); // width in pixels to use when wrapping text 371cb93a386Sopenharmony_cicanvas.drawParagraph(paragraph, 10, 10); // (x, y) position of left top corner of paragraph. 372cb93a386Sopenharmony_ci``` 373cb93a386Sopenharmony_ci 374