hello 啊,every body,大家好,之前咱们(其实只有我本人啦,为什么用咱们呢?可能是为了装作我有一个团队吧,也可能只是为了顺口????)公布了
hevue-img-preview 2.0
版本,减少了多图预览的性能,收到了宽广用户的好评 (其实并没有用户),咱们的产品经理(其实就是我)又贴心的减少了局部性能,推出了 2.5 版本,上面大家来跟我一起看看都有哪些新性能以及如何实现的吧 O(∩_∩)O
本次更新内容
- 版本号:2.5.2
- 减少键盘管制性能,管制退出预览,翻页、旋转缩放等
- 减少图片加载中,加载失败的显示成果
- 顺便修复了 z -index 显示层级不够高的问题
- 未装置的同学间接装置就好,已装置的同学能够带上版本号更新
npm i hevue-img-preview@2.5.2 --save
- 具体介绍文章:https://juejin.im/post/5ee6377b51882542ea2b50ec
- GitHub 地址:https://github.com/heyongsheng/hevue-img-preview
减少键盘事件
需要及理由
因为本款组件是面向 pc 端的一款图片预览组件,所以咱们的用户天然都是 pc 用户,那么其中必然有一部分用户是用的笔记本,那么这一部分中的一部分人可能习惯用触控板加键盘的形式进行操作,通常 pc 的屏幕都是比拟大的,所以让用户用手指在触控板上挪动好几下从上一张的按钮挪动到下一张的按钮,在挪动到敞开的按钮上是很吃力的(就算用鼠标也有那么一丢丢不不便),所以咱们贴心的推出了键盘管制性能,左手键盘点点,就能实现组件的简直所有性能,真香!上面就来看看咱们平凡的工程师(还是我)是如何实现的吧
代码实现
提醒:如果不关怀代码的同学只看思路就行了,疏忽掉代码局部,纵使你可能不须要此组件,也对性能不感兴趣,我也心愿你能急躁往下看看,兴许你能从中学到局部思路呢
留神
本文所有的代码批改都是在 2.0 版本的代码根底之上进行批改,如果有同学感兴趣的话能够到我的 GitHub 上下载历史版本源码进行对照浏览
让用户抉择是否应用键盘事件
首先呢,咱们要足够的为用户思考,就算咱们加了这个性能,也要看用户需不需要,再加上咱们这里是增加的键盘事件,有可能会笼罩掉用户的默认键盘事件或者导致反复触发,所以咱们必须让用户抉择是否启用此性能,咱们能够把这个开关作为一个配置项,让用户调用的时候传入进来。
props: {
...
keyboard: { // 是否启用键盘事件
type: Boolean
default: false // 默认不启用
}
}
// 而后在组件被触发的时候判断是否启用
if (this.keyboard) {document.addEventListener('keydown', fn) // fn 为键盘事件名
}
// 留神如果咱们启用了键盘事件要在敞开组件时登记掉
if (this.keyboard) {document.removeEventListener('keydown', fn) // fn 为键盘事件名
}
做一个繁难的防抖函数
留神,因为作者满腹经纶,编写代码时候还不晓得节流,只晓得防抖,所以不晓得以下所属状况其实合乎节流的场景,然而切实懒得改了,这一段内容大家参考参考就好,等我悟透了防抖和节流再专门写一篇文章讲讲。
大家看到了咱们下面增加的是 keydown
事件,也就是如果用户不松手是能够始终触发这个事件的,因为咱们给图片的缩放和旋转都增加了动画成果,执行工夫为 0.3s,所以当键盘事件间断两次触发距离小于 0.3s 时,画面就会呈现抖动景象,所以咱们这里要制作一个繁难的防抖
/*
* 此段代码为上面的示例,请先浏览下文
* fn [function] 须要防抖的函数
* delay [number] 毫秒,防抖期限值
*/
function debounce(fn,delay){
let timer = null // 借助闭包
return function() {if(timer){clearTimeout(timer) // 进入该分支语句,阐明以后正在一个计时过程中,并且又触发了雷同事件。所以要勾销以后的计时,从新开始计时
timer = setTimeOut(fn,delay)
}else{timer = setTimeOut(fn,delay) // 进入该分支阐明以后并没有在计时,那么就开始一个计时
}
}
}
传统的防抖函数是如果用户在规定的工夫内间断触发事件,那么只有用户最初一次触法事件会失效,例如咱们规定工夫是 0.5s,用户触发了一次事件(例如点击事件),又在 0.5s 内又触发了一次该事件,那么第一次事件就不会执行,如果用户在触发第二次事件后的 0.5s 内又触发了雷同的事件,那么这个事件还不会执行,直到用户最初一次触发事件并且在 0.5s 内没有触发雷同事件时,才会执行此事件(具体代码能够参考下面的代码,如果想具体理解能够去搜寻一下)。然而咱们这里显然不能这么做,如果用户间断点击,那么这个事件永远都不会执行,咱们须要让用户触发的事件先失效,而后在规定的工夫内再触发次工夫不失效,等到规定的工夫过来再让此事件可被触发。
// 咱们当初 data 里定义一个变量,管制咱们的事件是否可被触发,此处不思考封装防抖函数,前面有机会能够独自讲一下防抖
canRun: true // 默认为 true,事件第一次触发可被触发
// 而后咱们在 methods 里定义这个防抖函数
keyHandleDebounce (e) {if (this.canRun) {
// 如果 this.canRun 为 true 证实以后能够执行函数
this.keyHandle(e) // keyHandle 为键盘触发事件,上面会讲
this.canRun = false // 执行函数后一段时间内不可再次执行
setTimeout(() => {this.canRun = true // 等到了咱们设定的工夫之后,把 this.canRun 改为 true,能够再次执行函数}, 300) // 此处咱们把规定距离事件固定为 0.3s,也就是动画执行的工夫
}
},
// 这时候咱们能够在增加是移除键盘事件的中央,把触发键盘事件改为触发这个防抖事件
document.addEventListener('keydown', this.keyHandleDebounce)
document.removeEventListener('keydown', this.keyHandleDebounce)
这样咱们就达到了防抖又可间断触发键盘事件的目标
监听键盘事件
监听键盘事件就没啥好说的了,监听用户按下的对应按钮,而后触发对应事件就好了,须要留神的是上一张和下一张只有在多图预览时才无效,这里咱们没有用 ↑
↓
←
→
键来操作放大、放大、上一张、下一张,而是采纳了 w
s
a
d
键来操作相应性能,这是为什么呢,当然是尽量把功能键都放在左手边啊,要不功能键右边左边分散开来多不不便啊,这样也不便用户左右操作,右手在触控板上管制图片挪动嘛(其实是因为高低键会导致页面滚动,而作者没有找到很好的方法),为啥没有管制图片挪动的?当然是因为用户右手曾经空进去了,无论是用触控板还是鼠标挪动都很不便嘛!而且用键盘也不好管制挪动的间隔,体验不怎么好的
keyHandle (e) {
var e = window.event || e
var key = e.keyCode || e.which || e.charCode
switch (key) {
case 27: // esc
this.close()
break
case 65: // a 键 - 上一张
if (this.multiple) {this.toogleImg(false)
}
break
case 68: // d 键 - 下一张
if (this.multiple) {this.toogleImg(true)
}
break
case 87: // w 键 - 放大
this.scaleFunc(0.15)
break
case 83: // s 键 - 放大
this.scaleFunc(-0.15)
break
case 81: // q 键 - 逆时针旋转
this.rotateFunc(-90)
break
case 69: // e 键 - 顺时针旋转
this.rotateFunc(90)
break
case 82: // r 键 - 复位键
this.initImg()
break
default:
break
}
}
减少图片加载期待成果
因为有些图片来自于网络或者服务器加载,因为图片体积或者网速较慢的起因可能会加载图片较慢,为了良好的用户体验,咱们须要给用户展现一个正在加载的成果,如果因为各种起因(网络不好,图片地址有效等)导致图片加载失败的话,咱们也得给用户一个反馈的成果
首先这里修复一个 bug,之前咱们的图片切换等都是间接批改 prop
里传入的 url 值,当然这样是不标准的,咱们在子组件里不能间接怼 prop 里的值进行批改,尽管不晓得为啥没报错。。。刚好咱们这里要实现图片加载期待的成果,这个不标准的操作也棘手给解决了。
// 首先咱们在 data 里定义一个变量 imgurl
imgurl: ''
// 而后咱们把咱们以后展现图片的地址换成这个(此插件的 img 元素只有一个,多图预览就是通过批改这个图片 url 实现的)<img
:src="imgurl"
ref="heImView"
class="he-img-view"
:style="'transform: scale('+imgScale+') rotate('+imgRotate+'deg);margin-top:'+imgTop+'px;margin-left:'+imgLeft+'px;' + maxWH"@mousedown="addMove"
/>
// 因为咱们要加图片加载期待成果,所以咱们把所有对 url 的批改都封装成一个函数 changeUrl
// 这里咱们要当初 data 里加一个变量 imgState 示意图片状态以展现不同的反馈图片,1 为加载中,2 为加载胜利,3 为加载失败
methods: {changeUrl (url){
this.imgState = 1
let img = new Image()
img.src = url // 创立一个图片对象并把须要展现的图片赋值过来
img.onload = () => {
this.imgState = 2 // 图片加载胜利 页面显示图片
this.imgurl = url
}
img.onerror = () => {this.imgState = 3 // 图片加载失败 显示加载失败}
}
}
// 之后咱们要在本组件被调用的时候把用户的 url 当做参数调用咱们下面封装的批改图片地址的函数
if (this.multiple) {
// imgList 即多图预览时展现的图片数组
if (Array.isArray(this.imgList) && this.imgList.length > 0) {
// nowImgIndex 为默认展现的图片在图片数组中的下标,如果用户不传默认就是 0(即第一张)this.imgIndex = Number(this.nowImgIndex) || 0
// this.url = this.imgList[this.imgIndex] // 之前间接对 url 进行批改,这里改为调用函数
this.changeUrl(this.imgList[this.imgIndex])
} else {console.error('imgList 为空或格局不正确')
}
} else {
// 如果不是多图显示间接获取用户传入的 url 就能够了
this.changeUrl(this.url)
}
切换上一张下一张的中央也要改为调用函数的办法(能截图我就不贴代码了哈)
至此咱们加载中,加载实现,加载失败的成果就曾经实现咯
其实这里还有一个问题,就是如果咱们间断切换两次图片,可能会存在两张图片同时加载的状况,那么就会产生两张图片先后加载实现的问题,这时候每一张图片加载实现或失败就会批改图片加载状态,甚至会造成显示图片谬误的状况,这里我想过防抖或节流的办法都无奈解决,所以只能换一种思路,如果存在切换图片,那必定就是多图预览,那咱们须要显示的就是用户最初点击的那一次切换的图片,所以咱们只须要在批改图片地址的时候,把以后图片的下标传过来,再加载实现的时候,判断以后加载实现的图片的下标是否是以后须要显示的图片的下标就能够了。
// this.changeUrl(this.imgList[this.imgIndex])
this.changeUrl(this.imgList[this.imgIndex], this.imgIndex)
// 而后革新一下 changeUrl
changeUrl (url, index) {
this.imgState = 1
let img = new Image()
img.src = url
img.onload = () => {if (index != undefined && index == this.nowImgIndex) {
this.imgState = 2
this.imgurl = url
} else if (index == undefined) {
this.imgState = 2
this.imgurl = url
}
}
img.onerror = () => {if (index != undefined && index == this.nowImgIndex) {this.imgState = 3} else if (index == undefined) {this.imgState = 3}
}
},
功败垂成,这里测试后果就不展现了,反正就是胜利了。
文章中有很多不成熟的中央,还须要各位大佬多多指教,多谢多谢
QQ:1378431028
微信:heyongsheng1996