Web前端

前端,技术,生活的艺术


  • Startseite

  • Über

  • Tags

  • Kategorien

  • Archiv

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

Veröffentlicht am 2017-12-19 |

前言

最近项目中遇到了一个需要网页上传图片,剪裁并且预览的这么一个功能,找了好多插件,但是都找不到合适的,后来看到一个开源的插件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

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

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

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

thank you

数组的操作-迭代方法

Veröffentlicht am 2017-11-19 |

前言

JavaScript数组的迭代方法,前端开发人员想必都不陌生了,在开发中基本上都会用上其中的一种或是几种,说起迭代方法,可能很多人首先想到的就是for循环,我之前遍历数组的时候用的最多的就是for,但是在开始使用的迭代方法之后,就很少使用for循环进行数组操作了,我最常用的是 map和foreach ,也会使用every和some,如果使用得当,不仅可以减少代码冗余,提升可读性和可维护性,也可以在一定程度上优化性能。

es5新增了几个数组的迭代方法(every,filter,forEach,map,some),好了,废话不多说,下面开始进入到我们的正题,介绍各个方法的使用情况。
参数:这些方法都接收两个参数callback是数组每一项执行的那个指定函数,使用三个参数,thisArg(可选,执行 callback 函数时 使用的this 值)callback参数如下

  • currentValue,第一个参数,数组中正在处理的当前元素
  • index,第二个参数,数组中正在处理的当前元素的索引 (可选)
  • array,方法被调用的数组(可选)

1.map方法

为数组每一项执行一个指定函数,返回每次函数调用的结果组成的数组。这个是我使用的最多的,eg: 在react中循环输出组件

??? 试想一下,现在有一个需求,给定一个数组,然后给数组里面的每一项加1

我们先来看下for循环是怎么实现的

1
2
3
4
let array = [3, 6, 12, 33, 52]  
for(let i = 0; i < array.length; i++) {
array[i] += 1
}

map方法实现

1
array = array.map(function(item) {return item + 1})

map的es6写法

1
array = array.map(item => item + 1)

2.forEach方法

forEach()对数组中的每一项运行给定函数 ;简单点来说,本质上跟for没有区别,只是写法不一样。 它的使用也和map类似,参数也类似,只是这个方法没有返回值。这个方法比较简单,只说一些注意的点吧:

  • forEach和map一样,如果数组在迭代时被修改了,则其他元素会被跳过,看下面一个例子
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    let words = ["one", "two", "three", "four"];
    words.forEach((word) => {
    console.log(word);
    if (word === "two") {
    words.shift();
    }
    });
    // one
    // two
    // four

当word=“two”时,删掉数组的第一个元素,那么数组的每一项都会往前移动一个位置,因为元素 “four”现在在数组更前的位置,”three”会被跳过。 forEach()不会在迭代之前创建数组的副本。

  • forEach没有办法中止或者跳出循环,除非抛出一个异常。

3.filter方法

filter():对数组中的每一项运行给定函数。该函数会返回true的项组成的数组。

筛选出分数大于60分的同学。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let stu = [
{
name: 'mabiao',
score: 43,
flower: 3
},
{
name: 'haohua',
score: 87,
flower: 2
},
{
name: 'dahuang',
score: 74,
flower: 4
},
]
let qua_stu = stu.filter(item => item.score >= 60)
console.log(qua_stu)

还有一个应用是数组去重,用filter也是比较容易实现的。eg:
先来看看for循环是怎么实现的

1
2
3
4
5
6
7
var newarr = [],
arr = ['hello', 'hi', 'world', 'hello', 'apple', 'world', 'dream']
for(var i = 0,len = arr.length;i < len; i++){
if(newarr.indexOf(arr[i])===-1){
newarr.push(arr[i]);
}
}

再来对比一下filter,是不是精简了很多。

1
2
3
newarr = arr.filter((item,index,self) => {
return self.indexOf(item) == index
})

4.Every和Some

因为这两个方法的使用场景很像,所以就一起写了。
every()对数组中的每一项运行给定函数,如果该函数对每一项都返回true,则返回true;
some()对数组中的每一项运行给定函数,如果该函数对任一项返回true,则返回true;
every相当于并且,some相当于或

检测数组中的所有元素是否都大于 10
1
2
3
let arr = [12, 5, 8, 13, 44]
var passed = arr.every((item) => item > 10)
console.log(passed) // false
检测在数组中是否有元素大于 10
1
2
passed = arr.some((item) => item > 10)
console.log(passed) //true

补充 (es6新增的数组迭代方法)

1.for-of

介绍for of之前,先来看看什么是Iterator(遍历器)以下摘至es6标准入门的Iterator,只有一部分定义,要了解更多的可以去看看可以去看阮大神的es6标准入门
  • JavaScript 原有的表示“集合”的数据结构,主要是数组(Array)和对象(Object),ES6 又添加了Map和Set。这样就有了四种数据集合,用户还可以组合使用它们,定义自己的数据结构,比如数组的成员是Map,Map的成员是对象。这样就需要一种统一的接口机制,来处理所有不同的数据结构。
  • 遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
  • Iterator 的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是 ES6 创造了一种新的遍历命令for…of循环,Iterator 接口主要供for…of消费。
  • 那么问题来了,一个数据结构只要部署了Symbol.iterator属性,就被视为具有 iterator 接口,就可以用for…of循环遍历它的成员。也就是说,for…of循环内部调用的是数据结构的Symbol.iterator方法。
  • for…of循环可以使用的范围包括数组、Set 和 Map 结构、某些类似数组的对象(比如arguments对象、DOM NodeList 对象)、Generator 对象,以及字符串。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    const arr = ['red', 'green', 'blue'];

    for(let v of arr) {
    console.log(v); // red green blue
    }

    const obj = {};
    obj[Symbol.iterator] = arr[Symbol.iterator].bind(arr);

    for(let v of obj) {
    console.log(v); // red green blue
    }

空对象obj部署了数组arr的Symbol.iterator属性,结果obj的for…of循环,产生了与arr完全一样的结果。

for in 循环是获得键值,for of 允许遍历获得值

1
2
3
4
5
6
7
8
9
var arr = ['a', 'b', 'c', 'd'];

for (let a in arr) {
console.log(a); // 0 1 2 3
}

for (let a of arr) {
console.log(a); // a b c d
}

for…of循环调用遍历器接口,数组的遍历器接口只返回具有数字索引的属性。

1
2
3
4
5
6
let arr = [3, 5, 7];
arr.foo = 'hello';

for (let i of arr) {
console.log(i); // "3", "5", "7"
}

好了,先介绍到这里,这里只是简单描述了数组迭代方法的使用,具体项目中的使用还需要大家亲自操刀实践,实践才能出真知,谢谢!!!

Hello

Veröffentlicht am 2017-11-19 |

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment

12

hupei

13 Artikel
© 2018 hupei
Erstellt mit Hexo
|
Theme — NexT.Mist v5.1.3