之前看过一篇写关于图片滤镜的文章,蛮有兴趣,因此作出了这个小 DEMO,可以切换多种图片滤镜并提供图片下载功能。
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 标签,一个绘制原始图片,一个绘制添加滤镜效果的图片。当然还有图片上传下载按钮,以及滤镜选择框,具体如下:
"text-center">
"pictureMode"
style="width:200px"
placeholder="请选择图像模式"
@on-change="selectMode"
>
"item in selectList" :value="item.value" :key="item.value"
>{{ item.label }}
"margin: 24px 0">
"ios-cloud-upload-outline" type="primary" @click="$refs.input.click()"
>上传图片
"ios-cloud-download-outline" type="primary" @click="downloadImage"
>下载图片
type="file" ref="input" @change="uploadImage" style="display: none;" />
"origin" :width="width" :height="height" v-show="image">
"new" :width="width" :height="height" v-show="image">
复制代码
通过 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
,即得到我们的目标图片.
// 画出目标图像
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 属性并触发点击下载事件实现下载图片功能
// 下载图片
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);
是根据不同滤镜进行图片处理。这里简单介绍几种图像滤镜:
将颜色的RGB设置为相同的值即可使得图片为灰色,一般处理方法有:
1、取三种颜色的平均值
2、取三种颜色的最大值(最小值)
3、加权平均值: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插件