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

Programming

Awesome Demo of Cloth (fabric) using HTML5


Monday, March 25, 2013
1,502

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.