How to add external javascript in react app - javascript

I made a JavaScript particle animation and it works fine on a normal html website.
I tried implementing it into my react app using react helmet/useEffect but both didn't work and I don't know what's going on.
Here is my script:
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
canvas.style.position = 'absolute';
canvas.style.width = '100%';
canvas.style.height = '100%';
const container = document.querySelector('.particles');
container.appendChild(canvas);
let particleArray;
class Particle {
constructor(x, y, dirX, dirY, size, color) {
this.x = x;
this.y = y;
this.dirX = dirX;
this.dirY = dirY;
this.size = size;
this.color = color;
this.scale = .1;
this.counter = 0;
}
draw() {
var gradient = ctx.createRadialGradient(this.x, this.y, this.size / 8, this.x, this.y, this.size);
gradient.addColorStop(0, 'white');
gradient.addColorStop(1, 'white');
ctx.beginPath();
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2, false);
ctx.shadowColor = this.color;
ctx.shadowBlur = 10;
ctx.fillStyle = this.color;
ctx.fill();
}
update() {
if (this.x + this.size > canvas.width || this.x - this.size < 0) {
this.dirX = -this.dirX;
}
if (this.y + this.size > canvas.height || this.y - this.size < 0) {
this.dirY = -this.dirY;
}
this.size += this.scale;
this.counter += .1;
if (this.counter > 3) {
this.scale = -this.scale;
this.counter = 0;
}
this.x += this.dirX;
this.y += this.dirY;
this.draw();
}
}
function init() {
particleArray = [];
for (let i = 0; i < 25; i++) {
let size = Math.random() * 3;
let x = Math.random() * (innerWidth - size * 2);
let y = Math.random() * (innerHeight - size * 2);
let dirX = (Math.random() * .4) - .2;
let dirY = (Math.random() * .4) - .2;
let colors = ['#721240']
let color = colors[Math.floor(Math.random() * colors.length)];
particleArray.push(new Particle(x, y, dirX, dirY, size, color));
}
}
function animate() {
requestAnimationFrame(animate);
ctx.clearRect(0, 0, innerWidth, innerHeight);
for (let i = 0; i < particleArray.length; i++) {
particleArray[i].update();
}
}
init();
animate();
window.addEventListener('resize', () => {
canvas.width = innerWidth;
canvas.height = innerHeight;
init()
})
And here is how I tried to add it to my react app in App.js:
<div className="particles">
{useScript("./assets/particles.js")}
</div>
useScript is a react function using useEffect:
import { useEffect } from "react";
function useScript(url) {
useEffect(() => {
const script = document.createElement("script");
script.src = url;
script.async = false;
document.body.appendChild(script);
return () => {
document.body.removeChild(script);
};
}, [url]);
}
export default useScript;
Please help I'm stuck...

Instead of injecting the script, why don't you directly inject the code itself in useEffect. That's worked for me on multiple occassions
This is my proposed code. console.log() things like canvas and particleArray at multiple places to get it right
import { useEffect } from "react";
function useScript(url) {
useEffect(() => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
canvas.style.position = 'absolute';
canvas.style.width = '100%';
canvas.style.height = '100%';
const container = document.querySelector('.particles');
container.appendChild(canvas);
let particleArray;
class Particle {
constructor(x, y, dirX, dirY, size, color) {
this.x = x;
this.y = y;
this.dirX = dirX;
this.dirY = dirY;
this.size = size;
this.color = color;
this.scale = .1;
this.counter = 0;
}
draw() {
var gradient = ctx.createRadialGradient(this.x, this.y, this.size / 8, this.x, this.y, this.size);
gradient.addColorStop(0, 'white');
gradient.addColorStop(1, 'white');
ctx.beginPath();
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2, false);
ctx.shadowColor = this.color;
ctx.shadowBlur = 10;
ctx.fillStyle = this.color;
ctx.fill();
}
update() {
if (this.x + this.size > canvas.width || this.x - this.size < 0) {
this.dirX = -this.dirX;
}
if (this.y + this.size > canvas.height || this.y - this.size < 0) {
this.dirY = -this.dirY;
}
this.size += this.scale;
this.counter += .1;
if (this.counter > 3) {
this.scale = -this.scale;
this.counter = 0;
}
this.x += this.dirX;
this.y += this.dirY;
this.draw();
}
}
function init() {
particleArray = [];
for (let i = 0; i < 25; i++) {
let size = Math.random() * 3;
let x = Math.random() * (innerWidth - size * 2);
let y = Math.random() * (innerHeight - size * 2);
let dirX = (Math.random() * .4) - .2;
let dirY = (Math.random() * .4) - .2;
let colors = ['#721240']
let color = colors[Math.floor(Math.random() * colors.length)];
particleArray.push(new Particle(x, y, dirX, dirY, size, color));
}
}
function animate() {
requestAnimationFrame(animate);
ctx.clearRect(0, 0, innerWidth, innerHeight);
for (let i = 0; i < particleArray.length; i++) {
particleArray[i].update();
}
}
init();
animate();
window.addEventListener('resize', () => {
canvas.width = innerWidth;
canvas.height = innerHeight;
init()
})
return ()=>{
window.removeEventListener('resize', () => {
canvas.width = innerWidth;
canvas.height = innerHeight;
init()
})
}
}, [url]);
}
export default useScript;

Related

How can I perform a reset on the loop I have when pressing a button?

I'm creating a code where 3 balls will bounce when a button is clicked. I have a code I found in the internet and added the start button but clicking it multiple times speed up the balls. Also, I tried adding a reset button that will clear out the canvas but can't make it work. Sharing both the HTML and JS code.
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
const width = canvas.width = 1000;
const height = canvas.height = 500;
ctx.fillStyle = 'grey';
ctx.fillRect(0, 0, width, height);
function random(min, max) {
const num = Math.floor(Math.random() * (max - min + 1)) + min;
return num;
}
function Ball(x, y, velX, velY, color, size) {
this.x = x;
this.y = y;
this.velX = velX;
this.velY = velY;
this.color = color;
this.size = size;
}
Ball.prototype.draw = function() {
ctx.beginPath();
ctx.fillStyle = this.color;
ctx.arc(this.x, this.y, this.size, 0, 2 * Math.PI);
ctx.fill();
}
Ball.prototype.update = function() {
if ((this.x + this.size) >= width) {
this.velX = -(this.velX);
}
if ((this.x - this.size) <= 0) {
this.velX = -(this.velX);
}
if ((this.y + this.size) >= height) {
this.velY = -(this.velY);
}
if ((this.y - this.size) <= 0) {
this.velY = -(this.velY);
}
this.x += this.velX;
this.y += this.velY;
}
let balls = [];
function loop() {
while (balls.length < 3) {
let size = 35;
let ball = new Ball(
random(0 + size, width - size), random(0 + size, height - size), 5, 5, 'yellow', size);
balls.push(ball);
}
ctx.fillStyle = 'grey';
ctx.fillRect(0, 0, width, height);
for (let i = 0; i < balls.length; i++) {
balls[i].draw();
balls[i].update();
}
requestAnimationFrame(loop);
}
document.getElementById('Begin').addEventListener('click',loop);
<input type='button' id='Begin' value='start'>
<canvas id='myCanvas'></canvas>
You need to store the requestAnimationFrame in a var and use cancelAnimationFrame to stop
I added a div to hold the buttons and now delegate from that div so only one event handler is needed.
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
const width = canvas.width = 1000;
const height = canvas.height = 500;
ctx.fillStyle = 'grey';
ctx.fillRect(0, 0, width, height);
let rq; // this we can use to request and cancel
function random(min, max) {
const num = Math.floor(Math.random() * (max - min + 1)) + min;
return num;
}
function Ball(x, y, velX, velY, color, size) {
this.x = x;
this.y = y;
this.velX = velX;
this.velY = velY;
this.color = color;
this.size = size;
}
Ball.prototype.draw = function() {
ctx.beginPath();
ctx.fillStyle = this.color;
ctx.arc(this.x, this.y, this.size, 0, 2 * Math.PI);
ctx.fill();
}
Ball.prototype.update = function() {
if ((this.x + this.size) >= width) {
this.velX = -(this.velX);
}
if ((this.x - this.size) <= 0) {
this.velX = -(this.velX);
}
if ((this.y + this.size) >= height) {
this.velY = -(this.velY);
}
if ((this.y - this.size) <= 0) {
this.velY = -(this.velY);
}
this.x += this.velX;
this.y += this.velY;
}
let balls = [];
function loop() {
while (balls.length < 3) {
let size = 35;
let ball = new Ball(
random(0 + size, width - size), random(0 + size, height - size), 5, 5, 'yellow', size);
balls.push(ball);
}
ctx.fillStyle = 'grey';
ctx.fillRect(0, 0, width, height);
for (let i = 0; i < balls.length; i++) {
balls[i].draw();
balls[i].update();
}
rq = requestAnimationFrame(loop);
}
document.getElementById('butDiv').addEventListener('click', function(e) {
const tgt = e.target;
if (tgt.id === "Begin") {
if (tgt.value === "start") {
loop()
tgt.value = "stop";
document.getElementById("resetCanvas").classList.add("hide");
} else {
cancelAnimationFrame(rq)
this.value = "start";
document.getElementById("resetCanvas").classList.remove("hide");
}
} else if (tgt.id === "resetCanvas") {
cancelAnimationFrame(rq)
ctx.fillStyle = 'grey';
ctx.fillRect(0, 0, width, height);
document.getElementById("Begin").value="start";
tgt.classList.add("hide");
}
})
.hide {
display: none;
}
<div id="butDiv">
<input type='button' id='Begin' value='start'> <input type='button' class="hide" id='resetCanvas' value='reset'>
</div>
<canvas id='myCanvas'></canvas>

Javascript text particle in canvas slow in firefox but not on chrome (requestAnimationFrame ?)

I'm struggling with the implementation of a text particle in javascript.
My code is working fine with chromium-based browsers (Chromium: 84) but is unexpectedly slow on firefox (79.0).
I've searched a lot, but still don't find a solution and where the problem is.(This is pretty much linked to this Javascript Animation(Canvas) not working in firefox, Edge but works on Chrome).
Here is my codepen:
https://codepen.io/jgazeau/pen/LYNYPYB
function getRandomInt(max) {
return Math.floor(Math.random() * Math.floor(max));
}
function getCssVar(v) {
return getComputedStyle(document.documentElement).getPropertyValue(v);
}
let text404 = '404';
let canvas = document.getElementById('scene');
let ch = canvas.height = canvas.getBoundingClientRect().height;
let cw = canvas.width = canvas.getBoundingClientRect().width;
let sceneBackground = getCssVar('--scene-background');
let context = canvas.getContext('2d');
let previousMouseCoord = {x:0, y:0};
let mouseCoord = {x:0, y:0};
let sceneResize = false;
let particlesCount = 0;
let particles = [];
let colors = [
getCssVar('--particle-color-1'),
getCssVar('--particle-color-2'),
getCssVar('--particle-color-3'),
getCssVar('--particle-color-4'),
getCssVar('--particle-color-5')
];
const dpi = 200;
let Particle = function(x, y) {
this.x = getRandomInt(cw);
this.y = getRandomInt(ch);
this.coord = {x:x, y:y};
this.r = Math.min((getRandomInt((cw / dpi)) + 1), 6);
this.vx = (Math.random() - 0.5) * 100;
this.vy = (Math.random() - 0.5) * 100;
this.accX = 0;
this.accY = 0;
this.friction = Math.random() * 0.05 + 0.90;
this.color = colors[Math.floor(Math.random() * 6)];
}
Particle.prototype.render = function(isDisableMouse) {
this.accX = (this.coord.x - this.x) / 100;
this.accY = (this.coord.y - this.y) / 100;
this.vx += this.accX;
this.vy += this.accY;
this.vx *= this.friction;
this.vy *= this.friction;
this.x += this.vx;
this.y += this.vy;
if (!isDisableMouse) {
let a = this.x - mouseCoord.x;
let b = this.y - mouseCoord.y;
var distance = Math.sqrt(a * a + b * b);
if(distance < (cw / 15)) {
this.accX = (this.x - mouseCoord.x) / 100;
this.accY = (this.y - mouseCoord.y) / 100;
this.vx += this.accX;
this.vy += this.accY;
}
}
context.fillStyle = this.color;
context.beginPath();
context.arc(this.x, this.y, this.r, 0, Math.PI * 2, false);
context.fill();
context.closePath();
}
function onmouseCoordMove(e) {
mouseCoord.x = e.clientX;
mouseCoord.y = e.clientY;
}
function onTouchMove(e) {
if(e.touches.length > 0 ) {
mouseCoord.x = e.touches[0].clientX;
mouseCoord.y = e.touches[0].clientY;
}
}
function onTouchEnd() {
mouseCoord.x = -9999;
mouseCoord.y = -9999;
}
function initScene() {
ch = canvas.height = canvas.getBoundingClientRect().height;
cw = canvas.width = canvas.getBoundingClientRect().width;
context.clearRect(0, 0, canvas.width, canvas.height);
context.font = 'bold ' + (cw / 5) + 'px sans-serif';
context.fillStyle = sceneBackground;
context.textAlign = 'center';
context.fillText(text404, cw / 2, ch / 2);
let imageData = context.getImageData(0, 0, cw, ch).data;
context.clearRect(0, 0, canvas.width, canvas.height);
context.globalCompositeOperation = 'screen';
particles = [];
for(let y = 0; y < ch; y += Math.round(cw / dpi)) {
for(let x = 0; x < cw; x += Math.round(cw / dpi)) {
if(imageData[((x + y * cw) * 4) + 3] > 128){
particles.push(new Particle(x, y));
}
}
}
particlesCount = particles.length;
}
function renderScene() {
context.clearRect(0, 0, canvas.width, canvas.height);
let isDisableMouse = false;
if ((previousMouseCoord.x === mouseCoord.x) && (previousMouseCoord.x === mouseCoord.x)) {
isDisableMouse = true;
} else {
previousMouseCoord.x = mouseCoord.x;
previousMouseCoord.x = mouseCoord.x;
isDisableMouse = false;
}
for (let i = 0; i < particlesCount; i++) {
particles[i].render(isDisableMouse);
}
requestAnimationFrame(renderScene);
};
document.addEventListener('DOMContentLoaded', function() {
initScene();
renderScene();
window.addEventListener('mousemove', onmouseCoordMove);
window.addEventListener('touchmove', onTouchMove);
window.addEventListener('touchend', onTouchEnd);
window.addEventListener('resize', function() {
if (!sceneResize) {
requestAnimationFrame(function() {
initScene();
sceneResize = false;
});
sceneResize = true;
}
});
});
Can anyone know where this slow issue could come from ?
Thanks a lot for your help.

Logo particle animation

Im new to this area and hope some off you can help me.
I am super inspired be this site and the particle animation / function they have http://www.giantstepsmedias.com/
I found this one that are close to the inspiration. But i can't figure out how to change it to an images instead of the value in the input field.
var canvas = document.querySelector("#scene"),
ctx = canvas.getContext("2d"),
particles = [],
amount = 0,
mouse = {x:0,y:0},
radius = 1;
var colors = ["rgba(255,255,255,1)","rgba(255,255,255,.5)", "rgba(255,255,255,.25)","rgba(255,255,255,.1)"];
var copy = document.querySelector("#copy");
var ww = canvas.width = window.innerWidth;
var wh = canvas.height = window.innerHeight;
function Particle(x,y){
this.x = Math.random()*ww;
this.y = Math.random()*wh;
this.dest = {
x : x,
y: y
};
this.r = Math.random()*5 + 2;
this.vx = (Math.random()-0.5)*20;
this.vy = (Math.random()-0.5)*20;
this.accX = 0;
this.accY = 0;
this.friction = Math.random()*0.05 + 0.94;
this.color = colors[Math.floor(Math.random()*6)];
}
Particle.prototype.render = function() {
this.accX = (this.dest.x - this.x)/1000;
this.accY = (this.dest.y - this.y)/1000;
this.vx += this.accX;
this.vy += this.accY;
this.vx *= this.friction;
this.vy *= this.friction;
this.x += this.vx;
this.y += this.vy;
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(this.x, this.y, this.r, Math.PI * 2, false);
ctx.fill();
var a = this.x - mouse.x;
var b = this.y - mouse.y;
var distance = Math.sqrt( a*a + b*b );
if(distance<(radius*70)){
this.accX = (this.x - mouse.x)/100;
this.accY = (this.y - mouse.y)/100;
this.vx += this.accX;
this.vy += this.accY;
}
}
function onMouseMove(e){
mouse.x = e.clientX;
mouse.y = e.clientY;
}
function onTouchMove(e){
if(e.touches.length > 0 ){
mouse.x = e.touches[0].clientX;
mouse.y = e.touches[0].clientY;
}
}
function onTouchEnd(e){
mouse.x = -9999;
mouse.y = -9999;
}
function initScene(){
ww = canvas.width = window.innerWidth;
wh = canvas.height = window.innerHeight;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.font = "bold "+(ww/10)+"px sans-serif";
ctx.textAlign = "center";
ctx.fillText(copy.value, ww/2, wh/2);
var data = ctx.getImageData(0, 0, ww, wh).data;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.globalCompositeOperation = "screen";
particles = [];
for(var i=0;i<ww;i+=Math.round(ww/150)){
for(var j=0;j<wh;j+=Math.round(ww/150)){
if(data[ ((i + j*ww)*4) + 3] > 150){
particles.push(new Particle(i,j));
}
}
}
amount = particles.length;
}
function onMouseClick(){
radius++;
if(radius ===5){
radius = 0;
}
}
function render(a) {
requestAnimationFrame(render);
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < amount; i++) {
particles[i].render();
}
};
copy.addEventListener("keyup", initScene);
window.addEventListener("resize", initScene);
window.addEventListener("mousemove", onMouseMove);
window.addEventListener("touchmove", onTouchMove);
window.addEventListener("click", onMouseClick);
window.addEventListener("touchend", onTouchEnd);
initScene();
requestAnimationFrame(render);
canvas{
background: black;
width: 100vw;
height: 100vh;
}
<canvas id="scene"></canvas>
<input id="copy" type="text" value="Hello Codepen ♥" />
The animation is based on whatever has been rendered to the canvas.
In your code that is
ctx.font = "bold "+(ww/10)+"px sans-serif";
ctx.textAlign = "center";
ctx.fillText(copy.value, ww/2, wh/2);
instead, change this to render an image:
const img = document.getElementById('img');
ctx.drawImage(img, ww/2, wh/2);
you might like to adjust the positions for .drawImage: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage
Using HTML:
<canvas id="scene"></canvas>
<div style="display:none;">
<img id="img"
src="http://www.giantstepsmedias.com/img/logos/giant_steps_small.png">
</div>
However, rendering an image from a different server to your ctx gives The canvas has been tainted by cross-origin data so I can't give you a working snippet.
Use an image that is on the same server as the page or, as suggested, use a data: url:
var canvas = document.querySelector("#scene"),
ctx = canvas.getContext("2d"),
particles = [],
amount = 0,
mouse = {
x: 0,
y: 0
},
radius = 1;
var colors = ["rgba(255,255,255,1)", "rgba(255,255,255,.5)", "rgba(255,255,255,.25)", "rgba(255,255,255,.1)"];
var ww = canvas.width = window.innerWidth;
var wh = canvas.height = window.innerHeight;
function Particle(x, y) {
this.x = Math.random() * ww;
this.y = Math.random() * wh;
this.dest = {
x: x,
y: y
};
this.r = Math.random() * 5 + 2;
this.vx = (Math.random() - 0.5) * 20;
this.vy = (Math.random() - 0.5) * 20;
this.accX = 0;
this.accY = 0;
this.friction = Math.random() * 0.05 + 0.94;
this.color = colors[Math.floor(Math.random() * 6)];
}
Particle.prototype.render = function() {
this.accX = (this.dest.x - this.x) / 1000;
this.accY = (this.dest.y - this.y) / 1000;
this.vx += this.accX;
this.vy += this.accY;
this.vx *= this.friction;
this.vy *= this.friction;
this.x += this.vx;
this.y += this.vy;
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(this.x, this.y, this.r, Math.PI * 2, false);
ctx.fill();
var a = this.x - mouse.x;
var b = this.y - mouse.y;
var distance = Math.sqrt(a * a + b * b);
if (distance < (radius * 70)) {
this.accX = (this.x - mouse.x) / 100;
this.accY = (this.y - mouse.y) / 100;
this.vx += this.accX;
this.vy += this.accY;
}
}
function onMouseMove(e) {
mouse.x = e.clientX;
mouse.y = e.clientY;
}
function onTouchMove(e) {
if (e.touches.length > 0) {
mouse.x = e.touches[0].clientX;
mouse.y = e.touches[0].clientY;
}
}
function onTouchEnd(e) {
mouse.x = -9999;
mouse.y = -9999;
}
function initScene() {
ww = canvas.width = window.innerWidth;
wh = canvas.height = window.innerHeight;
ctx.clearRect(0, 0, canvas.width, canvas.height);
//ctx.font = "bold "+(ww/10)+"px sans-serif";
//ctx.textAlign = "center";
//ctx.fillText(copy.value, ww/2, wh/2);
const img = document.getElementById('img');
ctx.drawImage(img, ww / 2 - 75, (wh / 2) - 75, 150, 150);
var data = ctx.getImageData(0, 0, ww, wh).data;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.globalCompositeOperation = "screen";
particles = [];
for (var i = 0; i < ww; i += Math.round(ww / 150)) {
for (var j = 0; j < wh; j += Math.round(ww / 150)) {
if (data[((i + j * ww) * 4) + 3] > 150) {
particles.push(new Particle(i, j));
}
}
}
amount = particles.length;
}
function onMouseClick() {
radius++;
if (radius === 5) {
radius = 0;
}
}
function render(a) {
requestAnimationFrame(render);
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < amount; i++) {
particles[i].render();
}
};
window.addEventListener("resize", initScene);
window.addEventListener("mousemove", onMouseMove);
window.addEventListener("touchmove", onTouchMove);
window.addEventListener("click", onMouseClick);
window.addEventListener("touchend", onTouchEnd);
initScene();
requestAnimationFrame(render);
canvas {
background: black;
width: 100vw;
height: 100vh;
}
<canvas id="scene"></canvas>
<div style="display:none;">
<img id="img" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAAsCAYAAAAJpsrIAAAC9UlEQVR42syZSWgUQRSGezJJ0IhLwIDLRQkumEMOihdFPOghcd9QEEREiIokHkQRISAKRsUdEUxAL2pGxV08iQt40QgexAUNiBgFSQY0EjEzk/J/8kqHobvrRat76sE3E7prar55vdTrl4RS6oXneaNBzvsbSdAPjoATYMCzFw3gAMj6zEvf2w2mlOJlPBgVMAmJTQY7Qa8lsQowMmR/iX7JhAxKgM0gBSZaEjNlP/PHThB14BaY48UUJYMYW8NyG1wToxgB2sA+MNQlMX3e7QZnwTiXxHSsBjfBTNfEKKaDa2BF3GIK/DSMocN5ke91ZXGKHQPthnEk1ALO8CoSuRjtfwvWg1bBfDTuMpgaxzk2nA8nrXHbQZ9h/FxwG8yPWkzlvR/mrHwyfKaaM7clzquSvnAZeG4YRwv1KXAcDItCrN9n2xOwiA+ZKRr5qtVFQLktsTVgks/2j7zvqKBioB9xndfbPlHKUCh+UeZ4DWYDL4BG0CuYpxPcAwMhY8hHLEbRAzaGyNWBD+r/Y9Biin/pQVARIFcLOoohpuMSGBsgNwakiiWmODMzAuTKwH6QKYYYRRdYFXLeNYCvxRCjyIJmzpKf3Dy+Gq2K0cmeFk7YBqoC5GrAA9sZo2zsEU76kCX85CrBOc6wFbFtPHET+C6QewfqA+SSoFUiJlmS9NMQLcZrhZVFe0BlQW2IDltrZTbv7xtgMXgqqOGosjjp0w4YYkussOZ/BpaAq4LPbgXnwYQoyp6Ez7bPXFkcKsioXywAd/LaCyrqxzdqfuzgeuubYew0cAXUgx+SyUstPNCcBu/5fKoOGVcFLvDYHPfCInvg1XGXL4rHgnK71iRlU4ziJculXGgRFEYarAN7DQ3B2MX0w0sz32DTLonpoD7aSvDGNTGK+3wfe+SaGEUnWM4ZdEqMogdsArsEba1YxXRl0cK9j26JmKnZlrQsSCXRUvAqpNf2e0nq4vTmfBbvcsE6+C9BK8RCbi/Myju8+l823i8BBgAb0AQfHTBwWAAAAABJRU5ErkJggg=="
alt="" />
</div>

How do I get this canvas animation script to work in Firefox?

I wrote this canvas animation script in hopes to use it on my portfolio website, but it is basically non-functional in Firefox, and I am not sure why. The script draws stars on the canvas that slowly rotate, and if you hold down the mouse button they spin faster creating trails. It works awesome in chrome, but is extremely slow and choppy in Firefox.
let canvas = document.querySelector('canvas');
let c = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
let mouse = {
x: window.innerWidth / 2,
y: window.innerHeight / 2
}
let stars = [];
const starCount = 800;
class Star {
constructor(x, y, radius, color){
this.x = x;
this.y = y;
this.radius = radius;
this.color = color;
this.draw = () => {
c.save();
c.beginPath();
c.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
c.fillStyle = this.color;
c.shadowColor = this.color;
c.shadowBlur = 15;
c.fill();
c.closePath();
c.restore();
};
this.update = () => {
this.draw();
};
}
}
let colors = [
"#A751CC",
"#DE9AF9",
"#F9E0F9",
"#B5ECFB",
"#5F86F7"
];
(initializeStars = () =>{
for(let i = 0; i < starCount; i++){
let randomColorIndex = Math.floor(Math.random() * 5);
let randomRadius = Math.random() * 2;
let x = (Math.random() * (canvas.width + 400)) - (canvas.width + 400) / 2;
let y = (Math.random() * (canvas.width + 400)) - (canvas.width + 400) / 2;
stars.push(new Star(x, y, randomRadius, colors[randomColorIndex]));
}
})();
let opacity = 1;
let speed = 0.0005;
let time = 0;
let spinSpeed = desiredSpeed => {
speed += (desiredSpeed - speed) * 0.01;
time += speed;
return time;
}
let starOpacity = (desiredOpacity, ease) => {
opacity += (desiredOpacity - opacity) * ease;
return c.fillStyle = `rgba(18, 18, 18, ${opacity})`;
}
let animate = () => {
window.requestAnimationFrame(animate);
c.save();
if(mouseDown){
starOpacity(0.01, 0.03);
spinSpeed(0.012);
}else{
starOpacity(1, 0.01);
spinSpeed(0.001);
}
c.fillRect(0,0, canvas.width, canvas.height);
c.translate(canvas.width / 2, canvas.height / 2);
c.rotate(time);
for(let i = 0; i < stars.length; i++){
stars[i].update();
}
c.restore();
}
window.addEventListener('mousemove', e => {
mouse.x = e.clientX - canvas.width / 2;
mouse.y = e.clientY - canvas.height / 2;
});
window.addEventListener('resize', () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
stars = [];
initializeStars();
});
let mouseDown = false;
window.addEventListener("mousedown", () =>{
mouseDown = true;
});
window.addEventListener("mouseup", () => {
mouseDown = false;
});
animate();
Here is the link to the demo on Code Pen; any help is appreciated.
You can use rect instead of arc to draw your stars:
c.rect(this.x, this.y, 2*this.radius, 2*this.radius);
remove the blur It's extremely expensive:
//c.shadowBlur = 15;
You can use a radial gradient going from opaque in the center to transparent in his stead.

adding mouse movement to a canvas animation

i have made a an animations in canvas html but now i want to make the origin from where the balls originate move around the canvas according to my mouse position. i want to add mouse event function but i can't seem to get the logic straightand add added to the code , any help would be appreciated !
function runParticles () {
var canvas = document.createElement("canvas");
c = canvas.getContext("2d");
var particles = {};
var particleIndex = 0;
var particleNum = 15;
// set canvas size
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
// add canvas to body
document.body.appendChild(canvas);
// style the canvas
c.fillStyle = "black";
c.fillRect(0, 0, canvas.width, canvas.height);
function Particle() {
this.x = canvas.width / 2;
this.y = canvas.height / 2;
this.vx = Math.random() * 10 - 5;
this.vy = Math.random() * 10 - 5;
this.gravity = 0.3;
particleIndex++;
particles[particleIndex] = this;
this.id = particleIndex;
this.life = 0;
this.maxLife = Math.random() * 30 + 60;
this.color = "hsla(" + parseInt(Math.random() * 360, 10) + ",90%,60%,0.5";
}
Particle.prototype.draw = function() {
this.x += this.vx;
this.y += this.vy;
if (Math.random() < 0.1) {
this.vx = Math.random() * 10 - 5;
this.vy = Math.random() * 10 - 5;
}
this.life++;
if (this.life >= this.maxLife) {
delete particles[this.id];
}
c.fillStyle = this.color;
//c.fillRect(this.x, this.y, 5, 10);
c.beginPath();
c.arc(this.x, this.y, 2.5, degToRad(0), degToRad(360));
c.fill();
};
setInterval(function() {
//normal setting before drawing over canvas w/ black background
c.globalCompositeOperation = "source-over";
c.fillStyle = "rgba(0,0,0,0.5)";
c.fillRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < particleNum; i++) {
new Particle();
}
// c.globalCompositeOperation = "darken";
for (var i in particles) {
particles[i].draw();
}
}, 30);
}
function degToRad(deg) {
var radians = (deg * Math.PI / 180) - Math.PI / 2;
return radians;
}
<!DOCTYPE html5>
<html>
<head>
<title>disturbed</title>
<script src="toto.js" type="text/javascript"></script>
<script>
window.onload = () => runParticles();
</script>
</head>
<body>
</body>
</html>
I've added a function to detect the mouse position:
function oMousePos(canvas, evt) {
var ClientRect = canvas.getBoundingClientRect();
return { //objeto
x: Math.round(evt.clientX - ClientRect.left),
y: Math.round(evt.clientY - ClientRect.top)
}
}
I've declared a variable m (the mouse).
var m = {x:canvas.width/2,y:canvas.height/2};
and I've changed the origin of the particles from this.x = canvas.width / 2;
this.y = canvas.height / 2;to this.x = m.x; this.y = m.y;
This is the position when the mouse do not move over the canvas
I've added an event "mousemove". When the mouse move over the canvas it's position change.
canvas.addEventListener("mousemove", (evt)=>{
m = oMousePos(canvas, evt);
})
I've also added an event "mouseleave". When the mouse leaves the canvas, the mouse goes back in the center.
canvas.addEventListener("mouseleave", (evt)=>{
m = {x:canvas.width/2,y:canvas.height/2};
})
Also I've changed the setInterval for requestAnimationFrame ( much more efficient ). The code inside is your code.
function runParticles () {
var canvas = document.createElement("canvas");
c = canvas.getContext("2d");
var particles = {};
var particleIndex = 0;
var particleNum = 8;
// set canvas size
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var m = {x:canvas.width/2,y:canvas.height/2};///////
// add canvas to body
document.body.appendChild(canvas);
// style the canvas
c.fillStyle = "black";
c.fillRect(0, 0, canvas.width, canvas.height);
function Particle() {
this.x = m.x;//////////
this.y = m.y;//////////
this.vx = Math.random() * 10 - 5;
this.vy = Math.random() * 10 - 5;
this.gravity = 0.3;
particleIndex++;
particles[particleIndex] = this;
this.id = particleIndex;
this.life = 0;
this.maxLife = Math.random() * 30 + 60;
this.color = "hsla(" + parseInt(Math.random() * 360, 10) + ",90%,60%,0.5";
}
Particle.prototype.draw = function() {
this.x += this.vx;
this.y += this.vy;
if (Math.random() < 0.1) {
this.vx = Math.random() * 10 - 5;
this.vy = Math.random() * 10 - 5;
}
this.life++;
if (this.life >= this.maxLife) {
delete particles[this.id];
}
c.fillStyle = this.color;
//c.fillRect(this.x, this.y, 5, 10);
c.beginPath();
c.arc(this.x, this.y, 2.5, degToRad(0), degToRad(360));
c.fill();
};
function Draw() {
window.requestAnimationFrame(Draw);
c.globalCompositeOperation = "source-over";
c.fillStyle = "rgba(0,0,0,0.5)";
c.fillRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < particleNum; i++) {
new Particle();
}
// c.globalCompositeOperation = "darken";
for (var i in particles) {
particles[i].draw();
}
}
Draw();
///////////////////
canvas.addEventListener("mousemove", (evt)=>{
m = oMousePos(canvas, evt);
})
canvas.addEventListener("mouseleave", (evt)=>{
m = {x:canvas.width/2,y:canvas.height/2};
})
///////////////////
}
function degToRad(deg) {
var radians = (deg * Math.PI / 180) - Math.PI / 2;
return radians;
}
runParticles();
function oMousePos(canvas, evt) {
var ClientRect = canvas.getBoundingClientRect();
return { //objeto
x: Math.round(evt.clientX - ClientRect.left),
y: Math.round(evt.clientY - ClientRect.top)
}
}
body,canvas{margin:0;padding:0;}
I hope this helps.

Categories