1. 程式人生 > >使用canvas實現區域性高斯模糊效果

使用canvas實現區域性高斯模糊效果

這個功能目的是為了模糊一些人的臉部,一些文字資訊。主要用於使用者手動操作進行模糊。
實現的功能:本人實現了再pc端上面進行拖拽模糊。

實現思路:

  • 首先載入圖片,然後再載入完成的回撥中,建立一個高斯模糊過的圖片畫布。
  • 接著,繫結滑鼠的互動事件,在裡面獲取到在畫布上面點選的位置。通過位置獲取當前位置周圍的影象資料,進行生成圓形資料。
  • 最後,使用drawImage將資料覆蓋到畫布上面。

是不是感覺思路很簡單,其實實現起來也不那麼難。
接下來,檢視一下案例:地址還沒有

最後付上原始碼

<!doctype html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>canvas畫布的高斯模糊效果</title>
</head>
<body>
<canvas id="canvas" style="margin: 0 auto; display: block;"></canvas>
<canvas id="pixelCanvas"></canvas>
</body>
<script>
    var canvas = document.getElementById('canvas');
    let img = new Image();
    //這裡直接修改圖片的路徑
    img.src = "meinv.jpg";
    img.onload = function () {
        let bufferCanvas = document.createElement("canvas");
        let ctx = bufferCanvas.getContext('2d');

        let canvasCtx = canvas.getContext("2d");
        //設定canvas的寬高
        canvas.height = bufferCanvas.height = img.height;
        canvas.width = bufferCanvas.width = img.width;
        //將影象繪製到canvas上面
        ctx.drawImage(img, 0, 0, img.width, img.height);
        canvasCtx.drawImage(img, 0, 0, img.width, img.height);
        //從畫布獲取一半影象
        var emptyData = ctx.getImageData(0, 0, img.width, img.height);
        //將影象資料進行高斯模糊 data.data是一個數組,每四個值代表一個畫素點的rgba的值,data.width data.height 分別代表影象資料的寬高
        emptyData = gaussBlur(emptyData);
        //將模糊的影象資料再渲染到畫布上面
        ctx.putImageData(emptyData, 0, 0);

        //繫結點選事件,然後通過點選事件獲取當前點選區域進行繪製
        canvas.addEventListener("mousedown", function (e) {
            //獲取到canvas距離視窗左上角的位置
            let offsetLeft = canvas.getBoundingClientRect().left;
            let offsetTop = canvas.getBoundingClientRect().top;

            //獲取到滑鼠距離視窗左上角的
            let downX = e.clientX - offsetLeft;
            let downY = e.clientY - offsetTop;

            //console.log(downX, downY);

            generatePixel(downX, downY);

            function mouseMove(event) {
                let moveX = event.clientX - offsetLeft;
                let moveY = event.clientY - offsetTop;

                //console.log(moveX, moveY);

                generatePixel(moveX, moveY);
            }

            function mouseUp() {
                document.removeEventListener("mousemove", mouseMove);
                document.removeEventListener("mouseup", mouseUp);
            }

            document.addEventListener("mousemove", mouseMove);
            document.addEventListener("mouseup", mouseUp)
        });

        //根據座標位置,生成附近九畫素的貼圖
        let num = 50;
        let r = num / 2;
        let pixelCanvas = document.createElement("canvas");
        pixelCanvas.width = pixelCanvas.height = num;
        let pixelCtx = pixelCanvas.getContext("2d");

        function generatePixel(x, y) {
            let imageData = ctx.getImageData(x - r, y - r, num, num);

            pixelCtx.putImageData(imageData, 0, 0);

            //x右側超出
            if (x + r > bufferCanvas.width) {
                let imageData = ctx.getImageData(0, y - r, x + r - bufferCanvas.width, num);
                pixelCtx.putImageData(imageData, bufferCanvas.width - x + num / 2, 0);
            }

            //x左側超出
            if (x - r < 0) {
                let imageData = ctx.getImageData(bufferCanvas.width + x - r, y - r, -x + r, num);
                pixelCtx.putImageData(imageData, 0, 0);
            }

            //將繪製的正方形裁剪成圓形
            pixelCtx.fillStyle = pixelCtx.createPattern(pixelCanvas, 'no-repeat');
            pixelCtx.clearRect(0, 0, num, num);
            pixelCtx.arc(r, r, r, 0, Math.PI * 2);
            pixelCtx.fill();

            canvasCtx.drawImage(pixelCanvas, x - r, y - r, num, num);

            //如果是在左右邊緣,需要繪製兩次
            if(x + r > bufferCanvas.width){
                canvasCtx.drawImage(pixelCanvas, x-r-bufferCanvas.width, y-r);
            }

            if(x-r < 0){
                canvasCtx.drawImage(pixelCanvas, x-r+bufferCanvas.width, y-r);
            }
        }
    };

    //將資料進行高斯模糊
    function gaussBlur(imgData) {
        var pixes = imgData.data;
        var width = imgData.width;
        var height = imgData.height;
        var gaussMatrix = [],
            gaussSum = 0,
            x, y,
            r, g, b, a,
            i, j, k, len;

        var radius = 10;
        var sigma = 5;

        a = 1 / (Math.sqrt(2 * Math.PI) * sigma);
        b = -1 / (2 * sigma * sigma);
        //生成高斯矩陣
        for (i = 0, x = -radius; x <= radius; x++, i++) {
            g = a * Math.exp(b * x * x);
            gaussMatrix[i] = g;
            gaussSum += g;

        }

        //歸一化, 保證高斯矩陣的值在[0,1]之間
        for (i = 0, len = gaussMatrix.length; i < len; i++) {
            gaussMatrix[i] /= gaussSum;
        }
        //x 方向一維高斯運算
        for (y = 0; y < height; y++) {
            for (x = 0; x < width; x++) {
                r = g = b = a = 0;
                gaussSum = 0;
                for (j = -radius; j <= radius; j++) {
                    k = x + j;
                    if (k >= 0 && k < width) {//確保 k 沒超出 x 的範圍
                        //r,g,b,a 四個一組
                        i = (y * width + k) * 4;
                        r += pixes[i] * gaussMatrix[j + radius];
                        g += pixes[i + 1] * gaussMatrix[j + radius];
                        b += pixes[i + 2] * gaussMatrix[j + radius];
                        // a += pixes[i + 3] * gaussMatrix[j];
                        gaussSum += gaussMatrix[j + radius];
                    }
                }
                i = (y * width + x) * 4;
                // 除以 gaussSum 是為了消除處於邊緣的畫素, 高斯運算不足的問題
                // console.log(gaussSum)
                pixes[i] = r / gaussSum;
                pixes[i + 1] = g / gaussSum;
                pixes[i + 2] = b / gaussSum;
                // pixes[i + 3] = a ;
            }
        }
        //y 方向一維高斯運算
        for (x = 0; x < width; x++) {
            for (y = 0; y < height; y++) {
                r = g = b = a = 0;
                gaussSum = 0;
                for (j = -radius; j <= radius; j++) {
                    k = y + j;
                    if (k >= 0 && k < height) {//確保 k 沒超出 y 的範圍
                        i = (k * width + x) * 4;
                        r += pixes[i] * gaussMatrix[j + radius];
                        g += pixes[i + 1] * gaussMatrix[j + radius];
                        b += pixes[i + 2] * gaussMatrix[j + radius];
                        // a += pixes[i + 3] * gaussMatrix[j];
                        gaussSum += gaussMatrix[j + radius];
                    }
                }
                i = (y * width + x) * 4;
                pixes[i] = r / gaussSum;
                pixes[i + 1] = g / gaussSum;
                pixes[i + 2] = b / gaussSum;
            }
        }
        return imgData;
    }
</script>
</html>