当初在掘金看到那个小熊的登录页面,很多人都很喜欢,于是恬不知耻的说了一句要用canvas来实现一遍,真的是给自己立了个flag,还好结果很糟糕。
各位观众大老爷,这可能是你见过最惨淡的demo了,希望不要嫌弃
首先看看别人的效果图
进行思考- 为什么小熊会跟着输入框的输入而移动。
- 为什么小熊的耳朵会移动到前面
- 小熊的移动规律是什么
做出一些逻辑上的思索与排除
2d- 3d
- 判断文本框的焦点坐标,从而给小熊旋转等动作的数据支持
实现3d的向量类
var eyeLength = 250, centerX = 200/ 2, centerY = 200 / 2;
//设定视距,中心点的坐标
function Vector3(x, y, z) {
this.x = x;
this.y = y;
this.z = z;
this._get2d = function() {
//将空间左边进行缩放后生成平面视图中的坐标
var scale = eyeLength / (eyeLength + this.z);
var x = centerX + this.x * scale;
var y = centerY + this.y * scale;
return { x: x,
y: y };
}
}
复制代码
对物体进行空间的旋转
function rotateX(vec3,angleX) {
var cos = Math.cos(angleX);
var sin = Math.sin(angleX);
var y1 = vec3.y * cos - vec3.z * sin;
var z1 = vec3.z * cos + vec3.y * sin;
vec3.y = y1;
vec3.z = z1;
}
function rotateY(vec3,angleY) {
var cos = Math.cos(angleY);
var sin = Math.sin(angleY);
var x1 = vec3.x * cos - vec3.z * sin;
var z1 = vec3.z * cos + vec3.x * sin;
vec3.x = x1;
vec3.z = z1;
}
复制代码
通过输入向量与角度值,来进行计算,生成坐标在空间旋转后的坐标,此处使用的计算公式为旋转矩阵,矩阵并未单独抽离为单独类。
形状类
看过上一篇2d向量讲解的会知道,我们在使用向量来处理时,是要对每一个向量点进行处理,所以我们没法直接使用如fillRect
等的方法,这里我使用了贝兹曲线来绘制图形。
function shape(option) {
this.points=[];
this.site=new Vector3(0,0,0);
this.create(option);
this.face=[];
this.ctx={};
}
shape.prototype.render = function(ctx) {
this.ctx=ctx;
for(let f =0;f<this.paths.length;f++){
this.face[f]=new Face(this.ctx,this.color,...this.paths[f]);
}
this.face.sort(function(a,b){
return a-b;
})
this.face.forEach( function(face, index) {
face.draw(ctx);
});
}
复制代码
我们通过捕获paths中的向量,生成一个面,然后由面去构成物体,并且在同一形状中进行面重排,是为了使面在绘制的时候依据z(深度)
来进行渲染,产生遮挡的效果。
这样我们将可以通过如下方式来创建形状
var face=new shape({
paths:[[new Vector3(-50,0,4),new Vector3(-50,-70,4),new Vector3(50,-70,4),new Vector3(50,0,4)]],
color:"#3366C"
})
var eyes=new shape({
paths:[[new Vector3(-54,0,6),new Vector3(-54,-8,6),new Vector3(-46,-4,6),new Vector3(-46,0,6)],
[new Vector3(-54,0,6),new Vector3(-54,8,6),new Vector3(-46,4,6),new Vector3(-46,0,6)]],
color:"white"
})
复制代码
对于这种绘制的最快方法,就是先在纸面上进行坐标绘制,再用代码实现。
实现了绘制与变换之后,下一步就是进行交互,这里我使用vue来写的页面,通过监听输入框的文本长度来计算,(文本的长度*字体的大小)+左边距=焦点坐标。
var vm=new Vue({
el:'#bod',
data:{
username:"",//记录输入框
left:0,//距离左边的长度,用于判断旋转何时结束
len:0,//焦点坐标
pos:0,//是否开始选择
dir:'left'//旋转的方向
},
computed:{
//计算文本长度
roate:function (argument) {
this.len=this.username.length;
}
},
watch:{
//监听len的值,判断是增还是减
len:function(o,n){
this.pos=1;
if(nthis.dir='right';
}else {
this.dir='left'
}
}
}
})
复制代码
然后我们就可以把所有的东西交给浏览器了
function anima(){
if(vm.pos===1){//如果可以旋转
if(vm.left//如果长度不够,继续旋转
ctx.clearRect(0,0,200,200);
switch (vm.dir) {//判断方向
case 'right':
face.rotate(0,r);
eyes.rotate(0,r);
face.render(ctx);
eyes.render(ctx);
vm.left+=r/5;
break;
case 'left':
face.rotate(0,-r);
eyes.rotate(0,-r);
face.render(ctx);
eyes.render(ctx);
vm.left+=r/5;
break;
}
}else{//长度达到后则重置数据
vm.left=0;
vm.pos=0;
}
}
requestAnimationFrame(anima)
}
anima();
复制代码
不足
- 画的有点丑,不太可以很清晰的有那种感觉
- 只对单个物体进行了深度渲染,未实现空间深度的渲染
- 向量与形状的功能不完善,使得效果局限性很大
总结
其实这东西挺不好做的,以前用PS或者AE做动效的时候,只想着如何来美化效果,动手实现之后才感觉图形引擎那群人真的牛逼,从开始打算做到简单实现,花了接近半天,中间推倒了很多想法,然后再去整理,虽然最后的效果不怎么样,但是吧,我还是恬不知耻希望可以诱惑到一些大佬们加入canvas的队伍中,一起来写有趣的东西。
不定期更新canvas与svg的相关技术教程,有实战型,也会有主原理型的,2d 2.5d 3d都会包含到,同时涉及的有 线性代数 物理 图形学等相关的基础知识。
欢迎各位客官收藏关注 投硬币包养