| <!DOCTYPE html> |
| <title>CanvasKit (Skia via Web Assembly)</title> |
| <meta charset="utf-8" /> |
| <meta http-equiv="X-UA-Compatible" content="IE=edge"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| |
| <style> |
| svg, canvas, img { |
| border: 1px dashed #AAA; |
| } |
| |
| #patheffect,#paths,#sk_drinks,#sk_party, #sk_legos, #sk_onboarding, |
| img, canvas { |
| width: 300px; |
| height: 300px; |
| } |
| |
| </style> |
| |
| <h2>Drop in replacement for HTML Canvas (e.g. node.js)</h2> |
| <img id=api1 width=300 height=300> |
| <canvas id=api1_c width=300 height=300></canvas> |
| <img id=api2 width=300 height=300> |
| <canvas id=api2_c width=300 height=300></canvas> |
| <img id=api3 width=300 height=300> |
| <canvas id=api3_c width=300 height=300></canvas> |
| <img id=api4 width=300 height=300> |
| <canvas id=api4_c width=300 height=300></canvas> |
| <img id=api5 width=300 height=300> |
| <canvas id=api5_c width=300 height=300></canvas> |
| <img id=api6 width=300 height=300> |
| <canvas id=api6_c width=300 height=300></canvas> |
| |
| <h2> CanvasKit draws Paths to the browser</h2> |
| <canvas id=vertex1 width=300 height=300></canvas> |
| <canvas id=vertex2 width=300 height=300></canvas> |
| <canvas id=gradient1 width=300 height=300></canvas> |
| <canvas id=patheffect width=300 height=300></canvas> |
| <canvas id=paths width=200 height=200></canvas> |
| <canvas id=ink width=300 height=300></canvas> |
| |
| <h2> Skottie </h2> |
| <canvas id=sk_legos width=300 height=300></canvas> |
| <canvas id=sk_drinks width=500 height=500></canvas> |
| <canvas id=sk_party width=500 height=500></canvas> |
| <canvas id=sk_onboarding width=500 height=500></canvas> |
| |
| <script type="text/javascript" src="/node_modules/canvaskit/bin/canvaskit.js"></script> |
| |
| <script type="text/javascript" charset="utf-8"> |
| |
| var CanvasKit = null; |
| var legoJSON = null; |
| var drinksJSON = null; |
| var confettiJSON = null; |
| var onboardingJSON = null; |
| var fullBounds = {fLeft: 0, fTop: 0, fRight: 500, fBottom: 500}; |
| |
| var bonesImageData = null; |
| CanvasKitInit({ |
| locateFile: (file) => '/node_modules/canvaskit/bin/'+file, |
| }).then((CK) => { |
| // when debugging, it can be handy to not run directly in the then, because if there |
| // is a failure (for example, miscalling an API), the WASM loader tries to re-load |
| // the web assembly in the (much slower) ArrayBuffer version. This will also fail |
| // and thus there is a lot of extra log spew. |
| // Thus, the setTimeout to run on the next microtask avoids this second loading |
| // and the log spew. |
| setTimeout(() => { |
| CK.initFonts(); |
| CanvasKit = CK; |
| DrawingExample(CanvasKit); |
| PathExample(CanvasKit); |
| InkExample(CanvasKit); |
| // Set bounds to fix the 4:3 resolution of the legos |
| SkottieExample(CanvasKit, 'sk_legos', legoJSON, |
| {fLeft: -50, fTop: 0, fRight: 350, fBottom: 300}); |
| // Re-size to fit |
| SkottieExample(CanvasKit, 'sk_drinks', drinksJSON, fullBounds); |
| SkottieExample(CanvasKit, 'sk_party', confettiJSON, fullBounds); |
| SkottieExample(CanvasKit, 'sk_onboarding', onboardingJSON, fullBounds); |
| |
| CanvasAPI1(CanvasKit); |
| CanvasAPI2(CanvasKit); |
| CanvasAPI3(CanvasKit); |
| CanvasAPI4(CanvasKit); |
| CanvasAPI5(CanvasKit); |
| CanvasAPI6(CanvasKit); |
| |
| VertexAPI1(CanvasKit); |
| VertexAPI2(CanvasKit, bonesImageData); |
| |
| GradiantAPI1(CanvasKit); |
| }, 0); |
| }); |
| |
| fetch('https://storage.googleapis.com/skia-cdn/misc/lego_loader.json').then((resp) => { |
| resp.text().then((str) => { |
| legoJSON = str; |
| SkottieExample(CanvasKit, 'sk_legos', legoJSON, |
| {fLeft: -50, fTop: 0, fRight: 350, fBottom: 300}); |
| }); |
| }); |
| |
| fetch('https://storage.googleapis.com/skia-cdn/misc/drinks.json').then((resp) => { |
| resp.text().then((str) => { |
| drinksJSON = str; |
| SkottieExample(CanvasKit, 'sk_drinks', drinksJSON, fullBounds); |
| }); |
| }); |
| |
| fetch('https://storage.googleapis.com/skia-cdn/misc/confetti.json').then((resp) => { |
| resp.text().then((str) => { |
| confettiJSON = str; |
| SkottieExample(CanvasKit, 'sk_party', confettiJSON, fullBounds); |
| }); |
| }); |
| |
| fetch('https://storage.googleapis.com/skia-cdn/misc/onboarding.json').then((resp) => { |
| resp.text().then((str) => { |
| onboardingJSON = str; |
| SkottieExample(CanvasKit, 'sk_onboarding', onboardingJSON, fullBounds); |
| }); |
| }); |
| |
| fetch('https://storage.googleapis.com/skia-cdn/misc/bones.jpg').then((resp) => { |
| resp.arrayBuffer().then((buffer) => { |
| bonesImageData = buffer; |
| VertexAPI2(CanvasKit, bonesImageData); |
| }); |
| }); |
| |
| function DrawingExample(CanvasKit) { |
| const surface = CanvasKit.MakeCanvasSurface('patheffect'); |
| if (!surface) { |
| console.error('Could not make surface'); |
| return; |
| } |
| const context = CanvasKit.currentContext(); // only needed for WebGL; no-op for CPU |
| |
| const canvas = surface.getCanvas(); |
| |
| const paint = new CanvasKit.SkPaint(); |
| |
| const textPaint = new CanvasKit.SkPaint(); |
| textPaint.setColor(CanvasKit.RED); |
| textPaint.setTextSize(30); |
| textPaint.setAntiAlias(true); |
| |
| let i = 0; |
| |
| let X = 128; |
| let Y = 128; |
| |
| function drawFrame() { |
| const path = starPath(CanvasKit, X, Y); |
| CanvasKit.setCurrentContext(context); |
| const dpe = CanvasKit.MakeSkDashPathEffect([15, 5, 5, 10], i/5); |
| i++; |
| |
| paint.setPathEffect(dpe); |
| paint.setStyle(CanvasKit.PaintStyle.Stroke); |
| paint.setStrokeWidth(5.0 + -3 * Math.cos(i/30)); |
| paint.setAntiAlias(true); |
| paint.setColor(CanvasKit.Color(66, 129, 164, 1.0)); |
| |
| canvas.clear(CanvasKit.TRANSPARENT); |
| |
| canvas.drawPath(path, paint); |
| canvas.drawText('Try Clicking!', 10, 280, textPaint); |
| |
| surface.flush(); |
| |
| dpe.delete(); |
| path.delete(); |
| window.requestAnimationFrame(drawFrame); |
| } |
| window.requestAnimationFrame(drawFrame); |
| |
| // Make animation interactive |
| let interact = (e) => { |
| if (!e.pressure) { |
| return; |
| } |
| X = e.offsetX; |
| Y = e.offsetY; |
| }; |
| document.getElementById('patheffect').addEventListener('pointermove', interact); |
| document.getElementById('patheffect').addEventListener('pointerdown', interact); |
| preventScrolling(document.getElementById('patheffect')); |
| // A client would need to delete this if it didn't go on for ever. |
| //paint.delete(); |
| } |
| |
| function PathExample(CanvasKit) { |
| const surface = CanvasKit.MakeCanvasSurface('paths'); |
| if (!surface) { |
| console.error('Could not make surface'); |
| return; |
| } |
| const context = CanvasKit.currentContext(); |
| |
| const canvas = surface.getCanvas(); |
| |
| function drawFrame() { |
| CanvasKit.setCurrentContext(context); |
| const paint = new CanvasKit.SkPaint(); |
| paint.setStrokeWidth(1.0); |
| paint.setAntiAlias(true); |
| paint.setColor(CanvasKit.Color(0, 0, 0, 1.0)); |
| paint.setStyle(CanvasKit.PaintStyle.Stroke); |
| |
| const path = new CanvasKit.SkPath(); |
| path.moveTo(20, 5); |
| path.lineTo(30, 20); |
| path.lineTo(40, 10); |
| path.lineTo(50, 20); |
| path.lineTo(60, 0); |
| path.lineTo(20, 5); |
| |
| path.moveTo(20, 80); |
| path.cubicTo(90, 10, 160, 150, 190, 10); |
| |
| path.moveTo(36, 148); |
| path.quadTo(66, 188, 120, 136); |
| path.lineTo(36, 148); |
| |
| path.moveTo(150, 180); |
| path.arcTo(150, 100, 50, 200, 20); |
| path.lineTo(160, 160); |
| |
| path.moveTo(20, 120); |
| path.lineTo(20, 120); |
| |
| canvas.drawPath(path, paint); |
| |
| surface.flush(); |
| |
| path.delete(); |
| paint.delete(); |
| // Intentionally just draw frame once |
| } |
| window.requestAnimationFrame(drawFrame); |
| } |
| |
| function preventScrolling(canvas) { |
| canvas.addEventListener('touchmove', (e) => { |
| // Prevents touch events in the canvas from scrolling the canvas. |
| e.preventDefault(); |
| e.stopPropagation(); |
| }); |
| } |
| |
| function InkExample(CanvasKit) { |
| const surface = CanvasKit.MakeCanvasSurface('ink'); |
| if (!surface) { |
| console.error('Could not make surface'); |
| return; |
| } |
| const context = CanvasKit.currentContext(); |
| |
| const canvas = surface.getCanvas(); |
| |
| let paint = new CanvasKit.SkPaint(); |
| paint.setAntiAlias(true); |
| paint.setColor(CanvasKit.Color(0, 0, 0, 1.0)); |
| paint.setStyle(CanvasKit.PaintStyle.Stroke); |
| paint.setStrokeWidth(4.0); |
| paint.setPathEffect(CanvasKit.MakeSkCornerPathEffect(50)); |
| |
| // Draw I N K |
| let path = new CanvasKit.SkPath(); |
| path.moveTo(80, 30); |
| path.lineTo(80, 80); |
| |
| path.moveTo(100, 80); |
| path.lineTo(100, 15); |
| path.lineTo(130, 95); |
| path.lineTo(130, 30); |
| |
| path.moveTo(150, 30); |
| path.lineTo(150, 80); |
| path.moveTo(170, 30); |
| path.lineTo(150, 55); |
| path.lineTo(170, 80); |
| |
| let paths = [path]; |
| let paints = [paint]; |
| |
| function drawFrame() { |
| CanvasKit.setCurrentContext(context); |
| canvas.clear(CanvasKit.Color(255, 255, 255, 1.0)); |
| |
| for (let i = 0; i < paints.length && i < paths.length; i++) { |
| canvas.drawPath(paths[i], paints[i]); |
| } |
| surface.flush(); |
| |
| window.requestAnimationFrame(drawFrame); |
| } |
| |
| let hold = false; |
| let interact = (e) => { |
| let type = e.type; |
| if (type === 'lostpointercapture' || type === 'pointerup' || !e.pressure ) { |
| hold = false; |
| return; |
| } |
| if (hold) { |
| path.lineTo(e.offsetX, e.offsetY); |
| } else { |
| paint = paint.copy(); |
| paint.setColor(CanvasKit.Color(Math.random() * 255, Math.random() * 255, Math.random() * 255, Math.random() + .2)); |
| paints.push(paint); |
| path = new CanvasKit.SkPath(); |
| paths.push(path); |
| path.moveTo(e.offsetX, e.offsetY); |
| } |
| hold = true; |
| }; |
| document.getElementById('ink').addEventListener('pointermove', interact); |
| document.getElementById('ink').addEventListener('pointerdown', interact); |
| document.getElementById('ink').addEventListener('lostpointercapture', interact); |
| document.getElementById('ink').addEventListener('pointerup', interact); |
| preventScrolling(document.getElementById('ink')); |
| window.requestAnimationFrame(drawFrame); |
| } |
| |
| function starPath(CanvasKit, X=128, Y=128, R=116) { |
| let p = new CanvasKit.SkPath(); |
| p.moveTo(X + R, Y); |
| for (let i = 1; i < 8; i++) { |
| let a = 2.6927937 * i; |
| p.lineTo(X + R * Math.cos(a), Y + R * Math.sin(a)); |
| } |
| return p; |
| } |
| |
| function fps(frameTimes) { |
| let total = 0; |
| for (let ft of frameTimes) { |
| total += ft; |
| } |
| return frameTimes.length / total; |
| } |
| |
| function SkottieExample(CanvasKit, id, jsonStr, bounds) { |
| if (!CanvasKit || !jsonStr) { |
| return; |
| } |
| const animation = CanvasKit.MakeManagedAnimation(jsonStr); |
| const duration = animation.duration() * 1000; |
| const size = animation.size(); |
| let c = document.getElementById(id); |
| bounds = bounds || {fLeft: 0, fTop: 0, fRight: size.w, fBottom: size.h}; |
| |
| // Basic managed animation test. |
| if (id === 'sk_drinks') { |
| animation.setColor('BACKGROUND_FILL', CanvasKit.Color(0, 163, 199, 1.0)); |
| } |
| |
| const surface = CanvasKit.MakeCanvasSurface(id); |
| if (!surface) { |
| console.error('Could not make surface'); |
| return; |
| } |
| const context = CanvasKit.currentContext(); |
| const canvas = surface.getCanvas(); |
| |
| let firstFrame = Date.now(); |
| |
| function drawFrame() { |
| let seek = ((Date.now() - firstFrame) / duration) % 1.0; |
| CanvasKit.setCurrentContext(context); |
| animation.seek(seek); |
| canvas.clear(CanvasKit.WHITE); |
| animation.render(canvas, bounds); |
| surface.flush(); |
| window.requestAnimationFrame(drawFrame); |
| } |
| window.requestAnimationFrame(drawFrame); |
| |
| //animation.delete(); |
| return surface; |
| } |
| |
| function CanvasAPI1(CanvasKit) { |
| let skcanvas = CanvasKit.MakeCanvas(300, 300); |
| let realCanvas = document.getElementById('api1_c'); |
| |
| let skPromise = fetch('./test.png') |
| // if clients want to use a Blob, they are responsible |
| // for reading it themselves. |
| .then((response) => response.arrayBuffer()) |
| .then((buffer) => { |
| skcanvas._img = skcanvas.decodeImage(buffer); |
| }); |
| let realPromise = fetch('./test.png') |
| .then((response) => response.blob()) |
| .then((blob) => createImageBitmap(blob)) |
| .then((bitmap) => { |
| realCanvas._img = bitmap; |
| }); |
| |
| |
| Promise.all([realPromise, skPromise]).then(() => { |
| for (let canvas of [skcanvas, realCanvas]) { |
| let ctx = canvas.getContext('2d'); |
| ctx.fillStyle = '#EEE'; |
| ctx.fillRect(0, 0, 300, 300); |
| ctx.fillStyle = 'black'; |
| ctx.font = '30px Impact' |
| ctx.rotate(.1); |
| let text = ctx.measureText('Awesome'); |
| ctx.fillText('Awesome ', 50, 100); |
| ctx.strokeText('Groovy!', 60+text.width, 100); |
| |
| // Draw line under Awesome |
| ctx.strokeStyle = 'rgba(125,0,0,0.5)'; |
| ctx.beginPath(); |
| ctx.lineWidth = 6; |
| ctx.lineTo(50, 105); |
| ctx.lineTo(50 + text.width, 105); |
| ctx.stroke(); |
| |
| // squished vertically |
| ctx.globalAlpha = 0.7 |
| ctx.imageSmoothingQuality = 'medium'; |
| ctx.drawImage(canvas._img, 150, 150, 150, 100); |
| ctx.rotate(-.2); |
| ctx.imageSmoothingEnabled = false; |
| ctx.drawImage(canvas._img, 100, 150, 400, 350, 10, 200, 150, 100); |
| |
| let idata = ctx.getImageData(80, 220, 40, 45); |
| ctx.putImageData(idata, 250, 10); |
| ctx.putImageData(idata, 200, 10, 20, 10, 20, 30); |
| ctx.resetTransform(); |
| ctx.strokeStyle = 'black'; |
| ctx.lineWidth = 1; |
| ctx.strokeRect(200, 10, 40, 45); |
| |
| idata = ctx.createImageData(10, 20); |
| ctx.putImageData(idata, 10, 10); |
| } |
| |
| document.getElementById('api1').src = skcanvas.toDataURL(); |
| skcanvas.dispose(); |
| }); |
| |
| } |
| |
| function CanvasAPI2(CanvasKit) { |
| let skcanvas = CanvasKit.MakeCanvas(300, 300); |
| let realCanvas = document.getElementById('api2_c'); |
| realCanvas.width = 300; |
| realCanvas.height = 300; |
| |
| for (let canvas of [skcanvas, realCanvas]) { |
| let ctx = canvas.getContext('2d'); |
| ctx.scale(1.5, 1.5); |
| ctx.moveTo(20, 5); |
| ctx.lineTo(30, 20); |
| ctx.lineTo(40, 10); |
| ctx.lineTo(50, 20); |
| ctx.lineTo(60, 0); |
| ctx.lineTo(20, 5); |
| |
| ctx.moveTo(20, 80); |
| ctx.bezierCurveTo(90, 10, 160, 150, 190, 10); |
| |
| ctx.moveTo(36, 148); |
| ctx.quadraticCurveTo(66, 188, 120, 136); |
| ctx.lineTo(36, 148); |
| |
| ctx.rect(5, 170, 20, 25); |
| |
| ctx.moveTo(150, 180); |
| ctx.arcTo(150, 100, 50, 200, 20); |
| ctx.lineTo(160, 160); |
| |
| ctx.moveTo(20, 120); |
| ctx.arc(20, 120, 18, 0, 1.75 * Math.PI); |
| ctx.lineTo(20, 120); |
| |
| ctx.moveTo(150, 5); |
| ctx.ellipse(130, 25, 30, 10, -1*Math.PI/8, Math.PI/6, 1.5*Math.PI); |
| |
| ctx.lineWidth = 4/3; |
| ctx.stroke(); |
| } |
| document.getElementById('api2').src = skcanvas.toDataURL(); |
| } |
| |
| function CanvasAPI3(CanvasKit) { |
| let skcanvas = CanvasKit.MakeCanvas(300, 300); |
| let realCanvas = document.getElementById('api3_c'); |
| realCanvas.width = 300; |
| realCanvas.height = 300; |
| |
| for (let canvas of [skcanvas, realCanvas]) { |
| let ctx = canvas.getContext('2d'); |
| ctx.rect(10, 10, 20, 20); |
| |
| ctx.scale(2.0, 4.0); |
| ctx.rect(30, 10, 20, 20); |
| ctx.resetTransform(); |
| |
| ctx.rotate(Math.PI / 3); |
| ctx.rect(50, 10, 20, 20); |
| ctx.resetTransform(); |
| |
| ctx.translate(30, -2); |
| ctx.rect(70, 10, 20, 20); |
| ctx.resetTransform(); |
| |
| ctx.translate(60, 0); |
| ctx.rotate(Math.PI / 6); |
| ctx.transform(1.5, 0, 0, 0.5, 0, 0, 0); // effectively scale |
| ctx.rect(90, 10, 20, 20); |
| ctx.resetTransform(); |
| |
| ctx.save(); |
| ctx.setTransform(2, 0, -.5, 2.5, -40, 120); |
| ctx.rect(110, 10, 20, 20); |
| ctx.lineTo(110, 0); |
| ctx.restore(); |
| ctx.lineTo(220, 120); |
| |
| ctx.scale(3.0, 3.0); |
| ctx.font = '6pt Arial'; |
| ctx.fillText('This text should be huge', 10, 80); |
| ctx.resetTransform(); |
| |
| ctx.strokeStyle = 'black'; |
| ctx.lineWidth = 2; |
| ctx.stroke(); |
| |
| ctx.beginPath(); |
| ctx.moveTo(250, 30); |
| ctx.lineTo(250, 80); |
| ctx.scale(3.0, 3.0); |
| ctx.lineTo(280/3, 90/3); |
| ctx.closePath(); |
| ctx.strokeStyle = 'black'; |
| ctx.lineWidth = 5; |
| ctx.stroke(); |
| |
| } |
| document.getElementById('api3').src = skcanvas.toDataURL(); |
| } |
| |
| function CanvasAPI4(CanvasKit) { |
| let skcanvas = CanvasKit.MakeCanvas(300, 300); |
| let realCanvas = document.getElementById('api4_c'); |
| realCanvas.width = 300; |
| realCanvas.height = 300; |
| |
| for (let canvas of [skcanvas, realCanvas]) { |
| let ctx = canvas.getContext('2d'); |
| |
| ctx.strokeStyle = '#000'; |
| ctx.fillStyle = '#CCC'; |
| ctx.shadowColor = 'rebeccapurple'; |
| ctx.shadowBlur = 1; |
| ctx.shadowOffsetX = 3; |
| ctx.shadowOffsetY = -8; |
| ctx.rect(10, 10, 30, 30); |
| |
| ctx.save(); |
| ctx.strokeStyle = '#C00'; |
| ctx.fillStyle = '#00C'; |
| ctx.shadowBlur = 0; |
| ctx.shadowColor = 'transparent'; |
| |
| ctx.stroke(); |
| |
| ctx.restore(); |
| ctx.fill(); |
| |
| ctx.beginPath(); |
| ctx.moveTo(36, 148); |
| ctx.quadraticCurveTo(66, 188, 120, 136); |
| ctx.closePath(); |
| ctx.stroke(); |
| |
| ctx.beginPath(); |
| ctx.shadowColor = '#993366AA'; |
| ctx.shadowOffsetX = 8; |
| ctx.shadowBlur = 5; |
| ctx.setTransform(2, 0, -.5, 2.5, -40, 120); |
| ctx.rect(110, 10, 20, 20); |
| ctx.lineTo(110, 0); |
| ctx.resetTransform(); |
| ctx.lineTo(220, 120); |
| ctx.stroke(); |
| |
| ctx.fillStyle = 'green'; |
| ctx.font = '16pt Arial'; |
| ctx.fillText('This should be shadowed', 20, 80); |
| |
| ctx.beginPath(); |
| ctx.lineWidth = 6; |
| ctx.ellipse(10, 290, 30, 30, 0, 0, Math.PI * 2); |
| ctx.scale(2, 1); |
| ctx.moveTo(10, 290) |
| ctx.ellipse(10, 290, 30, 60, 0, 0, Math.PI * 2); |
| ctx.resetTransform(); |
| ctx.scale(3, 1); |
| ctx.moveTo(10, 290) |
| ctx.ellipse(10, 290, 30, 90, 0, 0, Math.PI * 2); |
| ctx.stroke(); |
| } |
| document.getElementById('api4').src = skcanvas.toDataURL(); |
| } |
| |
| function CanvasAPI5(CanvasKit) { |
| let skcanvas = CanvasKit.MakeCanvas(600, 600); |
| let realCanvas = document.getElementById('api5_c'); |
| realCanvas.width = 600; |
| realCanvas.height = 600; |
| |
| for (let canvas of [skcanvas, realCanvas]) { |
| let ctx = canvas.getContext('2d'); |
| ctx.scale(1.1, 1.1); |
| ctx.translate(10, 10); |
| // Shouldn't impact the fillRect calls |
| ctx.setLineDash([5, 3]); |
| |
| ctx.fillStyle = 'rgba(200, 0, 100, 0.81)'; |
| ctx.fillRect(20, 30, 100, 100); |
| |
| ctx.globalAlpha = 0.81; |
| ctx.fillStyle = 'rgba(200, 0, 100, 1.0)'; |
| ctx.fillRect(120, 30, 100, 100); |
| // This shouldn't do anything |
| ctx.globalAlpha = 0.1; |
| |
| ctx.fillStyle = 'rgba(200, 0, 100, 0.9)'; |
| ctx.globalAlpha = 0.9; |
| // Intentional no-op to check ordering |
| ctx.clearRect(220, 30, 100, 100); |
| ctx.fillRect(220, 30, 100, 100); |
| |
| ctx.fillRect(320, 30, 100, 100); |
| ctx.clearRect(330, 40, 80, 80); |
| |
| ctx.strokeStyle = 'blue'; |
| ctx.lineWidth = 3; |
| ctx.setLineDash([5, 3]); |
| ctx.strokeRect(20, 150, 100, 100); |
| ctx.setLineDash([50, 30]); |
| ctx.strokeRect(125, 150, 100, 100); |
| ctx.lineDashOffset = 25; |
| ctx.strokeRect(230, 150, 100, 100); |
| ctx.setLineDash([2, 5, 9]); |
| ctx.strokeRect(335, 150, 100, 100); |
| |
| ctx.setLineDash([5, 2]); |
| ctx.moveTo(336, 400); |
| ctx.quadraticCurveTo(366, 488, 120, 450); |
| ctx.lineTo(300, 400); |
| ctx.stroke(); |
| |
| ctx.font = '36pt Arial'; |
| ctx.strokeText('Dashed', 20, 350); |
| ctx.fillText('Not Dashed', 20, 400); |
| |
| } |
| document.getElementById('api5').src = skcanvas.toDataURL(); |
| } |
| |
| function CanvasAPI6(CanvasKit) { |
| let skcanvas = CanvasKit.MakeCanvas(600, 600); |
| let realCanvas = document.getElementById('api6_c'); |
| realCanvas.width = 600; |
| realCanvas.height = 600; |
| |
| for (let canvas of [skcanvas, realCanvas]) { |
| let ctx = canvas.getContext('2d'); |
| |
| var rgradient = ctx.createRadialGradient(200, 300, 10, 100, 100, 300); |
| |
| // Add three color stops |
| rgradient.addColorStop(0, 'red'); |
| rgradient.addColorStop(0.7, 'white'); |
| rgradient.addColorStop(1, 'blue'); |
| |
| ctx.fillStyle = rgradient; |
| ctx.globalAlpha = 0.7; |
| ctx.fillRect(0, 0, 600, 600); |
| ctx.globalAlpha = 0.95; |
| |
| ctx.beginPath(); |
| ctx.arc(300, 100, 90, 0, Math.PI*1.66); |
| ctx.closePath(); |
| ctx.strokeStyle = 'yellow'; |
| ctx.lineWidth = 5; |
| ctx.stroke(); |
| ctx.save(); |
| ctx.clip(); |
| |
| var lgradient = ctx.createLinearGradient(200, 20, 420, 40); |
| |
| // Add three color stops |
| lgradient.addColorStop(0, 'green'); |
| lgradient.addColorStop(0.5, 'cyan'); |
| lgradient.addColorStop(1, 'orange'); |
| |
| ctx.fillStyle = lgradient; |
| |
| ctx.fillRect(200, 30, 200, 300); |
| |
| ctx.restore(); |
| ctx.fillRect(550, 550, 40, 40); |
| |
| } |
| document.getElementById('api6').src = skcanvas.toDataURL(); |
| } |
| |
| function VertexAPI1(CanvasKit) { |
| const surface = CanvasKit.MakeCanvasSurface('vertex1'); |
| if (!surface) { |
| console.error('Could not make surface'); |
| return; |
| } |
| const context = CanvasKit.currentContext(); |
| const canvas = surface.getCanvas(); |
| let paint = new CanvasKit.SkPaint(); |
| |
| // See https://fiddle.skia.org/c/f48b22eaad1bb7adcc3faaa321754af6 |
| // for original c++ version. |
| let points = [[ 0, 0 ], [ 250, 0 ], [ 100, 100 ], [ 0, 250 ]]; |
| let colors = [CanvasKit.RED, CanvasKit.BLUE, |
| CanvasKit.YELLOW, CanvasKit.CYAN]; |
| let vertices = CanvasKit.MakeSkVertices(CanvasKit.VertexMode.TriangleFan, |
| points, null, colors); |
| |
| canvas.drawVertices(vertices, CanvasKit.BlendMode.Src, paint); |
| surface.flush(); |
| |
| vertices.delete(); |
| |
| // See https://fiddle.skia.org/c/e8bdae9bea3227758989028424fcac3d |
| // for original c++ version. |
| points = [[ 300, 300 ], [ 50, 300 ], [ 200, 200 ], [ 300, 50 ]]; |
| let texs = [[ 0, 0 ], [ 0, 250 ], [ 250, 250 ], [ 250, 0 ]]; |
| vertices = CanvasKit.MakeSkVertices(CanvasKit.VertexMode.TriangleFan, |
| points, texs, colors); |
| |
| let shader = CanvasKit.MakeLinearGradientShader([0, 0], [250, 0], |
| colors, null, CanvasKit.TileMode.Clamp); |
| paint.setShader(shader); |
| |
| canvas.drawVertices(vertices, CanvasKit.BlendMode.Darken, paint); |
| surface.flush(); |
| |
| shader.delete(); |
| paint.delete(); |
| surface.delete(); |
| } |
| |
| // bonesImageDatae is passed in as raw, encoded bytes. |
| function VertexAPI2(CanvasKit, bonesImageData) { |
| if (!CanvasKit || !bonesImageData) { |
| return; |
| } |
| const surface = CanvasKit.MakeCanvasSurface('vertex2'); |
| if (!surface) { |
| console.error('Could not make surface'); |
| return; |
| } |
| const context = CanvasKit.currentContext(); |
| const canvas = surface.getCanvas(); |
| let paint = new CanvasKit.SkPaint(); |
| |
| let shader = CanvasKit.MakeImageShader(bonesImageData, |
| CanvasKit.TileMode.Clamp, CanvasKit.TileMode.Clamp); |
| |
| // comment this out to see just the triangles move. |
| paint.setShader(shader); |
| |
| // points is the destination location on the canvas We want the output |
| // to be a 280x280 box (to start). |
| let points = [[ 0, 0 ], [ 280, 0 ], [ 280, 280 ], [ 0, 280 ]]; |
| // texs is the coordinates of the source in the texture |
| // (provided by the image shader). The image is 334x226 px big. |
| let texs = [[ 0, 0 ], [ 334, 0 ], [ 334, 226 ], [ 0, 226 ]]; |
| let boneidxs = [[1,0,0,0], [2,0,0,0], [3,0,0,0], [2,3,0,0]]; |
| let bonewts = [[1,0,0,0], [1,0,0,0], [1,0,0,0], [.5,.5,0,0]]; |
| let vertices = CanvasKit.MakeSkVertices(CanvasKit.VertexMode.TriangleFan, |
| points, texs, null, boneidxs, bonewts); |
| |
| function drawFrame() { |
| let now = Date.now(); |
| let bones = [ |
| [[1,0, // world bone (move 10px down and to the right to center) |
| 0,1, |
| 10,10]], |
| [[1,0, // identity bone (bone for vertices that are static) |
| 0,1, |
| 0,0]], |
| [[1,0, // ossilate in x bone |
| 0,1, |
| 10*Math.sin(now/500),0]], |
| [[1,0, // ossilate in y bone |
| 0,1, |
| 0,30*Math.cos(now/500)]], |
| ]; |
| let tVerts = vertices.applyBones(bones); |
| CanvasKit.setCurrentContext(context); |
| //canvas.clear(CanvasKit.TRANSPARENT); |
| canvas.drawVertices(tVerts, CanvasKit.BlendMode.Src, paint); |
| surface.flush(); |
| |
| tVerts.delete(); |
| window.requestAnimationFrame(drawFrame); |
| } |
| window.requestAnimationFrame(drawFrame); |
| //tVerts.delete(); |
| //vertices.delete(); |
| |
| //shader && shader.delete(); |
| //paint.delete(); |
| //surface.delete(); |
| |
| } |
| |
| function GradiantAPI1(CanvasKit) { |
| const surface = CanvasKit.MakeCanvasSurface('gradient1'); |
| if (!surface) { |
| console.error('Could not make surface'); |
| return; |
| } |
| const context = CanvasKit.currentContext(); |
| const canvas = surface.getCanvas(); |
| let paint = new CanvasKit.SkPaint(); |
| |
| // See https://fiddle.skia.org/c/f48b22eaad1bb7adcc3faaa321754af6 |
| // for original c++ version. |
| let points = [[ 0, 0 ], [ 250, 0 ], [ 100, 100 ], [ 0, 250 ]]; |
| let colors = [CanvasKit.BLUE, CanvasKit.YELLOW, CanvasKit.RED]; |
| let pos = [0, .7, 1.0]; |
| let transform = [2, 0, 0, |
| 0, 2, 0, |
| 0, 0, 1] |
| let shader = CanvasKit.MakeRadialGradientShader([150,150], 130, colors, |
| pos, CanvasKit.TileMode.Mirror, transform); |
| |
| paint.setShader(shader); |
| paint.setTextSize(100); |
| canvas.drawText("Radial", 10, 200, paint); |
| surface.flush(); |
| } |
| </script> |