add rectangle dynamically on button click inside canvas - javascript

function Shape(x, y, w, h, fill) {
this.x = x || 0;
this.y = y || 0;
this.w = w || 1;
this.h = h || 1;
this.fill = fill || '#AAAAAA';
}
Shape.prototype.draw = function(ctx) {
ctx.fillStyle = this.fill;
ctx.fillRect(this.x, this.y, this.w, this.h);
}
Shape.prototype.contains = function(mx, my) {
return (this.x <= mx) && (this.x + this.w >= mx) &&
(this.y <= my) && (this.y + this.h >= my);
}
function CanvasState(canvas) {
this.canvas = canvas;
this.width = canvas.width;
this.height = canvas.height;
this.ctx = canvas.getContext('2d');
var stylePaddingLeft, stylePaddingTop, styleBorderLeft, styleBorderTop;
if (document.defaultView && document.defaultView.getComputedStyle) {
this.stylePaddingLeft = parseInt(document.defaultView.getComputedStyle(canvas, null)['paddingLeft'], 10) || 0;
this.stylePaddingTop = parseInt(document.defaultView.getComputedStyle(canvas, null)['paddingTop'], 10) || 0;
this.styleBorderLeft = parseInt(document.defaultView.getComputedStyle(canvas, null)['borderLeftWidth'], 10) || 0;
this.styleBorderTop = parseInt(document.defaultView.getComputedStyle(canvas, null)['borderTopWidth'], 10) || 0;
}
var html = document.body.parentNode;
this.htmlTop = html.offsetTop;
this.htmlLeft = html.offsetLeft;
this.valid = false;
this.shapes = [];
this.dragging = false;
this.selection = null;
this.dragoffx = 0;
this.dragoffy = 0;
var myState = this;
canvas.addEventListener('selectstart', function(e) { e.preventDefault(); return false; }, false);
canvas.addEventListener('mousedown', function(e) {
var mouse = myState.getMouse(e);
var mx = mouse.x;
var my = mouse.y;
var shapes = myState.shapes;
var l = shapes.length;
for (var i = l-1; i >= 0; i--) {
if (shapes[i].contains(mx, my)) {
var mySel = shapes[i];
myState.dragoffx = mx - mySel.x;
myState.dragoffy = my - mySel.y;
myState.dragging = true;
myState.selection = mySel;
myState.valid = false;
return;
}
}
if (myState.selection) {
myState.selection = null;
myState.valid = false;
}
}, true);
canvas.addEventListener('mousemove', function(e) {
if (myState.dragging){
var mouse = myState.getMouse(e);
myState.selection.x = mouse.x - myState.dragoffx;
myState.selection.y = mouse.y - myState.dragoffy;
myState.valid = false;
}
}, true);
canvas.addEventListener('mouseup', function(e) {
myState.dragging = false;
}, true);
canvas.addEventListener('dblclick', function(e) {
var mouse = myState.getMouse(e);
myState.addShape(new Shape(mouse.x - 10, mouse.y - 10, 20, 20, 'rgba(0,255,0,.6)'));
}, true);
this.selectionColor = '#CC0000';
this.selectionWidth = 2;
this.interval = 30;
setInterval(function() { myState.draw(); }, myState.interval);
}
CanvasState.prototype.addShape = function(shape) {
this.shapes.push(shape);
this.valid = false;
}
CanvasState.prototype.clear = function() {
this.ctx.clearRect(0, 0, this.width, this.height);
}
CanvasState.prototype.draw = function() {
if (!this.valid) {
var ctx = this.ctx;
var shapes = this.shapes;
this.clear();
var l = shapes.length;
for (var i = 0; i < l; i++) {
var shape = shapes[i];
if (shape.x > this.width || shape.y > this.height ||
shape.x + shape.w < 0 || shape.y + shape.h < 0) continue;
shapes[i].draw(ctx);
}
if (this.selection != null) {
ctx.strokeStyle = this.selectionColor;
ctx.lineWidth = this.selectionWidth;
var mySel = this.selection;
ctx.strokeRect(mySel.x,mySel.y,mySel.w,mySel.h);
}
this.valid = true;
}
}
CanvasState.prototype.getMouse = function(e) {
var element = this.canvas, offsetX = 0, offsetY = 0, mx, my;
if (element.offsetParent !== undefined) {
do {
offsetX += element.offsetLeft;
offsetY += element.offsetTop;
} while ((element = element.offsetParent));
}
offsetX += this.stylePaddingLeft + this.styleBorderLeft + this.htmlLeft;
offsetY += this.stylePaddingTop + this.styleBorderTop + this.htmlTop;
mx = e.pageX - offsetX;
my = e.pageY - offsetY;
return {x: mx, y: my};
}
function init() {
var s = new CanvasState(document.getElementById('canvas1'));
s.addShape(new Shape(40,40,50,50)); // The default is gray
}
<canvas id="canvas1" width="200" height="200" style="border:1px solid #000000;">
This text is displayed if your browser does not support HTML5 Canvas.
</canvas>
<button onclick= "init()">click </button>
On click of a button, I am creating a rectangular element which can be drag and drop inside the canvas element restricted area.
however, Is it possible to create a new rectangular element dynamically on every click of a button which can further be drag and drop inside the canvas boundary.
And the previous rectangular element won't lost.

Doing this from scratch will be a lot of work. I suggest you use a library like fabric.js which has everything you asked plus a lot more to offer. Here is a demo page.

On every button click you was calling init(),.. Causing you CanvasState to reset.
I've just made another function called addShape, and just called init once.
I've also kept a reference to the CanvasState, called s.. So I could do s.addShape.. So changes were very minimal.
Hope this helps..
function Shape(x, y, w, h, fill) {
this.x = x || 0;
this.y = y || 0;
this.w = w || 1;
this.h = h || 1;
this.fill = fill || '#AAAAAA';
}
Shape.prototype.draw = function(ctx) {
ctx.fillStyle = this.fill;
ctx.fillRect(this.x, this.y, this.w, this.h);
}
Shape.prototype.contains = function(mx, my) {
return (this.x <= mx) && (this.x + this.w >= mx) &&
(this.y <= my) && (this.y + this.h >= my);
}
function CanvasState(canvas) {
this.canvas = canvas;
this.width = canvas.width;
this.height = canvas.height;
this.ctx = canvas.getContext('2d');
var stylePaddingLeft, stylePaddingTop, styleBorderLeft, styleBorderTop;
if (document.defaultView && document.defaultView.getComputedStyle) {
this.stylePaddingLeft = parseInt(document.defaultView.getComputedStyle(canvas, null)['paddingLeft'], 10) || 0;
this.stylePaddingTop = parseInt(document.defaultView.getComputedStyle(canvas, null)['paddingTop'], 10) || 0;
this.styleBorderLeft = parseInt(document.defaultView.getComputedStyle(canvas, null)['borderLeftWidth'], 10) || 0;
this.styleBorderTop = parseInt(document.defaultView.getComputedStyle(canvas, null)['borderTopWidth'], 10) || 0;
}
var html = document.body.parentNode;
this.htmlTop = html.offsetTop;
this.htmlLeft = html.offsetLeft;
this.valid = false;
this.shapes = [];
this.dragging = false;
this.selection = null;
this.dragoffx = 0;
this.dragoffy = 0;
var myState = this;
canvas.addEventListener('selectstart', function(e) { e.preventDefault(); return false; }, false);
canvas.addEventListener('mousedown', function(e) {
var mouse = myState.getMouse(e);
var mx = mouse.x;
var my = mouse.y;
var shapes = myState.shapes;
var l = shapes.length;
for (var i = l-1; i >= 0; i--) {
if (shapes[i].contains(mx, my)) {
var mySel = shapes[i];
myState.dragoffx = mx - mySel.x;
myState.dragoffy = my - mySel.y;
myState.dragging = true;
myState.selection = mySel;
myState.valid = false;
return;
}
}
if (myState.selection) {
myState.selection = null;
myState.valid = false;
}
}, true);
canvas.addEventListener('mousemove', function(e) {
if (myState.dragging){
var mouse = myState.getMouse(e);
myState.selection.x = mouse.x - myState.dragoffx;
myState.selection.y = mouse.y - myState.dragoffy;
myState.valid = false;
}
}, true);
canvas.addEventListener('mouseup', function(e) {
myState.dragging = false;
}, true);
canvas.addEventListener('dblclick', function(e) {
var mouse = myState.getMouse(e);
myState.addShape(new Shape(mouse.x - 10, mouse.y - 10, 20, 20, 'rgba(0,255,0,.6)'));
}, true);
this.selectionColor = '#CC0000';
this.selectionWidth = 2;
this.interval = 30;
setInterval(function() { myState.draw(); }, myState.interval);
}
CanvasState.prototype.addShape = function(shape) {
this.shapes.push(shape);
this.valid = false;
}
CanvasState.prototype.clear = function() {
this.ctx.clearRect(0, 0, this.width, this.height);
}
CanvasState.prototype.draw = function() {
if (!this.valid) {
var ctx = this.ctx;
var shapes = this.shapes;
this.clear();
var l = shapes.length;
for (var i = 0; i < l; i++) {
var shape = shapes[i];
if (shape.x > this.width || shape.y > this.height ||
shape.x + shape.w < 0 || shape.y + shape.h < 0) continue;
shapes[i].draw(ctx);
}
if (this.selection != null) {
ctx.strokeStyle = this.selectionColor;
ctx.lineWidth = this.selectionWidth;
var mySel = this.selection;
ctx.strokeRect(mySel.x,mySel.y,mySel.w,mySel.h);
}
this.valid = true;
}
}
CanvasState.prototype.getMouse = function(e) {
var element = this.canvas, offsetX = 0, offsetY = 0, mx, my;
if (element.offsetParent !== undefined) {
do {
offsetX += element.offsetLeft;
offsetY += element.offsetTop;
} while ((element = element.offsetParent));
}
offsetX += this.stylePaddingLeft + this.styleBorderLeft + this.htmlLeft;
offsetY += this.stylePaddingTop + this.styleBorderTop + this.htmlTop;
mx = e.pageX - offsetX;
my = e.pageY - offsetY;
return {x: mx, y: my};
}
var s;
function init() {
s = new CanvasState(document.getElementById('canvas1'));
}
function addShape() {
s.addShape(new Shape(40,40,50,50)); // The default is gray
}
init();
<canvas id="canvas1" width="400" height="300" style="border:1px solid #000000;">
This text is displayed if your browser does not support HTML5 Canvas.
</canvas>
<button onclick= "addShape()">click </button>

Related

What is the right offset for better drag effect?

I am trying to make my cloud draggable. It works but as can be seen in the example that i have, the cloud always clippes from the center to the mouse possition.
Here is my current set-up.
$(document).ready(function () {
var canvas = document.getElementById("background-canvas");
canvas.width = $(window).width();
canvas.height = $(window).height();
canvas.style.zIndex = -1;
var ctx = canvas.getContext("2d");
var mousePosition = new Vector2d(0,0);
var background = new Background(ctx, canvas.width, canvas.height, new Color(224,247,250,0.8));
var cloud = new Cloud(background, 300, 100, new Vector2d(10,10), 20, 1000);
img=new Image();
img.src="https://i.imgur.com/hIVsoho.png";
background.addCloud(cloud);
for (var i = 0; i < background.allClouds.length; i++){
var selectedCloud = background.allClouds[i];
for (var j = 0; j < selectedCloud.maxNumberofPixels; j++){
var pixel = cloud.createPixel(); // TODO: cloud shall not define pixel.
//new Pixel(2, 4, getRandomLocationWithinParent(selectedCloud), new Vector2d(0,5), new Vector2d(0,0), new Color(0,0,128,1));
cloud.addPixel(pixel);
}
}
/*
* Input listeners
*/
document.addEventListener("mousemove", function (evt) {
mousePosition = getMousePos(canvas, evt);
}, false);
document.addEventListener("mousedown", function (evt){
console.log(mousePosition);
for(var i = 0; i < background.allClouds.length; i++)
if(background.allClouds[i].hover(mousePosition))
background.allClouds[i].isClicked = true;
}, false)
document.addEventListener("mouseup", function (evt){
for(var i = 0; i < background.allClouds.length; i++)
if(background.allClouds[i].hover(mousePosition))
background.allClouds[i].isClicked = false;
}, false)
setInterval(updateBackground, 20);
function updateBackground() {
// paint background color.
ctx.fillStyle = background.color.getColorString();
ctx.fillRect(0,0,background.width, background.height);
// paint clouds
for(var i = 0; i < background.allClouds.length; i++){
var selectedCloud = background.allClouds[i];
//ctx.fillStyle = selectedCloud.color.getColorString();
//ctx.fillRect(0, 0, selectedCloud.width, selectedCloud.height); rectangle view of cloud.
// paint rain
var deadPixelContainer = [];
for (var j = 0; j < selectedCloud.allPixels.length; j++){
var selectedPixel = selectedCloud.allPixels[j];
ctx.fillStyle = selectedPixel.color.getColorString();
ctx.save();
ctx.translate(selectedPixel.location.x, selectedPixel.location.y);
ctx.fillRect(-selectedPixel.width / 2, -selectedPixel.height / 2, selectedPixel.width, selectedPixel.height);
ctx.restore();
if(!selectedPixel.alive){
deadPixelContainer.push(selectedPixel);
continue;
}
selectedPixel.update();
selectedPixel.checkEdges(background);
}
if(deadPixelContainer.length > 0){
selectedCloud.removePixels(deadPixelContainer);
}
ctx.save();
ctx.translate(selectedCloud.location.x, selectedCloud.location.y);
ctx.drawImage(img,0,0,img.width,img.height,-25, -10,350,100);
ctx.restore();
cloud.update(mousePosition);
}
}
// TODO: Create object for mouse
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return new Vector2d(evt.clientX - rect.left, evt.clientY - rect.top);
}
});
function Cloud(background, width, height, location, startNumberOfPixels, maxNumberofPixels){
this.width = width;
this.height = height;
this.location = location;
this.allPixels = [];
this.maxNumberofPixels = maxNumberofPixels;
this.color = new Color(255,255,255,0.5);
this.isClicked = false;
this.rainStrength = 5; // how often cloud spawns new pixels per update cycle.
this.addPixel = function(pixel){
if(this.allPixels.length <= startNumberOfPixels)
this.allPixels.push(pixel);
}
this.update = function(mousePosition){
// make cloud draggable
if(this.isClicked){
var offsetX = mousePosition.x - this.location.x;
var offsetY = mousePosition.y - this.location.y;
this.location = new Vector2d(this.location.x + offsetX - this.width/2, this.location.y + offsetY - this.height/2);
}
// add more pixels overtime.
if(this.allPixels.length <= this.maxNumberofPixels)
for(var i = 0; i < this.rainStrength; i++)
this.allPixels.push(this.createPixel());
}
this.hover = function(mousePosition){
if(mousePosition.x > this.location.x
&& mousePosition.x < this.location.x + this.width
&& mousePosition.y > this.location.y
&& mousePosition.y < this.location.y + this.height)
return true;
return false;
}
this.createPixel = function(){
return new Pixel(2, 4, this.getRandomLocation(), new Vector2d(0,7), new Vector2d(0,0.05), new Color(0,0,128,1));
}
this.removePixels = function(deadPixelContainer){
for(var i = 0; i < deadPixelContainer.length; i++){
try{
var pixelContainer = this.allPixels.slice();
pixelContainer.splice(this.allPixels.findIndex(v => v === deadPixelContainer[i]), 1).slice();
this.allPixels = pixelContainer.slice();
}catch(e){
console.log(e);
}
}
}
this.getRandomLocation = function(){
var minWidth = this.location.x;
var maxWidth = this.location.x + this.width;
var minHeight = this.location.y + this.height/2; // don't count upper part of cloud. Rain forms at the bottom.
var maxHeight = this.location.y + this.height;
var randomWidthLocation = Math.random() * (maxWidth - minWidth + 1)+minWidth;
var randomHeightLocation = Math.random() * (maxHeight - minHeight + 1) + minHeight;
return new Vector2d(randomWidthLocation, randomHeightLocation);
}
}
function Background(ctx, width, height, color){
this.width = width;
this.height = height;
this.color = color; //"#191919"
this.isPaused = false;
this.allPixels = []; // might need to be removed.
this.allClouds = [];
this.pixelCount = 150;
this.addCloud = function(cloud){
this.allClouds.push(cloud);
};
this.refreshCanvas = function(){
this.width = $(window).width();
this.height = $(window).height();
};
this.addPixelOn = function(pixelWidht, pixelHeight, location, velocity, acceleration, color) { // might need to be removed.
var pixel = new Pixel(pixelWidht, pixelHeight, location, velocity, acceleration, color);
this.allPixels.push(pixel);
};
this.addPixel = function(pixelWidht, pixelHeight, velocity, acceleration, color) { // might need to be removed.
var location = new Vector2d(Math.random() * this.width, Math.random() * this.height);
this.addPixelOn(pixelWidht, pixelHeight, location, velocity, acceleration, color);
};
}
function Pixel(widht, height, location, velocity, acceleration, color) {
this.height = height;
this.width = widht;
this.color = color; //"#00CC33"
this.location = location;
this.velocity = velocity;
this.acceleration = acceleration;
this.alive = true;
this.update = function () {
this.velocity.add(this.acceleration);
//this.velocity.limit(topspeed);
this.location.add(this.velocity);
};
this.checkEdges = function (background) {
if (this.location.y > background.height) {
this.alive = false;
}
};
this.setColor = function(color){
this.color = color;
};
this.setHeight = function(height){
this.height = height;
};
this.setWidth = function(width){
this.width = width;
}
}
function Color(r,g,b,o){
this.red = r;
this.green = g;
this.blue = b;
this.opacity = o;
this.getColorString = function(){
return "rgba("+this.red+","+this.green+","+this.blue+","+this.opacity+")";
}
}
function Vector2d(x, y) {
this.x = x;
this.y = y;
this.add = function (vector2d) {
this.x += vector2d.x;
this.y += vector2d.y;
};
this.sub = function (vector2d) {
this.x -= vector2d.x;
this.y -= vector2d.y;
};
this.mult = function (mult) {
this.x *= mult;
this.y *= mult;
};
this.div = function (div) {
this.x /= div;
this.y /= div;
};
this.mag = function () {
return Math.sqrt(this.x * this.x, this.y * this.y);
};
this.norm = function () {
var m = this.mag();
if (m !== 0) {
this.div(m);
}
}
}
#background-canvas {
position: fixed;
width: 100%;
height: 100%
background-color:red;
top:0;
left:0;
}
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<canvas id="background-canvas" />
</body>
</html>
The code that sets the location of the cloud when dragged is this:
// make cloud draggable
if(this.isClicked){
var offsetX = mousePosition.x - this.location.x;
var offsetY = mousePosition.y - this.location.y;
this.location = new Vector2d(this.location.x + offsetX - this.width/2, this.location.y + offsetY - this.height/2);
}
This does not work correctly. I want it not to clip to the center.
Can any one help me with this ?
If its not completely clear what the question is or if you need any extra information please ask.
You need to remember the click location within the cloud's local coordinate system:
if(background.allClouds[i].hover(mousePosition)) {
background.allClouds[i].isClicked = true;
background.allClouds[i].clickLocalPosition = new Vector2d(mousePosition.x, mousePosition.y);
background.allClouds[i].clickLocalPosition.sub(background.allClouds[i].location);
}
Then, when you update, you calculate the new position based on the click location:
this.location.x = mousePosition.x - this.clickLocalPosition.x;
this.location.y = mousePosition.y - this.clickLocalPosition.y;
$(document).ready(function () {
var canvas = document.getElementById("background-canvas");
canvas.width = $(window).width();
canvas.height = $(window).height();
canvas.style.zIndex = -1;
var ctx = canvas.getContext("2d");
var mousePosition = new Vector2d(0,0);
var background = new Background(ctx, canvas.width, canvas.height, new Color(224,247,250,0.8));
var cloud = new Cloud(background, 300, 100, new Vector2d(10,10), 20, 1000);
img=new Image();
img.src="https://i.imgur.com/hIVsoho.png";
background.addCloud(cloud);
for (var i = 0; i < background.allClouds.length; i++){
var selectedCloud = background.allClouds[i];
for (var j = 0; j < selectedCloud.maxNumberofPixels; j++){
var pixel = cloud.createPixel(); // TODO: cloud shall not define pixel.
//new Pixel(2, 4, getRandomLocationWithinParent(selectedCloud), new Vector2d(0,5), new Vector2d(0,0), new Color(0,0,128,1));
cloud.addPixel(pixel);
}
}
/*
* Input listeners
*/
document.addEventListener("mousemove", function (evt) {
mousePosition = getMousePos(canvas, evt);
}, false);
document.addEventListener("mousedown", function (evt){
console.log(mousePosition);
for(var i = 0; i < background.allClouds.length; i++)
if(background.allClouds[i].hover(mousePosition)) {
background.allClouds[i].isClicked = true;
background.allClouds[i].clickLocalPosition = new Vector2d(mousePosition.x, mousePosition.y);
background.allClouds[i].clickLocalPosition.sub(background.allClouds[i].location);
}
}, false)
document.addEventListener("mouseup", function (evt){
for(var i = 0; i < background.allClouds.length; i++)
if(background.allClouds[i].hover(mousePosition))
background.allClouds[i].isClicked = false;
}, false)
setInterval(updateBackground, 20);
function updateBackground() {
// paint background color.
ctx.fillStyle = background.color.getColorString();
ctx.fillRect(0,0,background.width, background.height);
// paint clouds
for(var i = 0; i < background.allClouds.length; i++){
var selectedCloud = background.allClouds[i];
//ctx.fillStyle = selectedCloud.color.getColorString();
//ctx.fillRect(0, 0, selectedCloud.width, selectedCloud.height); rectangle view of cloud.
// paint rain
var deadPixelContainer = [];
for (var j = 0; j < selectedCloud.allPixels.length; j++){
var selectedPixel = selectedCloud.allPixels[j];
ctx.fillStyle = selectedPixel.color.getColorString();
ctx.save();
ctx.translate(selectedPixel.location.x, selectedPixel.location.y);
ctx.fillRect(-selectedPixel.width / 2, -selectedPixel.height / 2, selectedPixel.width, selectedPixel.height);
ctx.restore();
if(!selectedPixel.alive){
deadPixelContainer.push(selectedPixel);
continue;
}
selectedPixel.update();
selectedPixel.checkEdges(background);
}
if(deadPixelContainer.length > 0){
selectedCloud.removePixels(deadPixelContainer);
}
ctx.save();
ctx.translate(selectedCloud.location.x, selectedCloud.location.y);
ctx.drawImage(img,0,0,img.width,img.height,-25, -10,350,100);
ctx.restore();
cloud.update(mousePosition);
}
}
// TODO: Create object for mouse
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return new Vector2d(evt.clientX - rect.left, evt.clientY - rect.top);
}
});
function Cloud(background, width, height, location, startNumberOfPixels, maxNumberofPixels){
this.width = width;
this.height = height;
this.location = location;
this.allPixels = [];
this.maxNumberofPixels = maxNumberofPixels;
this.color = new Color(255,255,255,0.5);
this.isClicked = false;
this.rainStrength = 5; // how often cloud spawns new pixels per update cycle.
this.addPixel = function(pixel){
if(this.allPixels.length <= startNumberOfPixels)
this.allPixels.push(pixel);
}
this.update = function(mousePosition){
// make cloud draggable
if(this.isClicked){
this.location.x = mousePosition.x - this.clickLocalPosition.x;
this.location.y = mousePosition.y - this.clickLocalPosition.y;
}
// add more pixels overtime.
if(this.allPixels.length <= this.maxNumberofPixels)
for(var i = 0; i < this.rainStrength; i++)
this.allPixels.push(this.createPixel());
}
this.hover = function(mousePosition){
if(mousePosition.x > this.location.x
&& mousePosition.x < this.location.x + this.width
&& mousePosition.y > this.location.y
&& mousePosition.y < this.location.y + this.height)
return true;
return false;
}
this.createPixel = function(){
return new Pixel(2, 4, this.getRandomLocation(), new Vector2d(0,7), new Vector2d(0,0.05), new Color(0,0,128,1));
}
this.removePixels = function(deadPixelContainer){
for(var i = 0; i < deadPixelContainer.length; i++){
try{
var pixelContainer = this.allPixels.slice();
pixelContainer.splice(this.allPixels.findIndex(v => v === deadPixelContainer[i]), 1).slice();
this.allPixels = pixelContainer.slice();
}catch(e){
console.log(e);
}
}
}
this.getRandomLocation = function(){
var minWidth = this.location.x;
var maxWidth = this.location.x + this.width;
var minHeight = this.location.y + this.height/2; // don't count upper part of cloud. Rain forms at the bottom.
var maxHeight = this.location.y + this.height;
var randomWidthLocation = Math.random() * (maxWidth - minWidth + 1)+minWidth;
var randomHeightLocation = Math.random() * (maxHeight - minHeight + 1) + minHeight;
return new Vector2d(randomWidthLocation, randomHeightLocation);
}
}
function Background(ctx, width, height, color){
this.width = width;
this.height = height;
this.color = color; //"#191919"
this.isPaused = false;
this.allPixels = []; // might need to be removed.
this.allClouds = [];
this.pixelCount = 150;
this.addCloud = function(cloud){
this.allClouds.push(cloud);
};
this.refreshCanvas = function(){
this.width = $(window).width();
this.height = $(window).height();
};
this.addPixelOn = function(pixelWidht, pixelHeight, location, velocity, acceleration, color) { // might need to be removed.
var pixel = new Pixel(pixelWidht, pixelHeight, location, velocity, acceleration, color);
this.allPixels.push(pixel);
};
this.addPixel = function(pixelWidht, pixelHeight, velocity, acceleration, color) { // might need to be removed.
var location = new Vector2d(Math.random() * this.width, Math.random() * this.height);
this.addPixelOn(pixelWidht, pixelHeight, location, velocity, acceleration, color);
};
}
function Pixel(widht, height, location, velocity, acceleration, color) {
this.height = height;
this.width = widht;
this.color = color; //"#00CC33"
this.location = location;
this.velocity = velocity;
this.acceleration = acceleration;
this.alive = true;
this.update = function () {
this.velocity.add(this.acceleration);
//this.velocity.limit(topspeed);
this.location.add(this.velocity);
};
this.checkEdges = function (background) {
if (this.location.y > background.height) {
this.alive = false;
}
};
this.setColor = function(color){
this.color = color;
};
this.setHeight = function(height){
this.height = height;
};
this.setWidth = function(width){
this.width = width;
}
}
function Color(r,g,b,o){
this.red = r;
this.green = g;
this.blue = b;
this.opacity = o;
this.getColorString = function(){
return "rgba("+this.red+","+this.green+","+this.blue+","+this.opacity+")";
}
}
function Vector2d(x, y) {
this.x = x;
this.y = y;
this.add = function (vector2d) {
this.x += vector2d.x;
this.y += vector2d.y;
};
this.sub = function (vector2d) {
this.x -= vector2d.x;
this.y -= vector2d.y;
};
this.mult = function (mult) {
this.x *= mult;
this.y *= mult;
};
this.div = function (div) {
this.x /= div;
this.y /= div;
};
this.mag = function () {
return Math.sqrt(this.x * this.x, this.y * this.y);
};
this.norm = function () {
var m = this.mag();
if (m !== 0) {
this.div(m);
}
}
}
#background-canvas {
position: fixed;
width: 100%;
height: 100%
background-color:red;
top:0;
left:0;
}
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<canvas id="background-canvas" />
</body>
</html>

HTML Canvas & JavaScript - Selection Menu - Setting Initial and Sustaining Current Selection

In the HTML canvas below I have a selection menu which triggers the drawing of an image beside it dependent on what is selected (numbers 1-5 in the example below). The JavaScript uses the pseudo-object approach to storing/manipulating images drawn on the canvas. Besides the EventListener's attached to the canvas, there is one EventListener attached to the whole window which resizes the canvas within a strict aspect ratio when the window size is changed.
The problem I am currently having with this is that the selection is cleared when the EventListener is triggered (when the window size is changed). To replicate this in the example below, you will have to run the code snippet in full screen mode and change your browser window's size. Instead, I would like the the current selection to be maintained after the window (and correspondingly, the canvas') size change. I have tried assigning the current selection to a variable, but I could only get it to leave a static selection where the onHover animation does not work.
Also, related to this, I am trying to set an initial selection that is selected on the first canvas draw until one of the other options is selected. In this case, when the script initially loads, I would like the number 1 and its corresponding image to be automatically selected/displayed until a new selection is made. Again, assigning this as an initialSelection variable or calling makeCurvedRect independently leaves a static selection, by which I mean the curvedRect (image) is not animated onHover.
I'm very unsure how to achieve either of these results so any help will be much appreciated. Apologies for the large amount of code but I could not manage to condense it any more than this.
var c=document.getElementById('game'),
rect = c.getBoundingClientRect(),
ctx=c.getContext('2d');
c.width = window.innerWidth;
c.height = (2/3)*c.width;
numberImages = ['https://i.stack.imgur.com/TZIUz.png','https://i.stack.imgur.com/6beTF.png','https://i.stack.imgur.com/wZk2H.png','https://i.stack.imgur.com/1K743.png','https://i.stack.imgur.com/jMMmQ.png'];
var curvedRect = function(number, x, y, w, h) {
this.text = number.toString();
this.img = new Image();
this.img.src=numberImages[number-1];
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.hovered = false;
this.clicked = false;
this.visible = false;
}
var selected;
curvedRect.prototype.makeCurvedRect = function() {
var delta=0, theta=0;
if (this.hovered) {
delta = (c.height*(3/500));
theta = -0.01;
shadowColor = '#000000';
shadowBlur = 20;
shadowOffsetX = 5;
shadowOffsetY = 5;
} else {
delta = 0;
theta = 0;
shadowColor = '#9F3A9B';
shadowBlur = 0;
shadowOffsetX = 0;
shadowOffsetY = 0;
}
var x = this.x-delta;
var y = this.y-delta;
var w = this.w+(2*delta);
var h = this.h+(2*delta);
var cornerRounder = (c.height*(10/500))
ctx.rotate(theta);
ctx.beginPath();
ctx.lineWidth='12';
ctx.strokeStyle='white';
ctx.moveTo(x+cornerRounder, y);
ctx.lineTo(x+w-cornerRounder, y);
ctx.quadraticCurveTo(x+w, y, x+w, y+cornerRounder);
ctx.lineTo(x+w, y+h-cornerRounder);
ctx.quadraticCurveTo(x+w, y+h, x+w-cornerRounder, y+h);
ctx.lineTo(x+cornerRounder, y+h);
ctx.quadraticCurveTo(x, y+h, x, y+h-cornerRounder);
ctx.lineTo(x, y+cornerRounder);
ctx.quadraticCurveTo(x, y, x+cornerRounder, y);
ctx.shadowColor = shadowColor;
ctx.shadowBlur = shadowBlur;
ctx.shadowOffsetX = shadowOffsetX;
ctx.shadowOffsetY = shadowOffsetY;
ctx.stroke();
ctx.shadowBlur = 0;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
ctx.drawImage(this.img, x+(c.width*(2.5/750)), y+(c.height*(2.5/500)), w-cornerRounder/2, h-cornerRounder/2);
ctx.rotate(-theta);
}
curvedRect.prototype.hitTest = function(x, y) {
return (x >= this.x) && (x <= (this.w+this.x)) && (y >= this.y) && (y <= (this.h+this.y));
}
var selectionForMenu = function(id, text, y) {
this.id = id;
this.text = text;
this.y = y;
this.hovered = false;
this.clicked = false;
this.lastClicked = false;
this.visible = true;
}
function makeTextForSelected(text, y) {
ctx.font='bold '+(c.height*(12/500))+'px Noto Sans'; // check
ctx.fillStyle='white';
ctx.textAlign='center';
ctx.fillText(text, (c.width*(200/750)), y);
}
selectionForMenu.prototype.makeSelection = function() {
ctx.globalAlpha=0.75;
var fillColor='#A84FA5';
if (this.hovered) {
if (this.clicked) {
if (this.lastClicked) {
fillColor='#E4C7E2';
makeTextForSelected(this.text, c.height*(375/500));
} else {
fillColor='#D5A9D3';
}
} else if (this.lastClicked) {
fillColor='#D3A4D0';
makeTextForSelected(this.text, c.height*(375/500));
} else {
fillColor='#BA74B7';
}
} else if (this.lastClicked) {
fillColor='#C78DC5';
makeTextForSelected(this.text, c.height*(375/500));
} else {
fillColor='#A84FA5';
}
ctx.beginPath();
ctx.fillStyle=fillColor;
ctx.fillRect(c.width*(400/750), this.y, c.width*(350/750), c.height*(100/500))
ctx.stroke();
ctx.font=c.height*(10/500)+'px Noto Sans';
ctx.fillStyle='white';
ctx.textAlign='left';
ctx.fillText(this.text, c.width*(410/750), this.y+(c.height*(38/500)));
ctx.globalAlpha=1;
}
selectionForMenu.prototype.hitTest = function(x, y) {
return (x >= (c.width*(400/750)) && (x <= c.width) && (y >= this.y) &&
(y <= (this.y+(c.height*(100/500))) && !((x >= c.width*(400/750) && (y > c.height*(450/500))))));
}
var Paint = function(element) {
this.element = element;
this.shapes = [];
}
Paint.prototype.addShape = function(shape) {
this.shapes.push(shape);
}
Paint.prototype.render = function() {
ctx.clearRect(0, 0, this.element.width, this.element.height);
for (var i=0; i<this.shapes.length; i++) {
try {
this.shapes[i].makeSelection();
}
catch(err) {}
try {
if(this.shapes[i].lastClicked == true) {
this.shapes[i].rect.makeCurvedRect();
}
}
catch(err) {}
}
ctx.beginPath();
ctx.fillStyle='white';
ctx.fillRect(0, 0, c.width, (c.height*(25/500)));
ctx.stroke();
ctx.beginPath();
ctx.fillStyle='#BC77BA';
ctx.fillRect(0, (c.height*(450/500)), c.width, (c.height*(50/500)));
ctx.stroke();
ctx.font='bold '+(c.height*(10/500))+'px Noto Sans';
ctx.fillStyle='#9F3A9B';
ctx.textAlign='center';
ctx.fillText('Test', (c.width*(365/750)), (c.height*(17/500)));
}
Paint.prototype.setHovered = function(shape) {
for (var i=0; i<this.shapes.length; i++) {
this.shapes[i].hovered = this.shapes[i] == shape;
}
this.render();
}
Paint.prototype.setClicked = function(shape) {
for (var i=0; i<this.shapes.length; i++) {
this.shapes[i].clicked = this.shapes[i] == shape;
}
this.render();
}
Paint.prototype.setUnclicked = function(shape) {
for (var i=0; i<this.shapes.length; i++) {
if (shape.constructor.name==this.shapes[i].constructor.name) {
this.shapes[i].clicked = false;
if (shape instanceof selectionForMenu) {
this.shapes[i].lastClicked = this.shapes[i] == shape;
if (this.shapes[i].lastClicked == true) {
this.shapes[i].rect.visible = true;
} else {
this.shapes[i].rect.visible = false;
}
}
}
}
this.render();
}
Paint.prototype.select = function(x, y) {
for (var i=this.shapes.length-1; i >= 0; i--) {
if (this.shapes[i].visible == true && this.shapes[i].hitTest(x, y)) {
return this.shapes[i];
}
}
return null
}
var numbers = [1,2,3,4,5];
var paint = new Paint(c);
var selection = [];
for (var i=0; i<numbers.length; i++) {
selection.push(new selectionForMenu(i+1, numbers[i], c.height*(25/500)+(c.height*((i*100)/500))));
}
for (var i=0; i<numbers.length; i++) {
var img = new curvedRect(i+1, (c.width*(112.5/750)), (c.height*(100/500)), (c.height*(175/500)), (c.height*(175/500)));
paint.addShape(img)
selection[i].rect = img;
}
for (var i=0; i<numbers.length; i++) {
paint.addShape(selection[i])
}
paint.render();
var clickedShape, clickIndex=0;
function mouseDown(event) {
var x = (event.pageX-rect.left)/(rect.right-rect.left)*c.width;
var y = (event.pageY-rect.top)/(rect.bottom-rect.top)*c.height;
var shape = paint.select(x, y);
if (shape instanceof selectionForMenu) {
if (clickIndex==0) {
clickedShape=shape;
clickIndex=1;
} else if (clickIndex==1) {
clickIndex=0;
}
}
paint.setClicked(shape);
}
function mouseUp(event) {
var x = (event.pageX-rect.left)/(rect.right-rect.left)*c.width;
var y = (event.pageY-rect.top)/(rect.bottom-rect.top)*c.height;
var shape = paint.select(x, y);
if (clickedShape instanceof selectionForMenu) {
if (x>c.width*(400/750) && y>c.height*(25/500) && y<c.height*(450/500)) {
paint.setUnclicked(shape);
} else if (shape && !(shape instanceof selectionForMenu)) {
paint.setUnclicked(shape);
}
}
}
function mouseMove(event) {
var x = (event.pageX-rect.left)/(rect.right-rect.left)*c.width;
var y = (event.pageY-rect.top)/(rect.bottom-rect.top)*c.height;
var shape = paint.select(x, y);
paint.setHovered(shape);
}
function paintCanvas() {
c.width = window.innerWidth;
c.height = (2/3)*c.width;
ctx=c.getContext('2d');
rect = c.getBoundingClientRect();
paint = new Paint(c);
selection = [];
for (var i=0; i<numbers.length; i++) {
selection.push(new selectionForMenu(i+1, numbers[i], c.height*(25/500)+(c.height*((i*100)/500))));
}
for (var i=0; i<numbers.length; i++) {
var img = new curvedRect(i+1, (c.width*(112.5/750)), (c.height*(100/500)), (c.height*(175/500)), (c.height*(175/500)));
paint.addShape(img)
selection[i].rect = img;
}
for (var i=0; i<numbers.length; i++) {
paint.addShape(selection[i])
}
paint.render();
}
paintCanvas();
window.addEventListener('resize', paintCanvas);
c.addEventListener('mousedown', mouseDown);
c.addEventListener('mouseup', mouseUp);
c.addEventListener('mousemove', mouseMove);
canvas {
z-index: -1;
margin: 1em auto;
border: 1px solid black;
display: block;
background: #9F3A9B;
}
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>uTalk Demo</title>
<link rel='stylesheet' type='text/css' href='wordpractice.css' media='screen'></style>
</head>
<body>
<div id='container'>
<canvas id="game"></canvas>
</div>
<script type='text/javascript' src='scaleStack.js'></script>
</body>
</html>
you must make the paint class (in your code) once,
function Game (elementID,width,height){
this.elementID = elementID;
this.element = document.getElementById(elementID);
this.width = width;
this.height = height;
this.palette = {
color1:'#fff',
color2:'#000',
color3:'#9F3A9B',
color4:'#a84ea5',
color5:'#b56ab2',
color6:'#bf7dbd',
color7:'#d5a8d2'
};
this.element.style.width = width + 'px';
this.element.style.height= height + 'px';
this.element.style.border='solid thin ' + this.palette.color2;
this.element.style.display= 'block';
//this.element.style.margin='1em auto';
this.element.style.background=this.palette.color3;
this.initialGame();
}
Game.prototype.initialGame = function(){
this.canvas = document.createElement("canvas");
this.canvas.width = this.width;
this.canvas.height = this.height;
this.element.appendChild(this.canvas);
this.initialTitle();
this.initialSideButtons();
this.initialBoard();
this.initialFooter();
// initial selection
this.sideButtons.select(this.sideButtons.buttons[0]);
this.resize(this.width,this.height);
this.render();
this.attachEvents();
}
Game.prototype.attachEvents = function(){
var element = this.element;
var getX = function(evt){return evt.offsetX || evt.layerX || (evt.clientX - element.offsetLeft);};
var getY = function(evt){return evt.offsetY || evt.layerY || (evt.clientY - element.offsetTop);};
var game = this;
this.element.addEventListener('mousemove',function(evt){
game.hover(getX(evt),getY(evt));
game.render();
});
this.element.addEventListener('click',function(evt){
game.sideButtons.click();
game.render();
});
}
Game.prototype.onSelect = function(button){
this.selected = button;
};
Game.prototype.hover=function(x,y){
this.hoverX = x;
this.hoverY = y;
};
Game.prototype.initialBoard = function(){
var game = this;
var Board = function(){
this.left = 0;
this.top = 0;
this.width =0;
this.height=0;
};
Board.prototype.render = function(ctx){
if(game.selected){
var shapeWidth = this.width/3;
ctx.fillStyle = game.palette.color1;
ctx.strokeStyle = game.palette.color1;
var fontSize = 14;
ctx.font = 'bold '+ fontSize +'px Noto Sans';
ctx.textAlign='center';
ctx.lineWidth=8;
ctx.lineJoin = 'round';
ctx.strokeRect(this.left + this.width/2 - (shapeWidth/2),this.height/2-(shapeWidth/2) + this.top,shapeWidth,shapeWidth);
ctx.fillText(game.selected.text,this.left + this.width/2,this.height/2 + this.top );
}
};
this.board = new Board();
};
Game.prototype.initialSideButtons = function(){
var game = this;
var ButtonBar =function(text){
this.text = text;
this.left = 0;
this.top = 0;
this.width = 1;
this.height= 1;
this.selected=false;
};
ButtonBar.prototype.hitTest=function(x,y){
return (this.left < x) && (x < (this.left + this.width)) &&
(this.top <y) && (y < (this.top + this.height));
};
ButtonBar.prototype.getColor=function(){
var hovered = this.hitTest(game.hoverX,game.hoverY);
if(this.selected){
if(hovered)
{
return game.palette.color7;
}
return game.palette.color6;
}
if(hovered){
return game.palette.color5;
}
return game.palette.color4;
};
ButtonBar.prototype.render = function(ctx){
var fontSize = 14;
ctx.fillStyle = this.getColor();
ctx.fillRect(this.left,this.top,this.width,this.height);
ctx.fillStyle = game.palette.color1;
ctx.textAlign = 'left';
ctx.font ='bold '+ fontSize +'px Noto Sans';
ctx.fillText(this.text,this.left + 10,this.top+ this.height/2);
};
var SideButtons = function(){
this.buttons = [];
this.width = 1;
this.height= 1;
this.left=1;
this.top=1;
};
SideButtons.prototype.render = function(ctx){
if(!this.buttons.length){
return;
}
var height = this.height / this.buttons.length ;
for(var i=0;i<this.buttons.length;i++){
var btn = this.buttons[i];
btn.left = this.left;
btn.top = i * height + this.top;
btn.width = this.width;
btn.height = height;
this.buttons[i].render(ctx);
}
};
SideButtons.prototype.click = function(){
var current = null;
for(var i=0;i<this.buttons.length;i++){
var btn = this.buttons[i];
if( btn.hitTest(game.hoverX,game.hoverY))
{
this.select(btn);
break;
}
}
};
SideButtons.prototype.select = function(btn)
{
for(var i=0;i<this.buttons.length;i++)
{
this.buttons[i].selected = false;
}
btn.selected=true;
game.onSelect(btn);
};
this.sideButtons = new SideButtons();
var btn1 = new ButtonBar('Button 1');
var btn2 = new ButtonBar('Button 2');
var btn3 = new ButtonBar('Button 3');
var btn4 = new ButtonBar('Button 4');
this.sideButtons.buttons.push(btn1);
this.sideButtons.buttons.push(btn2);
this.sideButtons.buttons.push(btn3);
this.sideButtons.buttons.push(btn4);
};
Game.prototype.initialTitle = function(){
var Title = function(value,width,height){
this.value=value;
this.width = width;
this.height= height;
};
var game = this;
Title.prototype.render=function(ctx){
var k = 2;
var fontSize = this.height / k;
ctx.fillStyle=game.palette.color1;
ctx.fillRect(0,0,this.width,this.height);
ctx.font='bold '+ fontSize +'px Noto Sans'; // check
ctx.fillStyle=game.palette.color3;
ctx.textAlign='center';
ctx.fillText(this.value,this.width/2,this.height - fontSize/2);
};
this.title = new Title('Test',this.width,this.height / 10);
}
Game.prototype.initialFooter = function(){
var Footer = function(){
this.width = 1;
this.height= 1;
this.left=0;
this.top=0;
}
var game = this;
Footer.prototype.render = function(ctx){
ctx.fillStyle = game.palette.color5;
ctx.fillRect(this.left,this.top,this.width,this.height);
};
this.footer = new Footer();
};
Game.prototype.resetCanvas = function(){
this.canvas.width = this.width;
this.canvas.height = this.height;
};
Game.prototype.render = function(){
this.resetCanvas();
var context = this.canvas.getContext('2d');
this.title.render(context);
this.sideButtons.render(context);
this.board.render(context);
this.footer.render(context);
};
Game.prototype.resize = function (width,height){
this.width = width;
this.height= height;
this.element.style.width = width + 'px';
this.element.style.height= height+ 'px';
this.title.height = this.height / 14;
this.title.width = this.width;
this.footer.height = this.title.height;
this.footer.width = this.width;
this.footer.top = this.height - this.footer.height;
this.footer.left = 0;
this.board.top = this.title.height;
this.board.left = 0;
this.board.width = this.width - 250;//or -> this.width / 2
this.board.height= this.height - this.title.height - this.footer.height;
this.sideButtons.left= this.board.width;
this.sideButtons.top = this.board.top;
this.sideButtons.width = this.width - this.board.width;
this.sideButtons.height = this.board.height;
this.render();
};
var game = new Game('game',window.innerWidth -50,window.innerWidth * 2/3);
window.addEventListener('resize', function(){
game.resize(window.innerWidth -50,window.innerWidth * 2/3);
});
<div id='container'>
<div id="game"></div>
</div>
The issue is that your resize handler calls paintCanvas and in your paintCanvas method you are assigning your global paint variable to new an entirely new instance of paint. This entirely wipes out your state and forces the canvas to be redrawn to match the initial state of an initial page load. Instead, you need to maintain your state, clear your canvas and render it again with its existing state but just with new sizes.
function paintCanvas() {
c.width = window.innerWidth;
c.height = (2/3)*c.width;
ctx=c.getContext('2d');
rect = c.getBoundingClientRect();
//paint = new Paint(c);
Commenting out //paint = new Paint(c); leaves your state intact. You still some remnants you need to flush out and redraw since you are no longer destroying your state.
var c=document.getElementById('game'),
rect = c.getBoundingClientRect(),
ctx=c.getContext('2d');
c.width = window.innerWidth;
c.height = (2/3)*c.width;
numberImages = ['https://i.stack.imgur.com/TZIUz.png','https://i.stack.imgur.com/6beTF.png','https://i.stack.imgur.com/wZk2H.png','https://i.stack.imgur.com/1K743.png','https://i.stack.imgur.com/jMMmQ.png'];
var curvedRect = function(number, x, y, w, h) {
this.text = number.toString();
this.img = new Image();
this.img.src=numberImages[number-1];
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.hovered = false;
this.clicked = false;
this.visible = false;
}
var selected;
curvedRect.prototype.makeCurvedRect = function() {
var delta=0, theta=0;
if (this.hovered) {
delta = (c.height*(3/500));
theta = -0.01;
shadowColor = '#000000';
shadowBlur = 20;
shadowOffsetX = 5;
shadowOffsetY = 5;
} else {
delta = 0;
theta = 0;
shadowColor = '#9F3A9B';
shadowBlur = 0;
shadowOffsetX = 0;
shadowOffsetY = 0;
}
var x = this.x-delta;
var y = this.y-delta;
var w = this.w+(2*delta);
var h = this.h+(2*delta);
var cornerRounder = (c.height*(10/500))
ctx.rotate(theta);
ctx.beginPath();
ctx.lineWidth='12';
ctx.strokeStyle='white';
ctx.moveTo(x+cornerRounder, y);
ctx.lineTo(x+w-cornerRounder, y);
ctx.quadraticCurveTo(x+w, y, x+w, y+cornerRounder);
ctx.lineTo(x+w, y+h-cornerRounder);
ctx.quadraticCurveTo(x+w, y+h, x+w-cornerRounder, y+h);
ctx.lineTo(x+cornerRounder, y+h);
ctx.quadraticCurveTo(x, y+h, x, y+h-cornerRounder);
ctx.lineTo(x, y+cornerRounder);
ctx.quadraticCurveTo(x, y, x+cornerRounder, y);
ctx.shadowColor = shadowColor;
ctx.shadowBlur = shadowBlur;
ctx.shadowOffsetX = shadowOffsetX;
ctx.shadowOffsetY = shadowOffsetY;
ctx.stroke();
ctx.shadowBlur = 0;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
ctx.drawImage(this.img, x+(c.width*(2.5/750)), y+(c.height*(2.5/500)), w-cornerRounder/2, h-cornerRounder/2);
ctx.rotate(-theta);
}
curvedRect.prototype.hitTest = function(x, y) {
return (x >= this.x) && (x <= (this.w+this.x)) && (y >= this.y) && (y <= (this.h+this.y));
}
var selectionForMenu = function(id, text, y) {
this.id = id;
this.text = text;
this.y = y;
this.hovered = false;
this.clicked = false;
this.lastClicked = false;
this.visible = true;
}
function makeTextForSelected(text, y) {
ctx.font='bold '+(c.height*(12/500))+'px Noto Sans'; // check
ctx.fillStyle='white';
ctx.textAlign='center';
ctx.fillText(text, (c.width*(200/750)), y);
}
selectionForMenu.prototype.makeSelection = function() {
ctx.globalAlpha=0.75;
var fillColor='#A84FA5';
if (this.hovered) {
if (this.clicked) {
if (this.lastClicked) {
fillColor='#E4C7E2';
makeTextForSelected(this.text, c.height*(375/500));
} else {
fillColor='#D5A9D3';
}
} else if (this.lastClicked) {
fillColor='#D3A4D0';
makeTextForSelected(this.text, c.height*(375/500));
} else {
fillColor='#BA74B7';
}
} else if (this.lastClicked) {
fillColor='#C78DC5';
makeTextForSelected(this.text, c.height*(375/500));
} else {
fillColor='#A84FA5';
}
ctx.beginPath();
ctx.fillStyle=fillColor;
ctx.fillRect(c.width*(400/750), this.y, c.width*(350/750), c.height*(100/500))
ctx.stroke();
ctx.font=c.height*(10/500)+'px Noto Sans';
ctx.fillStyle='white';
ctx.textAlign='left';
ctx.fillText(this.text, c.width*(410/750), this.y+(c.height*(38/500)));
ctx.globalAlpha=1;
}
selectionForMenu.prototype.hitTest = function(x, y) {
return (x >= (c.width*(400/750)) && (x <= c.width) && (y >= this.y) &&
(y <= (this.y+(c.height*(100/500))) && !((x >= c.width*(400/750) && (y > c.height*(450/500))))));
}
var Paint = function(element) {
this.element = element;
this.shapes = [];
}
Paint.prototype.addShape = function(shape) {
this.shapes.push(shape);
}
Paint.prototype.render = function() {
ctx.clearRect(0, 0, this.element.width, this.element.height);
for (var i=0; i<this.shapes.length; i++) {
try {
this.shapes[i].makeSelection();
}
catch(err) {}
try {
if(this.shapes[i].lastClicked == true) {
this.shapes[i].rect.makeCurvedRect();
}
}
catch(err) {}
}
ctx.beginPath();
ctx.fillStyle='white';
ctx.fillRect(0, 0, c.width, (c.height*(25/500)));
ctx.stroke();
ctx.beginPath();
ctx.fillStyle='#BC77BA';
ctx.fillRect(0, (c.height*(450/500)), c.width, (c.height*(50/500)));
ctx.stroke();
ctx.font='bold '+(c.height*(10/500))+'px Noto Sans';
ctx.fillStyle='#9F3A9B';
ctx.textAlign='center';
ctx.fillText('Test', (c.width*(365/750)), (c.height*(17/500)));
}
Paint.prototype.setHovered = function(shape) {
for (var i=0; i<this.shapes.length; i++) {
this.shapes[i].hovered = this.shapes[i] == shape;
}
this.render();
}
Paint.prototype.setClicked = function(shape) {
for (var i=0; i<this.shapes.length; i++) {
this.shapes[i].clicked = this.shapes[i] == shape;
}
this.render();
}
Paint.prototype.setUnclicked = function(shape) {
for (var i=0; i<this.shapes.length; i++) {
if (shape.constructor.name==this.shapes[i].constructor.name) {
this.shapes[i].clicked = false;
if (shape instanceof selectionForMenu) {
this.shapes[i].lastClicked = this.shapes[i] == shape;
if (this.shapes[i].lastClicked == true) {
this.shapes[i].rect.visible = true;
} else {
this.shapes[i].rect.visible = false;
}
}
}
}
this.render();
}
Paint.prototype.select = function(x, y) {
for (var i=this.shapes.length-1; i >= 0; i--) {
if (this.shapes[i].visible == true && this.shapes[i].hitTest(x, y)) {
return this.shapes[i];
}
}
return null
}
var numbers = [1,2,3,4,5];
var paint = new Paint(c);
var selection = [];
for (var i=0; i<numbers.length; i++) {
selection.push(new selectionForMenu(i+1, numbers[i], c.height*(25/500)+(c.height*((i*100)/500))));
}
for (var i=0; i<numbers.length; i++) {
var img = new curvedRect(i+1, (c.width*(112.5/750)), (c.height*(100/500)), (c.height*(175/500)), (c.height*(175/500)));
paint.addShape(img)
selection[i].rect = img;
}
for (var i=0; i<numbers.length; i++) {
paint.addShape(selection[i])
}
paint.render();
var clickedShape, clickIndex=0;
function mouseDown(event) {
var x = (event.pageX-rect.left)/(rect.right-rect.left)*c.width;
var y = (event.pageY-rect.top)/(rect.bottom-rect.top)*c.height;
var shape = paint.select(x, y);
if (shape instanceof selectionForMenu) {
if (clickIndex==0) {
clickedShape=shape;
clickIndex=1;
} else if (clickIndex==1) {
clickIndex=0;
}
}
paint.setClicked(shape);
}
function mouseUp(event) {
var x = (event.pageX-rect.left)/(rect.right-rect.left)*c.width;
var y = (event.pageY-rect.top)/(rect.bottom-rect.top)*c.height;
var shape = paint.select(x, y);
if (clickedShape instanceof selectionForMenu) {
if (x>c.width*(400/750) && y>c.height*(25/500) && y<c.height*(450/500)) {
paint.setUnclicked(shape);
} else if (shape && !(shape instanceof selectionForMenu)) {
paint.setUnclicked(shape);
}
}
}
function mouseMove(event) {
var x = (event.pageX-rect.left)/(rect.right-rect.left)*c.width;
var y = (event.pageY-rect.top)/(rect.bottom-rect.top)*c.height;
var shape = paint.select(x, y);
paint.setHovered(shape);
}
function paintCanvas() {
c.width = window.innerWidth;
c.height = (2/3)*c.width;
ctx=c.getContext('2d');
rect = c.getBoundingClientRect();
//paint = new Paint(c);
selection = [];
for (var i=0; i<numbers.length; i++) {
selection.push(new selectionForMenu(i+1, numbers[i], c.height*(25/500)+(c.height*((i*100)/500))));
}
for (var i=0; i<numbers.length; i++) {
var img = new curvedRect(i+1, (c.width*(112.5/750)), (c.height*(100/500)), (c.height*(175/500)), (c.height*(175/500)));
paint.addShape(img)
selection[i].rect = img;
}
for (var i=0; i<numbers.length; i++) {
paint.addShape(selection[i])
}
paint.render();
}
paintCanvas();
window.addEventListener('resize', paintCanvas);
c.addEventListener('mousedown', mouseDown);
c.addEventListener('mouseup', mouseUp);
c.addEventListener('mousemove', mouseMove);
canvas {
z-index: -1;
margin: 1em auto;
border: 1px solid black;
display: block;
background: #9F3A9B;
}
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>uTalk Demo</title>
<link rel='stylesheet' type='text/css' href='wordpractice.css' media='screen'></style>
</head>
<body>
<div id='container'>
<canvas id="game"></canvas>
</div>
<script type='text/javascript' src='scaleStack.js'></script>
</body>
</html>

Why is this text disappearing but not the shape?

How do I make the shape disappear like the text does? I've gone through the code and they're practically identical except that one is created when the user spins the mouse wheel and the other is created when the user clicks on the screen, but the text disappears after time and the triangle does not. I feel like there's something small I must be missing. Here's the code:
<html>
<head>
<script>
var canvas;
var context;
var triangles = [];
var texts = [];
var timer;
var textSayings = ['Cool!', 'Nice!', 'Awesome!', 'Wow!', 'Whoa!', 'Super!', 'Woohoo!', 'Yay!', 'Yeah!', ':)', ':D'];
function init() {
canvas = document.getElementById('canvas');
context = canvas.getContext("2d");
//resize canvas to fit the window
resizeCanvas();
window.addEventListener('resize', resizeCanvas, false);
window.addEventListener('orientationchange', resizeCanvas, false);
canvas.onwheel = function(event) {
handleWheel(event.clientX, event.clientY);
};
canvas.onclick = function(event) {
handleClick(event.clientX, event.clientY);
}
var timer = setInterval(resizeCanvas, 30);
}
function Triangle(x,y,triangleColor) {
this.x = x;
this.y = y;
this.triangleColor = triangleColor;
this.vx = Math.random() * 80 - 40;
this.vy = Math.random() * 80 - 40;
this.time = 250;
}
function Text(x,y,textColor,word) {
this.x = x;
this.y = y;
this.word = word;
this.textColor = textColor;
this.vx = Math.random() * 20 - 10;
this.vy = Math.random() * 20 - 10;
this.time = 300;
}
function handleWheel(x,y) {
var colors = [[255,0,0],[255,255,255],[0,0,255]];
var triangleColor = colors[Math.floor(Math.random()*colors.length)];
triangles.push(new Triangle(x,y,triangleColor));
for (var i=0; i<triangles.length; i++) {
drawTriangle(triangles[i]);
}
}
function handleClick(x,y) {
var colors = [[255,0,0],[255,255,0],[0,0,255]];
var textColor = colors[Math.floor(Math.random()*colors.length)];
texts.push(new Text(x,y,textColor,pickWord()));
for (var i=0; i<texts.length; i++) {
drawText(texts[i]);
}
}
function timeToFade(time) {
if(time > 100) {
return 1;
}
else {
return time / 100;
}
}
function pickWord() {
return textSayings[Math.floor(Math.random() * textSayings.length)];
}
function drawText(text) {
context.font = "bold 80px Verdana";
var gradient=context.createLinearGradient(0,0,canvas.width,0);
gradient.addColorStop("0","magenta");
gradient.addColorStop("0.25","yellow");
gradient.addColorStop("0.5","lime");
gradient.addColorStop("0.75","aqua");
gradient.addColorStop("1.0","magenta");
context.fillStyle = gradient;
context.fillText(text.word,text.x,text.y);
}
function drawTriangle(triangle) {
context.beginPath();
context.moveTo(triangle.x,triangle.y);
context.lineTo(triangle.x+25,triangle.y+25);
context.lineTo(triangle.x+25,triangle.y-25);
var gradient = context.createLinearGradient(0,0,canvas.width,0);
gradient.addColorStop("0","red");
gradient.addColorStop("0.25","salmon");
gradient.addColorStop("0.5","aqua");
gradient.addColorStop("0.75","lime");
gradient.addColorStop("1.0","orange");
context.fillStyle = gradient;
context.fill();
}
function resizeCanvas() {
canvas.width = window.innerWidth-20;
canvas.height = window.innerHeight-20;
fillBackgroundColor();
for (var i=0; i<triangles.length; i++) {
var t = triangles[i];
drawTriangle(t);
if (t.x + t.vx > canvas.width || t.x + t.vx < 0)
t.vx = -t.vx
if (t.y + t.vy > canvas.height || t.y + t.vy < 0)
t.vy = -t.vy
if (t.time === 0) {
triangles.splice(i,1);
}
t.time -= 3;
t.x += t.vx;
t.y += t.vy;
}
for (var i=0; i<texts.length; i++) {
var te = texts[i];
drawText(te);
if (te.x + te.vx > canvas.width || te.x + te.vx < 0)
te.vx = -te.vx
if (te.y + te.vy > canvas.height || te.y + te.vy < 0)
te.vy = -te.vy
if (te.time === 0) {
texts.splice(i,1);
}
te.time -= 3;
te.x += te.vx;
te.y += te.vy;
}
}
function fillBackgroundColor() {
context.globalCompositeOperation = 'source-over';
context.fillStyle = 'rgba(0, 0, 0, 1)';
context.fillRect(0,0,canvas.width,canvas.height);
context.globalCompositeOperation = 'lighter';
}
window.onload = init;
</script>
</head>
<body>
<canvas id="canvas" width="500" height="500"></canvas>
</body>
</html>
It's because the triangle time isn't a multiple of 3, while the text time is so when you check this if statement:
if (t.time === 0) {
triangles.splice(i,1);
}
It's never true.
You can fix this by changing the if statement to:
if (t.time <= 0) {
triangles.splice(i,1);
}
This is actually my fault since it's a bug that was in my previous answer. Sorry about that.
jsfiddle:
https://jsfiddle.net/0rst8def/

HTML5 Javascript Object Has Been Clicked On

I'm making a game where your mouse is a cross hair and you click on zombies moving across the screen. I'm having troubles figuring out how to find if the mouse has clicked on a zombie. The zombies are made in JavaScript so I can't use the onclick attribute in HTML. This is my code.
var mouseX;
var mouseY;
$(document).ready(function(){
var canvasWidth = 640;
var canvasHeight = 480;
var canvas = $("#gameCanvas")[0];
var context = canvas.getContext("2d");
var score = 0;
var timer = 0;
var spawnZombie = false;
//crosshair
var crossHair = new Image();
var crosshairX = 50;
var crosshairY = 50;
crossHair.src = "media/crosshair.png";
//zombies
var zombies = [];
var zombieLeft = new Image();
zombieLeft.src = "media/zombieLEFT.gif";
var zombieRight = new Image();
zombieRight.src = "media/zombieRIGHT.gif";
var fps = 30;
setInterval(function(){
update();
draw();
}, 1000/fps);
function update(){
timer += 1;
crosshairX = mouseX - 445;
crosshairY = mouseY - 125;
if(timer >= 70){
timer = 0;
spawnZombie = true;
}
zombies.forEach(function(zombie) {
zombie.update();
document.body.onmousedown = function() {
console.log(zombie.x.toString() + ", " + zombie.y.toString());
//if(mouseX)
};
});
zombies = zombies.filter(function(zombie){
return zombie.active;
});
if(spawnZombie){
zombies.push(Zombie(null, "left"));
zombies.push(Zombie(null, "right"));
spawnZombie = false;
}
}
function draw(){
context.clearRect(0,0, canvasWidth, canvasHeight);
context.fillStyle = "#000";
context.font = "20px Comic Sans MS";
context.fillText("Score: " + score, 50, 50);
zombies.forEach(function(zombie) {
zombie.draw();
});
context.drawImage(crossHair, crosshairX, crosshairY, 100, 100);
}
function Zombie(I, dir){
I = I || {};
I.active = true;
I.speed = 5;
I.y = getRandomInt(50, 350);
if(dir == "left"){
I.x = 800;
}
else if(dir == "right"){
I.x = -100;
}
I.width = 100;
I.height = 100;
I.inBounds = function() {
return I.x >= 0 && I.x <= canvasWidth &&
I.y >= 0 && I.y <= canvasHeight;
};
I.draw = function(){
if(dir == "left"){
context.drawImage(zombieLeft, this.x, this.y, this.width, this.height);
}
else if(dir == "right"){
context.drawImage(zombieRight, this.x, this.y, this.width, this.height);
}
};
I.update = function(){
if(dir == "left"){
this.x -= this.speed;
}
else if(dir == "right"){
this.x += this.speed;
}
};
I.onclick = function(){
I.remove();
};
return I;
}
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
});
$( document ).on( "mousemove", function( event ) {
mouseX = event.pageX;
mouseY = event.pageY;
});
This is the specific spot where I'm trying to detect if the zombie has been clicked on within my update functio
zombies.forEach(function(zombie) {
zombie.update();
document.body.onmousedown = function() {
console.log(zombie.x.toString() + ", " + zombie.y.toString());
//if(mouseX)
};
});
I can't use the onclick attribute in HTML.
But you can use the addEventListener method in your javascript:
eg.
zombieLeft.addEventListener('click', myFunction, false);
document.body.onmousedown = function(event) {
console.log(zombie.x.toString() + ", " + zombie.y.toString());
if(event.pageX == zombie.x && event.pageY == zombie.y){
//Zombie clicked
}
};
This is pseudo code. You can attach event and you can check for x and y.

Polymorphic Behavior in Javascript

I am trying to create a base class called Sprite and then child classes of whatever, in this case "Dog."
I want the objects to have all the same properties like x, y, width, height, but have different behaviors for methods, like this one called mouseEventListener.
I cannot seem to get the code to work correctly.
function Sprite(imgg,w,h)
{
this.img = imgg;
this.x = 350;//Math.random()*700;
this.y = 350;//Math.random()*700;
this.vx = 0;//Math.random()*8-4;
this.vy = 0;//Math.random()*8-4;
this.width = w;
this.height = h;
this.rotatespeed = 0.00;
this.rotate = 40;
}
Sprite.prototype.drawSprite = function(ctx)
{
ctx.save();
ctx.translate(this.x,this.y);
ctx.rotate(this.rotate);
ctx.drawImage(this.img,0,0,this.img.width,this.img.height,-this.width/2,-this.height/2,this.width,this.height);
ctx.restore();
}
Sprite.prototype.mouseEventListener = function(evt, type)
{
console.log("In Sprite");
}
Dog.prototype.mouseEventListener = function(evt,type)
{
console.log("In Dog");
}
Here is the full code:
/**
How to use this library
1.
*/
function Sprite(imgg,w,h)
{
this.img = imgg;
this.x = 350;//Math.random()*700;
this.y = 350;//Math.random()*700;
this.vx = 0;//Math.random()*8-4;
this.vy = 0;//Math.random()*8-4;
this.width = w;
this.height = h;
this.rotatespeed = 0.00;
this.rotate = 40;
}
Sprite.prototype.drawSprite = function(ctx)
{
ctx.save();
ctx.translate(this.x,this.y);
ctx.rotate(this.rotate);
ctx.drawImage(this.img,0,0,this.img.width,this.img.height,-this.width/2,-this.height/2,this.width,this.height);
ctx.restore();
}
Sprite.prototype.updateSprite = function()
{
this.x += this.vx;
this.y += this.vy;
this.rotate += this.rotatespeed;
if(this.x > 700)
this.vx = -this.vx;
if(this.x < 0)
this.vx = -this.vy;
if(this.y > 700)
this.vy = -this.vy;
if(this.y < 0)
this.vy = -this.vy;
}
Sprite.prototype.mouseEventListener = function(evt, type)
{
console.log("In Sprite");
}
Dog.prototype = new Sprite();
Dog.prototype.mouseEventListener = function(evt,type)
{
console.log("In Dog");
}
//------------------------------------------
//GLOBAL VARIALBES
var setIntervalAmount = 30;
var scrollAmount = 0.5;
var game;
function Camera()
{
this.x = -350;
this.y = -350;
this.rotate = 0;
this.scale = 0;
this.scale = 1;
this.previousX = 0;
this.previousY = 0;
this.drag = false;
}
function Game()
{
this.camera = new Camera();
this.canvas = document.createElement("canvas");
document.body.appendChild(this.canvas);
this.canvas.id="mycanvas";
this.canvas.width = 700;
this.canvas.height = 700;
this.context = this.canvas.getContext("2d");
this.objects = new Array();
}
Game.prototype.gameLoop = function()
{
this.context.clearRect(0,0,this.canvas.width, this.canvas.height);
this.context.save();
this.context.translate(this.canvas.width/2, this.canvas.height/2);
this.context.scale(this.camera.scale,this.camera.scale);
this.context.rotate(this.camera.rotate);
this.context.translate(this.camera.x,this.camera.y);
for(var i=0;i<this.objects.length;i++)
{
this.objects[i].updateSprite();
this.objects[i].drawSprite(this.context);
}
this.context.restore();
}
Game.prototype.handleMouse = function(evt,type)
{
var mouseX = event.clientX - this.context.canvas.offsetLeft;
var mouseY = event.clientY - this.context.canvas.offsetTop;
var canvasX = mouseX * this.context.canvas.width / this.context.canvas.clientWidth;
var canvasY = mouseY * this.context.canvas.height / this.context.canvas.clientHeight;
// canvasX = this.camera.scale / canvasX;
// canvasY = this.camera.scale / canvasY;
canvasX /= this.camera.scale;
canvasY /= this.camera.scale;
if(type == "move")
{
if(this.camera.drag == true)
{
this.camera.x -= this.camera.previousX-canvasX;
this.camera.y -= this.camera.previousY-canvasY;
}
this.camera.previousX = canvasX;
this.camera.previousY = canvasY;
}
if(type =="down")
{
this.camera.drag = true;
}
if(type == "up")
{
this.camera.drag = false;
}
for(var i=0;i<this.objects.length;i++)
{
this.objects[i].mouseEventListener(evt,type);
}
};
Game.prototype.mouseWheelListener = function(evt)
{
if(evt.wheelDelta < 0)
game.camera.scale /= (1+scrollAmount);
else
game.camera.scale *= (1+scrollAmount);
}
function mouseWheelListener()
{
var evt = window.event;
game.mouseWheelListener(evt);
}
function mouseDownListener()
{
var evt = window.event;
var type = "down"
game.handleMouse(evt,type);
}
function mouseUpListener()
{
var evt = window.event;
var type = "up"
game.handleMouse(evt,type);
}
function mouseMoveListener()
{
var evt = window.event;
var type = "move"
game.handleMouse(evt,type);
}
//------------------
window.addEventListener('load',function(event){startgame();});
var dog = new Image();
dog.src = "dog.png";
function startgame()
{
game = new Game();
for(var i=0;i<1;i++)
game.objects.push(new Dog(dog,250,250));
setInterval(function(){game.gameLoop()},setIntervalAmount);
document.getElementById("mycanvas").addEventListener("wheel", mouseWheelListener);
document.getElementById("mycanvas").addEventListener("mousedown", mouseDownListener);
document.getElementById("mycanvas").addEventListener("mouseup", mouseUpListener);
document.getElementById("mycanvas").addEventListener("mousemove", mouseMoveListener);
}
I found the answer on a Youtube video...
Dog.prototype = Object.create(Sprite.prototype);
That is the line of code that I need.

Categories