canvas实现鼠标悬浮拖动铅球重力感应效果代码

代码语言:html

所属分类:动画

代码描述:canvas实现鼠标悬浮拖动铅球重力感应效果代码

代码标签: canvas 鼠标 悬浮 拖动 铅球 重力 感应 代码

下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开

<!DOCTYPE html>
<html lang="en" >
<head>
  <meta charset="UTF-8">
<style>
    body, html {
	position: absolute;
	margin: 0;
	padding: 0;
	width: 100%;
	height: 100%;
	overflow: hidden;
}

canvas {
	position: absolute;
	width: 100%;
	height: 100%;
	cursor: none;
}
</style>

</head>
<body>

  <script >
      {
	// Code never lies, comments sometimes do.
	const Human = class {
		constructor(size, gravity, x, y, struct) {
			this.x = x;
			this.y = y;
			this.points = [];
			this.constraints = [];
			this.angles = [];
			this.shapes = [];
			// Points
			for (const point of struct.points) {
				this.points.push(
					new Human.Point(canvas.width * x, canvas.height * y, point, size, gravity)
				);
			}
			// constraints and shapes
			for (const constraint of struct.constraints) {
				const p0 = this.points[constraint.p0];
				const p1 = this.points[constraint.p1];
				this.constraints.push(new Human.Constraint(p0, p1, constraint));
				if (constraint.svg) {
					this.shapes.push(
						new Human.Shape(p0, p1, constraint, struct.svg[constraint.svg], size)
					);
				}
			}
			// angle constraints
			for (const angle of struct.angles) {
				this.angles.push(
					new Human.Angle(
						this.points[angle.p0],
						this.points[angle.p1],
						this.points[angle.p2],
						angle
					)
				);
			}
		}
		anim() {
			for (const point of this.points) point.integrate();
			for (let i = 0; i < 5; ++i) {
				for (const angle of this.angles) angle.update();
				for (const constraint of this.constraints) constraint.update();
			}
			
			for (const point of this.points) point.collide(ball);
		}
		draw() {
			for (const shape of this.shapes) shape.draw();
		}
	};
	// Points
	Human.Point = class {
		constructor(x, y, p, s, g) {
			this.x = x + p.x * s;
			this.y = y + p.y * s;
			this.px = this.x;
			this.py = this.y;
			this.vx = 0.0;
			this.vy = 0.0;
			this.m = p.m || 1.0;
			this.g = g;
		}
		join(p1, distance, force) {
			const dx = p1.x - this.x;
			const dy = p1.y - this.y;
			const dist = Math.sqrt(dx * dx + dy * dy);
			const tw = this.m + p1.m;
			const r1 = p1.m / tw;
			const r0 = this.m / tw;
			const dz = (distance - dist) * force;
			const sx = dx / dist * dz;
			const sy = dy / dist * dz;
			p1.x += sx * r0;
			p1.y += sy * r0;
			this.x -= sx * r1;
			this.y -= sy * r1;
		}
		dist(p1) {
			const dx = this.x - p1.x;
			const dy = this.y - p1.y;
			return Math.sqrt(dx * dx + dy * dy);
		}
		integrate() {
			// verlet integration
			this.vx = this.x - this.px;
			this.vy = this.y - this.py;
			this.px = this.x;
			this.py = this.y;
			this.x += this.vx;
			this.y += this.vy + this.g;
		}
		collide(ball) {
			// collision with ball
			const dx = this.x - ball.x;
			const dy = this.y - ball.y;
			const sd = dx * dx + dy * dy;
			if (sd < ball.radius * ball.radius) {
				const d = Math.sqrt(sd);
				const dz = (ball.radius - d) * 0.5;
				this.x += dx / d * dz;
				this.y += dy / d * dz;
			}
		}
	};
	// Shapes
	Human.Shape = class {
		constructor(p0, p1, shape, src, size) {
			this.p0 = p0;
			this.p1 = p1;
			this.width = shape.w * size;
			this.height = shape.h * size;
			this.shape = canvas.createImage(
				shape.svg,
				"data:image/svg+xml;base64," + window.btoa(src)
			);
			this.offset = shape.offset;
		}
		draw() {
			canvas.drawImage(
				this.shape,
				this.p0.x,
				this.p0.y,
				this.height + this.width * this.offset,
				this.width,
				-this.height * this.offset,
				-this.width * 0.5,
				Math.atan2(this.p1.y - this.p0.y, this.p1.x - this.p0.x)
			);
		}
	};
	// Constraints
	Human.Constraint = class {
		constructor(p0, p1, constraint) {
			this.p0 = p0;
			this.p1 = p1;
			this.distance = p0.dist(p1);
			this.force = constraint.force || 1.0;
		}
		update() {
			this.p0.join(this.p1, this.distance, this.force);
		}
	};
	// Angles constraints
	Human.Angle = class {
		constructor(p0, p1, p2, constraint) {
			this.p0 = p0;
			this.p1 = p1;
			this.p2 = p2;
			this.len1 = p0.dist(p1);
			this.len2 = p1.dist(p2);
			this.angle = constraint.angle;
			this.range = constraint.range;
			this.force = constraint.force || 0.1;
			let m = p0.m + p1.m;
			this.m1 = p0.m / m;
			this.m2 = p1.m / m;
			m = p1.m + p2.m;
			this.m3 = p1.m / m;
			this.m4 = p2.m / m;
		}
		a12(p0, p1, p2) {
			const a = Math.atan2(p1.y - p0.y, p1.x - p0.x);
			const b = Math.atan2(p2.y - p1.y, p2.x - p1.x);
			const c = this.angle - (b - a);
			const d = c > Math.PI ? c - 2 * Math.PI : c < -Math.PI ? c + 2 * Math.PI : c;
			const e = Math.abs(d) > this.range
				? (-Math.sign(d) * this.range + d) * this.force
				: 0;
			const cos = Math.cos(a - e);
			const sin = Math.sin(a - e);
			const x1 = p0.x + (p1.x - p0.x) * this.m2;
			const y1 = p0.y + (p1.y - p0.y) * this.m2;
			p0.x = x1 - cos * this.len1 * this.m2;
			p0.y = y1 - sin * this.len1 * this.m2;
			p1.x = x1 + cos * this.len1 * this.m1;
			p1.y = y1 + sin * this.len1 * this.m1;
			return e;
		}
		a23(e, p1, p2) {
			const a = Math.atan2(p1.y - p2.y, p1.x - p2.x) + e;
			const cos = Math.cos(a);
			const sin = Math.sin(a);
			const x1 = p2.x + (p1.x - p2.x) * this.m3;
			const y1 = p2.y + (p1.y - p2.y) * this.m3;
			p2.x = x1 - cos * this.len2 * this.m3;
			p2.y = y1 - sin * this.len2 * this.m3;
			p1.x = x1 + cos * this.len2 * this.m4;
			p1.y = y1 + sin * this.len2 * this.m4;
		}
		update() {
			// resolve angular constraints
			const e = this.a12(this.p0, this.p1, this.p2);
			this.a23(e, this.p1, this.p2);
		}
	};
	const canvas = {
		// @greggman thanks for webglfundamentals!
		// https://webglfundamentals.org/webgl/lessons/webgl-2d-drawimage.html
		init() {
			// create webGL canvas context
			this.elem = document.createElement("canvas");
			document.body.appendChild(this.elem);
			const options = {
				alpha: false,
				stencil: false,
				antialias: false,
				depth: false
			};
			let gl = this.elem.getContext("webgl", options);
			if (!gl) gl = this.elem.getContext("experimental-webgl", options);
			if (!gl) return false;
			this.gl = gl;
			// set shaders
			this.vertexShader = gl.createShader(gl.VERTEX_SHADER);
			gl.shaderSource(
				this.vertexShader,
				`
				precision highp float;
        attribute vec2 aPosition;
        uniform mat3 uMatrix;
        varying vec2 vTexcoord;
        void main() {
					gl_Position = vec4(uMatrix * vec3(aPosition, 1), 1);
					vTexcoord = aPosition;
        }
      `
			);
			gl.compileShader(this.vertexShader);
			this.fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
			gl.shaderSource(
				this.fragmentShader,
				`
        precision highp float;
        varying vec2 vTexcoord;
        uniform sampler2D texture;
        void main() {
           gl_FragColor = texture2D(texture, vTexcoord);
        }
      `
			);
			// compile shaders
			gl.compileShader(this.fragmentShader);
			this.program = gl.createProgram();
			gl.attachShader(this.program, this.vertexShader);
			gl.attachShader(this.program, this.fragmentShader);
			gl.linkProgram(this.program);
			// init attributes, uniforms and buffers
			this.position = gl.getAttribLocation(this.program, "aPosition");
			gl.enableVertexAttribArray(this.position);
			this.umatrix = gl.getUniformLocation(this.program, "uMatrix");
			this.positionBuffer = gl.createBuffer();
			gl.bindBuffer(gl.ARRAY_BUFFER, this.positionBuffer);
			gl.bufferData(
				gl.ARRAY_BUFFER,
				new Float32Array([0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1]),
				gl.STATIC_DRAW
			);
			gl.vertexAttribPointer(this.position, 2, gl.FLOAT, false, 0, 0);
			// position/orientation/scaling 3x3 matrix
			this.matrix = new Float32Array(9);
			this.matrix[8] = 1;
			// textures
			this.textures = {};
			this.lastTexture = "";
			this.frame = 0;
			// source over blending
			gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
			gl.enable(gl.BLEND);
			gl.disable(gl.DEPTH_TEST);
			gl.useProgram(this.program);
			// resize event
			this.resize();
			window.addEventListener("resize", () => this.resize(), false);
			return gl;
		},
		Texture: class {
			constructor(id, src, loaded) {
				this.id = id;
				this.loaded = loaded;
				this.width = loaded ? src.width : 0;
				this.height = loaded ? src.height : 0;
				this.texture = loaded ? this.createTexture(src) : null;
				if (!loaded) {
					const source = new Image();
					source.onload = e => {
						this.loaded = true;
						this.width = source.width;
						this.height = source.height;
						this.texture = this.createTexture(source);
					};
					source.src = src;
				}
			}
			createTexture(source) {
				// create gpu texture
				const texture = gl.createTexture();
				gl.bindTexture(gl.TEXTURE_2D, texture);
				// assume non power of 2
				gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
				gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
				gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
				gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
				// upload texture to gpu
				gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
				return texture;
			}
		},
		createImage(id, src, loaded) {
			// create texture from src
			let tex = this.textures[id];
			if (tex) return tex;
			tex = this.textures[id] = new this.Texture(id, src, loaded);
			return tex;
		},
		drawImage(
			img,
			x,
			y,
			width = img.width,
			height = img.height,
			offsetX = 0,
			offsetY = 0,
			angle = 0
		) {
			if (!img.loaded) return;
			// update 4x4 matrix
			const m = this.matrix;
			const sx = 2 / this.width;
			const sy = -2 / this.height;
			if (angle) {
				const c = Math.cos(angle);
				const s = Math.sin(angle);
				m[0] = c * sx * width;
				m[1] = s * sy * width;
				m[3] = -s * sx * height;
				m[4] = c * sy * height;
				m[6] = (c * offsetX - s * offsetY + x) * sx - 1;
				m[7] = (s * offsetX + c * offsetY + y) * sy + 1;
			} else {
				m[0] = sx * width;
				m[1] = 0;
				m[3] = 0;
				m[4] = sy * height;
				m[6] = (offsetX + x) * sx - 1;
				m[7] = (offsetY + y) * sy + 1;
			}
			this.gl.uniformMatrix3fv(this.umatrix, false, m);
			// bind texture
			if (img.id !== this.lastTexture) {
				this.lastTexture = img.id;
				this.gl.bindTexture(this.gl.TEXTURE_2D, img.texture);
			}
			//draw the quad
			this.gl.drawArrays(this.gl.TRIANGLES, 0, 6);
		},
		resize() {
			this.width = this.elem.width = this.elem.offsetWidth;
			this.height = this.elem.height = this.elem.offsetHeight;
			// set viewport
			this.gl.viewport(
				0,
				0,
				this.gl.drawingBufferWidth,
				this.gl.drawingBufferHeight
			);
		}
	};
	const pointer = {
		init(canvas) {
			this.x = .........完整代码请登录后点击上方下载按钮下载查看

网友评论0