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