<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>HTML5 Canvas绘制的机器人蹒跚滑落动画</title>
<style>
html {
overflow: hidden;
touch-action: none;
content-zooming: none;
}
body {
position: absolute;
margin: 0;
padding: 0;
width: 100%;
height: 100%;
background: #000;
}
canvas {
position: absolute;
width: 100vh;
height: 100vh;
margin: auto;
top: 0;
bottom: 0;
left: 0;
right: 0;
cursor: pointer;
user-select: none;
}
</style>
</head>
<body>
<script>
! function(Math) {
"use strict";
// main loop
function run() {
requestAnimationFrame(run);
ctx.drawImage(background, 0, 0);
PHY2D.render();
ctx.drawImage(texture, 0, 0);
};
// init canvas
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
document.body.appendChild(canvas);
var resolutionX = canvas.width = 1000;
var resolutionY = canvas.height = 1000;
// background texture
var background = document.createElement('canvas');
background.width = resolutionX;
background.height = resolutionY;
var ict = background.getContext('2d');
ict.fillStyle = '#000';
ict.fillRect(0, 0, resolutionX, resolutionY);
ict.beginPath();
ict.arc(resolutionX * 0.5 - 260, 800, 140, 0, 2 * Math.PI);
ict.fillStyle = "#135";
ict.fill();
mountains(ict, "#223", 220);
mountains(ict, "#445", 180);
mountains(ict, "#667", 140);
mountains(ict, "#889", 100);
function mountains(ctx, color, h) {
var ht = resolutionY - (Math.random() * h + 20);
ctx.fillStyle = color;
ctx.beginPath();
ctx.moveTo(0, ht);
for (var i = 1; i <= resolutionX / 40 + 2; ++i) {
var r = Math.random(),
ychange;
if (r < 0.6) ychange = Math.random() * 30 + 5;
else if (r < 0.7) ychange = Math.random() * 20 + 5;
else ychange = -(Math.random() * 30 + 5);
ht += ychange;
if (ht > resolutionY - 10) ht = resolutionY - 10;
ctx.lineTo(i * 40, ht);
}
ctx.lineTo(resolutionX, ht);
ctx.lineTo(resolutionX, resolutionY);
ctx.lineTo(0, resolutionY);
ctx.closePath();
ctx.fill();
}
// tv texture
var texture = document.createElement('canvas');
texture.width = resolutionX;
texture.height = resolutionY;
ict = texture.getContext('2d');
for (var j = 0; j < resolutionY + 5; j += 5) {
ict.fillStyle = 'rgba(0,0,0,0.4)';
ict.fillRect(0, j, resolutionX, 2);
}
// init pointer
var pointer = {
x: resolutionX / 2,
y: resolutionY / 2,
isDown: false,
add: function(elem, events, fn) {
for (var i = 0, e = events.split(','); i < e.length; i++) {
elem.addEventListener(
e[i],
fn.bind(pointer),
false
);
}
},
pointer: function(e) {
var touchMode = e.targetTouches,
pointer;
if (touchMode) {
e.preventDefault();
pointer = touchMode[0];
} else pointer = e;
this.x = (-canvas.offsetLeft + pointer.clientX) * resolutionX / canvas.offsetWidth;
this.y = (-canvas.offsetTop + pointer.clientY) * resolutionY / canvas.offsetHeight;
}
};
pointer.add(window, 'mousemove,touchmove', function(e) {
this.pointer(e);
});
pointer.add(canvas, 'mousedown,touchstart', function(e) {
this.isDown = true;
this.pointer(e);
});
pointer.add(window, 'mouseup,touchend,touchcancel', function(e) {
this.isDown = false;
e.preventDefault();
});
// init physics engine
var PHY2D = {};
(function(Math, ctx, pointer, kGravity, kTimeStep) {
// Many thanks to Paul Firth for his physics engines tutorials !!
// Reference: http://www.wildbunny.co.uk/blog/tag/collision-detection/
var objects = [],
pairs = [],
manipulate = null;
//////// vectors ////////
function Vec(x, y) {
this.x = x || 0.0;
this.y = y || 0.0;
}
Vec.array = function(n, x, y) {
var array = new Array(n);
for (var i = 0; i < n; i++) {
array[i] = new Vec(
x && (x[i]),
y && (y[i])
);
}
return array;
};
Vec.transform = function(poly, v0, v1) {
var x = poly.pos.x,
y = poly.pos.y,
cos = poly.cos.x,
sin = poly.cos.y;
for (var i = 0; i < 4; i++) {
var v0i = v0[i],
v1i = v1[i];
v0i.x = cos * v1i.x - sin * v1i.y + x;
v0i.y = sin * v1i.x + cos * v1i.y + y;
}
};
Vec.rotate = function(poly, v0, v1) {
var cos = poly.cos.x,
sin = poly.cos.y;
for (var i = 0; i < 4; i++) {
var v0i = v0[i],
v1i = v1[i];
v0i.x = cos * v1i.x - sin * v1i.y;
v0i.y = sin * v1i.x + cos * v1i.y;
}
};
Vec.clamp = function(n, min, max) {
if (n > max) n = max;
else if (n < min) n = min;
return n;
};
Vec.prototype.set = function(x, y) {
this.x = x;
this.y = y;
return this;
};
Vec.prototype.copy = function(v) {
this.x = v.x;
this.y = v.y;
return this;
};
Vec.prototype.dot = function(v) {
return this.x * v.x + this.y * v.y;
};
Vec.prototype.sub = function(v1, v2) {
this.x = v1.x - v2.x;
this.y = v1.y - v2.y;
return this;
};
Vec.prototype.perp = function() {
var x = this.x;
this.x = -this.y;
this.y = x;
return this;
};
Vec.prototype.subScale = function(v1, v2, s) {
this.x = v1.x - (v2.x * s);
this.y = v1.y - (v2.y * s);
return this;
};
Vec.prototype.addScale = function(v1, v2, s) {
this.x = v1.x + (v2.x * s);
this.y = v1.y + (v2.y * s);
return this;
};
Vec.prototype.normal = function(a, b) {
var x = a.x - b.x,
y = a.y - b.y,
len = Math.sqrt(x * x + y * y);
this.x = -y / len;
this.y = x / len;
return this;
};
Vec.prototype.unit = function() {
var invLen = 1.0 / Math.sqrt(this.x * this.x + this.y * this.y);
this.x *= invLen;
this.y *= invLen;
return this;
};
Vec.prototype.dist = function(v) {
var dx = this.x - v.x,
dy = this.y - v.y;
return Math.sqrt(dx * dx + dy * dy);
};
Vec.prototype.len = function() {
return Math.sqrt(this.x * this.x + this.y * this.y);
};
Vec.prototype.projectPointOntoEdge = function(p, e0, e1) {