Trampoline Goals

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
<!doctype html>
<meta charset='utf-8'>
<meta name="viewport" content="width=520">
<title>πŸ₯… Trampoline Goals</title>
Don't drop any items, and bounce them into the goal <br>
<canvas style='background:linear-gradient(blue, white);border-radius:3px;'
	id='canvas' height=500 width=500></canvas> <br>
<button id="ArrowLeft">&larr;</button><button id="ArrowRight">&rarr;</button>
<style> button {width:240px;margin:5px; height:130px;font-size:70px;} </style>
<script>
	var people = [..."πŸ•ΊπŸ€ΎπŸ§πŸ§˜β›„πŸ€ΈπŸ’ƒπŸƒπŸŽŽβ›Ήβ›·πŸšΆπŸ‘«"], fruits = [..."πŸπŸ“πŸŒπŸ‰πŸŠ"], drinks = [..."🍷🍸🍹🍺"],
		animals = [..."πŸπŸ…πŸ˜πŸŒπŸ“"], vehicles = [..."πŸš‚πŸš’πŸšŒπŸš“πŸšœπŸš›"];
	var levels = [vehicles, animals, drinks, fruits, people];
	var item_positions = [ {y: 100, speed_y: 100}, {y: 410, speed_y: -200}, {y: 10, speed_y: -20},
		{y: 25, speed_y: 40}, {y: 200, speed_y: -140}, {y: 70, speed_y: -80} ];
	var game = {level:0, over: false};
	var drawing = canvas.getContext("2d");
	Object.assign(drawing, {textAlign: "center", textBaseline: "middle", font: "30px serif"});
	var gravity = 50,
		deceleration = 2,
		bat = {x: 10, y: 480, width: 130, height: 10, speed_y: 0, speed_x: 0}, 
		balls = [],
		goal = {x: canvas.width-50, y: 400, symbol: "πŸ₯…", height:30, width: 30};
		keys_down = {}, frame = {};
	onkeydown = (e) => {keys_down[e.key] = true;}
	onkeyup = (e) => {keys_down[e.key]= false;}
	[ArrowLeft, ArrowRight].forEach(button=>{
		button.addEventListener("pointerdown", () => {keys_down[button.id] = true});
		button.addEventListener("pointerup", ()=>{keys_down[button.id] = false});
		button.addEventListener("pointerleave", ()=>{keys_down[button.id] = false});
	});
	function text(string,x,y,size=30){
		drawing.font = size+"px sans-serif";
		drawing.fillText(string,x,y);
	}
	var next_level = (level)=>{
		if (level > levels.length) return game.over = "You won!";
		var positions = item_positions.slice(0,game.level+1).sort((a,b)=>b.speed_y-a.speed_y);
		for (var i = 0; i < positions.length; i++){
			var ball = {width: 30, height:30, speed_x: 0, symbol: levels[game.level-1][i]};
			balls.push({x: 10+ i *80, ...positions[i], ...ball});
		};
		bat.x = bat.speed_x = 3;
		game.time_left = balls.length * 40;
	}
	var rectangle_hit=(a,b)=> (a.x < b.right && b.x< a.right && a.y < b.bottom && b.y < a.bottom);
	var hits_bat = (item) => {
		if (rectangle_hit(item, bat)){
			var y_overlap = bat.y - item.y;
			var x_overlap = Math.max(bat.x - item.x, (item.x + item.width) - (bat.x + bat.width));
			if (y_overlap > x_overlap) return "top";
			else return "side";
		}
	}
	function round_rectangle(x,y,width,height, corner_radius, stroke_colour, fill_colour){
		drawing.beginPath();
		drawing.lineWidth = 3;
		if (stroke_colour) drawing.strokeStyle = stroke_colour;
		if (fill_colour)  drawing.fillStyle = fill_colour;
		drawing.roundRect(x, y, width, height, [corner_radius]);
		if (fill_colour) drawing.fill();
		if (stroke_colour) drawing.stroke();
	}
	requestAnimationFrame(function do_one_frame(now) {
		drawing.clearRect(0,0, canvas.width, canvas.height);
		frame.interval = frame.last_time ? (now - frame.last_time) / 1000 : 0.016;
		frame.last_time = now;
		if (keys_down.ArrowLeft) bat.speed_x -= 800 * frame.interval;
		else if (keys_down.ArrowRight) bat.speed_x += 800 * frame.interval;
		else bat.speed_x -= (bat.speed_x * deceleration * frame.interval);		
		[bat, goal, ...balls].forEach(i => {
			if (i !== bat && i !== goal) i.speed_y += gravity * frame.interval;
			if (i.y > canvas.height) game.over = "Dropped item";
			if (i.x < 0 && i.speed_x < 0) i.speed_x *= -1;
			else if (i.x + i.width > canvas.width && i.speed_x > 0) i.speed_x *= -1;
			if (i.speed_x) i.x += i.speed_x * frame.interval;
			if (i.speed_y) i.y += i.speed_y * frame.interval;
			i.right = i.x + i.width;
			i.bottom = i.y + i.height;
			if (i !== bat && i.speed_y > 0 && hits_bat(i)) {
				if (hits_bat(i) == "top") {
					i.speed_y = -210;
					i.speed_x += (bat.speed_x - i.speed_x) * 0.5;
				}
				else i.speed_x = bat.speed_x;
			}
			if (i !== goal && rectangle_hit(i, goal)) i.deleted = true;
			if (i == bat) round_rectangle(i.x, i.y, i.width, i.height, 10, "green", "black");
			else text(i.symbol, i.x + i.width/2, i.y + i.height/2, 30);
			if (i == goal) round_rectangle(i.x-10, i.y-10, i.width+20, i.height+20, 15, "green");
		});
		balls = balls.filter(b=>!b.deleted);
		if (balls.length == 0) next_level(++game.level);
		if ((game.time_left -= frame.interval) <= 0) game.over = "Out of time";
		if (game.over) text("Game Over: "+game.over, 250, 250, 40);
		else requestAnimationFrame(do_one_frame);
		text("Time Left", 35, 20, 16);
		round_rectangle(80, 10, Math.ceil(game.time_left*1.5), 20, 5, null, "green");
	});
</script>