3D版HTML5模拟衣服撕扯动画

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

还记得很早以前向大家分享的这款HTML5 Canvas模拟衣服撕扯动画吗?这绝对是一款非常具有创意而且很好玩的HTML5动画。今天我们来分享一下它的3D版本,在原来的基础上,衣服布料呈3D环形显示,你同样可以用鼠标拖拽衣服,不过和之前不同的是,鼠标左键用来拖拽衣服,鼠标右键用来切割衣服,失去了之前鼠标用力程度和衣服破碎程度的关系,似乎功能上有所缺失,但是HTML5 3D功能还是不错的。

3d-html5-cloth-tear

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

HTML代码:

<canvas width="800" height="800" id="c"></canvas>

只是定义了一个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;
            }

        }
    }
}

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

<script type="text/javascript">
  init();
</script>

在线演示源码下载

热门推荐

3D版HTML5模拟衣服撕扯动画》上有1条评论

发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>