|
|
Line 1: |
Line 1: |
- | <html lang="en"> | + | <html> |
- | <head>
| + | |
- | <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;
| + | |
- | }
| + | |
- | #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;
| + | |
- | }
| + | |
- | </style>
| + | |
- | </head>
| + | |
- | | + | |
| <body> | | <body> |
- | <canvas id="c"></canvas> | + | <p data-height="268" data-theme-id="0" data-slug-hash="KrAwx" data-default-tab="result" class='codepen'>See the Pen <a href='http://codepen.io/suffick/pen/KrAwx/'>Tearable Cloth</a> by suffick (<a href='http://codepen.io/suffick'>@suffick</a>) on <a href='http://codepen.io'>CodePen</a>.</p> |
- | <div id="info">
| + | |
- | <div id="top">
| + | |
- | <a id="site" href="http://www.lonely-pixel.com" target="_blank">my website</a>
| + | |
- | <a id="close" href="">×</a>
| + | |
- | </div>
| + | |
- | <p>
| + | |
- | <br>- Tear the cloth with your mouse.
| + | |
- | <br>
| + | |
- | <br>- Right click and drag to cut the cloth
| + | |
- | <br>
| + | |
- | <br>- Reduce physics_accuracy if it's laggy.
| + | |
- | <br>
| + | |
- | <br>
| + | |
- | <br><a id="github" target="_blank" href="http://github.com/suffick/Tearable-Cloth">View on GitHub</a>
| + | |
- | <br>
| + | |
- | </p>
| + | |
- | </div>
| + | |
| </body> | | </body> |
- | | + | <script async src="//codepen.io/assets/embed/ei.js"></script> |
- | <script> | + | |
- | document.getElementById('close').onmousedown = function(e) {
| + | |
- | e.preventDefault();
| + | |
- | document.getElementById('info').style.display = 'none';
| + | |
- | return false;
| + | |
- | };
| + | |
- | | + | |
- | var physics_accuracy = 3,
| + | |
- | mouse_influence = 20,
| + | |
- | mouse_cut = 5,
| + | |
- | gravity = 120,
| + | |
- | cloth_height = 30,
| + | |
- | cloth_width = 50,
| + | |
- | start_y = 20,
| + | |
- | spacing = 14,
| + | |
- | 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>
| + | |
- | <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
| + | |
| </html> | | </html> |