Canvas 进阶(五)实现图片滤镜效果
背景
之前看过一篇写关于图片滤镜的文章,蛮有兴趣,因此作出了这个小 DEMO,可以切换多种图片滤镜并提供图片下载功能。
实现
- 使用工具:
vue iview canvas
- 实现功能:图片绘制,滤镜修改,图片下载
- 关键点:
ctx.getImageData()
ctx.putImageData()
ctx.drawImage()
滤镜逻辑
1. 引入 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>
复制代码
2. 设计整体静态页面
首先页面须有两个 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">
复制代码
3. 选择图片并绘制
通过 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);
},
}
复制代码
4. 选择滤镜并绘制新图片
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 中
},
5. 下载图片
通过对新的 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插件
Canvas 进阶(三)ts + canvas 重写”辨色“小游戏
- [编程经验] Python中的modlue和packages的区别
- Day5上午解题报告
- [编程经验] Pandas中比较好用的几个方法
- [编程经验] Elasticsearch 初识
- 2017.10.26水题大作战部分题解
- 2017.10.27涩会题大乱斗部分题解
- 【 关关的刷题日记50】 Leetcode 345. Reverse Vowels of a String
- Day1上午解题报告
- 【 关关的刷题日记51】 Leetcode 67. Add Binary
- 【 关关的刷题日记53】 Leetcode 100. Same Tree
- Day1下午解题报告
- 【关关的刷题日记54】Leetcode 226. Invert Binary Tree
- Day2上午解题报告
- 【关关的刷题日记55】Leetcode 404. Sum of Left Leaves
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法