blob: 00078810bb01811c460138d4fb6e5322ce460c26 [file] [log] [blame]
<!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>
<img id=api7 width=300 height=300>
<canvas id=api7_c width=300 height=300></canvas>
<img id=api8 width=300 height=300>
<canvas id=api8_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 robotoData = null;
var bonesImageData = null;
CanvasKitInit({
locateFile: (file) => '/node_modules/canvaskit/bin/'+file,
}).ready().then((CK) => {
CanvasKit = CK;
DrawingExample(CanvasKit, robotoData);
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);
CanvasAPI7(CanvasKit);
CanvasAPI8(CanvasKit)
VertexAPI1(CanvasKit);
VertexAPI2(CanvasKit, bonesImageData);
GradiantAPI1(CanvasKit);
});
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);
});
});
fetch('./Roboto-Regular.woff').then((resp) => {
resp.arrayBuffer().then((buffer) => {
robotoData = buffer;
DrawingExample(CanvasKit, robotoData)
});
});
function DrawingExample(CanvasKit, robotoData) {
if (!robotoData || !CanvasKit) {
return;
}
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 fontMgr = CanvasKit.SkFontMgr.RefDefault();
let roboto = fontMgr.MakeTypefaceFromData(robotoData);
const textPaint = new CanvasKit.SkPaint();
textPaint.setColor(CanvasKit.RED);
textPaint.setAntiAlias(true);
const textFont = new CanvasKit.SkFont(roboto, 30);
let i = 0;
let X = 128;
let Y = 128;
function drawFrame() {
const path = starPath(CanvasKit, X, Y);
// Some animations see performance improvements by marking their
// paths as volatile.
path.setIsVolatile(true);
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, textFont, 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();
// textPaint.delete();
// textFont.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);
let rrect = new CanvasKit.SkPath()
.addRoundRect(100, 10, 140, 62,
10, 4, true);
canvas.drawPath(rrect, paint);
surface.flush();
path.delete();
rrect.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;
});
let realFontLoaded = new FontFace('Bungee', 'url(/tests/assets/Bungee-Regular.ttf)', {
'family': 'Bungee',
'style': 'normal',
'weight': '400',
}).load().then((font) => {
document.fonts.add(font);
});
let skFontLoaded = fetch('/tests/assets/Bungee-Regular.ttf').then(
(response) => response.arrayBuffer()).then(
(buffer) => {
// loadFont is synchronous
skcanvas.loadFont(buffer, {
'family': 'Bungee',
'style': 'normal',
'weight': '400',
});
});
Promise.all([realPromise, skPromise, realFontLoaded, skFontLoaded]).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 = '26px Bungee';
ctx.rotate(.1);
let text = ctx.measureText('Awesome');
ctx.fillText('Awesome ', 25, 100);
ctx.strokeText('Groovy!', 35+text.width, 100);
// Draw line under Awesome
ctx.strokeStyle = 'rgba(125,0,0,0.5)';
ctx.beginPath();
ctx.lineWidth = 6;
ctx.moveTo(25, 105);
ctx.lineTo(25 + 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;
// svg data for a clock
skcanvas._path = skcanvas.makePath2D('M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67z');
realCanvas._path = new Path2D('M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67z');
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();
// make a clock
ctx.stroke(canvas._path);
// Test edgecases and draw direction
ctx.beginPath();
ctx.arc(50, 100, 10, Math.PI, -Math.PI/2);
ctx.stroke();
ctx.beginPath();
ctx.arc(75, 100, 10, Math.PI, -Math.PI/2, true);
ctx.stroke();
ctx.beginPath();
ctx.arc(100, 100, 10, Math.PI, 100.1 * Math.PI, true);
ctx.stroke();
ctx.beginPath();
ctx.arc(125, 100, 10, Math.PI, 100.1 * Math.PI, false);
ctx.stroke();
ctx.beginPath();
ctx.ellipse(155, 100, 10, 15, Math.PI/8, 100.1 * Math.PI, Math.PI, true);
ctx.stroke();
ctx.beginPath();
ctx.ellipse(180, 100, 10, 15, Math.PI/8, Math.PI, 100.1 * Math.PI, true);
ctx.stroke();
}
document.getElementById('api2').src = skcanvas.toDataURL();
skcanvas.dispose();
}
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 Noto Mono';
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();
skcanvas.dispose();
}
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 Noto Mono';
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();
skcanvas.dispose();
}
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 Noto Mono';
ctx.strokeText('Dashed', 20, 350);
ctx.fillText('Not Dashed', 20, 400);
}
document.getElementById('api5').src = skcanvas.toDataURL();
skcanvas.dispose();
}
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');
let 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();
let 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();
skcanvas.dispose();
}
function CanvasAPI7(CanvasKit) {
let skcanvas = CanvasKit.MakeCanvas(300, 300);
let realCanvas = document.getElementById('api7_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.lineWidth = 20;
ctx.scale(0.1, 0.2);
let pattern = ctx.createPattern(canvas._img, 'repeat');
ctx.fillStyle = pattern;
ctx.fillRect(0, 0, 1500, 750);
pattern = ctx.createPattern(canvas._img, 'repeat-x');
ctx.fillStyle = pattern;
ctx.fillRect(1500, 0, 3000, 750);
ctx.globalAlpha = 0.7
pattern = ctx.createPattern(canvas._img, 'repeat-y');
ctx.fillStyle = pattern;
ctx.fillRect(0, 750, 1500, 1500);
ctx.strokeRect(0, 750, 1500, 1500);
pattern = ctx.createPattern(canvas._img, 'no-repeat');
ctx.fillStyle = pattern;
pattern.setTransform({a: 1, b: -.1, c:.1, d: 0.5, e: 1800, f:800});
ctx.fillRect(0, 0, 3000, 1500);
}
document.getElementById('api7').src = skcanvas.toDataURL();
skcanvas.dispose();
});
}
function CanvasAPI8(CanvasKit) {
let skcanvas = CanvasKit.MakeCanvas(300, 300);
let realCanvas = document.getElementById('api8_c');
function drawPoint(ctx, x, y, color) {
ctx.fillStyle = color;
ctx.fillRect(x, y, 1, 1);
}
const IN = 'purple';
const OUT = 'orange';
const SCALE = 4;
const pts = [[3, 3], [4, 4], [5, 5], [10, 10], [8, 10], [6, 10],
[6.5, 9], [15, 10], [17, 10], [17, 11], [24, 24],
[25, 25], [26, 26], [27, 27]];
const tests = [
{
xOffset: 0,
yOffset: 0,
fillType: 'nonzero',
strokeWidth: 0,
testFn: (ctx, x, y) => ctx.isPointInPath(x * SCALE, y * SCALE, 'nonzero'),
},
{
xOffset: 30,
yOffset: 0,
fillType: 'evenodd',
strokeWidth: 0,
testFn: (ctx, x, y) => ctx.isPointInPath(x * SCALE, y * SCALE, 'evenodd'),
},
{
xOffset: 0,
yOffset: 30,
fillType: null,
strokeWidth: 1,
testFn: (ctx, x, y) => ctx.isPointInStroke(x * SCALE, y * SCALE),
},
{
xOffset: 30,
yOffset: 30,
fillType: null,
strokeWidth: 2,
testFn: (ctx, x, y) => ctx.isPointInStroke(x * SCALE, y * SCALE),
},
];
for (let canvas of [skcanvas, realCanvas]) {
let ctx = canvas.getContext('2d');
ctx.font = '11px Noto Mono';
// Draw some visual aids
ctx.fillText('path-nonzero', 30, 15);
ctx.fillText('path-evenodd', 150, 15);
ctx.fillText('stroke-1px-wide', 30, 130);
ctx.fillText('stroke-2px-wide', 150, 130);
ctx.fillText('purple is IN, orange is OUT', 10, 280);
// Scale up to make single pixels easier to see
ctx.scale(SCALE, SCALE);
for (let test of tests) {
ctx.beginPath();
let xOffset = test.xOffset;
let yOffset = test.yOffset;
ctx.fillStyle = '#AAA';
ctx.lineWidth = test.strokeWidth;
ctx.rect(5+xOffset, 5+yOffset, 20, 20);
ctx.arc(15+xOffset, 15+yOffset, 8, 0, Math.PI*2, false);
if (test.fillType) {
ctx.fill(test.fillType);
} else {
ctx.stroke();
}
for (let pt of pts) {
let [x, y] = pt;
x += xOffset;
y += yOffset;
// naively apply transform when querying because the points queried
// ignore the CTM.
if (test.testFn(ctx, x, y)) {
drawPoint(ctx, x, y, IN);
} else {
drawPoint(ctx, x, y, OUT);
}
}
}
}
document.getElementById('api8').src = skcanvas.toDataURL();
skcanvas.dispose();
}
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 bonesImage = CanvasKit.MakeImageFromEncoded(bonesImageData);
let shader = CanvasKit.MakeImageShader(bonesImage,
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();
// bonesImage && bonesImage.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);
const textFont = new CanvasKit.SkFont(null, 75);
const textBlob = CanvasKit.SkTextBlob.MakeFromText('Radial', textFont);
canvas.drawTextBlob(textBlob, 10, 200, paint);
paint.delete()
textFont.delete();
textBlob.delete();
surface.flush();
}
</script>