Add image to canvas - javascript

I saw on the internet beatiful example of canvas: https://codepen.io/anon/pen/BdjzPb
And I became interested in canvas. So I began to study it. But I can not understand how to change the dots (stars) in this example to pictures? I tried different combinations of context.drawImage(...) but I have no results...
The question is, where I should insert context.drawImage(...) to replace white dots with my images?
Here is js code of this canvas:
/*
* requestAnimationFrame pollyfill
*/
if (!window.requestAnimationFrame) {
window.requestAnimationFrame = (window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.msRequestAnimationFrame || window.oRequestAnimationFrame || function (callback) {
return window.setTimeout(callback, 1000 / 60);
});
}
// Init Stats
var stats = new Stats();
stats.setMode(0);
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '0px';
stats.domElement.style.top = '0px';
document.body.appendChild(stats.domElement);
/*!
* Mantis.js / jQuery / Zepto.js plugin for Constellation
* #version 1.2.2
* #author AcauĆ£ Montiel <contato#acauamontiel.com.br>
* #license http://acaua.mit-license.org/
*/
(function ($, window) {
/**
* Makes a nice constellation on canvas
* #constructor Constellation
*/
function Constellation (canvas, options) {
var $canvas = $(canvas),
context = canvas.getContext('2d'),
defaults = {
star: {
color: 'rgba(255, 255, 255, .5)',
width: 1,
randomWidth: true
},
line: {
color: 'rgba(255, 255, 255, .5)',
width: 0.2
},
position: {
x: 0, // This value will be overwritten at startup
y: 0 // This value will be overwritten at startup
},
width: window.innerWidth,
height: window.innerHeight,
velocity: 0.1,
length: 100,
distance: 120,
radius: 150,
stars: []
},
config = $.extend(true, {}, defaults, options);
function Star () {
this.x = Math.random() * canvas.width;
this.y = Math.random() * canvas.height;
this.vx = (config.velocity - (Math.random() * 0.5));
this.vy = (config.velocity - (Math.random() * 0.5));
this.radius = config.star.randomWidth ? (Math.random() * config.star.width) : config.star.width;
}
Star.prototype = {
create: function(){
context.beginPath();
context.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
context.fill();
},
animate: function(){
var i;
for (i = 0; i < config.length; i++) {
var star = config.stars[i];
if (star.y < 0 || star.y > canvas.height) {
star.vx = star.vx;
star.vy = - star.vy;
} else if (star.x < 0 || star.x > canvas.width) {
star.vx = - star.vx;
star.vy = star.vy;
}
star.x += star.vx;
star.y += star.vy;
}
},
line: function(){
var length = config.length,
iStar,
jStar,
i,
j;
for (i = 0; i < length; i++) {
for (j = 0; j < length; j++) {
iStar = config.stars[i];
jStar = config.stars[j];
if (
(iStar.x - jStar.x) < config.distance &&
(iStar.y - jStar.y) < config.distance &&
(iStar.x - jStar.x) > - config.distance &&
(iStar.y - jStar.y) > - config.distance
) {
if (
(iStar.x - config.position.x) < config.radius &&
(iStar.y - config.position.y) < config.radius &&
(iStar.x - config.position.x) > - config.radius &&
(iStar.y - config.position.y) > - config.radius
) {
context.beginPath();
context.moveTo(iStar.x, iStar.y);
context.lineTo(jStar.x, jStar.y);
context.stroke();
context.closePath();
}
}
}
}
}
};
this.createStars = function () {
var length = config.length,
star,
i;
context.clearRect(0, 0, canvas.width, canvas.height);
for (i = 0; i < length; i++) {
config.stars.push(new Star());
star = config.stars[i];
star.create();
}
star.line();
star.animate();
};
this.setCanvas = function () {
canvas.width = config.width;
canvas.height = config.height;
};
this.setContext = function () {
context.fillStyle = config.star.color;
context.strokeStyle = config.line.color;
context.lineWidth = config.line.width;
};
this.setInitialPosition = function () {
if (!options || !options.hasOwnProperty('position')) {
config.position = {
x: canvas.width * 0.5,
y: canvas.height * 0.5
};
}
};
this.loop = function (callback) {
callback();
window.requestAnimationFrame(function () {
stats.begin(); // Only for Stats
this.loop(callback);
stats.end(); // Only for Stats
}.bind(this));
};
this.bind = function () {
$canvas.on('mousemove', function(e){
config.position.x = e.pageX - $canvas.offset().left;
config.position.y = e.pageY - $canvas.offset().top;
});
};
this.init = function () {
this.setCanvas();
this.setContext();
this.setInitialPosition();
this.loop(this.createStars);
this.bind();
};
}
$.fn.constellation = function (options) {
return this.each(function () {
var c = new Constellation(this, options);
c.init();
});
};
})($, window);
// Init plugin
$('canvas').constellation({
star: {
width: 3
},
line: {
color: 'rgba(255, 0, 0, .5)'
},
radius: 250
});

You need to modify the create() method of Star constructor, because this is exactly the one that's being called when the canvas redraws everything (as a part of calling createStars() method inside the loop).
There is, however, one thing about context.drawImage(...) that you should be aware of: it needs image to be present in the DOM in order to render it in canvas, so first you would have to do something like
var img = new Image;
img.src = 'https://sourceofyourimage/image.jpg';
and then in create() method you can call context.drawImage(...) passing image, coordinates and size (if you want to).
https://codepen.io/Alvvi/pen/RZroPW here is a working CodePen
Also, you may want to check, if the image is loaded before trying to paint it, I suggest using onload for that, however in my case it works fine without it, because the image is quite small.

Related

Why does my vector splits in two vectors?

I'll try to explain as best I can.
I'm using canvas, and a basic vectors library I wrote in JS. Now my enemy object has a few vectors in it, the math seems right all the way through, but I end up seeing the enemy's weapon split into two and fire in both directions when I get too close to it.
Not close
Close
The player is the white circle with a green dot in the middle, and I use mousemove for the coordinates.
After looking at the images, I noticed that it creates two weapons that fire at the correct fireRate, one after the other. It doesn't show in the images, but when you look at it in the browser, there are two small red dots firing those yellow projectiles.
I looked over the code, and I can't find anything repeating itself that could cause the dot to split into two, and I started to wonder if it has anything to do with Math.atan2() or maybe something else.
Here's the enemy function:
function Enemy(enemyConfig){
this.enemyConfig = enemyConfig;
this.position = new Vector(enemyConfig.x, enemyConfig.y);
if(this.enemyConfig.hasVelocity){
this.velocity = new Vector(enemyConfig.vx, enemyConfig.vy);
}
this.radius = enemyConfig.radius;
this.hasWeapon = false;
if(enemyConfig.hasWeapon){
this.weaponId = enemyConfig.weaponId;
this.projectiles = [];
this.weapon = this.position.Copy();
this.weaponDistanceFromPosition = this.radius * .9;
this.weaponAngleToPlayer = 0;
this.weaponReloaded = true;
this.fireRate = enemyConfig.fireRate || .25;
this.reloadTimer = this.fireRate;
this.hasWeapon = true;
}
this.target = cfg.player;
};
Enemy.prototype.Draw = function(){
cfg.ctx.save();
cfg.ctx.beginPath();
cfg.ctx.fillStyle = "rgba(0, 0, 0, 1)";
cfg.ctx.strokeStyle = "rgba(255, 0, 0, 1)";
cfg.ctx.arc(this.position.x, this.position.y, this.radius, 0, PI*2, false);
cfg.ctx.fill();
cfg.ctx.stroke();
cfg.ctx.closePath();
cfg.ctx.restore();
if(this.hasWeapon){
cfg.ctx.save();
cfg.ctx.beginPath();
cfg.ctx.fillStyle = "rgba(255, 0, 0, 1)";
// cfg.ctx.strokeStyle = "rgba(0, 0, 0, 1)";
cfg.ctx.arc(this.weapon.x, this.weapon.y, 2, 0, PI*2, false);
cfg.ctx.fill();
// cfg.ctx.stroke();
cfg.ctx.closePath();
cfg.ctx.restore();
if(this.projectiles.length > 0 && this.projectiles){
this.projectiles.forEach((projectile, index, projectiles) => {
if(projectile.exists) projectile.Draw();
})
}
}
};
Enemy.prototype.Update = function(){
if(this.hasWeapon){
this.weaponAngleToPlayer = this.weapon.AngleTo(this.target.position);
this.weapon.Length = this.weaponDistanceFromPosition;
this.weapon.Angle = this.weaponAngleToPlayer;
this.weapon.AddTo(this.position);
this.weaponReloaded = (this.reloadTimer === 0);
if(!this.weaponReloaded){
this.reloadTimer -= Math.min(cfg.deltaTime / 1000, this.reloadTimer);
}else{
this.FireWeapon();
}
if(this.projectiles.length > 0){
this.projectiles.forEach((projectile, index, projectiles) => {
projectile.Update();
if(!projectile.exists) projectiles.splice(index, 1);
});
}
}
// console.log(this.projectiles);
// this.target = cfg.player;
// console.log(this.target.position);
};
Enemy.prototype.FireWeapon = function(){
var projectile = new Projectile();
projectile.position = this.weapon.Copy();
projectile.velocity = this.weapon.Copy();
projectile.velocity.Angle = this.weaponAngleToPlayer;
projectile.velocity.Limit(15);
projectile.exists = true;
this.projectiles.push(projectile);
this.reloadTimer = this.fireRate;
};
var PI = Math.PI;
var cfg = cfg || {};
var player;
cfg.expectedFPS = 60;
cfg.framesPerMs = cfg.expectedFPS / 1000;
cfg.deltaTime = 0;
cfg.previousTimeStamp = undefined;
cfg.direction = {
up: 1.5 * PI,
down: .5 * PI,
left: PI,
right: 0
};
cfg.mouse = {
x: 0,
y: 0,
radius: 2
};
cfg.projectiles = [];
cfg.player = null;
cfg.enemies = [];
function random(min, max = null){
if(max != null){
return Math.random() * (Math.max(min, max) - Math.min(min, max)) + Math.min(min, max);
}
return Math.random() * min;
};
function Background(config = {}){
var config = config;
cfg.ctx.fillStyle = "rgba(0, 0, 0, .5)";
cfg.ctx.fillRect(0, 0, cfg.canvas.width, cfg.canvas.height);
};
function KeepInCanvas(object){
if(object.position.x + object.radius >= cfg.canvas.width)
object.position.x = cfg.canvas.width - object.radius;
if(object.position.x - object.radius <= 0)
object.position.x = object.radius;
if(object.position.y + object.radius >= cfg.canvas.height)
object.position.y = cfg.canvas.height - object.radius;
if(object.position.y - object.radius <= 0)
object.position.y = object.radius;
};
function IsOutOfCanvas(object){
return (object.position.x + object.radius < 0 || object.position.x - object.radius > cfg.canvas.width
|| object.position.y + object.radius < 0 || object.position.y - object.radius > cfg.canvas.height);
};
function Vector(x = 0, y = 0){
this.x = x;
this.y = y;
};
Vector.prototype = {
get Length(){
return Math.sqrt(this.x * this.x + this.y * this.y);
},
set Length(length){
var angle = this.Angle;
this.x = Math.cos(angle) * length;
this.y = Math.sin(angle) * length;
},
get Angle(){
return Math.atan2(this.y, this.x);
},
set Angle(angle){
var length = this.Length;
this.x = Math.cos(angle) * length;
this.y = Math.sin(angle) * length;
},
Add: function(vector){ return new Vector(this.x + vector.x, this.y + vector.y); },
AddTo: function(vector){
this.x += vector.x;
this.y += vector.y;
},
Subtract: function(vector){ return new Vector(this.x - vector.x, this.y - vector.y); },
SubtractFrom: function(vector){
this.x -= vector.x;
this.y -= vector.y;
},
Multiply: function(factor){ return new Vector(this.x * factor, this.y * factor); },
MultiplyBy: function(factor){
this.x *= factor;
this.y *= factor;
},
DivideBy: function(factor){
this.x /= factor;
this.y /= factor;
},
AngleTo: function(vector){
return Math.atan2(vector.y - this.y, vector.x - this.x);
},
Copy: function(){ return new Vector(this.x, this.y); },
Normalize: function(arg = false){
if(this.Length > 0){
if(arg) return this.Divide(this.Length);
this.DivideBy(this.Length);
}
},
Limit: function(factor){
if(this.Length > factor){
this.Normalize();
this.MultiplyBy(factor);
}
}
};
function Player(){
this.position = new Vector(0, 0);
this.radius = 20;
};
Player.prototype.Draw = function(){
cfg.ctx.save();
cfg.ctx.beginPath();
cfg.ctx.fillStyle = "rgba(0, 180, 0, .35)";
cfg.ctx.strokeStyle = "rgba(255, 255, 255, 1)";
cfg.ctx.arc(this.position.x, this.position.y, this.radius, 0, PI*2, false);
cfg.ctx.fill();
cfg.ctx.stroke();
cfg.ctx.closePath();
cfg.ctx.restore();
};
Player.prototype.Update = function(){
this.position.x = cfg.mouse.x;
this.position.y = cfg.mouse.y;
KeepInCanvas(this);
};
function Projectile(){
this.position = new Vector(0, 0);
this.velocity;
this.radius = 5;
this.exists = false;
};
Projectile.prototype.SetInitialVelocity = function(angle, length){
var x = Math.cos(angle) * length,
y = Math.sin(angle) * length;
return new Vector(x, y);
};
Projectile.prototype.Draw = function(){
cfg.ctx.save();
cfg.ctx.beginPath();
cfg.ctx.fillStyle = "rgba(255, 160, 0, 1)";
cfg.ctx.arc(this.position.x, this.position.y, this.radius, 0, PI*2, false);
cfg.ctx.fill();
cfg.ctx.closePath();
cfg.ctx.restore();
};
Projectile.prototype.Update = function(){
this.position.AddTo(this.velocity.Multiply(cfg.framesPerMs).Multiply(cfg.deltaTime));
if(IsOutOfCanvas(this)) this.exists = false;
};
function Enemy(enemyConfig){
this.position = new Vector(enemyConfig.x, enemyConfig.y);
this.radius = enemyConfig.radius;
this.hasWeapon = false;
if(enemyConfig.hasWeapon){
this.projectiles = [];
this.weapon = this.position.Copy();
this.weaponAngleToPlayer;// = 0;
this.hasWeapon = true;
}
this.target = cfg.player;
};
Enemy.prototype.Draw = function(){
cfg.ctx.save();
cfg.ctx.beginPath();
cfg.ctx.fillStyle = "rgba(0, 0, 0, 1)";
cfg.ctx.strokeStyle = "rgba(255, 0, 0, 1)";
cfg.ctx.arc(this.position.x, this.position.y, this.radius, 0, PI*2, false);
cfg.ctx.fill();
cfg.ctx.stroke();
cfg.ctx.closePath();
cfg.ctx.restore();
if(this.hasWeapon){
cfg.ctx.save();
cfg.ctx.beginPath();
cfg.ctx.fillStyle = "rgba(255, 0, 0, 1)";
cfg.ctx.arc(this.weapon.x, this.weapon.y, 2, 0, PI*2, false);
cfg.ctx.fill();
cfg.ctx.closePath();
cfg.ctx.restore();
if(this.projectiles.length > 0 && this.projectiles){
this.projectiles.forEach(projectile => {
if(projectile.exists) projectile.Draw();
})
}
}
};
Enemy.prototype.Update = function(){
if(this.hasWeapon){
this.weaponAngleToPlayer = this.position.AngleTo(this.target.position);
// console.trace(this.weaponAngleToPlayer);
this.weapon.Length = this.radius * .9;
this.weapon.Angle = this.weaponAngleToPlayer;
this.weapon.AddTo(this.position);
this.FireWeapon();
if(this.projectiles.length > 0){
this.projectiles.forEach((projectile, index, projectiles) => {
projectile.Update();
if(!projectile.exists) projectiles.splice(index, 1);
});
}
}
};
Enemy.prototype.FireWeapon = function(){
var projectile = new Projectile();
projectile.position = this.weapon.Copy();
projectile.velocity = projectile.position.Copy();
projectile.velocity.Angle = this.weaponAngleToPlayer;
projectile.velocity.Limit(15);
projectile.exists = true;
// console.trace(projectile);
this.projectiles.push(projectile);
};
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Space Shooter</title>
</head>
<body>
<canvas id="gameCanvas"></canvas>
<script>
window.onload = function(){
cfg.canvas = document.getElementById("gameCanvas");
cfg.canvas.focus();
cfg.canvas.width = 760;
cfg.canvas.height = 1060;
cfg.canvas.style.cursor = "none";
cfg.ctx = cfg.canvas.getContext("2d");
cfg.canvasOffset = cfg.canvas.getBoundingClientRect();
cfg.player = player = new Player();
cfg.enemies[0] = new Enemy({
x: 200,
y: 500,
radius: 35,
hasWeapon: true
});
cfg.enemies[1] = new Enemy({
x: 500,
y: 200,
radius: 45,
hasWeapon: true
});
window.requestAnimationFrame(Game);
};
window.addEventListener("mousemove", function(e){
cfg.mouse.x = e.clientX - cfg.canvasOffset.x;
cfg.mouse.y = e.clientY - cfg.canvasOffset.y;
});
function Game(timestamp){
if(!cfg.previousTimeStamp) cfg.previousTimeStamp = timestamp;
cfg.deltaTime = timestamp - cfg.previousTimeStamp;
cfg.previousTimeStamp = timestamp;
Update();
Draw();
window.requestAnimationFrame(Game);
};
function Update(){
player.Update();
if(cfg.enemies.length > 0){
cfg.enemies.forEach(enemy => {
enemy.Update();
});
}
};
function Draw(){
Background();
if(cfg.enemies.length > 0){
cfg.enemies.forEach(enemy => {
enemy.Draw();
});
}
player.Draw();
};
</script>
</body>

PIXI Graphics "Out of Memory"

I tried to modify the following code (that draws splash animations on canvas directly):
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var particles = [];
canvas.onmousemove = function(e)
{
for (var i = 0; i < 36 * 2; i++)
{
particles.push({
x: e.clientX,
y: e.clientY,
angle: i * 5,
size: 5 + Math.random() * 3,
life: 200 + Math.random() * 50
});
}
}
canvas.onmouseup = function()
{
//ctx.clearRect(0, 0, 600, 600);
}
var delta = 0;
var last = Date.now();
function animate()
{
delta = Date.now() - last;
last = Date.now();
for (var i = 0; i < particles.length; i++)
{
var p = particles[i];
p.x += Math.cos(p.angle) * 4 + Math.random() * 2 - Math.random() * 2;
p.y += Math.sin(p.angle) * 4 + Math.random() * 2 - Math.random() * 2;
p.life -= delta;
p.size -= delta / 50;
if (p.size <= 0)
{
p.life = 0;
}
if (p.life <= 0)
{
particles.splice(i--, 1);
continue;
}
}
}
function render()
{
ctx.fillStyle = '#' + (Math.floor(Math.random() * (0xFFFFFF + 1))).toString(16);
for (var i = 0; i < particles.length; i++)
{
if (Math.random() < 0.1)
{
continue;
}
var p = particles[i];
ctx.beginPath();
ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2, false);
ctx.fill();
}
}
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function( callback ){
window.setTimeout(callback, 1000 / 60);
};
})();
(function animloop(){
requestAnimFrame(animloop);
animate();
render();
})();
...to draw using PIXI.Graphics:
const app = new PIXI.Application({
width: window.innerWidth,
height: window.innerHeight,
backgroundColor: 0x1099bb,
resolution: window.devicePixelRatio || 1,
resizeTo: window
});
document.body.appendChild(app.view);
const graphics = new PIXI.Graphics();
app.stage.addChild(graphics);
ctx = {
beginPath: function() {
graphics.beginFill(0x00ff00);
},
arc: function() {
graphics.arc(...arguments);
},
fill: function() {
graphics.endFill();
}
};
//var canvas = document.getElementById("canvas");
//var ctx = canvas.getContext("2d");
var particles = [];
window.onmousemove = function(e)
{
for (var i = 0; i < 36 * 2; i++)
{
particles.push({
x: e.clientX,
y: e.clientY,
angle: i * 5,
size: 5 + Math.random() * 3,
life: 200 + Math.random() * 50
});
}
}
window.onmouseup = function()
{
//ctx.clearRect(0, 0, 600, 600);
}
var delta = 0;
var last = Date.now();
function animate()
{
delta = Date.now() - last;
last = Date.now();
for (var i = 0; i < particles.length; i++)
{
var p = particles[i];
p.x += Math.cos(p.angle) * 4 + Math.random() * 2 - Math.random() * 2;
p.y += Math.sin(p.angle) * 4 + Math.random() * 2 - Math.random() * 2;
p.life -= delta;
p.size -= delta / 50;
if (p.size <= 0)
{
p.life = 0;
}
if (p.life <= 0)
{
particles.splice(i--, 1);
continue;
}
}
}
function render()
{
ctx.fillStyle = "#00FF00";
for (var i = 0; i < particles.length; i++)
{
if (Math.random() < 0.1)
{
continue;
}
var p = particles[i];
ctx.beginPath();
ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2, false);
ctx.fill();
}
}
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function( callback ){
window.setTimeout(callback, 1000 / 60);
};
})();
(function animloop(){
requestAnimFrame(animloop);
animate();
render();
})();
It seems to work, however after a few splashes it starts lagging and eventually crashes the browser with "Out of Memory" error.
The original code draws on mouse click, but I tried and it works on mouse move too.
My idea is that PIXI.Graphics creates too much cache causing the crash.
Does anybody know the reason of the crash and maybe a way to fix it?
Any ideas are appreciated!
Codepen HTML for testing:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta http-equiv="X-UA-Compatible" content="ie=edge"/>
<title>PIXI Splash Test</title>
<style>
html, body {
margin: 0;
padding: 0;
overflow: hidden;
height: 100vh;
}
</style>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/5.3.3/pixi.min.js"></script>
</body>
</html>
Is better to use more small "Graphics" objects - instead of one "Graphics" object with many shapes inside it. Please read second part of this answer" (after "Update 2020-03-15) : https://stackoverflow.com/a/60686761/3174731
Remove unneded objects / containers from "Pixi stage" when you dont need to use them anymore. When you remove the particles (when you do particles.splice(i--, 1);) you should also call app.stage.removeChild(somePixiContainer) (Graphics object is a PIXI.Container too - see "Extends" in https://pixijs.download/dev/docs/PIXI.Graphics.html )

Lowering CPU consumption of animated particles inside the canvas

I'm using this library zepto.min.js to add some particles over a canvas.
Here is the result on Codepen:
https://codepen.io/pixy-dixy/pen/JjGXQOo?editors=1111
I wonder if there is an option to:
control the number of particles inside the page
slow down the movements of the particles (velocity seems to not work properly here)
I want these especially for lowering CPU consumption.
Any recommendations will be greatly appreciated.
Here is the code:
(function ($, window) {
var $window = $(window);
function Constellation (canvas, options) {
var $canvas = $(canvas),
context = canvas.getContext('2d'),
defaults = {
star: {
color: 'rgba(255, 255, 255, .5)',
width: 1,
randomWidth: true
},
position: {
x: 0,
y: 0
},
width: window.innerWidth,
height: window.innerHeight,
velocity: 0.1,
length: 100,
distance: 120,
radius: 150,
stars: []
},
config = $.extend(true, {}, defaults, options);
function Star () {
this.x = Math.random() * canvas.width;
this.y = Math.random() * canvas.height;
this.vx = (config.velocity - (Math.random() * 0.5));
this.vy = (config.velocity - (Math.random() * 0.5));
this.radius = config.star.randomWidth ? (Math.random() * config.star.width) : config.star.width;
}
Star.prototype = {
create: function(){
context.beginPath();
context.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
context.fill();
},
animate: function(){
var i;
for (i = 0; i < config.length; i++) {
var star = config.stars[i];
if (star.y < 0 || star.y > canvas.height) {
star.vx = star.vx;
star.vy = - star.vy;
} else if (star.x < 0 || star.x > canvas.width) {
star.vx = - star.vx;
star.vy = star.vy;
}
star.x += star.vx;
star.y += star.vy;
}
},
};
this.createStars = function () {
var length = config.length,
star,
i;
context.clearRect(0, 0, canvas.width, canvas.height);
for (i = 0; i < length; i++) {
config.stars.push(new Star());
star = config.stars[i];
star.create();
}
star.animate();
};
this.setCanvas = function () {
canvas.width = config.width;
canvas.height = config.height;
};
this.setContext = function () {
context.fillStyle = config.star.color;
context.strokeStyle = config.line.color;
context.lineWidth = config.line.width;
};
this.setInitialPosition = function () {
if (!options || !options.hasOwnProperty('position')) {
config.position = {
x: canvas.width * 0.5,
y: canvas.height * 0.5
};
}
};
this.loop = function (callback) {
callback();
this.rAF = window.requestAnimationFrame(function () {
this.loop(callback);
}.bind(this));
};
this.handlers = {
window: {
mousemove: function(e){
config.position.x = e.pageX - $canvas.offset().left;
config.position.y = e.pageY - $canvas.offset().top;
},
resize: function () {
window.cancelAnimationFrame(this.rAF);
}
}
};
this.bind = function () {
$window
.on('resize', this.handlers.window.resize.bind(this));
};
this.unbind = function () {
$window
.off('resize', this.handlers.window.resize);
}
this.init = function () {
this.setCanvas();
this.setContext();
this.setInitialPosition();
this.loop(this.createStars);
this.bind();
};
}
function instantiate(element, options) {
var c = new Constellation(element, options);
c.init();
}
$.fn.constellation = function (options) {
return this.each(function () {
$window.on('resize', () => {
instantiate(this, options);
});
instantiate(this, options);
});
};
})($, window);
// Init plugin
$('.constellation').constellation({
star: {
width: 1
},
line: {
color: 'rgba(255, 255, 255, .5)'
},
length: (window.innerWidth / 6),
radius: (window.innerWidth / 5)
});
body {
overflow: hidden;
background: #111;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/zepto/1.0/zepto.min.js"></script>
<canvas class="constellation" data-radius="10" width="1000" height="500"></canvas>

Use same animation for multiple elements

I have this working canvas javascript animation but i would like to use it multiple times, currently it's only possible to have one canvas element with the id "stars" and use that one. Could i perhaps add a class for them instead and get the elements class and loop or what would be my best solution for achieving this? I would like to make this work without repeating to much since i could end up using the animation on different pages.
// Settings
var particleCount = 40,
flareCount = 0,
motion = 0.05,
tilt = 0.05,
color = '#00FF7B',
particleSizeBase = 1,
particleSizeMultiplier = 0.5,
flareSizeBase = 100,
flareSizeMultiplier = 100,
lineWidth = 1,
linkChance = 75, // chance per frame of link, higher = smaller chance
linkLengthMin = 5, // min linked vertices
linkLengthMax = 7, // max linked vertices
linkOpacity = 0.25; // number between 0 & 1
linkFade = 90, // link fade-out frames
linkSpeed = 0, // distance a link travels in 1 frame
glareAngle = -60,
glareOpacityMultiplier = 0.4,
renderParticles = true,
renderParticleGlare = true,
renderFlares = false,
renderLinks = false,
renderMesh = false,
flicker = false,
flickerSmoothing = 15, // higher = smoother flicker
blurSize = 0,
orbitTilt = true,
randomMotion = true,
noiseLength = 1000,
noiseStrength = 3;
var canvas = document.getElementById('stars'),
context = canvas.getContext('2d'),
mouse = {
x: 0,
y: 0
},
m = {},
r = 0,
c = 1000, // multiplier for delaunay points, since floats too small can mess up the algorithm
n = 0,
nAngle = (Math.PI * 2) / noiseLength,
nRad = 100,
nScale = 0.5,
nPos = {
x: 0,
y: 0
},
points = [],
vertices = [],
triangles = [],
links = [],
particles = [],
flares = [];
function init() {
var i, j, k;
// requestAnimFrame polyfill
window.requestAnimFrame = (function() {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
// Size canvas
resize();
mouse.x = canvas.clientWidth / 2;
mouse.y = canvas.clientHeight / 2;
// Create particle positions
for (i = 0; i < particleCount; i++) {
var p = new Particle();
particles.push(p);
points.push([p.x * c, p.y * c]);
}
vertices = Delaunay.triangulate(points);
var tri = [];
for (i = 0; i < vertices.length; i++) {
if (tri.length == 3) {
triangles.push(tri);
tri = [];
}
tri.push(vertices[i]);
}
// Tell all the particles who their neighbors are
for (i = 0; i < particles.length; i++) {
// Loop through all tirangles
for (j = 0; j < triangles.length; j++) {
// Check if this particle's index is in this triangle
k = triangles[j].indexOf(i);
// If it is, add its neighbors to the particles contacts list
if (k !== -1) {
triangles[j].forEach(function(value, index, array) {
if (value !== i && particles[i].neighbors.indexOf(value) == -1) {
particles[i].neighbors.push(value);
}
});
}
}
}
var fps = 15;
var now;
var then = Date.now();
var interval = 1000 / fps;
var delta;
// Animation loop
(function animloop() {
requestAnimFrame(animloop);
now = Date.now();
delta = now - then;
if (delta > interval) {
then = now - (delta % interval);
resize();
render();
}
})();
}
function render() {
if (randomMotion) {
n++;
if (n >= noiseLength) {
n = 0;
}
nPos = noisePoint(n);
}
if (renderParticles) {
// Render particles
for (var i = 0; i < particleCount; i++) {
particles[i].render();
}
}
}
function resize() {
canvas.width = window.innerWidth * (window.devicePixelRatio || 1);
canvas.height = canvas.width * (canvas.clientHeight / canvas.clientWidth);
}
// Particle class
var Particle = function() {
this.x = random(-0.1, 1.1, true);
this.y = random(-0.1, 1.1, true);
this.z = random(0, 4);
this.color = color;
this.opacity = random(0.1, 1, true);
this.flicker = 0;
this.neighbors = []; // placeholder for neighbors
};
Particle.prototype.render = function() {
var pos = position(this.x, this.y, this.z),
r = ((this.z * particleSizeMultiplier) + particleSizeBase) * (sizeRatio() / 1000),
o = this.opacity;
context.fillStyle = this.color;
context.globalAlpha = o;
context.beginPath();
context.fill();
context.closePath();
if (renderParticleGlare) {
context.globalAlpha = o * glareOpacityMultiplier;
context.ellipse(pos.x, pos.y, r * 100, r, (glareAngle - ((nPos.x - 0.5) * noiseStrength * motion)) * (Math.PI / 180), 0, 2 * Math.PI, false);
context.fill();
context.closePath();
}
context.globalAlpha = 1;
};
// Flare class
// Link class
var Link = function(startVertex, numPoints) {
this.length = numPoints;
this.verts = [startVertex];
this.stage = 0;
this.linked = [startVertex];
this.distances = [];
this.traveled = 0;
this.fade = 0;
this.finished = false;
};
// Utils
function noisePoint(i) {
var a = nAngle * i,
cosA = Math.cos(a),
sinA = Math.sin(a),
rad = nRad;
return {
x: rad * cosA,
y: rad * sinA
};
}
function position(x, y, z) {
return {
x: (x * canvas.width) + ((((canvas.width / 2) - mouse.x + ((nPos.x - 0.5) * noiseStrength)) * z) * motion),
y: (y * canvas.height) + ((((canvas.height / 2) - mouse.y + ((nPos.y - 0.5) * noiseStrength)) * z) * motion)
};
}
function sizeRatio() {
return canvas.width >= canvas.height ? canvas.width : canvas.height;
}
function random(min, max, float) {
return float ?
Math.random() * (max - min) + min :
Math.floor(Math.random() * (max - min + 1)) + min;
}
// init
if (canvas) init();
html,
body {
margin: 0;
padding: 0;
height: 100%;
}
body {
background: #000;
background-image: linear-gradient(-180deg, rgba(0, 0, 0, 0.00) 0%, #000000 100%);
}
#stars {
display: block;
position: relative;
width: 100%;
height: 100%;
z-index: 1;
position: absolute;
}
<script src="https://rawgit.com/ironwallaby/delaunay/master/delaunay.js"></script>
<script src="http://requirejs.org/docs/release/2.1.15/minified/require.js"></script>
<canvas id="stars" width="300" height="300"></canvas>
id="identifier" is unique
class="identifier" could be shared by a list of items
As you mention using class could be an option but you'll need to change your code to select all elements by that class before:
$(".identifier").each(function(a,b)
{
// Actions for each element
}
With javascript:
var elementList = document.getElementsByClassName("identifier");
var elementListSize=elementList.length;
for(var i=0;i<elementListSize;i++) {
// Actions for each element (elementList[i])
}

How to resize a canvas on resizing?

I'm using this plugin: https://codepen.io/acauamontiel/pen/mJdnw
/*
* requestAnimationFrame pollyfill
*/
if (!window.requestAnimationFrame) {
window.requestAnimationFrame = (window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.msRequestAnimationFrame || window.oRequestAnimationFrame || function (callback) {
return window.setTimeout(callback, 1000 / 60);
});
}
/*!
* Mantis.js / jQuery / Zepto.js plugin for Constellation
* #version 1.2.2
* #author AcauĆ£ Montiel <contato#acauamontiel.com.br>
* #license http://acaua.mit-license.org/
*/
(function ($, window) {
/**
* Makes a nice constellation on canvas
* #constructor Constellation
*/
function Constellation (canvas, options) {
var $canvas = $(canvas),
context = canvas.getContext('2d'),
defaults = {
star: {
color: 'rgba(255, 255, 255, .5)',
width: 1,
randomWidth: true
},
line: {
color: 'rgba(255, 255, 255, .5)',
width: 0.2
},
position: {
x: 0, // This value will be overwritten at startup
y: 0 // This value will be overwritten at startup
},
width: window.innerWidth,
height: window.innerHeight,
velocity: 0.1,
length: 100,
distance: 120,
radius: 150,
stars: []
},
config = $.extend(true, {}, defaults, options);
function Star () {
this.x = Math.random() * canvas.width;
this.y = Math.random() * canvas.height;
this.vx = (config.velocity - (Math.random() * 0.5));
this.vy = (config.velocity - (Math.random() * 0.5));
this.radius = config.star.randomWidth ? (Math.random() * config.star.width) : config.star.width;
}
Star.prototype = {
create: function(){
context.beginPath();
context.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
context.fill();
},
animate: function(){
var i;
for (i = 0; i < config.length; i++) {
var star = config.stars[i];
if (star.y < 0 || star.y > canvas.height) {
star.vx = star.vx;
star.vy = - star.vy;
} else if (star.x < 0 || star.x > canvas.width) {
star.vx = - star.vx;
star.vy = star.vy;
}
star.x += star.vx;
star.y += star.vy;
}
},
line: function(){
var length = config.length,
iStar,
jStar,
i,
j;
for (i = 0; i < length; i++) {
for (j = 0; j < length; j++) {
iStar = config.stars[i];
jStar = config.stars[j];
if (
(iStar.x - jStar.x) < config.distance &&
(iStar.y - jStar.y) < config.distance &&
(iStar.x - jStar.x) > - config.distance &&
(iStar.y - jStar.y) > - config.distance
) {
if (
(iStar.x - config.position.x) < config.radius &&
(iStar.y - config.position.y) < config.radius &&
(iStar.x - config.position.x) > - config.radius &&
(iStar.y - config.position.y) > - config.radius
) {
context.beginPath();
context.moveTo(iStar.x, iStar.y);
context.lineTo(jStar.x, jStar.y);
context.stroke();
context.closePath();
}
}
}
}
}
};
this.createStars = function () {
var length = config.length,
star,
i;
context.clearRect(0, 0, canvas.width, canvas.height);
for (i = 0; i < length; i++) {
config.stars.push(new Star());
star = config.stars[i];
star.create();
}
star.line();
star.animate();
};
this.setCanvas = function () {
canvas.width = config.width;
canvas.height = config.height;
};
this.setContext = function () {
context.fillStyle = config.star.color;
context.strokeStyle = config.line.color;
context.lineWidth = config.line.width;
};
this.setInitialPosition = function () {
if (!options || !options.hasOwnProperty('position')) {
config.position = {
x: canvas.width * 0.5,
y: canvas.height * 0.5
};
}
};
this.loop = function (callback) {
callback();
window.requestAnimationFrame(function () {
stats.begin(); // Only for Stats
this.loop(callback);
stats.end(); // Only for Stats
}.bind(this));
};
this.bind = function () {
$canvas.on('mousemove', function(e){
config.position.x = e.pageX - $canvas.offset().left;
config.position.y = e.pageY - $canvas.offset().top;
});
};
this.init = function () {
this.setCanvas();
this.setContext();
this.setInitialPosition();
this.loop(this.createStars);
this.bind();
};
}
$.fn.constellation = function (options) {
return this.each(function () {
var c = new Constellation(this, options);
c.init();
});
};
})($, window);
// Init plugin
$('canvas').constellation({
star: {
width: 3
},
line: {
color: 'rgba(255, 0, 0, .5)'
},
radius: 250
});
body {
overflow: hidden;
background: #111;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<canvas></canvas>
As you can see, when you resize your screen, the canvas keeps the original width and height, I would like to give the capacity to resize the window and do like a restart on the plugin to keep it fullscreen.
First
wrap your plugin initializing code with a function, like so ...
function init_plugin() {
$('canvas').constellation({
star: {
width: 3
},
line: {
color: 'rgba(255, 0, 0, .5)'
},
radius: 250
});
}
init_plugin(); //init plugin
Second
add a window resize event handler and within that do all the necessary stuff ...
window.onresize = function() {
cancelAnimationFrame(rAF); //cancel current animation frame
$('canvas')[0].width = window.innerWidth; //reset canvas width
$('canvas')[0].height = window.innerHeight; //reset canvas height
init_plugin(); //init plugin
}
also, you would need to assign requestAnimationFrame() to a globally accessible variable (here, it's rAF) , so you can cancel it later.
Here is the working code on CodePen
apology for not giving that much explanation

Categories

Resources