日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

canvas-应用大全

發(fā)布時間:2025/6/17 编程问答 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 canvas-应用大全 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

前言

去年在公司內(nèi)部做了一次canvas的分享,或者說canvas總結(jié)會更為貼切,但由于一直都因為公事或者私事,一直沒有把東西總結(jié)成文章分享給大家,實在抱歉~ 分享這篇文章的目的是為了讓同學(xué)們對canvas有一個全面的認識,廢話不多說,開拔!

介紹

Canvas是一個可以使用腳本(通常為Javascript,其它比如?Java Applets or JavaFX/JavaFX Script)來繪制圖形,默認大小為300像素×150像素的HTML元素。

<canvas style="background: purple;"></canvas> 復(fù)制代碼

小試牛刀

<!-- canvas --> <canvas id="canvas"></canvas> <!-- javascript --> <script>const canvas = document.getElementById('canvas')const ctx = canvas.getContext('2d')ctx.fillStyle = 'purple'ctx.fillRect(0, 0, 300, 150) </script> 復(fù)制代碼

經(jīng)過了以上地獄般的學(xué)習(xí),我相信同學(xué)們現(xiàn)在已精通canvas。 接下來,我將介紹很多案例,把自己能想到的都列舉出來,并且,結(jié)合其原理,為同學(xué)們一一介紹。

應(yīng)用案例

案例如下:

  • 動畫
  • 游戲
  • 視頻(因為生產(chǎn)環(huán)境還不成熟,略)
  • 截圖
  • 合成圖
  • 分享網(wǎng)頁截圖
  • 濾鏡
  • 摳圖
  • 旋轉(zhuǎn)、縮放、位移、形變
  • 粒子

動畫

API介紹

requestAnimationFrame

該方法告訴瀏覽器您希望執(zhí)行動畫并請求瀏覽器在下一次重繪之前調(diào)用指定的函數(shù)來更新動畫。 該方法使用一個回調(diào)函數(shù)作為參數(shù),這個回調(diào)函數(shù)會在瀏覽器重繪之前調(diào)用。

requestAnimationFrame 優(yōu)點

1.避免掉幀 完全依賴瀏覽器的繪制頻率,從而避免過度繪制,影響電池壽命。 2.提升性能 當(dāng)Tab或隱藏的iframe里,暫停調(diào)用。

Demo

方塊移動

<!-- canvas --> <canvas id="canvas" width="600" height="600"></canvas> <!-- javascript --> <script>const canvas = document.getElementById('canvas')const ctx = canvas.getContext('2d')ctx.fillStyle = 'purple'const step = 1 // 每步的長度let xPosition = 0 // x坐標(biāo)move() // call movefunction move() {ctx.clearRect(0, 0, 600, 600)ctx.fillRect(xPosition, 0, 300, 150)xPosition += stepif (xPosition <= 300) {requestAnimationFrame(() => {move()})}} </script> 復(fù)制代碼

游戲

三要素

個人做游戲總結(jié)的三要素:

  • 對象抽象
  • requestAnimationFrame
  • 緩動函數(shù)

對象抽象:即對游戲中角色的抽象,面向?qū)ο蟮乃季S在游戲中非常地普遍。舉個例子,我們來抽象一個《勇者斗惡龍》里的史萊姆:

class Slime {constructor(hp, mp, level, attack, defence) {this.hp = hpthis.mp = mpthis.level = levelthis.attack = attackthis.defence = defence}bite() {return this.attack}fire() {return this.attack * 2} } 復(fù)制代碼

requestAnimationFrame:之前我們已經(jīng)接觸過這個API了,結(jié)合上面動畫的例子,我們很容易自然的就能想到,游戲動起來的原理了。

緩動函數(shù):我們知道,勻速運動的動畫會顯得非常不自然,要變得自然就得時而加速,時而減速,那樣動畫就會變得更加靈活,不再生硬。

Demo

有興趣的同學(xué)可以看我以前寫的小游戲。 項目地址:github.com/CodeLittleP…

截圖

API介紹

drawImage(image, sx, sy [, sWidth, sHeight [, dx, dy, dWidth, dHeight]])

繪制圖像方法。

toDataURL(type, encoderOptions)

方法返回一個包含圖片展示的 data URI ??梢允褂?type 參數(shù)其類型,默認為 PNG 格式。圖片的分辨率為96dpi。 注意:

  • 該方法必須在http服務(wù)下
  • 非同源的圖片需要CORS支持,圖片設(shè)置crossOrigin =“”(只要crossOrigin的屬性值不是use-credentials,全部都會解析為anonymous,包括空字符串,包括類似'abc'這樣的字符)

canvas.style.width 和 canvas.width 的區(qū)別

把canvas元素比作畫框: canvas.width則是控制畫框尺寸的方式。 canvas.style.width則是控制在畫框中的畫尺寸的方式。

Demo

核心代碼

const captureResultBox = document.getElementById('captureResultBox') const captureRect = document.getElementById('captureRect') const style = window.getComputedStyle(captureRect) // 設(shè)置canvas畫布大小 canvas.width = parseInt(style.width) canvas.height = parseInt(style.height) // 畫圖 const x = parseInt(style.left) const y = parseInt(style.top) const w = parseInt(img.width) const h = parseInt(img.height) ctx.drawImage(img, x, y, w, h, 0, 0, w, h) // 將圖片append到html中 const resultImg = document.createElement('img') // toDataURL必須在http服務(wù)中 resultImg.src = canvas.toDataURL('image/png', 0.92) 復(fù)制代碼

合成圖

原理

回看之前的例子,我們知道了drawImage可以自己畫圖畫,也可以畫圖片。canvas完全就是個畫板,可任由我們發(fā)揮。 合成的思路其實就是把多張圖片都畫在同一個畫布(cavans)里。是不是一下子就知道接下來怎么做啦?

Demo

核心代碼

// 設(shè)置畫布大小canvas.width = bg.widthcanvas.height = bg.height// 畫背景ctx.drawImage(bg, 0, 0)// 畫第一個角色ctx.drawImage(character1, 100, 200,character1.width / 2,character1.height / 2)// 畫第二個角色ctx.drawImage(character2, 500, 200,character2.width / 2,character2.height / 2) 復(fù)制代碼

如圖,背景是一深夜無人后院,然后去網(wǎng)上搜兩張背景透明的角色圖片,再將兩張圖一次畫到畫布上就成了合成圖啦。

分享網(wǎng)頁截圖

原理

拿比較出名的html2canvas為例,實現(xiàn)方式就是遍歷整個dom,然后挨個拉取樣式,在canvas上一個個地畫出來。

Demo

濾鏡

API介紹

getImageData(sx, sy, sw, sh)

返回一個ImageData對象,用來描述canvas區(qū)域隱含的像素數(shù)據(jù),這個區(qū)域通過矩形表示,起始點為(sx, sy)、寬為sw、高為sh。 看段代碼:

const img = document.createElement('img') img.src = './filter.jpg' img.addEventListener('load', () => {canvas.width = img.widthcanvas.height = img.heightctx.drawImage(img, 0, 0)console.log(ctx.getImageData(0, 0, canvas.width, canvas.height)) }) 復(fù)制代碼

它會打印出如下數(shù)據(jù):

有點迷?不慌,接下去看。

數(shù)據(jù)類型介紹

Uint8ClampedArray

8位無符號整型固定數(shù)組) 類型化數(shù)組表示一個由值固定在0-255區(qū)間的8位無符號整型組成的數(shù)組;如果你指定一個在 [0,255] 區(qū)間外的值,它將被替換為0或255;如果你指定一個非整數(shù),那么它將被設(shè)置為最接近它的整數(shù)。(數(shù)組)內(nèi)容被初始化為0。一旦(數(shù)組)被創(chuàng)建,你可以使用對象的方法引用數(shù)組里的元素,或使用標(biāo)準(zhǔn)的數(shù)組索引語法(即使用方括號標(biāo)記)。 回看這張圖:

data里其實就是像素,按每4個為一組成為一個像素。 4個一組,難道是rgba? (o゜▽゜)o☆[BINGO!] 這樣的話,圖片的寬x高x4(w * h * 4 )就是所有像素的總和,剛好就死data的length。

數(shù)學(xué)推導(dǎo)

已知:924160 = 640 x 316 x 4 可知:數(shù)組的長度為length = canvas.width x canvas.height x 4

知道了這種關(guān)系,我們不妨把這個一維數(shù)組想象成二維數(shù)組,想象它是一個平面圖,如圖:

一個格子代表一個像素

w = 圖像寬度

h = 圖像高度

這樣,我們可以很容易得到點(x, y)在一維數(shù)組中對應(yīng)的位置。我們想一想,點(1, 1)坐標(biāo)對應(yīng)的是數(shù)組下標(biāo)為0,點(2, 1)對應(yīng)的是數(shù)組下標(biāo)4,假設(shè)圖像寬度為2*2,那么點(1,2)對應(yīng)下標(biāo)就是index=((2 - 1)*w + (1 - 1))*4 = 8。

推導(dǎo)出公式:index = [(y - 1) * w + (x - 1) ] * 4

繼續(xù)API介紹

createImageData(width, height)

createImageData是在canvas在取渲染上下文為2D(即canvas.getContext(‘2d'))的時候提供的接口。作用是創(chuàng)建一個新的、空的、特定尺寸的ImageData對象。其中所有的像素點初始都為黑色透明。并返回該ImageData對象。

putImageData

putImageData方法作為canvas 2D API 以給定的ImageData對象繪制數(shù)據(jù)進位圖。如果提供了臟矩形,將只有矩形的像素會被繪制。這個方法不會影響canvas的形變矩陣。

這小節(jié)我們學(xué)了好幾個新API,然后重新理了理數(shù)學(xué)知識。同學(xué)們好好消化完以后,就進Demo階段吧。

Demo

核心代碼:

最終效果:

摳圖

對于純背景摳圖,其實還是比較簡單的。上面我們已經(jīng)說過,我們可以拿到整個canvas的每個像素點的值了。所以,只需要把純色的色值轉(zhuǎn)為透明就好了。 但這種場景不多,因為,背景很少有純色的情況,而且即使背景純色,不保證被扣對象的身上沒有和背景同色值的情況。 所以,如果要處理復(fù)雜的情況,還是建議后端來做比較好,后端早已有了成熟的圖像處理解決方案,比如opencv等。像美圖的話,有專門的圖像算法團隊,天天研究這方面。 接下來,我將介紹下美圖人像摳圖的思路。

屬性介紹

globalCompositeOperation

控制drawImage的繪制圖層先后順序。

思路

我們將使用souce-in這個屬性。如上圖所示,這個屬性的作用是,兩圖疊加,只取疊加的部分。 為什么這樣搞?不是說好了,美圖是讓后端算法大佬們處理嗎? 因為,為了人像摳圖適應(yīng)更多的場景,算法大佬們只會把人物圖像處理成一個蒙版圖并返給前端,之后讓前端自己處理。 我們看下原圖:

再看下后端返給的蒙版圖:

得到以上的蒙版圖以后,先把黑色處理成透明; 先在canvas上draw原圖; 再把globalCompositeOperation 設(shè)置為 'source-in'; 然后再draw處理后的蒙版圖; 得到的就是最后的摳圖啦! 這個方案是咨詢前美圖大佬@xd-tayde的,感謝~

Demo

處理結(jié)果:

旋轉(zhuǎn)、縮放、位移、形變

對于旋轉(zhuǎn)、縮放、位移、形變,canvas的上下文ctx有對應(yīng)的API可以調(diào)用,也可以用martrix方式做更高級的變化。因為涉及的內(nèi)容很多,如果全寫這的話,篇幅太大。 所以,我這里直接推薦一篇文章給同學(xué)們學(xué)習(xí) ——《canvas 圖像旋轉(zhuǎn)與翻轉(zhuǎn)姿勢解鎖》

粒子

抽象

之前我們就知道了,我們可以獲取canvas上的每個像素點。 所謂的粒子,其實算是對一個像素的抽象。它具有自己坐標(biāo),自己的色值,可以通過改變自身的屬性“動”起來。 因此我們不妨將粒子作為一個對象來看待,它有坐標(biāo)和色值,如:

let particle = {x: 0,y: 0,rgba: '(1, 1, 1, 1)' } 復(fù)制代碼

Demo - 小試牛刀

我將把一張網(wǎng)易支付的logo圖,用散落的粒子重新畫出來。 核心代碼:

// 獲取像素顏色信息const originImageData = ctx.getImageData(0, 0, canvas.width, canvas.height)const originImageDataValue = originImageData.dataconst w = canvas.widthconst h = canvas.heightlet colors = []let index = 0for (let y = 1; y <= h; y++) {for (let x = 1; x <= w ; x++) {const r = originImageDataValue[index]const g = originImageDataValue[index + 1]const b = originImageDataValue[index + 2]const a = originImageDataValue[index + 3]index += 4// 將像素位置打亂,保存進返回數(shù)據(jù)中colors.push({x: x + getRandomArbitrary(-OFFSET, OFFSET),y: y + getRandomArbitrary(-OFFSET, OFFSET),color: `rgba(${r}, ${g}, $, ${a})`})} 復(fù)制代碼

效果:

Demo - 粒子動畫

三要素

  • 粒子對象化
  • 緩動函數(shù)
  • 性能

粒子對象化已經(jīng)介紹過了。 緩動函數(shù),在之前的游戲也提及過,是為了讓動畫更加的自然生動。 性能是一個很需要關(guān)注的問題。因為比如一張500x500的圖片,那數(shù)據(jù)量就是500x500x4=1000000。動畫借助了requestAnimationFrame,正常的情況下一般刷新頻率在60HZ,能展現(xiàn)非常流暢的動畫。但現(xiàn)在要處理這么大的數(shù)據(jù)量,瀏覽器抗不過來了,自然造成了降頻,導(dǎo)致動畫卡幀嚴重。

為了性能,粒子動畫往往采用選擇性的選取像素用來繪制。比如,只繪制原圖x坐標(biāo)為偶數(shù),或能被4等整除的像素。比如,只繪制原圖對應(yīng)像素r色值為155以上的像素。

結(jié)合上面的思路,就可以做出各種強大的例子動畫啦。

Demo

所有Demo項目地址

github.com/CodeLittleP…

原文出處

《canvas-應(yīng)用大全》

參考文章

《打造高大上的 Canvas 粒子動畫 - 騰訊 ISUX》

轉(zhuǎn)載于:https://juejin.im/post/5c81e6de5188257ade0231f4

總結(jié)

以上是生活随笔為你收集整理的canvas-应用大全的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。