different scenes with different animation won't play in p5 - p5.js

for school we need to make a sequence of animations in p5.
I wrote the code below, but it doesn't show any of my animations.
please help me...
let currentScene = 0;
let prevTime = 0;
let interval1 = 5000;
let interval2 = 8500;
let interval3 = 10000;
let interval4 = 11500;
let interval5 = 13000;
let interval6 = 15500;
let interval7 = 18000;
let interval8 = 20500;
let interval9 = 25000;
function setup() {
createCanvas(400, 400);
prevTime = millis();
}
function draw() {
if (millis() > prevTime + interval1) {
currentScene = 1;
}
if (millis() > prevTime + interval2) {
currentScene = 2;
}
if (millis() > prevTime + interval3) {
currentScene = 3;
}
if (millis() > prevTime + interval4) {
currentScene = 4;
}
if (millis() > prevTime + interval5) {
currentScene = 5;
}
if (millis() > prevTime + interval6) {
currentScene = 6;
}
if (millis() > prevTime + interval7) {
currentScene = 7;
}
if (millis() > prevTime + interval8) {
currentScene = 8;
}
if (millis() > prevTime + interval9) {
currentScene = 0;
prevTime = millis();
}
switch (currentScene) {
case 0:
Scene1();
break;
case 1:
Scene2();
break;
case 2:
Scene3();
break;
case 3:
Scene4();
break;
case 4:
Scene5();
break;
case 5:
Scene6();
break;
case 6:
Scene7();
break;
case 7:
Scene8();
break;
case 8:
Scene9();
break;
}
}
const Scene1 = () => {
const amount = 35;
let color = ["white", "black"];
function setup() {
createCanvas(400, 400);
}
function draw() {
background(random(color));
strokeWeight(2);
frameRate(5);
fill("black");
textStyle(BOLD);
textSize(140);
textAlign(CENTER);
text("WAKE", 200, 130);
textSize(300);
text("UP", 200, 380);
const grid = width / amount;
for (let a = 0; a < amount; a++) {
for (let b = 0; b < amount; b++) {
kruis(a * grid, b * grid, grid);
}
}
}
const kruis = (x, y, h) => {
push();
translate(x, y, h);
fill("white");
textSize(random(10, 15));
text("Z", 5, 10);
pop();
};
};
const Scene2 = () => {
let t = 0;
const increment = 0.8;
maxEllipse = 100;
angle = 0;
scaleValue = 400;
snelheid = 10;
function setup() {
createCanvas(400, 400);
}
function draw() {
background(0, 20);
const r = 255 * noise(t + 10);
const g = 255 * noise(t + 15);
const b = 255 * noise(t + 20);
for (let i = 0; i < maxEllipse; i++) {
push();
const y = sin(angle + i) * scaleValue;
const space = width / maxEllipse;
const x = (i + 0.5) * space;
stroke(r, g, b);
line(x, y, 400, 0);
pop();
}
angle += snelheid;
t += increment;
}
};
const Scene3 = () => {
let t = 0;
const increment = 0.8;
maxEllipse = 100;
angle = 0;
scaleValue = 400;
snelheid = 10;
function setup() {
createCanvas(400, 400);
}
function draw() {
background(0, 20);
const r = 255 * noise(t + 10);
const g = 255 * noise(t + 15);
const b = 255 * noise(t + 20);
for (let i = 0; i < maxEllipse; i++) {
push();
const y = sin(angle + i) * scaleValue;
const space = width / maxEllipse;
const x = (i + 0.5) * space;
stroke(r, g, b);
line(x, y, 400, 400);
pop();
}
angle += snelheid;
t += increment;
}
};
Not all animations are in it yet (it should be 9 in total), but the scenes I've already placed in it don't do anything.
I've also tried to remove the draw and setup function in the scenes, in the first scene this works, but afterwards it only shows a static image.

This is a good start, make other scenes like this.
let currentScene = 0;
let prevTime = 0;
let interval1 = 5000;
let interval2 = 8500;
let interval3 = 10000;
let interval4 = 11500;
let interval5 = 13000;
let interval6 = 15500;
let interval7 = 18000;
let interval8 = 20500;
let interval9 = 25000;
function setup() {
createCanvas(400, 400);
prevTime = millis();
}
function draw() {
if (millis() > prevTime + interval1) {
currentScene = 1;
}
if (millis() > prevTime + interval2) {
currentScene = 2;
}
if (millis() > prevTime + interval3) {
currentScene = 3;
}
if (millis() > prevTime + interval4) {
currentScene = 4;
}
if (millis() > prevTime + interval5) {
currentScene = 5;
}
if (millis() > prevTime + interval6) {
currentScene = 6;
}
if (millis() > prevTime + interval7) {
currentScene = 7;
}
if (millis() > prevTime + interval8) {
currentScene = 8;
}
if (millis() > prevTime + interval9) {
currentScene = 0;
prevTime = millis();
}
switch (currentScene) {
case 0:
Scene1();
break;
case 1:
Scene2();
break;
case 2:
Scene3();
break;
case 3:
Scene4();
break;
case 4:
Scene5();
break;
case 5:
Scene6();
break;
case 6:
Scene7();
break;
case 7:
Scene8();
break;
case 8:
Scene9();
break;
}
}
function Scene1()
{
const amount = 35;
var color = ["white", "black"];
var kruis = (x, y, h) =>
{
push();
translate(x, y, h);
fill("white");
textSize(random(10, 15));
text("Z", 5, 10);
pop();
}
background(random(color));
strokeWeight(2);
frameRate(5);
fill("black");
textStyle(BOLD);
textSize(140);
textAlign(CENTER);
text("WAKE", 200, 130);
textSize(300);
text("UP", 200, 380);
const grid = width / amount;
for (let a = 0; a < amount; a++) {
for (let b = 0; b < amount; b++) {
kruis(a * grid, b * grid, grid);
}
}
}
function Scene2()
{
let t = 0;
const increment = 0.8;
maxEllipse = 100;
angle = 0;
scaleValue = 400;
snelheid = 10;
background(0, 20);
const r = 255 * noise(t + 10);
const g = 255 * noise(t + 15);
const b = 255 * noise(t + 20);
for (let i = 0; i < maxEllipse; i++)
{
push();
const y = sin(angle + i) * scaleValue;
const space = width / maxEllipse;
const x = (i + 0.5) * space;
stroke(r, g, b);
line(x, y, 400, 0);
pop();
}
angle += snelheid;
t += increment;
}
function Scene3()
{
let t = 0;
const increment = 0.8;
maxEllipse = 100;
angle = 0;
scaleValue = 400;
snelheid = 10;
background(0, 20);
const r = 255 * noise(t + 10);
const g = 255 * noise(t + 15);
const b = 255 * noise(t + 20);
for (let i = 0; i < maxEllipse; i++) {
push();
const y = sin(angle + i) * scaleValue;
const space = width / maxEllipse;
const x = (i + 0.5) * space;
stroke(r, g, b);
line(x, y, 400, 400);
pop();
}
angle += snelheid;
t += increment;
}

Related

p5.js: Random movement of drawing tool

I coded this drawing tool:
var a = 0;
function setup() {
createCanvas(windowWidth, windowHeight);
background(0, 0, 255);
}
function draw() {
fill(0, 255, 255, random(255));
translate(mouseX, mouseY);
rotate(a);
textSize(120);
textAlign(CENTER, CENTER);
text('*', 0, 0);
rotate(a);
a = a + 0.08;
}
<script src="https://cdn.jsdelivr.net/npm/p5#1.4.1/lib/p5.js"></script>
Now I would like to have a movement, without doing it with the cursor. I have thought about something like this:
var a;
function centerCanvas() {
var x = (windowWidth - width) / 2;
var y = (windowHeight - height) / 2;
a.position(x, y);
}
function setup() {
a = createCanvas(windowHeight, windowHeight);
centerCanvas();
}
function draw() {
fill(0, 255, 255, random(255));
var x =
windowHeight / 2 +
sin(frameCount * 0.01) * cos(frameCount * 0.04) * windowHeight / 3;
var y =
windowHeight / 2 +
cos(frameCount * 0.01) * sin(frameCount * 0.04) * windowHeight / 3;
rotate(a);
textSize(120);
textAlign(CENTER, CENTER);
text('*', 0, 0);
rotate(a);
a = a + 0.08;
}
<script src="https://cdn.jsdelivr.net/npm/p5#1.4.1/lib/p5.js"></script>
Unfortunately, it doesn't work. It also would be cool if the movement would be random inside the canvas. Does anyone know how to do it?
This (almost) worked for me:
let wander;
function setup() {
createCanvas(windowWidth, windowHeight);
background(0, 0, 0);
wander = new Vehicle(0, 0);
}
function draw() {
wander.wander();
wander.update();
wander.edges();
wander.show();
}
class Vehicle {
constructor(x, y) {
this.pos = createVector(x, y);
this.vel = createVector(1, 0);
this.acc = createVector(0, 0);
this.maxSpeed = 4;
this.maxForce = 0.2;
this.r = 16;
this.wanderTheta = PI / 2;
this.xoff = 0;
}
wander() {
let angle = noise(this.xoff) * TWO_PI * 2;
let steer = p5.Vector.fromAngle(angle);
steer.setMag(this.maxForce);
this.applyForce(steer);
this.xoff += 0.01;
}
applyForce(force) {
this.acc.add(force);
}
update() {
this.vel.add(this.acc);
this.vel.limit(this.maxSpeed);
this.pos.add(this.vel);
this.acc.set(0, 0);
}
show() {
stroke(255);
strokeWeight(2);
fill(255);
push();
translate(this.pos.x, this.pos.y);
rotate(this.vel.heading());
textAlign(CENTER, CENTER);
textSize(120);
fill(0, 255, 255, random(255));
text("*", 0, 0);
pop();
}
edges() {
let hitEdge = false;
if (this.pos.x > width + this.r) {
this.pos.x = -this.r;
hitEdge = true;
} else if (this.pos.x < -this.r) {
this.pos.x = width + this.r;
hitEdge = true;
}
if (this.pos.y > height + this.r) {
this.pos.y = -this.r;
hitEdge = true;
} else if (this.pos.y < -this.r) {
this.pos.y = height + this.r;
hitEdge = true;
}
}
}
<script src="https://cdn.jsdelivr.net/npm/p5#1.4.1/lib/p5.js"></script>
Using P5.js's noise function is a really accurate way to represent wandering. With a little tinkering you can get the effect you want.
EDIT
This is your (debugged) code:
let a;
let r;
function centerCanvas() {
var x = (windowWidth - width) / 2;
var y = (windowHeight - height) / 2;
a.position(x, y);
}
function setup() {
a = createCanvas(windowHeight, windowHeight);
centerCanvas();
}
function draw() {
fill(0, 255, 255, random(255));
const x = windowHeight / 2 +
sin(frameCount * 0.01) * cos(frameCount * 0.04) * windowHeight / 3;
const y = windowHeight / 2 +
cos(frameCount * 0.01) * sin(frameCount * 0.04) * windowHeight / 3;
translate(x, y);
rotate(a * 0.08);
textSize(50);
textAlign(CENTER, CENTER);
text('*', 0, 0);
a += 1;
}
<script src="https://cdn.jsdelivr.net/npm/p5#1.4.1/lib/p5.js"></script>
How about this?
var a = 0;
var x = 0;
var y = 0;
var delta = 1;
function setup() {
createCanvas(windowWidth, windowHeight);
background(0, 0, 255);
}
function draw() {
fill(0, 255, 255, random(255));
x = x + 2 + Math.random();
y = y + 0.5 + Math.random();
x = x * delta;
y = y * delta;
if (x < 0) delta = -1;
translate((x) % windowWidth,(y) % windowHeight);
rotate(a);
textSize(120);
textAlign(CENTER, CENTER);
text('*', 0, 0);
rotate(a);
a = a + 0.08;
}
<script src="https://cdn.jsdelivr.net/npm/p5#1.4.1/lib/p5.js"></script>

moving a sketch from draw to setup

Im playing around with the prime spiral code from Dan Shiffman:
https://www.youtube.com/watch?v=a35KWEjRvc0&t=716s&ab_channel=TheCodingTrain
I need to use this code as a background to another sketch, but due to the moving nature of the spiral its impossible that the spiral will not draw ontop of the other sketch, even with p5.layers.
I am therefore searching for a way to refactor this code into something that draw all the figures at once so use it as a background.
My idea is to create some kind of for loop in setup, but i am afraid its to big of nut to crack for me.
let x, y;
let step = 1;
let stepSize = 20;
let numSteps = 1;
let state = 0;
let turnCounter = 1;
let totalSteps;
let offset = 0;
function isPrime(value) {
if (value == 1) return false;
for (let i = 2; i <= sqrt(value); i++) {
if (value % i == 0) {
return false;
}
}
return true;
}
function setup() {
createCanvas(500, 500);
const cols = width / stepSize;
const rows = height / stepSize;
totalSteps = cols * rows;
x = width / 2;
y = height / 2;
background(0);
}
function draw() {
primeSpiral(20, 1)
primeSpiral(30, 200)
incrementStep();
noStroke();
}
function incrementStep()
{
switch (state) {
case 0:
x += stepSize;
break;
case 1:
y -= stepSize;
break;
case 2:
x -= stepSize;
break;
case 3:
y += stepSize;
break;
}
if (step % numSteps == 0) {
state = (state + 1) % 4;
turnCounter++;
if (turnCounter % 2 == 0) {
numSteps++;
}
}
step++;
if (step > totalSteps) {
noLoop();
}
}
function primeSpiral(offset, color){
if (!isPrime(step+offset)) {
//might put something here
} else {
let r = stepSize * 0.5;
fill(color, 99, 164);
push();
translate(x, y);
rotate(-PI / 4);
triangle(-r, +r, 0, -r, +r, +r);
pop();
}
}
You can move the code from draw() to the loop in setup(), here is example:
...
function setup() {
createCanvas(500, 500);
const cols = width / stepSize;
const rows = height / stepSize;
totalSteps = cols * rows;
x = width / 2;
y = height / 2;
background(0);
for (let i = 0; i<100; i++) { // 100 is the number of processed frames, you can change it as you need
primeSpiral(20, 1)
primeSpiral(30, 200)
incrementStep();
noStroke();
}
}
function draw() { // now draw is empty and you can place new code here
}
...

p5.js change object colour after X frames

I would like to change the fill colour of an object over time. Is there a way to change the fill colour of an object after X frames?
I am learning about constructors, and I am thinking that in the code's updateParticle function, after this.age counts to 'X', the fill colour of the ellipse could change.
'''
function Particle(x, y, xSpeed, ySpeed, size, colour) {
this.x = x;
this.y = y;
this.xSpeed = xSpeed;
this.ySpeed = ySpeed;
this.size = size;
this.colour = colour;
this.age = 0;
this.drawParticle = function() {
fill(this.colour);
noStroke();
ellipse(this.x, this.y, this.size);
}
this.updateParticle = function() {
this.x += this.xSpeed;
this.y += this.ySpeed;
this.age++;
}
}
function Emitter(x, y, xSpeed, ySpeed, size, colour) {
this.x = x;
this.y = y;
this.xSpeed = xSpeed;
this.ySpeed = ySpeed;
this.size = size;
this.colour = colour;
this.particles = [];
this.startParticles = [];
this.lifetime = 0;
this.addParticle = function() {
var p = new Particle(random(this.x - 10, this.x + 10),
random(this.y - 10, this.y + 10),
random(this.xSpeed - 0.4, this.xSpeed + 0.4),
random(this.ySpeed - 5, this.ySpeed - 1),
random(this.size - 4, this.size + 40),
this.colour);
return p;
}
this.startEmitter = function(startParticles, lifetime) {
this.startParticles = startParticles;
this.lifetime = lifetime;
for (var i = 0; i < startParticles; i++) {
this.particles.push(this.addParticle());
}
}
this.updateParticles = function() {
var deadParticles = 0
for (var i = this.particles.length - 1; i >= 0; i--) {
this.particles[i].drawParticle();
this.particles[i].updateParticle();
if (this.particles[i].age > random(0, this.lifetime)) {
this.particles.splice(i, 1);
deadParticles++;
}
}
if (deadParticles > 0) {
for (var i = 0; i < deadParticles; i++) {
this.particles.push(this.addParticle());
}
}
}
}
var emit;
function setup() {
createCanvas(800, 600);
emit = new Emitter(width / 2, height - 100, 0, -1, 10, color(200, 0, 200, 50));
emit.startEmitter(600, 4000);
}
function draw() {
background(200);
emit.updateParticles();
}
'''
Well you could just:
if(frameCount % 30 == 0){ // % 30 is the remainder of num / 30, so 4 % 3 = 1, since 3 / 3 = 0 And 4 / 3 = 3.33
fill("lime") // these are just preset colors in p5.js AND css lime == rgb(0,255,0)
} else {
fill('darkred')
}
or you could also do it with for example a switch statement: using background for no reason
switch(MY_frameCount){
case 1:
background('blue')
break
case 2:
background("darkgreen")
break
case 376:
background(crimson)
// break
}
MY_frameCount ++
or:
if(Math.random() < 0.1){
fill(Math.random() * 255, Math.random() * 255, Math.random() * 255)
} // this should on average fill with random color every 10 frames

Image transformation matrix

I want to practice pixel manipulation with matrix for extract an image from another.
This is what I have done with css transformation matrix :
https://www.noelshack.com/2017-18-1493893008-capture-2.png
With the Left image 'L' I have place 4 points around the image and in the right image 'R' I find the content of the transformation.
For that i use the property transform of the css but i want to do the manipulation manually.
CSS version :
matrix3d(1.5456325781948308,1.6561987730956724,0,0.0012239101773909712,-0.4663849104791486,2.218793881308064,0,0.0009095626603861196,0,0,1,0,12.247969030166722,-17.754955132517754,0,0.9951722722714726)
Matrix 'M':
[[1.5456325781948308, 1.6561987730956724, 0, 0.0012239101773909712],
[-0.4663849104791486, 2.218793881308064, 0, 0.0009095626603861196],
[0, 0, 1, 0],
[12.247969030166722, -17.754955132517754, 0, 0.9951722722714726]]
I want to know for each pixel in the image R what are their pixel related position in the image L.
For example (0,0) in R is (52,203) in R.
For that i do this calculation.
M * P = P'
P is the pixel position in R image
P' is the pixel position in L image
P matrix is define like that:
[[x],
[y],
[0],
[1]]
So for the 0,0 position, I do this :
[[1.5456325781948308, 1.6561987730956724, 0, 0.0012239101773909712],
[-0.4663849104791486, 2.218793881308064, 0, 0.0009095626603861196],
[0, 0, 1, 0],
[12.247969030166722, -17.754955132517754, 0, 0.9951722722714726]]
X
[[0],
[0],
[0],
[1]]
=
[[0.0012239101773909712],
[0.0009095626603861196],
[0],
[0.9951722722714726]]
This is the result, but the 2 first component :
(0.0012239101773909712, 0.0009095626603861196)
is too smaller than expected. can you help me to find the problem.
scincerly,
MatrixCuriosity.
These are homogeneous coordinates. So given some [x1, y1, z1, 1] as input you obtain some [x2, y2, z2, w2] but the actual position they describe is [x2/w2, y2/w2, z2/w2], i.e. you have to divide by the last coordinate.
But this doesn't lead to the result you expected. Nor does replacing the matrix with its adjunct (or equivalently inverse), nor its transpose. Both of these are conventions that are easy to get wrong, so without spending too much thought about which version you actually have and should have, trying all four alternatives (with and without adjunct, with and without transpose) solves a huge number of trivial problems.
But not yours. So my next best bet would be that the coordinates you expect are measured from some corner of the image, while the CSS property transform-origin is at it's initial value of 50% 50% 0 so the origin of the coordinate system is in fact in the center of the object.
Actually sharing the HTML and CSS for this might have allowed me to verify this assumption. Now you have to check whether this applies to you. I remember that when I last created a projective image transformation demo to answer a question about finding the transform, I deliberately set transform-origin: 0 0; (and the various vendor-prefixed versions of this) to avoid such problems.
Thanks a lot MvG.
I follow your link and I find what I want [https://math.stackexchange.com/a/339033]
Just one thing, I have to invert the C matrix to find the pixel related L<-R
I share my code for give an idea of what you have to do
You can find my implementation in the function computeMat()
<style>
body {
touch-action: none;
overflow-y: hidden;
}
#canvas_toeic
{
position:absolute;
top:0;
left:0;
}
</style>
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/mathjs/3.12.2/math.min.js"></script>
</head>
<body>
<canvas id="canvas_toeic" width="600" height="400">
</canvas>
<script type="text/javascript">
var image = new Image();
image.src = 'image.jpg';
image.onload = function() {
var c = document.getElementById("canvas_toeic");
var ratio = image.width / image.height;
var canvasWidth = document.body.clientWidth;
var canvasHeight = canvasWidth / ratio;
if(document.body.clientHeight < canvasHeight)
{
canvasHeight = document.body.clientHeight;
canvasWidth = canvasHeight * ratio;
}
var canvasLargeur = canvasWidth;
var canvasLongueur = canvasHeight;
if(canvasLargeur < canvasHeight) {
canvasLargeur = canvasHeight;
canvasLongueur = canvasWidth;
}
var canvasPixelRatio = canvasLargeur / image.width;
c.setAttribute("width", canvasWidth);
c.setAttribute("height", canvasHeight);
var ctx = c.getContext("2d");
var idPoint = -1;
var points = [];
for(var i = 0; i < 4; i++)
points[i] = {x:0, y:0};
var marginImage = Math.round(40 * canvasPixelRatio);
points[0].x = marginImage;
points[0].y = marginImage;
points[1].x = marginImage;
points[1].y = canvasHeight - marginImage;
points[2].x = canvasWidth - marginImage;
points[2].y = canvasHeight - marginImage;
points[3].x = canvasWidth - marginImage;
points[3].y = marginImage;
function draw(points) {
console.log("draw");
// Fond
ctx.fillStyle = "#222";
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
ctx.drawImage(image, marginImage, marginImage, canvasWidth - marginImage * 2, canvasHeight - marginImage * 2); // this fait référence à l'objet courant (=image)
if(idPoint == -1)
ctx.lineWidth = 3 * canvasPixelRatio;
else
ctx.lineWidth = 5 * canvasPixelRatio;
ctx.beginPath(); // Début du chemin
ctx.lineJoin = "round";
ctx.lineCap = "round";
ctx.strokeStyle = "rgba(64, 128, 255, 0.5)";
ctx.moveTo(points[0].x, points[0].y); // Le tracé part du point 50,50
for(var i = 0; i < 4; i++)
ctx.lineTo(points[i].x, points[i].y); // Un segment est ajouté vers 200,200
ctx.closePath(); // Fermeture du chemin (facultative)
ctx.stroke();
for(var i = 0; i < 4; i++)
{
var radius = 30 * canvasPixelRatio;
if(idPoint == i)
radius = 60 * canvasPixelRatio;
ctx.beginPath();
ctx.arc(points[i].x, points[i].y, radius, 0, Math.PI*2, true);
ctx.strokeStyle = "#FF8800";
ctx.fillStyle = "rgba(255, 128, 0, 0.5)";
ctx.fill();
ctx.stroke();
}
if(idPoint != -1)
{
var zoomWidth = canvasWidth / 3;
var zoomHeight = canvasHeight / 3;
var zoomMargin = 5;
var zoomAroundWidth = 50;
var zoomAroundHeight = zoomAroundWidth / ratio;
var positionMouse = points[idPoint];
var imagePositionX = (positionMouse.x - marginImage) / (canvasWidth - marginImage * 2) * image.width;
var imagePositionY = (positionMouse.y - marginImage) / (canvasHeight - marginImage * 2) * image.height;
var zoomX = 0;
var zoomY = 0;
if(imagePositionX < image.width / 2)
zoomX = canvasWidth - zoomWidth;
if(imagePositionY < image.height / 2)
zoomY = canvasHeight - zoomHeight;
ctx.fillStyle = "#F08";
ctx.fillRect(zoomX, zoomY, zoomWidth, zoomHeight);
ctx.drawImage(image, imagePositionX - zoomAroundWidth, imagePositionY - zoomAroundHeight, zoomAroundWidth * 2, zoomAroundHeight * 2, zoomX + zoomMargin, zoomY + zoomMargin, zoomWidth - zoomMargin * 2, zoomHeight - zoomMargin * 2);
ctx.lineWidth = 3 * canvasPixelRatio;
ctx.beginPath();
ctx.lineJoin = "round";
ctx.lineCap = "round";
ctx.strokeStyle = "rgba(255, 0, 0, 0.5)";
ctx.moveTo(zoomX, zoomY + zoomHeight / 2);
ctx.lineTo(zoomX + zoomWidth, zoomY + zoomHeight / 2);
ctx.moveTo(zoomX + zoomWidth / 2, zoomY);
ctx.lineTo(zoomX + zoomWidth / 2, zoomY + zoomHeight);
ctx.closePath();
ctx.stroke();
}
}
function nearPoint(points, x, y)
{
var radiusDetection = 60 * canvasPixelRatio;
var distances = [];
for(i = 0; i < 4; i++) {
var mx = x - points[i].x;
var my = y - points[i].y;
distances[i] = Math.sqrt(mx * mx + my * my);
}
minI = 0;
minD = distances[0];
for(i = 1; i < 4; i++)
{
if(minD > distances[i])
{
minD = distances[i];
minI = i;
}
}
if(minD <= radiusDetection)
return minI;
return -1;
}
function getTouchPosition(e)
{
var target = null;
var mouse = null;
if(e.changedTouches != undefined)
{
var touches = e.changedTouches;
mouse = touches[0];
target = touches[0].target;
}
else if(e.originalTarget != undefined)
{
mouse = e;
target = e.originalTarget;
}
var coordX = 0;
var coordY = 0;
if(mouse.layerX != undefined)
{
coordX = mouse.layerX;
coordY = mouse.layerY;
}
else
{
coordX = mouse.pageX;
coordY = mouse.pageY;
}
var x = coordX - target.offsetLeft;
var y = coordY - target.offsetTop;
if(x < 0) x = 0;
if(y < 0) y = 0;
if(x >= canvasWidth) x = canvasWidth - 1;
if(y >= canvasHeight) y = canvasHeight - 1;
return {'x':x, 'y':y};
}
function mouseDown(e)
{
var position = getTouchPosition(e);
idPoint = nearPoint(points, position.x, position.y);
if(idPoint == -1)
{
if(position.x < marginImage * 3 && position.y < marginImage * 3)
{
computeMat();
}
}
}
function mouseUp(e)
{
if(idPoint != -1)
{
idPoint = -1;
draw(points);
}
}
function mouseMove(e)
{
if(idPoint != -1)
{
var position = getTouchPosition(e);
points[idPoint].x = position.x;
points[idPoint].y = position.y;
draw(points);
}
}
function cancelDefault(e)
{
e.preventDefault();
}
function matStep12(pts)
{
var matP = [
[pts[0].x, pts[1].x, pts[2].x],
[pts[0].y, pts[1].y, pts[2].y],
[1, 1, 1]
];
var vecP = [[pts[3].x], [pts[3].y], [1]];
var matPi = math.inv(matP);
var vecPi = math.multiply(matPi, vecP);
var result = [
[pts[0].x * vecPi[0][0], pts[1].x * vecPi[1][0], pts[2].x * vecPi[2][0]],
[pts[0].y * vecPi[0][0], pts[1].y * vecPi[1][0], pts[2].y * vecPi[2][0]],
[vecPi[0][0], vecPi[1][0], vecPi[2][0]]
];
return result;
}
function distance(a, b)
{
var mx = b.x - a.x;
var my = b.y - a.y;
return Math.sqrt(mx * mx + my * my);
}
function computeMat()
{
var pts = getPointRelativePosition();
var widthT = distance(pts[0], pts[3]);
var widthB = distance(pts[1], pts[2]);
var heightL = distance(pts[0], pts[1]);
var heightR = distance(pts[2], pts[3]);
var maxWidth = (widthT > widthB) ? widthT : widthB;
var maxHeight = (heightL > heightR) ? heightL : heightR;
var imgWidth = Math.round(maxWidth);
var imgHeight = Math.round(maxHeight);
var matA = matStep12(pts);
var matB = matStep12([{x:0,y:0}, {x:0,y:maxHeight}, {x:maxWidth,y:maxHeight}, {x:maxWidth,y:0}]);
var matC = math.multiply(matB, math.inv(matA));
var matCi = math.inv(matC);
console.log('width:' + imgWidth + ', height:' + imgHeight);
printMat(matC);
// construct image with transformation matrice
imageData = ctx.createImageData(imgWidth, imgHeight);
var tempCanvas = document.createElement('canvas');
var tempCtx = tempCanvas.getContext('2d');
tempCanvas.width = image.width;
tempCanvas.height = image.height;
tempCtx.drawImage(image, 0, 0, image.width, image.height);
var imageDataSrc = tempCtx.getImageData(0, 0, image.width, image.height);
var mz = [matCi[0][2], matCi[1][2], matCi[2][2]];
for(var y = 0; y < imgHeight; y++)
{
var my = [matCi[0][1] * y, matCi[1][1] * y, matCi[2][1] * y];
var offsetY = y * imgWidth;
for(var x = 0; x < imgWidth; x++)
{
var mx = [matCi[0][0] * x, matCi[1][0] * x, matCi[2][0] * x];
var cx = mx[0] + my[0] + mz[0];
var cy = mx[1] + my[1] + mz[1];
var cz = mx[2] + my[2] + mz[2];
var px = Math.round(cx / cz);
var py = Math.round(cy / cz);
if(px < 0.0 || py < 0.0 || px >= image.width || py >= image.height)
{
imageData.data[pixelIndex] = 0;
imageData.data[pixelIndex + 1] = 255;
imageData.data[pixelIndex + 2] = 0;
imageData.data[pixelIndex + 3] = 255;
}
else
{
var pixelIndex = (offsetY + x) * 4;
var pixelIndexSrc = (py * image.width + px) * 4;
imageData.data[pixelIndex] = imageDataSrc.data[pixelIndexSrc];
imageData.data[pixelIndex + 1] = imageDataSrc.data[pixelIndexSrc + 1];
imageData.data[pixelIndex + 2] = imageDataSrc.data[pixelIndexSrc + 2];
imageData.data[pixelIndex + 3] = 255;
}
}
}
// here to do, image analysis
}
function getPointRelativePosition()
{
var pointOrigin = [];
for(i = 0; i < 4; i++)
{
pointOrigin[i] = {x:(points[i].x - marginImage) * image.width / (canvasWidth - marginImage * 2), y:(points[i].y - marginImage) * image.height / (canvasHeight - marginImage * 2)};
}
return pointOrigin;
}
function getPointPosition()
{
var pointOrigin = [];
for(i = 0; i < 4; i++)
{
pointOrigin[i] = {x:(points[i].x - marginImage) / (canvasWidth - marginImage * 2), y:(points[i].y - marginImage) / (canvasHeight - marginImage * 2)};
}
return pointOrigin;
}
function printPoint(pts)
{
var result = '';
for(var i = 0; i < 4; i++)
{
result += "{x:" + pts[i].x + ", y:" + pts[i].y + "},\n";
}
console.log(result);
}
function printMat(mat)
{
var result = '';
for(var i = 0; i < mat.length; i++)
{
result += "[";
for(var j = 0; j < mat[i].length; j++)
{
result += mat[i][j] + ", ";
}
result += "],\n";
}
console.log(result);
}
function canvasResize()
{
if(canvasWidth != document.body.clientWidth && canvasHeight != document.body.clientHeight)
{
var transformPoint = getPointPosition();
ratio = image.width / image.height;
canvasWidth = document.body.clientWidth;
canvasHeight = canvasWidth / ratio;
if(document.body.clientHeight < canvasHeight)
{
canvasHeight = document.body.clientHeight;
canvasWidth = canvasHeight * ratio;
}
canvasLargeur = canvasWidth;
canvasLongueur = canvasHeight;
if(canvasLargeur < canvasHeight) {
canvasLargeur = canvasHeight;
canvasLongueur = canvasWidth;
}
canvasPixelRatio = canvasLargeur / image.width;
c.setAttribute("width", canvasWidth);
c.setAttribute("height", canvasHeight);
marginImage = Math.round(40 * canvasPixelRatio);
for(i = 0; i < 4; i++)
{
points[i].x = transformPoint[i].x * (canvasWidth - marginImage * 2) + marginImage;
points[i].y = transformPoint[i].y * (canvasHeight - marginImage * 2) + marginImage;
}
draw(points);
}
}
c.addEventListener("mousedown", mouseDown, false);
c.addEventListener("mouseup", mouseUp, false);
c.addEventListener("mousemove", mouseMove, false);
c.addEventListener("touchstart", mouseDown, false);
c.addEventListener("touchend", mouseUp, false);
c.addEventListener("touchmove", mouseMove, false);
document.addEventListener("touchstart", cancelDefault, true);
document.addEventListener("touchend", cancelDefault, true);
document.addEventListener("touchmove", cancelDefault, true);
setInterval(canvasResize, 30);
draw(points);
};
</script>

Setting background image in canvas animation

I need to set background image for this canvas animation without affecting the animation style.
This CodePen is shown below.
var c = document.getElementById('canv');
var $ = c.getContext('2d');
var w = c.width = window.innerWidth;
var h = c.height = window.innerHeight;
var grav = 0.00095;
var s = [20, 15, 10, 5];
var gravX = w / 2;
var gravY = h / 2;
var nodes;
var num = 55;
var minDist = 155;
var spr = 0.0000009;
part();
run();
//random size function
function S() {
var curr = s.length;
var cur_ = Math.floor(Math.random() * curr);
return s[cur_];
}
function part() {
nodes = [];
for (var i = 0; i < num; i++) {
var node = {
hue: Math.random()*360,
rad: S(),
x: Math.random() * w,
y: Math.random() * h,
vx: Math.random() * 8 - 4,
vy: Math.random() * 8 - 4,
upd: function() {
this.x += this.vx;
this.y += this.vy;
if (this.x > w) this.x = 0;
else if (this.x < 0) this.x = w;
if (this.y > h) this.y = 0;
else if (this.y < 0) this.y = h;
},
draw: function() {
//outer ring
var g = $.createRadialGradient(this.x, this.y, this.rad * 2, this.x, this.y, this.rad);
g.addColorStop(0,'hsla(242, 55%, 15%,.7)');
g.addColorStop(.5, 'hsla(242, 50%, 10%,.5)');
g.addColorStop(1,'hsla(242, 30%, 5%,.5)');
$.fillStyle = g;
$.beginPath();
$.arc(this.x, this.y, this.rad * 2, 0, Math.PI * 2, true);
$.fill();
$.closePath();
//inner particle
var g2 = $.createRadialGradient(this.x, this.y, 0, this.x, this.y, this.rad);
g2.addColorStop(0, 'hsla('+this.hue+', 85%, 40%, 1)');
g2.addColorStop(.5, 'hsla('+this.hue+',95%, 50%,1)');
g2.addColorStop(1,'hsla(0,0%,0%,0)');
$.fillStyle = g2;
$.beginPath();
$.arc(this.x, this.y, this.rad, 0, Math.PI * 2, true);
$.fill();
$.closePath();
}
};
nodes.push(node);
}
}
function run() {
$.globalCompositeOperation = 'source-over';
$.fillStyle = 'hsla(242, 40%, 5%,.85)';
$.fillRect(0, 0, w, h);
$.globalCompositeOperation = 'lighter';
for (i = 0; i < num; i++) {
nodes[i].upd();
nodes[i].draw();
}
for (i = 0; i < num - 1; i++) {
var n1 = nodes[i];
for (var j = i + 1; j < num; j++) {
var n2 = nodes[j];
Spr(n1, n2);
}
Grav(n1);
}
window.requestAnimationFrame(run);
}
function Spr(na, nb) {
var dx = nb.x - na.x;
var dy = nb.y - na.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < minDist) {
$.lineWidth = 1;
$.beginPath();
$.strokeStyle = "hsla(217, 95%, 55%, .15)";
$.moveTo(na.x, na.y);
$.lineTo(nb.x, nb.y);
$.stroke();
$.closePath();
var ax = dx * spr;
var ay = dy * spr;
na.vx += ax;
na.vy += ay;
nb.vx -= ax;
nb.vy -= ay;
}
}
function Grav(n) {
n.vx += (gravX - n.x) * grav;
n.vy += (gravY - n.y) * grav;
};
window.addEventListener('resize', function() {
c.width = w = window.innerWidth;
c.height = h = window.innerHeight;
});
body{
width:100%;
margin:0;
overflow:hidden;
}
<canvas id='canv' ></canvas>
CSS
Just replace the beginning of the run() code to:
function run() {
...
//$.fillStyle = 'hsla(242, 40%, 5%,.85)';
$.clearRect(0, 0, w, h);
...
Then move the color settings to CSS together with an image reference:
#canv {
background: hsla(242, 40%, 5%, .85) url(path/to/image.jpg);
}
Add background-size to the CSS rule if needed. Note that since you're using different blending modes such as lighter which depends on existing content, you may not get desired result as it will blend with an empty canvas and not a solid - the approach below should solve that in this case.
CodePen
JavaScript
As before, replace the first lines in run() but after you made sure the image you want to use has loaded, simply draw it in:
function run() {
...
//$.fillStyle = 'hsla(242, 40%, 5%,.85)';
$.drawImage(img, 0, 0, w, h); // img must be loaded (use onload)
...
If your image contains transparency you also need to clear the canvas first:
function run() {
...
//$.fillStyle = 'hsla(242, 40%, 5%,.85)';
$.clearRect(0, 0, w, h);
$.drawImage(img, 0, 0, w, h); // img must be loaded (use onload)
...
CodePen

Resources