Canvas 基础图形3D框架 Zdog

框架介绍

Zdog 是一个开源的基于 Canvas 和 SVG 的 3D 图形渲染引擎,官网地址:https://zzz.dog/

Zdog引擎示例 Zdog引擎示例

Canvas基础

Canvas就像一块画布,我们可以在这个画布上面绘制我们需要的图形。canvas 元素本身是没有绘图能力的。所有的绘制工作必须在 JavaScript 内部完成。

1
<canvas id="testcanvas" width="500" height="500" style="border:1px solid red"></canvas>

例如在上面的 200x200 画布内绘制两个矩形:

1
2
3
4
5
6
7
var c = document.getElementById('testcanvas');
var p = c.getContext("2d");

p.fillStyle="#FF0000";
p.fillRect(0, 0, 300, 300);
p.fillStyle="rgba(0,0,255,0.5)";
p.fillRect(200,200,500,500);

使用Canvas绘制两个矩形 使用Canvas绘制两个矩形

getContext("2d") 对象是内建的 HTML5 对象,拥有多种绘制路径、矩形、圆形、字符以及添加图像的方法。

使用Zdog

Zdog可以渲染到 <canvas><svg> 标签上,引入引擎的 cdn 地址即可,使用如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        canvas{
            margin: 50px;
            background: #FDB;
            border-radius: 8px;
        }
    </style>
</head>
<body>
    <canvas class="zdog-canvas" width="240" height="240"></canvas>
    <script src="https://unpkg.com/zdog@1/dist/zdog.dist.min.js"></script>
    <script>
        let illo = new Zdog.Illustration({
            // 设置选择的 canvas
            element: '.zdog-canvas',
        });

        // 添加一个圆环
        new Zdog.Ellipse({
            addTo: illo,
            diameter: 80,
            stroke: 20,
            color: '#636',
        });

        // 刷新渲染
        illo.updateRenderGraph();
    </script>
</body>
</html>

Zdog 绘制一个圆环 Zdog 绘制一个圆环

可以使用 js 动画让圆环动起来:

1
2
3
4
5
6
7
8
9
function animate() {
    // 每渲染一帧移动y轴位置
    illo.rotate.y += 0.03;
    illo.updateRenderGraph();
    // 请求下一帧动画
    requestAnimationFrame( animate );
}
// 开始动画
animate();

Zdog动画 Zdog动画

requestAnimationFrame 会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率,一般来说,这个频率为每秒60帧。

立体空间动画

上面的动画只是绕自身旋转,不够立体直观,接下来我们再添加一个立方块进去,让一同旋转。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
let illo = new Zdog.Illustration({
    element: '.zdog-canvas',
});

//椭圆
new Zdog.Ellipse({  
    addTo: illo,
    diameter: 80,           //直径
    translate: { z: 40 },   //z 轴位移
    stroke: 20,             //厚度
    color: '#636',          //颜色
});

//矩形
new Zdog.Rect({
    addTo: illo,
    width: 80,              //宽
    height: 80,             //高
    translate: { z: -40 },  //z 轴位移
    stroke: 12,             //厚度
    color: '#E62',          //颜色
    fill: true,             //填充
});

function animate() {
    illo.rotate.y += 0.03;     //y
    illo.updateRenderGraph();  //渲染
    requestAnimationFrame( animate );
}
animate();

立体空间旋转动画 立体空间旋转动画

接下来我们来分析一下坐标和旋转原理,从上面的 translate 可知,z 轴是垂直于屏幕方向并指向外的, 修改椭圆的初始角度位置:

1
2
3
4
5
6
7
8
new Zdog.Ellipse({
    addTo: illo,
    diameter: 80,
    translate: { z: 40 },
    rotate: {y: Zdog.TAU / 4},
    stroke: 20,
    color: '#636',
});

查看源码可知 Zdog.TAU = Math.PI * 2; , 你可以试试设置一个 rotate: {y: 1.57} 是同样的效果,我们可以知道 6.28 是旋转一圈。

为了确定 y 轴和 x 轴的方向,我们修改 illo.rotate.z += 0.03; 会看到顺时针方向旋转,这说明是 “左手定则”。我们可以推断出 y 轴竖直朝上,同样的我们修改 illo.rotate.x += 0.03; 可以推断出 x 轴水平朝右。

Zdog坐标示意图 Zdog坐标示意图

造型

接下来我们通过简单的图形来完成一个人物头像:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
let illo = new Zdog.Illustration({
    element: '.zdog-canvas',
    zoom: 10,   //放大倍数(可以让下面的设置数字变小)
    dragRotate: true,  //拖动旋转
});

let head = new Zdog.Shape({
    addTo: illo,
    stroke: 12,
    color: '#eeaa00',
});

let eye = new Zdog.Ellipse({
    addTo: head,    //注意添加到 head 上
    diameter: 2,
    quarters: 2,
    translate: { x: -2, y: 1, z: 4.5 },
    rotate: { z: - Zdog.TAU/4 },
    color: '#663366',
    stroke: 0.5,
    backface: false,
});

eye.copy({      //使用 copy 函数复制
    translate: { x: 2, y: 1, z: 4.5 },
});

new Zdog.Ellipse({
    addTo: head,
    diameter: 3,
    quarters: 2,
    translate: { y: 2.5, z: 4.5 },
    rotate: { z: Zdog.TAU/4 },
    closed: true,
    color: '#FED',
    stroke: 0.5,
    fill: true,
    backface: false,
});

illo.updateRenderGraph();

function animate() {
    illo.updateRenderGraph();
    requestAnimationFrame( animate );
}
animate();