當初在掘金看到那個小熊的登入頁面,很多人都很喜歡,於是恬不知恥的說了一句要用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(n<o){
this.dir='right';
}else {
this.dir='left'
}
}
}
})
複製程式碼
然後我們就可以把所有的東西交給瀏覽器了
function anima(){
if(vm.pos===1){//如果可以旋轉
if(vm.left<r){//如果長度不夠,繼續旋轉
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都會包含到,同時涉及的有 線性代數 物理 圖形學等相關的基礎知識。
歡迎各位客官收藏關注 投硬幣包養