2018-09-20 21:39:31 +00:00
|
|
|
<!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>
|
2018-10-19 18:34:34 +00:00
|
|
|
svg, canvas, img {
|
2018-09-20 21:39:31 +00:00
|
|
|
border: 1px dashed #AAA;
|
|
|
|
}
|
|
|
|
|
2018-11-20 19:07:42 +00:00
|
|
|
#patheffect,#paths,#sk_drinks,#sk_party, #sk_legos, #sk_onboarding,
|
2018-11-20 17:51:16 +00:00
|
|
|
#api1_c, #api2_c {
|
2018-09-20 21:39:31 +00:00
|
|
|
width: 300px;
|
|
|
|
height: 300px;
|
|
|
|
}
|
|
|
|
|
|
|
|
</style>
|
|
|
|
|
2018-10-17 11:57:18 +00:00
|
|
|
<h2> CanvasKit draws Paths to the browser</h2>
|
2018-11-03 11:51:19 +00:00
|
|
|
<canvas id=vertex1 width=300 height=300></canvas>
|
|
|
|
<canvas id=vertex2 width=300 height=300></canvas>
|
|
|
|
<canvas id=gradient1 width=300 height=300></canvas>
|
2018-09-20 21:39:31 +00:00
|
|
|
<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>
|
2018-09-21 14:14:30 +00:00
|
|
|
<canvas id=sk_party width=500 height=500></canvas>
|
|
|
|
<canvas id=sk_onboarding width=500 height=500></canvas>
|
2018-09-20 21:39:31 +00:00
|
|
|
|
|
|
|
<!-- Doesn't work yet. -->
|
|
|
|
<button id=lego_btn>Take a picture of the legos</button>
|
|
|
|
|
2018-10-30 19:05:04 +00:00
|
|
|
<h2> Nima </h2>
|
|
|
|
|
|
|
|
<canvas id=nima_example width=300 height=300></canvas>
|
|
|
|
|
2018-10-19 18:34:34 +00:00
|
|
|
<h2>Drop in replacement for HTML Canvas (e.g. node.js)</h2>
|
2018-11-20 17:51:16 +00:00
|
|
|
<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>
|
2018-10-19 18:34:34 +00:00
|
|
|
|
2018-10-17 11:57:18 +00:00
|
|
|
<script type="text/javascript" src="/node_modules/canvaskit/bin/canvaskit.js"></script>
|
2018-09-20 21:39:31 +00:00
|
|
|
|
|
|
|
<script type="text/javascript" charset="utf-8">
|
|
|
|
|
|
|
|
var CanvasKit = null;
|
|
|
|
var legoJSON = null;
|
|
|
|
var drinksJSON = null;
|
|
|
|
var confettiJSON = null;
|
2018-09-21 14:14:30 +00:00
|
|
|
var onboardingJSON = null;
|
|
|
|
var fullBounds = {fLeft: 0, fTop: 0, fRight: 500, fBottom: 500};
|
2018-10-30 19:05:04 +00:00
|
|
|
|
|
|
|
var nimaFile = null;
|
|
|
|
var nimaTexture = null;
|
2018-11-03 11:51:19 +00:00
|
|
|
|
|
|
|
var bonesImage = null;
|
2018-09-20 21:39:31 +00:00
|
|
|
CanvasKitInit({
|
2018-10-17 11:57:18 +00:00
|
|
|
locateFile: (file) => '/node_modules/canvaskit/bin/'+file,
|
2018-09-20 21:39:31 +00:00
|
|
|
}).then((CK) => {
|
2018-11-03 11:51:19 +00:00
|
|
|
// 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
|
|
|
|
addScreenshotListener(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);
|
|
|
|
|
|
|
|
NimaExample(CanvasKit, nimaFile, nimaTexture);
|
|
|
|
|
|
|
|
CanvasAPI1(CanvasKit);
|
2018-11-20 17:51:16 +00:00
|
|
|
CanvasAPI2(CanvasKit);
|
2018-11-03 11:51:19 +00:00
|
|
|
|
|
|
|
VertexAPI1(CanvasKit);
|
|
|
|
VertexAPI2(CanvasKit, bonesImage);
|
2018-10-19 18:34:34 +00:00
|
|
|
|
2018-11-03 11:51:19 +00:00
|
|
|
GradiantAPI1(CanvasKit);
|
|
|
|
}, 0);
|
2018-09-20 21:39:31 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
fetch('https://storage.googleapis.com/skia-cdn/misc/lego_loader.json').then((resp) => {
|
|
|
|
resp.text().then((str) => {
|
|
|
|
legoJSON = str;
|
2018-10-17 11:57:18 +00:00
|
|
|
addScreenshotListener(SkottieExample(CanvasKit, 'sk_legos', legoJSON,
|
|
|
|
{fLeft: -50, fTop: 0, fRight: 350, fBottom: 300}));
|
2018-09-20 21:39:31 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
fetch('https://storage.googleapis.com/skia-cdn/misc/drinks.json').then((resp) => {
|
|
|
|
resp.text().then((str) => {
|
|
|
|
drinksJSON = str;
|
2018-09-21 14:14:30 +00:00
|
|
|
SkottieExample(CanvasKit, 'sk_drinks', drinksJSON, fullBounds);
|
2018-09-20 21:39:31 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
fetch('https://storage.googleapis.com/skia-cdn/misc/confetti.json').then((resp) => {
|
|
|
|
resp.text().then((str) => {
|
|
|
|
confettiJSON = str;
|
2018-09-21 14:14:30 +00:00
|
|
|
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);
|
2018-09-20 21:39:31 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2018-10-30 19:05:04 +00:00
|
|
|
fetch('https://storage.googleapis.com/skia-cdn/misc/robot.nima').then((resp) => {
|
|
|
|
resp.blob().then((blob) => {
|
|
|
|
let reader = new FileReader();
|
|
|
|
reader.addEventListener("loadend", function() {
|
|
|
|
nimaFile = reader.result;
|
|
|
|
NimaExample(CanvasKit, nimaFile, nimaTexture);
|
|
|
|
});
|
|
|
|
reader.readAsArrayBuffer(blob);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
fetch('https://storage.googleapis.com/skia-cdn/misc/robot.nima.png').then((resp) => {
|
|
|
|
resp.blob().then((blob) => {
|
|
|
|
let reader = new FileReader();
|
|
|
|
reader.addEventListener("loadend", function() {
|
|
|
|
nimaTexture = reader.result;
|
|
|
|
NimaExample(CanvasKit, nimaFile, nimaTexture);
|
|
|
|
});
|
|
|
|
reader.readAsArrayBuffer(blob);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2018-11-03 11:51:19 +00:00
|
|
|
fetch('https://storage.googleapis.com/skia-cdn/misc/bones.jpg').then((resp) => {
|
|
|
|
resp.blob().then((blob) => {
|
|
|
|
let reader = new FileReader();
|
|
|
|
reader.addEventListener("loadend", function() {
|
|
|
|
bonesImage = reader.result;
|
|
|
|
VertexAPI2(CanvasKit, bonesImage);
|
|
|
|
});
|
|
|
|
reader.readAsArrayBuffer(blob);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2018-10-17 11:57:18 +00:00
|
|
|
function addScreenshotListener(surface) {
|
2018-09-20 21:39:31 +00:00
|
|
|
if (!surface) {
|
2018-10-17 11:57:18 +00:00
|
|
|
return;
|
2018-09-20 21:39:31 +00:00
|
|
|
}
|
2018-10-17 11:57:18 +00:00
|
|
|
if (CanvasKit.gpu) {
|
|
|
|
// Doesn't work on GPU (yet)
|
|
|
|
document.getElementById('lego_btn').remove();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const btn = document.getElementById('lego_btn');
|
|
|
|
btn.addEventListener('click', () => {
|
|
|
|
const img = surface.makeImageSnapshot()
|
|
|
|
if (!img) { return }
|
|
|
|
const png = img.encodeToData()
|
|
|
|
if (!png) { return }
|
|
|
|
const pngBytes = CanvasKit.getSkDataBytes(png);
|
|
|
|
// See https://stackoverflow.com/a/12713326
|
|
|
|
let b64encoded = btoa(String.fromCharCode.apply(null, pngBytes));
|
|
|
|
console.log("base64 encoded image", b64encoded);
|
|
|
|
});
|
|
|
|
}
|
2018-09-20 21:39:31 +00:00
|
|
|
|
|
|
|
function DrawingExample(CanvasKit) {
|
2018-10-17 11:57:18 +00:00
|
|
|
const surface = CanvasKit.MakeCanvasSurface('patheffect');
|
2018-09-20 21:39:31 +00:00
|
|
|
if (!surface) {
|
2018-10-17 11:57:18 +00:00
|
|
|
console.error('Could not make surface');
|
|
|
|
return;
|
2018-09-20 21:39:31 +00:00
|
|
|
}
|
2018-10-17 11:57:18 +00:00
|
|
|
const context = CanvasKit.currentContext(); // only needed for WebGL; no-op for CPU
|
2018-09-20 21:39:31 +00:00
|
|
|
|
|
|
|
const canvas = surface.getCanvas();
|
|
|
|
|
|
|
|
const paint = new CanvasKit.SkPaint();
|
|
|
|
|
|
|
|
const textPaint = new CanvasKit.SkPaint();
|
2018-11-03 11:51:19 +00:00
|
|
|
textPaint.setColor(CanvasKit.RED);
|
2018-09-20 21:39:31 +00:00
|
|
|
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);
|
2018-11-03 11:51:19 +00:00
|
|
|
paint.setStyle(CanvasKit.PaintStyle.Stroke);
|
2018-09-20 21:39:31 +00:00
|
|
|
paint.setStrokeWidth(5.0 + -3 * Math.cos(i/30));
|
|
|
|
paint.setAntiAlias(true);
|
|
|
|
paint.setColor(CanvasKit.Color(66, 129, 164, 1.0));
|
|
|
|
|
2018-11-03 11:51:19 +00:00
|
|
|
canvas.clear(CanvasKit.TRANSPARENT);
|
2018-09-20 21:39:31 +00:00
|
|
|
|
|
|
|
canvas.drawPath(path, paint);
|
2018-10-02 15:33:52 +00:00
|
|
|
canvas.drawText('Try Clicking!', 10, 280, textPaint);
|
2018-10-17 11:57:18 +00:00
|
|
|
|
|
|
|
surface.flush();
|
|
|
|
|
2018-09-20 21:39:31 +00:00
|
|
|
dpe.delete();
|
|
|
|
path.delete();
|
|
|
|
window.requestAnimationFrame(drawFrame);
|
|
|
|
}
|
|
|
|
window.requestAnimationFrame(drawFrame);
|
|
|
|
|
2018-10-02 15:33:52 +00:00
|
|
|
// Make animation interactive
|
|
|
|
let interact = (e) => {
|
|
|
|
if (!e.pressure) {
|
|
|
|
return;
|
|
|
|
}
|
2018-09-20 21:39:31 +00:00
|
|
|
X = e.offsetX;
|
|
|
|
Y = e.offsetY;
|
2018-10-02 15:33:52 +00:00
|
|
|
};
|
|
|
|
document.getElementById('patheffect').addEventListener('pointermove', interact);
|
|
|
|
document.getElementById('patheffect').addEventListener('pointerdown', interact);
|
|
|
|
preventScrolling(document.getElementById('patheffect'));
|
2018-09-20 21:39:31 +00:00
|
|
|
// A client would need to delete this if it didn't go on for ever.
|
|
|
|
//paint.delete();
|
|
|
|
}
|
|
|
|
|
|
|
|
function PathExample(CanvasKit) {
|
2018-10-17 11:57:18 +00:00
|
|
|
const surface = CanvasKit.MakeCanvasSurface('paths');
|
2018-09-20 21:39:31 +00:00
|
|
|
if (!surface) {
|
2018-10-17 11:57:18 +00:00
|
|
|
console.error('Could not make surface');
|
|
|
|
return;
|
2018-09-20 21:39:31 +00:00
|
|
|
}
|
|
|
|
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));
|
2018-11-03 11:51:19 +00:00
|
|
|
paint.setStyle(CanvasKit.PaintStyle.Stroke);
|
2018-09-20 21:39:31 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2018-10-17 11:57:18 +00:00
|
|
|
surface.flush();
|
2018-09-20 21:39:31 +00:00
|
|
|
|
|
|
|
path.delete();
|
|
|
|
paint.delete();
|
|
|
|
// Intentionally just draw frame once
|
|
|
|
}
|
|
|
|
window.requestAnimationFrame(drawFrame);
|
|
|
|
}
|
|
|
|
|
2018-10-02 15:33:52 +00:00
|
|
|
function preventScrolling(canvas) {
|
|
|
|
canvas.addEventListener('touchmove', (e) => {
|
|
|
|
// Prevents touch events in the canvas from scrolling the canvas.
|
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
|
|
|
});
|
|
|
|
}
|
2018-09-20 21:39:31 +00:00
|
|
|
|
|
|
|
function InkExample(CanvasKit) {
|
2018-10-17 11:57:18 +00:00
|
|
|
const surface = CanvasKit.MakeCanvasSurface('ink');
|
2018-09-20 21:39:31 +00:00
|
|
|
if (!surface) {
|
2018-10-17 11:57:18 +00:00
|
|
|
console.error('Could not make surface');
|
|
|
|
return;
|
2018-09-20 21:39:31 +00:00
|
|
|
}
|
|
|
|
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));
|
2018-11-03 11:51:19 +00:00
|
|
|
paint.setStyle(CanvasKit.PaintStyle.Stroke);
|
2018-09-20 21:39:31 +00:00
|
|
|
paint.setStrokeWidth(4.0);
|
|
|
|
paint.setPathEffect(CanvasKit.MakeSkCornerPathEffect(50));
|
|
|
|
|
2018-09-21 14:14:30 +00:00
|
|
|
// Draw I N K
|
2018-09-20 21:39:31 +00:00
|
|
|
let path = new CanvasKit.SkPath();
|
2018-09-21 14:14:30 +00:00
|
|
|
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);
|
2018-09-20 21:39:31 +00:00
|
|
|
|
|
|
|
let paths = [path];
|
|
|
|
let paints = [paint];
|
|
|
|
|
|
|
|
function drawFrame() {
|
|
|
|
CanvasKit.setCurrentContext(context);
|
2018-10-17 11:57:18 +00:00
|
|
|
canvas.clear(CanvasKit.Color(255, 255, 255, 1.0));
|
2018-09-20 21:39:31 +00:00
|
|
|
|
|
|
|
for (let i = 0; i < paints.length && i < paths.length; i++) {
|
|
|
|
canvas.drawPath(paths[i], paints[i]);
|
|
|
|
}
|
2018-10-17 11:57:18 +00:00
|
|
|
surface.flush();
|
2018-09-20 21:39:31 +00:00
|
|
|
|
|
|
|
window.requestAnimationFrame(drawFrame);
|
|
|
|
}
|
|
|
|
|
|
|
|
let hold = false;
|
2018-10-02 15:33:52 +00:00
|
|
|
let interact = (e) => {
|
|
|
|
let type = e.type;
|
|
|
|
if (type === 'lostpointercapture' || type === 'pointerup' || !e.pressure ) {
|
2018-09-20 21:39:31 +00:00
|
|
|
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;
|
2018-10-02 15:33:52 +00:00
|
|
|
};
|
|
|
|
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'));
|
2018-09-20 21:39:31 +00:00
|
|
|
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.MakeAnimation(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};
|
|
|
|
|
2018-10-17 11:57:18 +00:00
|
|
|
const surface = CanvasKit.MakeCanvasSurface(id);
|
2018-09-20 21:39:31 +00:00
|
|
|
if (!surface) {
|
2018-10-17 11:57:18 +00:00
|
|
|
console.error('Could not make surface');
|
|
|
|
return;
|
2018-09-20 21:39:31 +00:00
|
|
|
}
|
|
|
|
const context = CanvasKit.currentContext();
|
|
|
|
const canvas = surface.getCanvas();
|
|
|
|
|
2018-10-30 19:05:04 +00:00
|
|
|
let firstFrame = Date.now();
|
2018-09-20 21:39:31 +00:00
|
|
|
|
|
|
|
function drawFrame() {
|
2018-10-30 19:05:04 +00:00
|
|
|
let seek = ((Date.now() - firstFrame) / duration) % 1.0;
|
2018-09-20 21:39:31 +00:00
|
|
|
CanvasKit.setCurrentContext(context);
|
|
|
|
animation.seek(seek);
|
2018-11-03 11:51:19 +00:00
|
|
|
canvas.clear(CanvasKit.TRANSPARENT);
|
2018-09-20 21:39:31 +00:00
|
|
|
animation.render(canvas, bounds);
|
2018-10-17 11:57:18 +00:00
|
|
|
surface.flush();
|
2018-09-20 21:39:31 +00:00
|
|
|
window.requestAnimationFrame(drawFrame);
|
|
|
|
}
|
|
|
|
window.requestAnimationFrame(drawFrame);
|
|
|
|
|
|
|
|
//animation.delete();
|
2018-10-17 11:57:18 +00:00
|
|
|
return surface;
|
2018-09-20 21:39:31 +00:00
|
|
|
}
|
|
|
|
|
2018-10-19 18:34:34 +00:00
|
|
|
function CanvasAPI1(CanvasKit) {
|
2018-11-20 17:51:16 +00:00
|
|
|
let skcanvas = CanvasKit.MakeCanvas(300, 300);
|
|
|
|
let realCanvas = document.getElementById('api1_c');
|
|
|
|
|
|
|
|
for (let canvas of [skcanvas, realCanvas]) {
|
|
|
|
let ctx = canvas.getContext('2d');
|
|
|
|
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();
|
|
|
|
}
|
2018-10-19 18:34:34 +00:00
|
|
|
|
|
|
|
// TODO load image
|
2018-11-20 17:51:16 +00:00
|
|
|
document.getElementById('api1').src = skcanvas.toDataURL();
|
|
|
|
}
|
|
|
|
|
|
|
|
function CanvasAPI2(CanvasKit) {
|
|
|
|
let skcanvas = CanvasKit.MakeCanvas(200, 200);
|
|
|
|
let realCanvas = document.getElementById('api2_c');
|
|
|
|
realCanvas.width = 200;
|
|
|
|
realCanvas.height = 200;
|
|
|
|
|
|
|
|
for (let canvas of [skcanvas, realCanvas]) {
|
|
|
|
let ctx = canvas.getContext('2d');
|
|
|
|
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();
|
2018-10-19 18:34:34 +00:00
|
|
|
}
|
|
|
|
|
2018-10-30 19:05:04 +00:00
|
|
|
function NimaExample(CanvasKit, nimaFile, nimaTexture) {
|
|
|
|
if (!CanvasKit || !nimaFile || !nimaTexture) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const animation = CanvasKit.MakeNimaActor(nimaFile, nimaTexture);
|
|
|
|
if (!animation) {
|
|
|
|
console.error('could not make animation');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const surface = CanvasKit.MakeCanvasSurface('nima_example');
|
|
|
|
if (!surface) {
|
|
|
|
console.error('Could not make surface');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const context = CanvasKit.currentContext();
|
|
|
|
const canvas = surface.getCanvas();
|
|
|
|
canvas.translate(125, 275);
|
|
|
|
canvas.scale(0.4, -0.4);
|
|
|
|
|
|
|
|
let firstFrame = Date.now();
|
|
|
|
animation.setAnimationByName('attack');
|
|
|
|
|
|
|
|
function drawFrame() {
|
|
|
|
let seek = ((Date.now() - firstFrame) / 1000.0);
|
|
|
|
CanvasKit.setCurrentContext(context);
|
2018-11-03 11:51:19 +00:00
|
|
|
canvas.clear(CanvasKit.TRANSPARENT);
|
2018-10-30 19:05:04 +00:00
|
|
|
animation.seek(seek);
|
|
|
|
animation.render(canvas);
|
|
|
|
surface.flush();
|
|
|
|
window.requestAnimationFrame(drawFrame);
|
|
|
|
}
|
|
|
|
window.requestAnimationFrame(drawFrame);
|
|
|
|
}
|
|
|
|
|
2018-11-03 11:51:19 +00:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
|
|
|
function VertexAPI2(CanvasKit, bonesImage) {
|
|
|
|
if (!CanvasKit || !bonesImage) {
|
|
|
|
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(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();
|
|
|
|
|
|
|
|
//shader && shader.delete();
|
|
|
|
//paint.delete();
|
|
|
|
//surface.delete();
|
|
|
|
|
|
|
|
}
|
2018-11-20 17:51:16 +00:00
|
|
|
|
2018-11-03 11:51:19 +00:00
|
|
|
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();
|
|
|
|
}
|
2018-09-20 21:39:31 +00:00
|
|
|
</script>
|