前幾天因爲團隊須要,折騰了一下圖像液化的處理過程。
如今來整理一下思路,作個記錄。canvas
用到公式以下,網上拿來的ui
話很少說,上代碼
原本想盡可能寫出點逼格。。。後來發現怎麼寫也仍是幾個function搞定,就那樣了。spa
(function(global) { // 計算兩點距離平方 function distanceSqr( x1, y1, x2, y2 ) { return sqr(x1-x2) + sqr(y1-y2); } // 計算平方 function sqr(x) { return x*x; } // 遍歷一個指定圓內的全部點 // 經過callback傳入回調方法,回調傳出每個點的相關信息 function eachCircleDot( imageData, ox, oy, r, callback ) { var imgWidth = imageData.width, imgHeight = imageData.height, data = imageData.data, left = ox-r, right = ox+r, top = oy-r, bottom = oy+r, dotRedOffset,dotGreenOffset,dotBlueOffset,alphaOffset; for( var x = left; x < right; x++ ) for( var y = top; y < bottom; y++ ) if( distanceSqr( x, y, ox, oy ) <= sqr(r) ) { dotRedOffset = y*imgWidth*4+x*4; dotGreenOffset = dotRedOffset + 1; dotBlueOffset = dotGreenOffset + 1; alphaOffset = dotBlueOffset + 1; callback( // 當前點的座標 { x:x, y:y }, // 點的RGBA四個份量對應字節的下標 { r: dotRedOffset, g: dotGreenOffset, b: dotBlueOffset, a: alphaOffset, }, // 傳進來的ImageData的data部分 data ); } } // 複製一個imageData的data到一個buff裏 function copyImageDataBuff( imgData ) { var data = imgData.data, imgDataBuff = []; for( var i in data ) imgDataBuff[i] = data[i]; return imgDataBuff; } // 從buff按照指定座標複製像素點數據到目標imageData裏 function moveDot( imgData, dataBuff, x, y, srcX, srcY ) { var imgWidth = imgData.width, imgHeight = imgData.height, data = imgData.data; x = Math.floor(x); y = Math.floor(y); srcX = Math.floor(srcX); srcY = Math.floor(srcY); var targetStartOffset = y*imgHeight*4 + x*4, srcStartOffset = srcY*imgHeight*4 + srcX*4; for( var i = 0; i < 4; i++ ) data[ targetStartOffset + i ] = dataBuff[ srcStartOffset + i ]; } // 執行液化過程 // imgData 經過canvas的getImageData方法獲得的數據對象 // cx,cy 圓心座標 // mx,my 移動目標座標 // r 做用半徑 // strength 力度百分比(1-100) function liquify( imgData, cx, cy, mx, my, r, strenth ) { var imgDataBuff = copyImageDataBuff(imgData); eachCircleDot( imgData, cx, cy, r, function( posi ) { var tx = posi.x, ty = posi.y; var u = transFormula( cx, cy, mx, my, tx, ty, r, strenth ); moveDot( imgData, imgDataBuff, tx, ty, u.x, u.y ); function transFormula( cx, cy, mx, my, tx, ty, r, strenth ) { strenth = strenth || 100; var relativity = sqr(r) - distanceSqr( tx, ty, cx, cy ); var distanceMovedSqr = distanceSqr( mx, my, cx, cy ); var rate = sqr( relativity / ( relativity + distanceMovedSqr*(100/strenth) ) ); var ux = tx - rate * (mx-cx), uy = ty - rate * (my-cy); return { x:ux, y:uy }; } }); } // 掛到全局對象 global.LiquifyFilter = { liquify: liquify }; })(window);
使用它3d
先用canvas的code
getImageData();
方法獲取到要處理圖片的imageDataorm
全局做用域下調用對象
LiquifyFilter.liquify( imageData, 圓心X, 圓心Y, 目標點X, 目標點Y, 做用半徑, [力度百分比] );
完成轉換。blog
而後再用canvas的圖片
puImageData();
把轉換後的imageData輸出到canvas中作用域
效果圖以下