《每周一點canvas動畫》——3維環境搭建

2022-01-05 HTML5cn

每周一點canvas動畫代碼文件

在上一篇《每周一點canvas動畫》——從2D到3D中,我們討論了要在2D的平面實現3D的效果,是一件多麼複雜的事情。但是對於一些簡單的3D效果,使用webGL不僅有殺雞用牛刀的感覺,而且瀏覽器的兼容性也是一個很大的問題。所以,我們考慮在2D的canvas中去模擬3D的效果,將其作為我們項目中的降級方案。也許你對在2D的canvas中去模仿3D的效果保有懷疑,這裡我先給一個小小的demo,讓你直觀的感受下,canvas模擬的3D效果到底如何?


是不是很逼真,立體效果不錯吧!我記得前段時間淘寶首頁有一個簡單的3D效果(可是我沒找到,不過有那麼個印象),以為用了webGL。其實,canvas完全就可以模擬。下面我們介紹3D環境的搭建。

1.坐標系統


前面部分的動畫內容之所以是2維的,是因為我們所有的動畫都基於一個2維坐標系統。要實現3維的動畫效果,除了x軸,y軸,我們還需要另一條坐標軸—— z軸。但是,canvas先天是不具備這條坐標軸的。所以,我們需要手工的去設定這條坐標軸。

在坐標軸的設定上,我們有兩種選擇,如下圖所示:



第一種叫做左手坐標系(左手的食指,中指,大拇指三者垂直,大拇指指向自己),第二種叫做右手坐標系(右手的食指,中指,大拇指三者垂直,大拇指指向外面)。他們之間的區別如圖所示,z軸的指向不同。以右手坐標為例,反映到物體的表現上(如果只是考慮物體的大小),當物體朝著z軸的正向運動,那麼我們會看到物體變得越來越小。就如第一幅效果圖中展示的那樣,當物體朝著負方向運動的時候。我們可以看到物體變得越來越大,產生一種朝我們迎面飛來的感覺。另外,在本文中我們默認使用的是右手坐標系。

2.透視(perspective)


2.1 概念

不管你是叫他景深也好,透視也罷。perspective是3D場景中最重要的概念之一,如果你使用過three.js,就會發現camera中有一個參數,就是perspective。另一個你很熟悉的場景,恐怕就是css3中的perspective了吧!如果你對這其中的任何一個有了解,那麼perspective的概念就很好理解了。

perspective的作用是確定物體是靠近我們,還是遠離我們。因為在2維的空間中,我們只需要兩個坐標就可以確定一個物體在平面上的位置。但是,在3維的環境中這是行不通的,兩個物體可能具有相同的x坐標,y坐標。但是只要z坐標不相同,我們就不能判斷他們兩的位置是不是重合的。

那麼,怎麼在2維的平面去體現透視效果呢?各位看官請試想一下,當籃球從遠處飛向你的時候(這裡如果我們只考慮一個元素——籃球大小),籃球是不是越來越大呢,當你把籃球扔出去,它是不是越來越小呢!Ok,就是這個理。在2維的平面,我們想要讓物體感覺向我們走來,就放大它,同理遠離的效果就是讓它變小。


除了讓它的大小發生變化。另一個比較重要的點是:當物體遠離直至消失的過程中,要想模擬三維的消失效果,我們必須讓物體的x坐標,y坐標向消失點移動。這個消失點你可以理解為汽車向遠方駛去,最終它會逐漸變成黑點消失在地平線上,這個黑點就是消失點。


所以總結下來,我們在perspective這一塊要做兩件事:

放大或者縮小物體

讓它靠近或者遠離消失點

2.2 公式


上面的概念中我們了解了要想形成透視的效果,我們需要做兩件事。下圖展示了一個側面視圖,人眼代表觀察點,藍色的球體代表屏幕中的物體,人眼距離屏幕的距離為fl,物體與屏幕之間有一段距離z值(這個值在成像的時候並不存在,反映到物體大小的變化上)


物體的大小與這fl,z兩者之間滿足下面的關係:

scale = fl / (fl + z) = 1 / (1 + z/fl)

fl的值就如css3中我們設定的perspective值。 同理,這裡也是我們自己設定的一個值,200,300,500都無所謂啦。從公式中我們可以得出:scale的值一般情況下範圍為(0,1.0),這個值之後會用在縮放物體的比例與靠近消失點的比例。試想兩種比較極端的情況:當z軸無限大的時候(也就是朝著z軸的正向持續運動),scale的值就會趨近於0。當z的距離趨近於-fl的時候,scale的值就會變得很大,就像是戳進我們的眼裡。

以我們前面使用的小球為例,在draw方法上我們定義

context.scale(this.scaleX, this.scaleY)

當縮放比例確定後,一方面我們確定了球體大小的變化,另一方面我們用物體的坐標乘以這個比例就可以得到物體的新坐標。

可能這樣說比較抽象,我們假定fl=200,這時物體的z坐標等於0,由公式可得scale=1.0,那麼物體的大小不變,物體的位置不變。如果z=200,那麼scale=0.5。物體的大小變為原來的1/2,同時我們要讓它現在的坐標乘以縮放比例scale,得到新的位置。如果原來的為(200,300),那麼新坐標就為(100,150)。具體效果如下圖:


3.代碼實現


先上效果圖


這裡我們讓小球的位置跟隨滑鼠移動,通過鍵盤的上下鍵控制小球在z軸上的距離。

<canvas id="canvas" width="500" height="400" style="background:#000;"></canvas>   <script src="../js/utils.js"></script>   <script src="../js/ball.js"></script>   <script>       window.onload = function(){           var canvas = document.getElementById('canvas'),               context = canvas.getContext('2d'),               ball = new Ball(40, "red"),               mouse = utils.captureMouse(canvas);                     var xpos = 0,                 //物體的3D坐標               ypos = 0,               zpos = 0,               fl = 250,                 //距離屏幕的距離(焦距)               vpX = canvas.width/2,    //消失點               vpY = canvas.height/2;                     window.addEventListener('keydown', function(e){               if(e.keyCode === 38){ //up                   zpos += 5;               }else if(e.keyCode === 40){                   zpos -= 5;               }           }, false);                     (function drawFrame(){               window.requestAnimationFrame(drawFrame, canvas);               context.clearRect(0, 0, canvas.width, canvas.height);                             if(zpos > -fl){                    var scale = fl/(fl + zpos);       //縮放比列                           xpos = mouse.x - vpX;                           ypos = mouse.y - vpY;                   ball.scaleX = ball.scaleY = scale; //物體大小變化                   ball.x = vpX + xpos*scale;          //新坐標                   ball.y = vpY + ypos*scale;                   ball.visible = true;                 //物體可見               }else{                   ball.visible = false               }                             if(ball.visible){                   ball.draw(context);             }           }())           }

代碼相對來說比較簡單。首先,我們設定物體的3D坐標xpos,ypos,zpos,初始默認為0。然後設定焦距fl = 250,最後設置消失點(vpX, vpY)。這裡需要注意的地方是,我們設置的消失點為畫布的中心。如果不這樣做,物體就會向畫布的左上角(0,0)處匯集,這並不是我們想要的效果。

接下來,在動畫循環中我們根據公式計算縮放比例scale,然後作用於ball的scale上,最後計算物體的新位置。這裡有個值得注意的點是,我們在外層加了一個判定條件(zpos > -fl),這樣做的目的是當物體太大的時候,超出了canvas畫布我們就不再繪製它。

這一節的內容是整個3維效果的核心,後面的所有效果都是基於此。所以請務必弄明白!

原文連結

https://segmentfault.com/a/1190000006614206

原作者

我仍舊在這裡


相關焦點

  • 《每周一點canvas動畫》——3D物理效果
    在上一節《每周一點canvas動畫》——3維環境搭建中我們詳細的介紹了要想在2D的畫布上實現立體效果,需要做哪些事情。
  • 《每周一點canvas動畫》——3D點線與水波動畫
    每周一點canvas動畫代碼文件在上一章中,我們介紹了很多在三維環境下物體的運動效果。
  • 《每周一點canvas動畫》——差分函數的妙用
    每周一點canvas動畫代碼文件好像上次更新還是十一前,這唰唰唰的就過去大半個月了,現在更新我也沒什麼不好意思的。
  • 每周一點 canvas 動畫丨差分函數的妙用(文末有福利)
    每周一點 canvas 動畫代碼文件:https://github.com/supperjet/H5-Animation/tree/master
  • Canvas 動畫製作
    學過SVG的童鞋應該知道它是可以製作動畫,那麼Canvas是否能製作動畫呢?答案是肯定的。所以今天我們就給大家來介紹一下Canvas製作動畫。Canvas動畫製作原理簡單一句話概括:不斷的繪製與清除。Canvas實現動畫步驟(不斷循環)1、更新繪製的對象(比如位置的移動)2、清除畫布3、在畫布上重新繪製對象Canvas 動畫相關命令clearRect方法context.clearRect
  • HTML5 Canvas 動畫實例
    動畫的概念及原理1.動畫動畫是通過一幅幅靜止的,內容不同的畫面(即幀)快速播放使人們在視覺上產生運動的感覺。這是利用了人類眼睛的視覺暫留原理。利用人的這種生理特性可製作出具有高度想像力和表現力的動畫影片。
  • Canvas 動畫的性能優化實踐
    在實現這個動畫的過程中加深了對 canvas 動畫的一些了解,在這裡我僅是拋磚引玉的分享一下,歡迎各位大佬批評。代碼已上傳至 github 【https://github.com/wanqihua/blog】,感興趣的可以 clone 代碼到本地運行。
  • canvas繪製折線路徑動畫
    image.png其中的效果是一個折線路徑動畫效果,如下圖所示:動畫.gif要實現以上路徑動畫,一般可以使用svg的動畫功能。或者使用canvas繪製,結合路徑數學計算來實現。其實在本案例中,雖然是折線,但是整體的運動方向總是從左往右的,所以可以用從左往右的漸變來近似模擬既可以:function createGradient(ctx,x0,y0,x1,y1){          var grd = ctx.createLinearGradient(x0,y0,x1,y1);           grd.addColorStop(0,'#129ab3'
  • 打造高大上的Canvas粒子動畫
    1   繪製粒子輪廓圖首先要在canvas畫布上繪製一個由粒子組成的輪廓圖,記錄下每一個粒子的坐標,這樣才能有後續的動畫。1. 創建一個<canvas>元素,並獲取Canvas畫布渲染上下文
  • 超炫酷HTML5 Canvas蝴蝶飛舞動畫
    收錄於話題 #炫酷動畫 插件簡介還記得早些時候我們為大家分享過一款非常炫酷的HTML5蝴蝶3D動畫,它是基於HTML5和SVG實現的,效果十分逼真。
  • 必備的Canvas接口和動畫效果大全
    >ctx.lineWidth = 3;ctx.lineCap = "round";ctx.lineJoin = "round";ctx.setLineDash([15, 5]);ctx.stroke();上面代碼中,線條的寬度為 3,線條的末端和交點都改成圓角,並且設置為虛線。
  • HTML5 Canvas火焰畫筆動畫
    收錄於話題 #炫酷動畫 插件簡介在上一篇文章中,我們分享了一個利用HTML5技術在Canvas上實現的煙花動畫,效果非常震撼。
  • 簡單的 canvas 翻角效果
    右上角需要從無的狀態撕開一個標記,且有動畫過程,上圖是實現的效果圖,不是gif。對這個翻角效果的難點在於沒有翻開的時候露出的是dom下面的內容,實現角度來說純dom + css動畫的設計方案並沒有相出一個好的對策: 於是撿起了好久之前學的入門級別的canvas:下面說一下實現思路。
  • 7款超華麗的HTML5 Canvas文字動畫特效
    在線演示/源碼下載2、超華麗CSS3 3D五彩發光文字動畫不久前我們已經為大家介紹過一些炫酷的CSS3文字動畫和HTML5文字特效,有些都非常不錯,比如最近剛分享的CSS3文字跳動旋轉動畫以及HTML5 Canvas幻彩火焰文字特效。
  • 數學與canvas碰撞出環形進度條
    設計稿截圖如下:我的第一反應還是找現成的組件,市面上很多組件都實現了前3點,獨獨沒找到能畫進度圓點的組件,不然稍加定製也能復用。既然沒有現成的組件,只有自己用vue + canvas擼一個了。"canvasDemo" :width="canvasSize" :height="canvasSize" /></template>獲取繪圖上下文getContext('2d')方法返回一個用於在canvas上繪圖的環境,支持一系列2d繪圖API。
  • Canvas在超級瑪麗遊戲中的應用
    我們需要大致截取瑪麗的大小, 通過 cycle 鎖定瑪麗在動畫中的位置。在合成中, 我們只需要讓前面 8 個動作循環播放即可實現瑪麗的一個奔跑動作了。第一種方案比較簡單, 因此我們就選擇第二種比較複雜一點的方案。canvas 中可以調用 scale 方法按照比例尺調整然後繪製。此方法有兩個參數, 第一個參數用於設置水平方向比例尺, 另外一個設置垂直方向的比例尺。
  • 維他檸檬茶聯名鬥羅大陸動畫???我次元壁裂開了
    先不說出圈不出圈,但可以說明維他檸檬茶在某一程度上,確實讀懂了年輕人。大家也知道,身處應接不暇的品牌營銷叢林,身處每天都有新熱點的市場環境中,能吸引有效關注,在年輕人的主陣地吸引注意力、擁抱圈層,讓用戶愛上產品,然後轉化成購買力,這是很不容易的。但又不得不去做,因為但只有進入了對方的圈層,才會讓用戶具有歸屬感。
  • Canvas入門實戰之實現一個圖形驗證碼
    本文轉載自【微信公眾號:趣談前端,ID:beautifulFront】經微信公眾號授權轉載,如需轉載與原文作者聯繫本文主要介紹用canvas實現圖形驗證碼的一些思路以及如何用javascript面向對象的方式更友好的實現canvas的功能,關於canvas的一些基本使用方法和API我整理了一個思維導圖,大家感興趣的可以參考學習
  • Python環境搭建和sublime text 3配置(新手向)
    Python的環境搭建是Python學習的第一步,關於這一點,網上有很多的資源,方法五花八門
  • Canvas 實現刮刮卡
    soulighter兩圖形中重疊部分作加色處理實現思路2.1 效果013_實現效果2.2 原理在頁面上放一個 div 容器,設置這個 div 的寬高、把機器貓的圖片設為背景,在 div 中放一個 canvas 標籤,設置 canvas 的寬高和父容器 div 的一樣。獲取 canvas 的 context 對象繪製一個以灰色為背景寬高和 canvas 寬高相同的矩形,這樣機器貓背景圖就被遮住了,只能看見一個灰色的背景。