rotate 3d基础

本文介绍如何使用HTML5 Canvas实现3D图形的基础知识,并通过实例演示如何创建3D球体、标签云及动态粒子特效。文章涵盖从点到线再到面的3D图形构建过程,同时提供多个示例代码帮助读者理解和实践。

基础

  看了岑安大大的教程学习了3d基础,之前写了篇总结,觉得写的太散废话太多,重写一篇。

  本文需要实现的效果如下:3d球

  岑安的两篇教程写的很棒,但我感觉改变下顺序或许会更好理解。

  我们把画布(此文所讲所见所得均基于canvas)的中心当做是一个空间三维系的中心,画布的x和y轴正方向分别当做三维系的x和y轴的正方向,把垂直画布向内当做z轴正方向,那么三维系大致如下图:

  我们假设空间中有个点在围绕y轴转动,那么转动的轨迹就是个圆;如果画面感强的画,可以想象出就像地球上的某个点由于地球的自转做的向心运动。那么问题来了,如果三维系里的某个点围绕y轴转动确定的角度后,能计算出转动后的三维坐标吗?

  答案是肯定的,空间上的某个点(已知三维坐标)围绕x轴y轴或者z轴转动一定角度后,都能计算出相应的三维坐标。

  过程貌似是个线性代数里的矩阵变换,数学渣渣跪...

  直接把它写在矢量类里了:

// 矢量旋转
Vector3.prototype.rotateX = function(angleX) {
  var cosx = Math.cos(angleX);
  var sinx = Math.sin(angleX);
  var y1 = this.y * cosx - this.z * sinx;
  var z1 = this.y * sinx + this.z * cosx;
  this.y = y1;
  this.z = z1;
};

Vector3.prototype.rotateY = function(angleY) {
  var cosy = Math.cos(angleY);
  var siny = Math.sin(angleY);
  var x1 = this.z * siny + this.x * cosy;
  var z1 = this.z * cosy - this.x * siny;
  this.x = x1;
  this.z = z1;
};

  更近一步,空间上的某个点围绕任意轴转动一定角度,都能计算出转动后的三维坐标。有兴趣的可以参考【自己给自己题目做】:如何在Canvas上实现魔方效果

  那么就好办了,我们在三维空间初始化一些点(当做球心),随意赋给他们xyz坐标值,随意设定旋转角度,那么旋转后的xyz值也能很轻松地得到了!当然有了这些还只是成功了一半,我们的canvas只支持2d,如果支持3d问题就解决了,我们还得把三维降到二维上。我们知道,如果一个球在三维系上围绕y轴转动,球的大小是不会变化的,但是如果体现在二维上,z值越大时,球体在视觉上的体现就是越小(跟z值有关);在三维上,y值是不会变化的,但是在二维视觉上的体现不应该是这样,y值是应该变化的(跟z值有关)。另一方面,球体的任意角度视图都是一个圆形。

  于是我们需要“把z方向扁平化”,即把z轴数值的大小体现在x和y上:

// focalLength 表示当前焦距,一般可设为一个常量
var focalLength = 250;

// 把z方向扁平化
var scale = focalLength / (focalLength + this.z);
this.x2 = this.garden.vpx + this.x * scale;
this.y2 = this.garden.vpy + this.y * scale;
this.radius = this.ballR * scale;

  z方向扁平化使得z方向的值体现在x、y和r的大小以及球的透明度等变量上,一方面由于球体的旋转我们需要时刻调整球体在三维系上的坐标,另一方面在draw的时候需要计算二维上的坐标。

  所以简单的来说:

    1. 初始化球心在三维系上的坐标以及球的颜色半径等属性(三维系原点的设定)
    2. 将三维降到二维,绘制
    3. 根据旋转角度重新计算三维坐标
    4. 将三维降到二维,绘制
    5. 反复重复

  这里还有个注意点就是每帧重绘时,根据小球在三维系的z值排个序,因为后面的会被前面的挡住,所以绘制应该有个顺序。

<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title> rotate 3d</title> <script> window.onload = function() { var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); var garden = new Garden(canvas); // 设置原点(一般为画布中心) garden.setBasePoint(500, 250); // 构造三维系上的点 for(var i = 0; i < 10; i++) garden.createBall(); // 设置监听 garden.setListener(); // 渲染 setInterval(function() {garden.render();}, 1000 / 60); }; function Garden(canvas) { this.canvas = canvas; this.ctx = this.canvas.getContext('2d'); // 三维系在二维上的原点 this.vpx = undefined; this.vpy = undefined; this.balls = []; this.angleY = 0; this.angleX = 0; } Garden.prototype = { setBasePoint: function(x, y) { this.vpx = x; this.vpy = y; }, createBall: function(x, y, z, ballR) { this.balls.push(new Ball(this, x, y, z, ballR)); }, render: function() { this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); this.balls.sort(function (a, b) {return b.z - a.z }); for(var i = 0; i < this.balls.length; i++) { this.balls[i].rotateY(); this.balls[i].rotateX(); this.balls[i].draw(i); } }, setListener: function() { var that = this; document.addEventListener('mousemove', function(event){ var x = event.clientX - that.vpx; var y = event.clientY - that.vpy; that.angleY = -x * 0.0001; that.angleX = y * 0.0001; }); } }; function Ball(garden, x, y, z, ballR) { this.garden = garden; // 三维上坐标 this.x = x === undefined? Math.random() * 200 - 100: x; this.y = y === undefined? Math.random() * 200 - 100: y; this.z = z === undefined? Math.random() * 200 - 100: z; this.r = Math.floor(Math.random() * 255); this.g = Math.floor(Math.random() * 255); this.b = Math.floor(Math.random() * 255); // 三维上半径 this.ballR = ballR === undefined? 10 + Math.random() * 10: ballR; // 二维上半径 this.radius = undefined; // 二维上坐标 this.x2 = undefined; this.y2 = undefined; } Ball.prototype = { // 绕y轴变化,得出新的x,z坐标 rotateY: function() { var cosy = Math.cos(this.garden.angleY); var siny = Math.sin(this.garden.angleY); var x1 = this.z * siny + this.x * cosy; var z1 = this.z * cosy - this.x * siny; this.x = x1; this.z = z1; }, // 绕x轴变化,得出新的y,z坐标 rotateX: function() { var cosx = Math.cos(this.garden.angleX); var sinx = Math.sin(this.garden.angleX); var y1 = this.y * cosx - this.z * sinx; var z1 = this.y * sinx + this.z * cosx; this.y = y1; this.z = z1; }, draw: function(a) { // focalLength 表示当前焦距,一般可设为一个常量 var focalLength = 300; // 把z方向扁平化 var scale = focalLength / (focalLength + this.z); this.x2 = this.garden.vpx + this.x * scale; this.y2 = this.garden.vpy + this.y * scale; this.radius = this.ballR * scale; this.garden.ctx.beginPath(); this.garden.ctx.fillStyle = 'rgba('+this.r+','+this.g+','+this.b+','+ Math.min(1, scale)+')'; this.garden.ctx.arc(this.x2, this.y2, this.radius, 0, Math.PI * 2 , true); this.garden.ctx.fill(); } } </script> </head> <body bgcolor='#000'> <canvas id='canvas' width=1000 height=600 style='background-color:rgb(0,0,0)'> This browser does not support html5. </canvas> </body> </html>
 

3d标签云

  有了以上的基础,可以动手做个3d标签云。理论上3d标签云中的标签应该是个a标签,是可以点击跳转的,这里为了方便,不添加点击跳转功能了(直接在canvas上)。

  思路应该很简单,在一个球面上初始化一些点,把球换成文字就可以了。

  怎样构造球体获得坐标?充分认识到了数学的必要性...

  不懂数学,试了下觉得角度的取值有两种方法:

  • (0 <= θ <= PI && 0 <= Φ <= 2 * PI)
  • (0 <= θ <= 2 * PI && 0 <= Φ <= PI)

  可以验证x*x+y*y+z*z确实等于r*r。

  有了公式,我们可以枚举角度获得坐标。

  如果有n个点,我需要平均分配在球面上,怎么做?我们引入第二个公式:

  初始化坐标:

var all = 100;  // 100个
for(var i = 1; i <= all; i++) {
  var a1 = Math.acos(1 - (2 * i) / all);
  var a2 = a1 * Math.sqrt(all * Math.PI);
  var x = 150 * Math.sin(a1) * Math.cos(a2);  // r = 150
  var y = 150 * Math.sin(a1) * Math.sin(a2);
  var z = 150 * Math.cos(a1);
  garden.createBall(x, y, z);
}
 
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title> rotate 3d</title> <script> window.onload = function() { var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); var garden = new Garden(canvas); // x=r*sinθ*cosΦ y=r*sinθ*sinΦ z=r*cosθ; // θ = arccos((2*num-1)/all - 1); // Φ = θ*sqrt(all * π); // for(var i = 0; i <= 90; i += 5) // for(var j = 90; j <= 90; j += 5) { // var a1 = Math.PI / 180 * i; // var a2 = Math.PI / 180 * j; // var x = 150 * Math.sin(a1) * Math.cos(a2); // var y = 150 * Math.sin(a1) * Math.sin(a2); // var z = 150 * Math.cos(a1); // garden.createBall(x, y, z); // } var all = 30; for(var i = 1; i <= all; i++) { var a1 = Math.acos(1- (2 * i) / all); var a2 = a1 * Math.sqrt(all * Math.PI); var x = 150 * Math.sin(a1) * Math.cos(a2); var y = 150 * Math.sin(a1) * Math.sin(a2); var z = 150 * Math.cos(a1); garden.createBall(x, y, z); } document.addEventListener("mousemove" , function(event){ var x = event.clientX - garden.vpx; var y = event.clientY - garden.vpy; garden.angleY = -x * 0.0001; garden.angleX = y * 0.0001; }); setInterval(function() {garden.render();}, 1000/60); }; function Garden(canvas, vpx, vpy) { this.canvas = canvas; this.ctx = this.canvas.getContext('2d'); // 三维系在二维上的原点 this.vpx = vpx === undefined? 500: vpx; this.vpy = vpy === undefined? 250: vpy; this.balls = []; this.angleY = 0; this.angleX = 0; } Garden.prototype = { createBall: function(x, y, z) { this.balls.push(new Ball(this, x, y, z)); }, render: function() { this.ctx.clearRect(0,0,1000,500) this.balls.sort(function (a, b) {return b.z-a.z }) for(var i = 0; i < this.balls.length; i++) { this.balls[i].rotateY(); this.balls[i].rotateX(); this.balls[i].draw(); } } }; function Ball(garden, x, y, z, angleX, angleY, ballR) { this.garden = garden; // 三维下坐标 this.x = x === undefined? Math.random() * 200 - 100: x; this.y = y === undefined? Math.random() * 200 - 100: y; this.z = z === undefined? Math.random() * 200 - 100: z; this.r = Math.floor(Math.random() * 255); this.g = Math.floor(Math.random() * 255); this.b = Math.floor(Math.random() * 255); this.fontSize = (10 + 10 * Math.random()); this.angleX = 0; // this.angleX = angleX || Math.PI / 200; this.angleY = angleY === undefined? Math.PI / 100: angleY; // 三维上半径 this.ballR = 1; // 二维上半径 this.radius = undefined; // 二维上坐标 this.x2 = undefined; this.y2 = undefined; } Ball.prototype = { // 绕y轴变化,得出新的x,z坐标 rotateY: function() { var cosy = Math.cos(this.garden.angleY); var siny = Math.sin(this.garden.angleY); var x1 = this.z * siny + this.x * cosy; var z1 = this.z * cosy - this.x * siny; this.x = x1; this.z = z1; }, // 绕x轴变化,得出新的y,z坐标 rotateX: function() { var cosx = Math.cos(this.garden.angleX); var sinx = Math.sin(this.garden.angleX); var y1 = this.y * cosx - this.z * sinx; var z1 = this.y * sinx + this.z * cosx; this.y = y1; this.z = z1; }, draw: function() { // focalLength 表示当前焦距,一般可设为一个常量 var focalLength = 300; // 把z方向扁平化 var scale = focalLength / (focalLength + this.z); this.x2 = this.garden.vpx + this.x * scale; this.y2 = this.garden.vpy + this.y * scale; this.radius = this.ballR * scale; this.garden.ctx.beginPath(); this.garden.ctx.fillStyle = 'rgba('+this.r+','+this.g+','+this.b+','+ Math.min(1, scale)+')'; // this.garden.ctx.arc(this.x2, this.y2, this.radius, 0, Math.PI * 2 , true); this.garden.ctx.font = 'bold ' + this.fontSize * scale+ 'px serif'; this.garden.ctx.textAlign = "left"; this.garden.ctx.textBaseline = "top"; this.garden.ctx.fillText('博客园', this.x2, this.y2); this.garden.ctx.fill(); } } </script> </head> <body bgcolor='#000'> <canvas id='canvas' width=1000 height=500 style='background-color:rgb(0,0,0)'> This browser does not support html5. </canvas> </body> </html>
  其实利用这个球面公式还能做很多好看的效果,例如: 3D旋转球(W·Axes)
 

点-线-面

  有了点,根据点-线-面的原理,线和面也呼之欲出了。

  稍微动点脑筋搞点创意,就能搞点看起来炫酷(实际上没花头)的小东东。

  比如下面这样:

<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title> rotate 3d</title> <script> window.onload = function() { var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); var garden = new Garden(canvas); // x=r*sinθ*cosΦ y=r*sinθ*sinΦ z=r*cosθ; // θ = arccos((2*num-1)/all - 1); // Φ = θ*sqrt(all * π); // for(var i = 0; i <= 180; i += 10) // for(var j = 0; j <= 360; j += 10) { // var a1 = Math.PI / 180 * i; // var a2 = Math.PI / 180 * j; // var x = 150 * Math.sin(a1) * Math.cos(a2); // var y = 150 * Math.sin(a1) * Math.sin(a2); // var z = 150 * Math.cos(a1); // garden.createBall(x, y, z); // } for(var i = 10; i <= 30; i += 0.2) { var ans = getHeartPoint(i); garden.createBall(ans[0], ans[1], -50); garden.createBall(ans[0], ans[1], 0); // ctx.beginPath(); // ctx.arc(ans[0], ans[1], 1, 0, Math.PI * 2, true); // ctx.fill(); } function getHeartPoint(angle) { var t = angle / Math.PI; var sx = 10; var sy = -10; var x = sx * (16 * Math.pow(Math.sin(t), 3)); var y = sy * (13 * Math.cos(t) - 5 * Math.cos(2 * t) - 2 * Math.cos(3 * t) - Math.cos(4 * t)); return new Array(x, y); } // var all = 50; // for(var i = 1; i <= all; i++) { // garden.createBall(); // } document.addEventListener("mousemove" , function(event){ var x = event.clientX - garden.vpx; var y = event.clientY - garden.vpy; garden.angleY = -x * 0.0001; garden.angleX = y * 0.0001; }); setInterval(function() {garden.render();}, 1000/60); // for(var i = 0; i < garden.balls.length; i++) // console.log("%d %d %d", garden.balls[i].x, garden.balls[i].y, garden.balls[i].z) }; function Garden(canvas, vpx, vpy) { this.canvas = canvas; this.ctx = this.canvas.getContext('2d'); // 三维系在二维上的原点 this.vpx = vpx === undefined? 500: vpx; this.vpy = vpy === undefined? 250: vpy; this.balls = []; this.angleY = Math.PI / 180; this.angleX = 0; } Garden.prototype = { createBall: function(x, y, z) { this.balls.push(new Ball(this, x, y, z)); }, render: function() { this.ctx.clearRect(0,0,1600,700) // this.balls.sort(function (a, b) {return b.z-a.z }) for(var i = 0; i < this.balls.length; i++) { this.balls[i].rotateY(); this.balls[i].rotateX(); this.balls[i].draw(i); } } }; function Ball(garden, x, y, z, angleX, angleY, ballR) { this.garden = garden; // 三维下坐标 this.x = x === undefined? Math.random() * 800 : x; this.y = y === undefined? Math.random() * 200 : y; this.z = z === undefined? Math.random() * 400 - 200: z; this.r = Math.floor(Math.random() * 255); this.g = Math.floor(Math.random() * 255); this.b = Math.floor(Math.random() * 255); this.fontSize = (10 + 10 * Math.random()); // this.angleX = 0; // this.angleX = angleX || Math.PI / 200; // this.angleY = angleY === undefined? Math.PI / 100: angleY; // 三维上半径 this.ballR = 1; // 二维上半径 this.radius = undefined; // 二维上坐标 this.x2 = undefined; this.y2 = undefined; } Ball.prototype = { // 绕y轴变化,得出新的x,z坐标 rotateY: function() { var cosy = Math.cos(this.garden.angleY); var siny = Math.sin(this.garden.angleY); var x1 = this.z * siny + this.x * cosy; var z1 = this.z * cosy - this.x * siny; this.x = x1; this.z = z1; }, // 绕x轴变化,得出新的y,z坐标 rotateX: function() { var cosx = Math.cos(this.garden.angleX); var sinx = Math.sin(this.garden.angleX); var y1 = this.y * cosx - this.z * sinx; var z1 = this.y * sinx + this.z * cosx; this.y = y1; this.z = z1; }, draw: function(a) { // focalLength 表示当前焦距,一般可设为一个常量 var focalLength = 300; // 把z方向扁平化 var scale = focalLength / (focalLength + this.z); this.x2 = this.garden.vpx + this.x * scale; this.y2 = this.garden.vpy + this.y * scale; this.radius = this.ballR * scale; this.garden.ctx.beginPath(); this.garden.ctx.fillStyle = 'rgba('+this.r+','+this.g+','+this.b+','+ Math.min(1, scale)+')'; this.garden.ctx.arc(this.x2, this.y2, this.radius, 0, Math.PI * 2 , true); // this.garden.ctx.font = 'bold ' + this.fontSize * scale+ 'px serif'; // this.garden.ctx.textAlign = "left"; // this.garden.ctx.textBaseline = "top"; // this.garden.ctx.fillText('博客园', this.x2, this.y2); this.garden.ctx.fill(); if(a % 4 === 3) { var x1 = this.garden.balls[a - 3].x2; var y1 = this.garden.balls[a - 3].y2; var x2 = this.garden.balls[a - 2].x2; var y2 = this.garden.balls[a - 2].y2; var x3 = this.garden.balls[a - 1].x2; var y3 = this.garden.balls[a - 1].y2; var x4 = this.x2; var y4 = this.y2; this.garden.ctx.beginPath(); // this.garden.ctx.strokeStyle = 'rgba('+this.r+','+this.g+','+this.b+','+ Math.min(1, scale)+')'; this.garden.ctx.moveTo(x1, y1); this.garden.ctx.lineTo(x2, y2); this.garden.ctx.lineTo(x4, y4); this.garden.ctx.lineTo(x3, y3); this.garden.ctx.closePath(); this.garden.ctx.fill(); // debugger; } } } </script> </head> <body bgcolor='#000'> <canvas id='canvas' width=1200 height=500 style='background-color:rgb(0,0,0)'> This browser does not support html5. </canvas> </body> </html>
  或者这样: 
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title> rotate 3d</title> <script> window.onload = function() { var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); var garden = new Garden(canvas); for(var i = 10; i <= 30; i += 0.2) { var ans = getHeartPoint(i); garden.createBall(ans[0], ans[1], -50); garden.createBall(ans[0], ans[1], 0); } function getHeartPoint(angle) { var t = angle / Math.PI; var sx = 10; var sy = -10; var x = sx * (16 * Math.pow(Math.sin(t), 3)); var y = sy * (13 * Math.cos(t) - 5 * Math.cos(2 * t) - 2 * Math.cos(3 * t) - Math.cos(4 * t)); return new Array(x, y); } document.addEventListener("mousemove" , function(event){ var x = event.clientX - garden.vpx; var y = event.clientY - garden.vpy; garden.angleY = -x * 0.0001; garden.angleX = y * 0.0001; }); setInterval(function() {garden.render();}, 1000/60); }; function Garden(canvas, vpx, vpy) { this.canvas = canvas; this.ctx = this.canvas.getContext('2d'); // 三维系在二维上的原点 this.vpx = vpx === undefined? 500: vpx; this.vpy = vpy === undefined? 250: vpy; this.balls = []; this.angleY = Math.PI / 180; this.angleX = 0; } Garden.prototype = { createBall: function(x, y, z) { this.balls.push(new Ball(this, x, y, z)); }, render: function() { this.ctx.clearRect(0,0,1600,700) // this.balls.sort(function (a, b) {return b.z-a.z }) // alert(this.balls.length) for(var i = 0; i < this.balls.length; i++) { this.balls[i].rotateY(); this.balls[i].rotateX(); this.balls[i].draw(i); } } }; function Ball(garden, x, y, z, angleX, angleY, ballR) { this.garden = garden; // 三维下坐标 this.x = x === undefined? Math.random() * 800 : x; this.y = y === undefined? Math.random() * 200 : y; this.z = z === undefined? Math.random() * 400 - 200: z; this.r = Math.floor(Math.random() * 255); this.g = Math.floor(Math.random() * 255); this.b = Math.floor(Math.random() * 255); this.fontSize = (10 + 10 * Math.random()); // 三维上半径 this.ballR = 1; // 二维上半径 this.radius = undefined; // 二维上坐标 this.x2 = undefined; this.y2 = undefined; } Ball.prototype = { // 绕y轴变化,得出新的x,z坐标 rotateY: function() { var cosy = Math.cos(this.garden.angleY); var siny = Math.sin(this.garden.angleY); var x1 = this.z * siny + this.x * cosy; var z1 = this.z * cosy - this.x * siny; this.x = x1; this.z = z1; }, // 绕x轴变化,得出新的y,z坐标 rotateX: function() { var cosx = Math.cos(this.garden.angleX); var sinx = Math.sin(this.garden.angleX); var y1 = this.y * cosx - this.z * sinx; var z1 = this.y * sinx + this.z * cosx; this.y = y1; this.z = z1; }, draw: function(a) { // focalLength 表示当前焦距,一般可设为一个常量 var focalLength = 300; // 把z方向扁平化 var scale = focalLength / (focalLength + this.z); this.x2 = this.garden.vpx + this.x * scale; this.y2 = this.garden.vpy + this.y * scale; this.radius = this.ballR * scale; this.garden.ctx.beginPath(); this.garden.ctx.fillStyle = 'rgba('+this.r+','+this.g+','+this.b+','+ Math.min(1, scale)+')'; // this.garden.ctx.arc(this.x2, this.y2, this.radius, 0, Math.PI * 2 , true); // this.garden.ctx.font = 'bold ' + this.fontSize * scale+ 'px serif'; // this.garden.ctx.textAlign = "left"; // this.garden.ctx.textBaseline = "top"; // this.garden.ctx.fillText('博客园', this.x2, this.y2); // this.garden.ctx.fill(); if(a % 4 === 3 || (a % 4 === 1 && a != 1)) { var x1 = this.garden.balls[a - 3].x2; var y1 = this.garden.balls[a - 3].y2; var x2 = this.garden.balls[a - 2].x2; var y2 = this.garden.balls[a - 2].y2; var x3 = this.garden.balls[a - 1].x2; var y3 = this.garden.balls[a - 1].y2; var x4 = this.x2; var y4 = this.y2; this.garden.ctx.beginPath(); this.garden.ctx.fillStyle = 'rgba('+'255'+','+'0'+','+'0'+','+ Math.min(1, scale)+')'; this.garden.ctx.moveTo(x1, y1); this.garden.ctx.lineTo(x2, y2); this.garden.ctx.lineTo(x4, y4); this.garden.ctx.lineTo(x3, y3); this.garden.ctx.closePath(); console.log('%d %d %d %d %d %d %d %d', x1, y1, x2, y2, x4, y4, x3, y3) this.garden.ctx.fill(); this.garden.ctx.beginPath(); this.garden.ctx.strokeStyle = 'rgba('+'255'+','+'0'+','+'0'+','+ Math.min(1, scale)+')'; this.garden.ctx.moveTo(x4, y4); this.garden.ctx.lineTo(x3, y3); this.garden.ctx.lineWidth = 1; // this.garden.ctx.stroke(); debugger; } if(a !== 201) return; this.garden.ctx.fillStyle = 'red' this.garden.ctx.beginPath(); var x = this.garden.balls[0].x2; var y = this.garden.balls[0].y2; this.garden.ctx.moveTo(x, y); for(var i = 2; i <= 200; i+=2) { var x = this.garden.balls[i].x2; var y = this.garden.balls[i].y2; this.garden.ctx.lineTo(x, y); } this.garden.ctx.closePath(); this.garden.ctx.fill(); this.garden.ctx.beginPath(); var x = this.garden.balls[1].x2; var y = this.garden.balls[1].y2; this.garden.ctx.moveTo(x, y); for(var i = 3; i <= 201; i+=2) { var x = this.garden.balls[i].x2; var y = this.garden.balls[i].y2; this.garden.ctx.lineTo(x, y); } this.garden.ctx.closePath(); this.garden.ctx.fill(); } } </script> </head> <body bgcolor='#000'> <canvas id='canvas' width=1000 height=500 style='background-color:rgb(0,0,0)'> This browser does not support html5. </canvas> </body> </html>
  再添点东西加点文字音效,表个白什么的还是不错的。
  更多demo:
  不得不感叹数学的奇妙,有时改变一个数值就能出现完全不同的效果。
  更多数学公式请参考: Famous Curves Index
 

总结

  学习3d主要是为了做粒子特效。

  这只是很基础的入门,我也只会这么点,有更进一步兴趣的可以参考岑安当耐特W·Axes或者miloyip的博客。

  岑安的JCanvas也可以看看,添加了很多事件代理。

转载于:https://www.cnblogs.com/lessfish/p/4287187.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值