自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

用Canvas實(shí)現(xiàn)一個(gè)大氣球送給你

開發(fā) 前端
近期在做一個(gè)氣球掛件的特效需求,值此契機(jī),來跟大家分享一下如何利用canvas以及對(duì)應(yīng)的數(shù)學(xué)知識(shí)構(gòu)造一個(gè)栩栩如生的氣球。

[[423338]]

一、背景

近期在做一個(gè)氣球掛件的特效需求,值此契機(jī),來跟大家分享一下如何利用canvas以及對(duì)應(yīng)的數(shù)學(xué)知識(shí)構(gòu)造一個(gè)栩栩如生的氣球。

二、實(shí)現(xiàn)

在實(shí)現(xiàn)這個(gè)看似是圓鼓鼓的氣球之前,先了解一下其實(shí)現(xiàn)思路,主要分為以下幾個(gè)部分:

  1. 實(shí)現(xiàn)球體部分;
  2. 實(shí)現(xiàn)氣球口子部分;
  3. 實(shí)現(xiàn)氣球的線部分;
  4. 進(jìn)行顏色填充;
  5. 實(shí)現(xiàn)動(dòng)畫;

氣球.PNG

2.1 球體部分實(shí)現(xiàn)

對(duì)于這樣的氣球的球體部分,大家都有什么好的實(shí)現(xiàn)思路的?相信大家肯定會(huì)有多種多樣的實(shí)現(xiàn)方案,我也是在看到某位大佬的效果后,感受到了利用四個(gè)三次貝塞爾曲線實(shí)現(xiàn)這個(gè)效果的妙處。為了看懂后續(xù)代碼,先了解一下三次貝塞爾曲線的原理。(注:引用了CSDN上某位大佬的文章,寫的很好,下圖引用于此)

三次貝塞爾曲線.gif

在上圖中P0為起始點(diǎn)、P3為終止點(diǎn),P1和P2為控制點(diǎn),其最終的曲線公式如下所示:

  1. B(t)=(1?t)^3 * P0+3t(1?t)^2 * P1+3t ^ 2(1?t) * P2+t ^ 3P3, t∈[0,1] 

上述已經(jīng)列出了三次貝塞爾曲線的效果圖和公式,但是通過這個(gè)怎么跟我們的氣球掛上鉤呢?下面通過幾張圖就理解了:

如上圖所示,就是實(shí)現(xiàn)整個(gè)氣球球體的思路,具體解釋如下所示:

  1. A圖中起始點(diǎn)為p1,終止點(diǎn)為p2,控制點(diǎn)為c1、c2,讓兩個(gè)控制點(diǎn)重合,繪制出的效果并不是很像氣球的一部分,此時(shí)就要通過改變控制點(diǎn)來改變其外觀;
  2. 改變控制點(diǎn)c1、c2,c1中y值不變,減小x值;c2中x值不變,增大y值(注意canvas中坐標(biāo)方向即可),改變后就得到了圖B的效果,此時(shí)就跟氣球外觀很像了;
  3. 緊接著按照這個(gè)方法就可以實(shí)現(xiàn)整個(gè)的氣球球體部分的外觀。
  1. function draw() { 
  2.     const canvas = document.getElementById('canvas'); 
  3.     const ctx = canvas.getContext('2d'); 
  4.  
  5.     ctx.translate(250, 250); 
  6.     drawCoordiante(ctx); 
  7.     ctx.save(); 
  8.     ctx.beginPath(); 
  9.     ctx.moveTo(0, -80); 
  10.     ctx.bezierCurveTo(45, -80, 80, -45, 80, 0); 
  11.     ctx.bezierCurveTo(80, 85, 45, 120, 0, 120); 
  12.     ctx.bezierCurveTo(-45, 120, -80, 85, -80, 0); 
  13.     ctx.bezierCurveTo(-80, -45, -45, -80, 0, -80); 
  14.     ctx.stroke(); 
  15.     ctx.restore(); 
  16.  
  17. function drawCoordiante(ctx) { 
  18.     ctx.beginPath(); 
  19.     ctx.moveTo(-120, 0); 
  20.     ctx.lineTo(120, 0); 
  21.     ctx.moveTo(0, -120); 
  22.     ctx.lineTo(0, 120); 
  23.     ctx.closePath(); 
  24.     ctx.stroke(); 

2.2 口子部分實(shí)現(xiàn)

口子部分可以簡(jiǎn)化為一個(gè)三角形,效果如下所示:

  1. function draw() { 
  2.     const canvas = document.getElementById('canvas'); 
  3.     const ctx = canvas.getContext('2d'); 
  4.  
  5.     …… 
  6.  
  7.     ctx.save(); 
  8.     ctx.beginPath(); 
  9.     ctx.moveTo(0, 120); 
  10.     ctx.lineTo(-5, 130); 
  11.     ctx.lineTo(5, 130); 
  12.     ctx.closePath(); 
  13.     ctx.stroke(); 
  14.     ctx.restore(); 

2.3 線部分實(shí)現(xiàn)

線實(shí)現(xiàn)的比較簡(jiǎn)單,就用了一段直線實(shí)現(xiàn)

  1. function draw() { 
  2.     const canvas = document.getElementById('canvas'); 
  3.     const ctx = canvas.getContext('2d'); 
  4.  
  5.     …… 
  6.  
  7.     ctx.save(); 
  8.     ctx.beginPath(); 
  9.     ctx.moveTo(0, 120); 
  10.     ctx.lineTo(0, 300); 
  11.     ctx.stroke(); 
  12.     ctx.restore(); 

2.4 進(jìn)行填充

氣球部分的填充用了圓形漸變效果,相比于純色來說更加漂亮一些。

  1. function draw() { 
  2.     const canvas = document.getElementById('canvas'); 
  3.     const ctx = canvas.getContext('2d'); 
  4.  
  5.     ctx.fillStyle = getBalloonGradient(ctx, 0, 0, 80, 210); 
  6.     …… 
  7.      
  8.  
  9. function getBalloonGradient(ctx, x, y, r, hue) { 
  10.     const grd = ctx.createRadialGradient(x, y, 0, x, y, r); 
  11.     grd.addColorStop(0, 'hsla(' + hue + ', 100%, 65%, .95)'); 
  12.     grd.addColorStop(0.4, 'hsla(' + hue + ', 100%, 45%, .85)'); 
  13.     grd.addColorStop(1, 'hsla(' + hue + ', 100%, 25%, .80)'); 
  14.     return grd; 

2.5 動(dòng)畫效果及整體代碼

上述流程已經(jīng)將一個(gè)靜態(tài)的氣球部分繪制完畢了,要想實(shí)現(xiàn)動(dòng)畫效果只需要利用requestAnimationFrame函數(shù)不斷循環(huán)調(diào)用即可實(shí)現(xiàn)。下面直接拋出整體代碼,方便同學(xué)們觀察效果進(jìn)行調(diào)試,整體代碼如下所示:

  1. let posX = 225; 
  2. let posY = 300; 
  3. let points = getPoints(); 
  4. draw(); 
  5.  
  6. function draw() { 
  7.     const canvas = document.getElementById('canvas'); 
  8.     const ctx = canvas.getContext('2d'); 
  9.     ctx.clearRect(0, 0, canvas.width, canvas.height); 
  10.     if (posY < -200) { 
  11.         posY = 300; 
  12.         posX += 300 * (Math.random() - 0.5); 
  13.         points = getPoints(); 
  14.     } 
  15.     else { 
  16.         posY -= 2; 
  17.     } 
  18.     ctx.save(); 
  19.     ctx.translate(posX, posY); 
  20.     drawBalloon(ctx, points); 
  21.     ctx.restore(); 
  22.  
  23.     window.requestAnimationFrame(draw); 
  24.  
  25. function drawBalloon(ctx, points) { 
  26.     ctx.scale(points.scale, points.scale); 
  27.     ctx.save(); 
  28.     ctx.fillStyle = getBalloonGradient(ctx, 0, 0, points.R, points.hue); 
  29.     // 繪制球體部分 
  30.     ctx.moveTo(points.p1.x, points.p1.y); 
  31.     ctx.bezierCurveTo(points.pC1to2A.x, points.pC1to2A.y, points.pC1to2B.x, points.pC1to2B.y, points.p2.x, points.p2.y); 
  32.     ctx.bezierCurveTo(points.pC2to3A.x, points.pC2to3A.y, points.pC2to3B.x, points.pC2to3B.y, points.p3.x, points.p3.y); 
  33.     ctx.bezierCurveTo(points.pC3to4A.x, points.pC3to4A.y, points.pC3to4B.x, points.pC3to4B.y, points.p4.x, points.p4.y); 
  34.     ctx.bezierCurveTo(points.pC4to1A.x, points.pC4to1A.y, points.pC4to1B.x, points.pC4to1B.y, points.p1.x, points.p1.y); 
  35.  
  36.     // 繪制氣球鈕部分 
  37.     ctx.moveTo(points.p3.x, points.p3.y); 
  38.     ctx.lineTo(points.knowA.x, points.knowA.y); 
  39.     ctx.lineTo(points.knowB.x, points.knowB.y); 
  40.     ctx.fill(); 
  41.     ctx.restore(); 
  42.  
  43.     // 繪制線部分 
  44.     ctx.save(); 
  45.     ctx.strokeStyle = '#000000'
  46.     ctx.lineWidth = 1; 
  47.     ctx.beginPath(); 
  48.     ctx.moveTo(points.p3.x, points.p3.y); 
  49.     ctx.lineTo(points.lineEnd.x, points.lineEnd.y); 
  50.     ctx.stroke(); 
  51.     ctx.restore(); 
  52.  
  53. function getPoints() { 
  54.     const offset = 35; 
  55.     return { 
  56.         scale: 0.3 + Math.random() / 2, 
  57.         hue: Math.random() * 255, 
  58.         R: 80, 
  59.         p1: { 
  60.             x: 0, 
  61.             y: -80 
  62.         }, 
  63.         pC1to2A: { 
  64.             x: 80 - offset, 
  65.             y: -80 
  66.         }, 
  67.         pC1to2B: { 
  68.             x: 80, 
  69.             y: -80 + offset 
  70.         }, 
  71.         p2: { 
  72.             x: 80, 
  73.             y: 0 
  74.         }, 
  75.         pC2to3A: { 
  76.             x: 80, 
  77.             y: 120 - offset 
  78.         }, 
  79.         pC2to3B: { 
  80.             x: 80 - offset, 
  81.             y: 120 
  82.         }, 
  83.         p3: { 
  84.             x: 0, 
  85.             y: 120 
  86.         }, 
  87.         pC3to4A: { 
  88.             x: -80 + offset, 
  89.             y: 120 
  90.         }, 
  91.         pC3to4B: { 
  92.             x: -80, 
  93.             y: 120 - offset 
  94.         }, 
  95.         p4: { 
  96.             x: -80, 
  97.             y: 0 
  98.         }, 
  99.         pC4to1A: { 
  100.             x: -80, 
  101.             y: -80 + offset 
  102.         }, 
  103.         pC4to1B: { 
  104.             x: -80 + offset, 
  105.             y: -80 
  106.         }, 
  107.         knowA: { 
  108.             x: -5, 
  109.             y: 130 
  110.         }, 
  111.         knowB: { 
  112.             x: 5, 
  113.             y: 130 
  114.         }, 
  115.         lineEnd: { 
  116.             x: 0, 
  117.             y: 250 
  118.         } 
  119.     }; 
  120.  
  121. function getBalloonGradient(ctx, x, y, r, hue) { 
  122.     const grd = ctx.createRadialGradient(x, y, 0, x, y, r); 
  123.     grd.addColorStop(0, 'hsla(' + hue + ', 100%, 65%, .95)'); 
  124.     grd.addColorStop(0.4, 'hsla(' + hue + ', 100%, 45%, .85)'); 
  125.     grd.addColorStop(1, 'hsla(' + hue + ', 100%, 25%, .80)'); 
  126.     return grd; 

 

責(zé)任編輯:武曉燕 來源: 前端點(diǎn)線面
相關(guān)推薦

2017-08-29 15:34:10

CanvasWASM算法

2022-12-22 08:22:17

Python圖像圖像處理

2017-11-27 13:39:29

Python大數(shù)據(jù)搜索引擎

2017-12-27 14:51:12

Kotlin谷歌Java

2018-07-03 15:20:36

Promise函數(shù)借錢

2020-09-06 22:59:35

Linux文件命令

2018-05-04 09:14:09

Git技巧shell命令

2021-04-15 11:37:47

NumpyPython代碼

2021-05-07 07:59:52

WebFluxSpring5系統(tǒng)

2012-05-30 09:40:55

Linux鍋爐

2020-12-20 10:07:57

Canvas圖形驗(yàn)證碼javascript

2018-06-16 08:35:57

UnixLinux命令

2014-04-14 15:54:00

print()Web服務(wù)器

2021-06-25 10:38:05

JavaScript編譯器前端開發(fā)

2023-01-30 16:21:24

Linux外觀

2017-06-05 12:06:00

2021-02-14 19:24:45

SpringRegistrar對(duì)象

2019-02-11 11:16:13

2022-02-28 00:14:30

人工智能數(shù)據(jù)機(jī)器學(xué)習(xí)

2015-07-27 10:34:55

大數(shù)據(jù)大忽悠
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)