The code is different, I apologize may I didn't ask the question the right way, you can understand well in principle, When I'm hovering the dots get colours and connect to each other, other dots are invisible, I want that all dots should be visible all time. live example available on codepen, https://codepen.io/tati420/pen/RwBamQo?editors=1111
(function () {
var width,
height,
largeHeader,
canvas,
ctx,
points,
target,
animateHeader = true;
// Main
initHeader();
initAnimation();
addListeners();
function initHeader() {
width = window.innerWidth;
height = window.innerHeight;
target = { x: width / 2, y: height / 2 };
largeHeader = document.getElementById("large-header");
largeHeader.style.height = height + "px";
canvas = document.getElementById("demo-canvas");
canvas.width = width;
canvas.height = height;
ctx = canvas.getContext("2d");
// create points
points = [];
for (var x = 0; x < width; x = x + width / 20) {
for (var y = 0; y < height; y = y + height / 20) {
var px = x + (Math.random() * width) / 20;
var py = y + (Math.random() * height) / 20;
var p = { x: px, originX: px, y: py, originY: py };
points.push(p);
}
}
// for each point find the 5 closest points
for (var i = 0; i < points.length; i++) {
var closest = [];
var p1 = points[i];
for (var j = 0; j < points.length; j++) {
var p2 = points[j];
if (!(p1 == p2)) {
var placed = false;
for (var k = 0; k < 5; k++) {
if (!placed) {
if (closest[k] == undefined) {
closest[k] = p2;
placed = true;
}
}
}
for (var k = 0; k < 5; k++) {
if (!placed) {
if (getDistance(p1, p2) < getDistance(p1, closest[k])) {
closest[k] = p2;
placed = true;
}
}
}
}
}
p1.closest = closest;
}
// assign a circle to each point
for (var i in points) {
var c = new Circle(
points[i],
2 + Math.random() * 2,
"rgba(0,255,255,0.3)"
);
points[i].circle = c;
}
}
// Event handling
function addListeners() {
if (!("ontouchstart" in window)) {
window.addEventListener("mousemove", mouseMove);
}
window.addEventListener("scroll", scrollCheck);
window.addEventListener("resize", resize);
}
function mouseMove(e) {
var posx = (posy = 0);
if (e.pageX || e.pageY) {
posx = e.pageX;
posy = e.pageY;
} else if (e.clientX || e.clientY) {
posx =
e.clientX +
document.body.scrollLeft +
document.documentElement.scrollLeft;
posy =
e.clientY +
document.body.scrollTop +
document.documentElement.scrollTop;
}
target.x = posx;
target.y = posy;
}
function scrollCheck() {
if (document.body.scrollTop > height) animateHeader = false;
else animateHeader = true;
}
function resize() {
width = window.innerWidth;
height = window.innerHeight;
largeHeader.style.height = height + "px";
canvas.width = width;
canvas.height = height;
}
// animation
function initAnimation() {
animate();
for (var i in points) {
shiftPoint(points[i]);
}
}
function animate() {
if (animateHeader) {
ctx.clearRect(0, 0, width, height);
for (var i in points) {
// detect points in range
if (Math.abs(getDistance(target, points[i])) < 4000) {
points[i].active = 0.3;
points[i].circle.active = 0.6;
} else if (Math.abs(getDistance(target, points[i])) < 20000) {
points[i].active = 0.1;
points[i].circle.active = 0.3;
} else if (Math.abs(getDistance(target, points[i])) < 40000) {
points[i].active = 0.02;
points[i].circle.active = 0.1;
} else {
points[i].active = 0;
points[i].circle.active = 0;
}
drawLines(points[i]);
points[i].circle.draw();
}
}
requestAnimationFrame(animate);
}
function shiftPoint(p) {
TweenLite.to(p, 1 + 1 * Math.random(), {
x: p.originX - 50 + Math.random() * 100,
y: p.originY - 50 + Math.random() * 100,
ease: Circ.easeInOut,
onComplete: function () {
shiftPoint(p);
},
});
}
ctx.strokeStyle = "rgba(255,0,0," + p.active + ")";
// Canvas manipulation
function drawLines(p) {
if (!p.active) return;
for (var i in p.closest) {
ctx.beginPath();
ctx.moveTo(p.x, p.y);
ctx.lineTo(p.closest[i].x, p.closest[i].y);
ctx.strokeStyle = "rgba(0,255,255," + p.active + ")";
ctx.stroke();
}
}
function Circle(pos, rad, color) {
var _this = this;
// constructor
(function () {
_this.pos = pos || null;
_this.radius = rad || null;
_this.color = color || null;
})();
this.draw = function () {
if (!_this.active) return;
ctx.beginPath();
ctx.arc(_this.pos.x, _this.pos.y, _this.radius, 0, 2 * Math.PI, false);
ctx.fillStyle = "rgba(0,255,0," + _this.active + ")";
ctx.fill();
};
}
// Util
function getDistance(p1, p2) {
return Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2);
}
})();
If I understand your question correctly, you just want to make sure that every point.active = 1 and point.circle.active = 1:
function animate() {
if (animateHeader) {
ctx.clearRect(0, 0, width, height);
for (var i in points) {
points[i].active = 1;
points[i].circle.active = 1;
drawLines(points[i]);
points[i].circle.draw();
}
}
requestAnimationFrame(animate);
}
Related
What is wrong with the code, I can't find the problem. I think it's because of the drawFrame and loadImage function, but I don't know. So is there a way to make the image load and you will still be able to move it around and shoot?
And when I try to remove the drawFrame function nothing would work. And if posiible is there a way to change the bullet from squares to circles?
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta charset="UTF-8">
<style>
html,
body {
width: 100%;
height: 100%;
margin: 0;
}
canvas {
cursor: crosshair;
background-color: cornflowerblue;
}
</style>
</head>
<body>
<canvas id="Trump"></canvas>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
(async function main() {
var canvas = document.getElementById('Trump');
if (!canvas.getContext) return;
var ctx = canvas.getContext('2d');
ctx.canvas.width = window.innerWidth;
ctx.canvas.height = window.innerHeight;
//const fps = 60;
const ronaNum = 30; // number of particles at start
const ronaSize = 70; // corona size
const ronaSpeed = 100; // speed
let chinabmg;
function draw() {
$("canvas").one("click", function () {
chinabmg = new sound("stankytrumpchina.mp3");
chinabmg.play();
});
//mutes when clicked on m
document.body.onkeyup = function (e) {
if (e.keyCode == 77) { chinabmg.stop() }
};
//compatability
var requestAnimationFrame = window.requestAnimationFrame || //Chromium
window.webkitRequestAnimationFrame || //Webkit
window.mozRequestAnimationFrame || //Mozilla Geko
window.oRequestAnimationFrame || //Opera Presto
window.msRequestAnimationFrame || //IE Trident?
function (callback) { //Fallback function
window.setTimeout(callback, 1000 / 60);
};
var DrawX = 0;
var DrawY = 0;
var time = 0;
var width = canvas.width;
var height = canvas.height;
var offTop = canvas.offsetTop;
var offLeft = canvas.offsetLeft;
var rectWidth = 15;
var rectHeight = 15;
var speed = 1;
var x = width / 2;
var y = height / 2;
var size = 30;
var id = 0;
var bulletId = 0;
function sound(src) {
this.sound = document.createElement("audio");
this.sound.src = src;
this.sound.setAttribute("preload", "auto");
this.sound.setAttribute("controls", "none");
this.sound.setAttribute("loop", "auto");
this.sound.style.display = "none";
document.body.appendChild(this.sound);
this.play = function () {
this.sound.play();
}
this.stop = function () {
this.sound.pause();
}
}
function player(id, color, size, x, y) {
this.id = id;
this.color = color;
this.size = size;
this.x = x;
this.y = y;
this.speed = speed;
}
var playerList = [];
function addPlayer(color, size, x, y) {
playerList[id] = new player(id, color, size, x, y);
id += 1;
}
addPlayer("blue", size, width / 2 - 50, height / 2);
var pressedKeys = [];
function moveLeft(checkId, checkX, checkY, cSize, cSpeed, cKey) {
var x = checkX - cSpeed;
var y = checkY - cSpeed;
var x2 = checkX + cSize + cSpeed;
var y2 = checkY + cSize + cSpeed;
if (x > 0) {
playerList[checkId].x = checkX - cSpeed;
} else {
playerList[checkId].x = 0;
}
}
function moveUp(checkId, checkX, checkY, cSize, cSpeed, cKey) {
var x = checkX - cSpeed;
var y = checkY - cSpeed;
var x2 = checkX + cSize + cSpeed;
var y2 = checkY + cSize + cSpeed;
if (y > 0) {
playerList[checkId].y = checkY - cSpeed;
} else {
playerList[checkId].y = 0;
}
}
function moveRight(checkId, checkX, checkY, cSize, cSpeed, cKey) {
var x = checkX - cSpeed;
var y = checkY - cSpeed;
var x2 = checkX + cSize + cSpeed;
var y2 = checkY + cSize + cSpeed;
if (x2 < width) {
playerList[checkId].x = checkX + cSpeed;
} else {
playerList[checkId].x = width - cSize;
}
}
function moveDown(checkId, checkX, checkY, cSize, cSpeed, cKey) {
var x = checkX - cSpeed;
var y = checkY - cSpeed;
var x2 = checkX + cSize + cSpeed;
var y2 = checkY + cSize + cSpeed;
if (y2 < height) {
playerList[checkId].y = checkY + cSpeed;
} else {
playerList[checkId].y = height - cSize;
}
}
function Move(checkId, checkX, checkY, cSize, cSpeed, cKey) {
if (checkId === 0) {
switch (cKey) {
case 65:
// left
moveLeft(checkId, checkX, checkY, cSize, cSpeed, cKey);
break;
case 87:
// up
moveUp(checkId, checkX, checkY, cSize, cSpeed, cKey);
break;
case 68:
// right
moveRight(checkId, checkX, checkY, cSize, cSpeed, cKey);
break;
case 83:
// down
moveDown(checkId, checkX, checkY, cSize, cSpeed, cKey);
break;
default:
return; // exit this handler for other keys
}
}
}
var validKeys = [];
validKeys[0] = "65,87,68,83-107,109,80";
// == KEYDOWN ==
$(document.body).keydown(function (e) {
e.preventDefault();
//go through all players
$.each(playerList, function (i, currentPlayer) {
if (validKeys[currentPlayer.id].indexOf(e.which) == -1) return true;
if (!pressedKeys[e.which]) {
//set interval for the function
pressedKeys[e.which] = setInterval(function () {
Move(currentPlayer.id, currentPlayer.x, currentPlayer.y, currentPlayer.size, currentPlayer.speed, e.which);
}, 5);
}
});
});
// == KEYUP ==
$(document.body).keyup(function (e) {
if (pressedKeys[e.which]) {
clearInterval(pressedKeys[e.which]);
delete pressedKeys[e.which];
}
});
//=============================== SHOOTING ===============================
//Bullets
function bullet(id, color, size, speed, x, y, eX, eY) {
this.id = id;
this.color = color;
this.size = size;
this.x = x;
this.y = y;
this.eX = eX;
this.eY = eY;
this.velocityX = 1;
this.velocityY = 1;
this.speed = 9;
}
var bulletList = [];
function addBullet(color, bsize, bspeed, x, y, eX, eY) {
bulletList[bulletId] = new bullet(bulletId, color, bsize, 9, x, y, eX, eY);
bulletId += 1;
}
function updateBullet(bullet, player) {
var dx = (bullet.eX - player.x);
var dy = (bullet.eY - player.y);
var mag = Math.sqrt(dx * dx + dy * dy);
bullet.velocityX = (dx / mag) * 9;
bullet.velocityY = (dy / mag) * 9;
bullet.x += bullet.velocityX;
bullet.y += bullet.velocityY;
}
// Add event listener for `click` events.
canvas.onmousedown = function (e) {
addBullet("#696969", 10, 2, playerList[0].x, playerList[0].y, e.x, e.y);
};
//corona part
let corona = [];
createCoronaParticles();
// game loop
//setInterval(update, 1000 / fps);
function createCoronaParticles() {
corona = [];
let cx, cy;
for (var i = 0; i < ronaNum; i++) {
do {
cx = Math.floor(Math.random() * canvas.width);
cy = Math.floor(Math.random() * canvas.height);
} while (noSpawnOnFigure(canvas.height / 2, canvas.width / 2, cy, cx) < ronaSize * 5);
corona.push(newParticle(cx, cy));
}
}
function noSpawnOnFigure(cy1, cx1, cy2, cx2) {
return Math.sqrt(Math.pow(cy2 - cy1, 2) + Math.pow(cx2 - cx1, 2));
}
function newParticle(cx, cy) {
let rona = {
ca: Math.random() * Math.PI * 2, //radians
cr: ronaSize / 2,
cx: cx,
cy: cy,
cxv: Math.random() * ronaSpeed / 60 * (Math.random() < 0.5 ? 1 : -1),
cyv: Math.random() * ronaSpeed / 60 * (Math.random() < 0.5 ? 1 : -1)
};
return rona;
}
// function update() {
// }
// ======= DRAWING =======
function drawFrame() {
requestAnimationFrame(drawFrame);
ctx.font = "15pt Georgia"; // sets the font and font size of the text
ctx.clearRect(0, 0, width, height);
$.each(playerList, function (index, currentPlayer) {
//debug
//draw players
function loadImage(path) {
let image = new Image();
let promise = new Promise((resolve, reject) => {
image.onload = () => resolve(image);
image.onerror = reject
});
image.src = path;
return promise;
}
loadImage.src = 'trump.gif';
});
//draw bullets
$.each(bulletList, function (index, bullet) {
updateBullet(bullet, playerList[0]);
ctx.fillStyle = bullet.color;
ctx.fillRect(bullet.x, bullet.y, bullet.size, bullet.size);
});
// canvas
//ctx.clearRect(0, 0, canvas.width, canvas.height);
// draw corona particles
ctx.strokeStyle = "rgb(150, 0, 0)";
ctx.lineWidth = 20;
let ca, cr, cx, cy;
for (let i = 0; i < corona.length; i++) {
// get properties
ca = corona[i].ca;
cr = corona[i].cr;
cx = corona[i].cx;
cy = corona[i].cy;
// draw path
ctx.beginPath();
ctx.fillStyle = "rgb(200, 0, 0)"
ctx.moveTo(
cx + cr * Math.cos(ca),
cy + cr * Math.sin(ca)
);
// draw circle
for (let j = 1; j < 180; j++) {
ctx.lineTo(
cx + cr * Math.cos(ca + j * Math.PI * 2 / 180),
cy + cr * Math.sin(ca + j * Math.PI * 2 / 180)
);
}
ctx.closePath();
ctx.stroke();
ctx.fill();
// move particles
corona[i].cx += corona[i].cxv;
corona[i].cy += corona[i].cyv;
// particle edge of screen
if (corona[i].cx < 0 - corona[i].cr) {
corona[i].cx = canvas.width + corona[i].cr;
} else if (corona[i].cx > canvas.width + corona[i].cr) {
corona[i].cx = 0 - corona[i].cr
}
if (corona[i].cy < 0 - corona[i].cr) {
corona[i].cy = canvas.height + corona[i].cr;
} else if (corona[i].cy > canvas.height + corona[i].cr) {
corona[i].cy = 0 - corona[i].cr
}
}
}
drawFrame();
}
$(draw);
}
)();
</script>
</body>
</html>
//draw players
function loadImage(path) {
let image = new Image();
let promise = new Promise((resolve, reject) => {
image.onload = () => resolve(image);
image.onerror = reject
});
image.src = path;
//Add the following line
ctx.drawImage(image,playerList[0].x,playerList[0].y);
return promise;
}
// change loadImage.src = 'trump.gif' into this
loadImage('trump.gif');
I wish to create a simple webgame that involves a tiled board. I have a collection of svg's for the background of each square (i.e one for grass, one for stone, one for dirt etc). I also have svg's for items that will be displayed on the layer above the background (such as trees, wood, sword).
I have an in memory database of what the background for each square is and if and which item it contains.
I wish to be able to:
* Zoom in or out
* Scroll left or right
* Scolll up or down
* Have items displayed above the background for that square
Only needs to work in recent versions of modern browsers
What is the best approach for this:
1. Have a canvas object. Get the current zoom, top most XY, canvas width and canvas height. Loop though the squares in the in memory database and print the corresponding SVG's in the correct locations. Each time it is scrolled or zoomed reprint the entire board.
2. Have a div. Get the current zoom, top most XY, canvas width and canvas height. Loop though the squares in the in memory database and create SVG's in the correct locations.
Each time the board scrolls add new SVGs as they become visible, delete SVGs as they move of the board. Translate all the existing SVGs by the appropriate amount.
Each time the board zooms enlarge or shrink all the existing SVGs based on the new zoom level.
3. Some third approach that I am unaware of.
The example below uses two modules svg to load images (Any image format will work) & board which handles panning, zooming & rendering. It also provides an onClick event which will give you an object that describes the tile that has been clicked on.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<style>
body {
background-color: black;
}
canvas {
display: block;
margin: 30px auto 0px auto;
border: solid 1px white;
border-radius: 10px;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script type="application/javascript">
var svg = function() {
"use strict";
var svgImages = [];
var areImagesLoaded = true;
var isPageLoaded = false;
addEventListener("load",function() { isPageLoaded = true; isFinished(); });
var exports = {
onload: function() {},
request: null,
get: null
};
function isFinished() {
if (isPageLoaded && areImagesLoaded) {
exports.onload();
}
}
function onSvgError() {
this.isDone = true;
console.warn("SVG " + this.src + " failed to load.");
}
function onSvgLoaded() {
this.isDone = true;
for (var id in svgImages) {
if (!svgImages[id].isDone) {
return;
}
}
areImagesLoaded = true;
isFinished();
}
function request(id,path) {
if (svgImages[id]) {
return;
}
areImagesLoaded = false;
var img = document.createElement("img");
img.onerror = onSvgError;
img.onload = onSvgLoaded;
img.isDone = false;
img.id = id;
img.src = path;
svgImages[id] = img;
}
function get(id) {
return svgImages[id];
}
exports.request = request;
exports.get = get;
return exports;
}();
var board = function() {
"use strict";
var canvasWidth = 0;
var canvasHeight = 0;
var canvas = null;
var ctx = null;
var frameRequested = false;
var tileWidth = 0;
var tileHeight = 0;
var tileTypes = [];
var boardWidth = 0;
var boardHeight = 0;
var board = [];
var hasInitialized = false;
var camera = {
x: 0.0,
y: 0.0,
zoom: 1.0
};
function mapToBoard(x,y) {
var invZoom = 1.0 / camera.zoom;
return [
(x - (canvasWidth >> 1)) * invZoom - camera.x,
(y - (canvasHeight >> 1)) * invZoom - camera.y
];
}
var isMouseDragging = false;
var mouseStartX = 0;
var mouseStartY = 0;
var mouseLastX = 0;
var mouseLastY = 0;
var tileEvent = {
background: "",
foreground: "",
x: 0,
y: 0
};
function onTileSelected(e) {
}
function onMouseDown(e) {
isMouseDragging = true;
var bounds = canvas.getBoundingClientRect();
mouseStartX = mouseLastX = e.clientX - bounds.left;
mouseStartY = mouseLastY = e.clientY - bounds.top;
}
function onMouseUp(e) {
isMouseDragging = false;
var bounds = canvas.getBoundingClientRect()
var x = e.clientX - bounds.left - mouseStartX;
var y = e.clientY - bounds.top - mouseStartY;
var l = Math.sqrt(x * x + y * y);
if (l < 2.0) {
[x,y] = mapToBoard(e.clientX - bounds.left,e.clientY - bounds.top);
if (x > 0 && y > 0 && x < boardWidth * tileWidth && y < boardHeight * tileHeight) {
x = (x / tileWidth) | 0;
y = (y / tileHeight) | 0;
var tile = board[x + y * boardWidth];
tileEvent.background = tile.background;
tileEvent.foreground = tile.foreground;
tileEvent.x = x;
tileEvent.y = y;
} else {
tileEvent.background = "";
tileEvent.foreground = "";
tileEvent.x = -1;
tileEvent.y = -1;
}
onTileSelected(tileEvent);
}
}
function onMouseMove(e) {
if (hasInitialized && isMouseDragging) {
var bounds = canvas.getBoundingClientRect();
var x = e.clientX - bounds.left;
var y = e.clientY - bounds.top;
camera.x += (x - mouseLastX) / camera.zoom;
camera.y += (y - mouseLastY) / camera.zoom;
mouseLastX = x;
mouseLastY = y;
requestDraw();
}
}
function onWheel(e) {
if (Math.sign(e.deltaY) === -1) {
camera.zoom *= 1.1;
} else {
camera.zoom *= 0.9;
}
requestDraw();
}
function draw() {
ctx.fillStyle = "gray";
ctx.fillRect(-canvasWidth >> 1,-canvasHeight >> 1,canvasWidth,canvasHeight);
var _tileWidth = tileWidth * camera.zoom;
var _tileHeight = tileHeight * camera.zoom;
var _ox = camera.x * camera.zoom;
var _oy = camera.y * camera.zoom;
var _x = _ox;
var _y = _oy;
for (var x = 0; x <boardWidth; ++x) {
for (var y = 0; y < boardHeight; ++y) {
var index = x + y * boardWidth;
var tile = board[index];
var background = tileTypes[tile.background];
var foreground = tileTypes[tile.foreground];
if (background) {
ctx.drawImage(
background,
_x,
_y,
_tileWidth,
_tileHeight
);
}
if (foreground) {
ctx.drawImage(
foreground,
_x,
_y,
_tileWidth,
_tileHeight
);
}
_y += _tileHeight;
}
_y = _oy;
_x += _tileWidth;
}
frameRequested = false;
}
function requestDraw() {
if (!frameRequested) {
frameRequested = true;
requestAnimationFrame(draw);
}
}
return {
BACKGROUND: 0,
FOREGROUND: 1,
set canvas(canvasID) {
if (!hasInitialized) {
canvas = document.getElementById(canvasID);
canvas.onmousedown = onMouseDown;
canvas.onmouseup = onMouseUp;
canvas.onmousemove = onMouseMove;
canvas.onwheel = onWheel;
ctx = canvas.getContext("2d");
}
},
set canvasWidth(w) {
if (!hasInitialized && canvas) {
canvasWidth = canvas.width = w;
}
},
set canvasHeight(h) {
if (!hasInitialized && canvas) {
canvasHeight = canvas.height = h;
}
},
set tileWidth(w) {
if (!hasInitialized) {
tileWidth = w;
}
},
set tileHeight(h) {
if (!hasInitialized) {
tileHeight = h;
}
},
set width(w) {
if (!hasInitialized) {
boardWidth = w;
}
},
set height(h) {
if (!hasInitialized) {
boardHeight = h;
}
},
set onTileSelected(callback) {
onTileSelected = callback;
},
get width() {
return boardWidth;
},
get height() {
return boardHeight;
},
get onTileSelected() {
return onTileSelected;
},
defineTileTypes: function(types) {
if (types.length % 2 !== 0) {
return;
}
for (var i = 0; i < types.length; i += 2) {
var id = types[i];
var img = types[i + 1];
tileTypes[id] = img;
}
},
init: function() {
camera.x = -(boardWidth >> 1) * tileWidth;
camera.y = -(boardHeight >> 1) * tileHeight;
ctx.translate(canvasWidth >> 1,canvasHeight >> 1);
board.length = boardWidth * boardHeight;
for (var i = 0; i < board.length; ++i) {
board[i] = {
background: "",
foreground: ""
};
}
hasInitialized = true;
requestAnimationFrame(draw);
},
set: function(type,id,x,y) {
if (hasInitialized
&& tileTypes[id]
&& x > -1
&& x < boardWidth
&& y > -1
&& y < boardHeight) {
var index = x + y * boardWidth;
if (type === this.BACKGROUND) {
board[index].background = id;
} else {
board[index].foreground = id;
}
requestDraw();
}
}
};
}();
void function() {
"use strict";
svg.request("grass","https://i.stack.imgur.com/CkvU7.png");
svg.request("water","https://i.stack.imgur.com/an6a5.png");
svg.onload = function() {
board.canvas = "canvas";
board.canvasWidth = 180;
board.canvasHeight = 160;
board.tileWidth = 25;
board.tileHeight = 25;
board.width = 20;
board.height = 20;
board.defineTileTypes([
"GRASS",svg.get("grass"),
"WATER",svg.get("water")
]);
board.init();
for (var x = 0; x < board.width; ++x) {
for (var y = 0; y < board.height; ++y) {
board.set(board.BACKGROUND,"WATER",x,y);
if (Math.random() > 0.2) {
board.set(board.BACKGROUND,"GRASS",x,y);
} else {
board.set(board.BACKGROUND,"WATER",x,y);
}
}
}
}
board.onTileSelected = function(e) {
if (e.background === "GRASS") {
board.set(board.BACKGROUND,"WATER",e.x,e.y);
} else {
board.set(board.BACKGROUND,"GRASS",e.x,e.y);
}
}
}();
</script>
</body>
</html>
I am making a clone of agar.io and I am stuck in my code. I can't understand why my camera's position is not correctly calculated. I want my camera's position to half the vector between the farthest blob and the closest blob.
Below is a picture and my code:
<html>
<head>
<title>Play Agario Clone</title>
<style>
body {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<canvas id="game">
kindly update your browser.
</canvas>
<script>
var
canvas,
ctx,
width = innerWidth,
height = innerHeight,
mouseX = 0,
mouseY = 0;
var
camera = {
x: 0,
y: 0,
// camera
update: function(obj) {
var farthestBlobX = Math.max.apply(0, obj.blobs.map(function(cell) { return cell.x }));
var farthestBlobY = Math.max.apply(0, obj.blobs.map(function(cell) { return cell.y }));
var closestBlobX = Math.min.apply(0, obj.blobs.map(function(cell) { return cell.x }));
var closestBlobY = Math.min.apply(0, obj.blobs.map(function(cell) { return cell.y }));
var x = farthestBlobX - closestBlobX;
var y = farthestBlobY - closestBlobY;
var length = Math.sqrt(x * x + y * y);
this.x = length/2 - width/2;
this.y = length/2 - height/2;
}
},
player = {
defaultMass: 54,
x: 0,
y: 0,
blobs: [],
update: function () {
for (var i = 0; i < this.blobs.length; i ++) {
var x = mouseX + camera.x - this.blobs[i].x;
var y = mouseY + camera.y - this.blobs[i].y;
var length = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
var speed = 54/this.blobs[i].mass;
this.blobs[i].velX = x/length * speed * Math.min(1, Math.pow(x / this.blobs[i].mass, 2));
this.blobs[i].velY = y/length * speed * Math.min(1, Math.pow(x / this.blobs[i].mass, 2));
this.blobs[i].x += this.blobs[i].velX;
this.blobs[i].y += this.blobs[i].velY;
for (var j = 0; j < this.blobs.length; j ++) {
if (j != i && this.blobs[i] !== undefined) {
var blob1 = this.blobs[i];
var blob2 = this.blobs[j];
var x = blob2.x - blob1.x;
var y = blob2.y - blob1.y;
var dist = Math.sqrt(x * x + y * y);
if (dist < blob1.mass + blob2.mass) {
x /= dist;
y /= dist;
blob1.x = blob2.x - x * (blob1.mass + blob2.mass);
blob1.y = blob2.y - y * (blob1.mass + blob2.mass);
}
}
}
}
this.x += (mouseX - width/2)/(width/2) * 1;
this.y += (mouseY - height/2)/(height/2) * 1
},
split: function (cell) {
cell.mass /= 2;
this.blobs.push({
x: cell.x,
y: cell.y,
mass: cell.mass
});
},
draw: function () {
for (var i = 0; i < this.blobs.length; i ++) {
ctx.fillStyle = "red";
ctx.beginPath();
ctx.arc(-camera.x + this.blobs[i].x, -camera.y + this.blobs[i].y, this.blobs[i].mass, 0, Math.PI*2);
ctx.fill();
ctx.closePath();
}
}
};
function handleMouseMove (e) {
mouseX = e.clientX;
mouseY = e.clientY;
}
function setup () {
canvas = document.getElementById("game");
ctx = canvas.getContext("2d");
canvas.width = width;
canvas.height = height;
addEventListener("mousemove", handleMouseMove);
player.blobs.push({
x: 0,
y: 0,
mass: player.defaultMass
});
player.blobs.push({
x: 100,
y: 100,
mass: player.defaultMass/2
});
player.blobs.push({
x: 100,
y: 100,
mass: player.defaultMass*2
});
var loop = function () {
update();
draw();
requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
}
function update () {
camera.update(player);
player.update();
}
function draw () {
ctx.fillStyle = "#fff";
ctx.fillRect(0, 0, width, height);
player.draw();
}
setup();
</script>
</body>
</html>
Instead of computing everything relative to your camera, use your camera to set the global transformation matrix of your canvas, and only for this.
This way, your blobs' updates will be cleaner, and your camera easier to manage.
Now to get the middle position between two points, do (pt1 + pt2) / 2.
You were not clear in your question, if fartherstX and fartherstY should represent the same blob. In your code it wasn't, so I didn't change it.
Also, I didn't gone into all your logics, but beware NaN values, I got some while doing the edit.
function draw() {
var cw = ctx.canvas.width / 2;
var ch = ctx.canvas.height / 2;
// reset transform to clear the canvas
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.fillStyle = "#fff";
ctx.fillRect(0, 0, width, height);
// here we really set the camera position
ctx.setTransform(1, 0, 0, 1, -camera.x + cw, -camera.y + ch);
ctx.strokeRect(0, 0, width, height); // just to show the original area
player.draw();
}
var
canvas,
ctx,
width = innerWidth,
height = innerHeight,
mouseX = 0,
mouseY = 0;
var camera = {
x: 0,
y: 0,
// camera
update: function(obj) {
var farthestBlobX = Math.max.apply(0, obj.blobs.map(function(cell) {
return cell.x
}));
var farthestBlobY = Math.max.apply(0, obj.blobs.map(function(cell) {
return cell.y
}));
var closestBlobX = Math.min.apply(0, obj.blobs.map(function(cell) {
return cell.x
}));
var closestBlobY = Math.min.apply(0, obj.blobs.map(function(cell) {
return cell.y
}));
this.x = (closestBlobX + farthestBlobX) / 2 || 0;
this.y = (closestBlobY + farthestBlobY) / 2 || 0;
}
},
player = {
defaultMass: 54,
x: 0,
y: 0,
blobs: [],
update: function() {
for (var i = 0; i < this.blobs.length; i++) {
var x = mouseX - this.blobs[i].x || 0;
var y = mouseY - this.blobs[i].y || 0;
var length = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
var speed = 54 / this.blobs[i].mass;
this.blobs[i].velX = x / length * speed * Math.min(1, Math.pow(x / this.blobs[i].mass, 2));
this.blobs[i].velY = y / length * speed * Math.min(1, Math.pow(x / this.blobs[i].mass, 2));
this.blobs[i].x += this.blobs[i].velX;
this.blobs[i].y += this.blobs[i].velY;
for (var j = 0; j < this.blobs.length; j++) {
if (j != i && this.blobs[i] !== undefined) {
var blob1 = this.blobs[i];
var blob2 = this.blobs[j];
var x = blob2.x - blob1.x;
var y = blob2.y - blob1.y;
var dist = Math.sqrt(x * x + y * y);
if (dist < blob1.mass + blob2.mass) {
x /= dist;
y /= dist;
blob1.x = blob2.x - x * (blob1.mass + blob2.mass);
blob1.y = blob2.y - y * (blob1.mass + blob2.mass);
}
}
}
}
this.x += (mouseX - width / 2) / (width / 2) * 1;
this.y += (mouseY - height / 2) / (height / 2) * 1;
},
split: function(cell) {
cell.mass /= 2;
this.blobs.push({
x: cell.x,
y: cell.y,
mass: cell.mass
});
},
draw: function() {
for (var i = 0; i < this.blobs.length; i++) {
ctx.fillStyle = "red";
ctx.beginPath();
ctx.arc(this.blobs[i].x, this.blobs[i].y, this.blobs[i].mass, 0, Math.PI * 2);
ctx.fill();
ctx.closePath();
}
}
};
function handleMouseMove(e) {
mouseX = e.clientX;
mouseY = e.clientY;
}
function setup() {
canvas = document.getElementById("game");
ctx = canvas.getContext("2d");
canvas.width = width;
canvas.height = height;
addEventListener("mousemove", handleMouseMove);
player.blobs.push({
x: 10,
y: 10,
mass: player.defaultMass
});
player.blobs.push({
x: 100,
y: 100,
mass: player.defaultMass / 2
});
player.blobs.push({
x: 100,
y: 100,
mass: player.defaultMass * 2
});
var loop = function() {
update();
draw();
requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
}
function update() {
camera.update(player);
player.update();
}
setup();
body {
margin: 0;
padding: 0;
}
<canvas id="game">kindly update your browser.</canvas>
I see there is an answer already..
var canvas;
var ctx;
var width = innerWidth;
var height = innerHeight;
var mouseX = 0;
var mouseY = 0;
const camera = {
x : 0,
y : 0,
update(obj) { // camera
this.x = (obj.blobsExtent.minx + obj.blobsExtent.maxx) / 2;
this.y = (obj.blobsExtent.miny + obj.blobsExtent.maxy) / 2;
this.x -= width / 2;
this.y -= height / 2;
}
};
const player = {
defaultMass : 54,
blobs : [],
blobsExtent : { // get the extent while updating the blobs save you having to iterate all the objects a second time to get extent
minx :0,
miny : 0,
maxx : 0,
maxy : 0,
},
update () {
var be = this.blobsExtent; // be for Blob Extent alias to save typing and make code easier to read
for (var i = 0; i < this.blobs.length; i++) {
var blob1 = this.blobs[i];
var x = mouseX - blob1.x;
var y = mouseY - blob1.y;
// to stop the divide by zero propigating NaN set length to 1 if less than 1
var length = Math.max(1,Math.sqrt(x * x + y * y)); // x * x is quicker than Math.pow(x,2)
var speed = 54 / blob1.mass;
blob1.velX = x / length * speed * Math.min(1, Math.pow(x / blob1.mass, 2));
blob1.velY = y / length * speed * Math.min(1, Math.pow(x / blob1.mass, 2));
blob1.x += blob1.velX;
blob1.y += blob1.velY;
for (var j = 0; j < this.blobs.length; j++) {
if (j != i) {
var blob2 = this.blobs[j];
var x = blob2.x - blob1.x;
var y = blob2.y - blob1.y;
var dist = Math.sqrt(x * x + y * y);
var radTotal = blob1.mass + blob2.mass;
if (dist < radTotal) {
x /= dist;
y /= dist;
blob1.x = blob2.x - x * radTotal;
blob1.y = blob2.y - y * radTotal;
}
}
}
if(i === 0){ // use first blob to setup min max
be.maxx = be.minx = blob1.x;
be.maxy = be.miny = blob1.y;
}else{
be.maxx = Math.max(be.maxx, blob1.x);
be.maxy = Math.max(be.maxy, blob1.y);
be.minx = Math.min(be.minx, blob1.x);
be.miny = Math.min(be.miny, blob1.y);
}
}
},
split (cell) {
cell.mass /= 2;
this.blobs.push(createBlob(cell.x, cell.y, cell.mass));
},
draw () {
var b; // alias for blob
ctx.fillStyle = "red"; // all the same colour then can render as one path
ctx.setTransform(1,0,0,1,-camera.x,-camera.y);
ctx.beginPath();
for (var i = 0; i < this.blobs.length; i++) {
b = this.blobs[i];
ctx.arc( b.x, b.y, b.mass, 0, Math.PI * 2);
ctx.closePath();
}
ctx.fill();
ctx.setTransform(1,0,0,1,0,0); // restore default transform
}
};
function handleMouseMove(e) {
mouseX = e.clientX + camera.x;
mouseY = e.clientY + camera.y;
}
function createBlob(x,y,mass){ return {x,y,mass} }
function setup() {
canvas = document.getElementById("game");
ctx = canvas.getContext("2d");
canvas.width = width;
canvas.height = height;
addEventListener("mousemove", handleMouseMove);
player.blobs.push(createBlob(0,0,player.defaultMass));
player.blobs.push(createBlob(100,100,player.defaultMass / 2));
player.blobs.push(createBlob(100,100,player.defaultMass * 2));
}
function update() {
camera.update(player);
player.update();
}
function draw() {
ctx.fillStyle = "#fff";
ctx.fillRect(0, 0, width, height);
player.draw();
}
function loop() {
update();
draw();
requestAnimationFrame(loop);
}
setup();
requestAnimationFrame(loop);
body {
margin: 0;
padding: 0;
}
<canvas id="game"></canvas>
I am trying to create a animation where circles run from right to left. The circles' colours are selected randomly by a function. I have created a fiddle where one circle runs from right to left. Now my function creates a random colour. This function is executed every second and the circle changes its colour every second, instead of a new circle with the random picked colour become created. How can I change it so that it draws a new circle every second on the canvas and doesn't only change the colour of the circle?
This is my function:
function getRandomElement(array) {
if (array.length == 0) {
return undefined;
}
return array[Math.floor(Math.random() * array.length)];
}
var circles = [
'#FFFF00',
'#FF0000',
'#0000FF'
];
function drawcircles() {
ctx.beginPath();
ctx.arc(ballx * 108, canvasHeight / 2, x*5, 0, 2*Math.PI, false);
ctx.fillStyle = getRandomElement(circles);
ctx.fill();
ctx.closePath;
}
Comments on your question:
You are changing the color of the fillStyle to a random color at each frame. This is the reason why it keeps "changing" color. Set it to the color of the circle:
context.fillStyle = circle.color;
make circles with x, y, diameter, bounciness, speed, and color using an array
draw and update them with requestAnimationFrame (mine is a custom function)
My answer:
I made this just last night, where some circles follow the cursor and "bounce" off the edges of the screen. I tested the code and it works.
I might post a link later, but here is all of the code right now...
<!DOCTYPE html>
<html>
<body>
<canvas id="canvas"></canvas>
<script>
var lastTime = 0;
function requestMyAnimationFrame(callback, time)
{
var t = time || 16;
var currTime = new Date().getTime();
var timeToCall = Math.max(0, t - (currTime - lastTime));
var id = window.setTimeout(function(){ callback(currTime + timeToCall); }, timeToCall);
lastTime = currTime + timeToCall;
return id;
}
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
canvas.width = window.innerWidth - 20;
canvas.height = window.innerHeight - 20;
canvas.style.width = canvas.width + "px";
canvas.style.height = canvas.height + "px";
var circles = [];
var mouse =
{
x: 0,
y: 0
}
function getCoordinates(x, y)
{
return "(" + x + ", " + y + ")";
}
function getRatio(n, d)
{
// prevent division by 0
if (d === 0 || n === 0)
{
return 0;
}
else
{
return n/d;
}
}
function Circle(x,y,d,b,s,c)
{
this.x = x;
this.y = y;
this.diameter = Math.round(d);
this.radius = Math.round(d/2);
this.bounciness = b;
this.speed = s;
this.color = c;
this.deltaX = 0;
this.deltaY = 0;
this.drawnPosition = "";
this.fill = function()
{
context.beginPath();
context.arc(this.x+this.radius,this.y+this.radius,this.radius,0,Math.PI*2,false);
context.closePath();
context.fill();
}
this.clear = function()
{
context.fillStyle = "#ffffff";
this.fill();
}
this.draw = function()
{
if (this.drawnPosition !== getCoordinates(this.x, this.y))
{
context.fillStyle = this.color;
// if commented, the circle will be drawn if it is in the same position
//this.drawnPosition = getCoordinates(this.x, this.y);
this.fill();
}
}
this.keepInBounds = function()
{
if (this.x < 0)
{
this.x = 0;
this.deltaX *= -1 * this.bounciness;
}
else if (this.x + this.diameter > canvas.width)
{
this.x = canvas.width - this.diameter;
this.deltaX *= -1 * this.bounciness;
}
if (this.y < 0)
{
this.y = 0;
this.deltaY *= -1 * this.bounciness;
}
else if (this.y+this.diameter > canvas.height)
{
this.y = canvas.height - this.diameter;
this.deltaY *= -1 * this.bounciness;
}
}
this.followMouse = function()
{
// deltaX/deltaY will currently cause the circles to "orbit" around the cursor forever unless it hits a wall
var centerX = Math.round(this.x + this.radius);
var centerY = Math.round(this.y + this.radius);
if (centerX < mouse.x)
{
// circle is to the left of the mouse, so move the circle to the right
this.deltaX += this.speed;
}
else if (centerX > mouse.x)
{
// circle is to the right of the mouse, so move the circle to the left
this.deltaX -= this.speed;
}
else
{
//this.deltaX = 0;
}
if (centerY < mouse.y)
{
// circle is above the mouse, so move the circle downwards
this.deltaY += this.speed;
}
else if (centerY > mouse.y)
{
// circle is under the mouse, so move the circle upwards
this.deltaY -= this.speed;
}
else
{
//this.deltaY = 0;
}
this.x += this.deltaX;
this.y += this.deltaY;
this.x = Math.round(this.x);
this.y = Math.round(this.y);
}
}
function getRandomDecimal(min, max)
{
return Math.random() * (max-min) + min;
}
function getRoundedNum(min, max)
{
return Math.round(getRandomDecimal(min, max));
}
function getRandomColor()
{
// array of three colors
var colors = [];
// go through loop and add three integers between 0 and 255 (min and max color values)
for (var i = 0; i < 3; i++)
{
colors[i] = getRoundedNum(0, 255);
}
// return rgb value (RED, GREEN, BLUE)
return "rgb(" + colors[0] + "," + colors[1] + ", " + colors[2] + ")";
}
function createCircle(i)
{
// diameter of circle
var minDiameter = 25;
var maxDiameter = 50;
// bounciness of circle (changes speed if it hits a wall)
var minBounciness = 0.2;
var maxBounciness = 0.65;
// speed of circle (how fast it moves)
var minSpeed = 0.3;
var maxSpeed = 0.45;
// getRoundedNum returns a random integer and getRandomDecimal returns a random decimal
var x = getRoundedNum(0, canvas.width);
var y = getRoundedNum(0, canvas.height);
var d = getRoundedNum(minDiameter, maxDiameter);
var c = getRandomColor();
var b = getRandomDecimal(minBounciness, maxBounciness);
var s = getRandomDecimal(minSpeed, maxSpeed);
// create the circle with x, y, diameter, bounciness, speed, and color
circles[i] = new Circle(x,y,d,b,s,c);
}
function makeCircles()
{
var maxCircles = getRoundedNum(2, 5);
for (var i = 0; i < maxCircles; i++)
{
createCircle(i);
}
}
function drawCircles()
{
var ii = 0;
for (var i = 0; ii < circles.length; i++)
{
if (circles[i])
{
circles[i].draw();
ii++;
}
}
}
function clearCircles()
{
var ii = 0;
for (var i = 0; ii < circles.length; i++)
{
if (circles[i])
{
circles[i].clear();
ii++;
}
}
}
function updateCircles()
{
var ii = 0;
for (var i = 0; ii < circles.length; i++)
{
if (circles[i])
{
circles[i].keepInBounds();
circles[i].followMouse();
ii++;
}
}
}
function update()
{
requestMyAnimationFrame(update,10);
updateCircles();
}
function draw()
{
requestMyAnimationFrame(draw,1000/60);
context.clearRect(0,0,canvas.width,canvas.height);
drawCircles();
}
function handleError(e)
{
//e.preventDefault();
//console.error(" ERROR ------ " + e.message + " ------ ERROR ");
}
window.addEventListener("load", function()
{
window.addEventListener("error", function(e)
{
handleError(e);
});
window.addEventListener("resize", function()
{
canvas.width = window.innerWidth - 20;
canvas.height = window.innerHeight - 20;
});
window.addEventListener("mousemove", function(e)
{
mouse.x = e.layerX || e.offsetX;
mouse.y = e.layerY || e.offsetY;
});
makeCircles();
update();
draw();
});
</script>
</body>
</html>
When I run this script on Safari, everything works fine. However when I open it up in Chrome or Firefox it does not execute correctly. In the Chrome console it says that there are Uncaught type errors for:
ctx.drawImage(img, x + 1, y + 1, iwh, iwh);
and
function renderStarField() {
ctx.fillStyle = '#000000';
ctx.fillRect(0, 0, WIDTH, HEIGHT);
for (var i = 0; i < stars.length; i++) {
stars[i].plot();
}
}
Here is my entire script, thanks for the help!
<script type="text/javascript">
var starField = (function () {
var browserWIDTH = $(document).width(),
browserHEIGHT = $(document).height(),
WIDTH = browserWIDTH + 500,
HEIGHT = 400,
FIELD_DEPTH = 15,
DISTANCE = 500,
STAR_DIAMETER = 45,
STAR_SPEED = 0.003,
canvas,
ctx,
numStars = 2000,
stars = [];
function Star() {
this.calcPosition();
var RANDSTAR = Math.floor(Math.random() * 3) + 1;
}
Star.prototype.calcPosition = function (reset) {
this.x = this.randomise(-25, 50);
this.y = this.randomise(-25, 50);
this.z = reset ? FIELD_DEPTH : this.randomise(1, FIELD_DEPTH);
};
Star.prototype.randomise = function (min, max) {
return Math.floor((Math.random() * max) + min);
};
Star.prototype.plot = function () {
//calculate 3d to 2d using perspective projection with the screen as the origin
var x = this.x * (DISTANCE / this.z) + WIDTH / 2,
y = this.y * (DISTANCE / this.z) + HEIGHT / 2;
if ((x >= 0 && x <= WIDTH) && (y >= 0 && y <= HEIGHT)) {
ctx.beginPath();
var img = document.createElement('image');
img.src ='Star1.png';
var iwh = this.calcSize(this.z);
ctx.moveTo(x, y);
ctx.drawImage(img, x + 1, y + 1, iwh, iwh);
}
this.z -= STAR_SPEED;
if (this.z <= 0) {
this.calcPosition(true);
}
};
Star.prototype.calcColor = function (z) {
var rgb = Math.abs((z * 5) - 255).toFixed(0),
a = (1 - ((z / (FIELD_DEPTH / 100)) / 100)).toFixed(1);
return 'rgba(' + rgb + ', ' + rgb + ', ' + rgb + ', ' + a + ')';
};
Star.prototype.calcSize = function (z) {
return Math.abs(((z / (FIELD_DEPTH / 100)) * (STAR_DIAMETER / 100)) - STAR_DIAMETER);
};
function setUpCanvas() {
canvas = document.querySelector('#stage');
canvas.width = WIDTH;
canvas.height = HEIGHT;
ctx = canvas.getContext('2d');
}
function buildStars() {
for (var i = 0; i < numStars; i++) {
stars.push(new Star());
}
}
function renderStarField() {
ctx.fillStyle = '#000000';
ctx.fillRect(0, 0, WIDTH, HEIGHT);
for (var i = 0; i < stars.length; i++) {
stars[i].plot();
}
}
function initialise() {
setUpCanvas();
buildStars();
setInterval(renderStarField, 20);
}
return {
init: initialise
}
})();
document.addEventListener('DOMContentLoaded', function () {
starField.init();
});
</script>
if ((x >= 0 && x <= WIDTH) && (y >= 0 && y <= HEIGHT)) {
ctx.beginPath();
var img = document.createElement('image');
img.src ='Star1.png';
var iwh = this.calcSize(this.z);
ctx.moveTo(x, y);
ctx.drawImage(img, x + 1, y + 1, iwh, iwh);
}
This code has some error
img.src = 'Star1.png' is not working, try img.setAttribute('src','Star1.png');
and Create <img> tag code is not document.createElement('image')
try document.createElement('img');
Change To
if ((x >= 0 && x <= WIDTH) && (y >= 0 && y <= HEIGHT)) {
ctx.beginPath();
var img = document.createElement('img');
img.setAttribute('src','Star1.png');
var iwh = this.calcSize(this.z);
ctx.moveTo(x, y);
ctx.drawImage(img, x + 1, y + 1, iwh, iwh);
}