I have a canvas element that is currently animating lines. However I want to make an array of stored functions that change the numbers and colors of those lines animating in the canvas.
Thus, when I click a specific element, it will select one of the functions in an array which change the colors, speed, line-width, amplitude, etc. to one of those functions.
So let's say I have an array of functions, settings = [A, B, C, D];
where A to D are functions that change the canvas.
Ultimately, I want it so that when I click an element I randomly change the canvas element's settings to those in A, B, C, or D.
I have the following code but am having trouble refactoring the click function to include an array of settings to separate functions. Any help?
Below is the following code I have so far:
var c = document.querySelector('.c') /* canvas element */,
w /* canvas width */, h /* canvas height */,
ctx = c.getContext('2d') /* canvas context */,
/* previous & current coordinates */
x0, y0, x, y,
t = 0, t_step = 1/600,
u = 4, m,
tmp,
/* just me being lazy */
ceil = Math.ceil,
exp = Math.exp, pow = Math.pow, sqrt = Math.sqrt,
PI = Math.PI, sin = Math.sin, cos = Math.cos;
/* FUNCTIONS */
/* a random number between min & max */
var rand = function(max, min) {
var b = (max === 0 || max) ? max : 1, a = min || 0;
return a + (b - a)*Math.random();
};
var trimUnit = function(input_str, unit) {
return parseInt(input_str.split(unit)[0], 10);
};
var initCanvas = function() {
var s = getComputedStyle(c);
w = c.width = trimUnit(s.width, 'px');
h = c.height = trimUnit(s.height, 'px');
m = ceil(w/(10*u)) + 90;
};
var wave = function () {
ctx.clearRect(0, 0, w, h);
ctx.lineWidth = 1.75;
for(var i = 0; i < m; i++) {
x0 = -80;
y0 = i*4*u;
ctx.beginPath();
ctx.moveTo(x0, y0);
for(x = 0; x < w; x++) {
y = u*sin(x/4/(10*i/m + 1) - w*(i/m + 2)*t/20) + i*2*u;
ctx.lineTo(x, y);
x0 = x;
y0 = y;
}
ctx.strokeStyle = 'hsl(' + i*360/m + ', 100%, 70%)';
ctx.stroke();
}
t += t_step;
requestAnimationFrame(wave);
};
addEventListener('resize', initCanvas, false);
initCanvas();
wave();
/*Moods*/
var red = function () {
ctx.clearRect(0, 0, w, h);
ctx.lineWidth = 10;
for(var i = 0; i < m; i++) {
x0 = -100;
y0 = i*8*u;
ctx.beginPath();
ctx.moveTo(x0, y0);
for(x = 0; x < w; x++) {
y = u*sin(x/4/(16*i/m + 1) - w*(i/m + 1)*t/12) + i*2.5*u;
ctx.lineTo(x, y);
x0 = x;
y0 = y;
}
var gradient=ctx.createLinearGradient(0,1000,0,0);
gradient.addColorStop("0.1","orange");
gradient.addColorStop("0.5","red");
gradient.addColorStop("1.0","pink");
ctx.strokeStyle = gradient;
ctx.stroke();
}
t += t_step;
requestAnimationFrame(red);
};
var blue = function () {
ctx.clearRect(0, 0, w, h);
ctx.lineWidth = 1.5;
for(var i = 0; i < m; i++) {
x0 = -100;
y0 = i*8*u;
ctx.beginPath();
ctx.moveTo(x0, y0);
for(x = 0; x < w; x++) {
y = u*sin(x/4/(16*i/m + 1) - w*(i/m + 1)*t/12) + i*2.5*u;
ctx.lineTo(x, y);
x0 = x;
y0 = y;
}
var gradient=ctx.createLinearGradient(0,1000,0,0);
gradient.addColorStop("0.1","lightblue");
gradient.addColorStop("0.5","blue");
gradient.addColorStop("1.0","white");
ctx.strokeStyle = gradient;
ctx.stroke();
}
t += t_step;
requestAnimationFrame(blue);
};
/*Mood Functions Above This Point*/
function hundred(min, max) {
return Math.random() * (max - min) + min;
}
$('#click').on('click',function(){
$(".c").fadeOut('700');
setTimeout(function(){
$(".c").fadeIn('900');
},100);
setTimeout(function(){
m = ceil(w/(10*u)) + hundred(0,100);Math.random()*60*9;
/*m = ceil(w/(10*u)) + 100;*/
u = hundred(2,6)
},100);
blue();
});
Most of your red() & blue() code is identical so create 1 animation loop with the common code (animate()).
Gradients are expensive so create each gradient once at the beginning of the app and store them in an object (gradients{}).
Declare a gradientMix variable to tell animate() which gradient to use.
Here is refactored code:
// gradients are expensive so create them once at the start of the app
var gradients={};
gradients['red']=addGradient('orange','red','pink');
gradients['blue']=addGradient('lightblue','blue','white');
var gradientMix='blue';
// animate function with common code
function animate(time){
ctx.clearRect(0, 0, w, h);
ctx.lineWidth = 1.5;
for(var i = 0; i < m; i++) {
x0 = -100;
y0 = i*8*u;
ctx.beginPath();
ctx.moveTo(x0, y0);
for(x = 0; x < w; x++) {
y = u*sin(x/4/(16*i/m + 1) - w*(i/m + 1)*t/12) + i*2.5*u;
ctx.lineTo(x, y);
x0 = x;
y0 = y;
}
// set the strokeStyle to the selected gradient mix
ctx.strokeStyle = gradients[gradientMix];
ctx.stroke();
}
t += t_step;
requestAnimationFrame(animate);
};
function addGradient(color,g1,g2,g3){
var gradient=ctx.createLinearGradient(0,1000,0,0);
gradient.addColorStop("0.1",g1);
gradient.addColorStop("0.5",g2);
gradient.addColorStop("1.0",g3);
}
Related
I'm trying make render 3d rotating cube but cube has weird shape, I can't find the bug/glitch.
I'm following this tutorial on youtube.
I think I made something wrong on my code but everything seems good to me and I checked values on chrome debugging mode.
But when I following the tutorial I make some personal change but i'm sure this changes doesn't effect code working and make optimization on working.
Thanks.
const canvas = document.getElementById('canvas'), ctx = canvas.getContext('2d');
const W = 600, H = 600;
const MODEL_MAX_X = 2, MODEL_MIN_X = -2, MODEL_MAX_Y = 2, MODEL_MIN_Y = -2, STEP = 0.5;
var points = [], triangles = [];
for (let x = -1; x <= 1; x += STEP)
for (let y = -1; y <= 1; y += STEP)
for (let z = -1; z <= 1; z += STEP)
points.push([x, y, z]);
for (let dimension = 0; dimension <= 2; ++dimension)
for (let side = -1; side <= 1; side += 2) {
var sidePoints = points.filter(point => point[dimension] == side).slice(0,3);
triangles.push([...sidePoints]);
}
function persvectiveProjection([x, y, z]) {
return [x / (z + 4), y / (z + 4)];
}
function project(point) {
const [x, y] = persvectiveProjection(point);
return [
W * (x - MODEL_MIN_X) / (MODEL_MAX_X - MODEL_MIN_X),
H * (1 - y - MODEL_MIN_Y) / (MODEL_MAX_Y - MODEL_MIN_Y)
];
}
ctx.lineWidth = 4;
ctx.strokeStyle = '#000';
function renderPoint(point) {
const [x, y] = project(point);
ctx.beginPath();
ctx.moveTo(x, y);
ctx.lineTo(x + 1, y + 1);
ctx.stroke();
}
function renderTriangle (triangle) {
const projectedTriangle = triangle.map(project);
const [a, b, c] = projectedTriangle;
ctx.beginPath();
ctx.moveTo(a[0], a[1]);
ctx.lineTo(b[0], b[1]);
ctx.lineTo(c[0], c[1]);
ctx.lineTo(a[0], a[1]);
ctx.stroke();
}
function rotateY(point, theta) {
const [x, y, z] = point;
return [
Math.cos(theta) * x - Math.sin(theta) * z,
y,
Math.sin(theta) * x + Math.cos(theta) * z
]
}
function rotateX(point, theta) {
const [x, y, z] = point;
return [
x,
Math.cos(theta) * y - Math.sin(theta) * z,
Math.sin(theta) * y + Math.cos(theta) * z
]
}
var theta = 0;
var dtheta = 0.01;
function render() {
ctx.clearRect(0, 0, W, H);
theta += dtheta;
triangles.forEach(triangle => {
var rotatedTriangle = triangle.map(point => rotateX(rotateY(point, theta), 0.43 * theta));
renderTriangle(rotatedTriangle);
})
requestAnimationFrame(render);
}
render();
I'm trying to create an idle animation where the red rectangle moves back and forth slightly in a loop. For some reason once it reaches the specified threshhold instead of proceeding to move in the opposite direction, it just stops.
What did I do wrong?
<canvas id="myCanvas" width="1500" height="500" style="border:1px solid #c3c3c3;">
Your browser does not support the canvas element.
</canvas>
<script>
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
// Spaceship structure
var shipWidth = 250;
var shipHeight = 100;
// Canvas parameters
var cWidth = canvas.width;
var cHeight = canvas.height;
// Positioning variables
var centerWidthPosition = (cWidth / 2) - (shipWidth / 2);
var centerHeightPosition = (cHeight / 2) - (shipHeight / 2);
var requestAnimationFrame = window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame;
function drawShip(){
ctx.clearRect(0, 0, cWidth, cHeight);
ctx.fillStyle = "#FF0000";
ctx.fillRect(centerWidthPosition,centerHeightPosition,shipWidth,shipHeight);
centerWidthPosition--;
if (centerWidthPosition < 400){
++centerWidthPosition;
}
requestAnimationFrame(drawShip);
}
drawShip();
</script>
#TheAmberlamps explained why it's doing that. Here I offer you a solution to achieve what I believe you are trying to do.
Use a velocity variable that changes magnitude. X position always increases by velocity value. Velocity changes directions at screen edges.
// use a velocity variable
var xspeed = 1;
// always increase by velocity
centerWidthPosition += xspeed;
// screen edges are 0 and 400 in this example
if (centerWidthPosition > 400 || centerWidthPosition < 0){
xspeed *= -1; // change velocity direction
}
I added another condition in your if that causes the object to bounce back and forth. Remove the selection after || if you don't want it doing that.
Your function is caught in a loop; once centerWidthPosition reaches 399 your conditional makes it increment back up to 400, and then it decrements back to 399.
here is another one as a brain teaser - how would go by making this animation bounce in the loop - basically turn text into particles and then reverse back to text and reverse back to particles and back to text and so on and on and on infinitely:
var random = Math.random;
window.onresize = function () {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
};
window.onresize();
var ctx = canvas.getContext('2d');
ctx.font = 'bold 50px "somefont"';
ctx.textBaseline = 'center';
ctx.fillStyle = 'rgba(255,255,255,1)';
var _particles = [];
var particlesLength = 0;
var currentText = "SOMETEXT";
var createParticle = function createParticle(x, y) {_particles.push(new Particle(x, y));};
var checkAlpha = function checkAlpha(pixels, i) {return pixels[i * 4 + 3] > 0;};
var createParticles = function createParticles() {
var textSize = ctx.measureText(currentText);
ctx.fillText(currentText,Math.round((canvas.width / 2) - (textSize.width / 2)),Math.round(canvas.height / 2));
var imageData = ctx.getImageData(1, 1, canvas.width, canvas.height);
var pixels = imageData.data;
var dataLength = imageData.width * imageData.height;
for (var i = 0; i < dataLength; i++) {
var currentRow = Math.floor(i / imageData.width);
var currentColumn = i - Math.floor(i / imageData.height);
if (currentRow % 2 || currentColumn % 2) continue;
if (checkAlpha(pixels, i)) {
var cy = ~~(i / imageData.width);
var cx = ~~(i - (cy * imageData.width));
createParticle(cx, cy);
}}
particlesLength = _particles.length;
};
var Point = function Point(x, y) {
this.set(x, y);
};
Point.prototype = {
set: function (x, y) {
x = x || 0;
y = y || x || 0;
this._sX = x;
this._sY = y;
this.reset();
},
add: function (point) {
this.x += point.x;
this.y += point.y;
},
multiply: function (point) {
this.x *= point.x;
this.y *= point.y;
},
reset: function () {
this.x = this._sX;
this.y = this._sY;
return this;
},
};
var FRICT = new Point(0.98);//set to 0 if no flying needed
var Particle = function Particle(x, y) {
this.startPos = new Point(x, y);
this.v = new Point();
this.a = new Point();
this.reset();
};
Particle.prototype = {
reset: function () {
this.x = this.startPos.x;
this.y = this.startPos.y;
this.life = Math.round(random() * 300);
this.isActive = true;
this.v.reset();
this.a.reset();
},
tick: function () {
if (!this.isActive) return;
this.physics();
this.checkLife();
this.draw();
return this.isActive;
},
checkLife: function () {
this.life -= 1;
this.isActive = !(this.life < 1);
},
draw: function () {
ctx.fillRect(this.x, this.y, 1, 1);
},
physics: function () {
if (performance.now()<nextTime) return;
this.a.x = (random() - 0.5) * 0.8;
this.a.y = (random() - 0.5) * 0.8;
this.v.add(this.a);
this.v.multiply(FRICT);
this.x += this.v.x;
this.y += this.v.y;
this.x = Math.round(this.x * 10) / 10;
this.y = Math.round(this.y * 10) / 10;
}
};
var nextTime = performance.now()+3000;
createParticles();
function clearCanvas() {
ctx.fillStyle = 'rgba(0,0,0,1)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
(function clearLoop() {
clearCanvas();
requestAnimationFrame(clearLoop);
})();
(function animLoop(time) {
ctx.fillStyle = 'rgba(255,255,255,1)';
var isAlive = true;
for (var i = 0; i < particlesLength; i++) {
if (_particles[i].tick()) isAlive = true;
}
requestAnimationFrame(animLoop);
})();
function resetParticles() {
for (var i = 0; i < particlesLength; i++) {
_particles[i].reset();
}}
I am experimenting with canvas in HTML and JS and attempting to draw a canvas of a chess board with 16 pieces on each side of it. I was able to create the chess board but am stuck on how I would draw just specifically the 16 pieces on each side (The pieces can just be circles so just one side with 16 red circles, one side with 16 blue circles).
I don't know why this is so confusing to me, I know you probably just need a for loop stopping at the specific coordinates but to get different colored pieces on each side as well as stopping at certain part is giving me trouble.
I would just like assistance on where in my code would I be placing the chess pieces in. If you could just modify my current code and place comments on where you made the changes so I could see then that would be very appreciated.
Here is what I have so far to make the checkers board:
<canvas id="canvas" width="300" height="300"></canvas>
function drawCheckeredBackground(can, nRow, nCol) {
var ctx = can.getContext("2d");
var w = can.width;
var h = can.height;
nRow = nRow || 8;
nCol = nCol || 8;
w /= nCol;
h /= nRow;
for (var i = 0; i < nRow; ++i) {
for (var j = 0, col = nCol / 2; j < col; ++j) {
ctx.rect(2 * j * w + (i % 2 ? 0 : w), i * h, w, h);
}
}
ctx.fill();
}
var canvas = document.getElementById("canvas");
drawCheckeredBackground(canvas);
Here is how I want the chess board to look like, with 16 pieces on each side like so. I just quickly made this example in paint:
https://i.imgur.com/BvbxzSZ.png
This isn't the most beautiful solution possible, but it should offer some basic ideas and is adjustable using your step variable idea. Chances are, you'll need to refactor when going for actual pieces.
const drawBoard = (ctx, step) => {
for (let i = 0; i < 8; i++) {
for (let j = 0; j < 8; j++) {
ctx.fillStyle = (i + j) & 1 ? "black" : "white";
ctx.fillRect(j * step, i * step, step, step);
}
}
};
const drawPieces = (ctx, y, color, step) => {
ctx.fillStyle = color;
for (let i = y; i < 2 * step + y; i += step) {
for (let j = step / 2; j < 8 * step; j += step) {
ctx.beginPath();
ctx.arc(j, i - step / 2, step / 3, 0, Math.PI * 2);
ctx.fill();
}
}
};
const step = 60;
const c = document.createElement("canvas");
c.height = c.width = step * 8;
document.body.appendChild(c);
const ctx = c.getContext("2d");
drawBoard(ctx, step);
drawPieces(ctx, step, "red", step);
drawPieces(ctx, step * 7, "blue", step);
Play with it at JSFiddle.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<style>
body {
background-color: black;
}
canvas {
display: block;
margin: auto;
border: solid 1px white;
border-radius: 10px;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script type="application/javascript">
// Self executing function
void function() {
// Turn on strict js rules for this scope
"use strict";
// Classes
function ChessPeice(x,y,radius) {
this.x = x || 0.0;
this.y = y || 0.0;
this.radius = radius || 1.0;
}
ChessPeice.prototype = {
tick: function() {
},
render: function(ctx) {
ctx.moveTo(
this.x + this.radius,
this.y
);
ctx.arc(
this.x,
this.y,
this.radius,
0.0,
2.0 * Math.PI,
false
);
}
};
// Constructor, when called with 'new' creates an object and puts it
// in the 'this' variable, new properties can then be added to it.
function Chessboard(width,height) {
this.boardWidth = width || 1;
this.boardHeight = height || 1;
this.tileWidth = this.boardWidth / this.H_TILE_COUNT;
this.tileHeight = this.boardHeight / this.V_TILE_COUNT;
this.whitePeices = [];
this.blackPeices = [];
for (var y = 0; y < 2; ++y) {
for (var x = 0; x < this.V_TILE_COUNT; ++x) {
this.whitePeices.push(
new ChessPeice(
x * this.tileWidth + (this.tileWidth >> 1),
y * this.tileHeight + (this.tileHeight >> 1),
this.CHESS_PIECE_RADIUS
)
);
this.blackPeices.push(
new ChessPeice(
x * this.tileWidth + (this.tileWidth >> 1),
(this.V_TILE_COUNT - 1 - y) * this.tileHeight + (this.tileHeight >> 1),
this.CHESS_PIECE_RADIUS
)
);
}
}
}
// Prototype object, all objects created with 'new Chessboard()'
// will share the properties in the prototype, use it for constant values
// & class functions
Chessboard.prototype = {
H_TILE_COUNT: 8, // How many white & black tiles per axis?
V_TILE_COUNT: 8,
EDGE_THICKNESS: 10.0,
EDGE_COLOUR: "#603E11FF",
WHITE_TILE_COLOUR: "#BBBBBBFF",
BLACK_TILE_COLOUR: "#555555FF",
CHESS_PIECE_RADIUS: 5.0,
WHITE_PIECE_COLOUR: "#EEEEEEFF",
BLACK_PIECE_COLOUR: "#333333FF",
tick: function() {
// You can add game logic here
},
render: function(ctx) {
// Draw white tiles
var x = 0;
var y = 0;
var totalTiles = this.H_TILE_COUNT * this.V_TILE_COUNT;
ctx.fillStyle = this.WHITE_TILE_COLOUR;
ctx.beginPath();
for (var i = 0; i < totalTiles; ++i) {
ctx.rect(
x * this.tileWidth,
y * this.tileHeight,
this.tileWidth,
this.tileHeight
);
x += 2;
if (x >= this.H_TILE_COUNT) {
x = this.H_TILE_COUNT - x + 1;
++y;
}
}
ctx.fill();
// Draw black tiles
x = 1;
y = 0;
ctx.fillStyle = this.BLACK_TILE_COLOUR;
ctx.beginPath();
for (var i = 0; i < totalTiles; ++i) {
ctx.rect(
x * this.tileWidth,
y * this.tileHeight,
this.tileWidth,
this.tileHeight
);
x += 2;
if (x >= this.H_TILE_COUNT) {
x = this.H_TILE_COUNT - x + 1;
++y;
}
}
ctx.fill();
// Draw edge
ctx.lineWidth = this.EDGE_THICKNESS >> 1;
ctx.strokeStyle = this.EDGE_COLOUR;
ctx.beginPath();
ctx.rect(0,0,this.boardWidth,this.boardHeight);
ctx.stroke();
// Draw white pieces
ctx.lineWidth = 2;
ctx.strokeStyle = "#000000FF";
ctx.fillStyle = this.WHITE_PIECE_COLOUR;
ctx.beginPath();
for (var i = 0; i < this.whitePeices.length; ++i) {
this.whitePeices[i].render(ctx);
}
ctx.fill();
ctx.stroke();
// Draw black pieces
ctx.lineWidth = 2;
ctx.strokeStyle = "#000000FF";
ctx.fillStyle = this.BLACK_PIECE_COLOUR;
ctx.beginPath();
for (var i = 0; i < this.blackPeices.length; ++i) {
this.blackPeices[i].render(ctx);
}
ctx.fill();
ctx.stroke();
}
};
// Variables
var canvasWidth = 160;
var canvasHeight = 160;
var canvas = null;
var ctx = null;
var board = null;
// Game Loop
function loop() {
// Tick (Update game logic)
board.tick();
// Render
ctx.fillStyle = "gray";
ctx.fillRect(0,0,canvasWidth,canvasHeight);
board.render(ctx);
//
requestAnimationFrame(loop);
}
// Entry Point (Runs when the page loads)
onload = function() {
canvas = document.getElementById("canvas");
canvas.width = canvasWidth;
canvas.height = canvasHeight;
ctx = canvas.getContext("2d");
board = new Chessboard(canvasWidth,canvasHeight);
loop();
}
}();
</script>
</body>
</html>
I have drawn a grid in html5 canvas using StrokeLineX and StrokeLineY. I want to highlight the particular square/rectangle when I click on it in the grid.
I have tried using math.floor to define an index for a space but as soon as the width or height increases it starts giving different answers. I have tried too many times before finally posting it here.
Here is the code.
var canvas = document.getElementById("myCanvas");
canvas.addEventListener('click', on_canvas_click, false);
var ctx = canvas.getContext("2d");
var tray_length = 800;
var tray_depth = 800;
var boxHeight = 50;
var boxWidth = 50;
var canvas_X = tray_length;
var canvas_Y = tray_depth;
var box_x_pixels = canvas_X/no_of_columns;
var box_y_pixels = canvas_Y/no_of_rows;
// Drawing the grid
for (var y = boxWidth; y < canvas_Y; y += boxWidth) {
strokeLineX(ctx, y);
}
for (var x = boxHeight; x < canvas_X; x += boxHeight) {
strokeLineY(ctx, x);
}
function strokeLineX(ctx, y) {
ctx.beginPath();
ctx.strokeStyle = 'green';
ctx.moveTo(0, y);
ctx.lineTo(canvas_X, y);
ctx.stroke();
ctx.closePath();
}
function strokeLineY(ctx, x) {
ctx.beginPath();
ctx.strokeStyle = 'red';
ctx.moveTo(x, 0);
ctx.lineTo(x, canvas_Y);
ctx.stroke();
ctx.closePath();
}
function on_canvas_click(ev) {
var x = ev.pageX - canvas.offsetLeft;
var y = ev.pageY - canvas.offsetTop;
console.log(x+":"+y);
var coordinateDisplay = "x=" + x + ", y=" + y;
if (y>= 0 && y <= y+boxHeight ) {
var indexOfX = Math.floor(x/boxWidth); //divide on width and round off
var indexOfY = Math.floor(y/boxHeight);
// alert('You clicked bar index: ' + indexOfX+"-"+indexOfY);
ctx.fillRect="green";
ctx.rect(x,y,box_x_pixels,box_y_pixels);
ctx.stroke();
console.log(indexOfX + "-" + indexOfY);
}
}
In on_canvas_click change the following:
Instead of
ctx.fillRect="green";
do:
ctx.fillStyle="green";
And instead of:
ctx.rect(x,y,box_x_pixels,box_y_pixels);
do:
ctx.fillRect(boxWidth*indexOfX, boxHeight*indexOfY, boxWidth, boxHeight);
... and you don't need:
ctx.stroke();
Here is a working snippet with those changes applied, but also with some simplifications which are unrelated to your problem:
Removal of unused variables;
Use of one function to draw lines, instead of two;
Use of canvas.width and canvas.height properties;
Drawing grid lines also on the outer grid boundaries
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var boxHeight = 50;
var boxWidth = 50;
var canvas_X = canvas.width;
var canvas_Y = canvas.height;
canvas.addEventListener('click', on_canvas_click, false);
// Drawing the grid
for (var y = 0; y <= canvas_Y; y += boxWidth) {
strokeLine(ctx, 0, y, canvas_X, y, 'green');
}
for (var x = 0; x <= canvas_X; x += boxHeight) {
strokeLine(ctx, x, 0, x, canvas_Y, 'red');
}
function strokeLine(ctx, x0, y0, x1, y1, color) {
ctx.beginPath();
ctx.strokeStyle = color;
ctx.moveTo(x0, y0);
ctx.lineTo(x1, y1);
ctx.stroke();
ctx.closePath();
}
function on_canvas_click(ev) {
var x = ev.pageX - canvas.offsetLeft;
var y = ev.pageY - canvas.offsetTop;
if (y>= 0 && y <= y+boxHeight ) {
var indexOfX = Math.floor(x/boxWidth); //divide on width and round off
var indexOfY = Math.floor(y/boxHeight);
ctx.fillStyle="green";
ctx.fillRect(boxWidth*indexOfX, boxHeight*indexOfY, boxWidth, boxHeight);
}
}
<canvas id="myCanvas" width="600" height="200"></canvas>
This is my code for the static grid. Now I want to place an object in any one of the cells dynamically by clicking on it. How do I get that done?
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
for (var x = 0; x <= 400; x += 80) // Vertical lines in the canvas
{
ctx.moveTo(x, 0);
ctx.lineTo(x, 400);
ctx.stroke();
}
for (var y=0 ; y<=400 ; y +=80) // Horizontal lines in the canvas
{
ctx.moveTo(0, y);
ctx.lineTo(400, y);
ctx.stroke();
}
Let's build this up in stages. First, we need a function to get the mouse position relative to the canvas (or any other element):
function findPos(obj) {
var curleft = 0, curtop = 0;
if (obj.offsetParent) {
do {
curleft += obj.offsetLeft;
curtop += obj.offsetTop;
} while (obj = obj.offsetParent);
return { x: curleft, y: curtop };
}
return undefined;
}
And a function to convert those coordinates to the index of a cell on the board you've drawn:
function cell(x, y) {
return Math.floor(x / CELL_SIZE) + (ROWS * Math.floor(y / CELL_SIZE));
}
And a function to place an object on the canvas at the position given by cell (this uses a small circle):
function put(cell) {
var x = CELL_SIZE * (cell % ROWS);
var y = CELL_SIZE * Math.floor(cell / ROWS);
ctx.beginPath();
ctx.arc(x + (CELL_SIZE / 2), y + (CELL_SIZE / 2), 5, 0, 2 * Math.PI);
ctx.stroke();
}
Now let's use that stuff:
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var CELL_SIZE = 80;
var ROWS = 5;
c.addEventListener("click", function(e) {
var pos = findPos(this);
var x = e.pageX - pos.x;
var y = e.pageY - pos.y;
put(cell(x, y));
});
function createBoard() {
for (var x = 0; x <= 400; x += CELL_SIZE) {
ctx.moveTo(x, 0);
ctx.lineTo(x, 400);
ctx.stroke();
}
for (var y = 0; y <= 400; y += CELL_SIZE) {
ctx.moveTo(0,y);
ctx.lineTo(400,y);
ctx.stroke();
}
}
createBoard();
See a complete working example on JSFIDDLE.
http://jsfiddle.net/2n6H5/