用于在伪3D中绘制的窗格

Pane for drawing in pseudo 3D
Pane for drawing in pseudo 3D

Pane for drawing in pseudo 3D Today we are going to go back to the practical lessons for html5. I think we have already done a good break in our lessons. In this tutorial I will show you how to create a pane for drawing which is spinning on its axis (on html5 canvas object). Each of your drawn shape will be spinning in pseudo 3D mode. Two objects (prototypes) are defined here: Point and Shape (each shape consists of multiple points). The main idea – to project and be rotated points of figures, as well as to draw a curved line between these points.

用于绘制伪3D的窗格今天,我们将回到html5的实际课程。 我认为我们已经在课程中取得了不错的成绩。 在本教程中,我将向您展示如何创建一个在其轴上旋转的绘图窗格(在html5 canvas对象上)。 您绘制的每个形状都将以伪3D模式旋转。 此处定义了两个对象(原型):点和形状(每个形状由多个点组成)。 主要思想是投影和旋转图形的点,并在这些点之间绘制曲线。

现场演示

[sociallocker]

[社交储物柜]

打包下载

[/sociallocker]

[/ sociallocker]

Now we can download the source package, unpack it, and start looking at the code

现在我们可以下载源代码包,将其解压缩,然后开始查看代码

步骤1. HTML (Step 1. HTML)

In the first step we have to prepare some basic html code:

第一步,我们必须准备一些基本的html代码:

index.html (index.html)

    <!-- add scripts -->
    <script src="js/pointer.js"></script>
    <script src="js/main.js"></script>
    ....
    <canvas id="scene" height="600" width="800" tabindex="1"></canvas>

    <!-- add scripts -->
    <script src="js/pointer.js"></script>
    <script src="js/main.js"></script>
    ....
    <canvas id="scene" height="600" width="800" tabindex="1"></canvas>

As usual, we include scripts in the header section of the document, but, in our case, it doesn’t actually matter where it is exactly. Our JS code will be executed only when the document will be downloaded

像往常一样,我们在文档的标题部分中包含脚本,但就我们而言,确切位置实际上并不重要。 仅当文档下载后,我们的JS代码才会执行

步骤2. JS(HTML5) (Step 2. JS (HTML5))

As usual, in the beginning of every JS code, we can define several global variables:

像往常一样,在每个JS代码的开头,我们可以定义几个全局变量:

js / main.js (js/main.js)

// Variables
var canvas, ctx; // canvas and context objects
var vPointer; // draw pointer pbject
var aShapes = []; // shapes array
var iLMx = iLMy = 0; // last pointer positions
var vActShape; // active shape object

// Variables
var canvas, ctx; // canvas and context objects
var vPointer; // draw pointer pbject
var aShapes = []; // shapes array
var iLMx = iLMy = 0; // last pointer positions
var vActShape; // active shape object

Now, we can introduce our first low level object – Point:

现在,我们可以介绍我们的第一个低级对象–点:


// Point object
var Point = function (x, y, z) {
    this.x  = x;
    this.y  = y;
    this.z  = z;
    this.x0 = x;
    this.z0 = z;
    this.xp = 0;
    this.yp = 0;
    this.zp = 0;
    this.fov = 2000;
}
Point.prototype.project = function () {
    this.zp = this.fov / (this.fov + this.z);
    this.xp = this.x * this.zp;
    this.yp = this.y * this.zp;
}
Point.prototype.rotate = function (ax, ay) {
    this.x = parseInt(Math.round(this.x0 * ax + this.z0 * ay));
    this.z = parseInt(Math.round(this.x0 * -ay + this.z0 * ax));
}

// Point object
var Point = function (x, y, z) {
    this.x  = x;
    this.y  = y;
    this.z  = z;
    this.x0 = x;
    this.z0 = z;
    this.xp = 0;
    this.yp = 0;
    this.zp = 0;
    this.fov = 2000;
}
Point.prototype.project = function () {
    this.zp = this.fov / (this.fov + this.z);
    this.xp = this.x * this.zp;
    this.yp = this.y * this.zp;
}
Point.prototype.rotate = function (ax, ay) {
    this.x = parseInt(Math.round(this.x0 * ax + this.z0 * ay));
    this.z = parseInt(Math.round(this.x0 * -ay + this.z0 * ax));
}

For an object ‘Point’ we have a whole set of internal properties and only two functions: project and rotate. Another object ‘Shape’ is a more complex object:

对于对象“点”,我们具有一整套内部属性,只有两个功能:投影和旋转。 另一个对象“形状”是一个更复杂的对象:


// Shape object
var Shape = function () {
    this.angle = 0;
    this.color = '#000';
    this.halfheight = canvas.height / 2;
    this.halfwidth = canvas.width / 2;
    this.len = 0;
    this.points = [];
    return this;
}
// Add point to shape
Shape.prototype.addPoint = function (x, y, z) {
    this.points.push(
        new Point(Math.round(x), Math.round(y), Math.round(z))
    );
    this.len++;
}
// Rotate shape
Shape.prototype.rotate = function () {
    this.angle += Math.PI / 180; // offset by 1 degree (radians in one degrees)
    var ax = Math.cos(this.angle);
    var ay = Math.sin(this.angle);
    // Rotate all the points of the shape object
    for (var i = 0; i < this.len; i++) {
        this.points[i].rotate(ax, ay);
    }
}
Shape.prototype.draw = function () {
    // points projection
    for (var i = 0; i < this.len; i++) {
        this.points[i].project();
    }
    // draw a curved line between points
    var p0 = this.points[0];
    ctx.beginPath();
    ctx.moveTo(p0.xp + this.halfwidth, p0.yp + this.halfheight);
    for (var i = 1, pl = this.points.length; i < pl; i++) {
        var apnt = this.points[i];
        var xc = (p0.xp + apnt.xp) / 2;
        var yc = (p0.yp + apnt.yp) / 2;
        ctx.quadraticCurveTo(p0.xp + this.halfwidth, p0.yp + this.halfheight, xc + this.halfwidth, yc + this.halfheight);
        p0 = apnt;
    }
    // stroke properties
    ctx.lineWidth = 8;
    ctx.strokeStyle = this.color;
    ctx.lineCap = 'round'; // rounded end caps
    ctx.stroke();
}

// Shape object
var Shape = function () {
    this.angle = 0;
    this.color = '#000';
    this.halfheight = canvas.height / 2;
    this.halfwidth = canvas.width / 2;
    this.len = 0;
    this.points = [];
    return this;
}
// Add point to shape
Shape.prototype.addPoint = function (x, y, z) {
    this.points.push(
        new Point(Math.round(x), Math.round(y), Math.round(z))
    );
    this.len++;
}
// Rotate shape
Shape.prototype.rotate = function () {
    this.angle += Math.PI / 180; // offset by 1 degree (radians in one degrees)
    var ax = Math.cos(this.angle);
    var ay = Math.sin(this.angle);
    // Rotate all the points of the shape object
    for (var i = 0; i < this.len; i++) {
        this.points[i].rotate(ax, ay);
    }
}
Shape.prototype.draw = function () {
    // points projection
    for (var i = 0; i < this.len; i++) {
        this.points[i].project();
    }
    // draw a curved line between points
    var p0 = this.points[0];
    ctx.beginPath();
    ctx.moveTo(p0.xp + this.halfwidth, p0.yp + this.halfheight);
    for (var i = 1, pl = this.points.length; i < pl; i++) {
        var apnt = this.points[i];
        var xc = (p0.xp + apnt.xp) / 2;
        var yc = (p0.yp + apnt.yp) / 2;
        ctx.quadraticCurveTo(p0.xp + this.halfwidth, p0.yp + this.halfheight, xc + this.halfwidth, yc + this.halfheight);
        p0 = apnt;
    }
    // stroke properties
    ctx.lineWidth = 8;
    ctx.strokeStyle = this.color;
    ctx.lineCap = 'round'; // rounded end caps
    ctx.stroke();
}

It has less params, but more functions (addPoint, rotate and draw). Now, we can start adding few main scene functions: main scene (canvas) initialization (sceneInit), rendering function (drawScene) and random color generator (getRandomColor):

它具有更少的参数,但是具有更多的功能(addPoint,rotate和draw)。 现在,我们可以开始添加一些主要场景功能:主要场景(画布)初始化(sceneInit),渲染功能(drawScene)和随机颜色生成器(getRandomColor):


// Get random color
function getRandomColor() {
    var letters = '0123456789ABCDEF'.split('');
    var color = '#';
    for (var i = 0; i < 6; i++ ) {
        color += letters[Math.round(Math.random() * 15)];
    }
    return color;
}
// Draw main scene function
function drawScene() {
    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); // Clear canvas
    if (vPointer.bDown) { // on mouse down
        var iDx = iLMx - vPointer.X;
        var iDy = iLMy - vPointer.Y;
        var dif = Math.sqrt(iDx * iDx + iDy * iDy); // difference between two last points
        if (dif > 5) {
            if (! vActShape) {
                aShapes.push( // prepare a new shape object
                    vActShape = new Shape()
                );
                vActShape.color = getRandomColor();
            }
            iLMx = vPointer.X;
            iLMy = vPointer.Y;
            vActShape.addPoint(vPointer.X - canvas.width  * 0.5, vPointer.Y - canvas.height * 0.5, 0);
        }
    } else {
        // Once mouse is released - cleanup
        if (vActShape) {
            vActShape = '';
            iLMx = iLMy = 0;
        }
        // Rotate the shapes
        aShapes.forEach(function(sh) {
            sh.rotate();
        });
    }
    // Draw all shapes
    aShapes.forEach(function(sh) {
        sh.draw();
    });
}
// Initialization
function sceneInit() {
    // Prepare canvas and context objects
    canvas = document.getElementById('scene');
    // Canvas resize
    canvas.width = canvas.clientWidth;
    canvas.height = window.innerHeight;
    ctx = canvas.getContext('2d');
    // Add two custom shapes
    var oShape = new Shape();
    oShape.addPoint(-200,-200,50);
    oShape.addPoint(0,0,0);
    oShape.addPoint(-400,200,0);
    oShape.addPoint(200,-400,-50);
    aShapes.push(oShape);
    var oShape2 = new Shape();
    oShape2.addPoint(200,200,-50);
    oShape2.addPoint(0,0,0);
    oShape2.addPoint(400,-200,0);
    oShape2.addPoint(-200,400,50);
    aShapes.push(oShape2);
    // Mouse pointer event handler
    vPointer = new CPointer(canvas);
    // Main scene loop
    setInterval(drawScene, 20);
}
// Window onload init
if (window.attachEvent) {
    window.attachEvent('onload', sceneInit);
} else {
    if (window.onload) {
        var curronload = window.onload;
        var newonload = function() {
            curronload();
            sceneInit();
        };
        window.onload = newonload;
    } else {
        window.onload = sceneInit;
    }
}

// Get random color
function getRandomColor() {
    var letters = '0123456789ABCDEF'.split('');
    var color = '#';
    for (var i = 0; i < 6; i++ ) {
        color += letters[Math.round(Math.random() * 15)];
    }
    return color;
}
// Draw main scene function
function drawScene() {
    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); // Clear canvas
    if (vPointer.bDown) { // on mouse down
        var iDx = iLMx - vPointer.X;
        var iDy = iLMy - vPointer.Y;
        var dif = Math.sqrt(iDx * iDx + iDy * iDy); // difference between two last points
        if (dif > 5) {
            if (! vActShape) {
                aShapes.push( // prepare a new shape object
                    vActShape = new Shape()
                );
                vActShape.color = getRandomColor();
            }
            iLMx = vPointer.X;
            iLMy = vPointer.Y;
            vActShape.addPoint(vPointer.X - canvas.width  * 0.5, vPointer.Y - canvas.height * 0.5, 0);
        }
    } else {
        // Once mouse is released - cleanup
        if (vActShape) {
            vActShape = '';
            iLMx = iLMy = 0;
        }
        // Rotate the shapes
        aShapes.forEach(function(sh) {
            sh.rotate();
        });
    }
    // Draw all shapes
    aShapes.forEach(function(sh) {
        sh.draw();
    });
}
// Initialization
function sceneInit() {
    // Prepare canvas and context objects
    canvas = document.getElementById('scene');
    // Canvas resize
    canvas.width = canvas.clientWidth;
    canvas.height = window.innerHeight;
    ctx = canvas.getContext('2d');
    // Add two custom shapes
    var oShape = new Shape();
    oShape.addPoint(-200,-200,50);
    oShape.addPoint(0,0,0);
    oShape.addPoint(-400,200,0);
    oShape.addPoint(200,-400,-50);
    aShapes.push(oShape);
    var oShape2 = new Shape();
    oShape2.addPoint(200,200,-50);
    oShape2.addPoint(0,0,0);
    oShape2.addPoint(400,-200,0);
    oShape2.addPoint(-200,400,50);
    aShapes.push(oShape2);
    // Mouse pointer event handler
    vPointer = new CPointer(canvas);
    // Main scene loop
    setInterval(drawScene, 20);
}
// Window onload init
if (window.attachEvent) {
    window.attachEvent('onload', sceneInit);
} else {
    if (window.onload) {
        var curronload = window.onload;
        var newonload = function() {
            curronload();
            sceneInit();
        };
        window.onload = newonload;
    } else {
        window.onload = sceneInit;
    }
}

In sceneInit we added two shapes. As you have already noticed, in order to handle with mouse events we use an instance of CPointer class. Here is it:

在sceneInit中,我们添加了两个形状。 正如您已经注意到的,为了处理鼠标事件,我们使用CPointer类的实例。 就这个:

js / pointer.js (js/pointer.js)

CPointer = function (canvas) {
    var self = this;
    this.body = document.body;
    this.html = document.documentElement;
    this.elem = canvas;
    this.X = 0;
    this.Y = 0;
    this.Xi = 0;
    this.Yi = 0;
    this.Xr = 0;
    this.Yr = 0;
    this.startX = 0;
    this.startY = 0;
    this.bDrag = false;
    this.bMoved = false;
    this.bDown = false;
    this.bXi = 0;
    this.bYi = 0;
    this.sX = 0;
    this.sY = 0;
    this.left = canvas.offsetLeft;
    this.top = canvas.offsetTop;
    self.elem.onmousedown = function (e) {
        self.bDrag = false;
        self.bMoved = false;
        self.bDown = true;
        self.Xr = e.clientX;
        self.Yr = e.clientY;
        self.X  = self.sX = self.Xr - self.left;
        self.Y  = self.sY = self.Yr - self.top + ((self.html && self.html.scrollTop) || self.body.scrollTop);
    }
    self.elem.onmousemove = function(e) {
        self.Xr = (e.clientX !== undefined ? e.clientX : e.touches[0].clientX);
        self.Yr = (e.clientY !== undefined ? e.clientY : e.touches[0].clientY);
        self.X  = self.Xr - self.left;
        self.Y  = self.Yr - self.top + ((self.html && self.html.scrollTop) || self.body.scrollTop);
        if (self.bDown) {
            self.Xi = self.bXi + (self.X - self.sX);
            self.Yi = self.bYi - (self.Y - self.sY);
        }
        if (Math.abs(self.X - self.sX) > 2 || Math.abs(self.Y - self.sY) > 2) {
            self.bMoved = true;
            if (self.bDown) {
                if (! self.bDrag) {
                    self.startX = self.sX;
                    self.startY = self.sY;
                    self.bDrag = true;
                }
            } else {
                self.sX = self.X;
                self.sY = self.Y;
            }
        }
    }
    self.elem.onmouseup = function() {
        self.bXi = self.Xi;
        self.bYi = self.Yi;
        if (! self.bMoved) {
            self.X = self.sX;
            self.Y = self.sY;
        }
        self.bDrag = false;
        self.bDown = false;
        self.bMoved = false;
    }
}

CPointer = function (canvas) {
    var self = this;
    this.body = document.body;
    this.html = document.documentElement;
    this.elem = canvas;
    this.X = 0;
    this.Y = 0;
    this.Xi = 0;
    this.Yi = 0;
    this.Xr = 0;
    this.Yr = 0;
    this.startX = 0;
    this.startY = 0;
    this.bDrag = false;
    this.bMoved = false;
    this.bDown = false;
    this.bXi = 0;
    this.bYi = 0;
    this.sX = 0;
    this.sY = 0;
    this.left = canvas.offsetLeft;
    this.top = canvas.offsetTop;
    self.elem.onmousedown = function (e) {
        self.bDrag = false;
        self.bMoved = false;
        self.bDown = true;
        self.Xr = e.clientX;
        self.Yr = e.clientY;
        self.X  = self.sX = self.Xr - self.left;
        self.Y  = self.sY = self.Yr - self.top + ((self.html && self.html.scrollTop) || self.body.scrollTop);
    }
    self.elem.onmousemove = function(e) {
        self.Xr = (e.clientX !== undefined ? e.clientX : e.touches[0].clientX);
        self.Yr = (e.clientY !== undefined ? e.clientY : e.touches[0].clientY);
        self.X  = self.Xr - self.left;
        self.Y  = self.Yr - self.top + ((self.html && self.html.scrollTop) || self.body.scrollTop);
        if (self.bDown) {
            self.Xi = self.bXi + (self.X - self.sX);
            self.Yi = self.bYi - (self.Y - self.sY);
        }
        if (Math.abs(self.X - self.sX) > 2 || Math.abs(self.Y - self.sY) > 2) {
            self.bMoved = true;
            if (self.bDown) {
                if (! self.bDrag) {
                    self.startX = self.sX;
                    self.startY = self.sY;
                    self.bDrag = true;
                }
            } else {
                self.sX = self.X;
                self.sY = self.Y;
            }
        }
    }
    self.elem.onmouseup = function() {
        self.bXi = self.Xi;
        self.bYi = self.Yi;
        if (! self.bMoved) {
            self.X = self.sX;
            self.Y = self.sY;
        }
        self.bDrag = false;
        self.bDown = false;
        self.bMoved = false;
    }
}

现场演示

结论 (Conclusion)

Today, we have built the rotating panel where we can draw (with the mouse) using HTML5. I hope you enjoyed our lesson. Good luck and welcome back.

今天,我们已经建立了旋转面板,可以在其中使用HTML5绘制(使用鼠标)。 希望您喜欢我们的课程。 祝你好运,欢迎回来。

翻译自: https://www.script-tutorials.com/pane-for-drawing-in-pseudo-3d/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值