Статьи

Крутой HTML5 3D Cube Slideshow

В нашем новом уроке показано, как создать анимированное слайд-шоу 3D-куба (изображения расположены внутри стен куба). Сам куб вращается непрерывно.

Вот наш демонстрационный и загружаемый пакет:

Live Demo

скачать в упаковке

Хорошо, загрузите исходные файлы и давайте начнем кодировать!


Шаг 1. HTML

Это разметка нашей страницы слайд-шоу.

index.html

<!DOCTYPE html>
<html lang="en" >
    <head>
        <meta charset="utf-8" />
        <title>HTML5 3D Cube Slideshow | Script Tutorials</title>
        <link href="css/main.css" rel="stylesheet" type="text/css" />
        <script src="js/script.js"></script>
    </head>
    <body>
        <header>
            <h2>HTML5 3D Cube Slideshow</h2>
            <a href="http://www.script-tutorials.com/html5-3d-cube-slideshow/" class="stuts">Back to original tutorial on <span>Script Tutorials</span></a>
        </header>
        <canvas id="slideshow" width="1280" height="800"></canvas>
    </body>
</html>

 

Шаг 2. CSS

CSS / main.css

Этот файл доступен в пакете (потому что он просто содержит основные стили макета страницы).

Шаг 3. JS

JS / script.js

var canvas, ctx;
var aImages = [];
var points = [];
var triangles = [];
var textureWidth, textureHeight;
var lev = 3;
var angle = 0;

// scene vertices
var vertices = [
    new Point3D(-2,-1,2),
    new Point3D(2,-1,2),
    new Point3D(2,1,2),
    new Point3D(-2,1,2),
    new Point3D(-2,-1,-2),
    new Point3D(2,-1,-2),
    new Point3D(2,1,-2),
    new Point3D(-2,1,-2)
];

// scene faces (6 faces)
var faces  = [[0,1,2,3],[1,5,6,2],[5,4,7,6],[4,0,3,7],[0,4,5,1],[3,2,6,7]];

function Point3D(x,y,z) {
    this.x = x;
    this.y = y;
    this.z = z;

    this.rotateX = function(angle) {
        var rad, cosa, sina, y, z
        rad = angle * Math.PI / 180
        cosa = Math.cos(rad)
        sina = Math.sin(rad)
        y = this.y * cosa - this.z * sina
        z = this.y * sina + this.z * cosa
        return new Point3D(this.x, y, z)
    }
    this.rotateY = function(angle) {
        var rad, cosa, sina, x, z
        rad = angle * Math.PI / 180
        cosa = Math.cos(rad)
        sina = Math.sin(rad)
        z = this.z * cosa - this.x * sina
        x = this.z * sina + this.x * cosa
        return new Point3D(x,this.y, z)
    }
    this.rotateZ = function(angle) {
        var rad, cosa, sina, x, y
        rad = angle * Math.PI / 180
        cosa = Math.cos(rad)
        sina = Math.sin(rad)
        x = this.x * cosa - this.y * sina
        y = this.x * sina + this.y * cosa
        return new Point3D(x, y, this.z)
    }
    this.projection = function(viewWidth, viewHeight, fov, viewDistance) {
        var factor, x, y
        factor = fov / (viewDistance + this.z)
        x = this.x * factor + viewWidth / 2
        y = this.y * factor + viewHeight / 2
        return new Point3D(x, y, this.z)
    }
}

// array of photos
var aImgs = [
    'images/pic1.jpg',
    'images/pic2.jpg',
    'images/pic3.jpg',
    'images/pic4.jpg'
];
for (var i = 0; i < aImgs.length; i++) {
    var oImg = new Image();
    oImg.src = aImgs[i];
    aImages.push(oImg);

    oImg.onload = function () {
        textureWidth = oImg.width;
        textureHeight = oImg.height;
    }
}

window.onload = function(){
    // creating canvas objects
    canvas = document.getElementById('slideshow');
    ctx = canvas.getContext('2d');

    // prepare points
    for (var i = 0; i <= lev; i++) {
        for (var j = 0; j <= lev; j++) {
            var tx = (i * (textureWidth / lev));
            var ty = (j * (textureHeight / lev));
            points.push({
                tx: tx,
                ty: ty,
                nx: tx / textureWidth,
                ny: ty / textureHeight,
                ox: i,
                oy: j
            });
        }
    }

    // prepare triangles ----
    var levT = lev + 1;
    for (var i = 0; i < lev; i++) {
        for (var j = 0; j < lev; j++) {
            triangles.push({
                p0: points[j + i * levT],
                p1: points[j + i * levT + 1],
                p2: points[j + (i + 1) * levT],
                up: true
            });
            triangles.push({
                p0: points[j + (i + 1) * levT + 1],
                p1: points[j + (i + 1) * levT],
                p2: points[j + i * levT + 1],
                up: false
            });
        }
    }

    drawScene();
};

function drawScene() {
    // clear context
    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);

    // rotate scene
    var t = new Array();
    for (var iv = 0; iv < vertices.length; iv++) {
        var v = vertices[iv];
        var r = v.rotateY(angle);
        //var r = v.rotateX(angle).rotateY(angle);
        var prj = r.projection(ctx.canvas.width, ctx.canvas.height, 1000, 3);
        t.push(prj)
    }

    var avg_z = new Array();
    for (var i = 0; i < faces.length; i++) {
        var f = faces[i];
        avg_z[i] = {"ind":i, "z":(t[f[0]].z + t[f[1]].z + t[f[2]].z + t[f[3]].z) / 4.0};
    }

    // get around through all faces
    for (var i = 0; i < faces.length; i++) {
        var f = faces[avg_z[i].ind];

        if (t[f[3]].z+t[f[2]].z+t[f[1]].z+t[f[0]].z > -3) {
            ctx.save();

            // draw surfaces
            ctx.fillStyle = "rgb(160,180,160)";
            ctx.beginPath();
            ctx.moveTo(t[f[0]].x,t[f[0]].y);
            ctx.lineTo(t[f[1]].x,t[f[1]].y);
            ctx.lineTo(t[f[2]].x,t[f[2]].y);
            ctx.lineTo(t[f[3]].x,t[f[3]].y);
            ctx.closePath();
            ctx.fill();

            // draw stretched images
            if (i < 4) {
                var ip = points.length;
                while (--ip > -1) {
                    var p = points[ip];
                    var mx = t[f[0]].x + p.ny * (t[f[3]].x - t[f[0]].x);
                    var my = t[f[0]].y + p.ny * (t[f[3]].y - t[f[0]].y);
                    p.px = (mx + p.nx * (t[f[1]].x + p.ny * (t[f[2]].x - t[f[1]].x) - mx)) + p.ox;
                    p.py = (my + p.nx * (t[f[1]].y + p.ny * (t[f[2]].y - t[f[1]].y) - my)) + p.oy;
                }

                var n = triangles.length;
                while (--n > -1) {
                    var tri = triangles[n];
                    var p0 = tri.p0;
                    var p1 = tri.p1;
                    var p2 = tri.p2;

                    var xc = (p0.px + p1.px + p2.px) / 3;
                    var yc = (p0.py + p1.py + p2.py) / 3;

                    ctx.save();
                    ctx.beginPath();
                    ctx.moveTo((1.05 * p0.px - xc * 0.05), (1.05 * p0.py - yc * 0.05));
                    ctx.lineTo((1.05 * p1.px - xc * 0.05), (1.05 * p1.py - yc * 0.05));
                    ctx.lineTo((1.05 * p2.px - xc * 0.05), (1.05 * p2.py - yc * 0.05));
                    ctx.closePath();
                    ctx.clip();

                    // transformation
                    var d = p0.tx * (p2.ty - p1.ty) - p1.tx * p2.ty + p2.tx * p1.ty + (p1.tx - p2.tx) * p0.ty;
                    ctx.transform(
                        -(p0.ty * (p2.px - p1.px) -  p1.ty * p2.px  + p2.ty *  p1.px + (p1.ty - p2.ty) * p0.px) / d, // m11
                         (p1.ty *  p2.py + p0.ty  * (p1.py - p2.py) - p2.ty *  p1.py + (p2.ty - p1.ty) * p0.py) / d, // m12
                         (p0.tx * (p2.px - p1.px) -  p1.tx * p2.px  + p2.tx *  p1.px + (p1.tx - p2.tx) * p0.px) / d, // m21
                        -(p1.tx *  p2.py + p0.tx  * (p1.py - p2.py) - p2.tx *  p1.py + (p2.tx - p1.tx) * p0.py) / d, // m22
                         (p0.tx * (p2.ty * p1.px  -  p1.ty * p2.px) + p0.ty * (p1.tx *  p2.px - p2.tx  * p1.px) + (p2.tx * p1.ty - p1.tx * p2.ty) * p0.px) / d, // dx
                         (p0.tx * (p2.ty * p1.py  -  p1.ty * p2.py) + p0.ty * (p1.tx *  p2.py - p2.tx  * p1.py) + (p2.tx * p1.ty - p1.tx * p2.ty) * p0.py) / d  // dy
                    );
                    ctx.drawImage(aImages[i], 0, 0);
                    ctx.restore();
                }
            }
        }
    }

    // shift angle and redraw scene
    angle += 0.3;
    setTimeout(drawScene, 40);
}

 

Сначала я определил все вершины и грани (стены) нашего виртуального куба. Затем я определил правила вращения. А потом — самое сложное — я закодировал преобразование изображений с помощью «clip» и «transform».


Вывод

Я надеюсь, что сегодняшний урок 3D html5 cube был для вас интересным. Мы сделали еще один хороший пример HTML5. Буду рад видеть ваши благодарности и комментарии. Удачи!