I have a function that looks kind of primitive, I am wondering whether anyone has any solution on how to improve the looks of this function. Can I change this primitive loop if()... if()... to something that looks cleaner and nicer?
function drawPlayers () {
if (players[0].nick != null) {
let player1Img = new Image(SQUARE, SQUARE)
player1Img.onload = function() {
ctx.drawImage(player1Img, LEFT_LINE + players[0].x * SQUARE, UPPER_LINE + players[0].y * SQUARE, this.width, this.height)
}
player1Img.src = "sprites/player1.png"
}
if (players[1].nick != null) {
let player2Img = new Image(SQUARE, SQUARE)
player2Img.onload = function() {
ctx.drawImage(player2Img, LEFT_LINE + players[1].x * SQUARE, UPPER_LINE + players[1].y * SQUARE, this.width, this.height)
}
player2Img.src = "sprites/player1.png"
}
if (players[2].nick != null) {
let player3Img = new Image(SQUARE, SQUARE)
player3Img.onload = function() {
ctx.drawImage(player3Img, LEFT_LINE + players[2].x * SQUARE, UPPER_LINE + players[2].y * SQUARE, this.width, this.height)
}
player3Img.src = "sprites/player1.png"
}
if (players[3].nick != null) {
let player4Img = new Image(SQUARE, SQUARE)
player4Img.onload = function() {
ctx.drawImage(player4Img, LEFT_LINE + players[3].x * SQUARE, UPPER_LINE + players[3].y * SQUARE, this.width, this.height)
}
player4Img.src = "sprites/player1.png"
}
}
Like this
players.forEach(player => {
if (player.nick != null) {
let img = new Image(SQUARE, SQUARE)
img.onload = function() {
ctx.drawImage(img, LEFT_LINE + player.x * SQUARE, UPPER_LINE + player.y * SQUARE, this.width, this.height)
}
img.src = "sprites/player1.png"; // or `sprites/${player.image}`;
}
});
If you have another array for image names you can add an index to the forEach :
players.forEach((player,i) => {
if (player.nick != null) {
let img = new Image(SQUARE, SQUARE)
img.onload = function() {
ctx.drawImage(img, LEFT_LINE + player.x * SQUARE, UPPER_LINE + player.y * SQUARE, this.width, this.height)
}
img.src = `sprites/${images[i]}`;
}
});
const SQUARE = 100;
const images = [
"https://via.placeholder.com/100x100?text=Image1",
"https://via.placeholder.com/100x100?text=Image2",
"https://via.placeholder.com/100x100?text=Image3"
];
const players = [
{ nick: "Fred", x: 10, y: 20 },
{ nick: "Joe", x: 20, y: 40 },
{ nick: "Sam", x: 30, y: 50 }
];
players.forEach((player, i) => {
if (player.nick != null) {
let img = new Image(SQUARE, SQUARE)
img.onload = function() {
console.log(i,player.nick,`ctx.drawImage(img, LEFT_LINE ${player.x} * SQUARE, UPPER_LINE + ${player.y} * SQUARE, ${this.width}, ${this.height})`)
}
img.src = `${images[i]}`;
}
});
You could do a for loop
function drawPlayers() {
for (let i = 0; i < players.length; i++) {
if (players[i].nick != null) {
let playerImg = new Image(SQUARE, SQUARE)
playerImg.onload = function() {
ctx.drawImage(
playerImg,
LEFT_LINE + players[i].x * SQUARE,
UPPER_LINE + players[i].y * SQUARE,
this.width,
this.height
)
}
// if the image is fixed
playerImg.src = 'sprites/player1.png'
// else
// playerImg.src = `sprites/player${i + 1}.png`
}
}
}
function drawPlayers() {
players.forEach((player, idx) => {
if (player.nick != null) {
// uncomment following comment block and delete this comment
/*
var img = new Image(SQUARE, SQUARE)
img.onload = () => {
ctx.drawImage(img, LEFT_LINE + player.x * SQUARE, UPPER_LINE + player.y * SQUARE, this.width, this.height)
};
img.src = "sprites/player"+(idx+1)+".png";
*/
console.log(idx, player.nick, "sprites/player"+(idx+1)+".png");
}
});
}
var players=[{nick:"abe"},{},{nick:"chuck"},{nick:"dick"}];
drawPlayers();
Another variant:
for(let p of players){
if(p.nick){
let playerImg = new Image(SQUARE,SQUARE);
playerImg.onload = function() {
ctx.drawImage(player1Img, LEFT_LINE + p.x*SQUARE, UPPER_LINE + p.y*SQUARE, this.width, this.height)
}
playerImg.src = "sprites/player1.png"
}
}
Another feature, I learned recently:
for(let p of players){
if(!p.nick) continue;
let playerImg = new Image(SQUARE,SQUARE);
playerImg.onload = function() {
ctx.drawImage(player1Img, LEFT_LINE + p.x*SQUARE, UPPER_LINE + p.y*SQUARE, this.width, this.height)
}
playerImg.src = "sprites/player1.png"
}
Related
I downloaded this javascript blob graphic, I've added it to my page, and moved it to the right using CSS, But when I move the mouse to interact with the blob, it still thinks the blob is in the middle of the screen. How do I move the interaction to the right with the blob?
let canvas, ctx;
let render, init;
let blob;
class Blob {
constructor() {
this.points = [];
}
init() {
for (let i = 0; i < this.numPoints; i++) {
let point = new Point(this.divisional * (i + 1), this);
// point.acceleration = -1 + Math.random() * 2;
this.push(point);
}
}
render() {
let canvas = this.canvas;
let ctx = this.ctx;
let position = this.position;
let pointsArray = this.points;
let radius = this.radius;
let points = this.numPoints;
let divisional = this.divisional;
let center = this.center;
ctx.clearRect(0, 0, canvas.width, canvas.height);
pointsArray[0].solveWith(pointsArray[points - 1], pointsArray[1]);
let p0 = pointsArray[points - 1].position;
let p1 = pointsArray[0].position;
let _p2 = p1;
ctx.beginPath();
ctx.moveTo(center.x, center.y);
ctx.moveTo((p0.x + p1.x) / 2, (p0.y + p1.y) / 2);
for (let i = 1; i < points; i++) {
pointsArray[i].solveWith(pointsArray[i - 1], pointsArray[i + 1] || pointsArray[0]);
let p2 = pointsArray[i].position;
var xc = (p1.x + p2.x) / 2;
var yc = (p1.y + p2.y) / 2;
ctx.quadraticCurveTo(p1.x, p1.y, xc, yc);
// ctx.lineTo(p2.x, p2.y);
ctx.fillStyle = '#000000';
// ctx.fillRect(p1.x-2.5, p1.y-2.5, 5, 5);
p1 = p2;
}
var xc = (p1.x + _p2.x) / 2;
var yc = (p1.y + _p2.y) / 2;
ctx.quadraticCurveTo(p1.x, p1.y, xc, yc);
// ctx.lineTo(_p2.x, _p2.y);
// ctx.closePath();
ctx.fillStyle = this.color;
ctx.fill();
ctx.strokeStyle = '#000000';
// ctx.stroke();
/*
ctx.fillStyle = '#000000';
if(this.mousePos) {
let angle = Math.atan2(this.mousePos.y, this.mousePos.x) + Math.PI;
ctx.fillRect(center.x + Math.cos(angle) * this.radius, center.y + Math.sin(angle) * this.radius, 5, 5);
}
*/
requestAnimationFrame(this.render.bind(this));
}
push(item) {
if (item instanceof Point) {
this.points.push(item);
}
}
set color(value) {
this._color = value;
}
get color() {
return this._color || '#000000';
}
set canvas(value) {
if (value instanceof HTMLElement && value.tagName.toLowerCase() === 'canvas') {
this._canvas = canvas;
this.ctx = this._canvas.getContext('2d');
}
}
get canvas() {
return this._canvas;
}
set numPoints(value) {
if (value > 2) {
this._points = value;
}
}
get numPoints() {
return this._points || 32;
}
set radius(value) {
if (value > 0) {
this._radius = value;
}
}
get radius() {
return this._radius || 150;
}
set position(value) {
if (typeof value == 'object' && value.x && value.y) {
this._position = value;
}
}
get position() {
return this._position || { x: 0.5, y: 0.5 };
}
get divisional() {
return Math.PI * 2 / this.numPoints;
}
get center() {
return { x: this.canvas.width * this.position.x, y: this.canvas.height * this.position.y };
}
set running(value) {
this._running = value === true;
}
get running() {
return this.running !== false;
}}
class Point {
constructor(azimuth, parent) {
this.parent = parent;
this.azimuth = Math.PI - azimuth;
this._components = {
x: Math.cos(this.azimuth),
y: Math.sin(this.azimuth) };
this.acceleration = -0.3 + Math.random() * 0.6;
}
solveWith(leftPoint, rightPoint) {
this.acceleration = (-0.3 * this.radialEffect + (leftPoint.radialEffect - this.radialEffect) + (rightPoint.radialEffect - this.radialEffect)) * this.elasticity - this.speed * this.friction;
}
set acceleration(value) {
if (typeof value == 'number') {
this._acceleration = value;
this.speed += this._acceleration * 2;
}
}
get acceleration() {
return this._acceleration || 0;
}
set speed(value) {
if (typeof value == 'number') {
this._speed = value;
this.radialEffect += this._speed * 5;
}
}
get speed() {
return this._speed || 0;
}
set radialEffect(value) {
if (typeof value == 'number') {
this._radialEffect = value;
}
}
get radialEffect() {
return this._radialEffect || 0;
}
get position() {
return {
x: this.parent.center.x + this.components.x * (this.parent.radius + this.radialEffect),
y: this.parent.center.y + this.components.y * (this.parent.radius + this.radialEffect) };
}
get components() {
return this._components;
}
set elasticity(value) {
if (typeof value === 'number') {
this._elasticity = value;
}
}
get elasticity() {
return this._elasticity || 0.001;
}
set friction(value) {
if (typeof value === 'number') {
this._friction = value;
}
}
get friction() {
return this._friction || 0.0085;
}}
blob = new Blob();
init = function () {
canvas = document.getElementById('canvas');
canvas.setAttribute('touch-action', 'none');
let resize = function () {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
};
window.addEventListener('resize', resize);
resize();
let oldMousePoint = { x: 0, y: 0 };
let hover = false;
let mouseMove = function (e) {
let pos = blob.center;
let diff = { x: e.clientX - pos.x, y: e.clientY - pos.y };
let dist = Math.sqrt(diff.x * diff.x + diff.y * diff.y);
let angle = null;
blob.mousePos = { x: pos.x - e.clientX, y: pos.y - e.clientY };
if (dist < blob.radius && hover === false) {
let vector = { x: e.clientX - pos.x, y: e.clientY - pos.y };
angle = Math.atan2(vector.y, vector.x);
hover = true;
// blob.color = '#77FF00';
} else if (dist > blob.radius && hover === true) {
let vector = { x: e.clientX - pos.x, y: e.clientY - pos.y };
angle = Math.atan2(vector.y, vector.x);
hover = false;
blob.color = null;
}
if (typeof angle == 'number') {
let nearestPoint = null;
let distanceFromPoint = 100;
blob.points.forEach(point => {
if (Math.abs(angle - point.azimuth) < distanceFromPoint) {
// console.log(point.azimuth, angle, distanceFromPoint);
nearestPoint = point;
distanceFromPoint = Math.abs(angle - point.azimuth);
}
});
if (nearestPoint) {
let strength = { x: oldMousePoint.x - e.clientX, y: oldMousePoint.y - e.clientY };
strength = Math.sqrt(strength.x * strength.x + strength.y * strength.y) * 10;
if (strength > 100) strength = 100;
nearestPoint.acceleration = strength / 100 * (hover ? -1 : 1);
}
}
oldMousePoint.x = e.clientX;
oldMousePoint.y = e.clientY;
};
// window.addEventListener('mousemove', mouseMove);
window.addEventListener('pointermove', mouseMove);
blob.canvas = canvas;
blob.init();
blob.render();
};
window.addEventListener('DOMContentLoaded', () => {
init();
});
canvas {
position: absolute;
touch-action: none;
}
#canvarse {
padding-left: 800px;
}
<div id="canvarse">
<canvas id="canvas"></canvas>
<!-- partial:index.partial.html -->
<script src="https://code.jquery.com/pep/0.4.3/pep.js"></script>
<!-- partial -->
<script src="js/script.js"></script>
</div>
If you run the code snippet, you can see that the blob still thinks its in the middle of the canvas when it's actually where its supposed to be on the right.
I'm struggling to see how I can move the interaction across with the blob?
Anyone help?
seems like it was pretty easy you just have to play with the position function in your JavaScript, First remove your CSS completely because it is unnecessary and find this particular line on your JavaScript and change the X axis position if you want to move the blob horizontally and Y axis if you want vertical movement
get position() {
return this._position || { x: 0.8, y: 0.5 };
}
by default it was x:0.5 which means center so I changed it to 0.8 slight movement towards right side. hope this helps
I have this canvas particle animation that follows the mouse event correctly but it does not follow a touch event on mobile. I realized that it's because I have this animation beneath the rest of the content which is positioned on top. When there is something on top of the canvas that it's hitting, it does not trigger the touch event. I'm hoping someone can help me figure out how to avoid this issue so that the animation will still follow the user underneath the page content.
JS
var PI2 = Math.PI * 2;
var HALF_PI = Math.PI / 2;
var isTouch = 'ontouchstart' in window;
var isSafari = !!navigator.userAgent.match(/Version\/[\d\.]+.*Safari/);
function Canvas(options) {
options = _.clone(options || {});
this.options = _.defaults(options, this.options);
this.el = this.options.el;
this.ctx = this.el.getContext('2d');
this.dpr = window.devicePixelRatio || 1;
this.updateDimensions();
window.addEventListener('resize', this.updateDimensions.bind(this), false);
this.resetTarget();
if(isTouch){
// touch
this.el.addEventListener('touchstart', this.touchMove.bind(this), false);
this.el.addEventListener('touchmove', this.touchMove.bind(this), false);
// this.el.addEventListener('touchend', this.resetTarget.bind(this), false);
} else {
// Mouse
window.addEventListener('mousemove', this.mouseMove.bind(this), false);
window.addEventListener('mouseout', this.resetTarget.bind(this), false);
}
this.setupParticles();
this.loop();
}
Canvas.prototype.updateDimensions = function() {
this.width = this.el.width = _.result(this.options, 'width') * this.dpr;
this.height = this.el.height = _.result(this.options, 'height') * this.dpr;
this.el.style.width = _.result(this.options, 'width') + 'px';
this.el.style.height = _.result(this.options, 'height') + 'px';
}
// Update the orb target
Canvas.prototype.mouseMove = function(event) {
this.target = new Vector(event.clientX * this.dpr, event.clientY* this.dpr);
}
// Reset to center when we mouse out
Canvas.prototype.resetTarget = function() {
this.target = new Vector(this.width / 2, this.height /2);
}
// Touch Eent
Canvas.prototype.touchMove = function(event) {
if(event.touches.length === 1) { event.preventDefault(); }
this.target = new Vector(event.touches[0].pageX * this.dpr, event.touches[0].pageY * this.dpr);
}
// Defaults
Canvas.prototype.options = {
count: 11,
speed: 0.001,
width: 400,
height: 400,
size: 5,
radius: 1,
background: '240, 240, 240, 0.6',
maxDistance: 100
}
Canvas.prototype.setupParticles = function() {
this.particles = [];
var index = -1;
var between = PI2 / this.options.count;
while(++index < this.options.count) {
var x;
var y;
var angle;
var max = Math.max(this.width, this.height);
angle = (index + 1) * between;
x = Math.cos(angle) * max;
x += this.width / 2;
y = Math.sin(angle) * max;
y += this.height / 2;
var particle = new Particle({
x: x,
y: y,
radius: this.options.radius,
size: this.options.size,
angle: angle,
color: this.options.color
});
this.particles.push(particle);
}
}
Canvas.prototype.findClosest = function() {
var index = -1;
var pointsLength = this.particles.length;
while(++index < pointsLength) {
var closestIndex = -1;
this.particles[index].closest = [];
while(++closestIndex < pointsLength) {
var closest = this.particles[closestIndex];
var distance = this.particles[index].position.distanceTo(closest.position);
if(distance < this.options.maxDistance) {
var vector = new Vector(closest.position.x, closest.position.y);
vector.opacity = 1 - (distance / this.options.maxDistance);
vector.distance = distance;
this.particles[index].closest.push(vector);
}
}
}
}
Canvas.prototype.loop = function() {
// this.clear();
if(isTouch || isSafari) {
this.ghost();
} else {
this.ghostGradient();
}
if(this.options.maxDistance > 0) {
this.findClosest();
}
this.draw();
window.requestAnimationFrame(_.bind(this.loop, this));
}
Canvas.prototype.clear = function() {
this.ctx.clearRect(0, 0 , this.width, this.height);
}
Canvas.prototype.ghost = function() {
this.ctx.globalCompositeOperation = "source-over";
this.ctx.rect(0, 0 , this.width, this.height);
if(typeof this.options.background === 'string') {
this.ctx.fillStyle = "rgba(" + this.options.background + ")";
} else {
this.ctx.fillStyle = "rgba(" + this.options.background[0] + ")";
}
this.ctx.fill();
}
Canvas.prototype.ghostGradient = function() {
var gradient;
if(typeof this.options.background === 'string') {
this.ctx.fillStyle = 'rgba(' + this.options.background + ')';
} else {
var gradient = this.ctx.createLinearGradient(0, 0, 0, this.height);
var length = this.options.background.length;
for(var i = 0; i < length; i++){
gradient.addColorStop((i+1) / length, 'rgba(' + this.options.background[i] + ')');
}
this.ctx.fillStyle = gradient;
}
this.ctx.globalOpacity = 0.1;
this.ctx.globalCompositeOperation = "darken";
this.ctx.fillRect(0, 0 , this.width, this.height);
}
// Draw
Canvas.prototype.draw = function() {
var index = -1;
var length = this.particles.length;
while(++index < length) {
var point = this.particles[index];
var color = point.color || this.options.color;
point.update(this.target, index);
this.ctx.globalAlpha = 0.3;
this.ctx.globalCompositeOperation = "lighten";
this.ctx.fillStyle = 'rgb(' + color + ')';
this.ctx.beginPath();
this.ctx.arc(point.position.x, point.position.y, point.size, 0, PI2, false);
this.ctx.closePath();
this.ctx.fill();
if(this.options.maxDistance > 0) {
this.drawLines(point, color);
}
}
}
// Draw connecting lines
Canvas.prototype.drawLines = function (point, color) {
color = color || this.options.color;
var index = -1;
var length = point.closest.length;
this.ctx.globalAlpha = 0.2;
this.ctx.globalCompositeOperation = "screen";
this.ctx.lineCap = 'round';
while(++index < length) {
this.ctx.lineWidth = (point.size * 2) * point.closest[index].opacity;
this.ctx.strokeStyle = 'rgba(250,250,250, ' + point.closest[index].opacity + ')';
this.ctx.beginPath();
this.ctx.moveTo(point.position.x, point.position.y);
this.ctx.lineTo(point.closest[index].x, point.closest[index].y);
this.ctx.stroke();
}
}
function Particle(options) {
options = _.clone(options || {});
this.options = _.defaults(options, this.options);
this.position = this.shift = new Vector(this.options.x, this.options.y);
this.speed = this.options.speed || 0.01 + Math.random() * 0.04;
this.angle = this.options.angle || 0;
if(this.options.color) {
var color = this.options.color.split(',');
var colorIndex = -1;
while(++colorIndex < 3) {
color[colorIndex] = Math.round(parseInt(color[colorIndex], 10) + (Math.random()*100)-50);
// Clamp
color[colorIndex] = Math.min(color[colorIndex], 255);
color[colorIndex] = Math.max(color[colorIndex], 0);
}
this.color = color.join(', ');
}
// Size
this.options.size = this.options.size || 7;
this.size = 1 + Math.random() * this.options.size;
this.targetSize = this.options.targetSize || this.options.size;
this.orbit = this.options.radius * 0.5 + (this.options.radius * 0.5 * Math.random());
}
Particle.prototype.update = function(target, index) {
this.angle += this.speed;
this.shift.x += (target.x - this.shift.x) * this.speed;
this.shift.y += (target.y - this.shift.y) * this.speed;
this.position.x = this.shift.x + Math.cos(index + this.angle) * this.orbit;
this.position.y = this.shift.y + Math.sin(index + this.angle) * this.orbit;
if(!isSafari) {
this.size += (this.targetSize - this.size) * 0.03;
if(Math.round(this.size) === Math.round(this.targetSize)) {
this.targetSize = 1 + Math.random() * this.options.size;
}
}
}
function Vector(x, y) {
this.x = x || 0;
this.y = y || 0;
}
Vector.prototype.distanceTo = function(vector, abs) {
var distance = Math.sqrt(Math.pow(this.x - vector.x, 2) + Math.pow(this.y - vector.y, 2));
return abs || false ? Math.abs(distance) : distance;
};
new Canvas({
el: document.getElementById('canvas'),
count: 25,
speed: 0.3,
radius: 6,
width: function() { return window.innerWidth; },
height: function() { return window.innerHeight; },
size: 15,
color: '30, 180, 1',
maxDistance: 100,
background: ['250,250,250,1', '215,216,215,0.8']
})
CSS
html, body {
min-height: 100%;
height: 100%;
}
#canvas {
width: 100%;
height: 100%;
position: fixed;
z-index: 100;
}
.page-content {
position: relative;
z-index: 900;
display: flex;
flex-direction: column;
}
HTML
<body>
<div id="container">
<canvas id="canvas"></canvas>
<div class="page-content">
</div>
</div>
</body>
This question already has answers here:
How do I rotate a single object on an html 5 canvas?
(8 answers)
Closed 2 years ago.
I want to simulate car rotation and movement in the new direction in a game I am designing.
According to the following answer, within the HTML5 canvas element you cannot rotate individual elements.
The movement itself is happening in this function, I expect the vehicle to move but the entire canvas moves (try pressing left and right). I couldn't figure out how to rotate the car.
Game._rotate = function(dirx, direction) {
this.ctx.translate(200,200);
}
According to this tutorial, I can only rotate the car itself by rotating the canvas. I'd like the rotation and movement to emulate the following: https://oseiskar.github.io/js-car/.
The code itself here: https://plnkr.co/edit/K5X8fMhUlRLhdeki.
You need to render the car relative to ITS position. Also, you can do the rotation inside its RENDER.
The changes below will handle rotation for the car, but you have bigger problems. I would take time and invest in learning how vectors work. The position, speed and acceleration should all be 2D vectors that provide vector math like adding and multiplication.
Also, provide an initial angle to your car so it is initially rendered the right way. It also appears that your acceleration and deceleration are mixed up.
function Car(map, x, y) {
this.map = map;
this.x = x;
this.y = y;
this.angle = 0; // IMPORTANT
this.width = map.tsize;
this.height = map.tsize;
this.image = Loader.getImage('car');
}
Car.speed = 0;
Car.acceleration = 0;
Car.friction = 5;
Car.moveAngle = 0;
Car.maxSpeed = 500;
Car.forward = 'FORWARD';
Car.backward = 'BACKWARD';
Car.left = 'LEFT';
Car.right = 'RIGHT';
// Render relative to car...
Car.prototype.render = function(ctx) {
ctx.save();
ctx.translate(this.x, this.y);
ctx.rotate(this.angle);
ctx.drawImage(this.image, -this.width / 2, -this.height / 2);
ctx.restore();
};
Game.update = function (delta) {
var dirx = 0;
var diry = 0;
if (Keyboard.isDown(Keyboard.LEFT)) {
this._rotate(dirx, Car.left)
}
else if (Keyboard.isDown(Keyboard.RIGHT)) {
this._rotate(dirx, Car.right)
}
else if (Keyboard.isDown(Keyboard.UP)) {
this._rotate(dirx, Car.up)
diry = accelerate(diry, Car.forward);
}
else if (Keyboard.isDown(Keyboard.DOWN)) {
this._rotate(dirx, Car.down)
diry = accelerate(diry, Car.backward);
}
else {
decelerate();
}
this.car.move(delta, dirx, diry);
this.camera.update();
};
Game._rotate = function(dirx, direction) {
let angleInDegrees = 0;
switch (direction) {
case 'UP':
angleInDegrees = 0;
break;
case 'RIGHT':
angleInDegrees = 90;
break;
case 'DOWN':
angleInDegrees = 180;
break;
case 'LEFT':
angleInDegrees = 270;
break;
}
this.car.angle = angleInDegrees * (Math.PI / 180);
}
Game.render = function () {
// draw map background layer
this._drawLayer(0);
this.car.render(this.ctx);
// draw map top layer
this._drawLayer(1);
};
Vectors
Here is an example using vectors.
loadImage('https://i.stack.imgur.com/JY7ai.png')
.then(function(img) {
main(img);
})
function main(img) {
let game = new Game({
canvas: document.querySelector('#viewport')
});
let car = new Car({
img: img,
imgRadiansOffset : -Math.PI / 2,
position: new Victor(game.canvas.width, game.canvas.height).divide(new Victor(2, 2)),
velocity: new Victor(0, -1),
showBorder: true
});
game.addLayer(car);
game.start();
}
class Game {
constructor(options) {
Object.assign(this, Game.defaultOptions, options);
if (this.canvas != null) {
Object.assign(this, {
width: this.canvas.width,
height: this.canvas.height
});
this.addListeners();
}
}
addLayer(layer) {
this.layers.push(layer);
layer.parent = this;
}
start() {
this.id = setInterval(() => {
this.render();
}, 1000 / this.rate); // frames per second
}
render() {
let ctx = this.canvas.getContext('2d');
ctx.clearRect(0, 0, this.width, this.height);
this.layers.forEach(layer => layer.render(ctx));
}
addListeners() {
if (this.canvas != null) {
window.addEventListener('keydown', (e) => {
this.handleKeyDown(e.keyCode);
});
window.addEventListener('keyup', (e) => {
this.handleKeyUp(e.keyCode);
});
}
}
handleKeyDown(keyCode) {
this.layers.forEach(layer => {
layer.update(keyCode !== this.lastKeyCode ? keyCode : null);
});
this.lastKeyCode = keyCode;
}
handleKeyUp(keyCode) {
this.layers.forEach(layer => {
layer.update(this.lastKeyCode); // calls reset...
});
}
}
Game.defaultOptions = {
id: null,
rate: 30,
layers: [],
canvas: null,
width: 0,
height: 0
};
class Car {
constructor(options) {
Object.assign(this, Car.defaultOptions, options);
if (this.img != null) {
Object.assign(this, {
width: this.img.width,
height: this.img.height
});
}
}
render(ctx) {
ctx.save();
ctx.translate(this.position.x, this.position.y);
ctx.rotate(this.velocity.angle() - this.imgRadiansOffset);
ctx.drawImage(this.img, -this.width / 2, -this.height / 2, this.width, this.height);
if (this.showBorder) {
ctx.strokeStyle = '#C00';
ctx.setLineDash([4, 8]);
ctx.lineWidth = 1;
ctx.beginPath();
ctx.rect(-this.width / 2, -this.height / 2, this.width, this.height);
ctx.stroke();
}
ctx.restore();
}
update(keyCode) {
if (keyCode != null) this.changeDirection(keyCode);
this.position.add(this.velocity.add(this.acceleration));
this.detectCollision();
}
detectCollision() {
let xMin = this.width / 2, xMax = this.parent.width - xMin;
let yMin = this.height / 2, yMax = this.parent.height - yMin;
if (this.position.x < xMin) this.position.x = xMin;
if (this.position.x > xMax) this.position.x = xMax;
if (this.position.y < yMin) this.position.y = yMin;
if (this.position.y > yMax) this.position.y = yMax;
}
changeDirection(keyCode) {
switch (keyCode) {
case 37:
this.reset(new Victor(-1, 0)); // LEFT
break;
case 38:
this.reset(new Victor(0, -1)); // UP
break;
case 39:
this.reset(new Victor(1, 0)); // RIGHT
break;
case 40:
this.reset(new Victor(0, 1)); // DOWN
break;
}
}
reset(dirVect) {
this.velocity = new Victor(this.speedFactor, this.speedFactor).multiply(dirVect);
this.acceleration = new Victor(this.accelFactor, this.accelFactor).multiply(dirVect);
}
}
Car.defaultOptions = {
position: new Victor(0, 0),
width: 0,
height: 0,
img: null,
imgRadiansOffset: 0,
velocity: new Victor(0, 0),
acceleration: new Victor(0, 0),
showBorder: false,
speedFactor: 3, // Velocity scalar
accelFactor: 1 // Acceleration scalar
};
function loadImage(url) {
return new Promise(function(resolve, reject) {
var img = new Image;
img.onload = function() {
resolve(this)
};
img.onerror = img.onabort = function() {
reject("Error loading image")
};
img.src = url;
})
}
#viewport {
border: thin solid grey;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/victor/1.1.0/victor.min.js"></script>
<canvas id="viewport" width="400" height="160"></canvas>
I am trying to make some sort of API for myself, and today I was working on the image loading system with Promises. I did some testing and for some reason the image of mario does not get displayed on the canvas. There was no error message. I've tried debugging it for a while but I can' get it working :(
Has anyone ideas on how to solve it?
When I was trying to debug it I noticed something weird: when you try drawing mario with grid.context.drawImage(mario, 0, 0); in e.g. Loader.add its displayed, if you do the same thing in Cellimage.draw its not.
I hope this helps :/
function random(a, b, c) {
c = c || 1;
return Math.floor(Math.random()*Math.floor((b-a+1)/c))*c+a;
}
function loop(n, callback) {
for (let i = 0; i < n; i++) {
callback();
}
}
function varNameToString(objVar) {
return Object.keys(objVar)[0];
}
function createCanvas(container, width, height, id) {
container = container || document.body;
id = id || null;
width = width || 0;
height || height || 0;
let canvas = document.createElement("canvas");
if (id != null) { canvas.setAttribute("id", id); }
canvas.width = width;
canvas.height = height;
return container.appendChild(canvas);
}
Array.prototype.random = function() {
let randomElement = random(0, this.length - 1);
return this.slice(randomElement, randomElement + 1);
}
class Loader {
constructor(){
this.loaderImages = [];
}
add(img, src, onLoad) {
this.loaderImages.push({promise: new Promise(resolve => {
img.onload = function(){
//grid.context.drawImage(mario, 0,0);
resolve(img);
}
img.src = src;
}), onLoad: function(){
onLoad();
}
});
}
load(img, onLoad) {
this.loaderImages.img.promise.then(onLoad());
}
loadAll(onLoad) {
Promise.all(this.loaderImages).then((imgs) => {
console.log(imgs);
onLoad(imgs);
});
}
}
class Vector2d {
constructor(x, y) {
this.x = x;
this.y = y;
}
static add(vec1, vec2) {
return new Vector(vec1.x + vec2.x, vec1.y + vec2.y);
}
static dotProduct(vec1, vec2) {
return vec1.x * vec2.x + vec1.y * vec2.y;
}
static determinant(vec1, vec2) {
return new Vector(vec1.x * vec2.y - vec1.y * vec2.x);
}
arg() {
return Math.tan(this.y/this.x);
}
abs() {
return Math.sqrt(this.x*this.x + this.y*this.y);
}
multiplyScalar(scalar) {
return new Vector(this.x * scalar, this.y * scalar);
}
}
class Grid {
constructor(context, cellCountX, cellCountY, width, height, options) {
this.options = options || {
gridOffsetX: 0,
gridOffsetY: 0
}
this.context = context;
this.cellCountX = cellCountX;
this.cellCountY = cellCountY;
this.width = width;
this.height = height;
this.cells = [];
this.griOffsetX = this.options.gridOffsetX;
this.griOffsetY = this.options.gridOffsetY;
for(let i = 0; i < cellCountX; i++) {
this.cells[i] = [];
for(let j = 0; j < cellCountY; j++) {
this.cells[i][j] = {
context: context,
x: i,
y: j,
xcoord: i*width,
ycoord: j*height,
color: "white",
opacity: 0.0,
images: {},
text: "",
font: "",
border: {
thickness: 0,
color: "",
moveCellOrigin: false
},
fill: function(color, cellX, cellY, fillWidth, fillHeight) {
cellX = cellX + this.xcoord || this.xcoord;
cellY = cellY + this.ycoord || this.ycoord;
fillWidth = fillWidth || width;
fillHeight = fillHeight || height;
context.fillStyle = color;
context.fillRect(cellX, cellY, fillWidth, fillHeight);
},
addImage: function(image, name, width, height) {
this.images[name] = (new CellImage(this, image, this.x, this.y, width, height));
}
}
}
}
}
loop(callback) {
for(let i = 0; i < this.cellCountX; i++) {
for(let j = 0; j < this.cellCountY; j++) {
callback(i, j);
}
}
}
updateImgs() {
loop((i, j) => {
this.cells[i, j].drawImage(this.cells[i, j].img.image, this.cells[i, j].img.width, this.cells[i, j].img.height);
});
}
updateAll() {
updateImg();
}
}
class CellImage {
constructor(grid, image, x, y, width, height, options) {
this.grid = grid;
this.image = image;
this.x = x;
this.y = y;
this.width = width || image.width;
this.height = height || image.height;
this.options = options || {
offsetX: 0,
offsetY: 0,
sourceOffsetX: 0,
sourceOffsetY: 0,
sourceWidth: null,
sourceHeight: null,
rotationAngle: 0,
centerOfRotation: "CENTER"
}
}
draw() {
this.options.sourceWidth = (this.options.sourceWidth === null) ? this.image.width : this.options.sourceWidth;
this.options.sourceHeight = (this.options.sourceHeight === null) ? this.image.height : this.options.sourceHeight;
if (this.options.rotationAngle % 360 !== 0) {
if (this.options.centerOfRotation === "CENTER") {
this.grid.context.save();
this.grid.context.translate(this.grid.cells[this.x, this.y].xcoord + this.width / 2, this.grid.cells[this.x, this.y].ycoord + this.height / 2);
this.grid.context.rotate(this.options.rotationAngle*Math.PI/180);
this.grid.context.drawImage(this.image, this.options.sourceOffsetX,this.options.sourceOffsetY,this.options.sourceWidth,this.options.sourceHeight, -this.width/2, -this.height/2, this.width, this.height);
this.grid.context.restore();
} else {
this.grid.context.save();
this.grid.context.translate(this.options.centerOfRotation.x, this.options.centerOfRotation.y);
this.grid.context.rotate(this.options.rotationAngle*Math.PI/180);
this.grid.context.drawImage(this.image, this.options.sourceOffsetX,this.options.sourceOffsetY,this.options.sourceWidth,this.options.sourceHeight, -this.width/2, -this.height/2, this.width, this.height);
this.grid.context.restore();
}
} else {
this.grid.context.drawImage(this.image, this.options.sourceOffsetX, this.options.sourceOffsetY, this.options.sourceWidth, this.options.sourceHeight, this.xcoord + this.options.offsetX, this.ycoord + this.options.offsetY, this.width, this.height);
this.grid.context.drawImage(this.image, 300, 300);
}
}
}
var canvas = createCanvas(document.body, 640, 640, "myCanvas");
const ctx = canvas.getContext("2d");
var grid = new Grid(ctx, 3, 3, 200, 200);
grid.loop((x, y) => {
let color = ((x + y)%2 === 1) ? "grey" : "darkgrey";
grid.cells[x][y].fill(color);
});
var mario = new Image(), luigi = new Image();
var loader = new Loader();
loader.add(mario, "mario.png", () => {
grid.cells[1][2].addImage(mario, "mario", 200, 200);
console.log(grid.cells[1][2].images);
grid.cells[1][2].images.mario.draw();
});
loader.loadAll(imgs => {
loader.loaderImages.forEach(loaderImage => {
loaderImage.onLoad();
});});
I have been struggling with a problem for some time now. I am trying to create a image onto a canvas, and when the user clicks on a div outside the canvas the background of the canvas should be changed to that image. This works, but the problem is that when I click a image nothing happens, if I click on a different image the image I clicked before gets loaded. This is making me so frustrated, and I really could need a set of new eyes on this.
The code that generates the image is very complex, it is a angular app and it depends on many directives to do some heavy lifting. The code that is generating the image is below:
var canvas = document.getElementById("image");
var context = canvas.getContext("2d");
// Setup the margin box
context.fillStyle = '#000000';
context.globalAlpha = 0;
context.fillRect(53, 53, 319, 107);
context.globalAlpha = 1;
$scope.resultTempImage = false;
$scope.generate = {
price: 1
};
$scope.color = "#ffffff";
$scope.product = {
bg: 'img/bg/bg-green-pattern.png',
icon: '',
color: "#000000",
font: 'Acme',
text1: "",
text2: "",
text3: "",
quantity: 120,
price: {
id: 1,
value: 17900
}
};
// Generates the first image
var firstLoadBg = false;
var bgImage = new Image();
var loadBg = function(image, callback) {
bgImage.src = '';
bgImage.onload = function() {
firstLoadBg = true;
callback(this);
};
bgImage.src = image;
}
var iconImage = new Image();
iconImage.onload = function() {
console.log('icon image loaded');
context.drawImage(this, 53, 53);
};
$scope.generateImage = function(product) {
/**
* Generates the image in the canvas
*/
// Generate a bg image
loadBg(product.bg, function(image) {
if (firstLoadBg == true) {
context.drawImage(image, 0, 0);
console.log('bg drawn');
}
if (product.icon) {
iconImage.src = '';
iconImage.src = product.icon;
}
// Add lines to the lines array
var lines = [];
if (product.text1 != "") {
lines.push(product.text1);
}
if (product.text2 != "") {
lines.push(product.text2);
}
if (product.text3 != "") {
lines.push(product.text3);
}
calculatePaint(lines, product.icon, product.font, function(Paint) {
/**
* Response from the Paint function, holds the position of the generated text.
*/
context.font = Paint.textSize;
context.fillStyle = product.color;
context.textAlign = "center";
context.textBaseline = "middle";
// Draw text lines
var y = Paint.bleedHeight;
angular.forEach(lines, function(value, key) {
if (lines.length == 2) {
context.textBaseline = "top";
if (key == 0) {
y = y - Paint.topPadding;
}
if (key == 1) {
y = y + Paint.padding;
}
}
if (lines.length == 3) {
context.textBaseline = "top";
if (key == 0) {
y = y - Paint.topPadding;
}
if (key == 1) {
y = y + Paint.padding;
}
if (key == 2) {
y = y + Paint.padding;
}
}
context.fillText(lines[key], Paint.bleedWidth, y, Paint.maxWidth);
});
var dataURL = canvas.toDataURL();
$scope.resultTempImage = dataURL;
console.log('done painting');
});
});
}
function calculatePaint(lines, icon, font, callback) {
/**
* #function for fitting text to the margin box
* #param line1 String
* #param line2 String
* #param line3 String
* #return obj
*/
// Margin of icon
var iconMargin = 90;
// Initial fontSize
var fontSize = 80;
// Bleed
var marginWidth = 53;
var marginHeight = 53;
// Inner box width
var w = 319;
var iconw = 319 - 100;
// Inner box height
var h = 107;
var Paint = {
bleedWidth: marginWidth + (w / 2),
bleedHeight: marginHeight + (h / 2),
maxWidth: w,
padding: 2,
topPadding: 53
};
if (icon) {
Paint.bleedWidth = marginWidth + 100 + (iconw / 2);
Paint.textSize = fontSize + 'px ' + font;
Paint.maxWidth = iconw;
fontSize = 60;
}
// Check lines array
if (lines.length == 1) {
// only one line is present
Paint.textSize = fontSize + 'px ' + font;
}
if (lines.length == 2) {
Paint.textSize = (Paint.bleedHeight / 2) - 8 + 'px ' + font;
Paint.padding = Paint.padding + (Paint.bleedHeight / 2) - 2;
}
if (lines.length == 3) {
Paint.textSize = (Paint.bleedHeight / 3) - 8 + 'px ' + font;
Paint.padding = Paint.padding + (Paint.bleedHeight / 3) - 2;
}
callback(Paint);
}
I found a solution to my own problem. What i did was preload all the images when the page load. Like this:
Data.get( 'assets/images' ).then( function( res ) {
if( res.success == true ) {
$scope.items = res.message;
angular.forEach( res.message, function( item, key ) {
preload( item.data, function(image) {
item.image = image;
} )
} );
}
} );
var preload = function( src, callback ) {
var image = new Image();
image.onload = function() {
callback( this );
}
image.src = src;
}
This preloads all my images and when im ready to draw my canvas I have all the image obj ready to go.