以前看过一篇写关于图片滤镜的文章,蛮有兴趣,所以做出了这个小 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 重写”辨色“小游戏