WEB骇客
<!--

─────█─▄▀█──█▀▄─█───── 
────▐▌──────────▐▌──── 
────█▌▀▄──▄▄──▄▀▐█──── 
───▐██──▀▀──▀▀──██▌─── 
──▄████▄──▐▌──▄████▄──

-->
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:100%;
	height:100%;
	background: #c9c4b5;
}
.draggable {
		cursor: pointer;
		cursor: -webkit-grab;
}
.dragging {
		cursor: move;
		cursor: -webkit-grabbing; 
}
;(function() {

	'use strict';
	
	// variables
	var DEBUG       = false;
	var points      = null;
	var constraints = null;
	var skins       = null;
	var kGravity    = 0.5;
	var kFriction   = 0.98;
	var base        = 'https://www.webhek.com/demos/codepens/222599/';

	Math.sign = Math.sign || function(x) {
		return x > 0 ? 1 : -1;
	};

	// main loop

	function run() {

		requestAnimationFrame(run);
		ctx.clearRect(0, 0, canvas.width, canvas.height);

		// dragging

		pointer.dragging();

		// static points

		scene.static();

		// draw scene

		ctx.save();
		var s = skins;
		while (s) s = s.draw();
		ctx.restore();

		// verlet integration

		var p = points;
		while (p) p = p.integrate();

		// cursor

		pointer.cursor();
		
		// solve constraints

		var c = constraints;
		while (c) c = c.solve();

		// show constraints and points

		if (DEBUG) {
			c = constraints;
			while (c) c = c.draw();
		}

	}

	// point 2D constructor

	function Point (x, y, radius, mass, gravity) {

		this.x        = canvas.width * 0.5 + x;
		this.y        = y;
		this.oldX     = this.x;
		this.oldY     = y;
		this.radius   = radius || 1;
		this.mass     = mass || 1.0;
		this.friction = kFriction;
		this.gravity  = gravity || kGravity;
		this.next     = null;
		points        = iterator.link (points, this);

	}

	// set position

	Point.prototype.position = function (x, y) {

		this.x = x;
		this.y = y;

	};

	// verlet integration
	
	Point.prototype.integrate = function () {

		var x       = this.x;
		var y       = this.y;
		this.x     += (this.x - this.oldX) * this.friction;
		this.y     += (this.y - this.oldY) * this.friction + this.gravity;
		this.oldX   = x;
		this.oldY   = y;

		// bottom + friction

		if (this.y > canvas.height - this.radius) {
			this.x = x;
			this.y = canvas.height - this.radius;
		}

		// cursor style
		
		if (!pointer.draggable) {
			var dx = this.x - pointer.x;
			var dy = this.y - pointer.y;
			if (Math.sqrt(dx * dx + dy * dy) < this.radius * 2) pointer.draggable = true;
		}
		
		return this.next;

	};

	// calculate distance between 2 points

	Point.prototype.dist = function (p) {

		var dx = this.x - p.x;
		var dy = this.y - p.y;
		return Math.sqrt(dx * dx + dy * dy);

	};

	// Angled Constraint constructor

	function AngleConstraint (p1, p2, p3, angle, range, force) {

		this.p1     = p1;
		this.p2     = p2;
		this.p3     = p3;
		this.len1   = p1.dist(p2);
		this.len2   = p2.dist(p3);
		this.angle  = angle;
		this.range  = range;
		this.force  = force || 0.2;
		constraints = iterator.link (constraints, this);

    }

    // solve 2 vectors angled (+ stick) constraint
    // https://stackoverflow.com/questions/16336702/ragdoll-joint-angle-constraints

	AngleConstraint.prototype.solve = function () {

		var a, b, c, e, m, m1, m2, m3, x1, y1, cos, sin;
		a         = Math.atan2(this.p2.y - this.p1.y, this.p2.x - this.p1.x);
		b         = Math.atan2(this.p3.y - this.p2.y, this.p3.x - this.p2.x);
		c         = this.angle - (b - a);
		c         = c > Math.PI ? (c - 2 * Math.PI) : (c < -Math.PI ? (c + 2 * Math.PI) : c);
		e         = (Math.abs(c) > this.range) ? (-Math.sign(c) * this.range + c) * this.force : 0;
		m         = this.p1.mass + this.p2.mass;
		m1        = this.p1.mass / m;
		m2        = this.p2.mass / m;
		cos       = Math.cos(a - e);
		sin       = Math.sin(a - e);
		x1        = this.p1.x + (this.p2.x - this.p1.x) * m2;
		y1        = this.p1.y + (this.p2.y - this.p1.y) * m2;
		this.p1.x = x1 - cos * this.len1 * m2;
		this.p1.y = y1 - sin * this.len1 * m2;
		this.p2.x = x1 + cos * this.len1 * m1;
		this.p2.y = y1 + sin * this.len1 * m1;
		a         = Math.atan2(this.p2.y - this.p3.y, this.p2.x - this.p3.x) + e;
		m         = this.p2.mass + this.p3.mass;
		m2        = this.p2.mass / m;
		m3        = this.p3.mass / m;
		cos       = Math.cos(a);
		sin       = Math.sin(a);
		x1        = this.p3.x + (this.p2.x - this.p3.x) * m2;
		y1        = this.p3.y + (this.p2.y - this.p3.y) * m2;
		this.p3.x = x1 - cos * this.len2 * m2;
		this.p3.y = y1 - sin * this.len2 * m2;
		this.p2.x = x1 + cos * this.len2 * m3;
		this.p2.y = y1 + sin * this.len2 * m3;
		return this.next;

	};

	// draw angle constraint (DEBUG mode)

	AngleConstraint.prototype.draw = function () {

		ctx.beginPath();
		ctx.moveTo (this.p1.x, this.p1.y);
		ctx.lineTo(this.p2.x, this.p2.y);
		ctx.lineTo(this.p3.x, this.p3.y);
		ctx.stroke();
		ctx.beginPath();
		ctx.arc(this.p1.x, this.p1.y, this.p1.radius * 2, 0, Math.PI * 2);
		ctx.stroke();
		ctx.beginPath();
		ctx.arc(this.p2.x, this.p2.y, this.p2.radius * 2, 0, Math.PI * 2);
		ctx.stroke();
		ctx.beginPath();
		ctx.arc(this.p3.x, this.p3.y, this.p3.radius * 2, 0, Math.PI * 2);
		ctx.stroke();
		return this.next;

	};

	// simple stick constraint constructor

	function Constraint (p1, p2, force, len) {

		this.p1     = p1;
		this.p2     = p2;
		this.len    = len || p1.dist(p2);
		this.force  = force || 2;
		constraints = iterator.link (constraints, this);

	}

	// solve stick constraint

	Constraint.prototype.solve = function () {

		var d, dx, dy, s1, s2, tm;
		dx = this.p1.x - this.p2.x;
		dy = this.p1.y - this.p2.y;
		d  = Math.sqrt(dx * dx + dy * dy);
		tm = this.p1.mass + this.p2.mass;
		d  = (d - (d + (this.len - d) * this.force)) / d * 0.5;
		s1 = d * (this.p1.mass / tm);
		s2 = d * (this.p2.mass / tm);
		this.p1.x = this.p1.x - dx * s2;
		this.p1.y = this.p1.y - dy * s2;
		this.p2.x = this.p2.x + dx * s1;
		this.p2.y = this.p2.y + dy * s1;
		return this.next;

	};

	// draw constraint (DEBUG mode)

	Constraint.prototype.draw = function () {

		ctx.beginPath();
		ctx.moveTo (this.p1.x, this.p1.y);
		ctx.lineTo(this.p2.x, this.p2.y);
		ctx.stroke();
		ctx.beginPath();
		ctx.arc(this.p1.x, this.p1.y, this.p1.radius * 2, 0, Math.PI * 2);
		ctx.stroke();
		ctx.beginPath();
		ctx.arc(this.p2.x, this.p2.y, this.p2.radius * 2, 0, Math.PI * 2);
		ctx.stroke();
		return this.next;

	};

	// skin constructor

	function Skin (img, p1, p2, offsetX, offsetY, width, height, angle) {

		this.p1      = p1;
		this.p2      = p2;
		this.img     = new Image();
		this.img.src = base + img;
		this.ox      = offsetX;
		this.oy      = offsetY;
		this.w       = width;
		this.h       = height;
		this.angle   = angle || 0;
		this.ctx     = ctx;
		skins        = iterator.link(skins, this);

	}

	// draw skin

	Skin.prototype.draw = function () {

		var a   = Math.atan2((this.p2.y - this.p1.y), (this.p2.x - this.p1.x));
		var cos = Math.cos(a + this.angle);
		var sin = Math.sin(a + this.angle);
		this.ctx.setTransform(cos, sin, -sin, cos, this.p1.x, this.p1.y);
		this.ctx.drawImage(this.img, -this.ox, -this.oy, this.w,	this.h);
		return this.next;

	};

	// Stroke constructor

	function Stroke (color, p1, p2) {

		this.p1 = p1;
		this.p2 = p2;
		skins   = iterator.link(skins, this);

	}

	// draw stroke

	Stroke.prototype.draw = function () {

		ctx.beginPath();
		ctx.strokeStyle = this.color;
		ctx.moveTo(this.p1.x, this.p1.y);
		ctx.lineTo(this.p2.x, this.p2.y);
		ctx.stroke();
		return this.next;

	};

	// set canvas

	var canvas = {  
		width:  0, 
		height: 0,
		elem: document.createElement('canvas'),
		resize: function () {
			this.width  = this.elem.width  = this.elem.offsetWidth;
			this.height = this.elem.height = this.elem.offsetHeight;
		},
		init: function () {
			var ctx = canvas.elem.getContext('2d');
			document.body.appendChild(canvas.elem);
			window.addEventListener('resize', canvas.resize.bind(canvas), false);
			return ctx;
		}
	};

	var ctx = canvas.init();

	// iterator

	var iterator = {
		last: null,
		link: function (list, object) {
			if (!list) list = object;
			if (this.last) this.last.next = object;
			this.last = object;
			return list;
		}
	};

	// set pointer / touch

	var pointer = {
		x:  0, 
		y:  0,
		drag: null,
		draggable: false,
		cursor: function () {
			canvas.elem.className = this.drag ? 'dragging' : (this.draggable ? 'draggable' : 'default');
		},
		dragging: function () {
			this.draggable = false;
			if (pointer.drag) {
				this.drag.x += (this.x - this.drag.x) / 20;
				this.drag.y += (this.y - this.drag.y) / 20;
			}
		},
		pointer: function (e) {
			var pointer;
			if (e.targetTouches) {
				e.preventDefault();
				pointer = e.targetTouches[0]
			} else pointer = e;
			return pointer;
		},
		addEvents: function () {
			[
				[window, 'mousemove,touchmove', function (e) {
					var pointer = this.pointer(e);
					this.x = pointer.clientX;
					this.y = pointer.clientY;
				}],
				[canvas.elem, 'mousedown,touchstart', function (e) {
					if (!this.drag) {
						var pointer = this.pointer(e);
						this.x = pointer.clientX;
						this.y = pointer.clientY;
						var dm = 9999, p = points;
						while (p) {
							var dx = p.x - this.x;
							var dy = p.y - this.y;
							var d = Math.sqrt(dx * dx + dy * dy);
							if (d < p.radius * 2) {
								if (d < dm) {
									dm = d;
									this.drag = p;
								}
							}
							p = p.next;
						}
					}
				}],
				[window, 'mouseup,touchend,touchcancel', function () {
					this.drag = null;
				}]
			].forEach(function (e) {
				for (var i = 0, events = e[1].split(','); i < events.length; i++) {
					e[0].addEventListener(events[i], e[2].bind(this), false );
				}

			}.bind(this));
		}
	};

	// add events

	pointer.addEvents();
	
	// resize

	canvas.resize();

	// scene definition

	var scene = {
		points: {
			p0: [-70, -80, 20, 1],
			p1: [0  , -200, 40, 1],
			p2: [   0,  100, 20, 1],
			p3: [-46,  100, 20, 1],
			p4: [ 75, -75, 20, 1],
			p5: [ 50,  104, 20, 1],
			p6: [ 50,  244, 20, 0.5],
			p7: [ -46, 244, 20, 0.5],
			p8: [ 50, 450, 15, 0.35],
			p9: [-46, 450, 15, 0.35],
			p10: [ -181, -80, 20, 0.5],
			p11: [ 187, -75, 20, 0.5],
			p12: [ -340, -80, 12, 0.35, -0.02],
			p13: [ 345, -75, 12, 0.35, -0.02],
			p14: [ 0, -80],
			p15: [ 0, -40],
			cloudLeft1: [ 0, 0, 0, 0],
			cloudLeft2: [ 0, 0, 0, 0],
			cloudRight1: [ 0, 0, 0, 0],
			cloudRight2: [ 0, 0, 0, 0],
			mong10: [0, -50 - canvas.height / 10],
			mong11: [0, 20, 30],
			mong12: [0, 160, 30],
			mong20: [0, -50 - canvas.height / 10],
			mong21: [0, 20, 30],
			mong22: [0, 160, 30],
			mong30: [0, -150, 0, 1, 0],
			mong31: [0, canvas.height / 2 - 150, 0, 1, -0.25],
			mong32: [0, canvas.height / 2, 80, 1, -0.25]
		},
		static: function () {
			this.points.cloudLeft1.position(0, 0);
			this.points.cloudLeft2.position(100, 0);
			this.points.cloudRight1.position(canvas.width - 183, 0);
			this.points.cloudRight2.position(canvas.width, 0);
			this.points.mong10.position(canvas.width * 0.5 -200, -100);
			this.points.mong20.position(canvas.width * 0.5 + 200, -100);
			this.points.mong30.position(300, canvas.height + 100);
		},
		constraints: [
			['p0','p1'],
			['p1','p2'],
			['p2','p3'],
			['p0','p2'],
			['p1','p3'],
			['p1','p4'],
			['p5','p2'],
			['p1','p5'],
			['p2','p4'],
			['p0','p4'],
			['p3','p5'],
			['p3','p4'],
			['p0','p10'],
			['p4','p11'],
			['p10','p12'],
			['p11','p13'],
			['p14','p3'],
			['p14','p0'],
			['p14','p4'],
			['p14','p5'],
			['p14','p15'],
			['p14','p1'],
			['p14','p2'],
			['mong10','mong11'],
			['mong11','mong12'],
			['mong20','mong21'],
			['mong21','mong22'],
			['mong30','mong31'],
			['mong31','mong32'],
			['mong12','p14', 0, canvas.height / 3],
			['mong22','p14', 0, canvas.height / 3]
		],
		angleConstraints: [
			['p0', 'p3', 'p7',  1.2, Math.PI / 1.8, 0.2],
			['p4', 'p5', 'p6', -1.2, Math.PI / 1.8, 0.2],
			['p3', 'p7', 'p9', -1.2, Math.PI / 1.8, 0.2],
			['p5', 'p6', 'p8',  1.2, Math.PI / 1.8, 0.2]
		],
		strokes: [
			['#000', 'mong30', 'mong31'],
			['#000', 'mong10', 'mong11'],
			['#000', 'mong20', 'mong21'],
			['#000', 'mong12', 'p14'],
			['#000', 'mong22', 'p14']
		],
		// pics credits Lindsey Carr - https://www.pinterest.com/catheyhaynie/lindsey-carr/
		images: [
			['gd14.png', 'cloudLeft1', 'cloudLeft2', 0, 0, 195, 188],
			['gd15.png', 'cloudRight1', 'cloudRight2', 0, 0, 183, 188],
			['gd16.png', 'mong31', 'mong32', 305, 305 / 2, 325, 305, Math.PI],
			['gd20.png', 'mong11', 'mong12', 0, 102 / 2, 150, 102],
			['gd19.png', 'mong21', 'mong22', 0, 102 / 2, 144, 102],
			['gd6.png', 'p6', 'p8', 20, 50, 246, 84, 0.16],
			['gd12.png', 'p5', 'p6', 24, 35, 188, 71],
			['gd13.png', 'p7', 'p9', 20, 35, 246, 84, -0.16],
			['gd5.png', 'p3', 'p7', 24, 35, 189, 69],
			['gd1.png', 'p2', 'p1', 30, 181 / 2, 375, 181],
			['gd18.png', 'p14', 'p15', 5, 140 / 6, 157 / 2, 140 / 3],
			['gd2.png', 'p0', 'p10', 22, 26, 148, 49],
			['gd2.png', 'p4', 'p11', 22, 26, 148, 49],
			['gd3.png', 'p11', 'p13', 17, 22, 199, 43],
			['gd11.png', 'p10', 'p12', 17, 22, 199, 43]
		],
		build: function () {

			var p, i, c, o, s;
			iterator.last = null;
			for (p in scene.points) {
				o = scene.points[p];
				scene.points[p] = new Point (o[0], o[1], o[2], o[3], o[4]);
			}
			// build stick constraints
			iterator.last = null;
			for (i = 0; i < scene.constraints.length; i++) {
				c = scene.constraints[i];
				new Constraint (scene.points[c[0]], scene.points[c[1]], c[2], c[3]);
			}
			// build angle constraints
			for (i = 0; i < scene.angleConstraints.length; i++) {
				c = scene.angleConstraints[i];
				new AngleConstraint (scene.points[c[0]], scene.points[c[1]], scene.points[c[2]], c[3], c[4], c[5]);
			}
			// build strokes
			iterator.last = null;
			for (i = 0; i < scene.strokes.length; i++) {
				s = scene.strokes[i];
				new Stroke (s[0], scene.points[s[1]], scene.points[s[2]]);
			}
			// build images
			for (i = 0; i < scene.images.length; i++) {
				s = scene.images[i];
				new Skin (s[0], scene.points[s[1]], scene.points[s[2]], s[3], s[4], s[5], s[6], s[7]);
			}
		}

	};

	// build scene

	scene.build();

	// start animation

	run();

}());
返回