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

后端 未结 2 1741
长情又很酷
长情又很酷 2021-01-23 01:26

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

相关标签:
2条回答
  • 2021-01-23 02:16

    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>

    0 讨论(0)
  • 2021-01-23 02:26

    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>

    0 讨论(0)
提交回复
热议问题