以前看過一篇寫關於圖片濾鏡的文章,蠻有興趣,所以做出了這個小 DEMO,能夠切換多種圖片濾鏡並提供圖片下載功能。javascript
vue iview canvas
ctx.getImageData()
ctx.putImageData()
ctx.drawImage()
濾鏡邏輯iview
與 vue
<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> 複製代碼
首先頁面須有兩個 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> 複製代碼
經過 input
標籤獲取選擇的 file
文件,將其轉化爲 base64
字符串後賦值給 image
的 src
屬性,待圖片加載完成後在兩個 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); }, } 複製代碼
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 中 }, 複製代碼
經過對新的 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; } 複製代碼
牛頓說: 「我只是站在了巨人的肩膀上」。更多詳細的濾鏡請移步巨人的肩膀:《圖像處理的濾鏡算法》( ̄▽ ̄)~*
本案例主要是對 canvas
的 ctx.getImageData
,ctx.putImageData()';
及圖片數據處理的運用實現咱們想要的效果。後續還有 canvas
系列的相關文章,敬請期待!
Canvas 進階(二)寫一個生成帶logo的二維碼npm插件