canvas進階——實現靜態圖像的變形併合成動態效果

寫在最前

在以前的這篇bezierMaker.js——N階貝塞爾曲線生成器的文章中咱們提到了對於高階貝塞爾公式的繪製與生成。不過更多的童鞋看到後可能會不知道其使用場景是什麼。故做者本次分享一下基於bezierMaker.js實現的將靜態圖片按照自定義曲線軌跡扭曲圖片併合稱爲動態效果。html

歡迎關注個人博客,不按期更新中——git

效果預覽

以前的描述可能不是很清楚咱們直接看下效果圖:github

首先加載一張圖:
imageweb

而後經過bezierMaker.js提供的試驗場功能來繪製一段曲線,進行圖片扭曲:
imagecanvas

最後擬合爲動態圖:數組

2018-01-19 12_40_32

再來一個豎直方向的扭動:動畫

anmate

demo地址ui

源碼地址url

圖像變形實現思路

  1. 繪製一條由bezierMaker.js生成的貝塞爾曲線,以此來掌握曲線各點的準確座標值
  2. 肯定扭曲方向爲橫向或縱向
  3. 根據該方向的基準線(圖中的灰色線)來計算本次繪製的曲線與基準對比的偏移量,按照該方向每隔1px記錄一個值
  4. 將圖像數據按照選定方向進行切分,將一個一維數組imgData.data變爲一段一段有方向的二維數組
  5. 將每段數組按照以前記錄的偏移值進行移位後再拼接爲一維數組
  6. 將新拼接好的數組從新賦值到imgData中

其中較爲核心的實現即橫向與縱向對一維圖像數據的切分。其中橫向相對簡單,細節以下:spa

image

如上圖所示,在原始圖像的數據中的數據形式爲一維數組的形式,而對其進行拆分則是一個從中不斷截取與提取數據的過程。橫向拆分較爲簡單,只須要肯定每一行開始的位置便可,截取的數量就是一行的元素數。同時縱向拆分則須要多加一步,咱們須要計算每一層數組中的每個數,像上圖通常拆分第每列數組時首先要遍歷圖的寬獲得每一列的索引,再遍歷圖的高,經過高✖️寬✖️4 + 寬 ✖️ 4算出當前值在原數據中的位置。當拆分紅功數組後,將數組依次移位,移位數爲以前曲線與基準線的偏移量決定。

//pg.js
//按行拆分
bezierArr.forEach(function (obj, index) {
    if (_.imgStartY < obj.y && _.imgStartY + _.imgHeight > obj.y && type === 'row') {
    
        var diffX = parseInt(obj.x - _.baseX, 10) //計算偏移量
        var dissY = parseInt(obj.y - _.imgStartY, 10)
        var rowNum = dissY
        imgDataSlice = _.imgData.data.slice((rowNum) * _.imgWidth * 4, rowNum * _.imgWidth * 4 + _.imgWidth * 4) //按層切片
        ...
    }
})

//按列拆分
for (var i = 0; i < _.imgWidth; i++) {
    imgDataSlice = []
    for (var j = 0; j < _.imgHeight; j++) {
        var index = j * _.imgWidth * 4 + i * 4
        var sliceArr = _.imgData.data.slice(index, index + 4)
        imgDataSlice = imgDataSlice.concat(Array.from(sliceArr))
    }
    if(_.imgChangeObj[i]) {
        for (var k = 0; k < Math.abs(_.imgChangeObj[i].diffY * 4); k++) {
            imgDataSlice = _.arraymove(_.imgChangeObj[i].diffY, imgDataSlice)
        }
        for (var p = 0; p < imgDataSlice.length / 4; p++) {
            arr[p * _.imgWidth * 4 + i * 4] = imgDataSlice[p * 4]
            arr[p * _.imgWidth * 4 + i * 4 + 1] = imgDataSlice[p * 4 + 1]
            arr[p * _.imgWidth * 4 + i * 4 + 2] = imgDataSlice[p * 4 + 2]
            arr[p * _.imgWidth * 4 + i * 4 + 3] = imgDataSlice[p * 4 + 3]
        }
    }
}

核心的數組拆分移位再合併的邏輯相對分散,知道思路便可有興趣的同窗歡迎戳源碼~

合併成動態效果

核心思想爲從咱們的原始形態到最終態的兩張靜態圖咱們已經獲得了。如今咱們須要作的是添加幾張過渡態。在這裏面有兩種方式:

  • 將計算的各點偏移量進行按比例偏移,好比一共四張圖合成則須要三次改變狀態,那麼每次將數組移位的量設定爲總量的1/3,每次移位後拼出一維數組更新到一張離屏canvas中將其保存爲base64,做爲後續合併時的替換url
  • 計算貝塞爾曲線控制點的偏移量且進行按比例偏移。如一開始的垂直或水平的初始圖控制點造成了一條直線。同時最終形態的控制點位置咱們已經知道了,藉此咱們能夠將控制點由直線到兩邊的過程按比例切分,依次計算各中間態控制點所造成曲線致使的偏移圖像數據,導出base64,做爲後續合併替換的url

做者一開始使用了第一種方式,可是有一個明顯的缺陷及經過按比例直接偏移會致使拆分出來的每層的偏移每次都是相同的,那麼就會出現鋸齒現象。由於圖像扭曲可能上一層在這一次移位的時候偏移5合適但是你仍然偏移了總量的1/3致使與下一層的圖像不匹配從而出現鋸齒。故從新選擇了第二種方式,由從新計算各中間態圖像的控制點再來移位圖像數據,圖像的呈現狀況就改善了不少。

小結

因爲操做圖像數據量比較大,故在嘗試demo的時候若是遇到ui卡頓那是正在計算中,並無引入webworker之類的因此請稍等一會就會出現結果=。=
PS:demo使用步驟

  • 加載圖像
  • 畫曲線,豎向切分請點擊checkbox,同時曲線寬要大於圖像的寬。橫向切分數據則曲線高要大於圖像,保證起終點在基準線外。描點後點擊繪製
  • 計算結束後點擊合成

其餘canvas相關文章

最後

demo地址

源碼地址

慣例po做者的博客,不定時更新中——

有問題歡迎在issues下交流。

相關文章
相關標籤/搜索