Canvas 進階(五)實現圖片濾鏡效果

背景

以前看過一篇寫關於圖片濾鏡的文章,蠻有興趣,所以做出了這個小 DEMO,能夠切換多種圖片濾鏡並提供圖片下載功能。javascript

話很少說,先上 demogithub地址. css

實現

  • 使用工具:vue iview canvas
  • 實現功能:圖片繪製,濾鏡修改,圖片下載
  • 關鍵點:ctx.getImageData() ctx.putImageData() ctx.drawImage() 濾鏡邏輯

1. 引入 iviewvue

<link rel="stylesheet" type="text/css" href="https://unpkg.com/view-design/dist/styles/iview.css" />
<script type="text/javascript" src="https://vuejs.org/js/vue.min.js"></script>
<script type="text/javascript" src="https://unpkg.com/view-design/dist/iview.min.js"></script>
複製代碼

2. 設計總體靜態頁面

首先頁面須有兩個 canvas 標籤,一個繪製原始圖片,一個繪製添加濾鏡效果的圖片。固然還有圖片上傳下載按鈕,以及濾鏡選擇框,具體以下:html

<div class="text-center">
    <div>
        <i-select
            v-model="pictureMode"
            style="width:200px"
            placeholder="請選擇圖像模式"
            @on-change="selectMode"
        >
            <i-option v-for="item in selectList" :value="item.value" :key="item.value"
                >{{ item.label }}</i-option
            >
        </i-select>
    </div>
    <div style="margin: 24px 0">
        <i-button icon="ios-cloud-upload-outline" type="primary" @click="$refs.input.click()"
            >上傳圖片</i-button
        >
        <i-button icon="ios-cloud-download-outline" type="primary" @click="downloadImage"
            >下載圖片</i-button
        >
        <input type="file" ref="input" @change="uploadImage" style="display: none;" />
    </div>
    <div>
        <!-- 用戶原始圖片 -->
        <canvas id="origin" :width="width" :height="height" v-show="image"></canvas>
        <!-- 目標圖片 -->
        <canvas id="new" :width="width" :height="height" v-show="image"></canvas>
    </div>
</div>
複製代碼

3. 選擇圖片並繪製

經過 input 標籤獲取選擇的 file 文件,將其轉化爲 base64 字符串後賦值給 imagesrc 屬性,待圖片加載完成後在兩個 canvas 中進行繪製,此爲原始圖片。前端

methods: {
    // 上傳圖片
    uploadImage(e) {
        var that = this;
        var file = e.target.files[0];
        if (typeof FileReader === 'undefined') {
            alert('您的瀏覽器不支持圖片上傳,請升級您的瀏覽器');
            return false;
        }
        var image = new Image(); // 建立圖片
        image.crossOrigin = 'Anonymous'; // 解決一些跨域問題
        image.onload = function() {
            that.width = image.width; // 設置canvas的寬
            that.height = image.height; // 設置canvas的高
            that.image = image;
            // 等待canvas的寬高屬性渲染完畢繪製canvas
            that.$nextTick(() => { 
                that.drawOriginImage(image);
            })
        };
        let reader = new FileReader();
        reader.readAsDataURL(file); // 生成base64
        reader.onload = e => {
            image.src = e.target.result;
        };
    },
    // 畫出原始圖像
    drawOriginImage(image) {
        var canvasOrigin = document.getElementById('origin');
        var ctxOrigin = canvasOrigin.getContext('2d');
        var canvasNew = document.getElementById('new');
        var ctxNew = canvasNew.getContext('2d');
        ctxOrigin.drawImage(image, 0, 0, image.width, image.height);
        ctxNew.drawImage(image, 0, 0, image.width, image.height);
    },
}
複製代碼

4. 選擇濾鏡並繪製新圖片

canvas 中的 ctx 對象提供了一個方法 getImageData(), 該方法可返回某個區域內每一個像素點的數值的組成的數組(例如:ImageData { width: 100, height: 100, data: Uint8ClampedArray[40000] }),data 數組中 4 個元素表示一個像素點的 rgba 值。經過對此數組每四個元素值的修改,而後從新繪製成新的 canvas,即獲得咱們的目標圖片.vue

// 畫出目標圖像
drawImage() {
    var canvasOrigin = document.getElementById('origin');
    var ctxOrigin = canvasOrigin.getContext('2d');
    var canvasNew = document.getElementById('new');
    var ctxNew = canvasNew.getContext('2d'); 
    var imageData = ctxOrigin.getImageData(0, 0, this.width, this.height);
    var data = imageData.data; // 獲取原始圖像每個像素
    this.chooseFilter(data, canvasNew, imageData); // 根據選擇的濾鏡處理數組
    ctxNew.putImageData(imageData, 0, 0); // 將處理的原圖像的數據繪製到新圖像的 canvas 中
},
複製代碼

5. 下載圖片

經過對新的 canvas 調用 toDataURL() 返回一個包含圖片展現的 data URI, 將其賦值的新的圖片的 src 屬性並觸發點擊下載事件實現下載圖片功能java

// 下載圖片
downloadImage(image, name) {
    if (!this.image) {
        this.$Modal.error({
            title: '錯誤',
            content: '請上傳圖片先啦!!',
        });
        return;
    }
    var image = new Image();
    var canvas = document.getElementById('new');
    image.src = canvas.toDataURL();
    this.downLoad(image, 'suporka-image-filter.jpg');
},
// 下載
downLoad(image, name) {
    const dataURL = image.src;
    const link = document.createElement('a');
    link.download = name;
    link.href = dataURL;
    link.dispatchEvent(new MouseEvent('click'));
},
複製代碼

圖像濾鏡

this.chooseFilter(data, canvasNew, imageData); 是根據不一樣濾鏡進行圖片處理。這裏簡單介紹幾種圖像濾鏡:webpack

灰度濾鏡

將顏色的RGB設置爲相同的值便可使得圖片爲灰色,通常處理方法有:ios

一、取三種顏色的平均值git

二、取三種顏色的最大值(最小值)github

三、加權平均值:0.3R + 0.59G + 0.11*B

本文用的是第一種方法

for(var i = 0; i < data.length; i+=4) {
     var grey = (data[i] + data[i+1] + data[i+2]) / 3;
     data[i] = data[i+1] = data[i+2] = grey;
}
複製代碼

黑白濾鏡

顧名思義,就是圖片的顏色只有黑色和白色,能夠計算rgb的平均值arg,arg>=100,r=g=b=255,不然均爲0

for(var i = 0; i < data.length; i += 4) {
     var avg = (data[i] + data[i+1] + data[i+2]) / 3;
     data[i] = data[i+1] = data[i+2] = avg >= 100 ? 255 : 0;
}
複製代碼

反向濾鏡

取 RGB 三種顏色分別取 255 的差值。

for(var i = 0; i < data.length; i+= 4) {
      data[i] = 255 - data[i];
      data[i + 1] = 255 - data[i + 1];
      data[i + 2] = 255 - data[i + 2];
}
複製代碼

去色濾鏡

rgb三種顏色取三種顏色的最值的平均值。

for(var i = 0; i < data.length; i++) {
   var avg = Math.floor((Math.min(data[i], data[i+1], data[i+2]) + Math.max(data[i], data[i+1], data[i+2])) / 2 );
   data[i] = data[i+1] = data[i+2] = avg;
}
複製代碼

單色濾鏡

只保留一種顏色,其餘顏色設爲0

for(var i = 0; i < canvas.height * canvas.width; i++) {
    data[i*4 + 2] = 0;
    data[i*4 + 1] = 0;
}
複製代碼

牛頓說: 「我只是站在了巨人的肩膀上」。更多詳細的濾鏡請移步巨人的肩膀:《圖像處理的濾鏡算法》( ̄▽ ̄)~*

總結

本案例主要是對 canvasctx.getImageData,ctx.putImageData()'; 及圖片數據處理的運用實現咱們想要的效果。後續還有 canvas 系列的相關文章,敬請期待!

更多推薦

前端進階小書(advanced_front_end)

前端每日一題(daily-question)

webpack4 搭建 Vue 應用(createVue)

Canvas 進階(一)二維碼的生成與掃碼識別

Canvas 進階(二)寫一個生成帶logo的二維碼npm插件

Canvas 進階(三)ts + canvas 重寫」辨色「小遊戲

Canvas 進階(四)實現一個「刮刮樂」遊戲

相關文章
相關標籤/搜索