这是一款借助 Canvas 的强大绘图和动画功能模拟可撕裂布料的html5特效,完整源码下载链接: http://pan.baidu.com/s/1kVG0dfx 密码: 65d6
效果预览图:
index.html代码
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>HTML5 Canvas可撕裂布料效果</title> <link rel="stylesheet" type="text/css" href="style.css" /> <style> .top-banner { background-color: #666; } a { font-size: 13px; } #info { position: absolute; left: -1px; top: 30px; } #c { display: block; margin: 60px auto 0; } </style> </head> <body> <canvas id="c"></canvas> <div id="info"> <p> <br>- 使用鼠标可以拉扯 <br> <br>- 右键可以切断布料 <br> <br> </p> </div> <script type="text/javascript" src="Cloth.js"></script> </body> </html>
style.css代码:
* { 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: 380px; 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; } a { font-family: sans-serif; color: #444; text-decoration: none; font-size: 20px; } #site { float: left; margin: 10px; color: #38a; border-bottom: 1px dashed #888; } #site: hover { color: #7af; } #close { float: right; margin: 10px; } #p { font-family: Verdana, sans-serif; position: absolute; right: 10px; bottom: 10px; color: #167fc6; border: 1px dashed #555; padding: 4px 8px; }
Cloth.js代码
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 <= 0) 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(); if (this.x > boundsx) { this.x = 2 * boundsx - this.x; } else if (this.x < 1) { this.x = 2 - this.x; } if (this.y > boundsy) { this.y = 2 * boundsy - this.y; } else if (this.y < 1) { this.y = 2 - this.y; } }; Point.prototype.attach = function (point) { this.constraints.push( new Constraint(this, point) ); }; Point.prototype.remove_constraint = function (lnk) { var i = this.constraints.length; while (i--) if (this.constraints[i] == lnk) this.constraints.splice(i, 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(); };