以前看過一篇寫關於圖片濾鏡的文章,蠻有興趣,所以做出了這個小 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插件
Canvas 進階(三)ts + canvas 重寫」辨色「小遊戲