![模拟可撕裂布料效果的演示_图一 模拟可撕裂布料效果的演示_图一](http://pic.erlangyun.com/assets/attachment/product/201909/22/276_1569125713LUxm.jpg)
以前在前端网收藏的一个好玩特效分享给大家
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>模拟可撕裂布料效果</title> <style> * { margin: 0; overflow: hidden; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; -o-user-select: none; user-select: none; } body { background: #F2F2F2; } #c { display: block; margin: 20px auto 0; } #info { position: absolute; left: -1px; top: -1px; width: auto; max-width: 420px; height: auto; background: #f2f2f2; border-bottom-right-radius: 10px; border: 1px solid #333; } #top { background: #fff; width: 100%; height: auto; position: relative; border-bottom: 1px solid #eee; } p { font-family: Arial, sans-serif; color: #666; text-align: justify; font-size: 16px; margin: 10px 16px; } #github { color: #3377ee; font-family: Helvetica, Arial, sans-serif; font-size: 19px; display: block; margin: 0 auto; text-align: center; text-decoration: none; } #net { text-align: center; white-space: nowrap; font-size: 19px; background: rgba(0, 0, 0, 0.1); padding: 8px 12px; border-radius: 8px; display: block; color: #888; } #net>span { color: #3377ee; font-family: Helvetica, Arial, sans-serif; font-size: 14px; display: block; margin: 0 auto; text-align: center; text-decoration: none; } a { font-family: sans-serif; color: #444; text-decoration: none; font-size: 20px; } #site { float: left; margin: 10px; color: #ff9900; border-bottom: 1px dashed #ccc; padding-bottom: 3px } #site:hover { color: #ffaa11; } #close { float: right; margin: 10px; } #p { font-family: Verdana, sans-serif; position: absolute; right: 10px; bottom: 10px; color: #4099ff; border: 1px dashed #4099ff; padding: 4px 8px; } #irc { display: block; text-align: center; margin-top: 4px; color: #333; } #irc a { color: #3377ee; font-size: 17px } #irc-info { display: block; text-align: center; font-size: 14px; margin-top: -10px; color: #828282; margin-bottom: 4px } #new { width: 100%; text-align: center; font-size: 18px; font-family: Helvetica; } #new a { font-size: 18px; color: #4099ff; } </style> </head> <body> <canvas id="c"></canvas> <script> var physics_accuracy = 3, mouse_influence = 20, mouse_cut = 5, gravity = 1200, cloth_height = 30, cloth_width = 50, start_y = 20, spacing = 7, tear_distance = 60; window.requestAnimFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60); }; var canvas, ctx, cloth, boundsx, boundsy, mouse = { down : false, button : 1, x : 0, y : 0, px : 0, py : 0 }; var Point = function(x, y) { this.x = x; this.y = y; this.px = x; this.py = y; this.vx = 0; this.vy = 0; this.pin_x = null; this.pin_y = null; this.constraints = []; }; Point.prototype.update = function(delta) { if (mouse.down) { var diff_x = this.x - mouse.x, diff_y = this.y - mouse.y, dist = Math .sqrt(diff_x * diff_x + diff_y * diff_y); if (mouse.button == 1) { if (dist < mouse_influence) { this.px = this.x - (mouse.x - mouse.px) * 1.8; this.py = this.y - (mouse.y - mouse.py) * 1.8; } } else if (dist < mouse_cut) this.constraints = []; } this.add_force(0, gravity); delta *= delta; nx = this.x + ((this.x - this.px) * .99) + ((this.vx / 2) * delta); ny = this.y + ((this.y - this.py) * .99) + ((this.vy / 2) * delta); this.px = this.x; this.py = this.y; this.x = nx; this.y = ny; this.vy = this.vx = 0 }; Point.prototype.draw = function() { if (!this.constraints.length) return; var i = this.constraints.length; while (i--) this.constraints[i].draw(); }; Point.prototype.resolve_constraints = function() { if (this.pin_x != null && this.pin_y != null) { this.x = this.pin_x; this.y = this.pin_y; return; } var i = this.constraints.length; while (i--) this.constraints[i].resolve(); this.x > boundsx ? this.x = 2 * boundsx - this.x : 1 > this.x && (this.x = 2 - this.x); this.y < 1 ? this.y = 2 - this.y : this.y > boundsy && (this.y = 2 * boundsy - this.y); }; Point.prototype.attach = function(point) { this.constraints.push(new Constraint(this, point)); }; Point.prototype.remove_constraint = function(constraint) { this.constraints.splice(this.constraints.indexOf(constraint), 1); }; Point.prototype.add_force = function(x, y) { this.vx += x; this.vy += y; }; Point.prototype.pin = function(pinx, piny) { this.pin_x = pinx; this.pin_y = piny; }; var Constraint = function(p1, p2) { this.p1 = p1; this.p2 = p2; this.length = spacing; }; Constraint.prototype.resolve = function() { var diff_x = this.p1.x - this.p2.x, diff_y = this.p1.y - this.p2.y, dist = Math .sqrt(diff_x * diff_x + diff_y * diff_y), diff = (this.length - dist) / dist; if (dist > tear_distance) this.p1.remove_constraint(this); var px = diff_x * diff * 0.5; var py = diff_y * diff * 0.5; this.p1.x += px; this.p1.y += py; this.p2.x -= px; this.p2.y -= py; }; Constraint.prototype.draw = function() { ctx.moveTo(this.p1.x, this.p1.y); ctx.lineTo(this.p2.x, this.p2.y); }; var Cloth = function() { this.points = []; var start_x = canvas.width / 2 - cloth_width * spacing / 2; for (var y = 0; y <= cloth_height; y++) { for (var x = 0; x <= cloth_width; x++) { var p = new Point(start_x + x * spacing, start_y + y * spacing); x != 0 && p.attach(this.points[this.points.length - 1]); y == 0 && p.pin(p.x, p.y); y != 0 && p.attach(this.points[x + (y - 1) * (cloth_width + 1)]) this.points.push(p); } } }; Cloth.prototype.update = function() { var i = physics_accuracy; while (i--) { var p = this.points.length; while (p--) this.points[p].resolve_constraints(); } i = this.points.length; while (i--) this.points[i].update(.016); }; Cloth.prototype.draw = function() { ctx.beginPath(); var i = cloth.points.length; while (i--) cloth.points[i].draw(); ctx.stroke(); }; function update() { ctx.clearRect(0, 0, canvas.width, canvas.height); cloth.update(); cloth.draw(); requestAnimFrame(update); } function start() { canvas.onmousedown = function(e) { mouse.button = e.which; mouse.px = mouse.x; mouse.py = mouse.y; var rect = canvas.getBoundingClientRect(); mouse.x = e.clientX - rect.left, mouse.y = e.clientY - rect.top, mouse.down = true; e.preventDefault(); }; canvas.onmouseup = function(e) { mouse.down = false; e.preventDefault(); }; canvas.onmousemove = function(e) { mouse.px = mouse.x; mouse.py = mouse.y; var rect = canvas.getBoundingClientRect(); mouse.x = e.clientX - rect.left, mouse.y = e.clientY - rect.top, e.preventDefault(); }; canvas.oncontextmenu = function(e) { e.preventDefault(); }; boundsx = canvas.width - 1; boundsy = canvas.height - 1; ctx.strokeStyle = '#888'; cloth = new Cloth(); update(); } window.onload = function() { canvas = document.getElementById('c'); ctx = canvas.getContext('2d'); canvas.width = 560; canvas.height = 350; start(); }; </script> </body> </html>