import scrawl from '../source/scrawl.js'
import scrawl from '../source/scrawl.js'
let canvas = scrawl.library.artefact.mycanvas;
scrawl.importDomImage('#bunny');
canvas.setBase({
backgroundColor: '#000040',
});
Create entitys that we can use with the particle emitter
noXYZ
flags to false in the hope that this will speed up stamp computation timesvisibility
to false to prevent the entity appearing in the scene outside of the emitter’s controlfillStyle
and strokeStyle
colors - we’ll do that via the particle emitter and the world objectlet commonValues = {
handle: ['center', 'center'],
lineWidth: 2,
method: 'fillThenDraw',
visibility: false,
noUserInteraction: true,
noPositionDependencies: true,
noFilters: true,
noDeltaUpdates: true,
};
let wheel = scrawl.makeWheel({
name: 'particle-wheel-entity',
radius: 16,
startAngle: 20,
endAngle: -20,
includeCenter: true,
}).set(commonValues);
let block = scrawl.makeBlock({
name: 'particle-block-entity',
dimensions: [30, 20],
}).set(commonValues);
let star = scrawl.makeStar({
name: 'particle-star-entity',
radius1: 18,
radius2: 12,
points: 5,
}).set(commonValues);
let picture = scrawl.makePicture({
name: 'particle-image-entity',
asset: 'bunny',
width: 26,
height: 37,
copyWidth: '100%',
copyHeight: '100%',
}).set(commonValues);
let phrase = scrawl.makePhrase({
name: 'particle-phrase-entity',
text: 'Hello',
font: 'bold 40px Garamond, serif',
exposeText: false,
}).set(commonValues);
Create a World object which we can then assign to the particle emitter
let myWorld = scrawl.makeWorld({
name: 'demo-world',
tickMultiplier: 2,
userAttributes: [
{
key: 'strokeStyle',
defaultValue: '#b9b5df',
},
{
key: 'globalAlpha',
defaultValue: 0.2,
},
],
});
Create the particle Emitter entity
const myemitter = scrawl.makeEmitter({
name: 'use-artefact',
world: myWorld,
start: ['center', 'center'],
historyLength: 20,
generationRate: 10,
killAfterTime: 5,
As well as killing our particles beyond their lifetime limit (as set in killAfterTime
) we can also kill particles that move a certain distance beyond their initial position
killRadius: 50,
killRadiusVariation: 0,
Assign the artefact that we will be using for the particle animation
artefact: star,
rangeX: 40,
rangeFromX: -20,
rangeY: 40,
rangeFromY: -20,
rangeZ: -1,
rangeFromZ: -0.2,
Define the stampAction function
stampAction: function (artefact, particle, host) {
We will use the (semi-)random fill
color assigned to the particle when it was generated
let {history, fill} = particle;
let remaining, scale, roll, start, z;
For the stroke color, we shall give all particles the same color, as defined in our World object
let {strokeStyle, globalAlpha} = myWorld;
We will display each particle on the canvas using the entity currently assigned to our emitter’s artefact
attribute
history.forEach((p, index) => {
Entitys use Scrawl-canvas Coordinate arrays for their positioning data; we can set the start
Coordinate using a normal Array containing [x, y]
data - which we can easily extract from the particle’s history arrays like so:
[remaining, z, ...start] = p;
We change the size of this instance of the particle based on it’s z coordinate
value, by scaling the artefact.
scale = 1 + (z / 3);
if (scale > 0) {
We can roll the artefact (for added drama), calculating the roll
value based on the amount of time left before the particle gets killed.
roll = (remaining / 5) * 720;
The artefact’s simpleStamp
function bypasses much of the normal Display cycle functionality; instead it forces the artefact to stamp itself onto the host Cell’s <canvas> element immediately
artefact.simpleStamp(host, {
start,
scale,
roll,
globalAlpha,
strokeStyle,
fillStyle: fill,
});
}
});
},
});
Function to display frames-per-second data, and other information relevant to the demo
let report = function () {
let testTicker = Date.now(),
testTime, testNow, dragging,
testMessage = document.querySelector('#reportmessage');
let particlenames = scrawl.library.particlenames,
particle = scrawl.library.particle,
historyCount, kr, krv;
let worldSpeed = document.querySelector('#world-speed'),
minFill = document.querySelector('#min-fill'),
maxFill = document.querySelector('#max-fill'),
background = document.querySelector('#background'),
outlineColor = document.querySelector('#outline-color'),
opacity = document.querySelector('#opacity'),
killRadius = document.querySelector('#kill-radius'),
killRadiusVariation = document.querySelector('#kill-radius-variation'),
historyLength = document.querySelector('#historyLength'),
generationRate = document.querySelector('#generationRate');
return function () {
testNow = Date.now();
testTime = testNow - testTicker;
testTicker = testNow;
historyCount = 0;
particlenames.forEach(n => {
let p = particle[n];
if (p) historyCount += p.history.length;
});
kr = parseFloat(killRadius.value);
krv = parseFloat(killRadiusVariation.value) / 2;
testMessage.textContent = `Screen refresh: ${Math.ceil(testTime)}ms; fps: ${Math.floor(1000 / testTime)}
Particles: ${particlenames.length}, generationRate: ${generationRate.value}, historyLength: ${historyLength.value}
Stamps per display: ${historyCount}
Background color: ${background.value}, World speed (tickMultiplier): ${worldSpeed.value}
Outline color (strokeStyle): ${outlineColor.value}, Opacity (globalAlpha): ${opacity.value}
Kill radius: from ${(kr - krv) > 0 ? kr - krv : 0}px to ${kr + krv}px
Minimum fill color: ${minFill.value}, Maximum fill color: ${maxFill.value} - (fillStyle)`;
};
}();
let mouseCheck = function () {
let active = false;
return function () {
if (canvas.here.active !== active) {
active = canvas.here.active;
myemitter.set({
lockTo: (active) ? 'mouse' : 'start'
});
}
};
}();
Create the Display cycle animation
scrawl.makeRender({
name: 'demo-animation',
target: canvas,
commence: mouseCheck,
afterShow: report,
});
Setting (restricting) the range of colors assigned to particles when they are generated.
const setLowColor = function () {
const selector = document.querySelector('#min-fill'),
colorFactory = myemitter.fillColorFactory;
return function () {
colorFactory.setMinimumColor(selector.value);
}
}();
scrawl.addNativeListener(['input', 'change'], setLowColor, '#min-fill');
const setHighColor = function () {
const selector = document.querySelector('#max-fill'),
colorFactory = myemitter.fillColorFactory;
return function () {
colorFactory.setMaximumColor(selector.value);
}
}();
scrawl.addNativeListener(['input', 'change'], setHighColor, '#max-fill');
Change the artefact to be used in the animation
const useArtefact = function () {
const selector = document.querySelector('#artefact');
let choice, val;
return function () {
val = selector.value;
choice = false;
switch (val) {
case 'star' :
choice = star;
break;
case 'wheel' :
choice = wheel;
break;
case 'block' :
choice = block;
break;
case 'picture' :
choice = picture;
break;
case 'phrase' :
choice = phrase;
break;
}
if (choice) {
myemitter.set({
artefact: choice,
});
}
}
}();
scrawl.addNativeListener(['input', 'change'], useArtefact, '#artefact');
Setup other form observer functionality
scrawl.observeAndUpdate({
event: ['input', 'change'],
origin: '.controlItem',
target: myWorld,
useNativeListener: true,
preventDefault: true,
updates: {
'outline-color': ['strokeStyle', 'raw'],
'world-speed': ['tickMultiplier', 'float'],
'opacity': ['globalAlpha', 'float'],
},
});
scrawl.observeAndUpdate({
event: ['input', 'change'],
origin: '.controlItem',
target: myemitter,
useNativeListener: true,
preventDefault: true,
updates: {
generationRate: ['generationRate', 'int'],
historyLength: ['historyLength', 'int'],
'kill-radius': ['killRadius', 'int'],
'kill-radius-variation': ['killRadiusVariation', 'int'],
},
});
scrawl.observeAndUpdate({
event: ['input', 'change'],
origin: '.controlItem',
target: canvas,
useNativeListener: true,
preventDefault: true,
updates: {
background: ['backgroundColor', 'raw'],
},
});
document.querySelector('#min-fill').value = '#000000';
document.querySelector('#max-fill').value = '#ffffff';
document.querySelector('#outline-color').value = '#F0F8FF';
document.querySelector('#background').value = '#000040';
document.querySelector('#world-speed').value = 2;
document.querySelector('#opacity').value = 0.2;
document.querySelector('#generationRate').value = 10;
document.querySelector('#historyLength').value = 20;
document.querySelector('#kill-radius').value = 50;
document.querySelector('#kill-radius-variation').value = 0;
document.querySelector('#artefact').value = 'star';
console.log(scrawl.library);