scrawl-canvas
Version:
Version 8.9.4 - 19 Nov 2022
202 lines (149 loc) • 5.62 kB
JavaScript
// # Demo Canvas 019a
// Gradient and Color factories - transparency - alternative approach using Cells instead of images
// [Run code](../../demo/canvas-019a.html)
import {
library as L,
importDomImage,
makeFilter,
makePicture,
createImageFromEntity,
makeRadialGradient,
makeBlock,
makeRender,
addNativeListener,
} from '../source/scrawl.js';
import { reportSpeed } from './utilities.js';
// #### Scene setup
const canvases = L.canvas;
// Import the images we defined in the DOM (in <img> elements)
importDomImage('.places');
// For this scene, we'll build a data structure which we can iterate over, to build the entitys, assets and gradients required by the scene
const data = [
{
canvas: canvases['hackney'],
image: 'hackney-bg',
transparency: 'transparent',
},
{
canvas: canvases['heathrow'],
image: 'heathrow-bg',
transparency: 'rgba(0,0,0,0)',
},
{
canvas: canvases['kingston'],
image: 'kingston-bg',
transparency: '#00000000',
},
{
canvas: canvases['burglary'],
image: 'burglary-bg',
transparency: '#0000',
},
];
// This array will hold a set of functions which we will invoke in turn at the start of each Display cycle
const checkFunctions = [];
// The blur filter is temporary - we use it once on each image to generate a blurred version of that image
// + We do it this way because the blur filter is computationally very expensive - capturing a blurred version of the image is a lot better for end user power consumption
makeFilter({
name: 'blur',
method: 'gaussianBlur',
radius: 20,
});
// Iterate through our data array to create Scrawl-canvas objects
data.forEach(scene => {
// Firstly, build the blurred image in its own cell
// + We only need to compile this once, thus don't include the cell in any part of the Display cycle
let mycell = scene.canvas.buildCell({
name: `${scene.image}-blurred-cell`,
dimensions: ['100%', '100%'],
cleared: false,
compiled: false,
shown: false,
});
makePicture({
name: `${scene.image}-blurred-image`,
group: `${scene.image}-blurred-cell`,
asset: scene.image,
dimensions: ['100%', '100%'],
copyDimensions: ['100%', '100%'],
filters: ['blur'],
method: 'fill',
});
// The new Cell's compile action is blocking, because the blur filter takes a long time to complete. To get something up on the screen we can defer the compile action until after the current RAF completes by putting it in a setTimeout invocation.
setTimeout(() => mycell.compile(), 0);
// Next, build a gradient using transparency and apply it in a second Block which displays in the original canvas
makeRadialGradient({
name: `${scene.image}-gradient`,
start: ['50%', '50%'],
end: ['50%', '50%'],
startRadius: '5%',
endRadius: '20%',
colors: [
[0, scene.transparency],
[999, 'lightgray']
],
});
const gradientBlock = makeBlock({
name: `${scene.image}-gradient-block`,
group: scene.canvas.base.name,
start: ['center', 'center'],
handle: ['center', 'center'],
dimensions: ['200%', '200%'],
fillStyle: `${scene.image}-gradient`,
lockFillStyleToEntity: true,
});
// Now we can draw our blurred image into the scene
// + We use some compositing magic so it only appears where the gradient is not transparent
makePicture({
name: `${scene.image}-blurred-block`,
group: scene.canvas.base.name,
asset: `${scene.image}-blurred-cell`,
dimensions: ['100%', '100%'],
copyDimensions: ['100%', '100%'],
method: 'fill',
globalCompositeOperation: 'source-atop',
});
// Lastly, display the original image in the canvas
// + Again, we use compositing magic to draw on the bits missed by the blurred image
makePicture({
name: `${scene.image}-original-image`,
group: scene.canvas.base.name,
asset: scene.image,
dimensions: ['100%', '100%'],
copyDimensions: ['100%', '100%'],
method: 'fill',
globalCompositeOperation: 'destination-over',
});
// Check whether the mouse cursor is hovering over this canvas, and update the filter Block entity's positioning accordingly
const f = () => {
const here = scene.canvas.here;
return function () {
gradientBlock.set({
lockTo: (here.active) ? 'mouse' : 'start',
});
}
};
checkFunctions.push(f());
});
// #### Scene animation
// Function to display frames-per-second data, and other information relevant to the demo
const report = reportSpeed('#reportmessage');
// Create the Display cycle animation
makeRender({
name: 'demo-animation',
target: [canvases['hackney'], canvases['heathrow'], canvases['kingston'], canvases['burglary']],
observer: true,
commence: () => checkFunctions.forEach(f => f()),
});
makeRender({
name: 'demo-speed',
noTarget: true,
afterShow: report,
});
// For this demo we will suppress touchmove functionality over the canvas
addNativeListener('touchmove', (e) => {
e.preventDefault();
e.returnValue = false;
}, [canvases['hackney'].domElement, canvases['heathrow'].domElement, canvases['kingston'].domElement, canvases['burglary'].domElement]);
// #### Development and testing
console.log(L);