scrawl-canvas
Version:
Version 8.9.4 - 19 Nov 2022
261 lines (180 loc) • 5.55 kB
JavaScript
// # Demo Particles 007
// Particle Force objects: generation and functionality
// [Run code](../../demo/particles-007.html)
import * as scrawl from '../source/scrawl.js'
import { reportSpeed } from './utilities.js';
// #### Scene setup
let canvas = scrawl.library.artefact.mycanvas;
scrawl.importDomImage('#bunny');
// Define some filters
scrawl.makeFilter({
name: 'grayscale',
method: 'grayscale',
}).clone({
name: 'sepia',
method: 'sepia',
}).clone({
name: 'invert',
method: 'invert',
});
scrawl.makeFilter({
name: 'tint',
method: 'tint',
redInRed: 0.5, redInGreen: 1, redInBlue: 0.9,
greenInRed: 0, greenInGreen: 0.3, greenInBlue: 0.8,
blueInRed: 0.8, blueInGreen: 0.8, blueInBlue: 0.4,
});
scrawl.makeFilter({
name: 'pixelate',
method: 'pixelate',
tileWidth: 4,
tileHeight: 4,
});
scrawl.makeFilter({
name: 'blur',
method: 'gaussianBlur',
radius: 5,
});
scrawl.makeFilter({
name: 'matrix',
method: 'matrix',
weights: [-1, -1, 0, -1, 1, 1, 0, 1, 1],
});
// Create a Block entity which covers the entire canvas; this will act as the area in which particles will be generated by the Emitter entity
scrawl.makeBlock({
name: 'field-block',
width: '100%',
height: '100%',
method: 'none',
});
// #### Particle physics animation scene
// Create a World object which we can then assign to the particle emitter
let myWorld = scrawl.makeWorld({
name: 'demo-world',
tickMultiplier: 2,
userAttributes: [
{
key: 'brownianIntensity',
defaultValue: 2,
},
],
});
// Create some Force objects
scrawl.makeForce({
name: 'brownian-motion',
action: (particle, world, host) => {
let {load} = particle;
// @ts-expect-error
let intensity = world.brownianIntensity;
load.vectorAdd({
x: (Math.random() * intensity * 2) - intensity,
y: (Math.random() * intensity * 2) - intensity,
});
},
});
scrawl.makeForce({
name: 'mouse-disruptor',
action: (particle, world, host) => {
let {load, position} = particle;
let {here} = canvas;
if (here.active) {
let v = scrawl.requestVector(here).vectorSubtract(position);
let mag = v.getMagnitude();
if (mag < 100) load.vectorAdd(v);
scrawl.releaseVector(v);
}
},
});
const myEmitter = scrawl.makeEmitter({
name: 'field-emitter',
world: myWorld,
generationRate: 100,
particleCount: 50,
generateInArea: 'field-block',
killBeyondCanvas: true,
// Add the forces to all Particles that this Emitter entity generates
forces: ['brownian-motion', 'mouse-disruptor'],
artefact: scrawl.makePicture({
name: 'particle-image-entity',
asset: 'bunny',
width: 26,
height: 37,
handle: ['center', 'center'],
copyWidth: '100%',
copyHeight: '100%',
method: 'fill',
visibility: false,
noUserInteraction: true,
noPositionDependencies: true,
noDeltaUpdates: true,
}),
stampAction: function (artefact, particle, host) {
let [r, z, ...start] = particle.history[0];
artefact.simpleStamp(host, {start});
},
});
// #### Scene animation
// Function to display frames-per-second data, and other information relevant to the demo
const particlenames = scrawl.library.particlenames,
particle = scrawl.library.particle;
const report = reportSpeed('#reportmessage', function () {
// ParticleHistory arrays are not saved in the Scrawl-canvas library; instead we need to count them in each particle
let historyCount = 0;
particlenames.forEach(n => {
let p = particle[n];
if (p) historyCount += p.history.length;
});
return ` Particles: ${particlenames.length}
Stamps per display: ${historyCount}`;
});
// Create the Display cycle animation
scrawl.makeRender({
name: 'demo-animation',
target: canvas,
afterShow: report,
});
// #### User interaction
// For this demo we will suppress touchmove functionality over the canvas
scrawl.addNativeListener('touchmove', (e) => {
e.preventDefault();
e.returnValue = false;
}, canvas.domElement);
// Setup form observer functionality
scrawl.observeAndUpdate({
event: ['input', 'change'],
origin: '.controlItem',
target: myWorld,
useNativeListener: true,
preventDefault: true,
updates: {
brownianIntensity: ['brownianIntensity', 'float'],
},
});
scrawl.observeAndUpdate({
event: ['input', 'change'],
origin: '.controlItem',
target: myEmitter,
useNativeListener: true,
preventDefault: true,
updates: {
particleCount: ['particleCount', 'int'],
},
});
// When we set a filter on an Emitter entity, all the entity's visual output will be filtered
const filterChoice = function (e) {
e.preventDefault();
e.returnValue = false;
let val = e.target.value;
myEmitter.clearFilters();
if (val) myEmitter.addFilters(val);
};
scrawl.addNativeListener(['input', 'change'], filterChoice, '#filter');
// Set DOM form initial input values
// @ts-expect-error
document.querySelector('#filter').value = '';
// @ts-expect-error
document.querySelector('#particleCount').value = 50;
// @ts-expect-error
document.querySelector('#brownianIntensity').value = 2;
// #### Development and testing
console.log(scrawl.library);