1. Awesome Demo of Cloth (fabric) using HTML5
3/25/2013 9:26:07 AM
Awesome Demo of Cloth (fabric) using HTML5
\news_old\files\Cloth-HTML5-20130325-092604.png
App Developer Magazine
Programming

Awesome Demo of Cloth (fabric) using HTML5


Monday, March 25, 2013

Richard Harris Richard Harris

The flexibly of HTML5 and the power of browser physics meet in this neat little demo on fabric which demonstrats how far along real-time simulations have come.


CSS Code:
* {
margin: 0;
  background:#333;
}

p {
  position:absolute;
  top:16px;
  width:auto;
  left:620px;
  color: #777;
  font-family:sans-serif;
  font-size:20px;
}


JS Code:

// settings

var physics_accuracy = 3,
mouse_influence   = 20, 
mouse_cut     = 6,
gravity         = 900, 
  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,
points,
physics,
mouse = {
down: false,
button: 1,
x: 0,
y: 0,
px: 0,
py: 0
};

window.onload = function() {

canvas = document.getElementById('c');
ctx    = canvas.getContext('2d');

canvas.width  = 600;//window.innerWidth;
canvas.height = 320;//window.innerHeight;

canvas.onmousedown = function(e) {
mouse.button = e.which;
mouse.px = mouse.x;
mouse.py = mouse.y;
mouse.x = e.clientX || e.layerX;
mouse.y = e.clientY || e.layerY;
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;
mouse.x = e.clientX || e.layerX;
mouse.y = e.clientY || e.layerY;
e.preventDefault();
};

canvas.oncontextmenu = function(e) {
e.preventDefault(); 
};

init();
};

var Constraint = function(p1, p2, spacing, tear_distance) {

this.p1 = p1;
this.p2 = p2;
this.length = spacing;
this.tear_distance = tear_distance;
};

Constraint.prototype.solve = 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 > this.tear_distance) this.p1.remove_constraint(this);

var scalar_1 = ((1 / this.p1.mass) / ((1 / this.p1.mass) + (1 / this.p2.mass))),
scalar_2 = 1 - scalar_1;

this.p1.x += diff_x * scalar_1 * diff;
this.p1.y += diff_y * scalar_1 * diff;

this.p2.x -= diff_x * scalar_2 * diff;
this.p2.y -= diff_y * scalar_2 * diff;
};

Constraint.prototype.draw = function() {
ctx.moveTo(this.p1.x, this.p1.y);
ctx.lineTo(this.p2.x, this.p2.y);
};

var Point = function(x, y) {

this.x = x;
this.y = y;
this.px = x;
this.py = y;
this.ax = 0;
this.ay = 0;
this.mass = 1;
this.constraints = [];
this.pinned = false;
this.pin_x;
this.pin_y;
};

Point.prototype.update = function(delta) {

this.add_force(0, this.mass * gravity);

var vx = this.x - this.px,
  vy = this.y - this.py;

delta *= delta;
nx = this.x + 0.99 * vx + 0.5 * this.ax * delta;
ny = this.y + 0.99 * vy + 0.5 * this.ay * delta;

this.px = this.x;
this.py = this.y;

this.x = nx;
this.y = ny;

this.ay = this.ax = 0
};

Point.prototype.update_mouse = function() {

if (!mouse.down) return;

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 = [];
};

Point.prototype.draw = function() {

if (this.constraints.length <= 0) return;
var i = this.constraints.length;
while(i--) this.constraints[i].draw();
};

Point.prototype.solve_constraints = function() {

var i = this.constraints.length;
while(i--) this.constraints[i].solve();

if (this.y < 1) this.y = 2 * (1) - this.y;
else if (this.y > canvas.height-1) this.y = 2 * (canvas.height - 1) - this.y;
 
if (this.x > canvas.width-1) this.x = 2 * (canvas.width - 1) - this.x;
else if (this.x < 1) this.x = 2 * (1) - this.x;

if (this.pinned) {
this.x = this.pin_x;
this.y = this.pin_y; 
}
};

Point.prototype.attach = function(P, spacing, tear_distance) {

this.constraints.push(
new Constraint(this, P, spacing, tear_distance)
);
};

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(fX, fY) {

this.ax += fX/this.mass;
this.ay += fY/this.mass;
};

Point.prototype.pin = function(pX, pY) {

this.pinned = true;
this.pin_x = pX;
this.pin_y = pY;
};

var Physics = function() {

this.delta_sec = 16 / 1000;
this.accuracy = physics_accuracy;
};

Physics.prototype.update = function() {

var i = this.accuracy;

while(i--) {
var p = points.length;
while(p--) points[p].solve_constraints();
}

i = points.length;
while(i--) {
points[i].update_mouse();
points[i].update(this.delta_sec);
}
};

function init() {
  
physics = new Physics();
points = [];
build_cloth();
update();
}

function update() {

ctx.clearRect(0, 0, canvas.width, canvas.height);

physics.update();
  
ctx.strokeStyle = 'rgba(222,222,222,0.6)';
ctx.beginPath();
var i = points.length;
while(i--) points[i].draw();
ctx.stroke();

requestAnimFrame(update);
}

function build_cloth() {

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, y * spacing + start_y);

0 != x &&
p.attach(points[points.length - 1], spacing, tear_distance);

0 != y &&
p.attach(points[(y - 1) * (cloth_width + 1) + x], spacing, tear_distance);

0 == y &&
p.pin(p.x, p.y);

points.push(p)
}
}
}

HTML Code:

  Tear the cloth with your mouse.

  Right click and drag to cut the cloth.

  Reduce physics_accuracy if it's laggy, increase it for a better effect.



Read more: http://codepen.io/stuffit/pen/KrAwx

Subscribe to App Developer Magazine

Become a subscriber of App Developer Magazine for just $5.99 a month and take advantage of all these perks.

MEMBERS GET ACCESS TO

  • - Exclusive content from leaders in the industry
  • - Q&A articles from industry leaders
  • - Tips and tricks from the most successful developers weekly
  • - Monthly issues, including all 90+ back-issues since 2012
  • - Event discounts and early-bird signups
  • - Gain insight from top achievers in the app store
  • - Learn what tools to use, what SDK's to use, and more

    Subscribe here