牛刀小试--仿QQ上传头像功能

前言

最近项目中遇到了一个需要网页上传图片,剪裁并且预览的这么一个功能,找了好多插件,但是都找不到合适的,后来看到一个开源的插件AlloyCrop,AlloyCrop是一款手机移动端图片剪裁组件。AlloyCrop这个项目是腾讯Web 前端团队发布的,适用于用户交互式体验。

AlloyCrop插件介绍

安装

可以通过npm来安装AlloyCrop组件。

1
npm install alloycrop --save

使用方法

AlloyCrop组件需要CSS3 transform库transform.js和移动手势库AlloyFinger的支持。

1
2
3
import './transform'
import './alloy-finger'
import 'alloy-crop'

API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
new AlloyCrop({
image_src: src,
circle: true,
width: 220,
height: 220,
output: 1, // 放缩比例
ok: (base64, canvas) => {
// 确定 to do sth
},
cancel: () => {
// 取消
},
ok_text: 'ok', // 确定按钮文字
cancel_text: 'cancel', // 取消按钮文字
})
参数 是否必填 意义
image_src 必须 需要裁剪图片的src地址
circle 不必须,默认是false 代表选取的是否是圆形还是矩形,默认是矩形,注意:圆形选取裁剪出来的也是正方形图片
width 必须 裁剪选区的宽
height 必须 裁剪选区的高
output 必须 输出的倍率。比如如果output为2,选区的宽300,选区的高100,输出的图像的分辨率为 (2×300,2×100
ok 必须 点击ok按钮的回调
cancel 必须 点击cancel按钮的回调
ok_text 不必须,默认是ok ok按钮的文本
cancel_text 不必须,默认是cancel cancel按钮的文本

上传图像代码

这个插件的使用方式比较简单,接下来看下完整的上传和预览代码

HTML

1
2
3
4
5
6
<input type="file" 
id="upload-img"
accept="image/*"
@change="choosePhoto($event)"
>
<img ref="img" src="" />

input的change方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
choosePhoto(event) {
const target = event.currentTarget
const file = target.files[0]
// 压缩,剪裁图片需要的一些元素和对象
const reader = new FileReader()
const img = new Image()

// 利用fileReader 预览图片
if (typeof FileReader === 'undefined') {
alert(浏览器不支持图片预览功能,请换浏览器重试)
} else {
// 实例化图片读取
reader.readAsDataURL(file) // 将文件读取为base64
reader.onload = () => {
this.clipImg(reader.result) // 剪裁图片的方法,下面介绍
// 将input的value值置为空,防止两次不能选到同一张图片
target.value = ''
}
}
},

FileReader介绍

FileReader 的实例拥有 4 个方法,其中 3 个用以读取文件,另一个用来中断读取。下面的表格列出了这些方法以及他们的参数和功能,需要注意的是 ,无论读取成功或失败,方法并不会返回读取结果,这一结果存储在 result属性中。

方法名 参数 描述
abort none 中断读取
readAsBinaryString file 将文件读取为二进制码
readAsDataURL file 将文件读取为 DataURL
readAsText file, [encoding] 将文件读取为文本

readAsText:该方法有两个参数,其中第二个参数是文本的编码方式,默认值为 UTF-8。这个方法非常容易理解,将文件以文本方式读取,读取的结果即是这个文本文件中的内容。
readAsBinaryString:该方法将文件读取为二进制字符串,通常我们将它传送到后端,后端可以通过这段字符串存储文件。
readAsDataURL:这是例子中用到的方法,该方法将文件读取为一段以 data: 开头的字符串(base64),这段字符串的实质就是 Data URL。

图片裁剪

接下来就是利用上面介绍的那个插件alloycrop来对图片进行裁剪

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
clipImg(src) {
new AlloyCrop({
image_src: src,
circle: true,
width: 220,
height: 220,
output: 1,
ok: (base64, canvas) => {
this.$refs.img.src = base64
},
cancel: () => { },
ok_text: '确定',
cancel_text: '取消',
})
},

到现在为止,就可以很好的上传、裁剪、预览图片了

exif介绍

但是在上传的过程中,也发现了一个问题,在三星或OS上传图片有一个旋转角度的问题,查了一下这个问题,发现:iphone手机拍摄的照片,默认的是home键在右方的横屏拍摄方式,其他方式拍摄就会出现一个旋转的问题。

为了解决这个问题,我们利用exif这个插件,这个插件提供了 JavaScript 读取图像的原始数据的功能扩展,例如:拍照方向、相机设备型号、拍摄时间、ISO 感光度、GPS 地理位置等数据。
需要注意的是:exif多用于移动端开发,PC 端也会用到,此插件兼容主流浏览器,IE10 以下不支持。

exif 的使用方式

1
2
3
4
5
6
7
8
9
npm install exif --save
import 'exif-js/exif.js'
// 或者直接下载exif.js文件
import { EXIF } from './exif.js'

EXIF.getData(document.getElementById('imgElement'), function() {
EXIF.getAllTags(this) // 获取全部信息
EXIF.getTag(this, 'Orientation') // 获取拍摄信息
});

API方法

方法 描述
EXIF.getData(img, callback) 获取图像的数据能兼容尚未支持提供 EXIF 数据的浏览器获取到元数据。
EXIF.getTag(img, tag) 获取图像的某个数据
EXIF.getAllTags(img) 获取图像的全部数据,值以对象的方式返回
EXIF.pretty(img) 获取图像的全部数据,值以字符串的方式返回

EXIF标识就有很多了,这里就不列举出来,有兴趣的可以看看exif的文档
我们这里需要的就是Orientation拍摄方向

Orientation

旋转角度 参数
1,默认home键在右方
顺时针90° 6,iphone竖屏拍摄,此时home键在下方(正常拿手机的方向)
逆时针90° 8,iphone竖屏拍摄,此时home键在上方
180° 8,iphone横屏拍摄,此时home键在左侧

注意: 有些手机是获取不到这些信息的,都会返回undefined,如小米4S

获取到了拍摄角度,那我们在裁剪之后,再旋转对应的角度就可以了,这里就不贴具体的代码了,大家随意发挥。

thank you

感谢支持,我会不断进步