做者: 首席填坑官∙蘇南
來源: @IT·平頭哥聯盟
交流羣:91259409五、公衆號:honeyBadger8
。本文原創,著做權歸做者全部,轉載請註明原連接及出處
靈感來源於前些天撿到錢了,就想着是時候給本身買輛車了,工做這麼多年了應該對本身好一點,在網上搜索了一下看到這個車型。其實幾年前是買過一輛的,可是不到一個月就被人偷了,傷心了很久。此次必定鎖好,上三把鎖保證小偷再也偷不走了,因而我拿着錢去買了些益力多,跟同事分享了,心情仍是比較愉悅的。—— @IT·平頭哥聯盟,我是首席填坑官
∙蘇南(South·Su) ^_^~javascript
但想來做爲一名程序(嗯,仍是個菜鳥,專業首席填坑官哦😇),車基本是用不上的啦,爲啥?由於有改不完的bug,記得剛畢業那時候最大的夢想是:「撩個妹子 攜手仗劍天涯,懲奸除惡、劫富濟貧,快意人生~」,無奈一入IT深似海,今後BUG改不完啊。因此仍是多學習吧,這不就學着畫了個車知足一下本身的內心安慰,在這裏把你們一塊兒分享一下,唉,有點扯偏了~,你們先來看一下最終的效果圖吧!html
效果已經看了到,有沒有感受很牛B??其實也就通常般啦~,接下來就讓我帶你們一塊兒分解一下它的實現過程吧
canvas
中文名中:畫布,它就跟咱們在紙上畫畫同樣,畫某樣東西以前,咱們要先學會構思、拆解你要畫的東西,就跟汽車、手機等東西同樣,一個成品都是由不少零件組成的,當你拆解開來,一點點完成再組裝的,就會變的容易的多。前端
繪製地平線 :java
車的速度
與周圍的事物、建築、人產生一個交差,那種感受是很刺激的,那麼咱們也來加一點東西,讓動畫看起來更豐富一些,我選擇了 三條線,線自己有個漸變過渡的效果,比純色要靈動些動畫看起來更逼真,並且初始它是不在畫布範圍內的,這個點要注意一下;所謂的動畫
,也是由一張張靜態圖組成,而後快速過渡,讓視覺造成了視差,最後欺騙了大腦,我看見動畫了……lineTo
、strokeStyle
、stroke
、restore
等,這裏不一一講解了,若有不瞭解可自行查看 w3school API,horizon(){ /** * 輪子的底部,也稱地平線: 1.清除畫布 2.畫一條直線,且高度6px 本文@IT·平頭哥聯盟-首席填坑官∙蘇南分享,非商業轉載請註明原連接及出處 */ this.wheelPos = []; this.ctx.save(); this.ctx.clearRect(0, 0, this.canvasW, this.canvasH); let horizonX = 0,horizonY = this.canvasH-100; this.ctx.beginPath(); this.ctx.strokeStyle = this.color; this.ctx.lineWidth=6; this.ctx.moveTo(horizonX,horizonY); this.ctx.lineTo(this.canvasW,horizonY); this.ctx.closePath(); this.ctx.stroke(); Array.from({length:5}).map((k,v)=>{ let dotProportion = (this.canvasW*0.49)*v-this.oneCent; this.wheelPos.push({x:dotProportion,y:horizonY-this.wheelRadius}); let startX = dotProportion-(this.animateNum*2); //用於動畫滾動移動 this.ctx.beginPath(); this.ctx.strokeStyle = "#f9f8ef"; this.ctx.lineWidth=6; this.ctx.moveTo(startX,horizonY); this.ctx.lineTo(startX+5,horizonY); this.ctx.closePath(); this.ctx.stroke(); }); this.ctx.restore(); this.shuttle(); // this.wheel(); } shuttle(){ /** * 畫幾根橫線,有點視差,感受騎車在飛速穿梭的感受: 本文@IT·平頭哥聯盟-首席填坑官∙蘇南分享,非商業轉載請註明原連接及出處 */ let shuttleX = this.canvasW+100, shuttleY = this.canvasH/6; let shuttleW = shuttleX+100; [0,40,0].map((k,v)=>{ let random = Math.random()+2; let x = shuttleX+k-(this.animateNum*(2.2*random)); let y = shuttleY+v*24; let w = shuttleW+k-(this.animateNum*(2.2*random)); let grd=this.ctx.createLinearGradient(x,y,w,y); grd.addColorStop(0,"#30212c"); grd.addColorStop(1,"#fff"); this.ctx.beginPath(); this.ctx.lineCap="round"; this.ctx.strokeStyle = grd; this.ctx.lineWidth=3; this.ctx.moveTo(x,y); this.ctx.lineTo(w,y); this.ctx.stroke(); this.ctx.closePath(); }); }
繪製車輪 :git
arc
、fill
console.log(this.wheelPos); this.wheelPos = this.wheelPos.slice(1,3); //這裏取1-3 console.log(this.wheelPos); this.wheelPos.map((wheelItem,v)=>{ let wheelItemX = wheelItem.x, wheelItemY= wheelItem.y-this.wheelBorder/1.5; //外胎 this.ctx.beginPath(); this.ctx.lineWidth=this.wheelBorder; this.ctx.fillStyle = "#f5f5f0"; this.ctx.strokeStyle = this.color; this.ctx.arc(wheelItemX,wheelItemY,this.wheelRadius,0,Math.PI*2,false); this.ctx.closePath(); this.ctx.stroke(); this.ctx.fill(); //最後兩輪胎中心點圓軸承 this.axisDot(wheelItemX,wheelItemY); this.ctx.restore(); }); this.ctx.restore();
createRadialGradient
-建立放射狀/環形的漸變(用在畫布內容上)context.createRadialGradient(x0,y0,r0,x1,y1,r1); + createRadialGradient API 說明: x0 = 漸變的開始圓的 x 座標 y0 = 漸變的開始圓的 y 座標 r0 = 開始圓的半徑 x1 = 漸變的結束圓的 x 座標 y1 = 漸變的結束圓的 y 座標 r1 = 結束圓的半徑 詳細使用請看下面代碼的實例
let scaleMultiple = this.wheelRadius*.94; let speed1 = this.animateNum*2; //外圈半圓速度 let speed2 = this.animateNum*3; //內小圈半圓速度 //後輪 if(v === 0){ //內圓 this.ctx.beginPath(); let circleGrd=this.ctx.createRadialGradient(wheelItemX,wheelItemY,18,wheelItemX,wheelItemY,scaleMultiple); circleGrd.addColorStop(0,"#584a51"); circleGrd.addColorStop(1,"#11090d"); this.ctx.fillStyle = circleGrd; this.ctx.arc(wheelItemX,wheelItemY,scaleMultiple,0,Math.PI*2,false); this.ctx.fill(); this.ctx.closePath(); //兩個半圓線 [ {lineW:2,radius:scaleMultiple*.6,sAngle:getRads(-135+speed1) , eAngle:getRads(110+speed1)}, {lineW:1.2,radius:scaleMultiple*.45,sAngle:getRads(45+speed2) , eAngle:getRads(-50+speed2)} ].map((k,v)=>{ this.ctx.beginPath(); this.ctx.lineCap="round"; this.ctx.strokeStyle ="#fff"; this.ctx.lineWidth=k.lineW; this.ctx.arc(wheelItemX,wheelItemY,k.radius,k.sAngle,k.eAngle,true); this.ctx.stroke(); this.ctx.closePath(); }); this.ctx.restore(); }
//兩個圓,再縮小一圈,畫線圓 Array.from({length:3}).map((k,v)=>{ let prevIndex = v-1 <= 0 ? 0 : v-1; let eAngle = v*135, sAngle = -45+(prevIndex*45)+v*90; let radius = scaleMultiple*.75; let _color_ = "#120008"; this.ctx.beginPath(); this.ctx.lineCap="round"; this.ctx.strokeStyle = _color_; this.ctx.lineWidth=3.5; this.ctx.arc(wheelItemX,wheelItemY,radius,getRads(sAngle+speed1),getRads(eAngle+speed1),false); this.ctx.stroke(); this.ctx.closePath(); if(v<2){ //再縮小一圈 let eAngleSmaller = 15+ v*210, sAngleSmaller = -30+v*90; let radiusSmaller = scaleMultiple*.45; this.ctx.beginPath(); this.ctx.lineCap="round"; this.ctx.strokeStyle = _color_; this.ctx.lineWidth=3; this.ctx.arc(wheelItemX,wheelItemY,radiusSmaller,getRads(sAngleSmaller+speed2),getRads(eAngleSmaller+speed2),false); this.ctx.stroke(); this.ctx.closePath(); } this.ctx.restore(); });
繪製車身車架 :github
最開始是用了最笨的辦法,lineTO
、moveTo
、一根一根線的畫,畫到一半時發現畫兩個三角
或者一個菱形
便可,而後再把幾根主軸從新畫一下,因而兩種方法都嘗試了一下,canvas
lineTo
點對點的劃線,結論 :使用moveTo
把畫布座標從O
移動到A
點 x/y,lineTo
從A
開始畫到B
結束,再從B
到C
點,閉合,即一個三角完成
//方法二:三角形 …………此處省略N行代碼 [ { moveX:triangleX1, moveY:triangleY1, lineX1:coordinateX, lineY1:triangleH1, lineX2:discX, lineY2:discY, }, { moveX:triangleX2+15, moveY:triangleY2, lineX1:triangleX1, lineY1:triangleY1, lineX2:discX, lineY2:triangleH2, }, ].map((k,v)=>{ this.ctx.beginPath(); this.ctx.moveTo(k.moveX,k.moveY); //把座標移動到A點,從A開始 this.ctx.strokeStyle = this.gearColor; this.ctx.lineWidth=coordinateW; this.ctx.lineTo(k.lineX1,k.lineY1);//從A開始,畫到B點結束 this.ctx.lineTo(k.lineX2,k.lineY2); //再從B到C點,閉合 this.ctx.closePath(); this.ctx.stroke(); this.ctx.restore(); }); …… //方法一:菱形 …………此處省略N行代碼 this.ctx.beginPath(); this.ctx.strokeStyle = this.gearColor; this.ctx.lineWidth=coordinateW; this.ctx.moveTo(polygon1X,polygon1Y); this.ctx.lineTo(coordinateX,height); this.ctx.lineTo(discX,discY); this.ctx.lineTo(polygon2X,polygon1Y+5); this.ctx.lineTo(polygon2X-5,polygon1Y); this.ctx.lineTo(polygon1X,polygon1Y); this.ctx.closePath(); this.ctx.stroke(); ……
繪製車的豪華寶坐、扶手 :segmentfault
quadraticCurveTo
能知足這個需求,—— 二次貝塞爾曲線 quadraticCurveTo
,半天也沒畫成功,後面嘗試去找了它鄰居bezierCurveTo
,—— 三次貝塞爾曲線 quadraticCurveTo
、bezierCurveTo
、createLinearGradient
//坐位 this.ctx.restore(); let seatX = (discX-85),seatY=discY-140; let curve1Cpx = [seatX-5,seatY+30,seatX+75,seatY+8]; let curve2Cpx =[seatX+85,seatY-5,seatX,seatY]; this.ctx.beginPath(); // this.ctx.fillStyle = this.gearColor; let grd=this.ctx.createLinearGradient(seatX,seatY,seatX+10,seatY+60); //漸變的角度 grd.addColorStop(0,"#712450"); grd.addColorStop(1,"#11090d"); this.ctx.fillStyle = grd; this.ctx.moveTo(seatX,seatY); this.ctx.quadraticCurveTo(...curve1Cpx); this.ctx.quadraticCurveTo(...curve2Cpx); this.ctx.fill(); //車前軸上的手柄 let steeringX = lever1X-20,steeringY = lever1Y-45; let steeringStep1 = [steeringX+40,steeringY-10,steeringX+40,steeringY-10,steeringX+35,steeringY+15] let steeringStep2 = [steeringX+30,steeringY+25,steeringX+25,steeringY+23,steeringX+18,steeringY+23] this.ctx.beginPath(); this.ctx.lineCap="round"; this.ctx.strokeStyle = "#712450"; this.ctx.lineWidth=coordinateW; this.ctx.moveTo(steeringX,steeringY); //40 60; this.ctx.bezierCurveTo(...steeringStep1); this.ctx.bezierCurveTo(...steeringStep2); this.ctx.stroke(); this.ctx.closePath();
繪製車的發動機、腳踏板 :dom
腳踏板,這個好理解,就是用lineTo
畫兩跟線,其中一根進行一個90度的旋轉就ok了,但重點是它在動畫過程當中的一個過程呢,個人分析過程是這樣:工具
N* (Math.PI / 180)
轉動;N* (Math.PI / 180)
的 rotate
角度旋轉。discGear(coordinateX,coordinateY,coordinateW){ //車中間齒輪盤 disc let discX = coordinateX,discY = coordinateY; let discRadius = this.wheelRadius*.36;//車輪的3.6; let discDotX = discX+discRadius+8,discDotY = discRadius/.98; this.ctx.restore(); this.ctx.save(); this.ctx.translate(discX,discY); // this.ctx.rotate(-(Math.PI/2)); Array.from({length:30}).map((v,index)=>{ let radian = (Math.PI / 15) ; this.ctx.beginPath(); this.ctx.lineCap="round"; this.ctx.strokeStyle = this.color; this.ctx.rotate(radian); this.ctx.lineWidth=3; this.ctx.moveTo(0,discDotY); this.ctx.lineTo(1.5,discDotY); // ctx.arc(discDotX,discDotY,6,0,Math.PI*2,false); this.ctx.closePath(); this.ctx.stroke(); }); this.pedal(discX,discY,discRadius); this.pedal(discX,discY,discRadius,1); this.ctx.restore(); } pedal(coordinateX,coordinateY,discRadius,turnAngle=0){ //腳踏板,分兩次初始化,一次在中心齒輪繪製以前,一次在以後, let pedalX = coordinateX, pedalY = coordinateY - discRadius*.7; let pedalW = 6, pedalH = discRadius*1.9; let radian = (this.animateNum)*(Math.PI / 180) ; let radianHor = (this.animateNum)*(Math.PI / 180) ; let turnAngleNum = 1; let moveY = 28; if(turnAngle !== 0){ this.ctx.rotate(-180*(Math.PI/180)); turnAngleNum = (Math.PI/180); }; this.ctx.beginPath(); this.ctx.rotate(radian*turnAngleNum); this.ctx.lineCap="round"; this.ctx.strokeStyle = this.gearColor; this.ctx.lineWidth=pedalW; this.ctx.moveTo(-1,moveY); this.ctx.lineTo(0,pedalH); this.ctx.closePath(); this.ctx.stroke(); this.ctx.save(); let pedalHorW = pedalH/1.5,pedalHorH=pedalW; this.ctx.translate(0,pedalH); this.ctx.beginPath(); this.ctx.rotate(-radianHor); this.ctx.lineCap="round"; this.ctx.fillStyle = "#fff"; this.ctx.strokeStyle = this.gearColor; this.ctx.lineWidth =2; this.ctx.roundRect(-pedalHorW/2,-2,pedalHorW,pedalHorH,5); this.ctx.closePath(); this.ctx.fill(); this.ctx.stroke(); this.ctx.restore(); }
繪製車的鏈條 :
bezierCurveTo
,cp1x,cp1y,cp2x,cp2y,x,y等參數畫出來的,具體看下面代碼吧,其實就是兩個半橢圓的拼接……
//鏈條 let chainW = ( coordinateX+discRadius - this.wheelPos[0].x) / 2; let chainX = this.wheelPos[0].x +chainW-5 ; let chainY = coordinateY; this.ctx.save(); this.ctx.translate(chainX,chainY+4.8); this.ctx.rotate(-2*(Math.PI/180)); let r = chainW+chainW*.06,h = discRadius/2; this.ctx.beginPath(); this.ctx.moveTo(-r, -1); this.ctx.lineWidth=3; this.ctx.strokeStyle = "#1e0c1a"; this.ctx.bezierCurveTo(-r,h*1.5,r,h*4,r,0); this.ctx.bezierCurveTo(r,-h*4,-r,-h*1.5,-r,0); this.ctx.closePath(); this.ctx.stroke(); this.ctx.restore();
以上就是今天@IT·平頭哥聯盟-首席填坑官
∙蘇南給你帶來的分享,整個車的繪製過程,感受車架部分應該還有更好的作法,若是您有更好的建議及想法,歡迎斧正,最後送上完整的示例圖,如以爲不錯,記得關注咱們的公衆號哦
!
文章源碼獲取-> blog-resource 👈
想直接在線預覽 👈
做者:蘇南 - 首席填坑官
交流羣:912594095,公衆號:honeyBadger8
本文原創,著做權歸做者全部。商業轉載請聯繫@IT·平頭哥聯盟
得到受權,非商業轉載請註明原連接及出處。