scrawl-canvas
Version:
Version 8.9.4 - 19 Nov 2022
196 lines (153 loc) • 6.72 kB
JavaScript
// # Demo Canvas 046
// Kill cycles for Cell, Group, Tween/Ticker, Picture and Asset objects, and Picture source elements in the DOM
// [Run code](../../demo/canvas-046.html)
import * as scrawl from '../source/scrawl.js';
import { reportSpeed } from './utilities.js';
// #### Smoke ring generator
// We could place this code in its own module then import it into this code
// + The generator creates a new Cell, adds a Polyline entity to it, compiles the Cell and captures the output into a DOM image.
// + It then destroys the Cell and Polyline and creates a Picture entity from the asset they produced
// + Finally it constructs a Tween to animate various aspects of the Picture entity
// + When the Tween completes it will destroy the image asset wrapper and the Picture entity, then remove the <img> element from the DOM, after which it will destroy itself
// + In other words, by the time the Tween completes, none of the objects generated for the smoke ring effect should remain in the Scrawl-canvas library or the page DOM.
// Use a Color object to generate colors on demand, in a restricted color range
const colorMaker = scrawl.makeColor({
name: 'color-maker',
minimumColor: 'blue',
maximumColor: 'green',
});
// To avoid nameclashes, we're using a counter as part of the smokering's name
let counter = 0;
// The main function
const buildSmokeRing = function (namespace, canvasWrapper, color) {
// Create new Cell
const cell = canvasWrapper.buildCell({
name: `${namespace}-cell`,
width: 600,
height: 600,
cleared: false,
compiled: false,
shown: false,
});
// Create a set of coordinates to be consumed by the Polyline entity
const v = scrawl.requestVector(0, 200),
pins = [];
for (let i = 0; i < 120; i++) {
v.rotate((Math.random() * 45) - 10);
pins.push([v.x + ((Math.random() * 120) - 60) + 300, v.y + ((Math.random() * 120) - 60) + 300])
}
scrawl.releaseVector(v);
// Create Polyline entity
let snake = scrawl.makePolyline({
name: `${namespace}-snake`,
group: `${namespace}-cell`,
pins: pins,
start: ['center', 'center'],
handle: ['center', 'center'],
lineWidth: 2,
lineJoin: 'round',
tension: 1,
strokeStyle: color,
method: 'draw',
globalAlpha: 0.1,
});
// We need to perform a clear-compile on the Cell, outside of the Display cycle
cell.clear();
scrawl.createImageFromCell(cell, true);
cell.compile();
snake.kill();
cell.kill();
// Create the Picture entity which will destroy the (dearly departed) Polyline on the canvas
let img = scrawl.makePicture({
name: `${namespace}-smokering`,
group: canvasWrapper.base.name,
start: ['center', 'center'],
handle: ['center', 'center'],
asset: `${namespace}-cell-image`,
// 'dom' - is the magic String that tells Scrawl-canvas to remove both the asset wrapper object, and the <img> element itself. Any other non-false value will cause only the asset wrapper object to remove itself.
removeAssetOnKill: 'dom',
dimensions: [600, 600],
copyDimensions: [600, 600],
});
// Create the Tween that will animate the Picture entity
scrawl.makeTween({
name: `${namespace}-tween`,
duration: '16s',
targets: `${namespace}-smokering`,
// The Tween will run once and then destroy the Picture, the <img> element and its asset wrapper, and then itself
cycles: 1,
killOnComplete: true,
completeAction: () => {
img.kill(true);
counter++;
buildSmokeRing(`ring${counter}`, canvas, colorMaker.getRangeColor(Math.random()));
},
definitions: [
{
attribute: 'scale',
start: 0,
end: 2,
engine: 'easeIn3',
},
{
attribute: 'globalAlpha',
start: 1,
end: 0,
},
{
attribute: 'roll',
start: 0,
end: (Math.random() * 180) - 90,
engine: 'easeIn',
},
]
}).run();
};
// #### Scene setup
const canvas = scrawl.library.canvas.mycanvas;
// #### Scene animation
// Function to display frames-per-second data, and other information relevant to the demo
const report = reportSpeed('#reportmessage', function () {
const lib = scrawl.library;
return `Cells: ${lib.cellnames.join(', ')}
Groups: ${lib.groupnames.join(', ')}
Assets: ${lib.assetnames.join(', ')}
Entitys: ${lib.entitynames.join(', ')}
Tickers: ${lib.animationtickersnames.join(', ')}
Tweens: ${lib.tweennames.join(', ')}`;
});
// Create the Display cycle animation
const render = scrawl.makeRender({
name: 'demo-animation',
target: canvas,
afterShow: report,
});
// The tween runs for 16 seconds, and we want a regular supply of rings ... so we release some initially to get the scene running
setTimeout(() => buildSmokeRing(`initRingFirst`, canvas, colorMaker.getRangeColor(Math.random())), 0);
setTimeout(() => buildSmokeRing(`initRingSecond`, canvas, colorMaker.getRangeColor(Math.random())), 3700);
setTimeout(() => buildSmokeRing(`initRingThird`, canvas, colorMaker.getRangeColor(Math.random())), 8100);
setTimeout(() => buildSmokeRing(`initRingFourth`, canvas, colorMaker.getRangeColor(Math.random())), 12300);
// We also want to halt the animation when the browser page loses focus, and resume them when it regains focus
// + We could use` scrawl.stopCoreAnimationLoop()` and `scrawl.startCoreAnimationLoop()` to achieve this, but that action will stop ALL canvas animations on the page, not just this demo.
// + Instead, we can halt/resume/run our render animation, and also any existing smoke ring Tweens.
window.addEventListener('blur', () => {
scrawl.library.animationtickersnames.forEach(t => {
if (t.indexOf('ring') === 0) {
let ticker = scrawl.library.animationtickers[t];
if (ticker) ticker.halt();
}
});
render.halt();
});
// We could add the focus event listener the same as we did the blur listener, or we can add it using Scrawl-canvas functionality:
scrawl.addNativeListener('focus', () => {
scrawl.library.animationtickersnames.forEach(t => {
if(t.indexOf('ring') === 0) {
let ticker = scrawl.library.animationtickers[t];
if (ticker) ticker.resume();
}
});
render.run();
}, window);
// #### Development and testing
console.log(scrawl.library);