<!DOCTYPE html>
<html lang="en-US">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<style>
#canvas {
background:red;
background-image: url("wall.jpg");
background-size: 510px 310px;
background-repeat: no-repeat;
cursor: pointer;
-webkit-box-shadow: 4px 4px 8px rgba(0,0,0,0.5);
-moz-box-shadow: 4px 4px 8px rgba(0,0,0,0.5);
box-shadow: 4px 4px 8px rgba(0,0,0,0.5);
display:block;
margin:0 auto;
}
#bBtn{
background-color: #4CAF50;
border: #2f6a31;
color: white;
padding: 14px;
letter-spacing:3px;
width:120px;
text-align: center;
text-decoration: none;
display:block;
margin:15px auto;
font-size: 16px;
}
</style>
</head>
<body>
<canvas id='canvas' width='510' height='310'>
</canvas>
<button id="bBtn">Play</button>
<script>
var ImagePainter = function(imageUrl){
this.image = new Image;
this.image.src = imageUrl;
};
ImagePainter.prototype = {
image: undefined,
paint: function(sprite, context){
if(this.image !== undefined){
if(!this.image.complete){
this.image.onload = function(e){
sprite.width = this.width;
sprite.height = this.height;
context.drawImage(this, // this is image
sprite.left, sprite.top,
sprite.width, sprite.height);
};
}
else{
context.drawImage(this.image, sprite.left, sprite.top,
sprite.width, sprite.height);
}
}
}
};
SpriteSheetPainter = function(cells) {
this.cells = cells;
};
SpriteSheetPainter.prototype = {
cells: [],
cellIndex: 0,
advance: function(){
if(this.cellIndex == this.cells.length-1){
this.cellIndex = 0;
}
else{
this.cellIndex++;
}
},
paint: function(sprite, context){
var cell = this.cells[this.cellIndex];
context.drawImage(spritesheet, cell.left, cell.top,
cell.width, cell.height,
sprite.left, sprite.top,
cell.width, cell.height);
}
};
var SpriteAnimator = function(painters, elapsedCallback){
this.painters = painters;
if(elapsedCallback){
this.elapsedCallback = elapsedCallback;
}
};
SpriteAnimator.prototype = {
painters: [],
duration: 1000,
startTime: 0,
index: 0,
elapsedCallback: undefined,
end: function(sprite, originalPainter){
sprite.animating = false;
if(this.elapsedCallback){
this.elapsedCallback(sprite);
}
else{
sprite.painter = originalPainter;
}
},
start: function(sprite, duration){
var endTime = +new Date() + duration,
period = duration / (this.painters.length),
interval = undefined,
animator = this, // for setInterval() function
originalPainter = sprite.painter;
this.index = 0;
sprite.animating = true;
sprite.painter = this.painters[this.index];
interval = setInterval(function(){
if(+new Date() < endTime){
sprite.painter = animator.painters[++animator.index];
}
else {
animator.end(sprite, originalPainter);
clearInterval(interval);
}
}, period);
},
};
var Sprite = function(name, painter, behaviors){
if(name !== undefined) this.name = name;
if(painter !== undefined) this.painter = painter;
if(behaviors !== undefined) this.behaviors = behaviors;
return this;
};
Sprite.prototype = {
left: 0,
top: 0,
width: 10,
height: 10,
velocityX: 0,
velocityY: 0,
visible: true,
animating: false,
painter: undefined, // object with paint(sprite, context)
behaviors: [], // objects with execute(sprite, context, time)
paint: function(context){
if(this.painter !== undefined && this.visible){
this.painter.paint(this, context);
}
},
update: function(context, time){
for (var i = this.behaviors.length; i > 0; --i){
this.behaviors[i-1].execute(this, context, time);
}
}
};
</script>
<script>
window.requestNextAnimationFrame =
(function(){
var originalWebkitRequestAnimationFrame = undefined,
wrapper = undefined,
callback = undefined,
geckoVersion = 0,
userAgent = navigator.userAgent,
index = 0,
self = this;
if(window.webkitRequestAnimationFrame){
// Define the wrapper
wrapper = function(time){
if(time === undefined){
time = +new Date();
}
self.callback(time);
};
// Make the switch
originalWebkitRequestAnimationFrame = window.webkitRequestAnimationFrame;
window.webkitRequestAnimationFrame = function(callback, element){
self.callback = callback;
// Browser calls the wrapper and wrapper calls the callback
originalWebkitRequestAnimationFrame(wrapper, element);
}
}
// Workaround for Gecko 2.0, which has a bug in
// mozRequestAnimationFrame() that restricts animations
// to 30-40 fps.
if(window.mozRequestAnimationFrame){
// Check the Gecko version. Gecko is used by browsers
// other than Firefox. Gecko 2.0 corresponds to
// Firefox 4.0.
index = userAgent.indexOf('rv:');
if(userAgent.indexOf('Gecko') != -1){
geckoVersion = userAgent.substr(index + 3, 3);
if(geckoVersion === '2.0'){
// Forces the return statement to fall through
// to the setTimeout() function.
window.mozRequestAnimationFrame = undefined;
}
}
}
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback, element){
var start,
finish;
window.setTimeout( function(){
start = +new Date();
callback(start);
finish = +new Date();
self.timeout = 1000 / 60 - (finish - start);
}, self.timeout);
};
}
)();
</script>
<script>
var canvas = document.getElementById('canvas'),
context = canvas.getContext('2d'),
ball = new Sprite('ball',
{
paint: function(sprite, context){
context.save();
context.strokeStyle = 'blue';
context.fillStyle = 'yellow';
context.beginPath();
context.arc(sprite.left + sprite.width/2,
sprite.top + sprite.height/2,
10, 0, Math.PI*2, false);
context.stroke();
context.fill();
context.restore();
}
}),
ballMoving = false,
lastTime = undefined,
velocityX = 350,
velocityY = 190;
// Functions.....................................................
function windowToCanvas(e){
var x = e.x || e.clientX,
y = e.y || e.clientY,
bbox = canvas.getBoundingClientRect();
return { x: x - bbox.left * (canvas.width / bbox.width),
y: y - bbox.top * (canvas.height / bbox.height)
};
};
function getBoundingBox(ball){
return { left: ball.left,
top: ball.top,
width: ball.width,
height: ball.height
};
}
function handleEdgeCollisions(){
var bbox = getBoundingBox(ball),
right = bbox.left + bbox.width,
bottom = bbox.top + bbox.height;
if(right > canvas.width || bbox.left < 0){
velocityX = -velocityX;
if(right > canvas.width){
ball.left -= right-canvas.width;
}
if(bbox.left < 0){
ball.left -= bbox.left;
}
}
if(bottom > canvas.height || bbox.top < 0){
velocityY = -velocityY;
if(bottom > canvas.height){
ball.top -= bottom-canvas.height;
}
if(bbox.top < 0){
ball.top -= bbox.top;
}
}
};
function detectCollisions(){
if(ballMoving){
handleEdgeCollisions();
}
};
function isPointInBall(x, y){
return x > ball.left && x < ball.left + ball.width &&
y > ball.top && y < ball.top + ball.height;
}
// Event Handlers................................................
$("#bBtn").click(function(e){
if(ballMoving){
$("#bBtn").text("Play");
}
else{
$("#bBtn").text("Pause");
}
var location = windowToCanvas(e);
ballMoving = !ballMoving;
});
// Animation.....................................................
function animate(time){
var elapsedTime;
if(lastTime === 0){
lastTime = time;
}
else{
context.clearRect(0,0,canvas.width,canvas.height);
if(ballMoving){
elapsedTime = parseFloat(time - lastTime) / 1000;
ball.left += velocityX * elapsedTime;
ball.top += velocityY * elapsedTime;
detectCollisions();
}
lastTime = time;
ball.paint(context);
}
window.requestNextAnimationFrame(animate);
};
// Initialization................................................
ball.fillStyle = 'rgba(255,255,0,1.0)';
ball.left = 100;
ball.top = 100;
context.shadowColor = 'rgba(100,140,255,0.5)';
context.shadowBlur = 4;
context.shadowOffsetX = 2;
context.shadowOffsetY = 2;
context.font = '38px Arial';
window.requestNextAnimationFrame(animate);
</script> </body>
</html>