html5 3d在线试衣源码,3D版HTML5模拟衣服撕扯动画

本文作者html5tricks,转载请注明出处

还记得很早以前向大家分享的这款HTML5 Canvas模拟衣服撕扯动画吗?这绝对是一款非常具有创意而且很好玩的

8f7fb6cc78b4c91dda08c07a764109c1.png

下面我们来简单分享一下实现的源码,代码主要由HTML和JavaScript组成,主要还是JavaScript。

HTML代码:

只是定义了一个canvas标签,非常简单。

JavaScript代码:

canvas = document.getElementById('c');

ctx = canvas.getContext('2d');

//settings

var physics_acc = 5,

tear_distance = 40,

auto_rotate = 1,

field_of_view = 500,

gravity = 0.2,

friction = 0.99,

cloth_rows = 20,

pm_locked_c = 39; //pointmass locked count - prevents the cloth from falling to the ground

//arrays

var pointmass = [],

constraints = [];

vertex = [];

//random

var halfx = canvas.width / 2,

halfy = canvas.height / 2,

rotatex = 0,

rotatey = 0,

rotatez = 0,

mouse = {

down: false,

x: 0,

y: 0,

ox: 0,

oy: 0,

button: 0

};

ctx.lineWidth = 0.5;

ctx.strokeStyle = "#ddd";

window.requestAnimFrame =

window.requestAnimationFrame ||

window.webkitRequestAnimationFrame ||

window.mozRequestAnimationFrame ||

window.oRequestAnimationFrame ||

window.msRequestAnimationFrame ||

function (callback) {

window.setTimeout(callback, 1000 / 60);

};

function init() {

//create top row

for(i = 0; i < 40; i++) {

x = Math.cos(2 * Math.PI * (i / 40)) * 100;

z = Math.sin(2 * Math.PI * (i / 40)) * 100;

create_pointmass(x, cloth_rows / 2 * -20, z);

}

//rest

for(i = 0; i < 40; i++) {

x = Math.cos(2 * Math.PI * (i / 40)) * 100;

z = Math.sin(2 * Math.PI * (i / 40)) * 100;

for(y = cloth_rows / 2 * -20 + 20; y < cloth_rows / 2 * 20; y+=20) {

create_pointmass(x,y,z);

}

}

//create constraints based on distance

for(i = 0; i < pointmass.length; i++) {

for(c = i + 1; c < pointmass.length; c++) {

dist = Math.sqrt(

Math.pow(pointmass[i][0] - pointmass[c][0], 2)

+ Math.pow(pointmass[i][1] - pointmass[c][1], 2)

+ Math.pow(pointmass[i][2] - pointmass[c][2], 2));

if(dist < 21) {

create_constraint(i,c);

}

}

}

//mouse

canvas.onmousemove = function(e) {

mouse.ox = mouse.x;

mouse.oy = mouse.y;

mouse.x = e.pageX - canvas.offsetLeft,

mouse.y = e.pageY - canvas.offsetTop;

e.preventDefault();

};

canvas.onmousedown = function(e) {

mouse.button = e.which;

mouse.down = true;

e.preventDefault();

};

canvas.oncontextmenu = function(e) {

e.preventDefault();

};

canvas.onmouseup = function(e) {

mouse.down = false;

e.preventDefault();

};

render()

}

function draw3D() {

vertex = [];

for(i = 0; i < pointmass.length; i++) {

vertex.push([pointmass[i][0],pointmass[i][1],pointmass[i][2]])

}

if(auto_rotate == 1)rotatey+= 0.01;

for(i = 0; i < vertex.length; i++) {

xyz = vertex[i];

x = xyz[0];

y = xyz[1];

z = xyz[2];

xcosa = Math.cos(rotatex);

xsina = Math.sin(rotatex);

ycosa = Math.cos(rotatey);

ysina = Math.sin(rotatey);

zcosa = Math.cos(rotatez);

zsina = Math.sin(rotatez);

xy = xcosa*y - xsina*z; //x

xz = xsina*y + xcosa*z;

yz = ycosa*xz - ysina*x; //y

yx = ysina*xz + ycosa*x;

zx = zcosa*yx - zsina*xy; //z

zy = zsina*yx + zcosa*xy;

xyz[0] = zx;

xyz[1] = zy;

xyz[2] = yz;

}

ctx.beginPath();

for(i = 0; i < constraints.length; i++) {

for(c = 0; c < 2; c++) {

xyz = vertex[constraints[i][c]];

fov = field_of_view / (field_of_view + xyz[2]);

x = xyz[0] * fov + halfx;

y = xyz[1] * fov + halfy;

if(c == 0) {

ctx.moveTo(x,y);

} else {

ctx.lineTo(x,y);

}

}

}

ctx.closePath();

ctx.stroke();

}

function update_pointmass() {

for(i = 0; i < pointmass.length; i++) {

x = pointmass[i][0];

y = pointmass[i][1];

z = pointmass[i][2];

ox = pointmass[i][3];

oy = pointmass[i][4];

oz = pointmass[i][5];

dx = x - ox;

dy = y - oy;

dz = z - oz;

if(i > pm_locked_c) {

pointmass[i][3] = x;

pointmass[i][4] = y;

pointmass[i][5] = z;

pointmass[i][0] = x + dx * friction;

pointmass[i][1] = y + dy + gravity;

pointmass[i][2] = z + dz * friction;

} else {

pointmass[i][0] = ox;

pointmass[i][1] = oy;

pointmass[i][2] = oz;

}

}

}

function update_constraint() {

for(i = 0; i < physics_acc; i++) {

for(c = 0; c < constraints.length; c++) {

c1 = pointmass[constraints[c][0]];

c2 = pointmass[constraints[c][1]];

diffx = c1[0] - c2[0];

diffy = c1[1] - c2[1];

diffz = c1[2] - c2[2];

dist = Math.sqrt(diffx * diffx + diffy * diffy + diffz * diffz);

diff = (constraints[c][2] - dist) / dist;

dx = c1[0] - c2[0];

dy = c1[1] - c2[1];

dz = c1[2] - c2[2];

dx = dx * 0.5;

dy = dy * 0.5;

dz = dz * 0.5;

c1[0] = c1[0] + dx * diff;

c1[1] = c1[1] + dy * diff;

c1[2] = c1[2] + dz * diff;

c2[0] = c2[0] - dx * diff;

c2[1] = c2[1] - dy * diff;

c2[2] = c2[2] - dz * diff;

constraints[c][3] = dist;

if(dist > tear_distance) {

constraints.splice(c, 1);

}

}

}

}

function create_pointmass(x,y,z) {

pointmass.push([x, y, z, x, y, z]);

}

function create_constraint(f,s) {

constraints.push([f, s, Math.sqrt(

Math.pow((pointmass[f][0] - pointmass[s][0]), 2)

+ Math.pow((pointmass[f][1] - pointmass[s][1]), 2)

+ Math.pow((pointmass[f][2] - pointmass[s][2]), 2)), 1])

}

function render() {

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

update_mouse();

update_pointmass();

update_constraint();

draw3D();

requestAnimFrame(render);

}

function update_mouse() {

if(mouse.down == true) {

for(i = 0; i < pointmass.length; i++) {

dist = Math.sqrt(Math.pow((vertex[i][0] - (mouse.x - halfx)), 2)

+ Math.pow((vertex[i][1] - (mouse.y - halfy)), 2)

+ Math.pow((vertex[i][2] + 50), 2));

if(dist < 100 && mouse.button == 1) {

pointmass[i][3] = pointmass[i][3] - Math.min(1, (mouse.x - mouse.ox) / 10);

pointmass[i][4] = pointmass[i][4] - Math.min(1, (mouse.y - mouse.oy) / 10);

}

//lazy cut for m2

if(dist < 21 && mouse.button == 3) {

pointmass[i][4] = -1000;

pointmass[i][3] = -1000;

}

}

}

}

最后在页面加载完后调用一下初始化的代码即可:

init();