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 JavaScrip
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>