vue.js上传头像插件_vue移动助手实践(二)——用vue指令实现插件并完成一个修改头像功能...

(By: Kath & kimmy)

最近在做的一个几月vue的移动端小demo,其中有一块是实现各个页面的统一换肤功能的。想着写一篇文章,来写一写实现过程中遇到的一些问题。

项目在线demo

项目github地址

demo里有这么一个较隐蔽的修改头像操作

修改头像

正常的上传头像都带选取裁切功能,这里先实现一张完整图片的缩放和居中显示,下个迭代开发再加入自定义选取框吧,

主要介绍的是下面两点实现

1. 用transition 实现无缝过渡

2. 用directive (vue 指令)实现图片的按宽高比缩放和居中显示

一 用transition 实现无缝过渡

Kath 说为什么我做起来好像很好看样子,果然年轻人都是喜欢特效的。

transition 在项目里面一般多少有人用到,主要用于实现一些动态交互效果,它的出现解决了部分vue 在动画方面的薄弱——我们仍旧可以通过数据驱动的形式,用v-show 和 v-if 去控制我们想要的效果,避免过多的dom操作。

我用transition实现的是一个入场离场的效果,home页和修改头像的info页其实是两个不同页面,通过路由跳转,为了制造无缝的效果,我在两个页面都保留了头像图片这一个相同元素,制造了两个页面相关的假象,

所以实际的实现其实是

别人的出现效果

点击home 页头像, 路由跳转到info页, 触发info页入场transition, 使图片从起始位置,即home 页头像所在位置,过渡到当前页面的实际位置。 触发info页入场的操作,通过定义一个appear Boolean变量控制,用于v-show。而文字上升的效果,同样是在进场时候触发transition, 而进场动画的交互效果, 参考了ant design的设计风格,看了人家那些列表元素进场效果是怎样的···

![](userinfo.headUrl)

···

data () {

return {

appear: false // 控制进场

}

},

mounted () {

this.$nextTick(() => {

this.appear = true

})

},

···

.slide-enter-active,

.slide-leave-active {

transform: translateY(0);

transition: transform 1s;

}

.slide-enter,

.slide-leave-to/* .fade-leave-active in below version 2.1.8 */

{

transform: translateY(-50px);

}

关于文字效果的实现,这里又可以普及小scss的小众用法,我的实现看起来是这样的

K.K

wanna to be a Brilliant gentle

And a pretty girl

···

@for $i from 1 to 4 {

.slide-#{$i}-enter-active {

transform: translateY(0);

opacity: 1;

transition: transform 1s, opacity 1s;

transition-delay: ($i - 1s) / 5;

}

.slide-#{$i}-leave-active {

transform: translateY(0);

opacity: 1;

transition: transform .5s, opacity .5s;

}

.slide-#{$i}-enter,

.slide-#{$i}-leave-to {

opacity: 0;

transform: translateY(50px);

}

}

太多个transition以及还没循环的页面模板还要优化,这个还在考虑一个好的实现,想说的是transition-delay: ($i - 1s) / 5; 这句看起来就很优雅有没有, 主要功能是给他们进场时候打了个时间差,通过变量加上一些修正就可以制造契合优雅的数列,在css里面写表达式还是有种成就感的···

2. 离场

离场的效果和入场如出一辙,样式交互以及在上面定义好了,主要我们要考虑的是转场需要一点时间去完成这系列出场动画,(否则下一个进来的页面就会立刻出现,动画会中止或覆盖)

beforeRouteLeave (to, from, next) {

this.appear = false

setTimeout(() => {

next()

}, 800)

},

二 用directive 指令实现图片缩放居中显示

和jquery有很多插件一样,vue 也有很多逐渐完善的插件, 而directive 可以说是vue插件开发里面的很重要的一个部分。

和我们写组件不一样,我们的组件大多针对一个功能或或一个业务块,实现完整的功能。然后插件我理解为比较嵌入式的,针对多是全局的通用的,辅助性质功能。比如在一张图片绑定一个v-preview 指令,实现图片预览, 在一个div绑定指令,实现popover功能等。

观察element.ui 源码发现也有很多值得借鉴的东西,比如我在项目的指令里面加了clickoutside的功能,在对应的元素绑定 v-myclickoutside, 用户在点击除该元素外的页面其他地方都会触发绑定事件。常用的场景就是我们自己写下拉框,弹出框时候,点击页面外部会自动收起下拉框,(换做以前我们得监听body点击事件,可能还要解绑,一个元素写一次绑定那种),具体实现可以参照项目代码

点击组件外部自动收起

这里我说下对图片绑定v-autofix, 实现图片自动压缩居中显示的功能, 来简述指令插件的开发过程

/**

// v-autofix指令

export default {

install (Vue) {

let handleImg = (el, binding, vnode) => {

if (!el || !el.parentNode) {

return

}

// console.log('carry', el, binding, el.parentNode)

let img = new Image()

let boxWidth = el.parentNode.offsetWidth

img.onload = () => {

// 以长度小的边为基准, 按比例缩放,然后偏移最长边和当前边框长度差的一半

if (img.width < img.height) {

el.style.height = Math.floor(img.height / img.width * boxWidth) + 'px'

el.style.width = boxWidth + 'px'

el.style.marginTop = -(el.offsetHeight - boxWidth) / 2 + 'px'

} else {

el.style.width = Math.floor(img.width / img.height * boxWidth) + 'px'

el.style.height = boxWidth + 'px'

el.style.marginLeft = -(el.offsetWidth - boxWidth) / 2 + 'px'

}

}

img.src = el.src

}

Vue.directive('autofix', {

inserted (el, binding, vnode) {

handleImg(el, binding, vnode)

},

update (el, binding, vnode) {

handleImg(el, binding, vnode)

},

unbind (el) {

}

})

}

}

1. directive

首先第一步,关于vue directive, 我们可以用directive这么注册一个指令, 参照vue directive

// 注册一个全局自定义指令 v-focus

Vue.directive('focus', {

// 当绑定元素插入到 DOM 中。

inserted: function (el) {

// 聚焦元素

el.focus()

}

})

其中, 我们可以绑定的钩子函数有几个,他们的参数都为 el,binding,vnode,oldVnode等,先看官方描述,再看我的理解

bind:指令第一次绑定到元素时调用,可以定义一个在绑定时执行一次的初始化动作,和inserted区别是,这个过程发生在这个节点生成,但还没有插入dom时候,所以你会发现,你企图在这个钩子里面获取到el.parentNode时候是失败的。

inserted:被绑定元素插入父节点时调用,如果说我们希望我们的动作只执行一次,但又需要和其他节点关联(如获取父元素宽高,修改他们属性值等),那么我们就应该在inserted执行我们的操作。

update:任何节点变化,属性值变化等都会执行该钩子,所以可以作为一个监听事件,而且他有其他钩子不具备的oldValue等参数值,方便我们判断是否该变化需要执行我们的操作。

unbind:只调用一次,指令与元素解绑时调用。

2. 了解我们的需求

我们需要的是这么个东西,在图片上绑定一个v-autofix指令,当这张图片src变化后(我们获取到上传的图片后,修改图片src), 能自动根据获取的图片的宽高,根据他们比例去压缩成我们div的大小,

实际图

效果图

所以我们可以确定我们要触发的时机,一个是页面加载时候,一个是src变化时候,所以我们可以确定用

bind/inserted 以及 update作为钩子函数

理解各参数意义,实现逻辑

bind/inserted 以及 update函数都提供了我们 el(绑定元素), binding对象等值,我们思考我们获取图片宽高的方法,实际上是等待image加载完毕,获取img 宽高的过程,因此,我们可以通过以下实现,获取元素src,

通过new image加载图片,获取对应宽高

let img = new Image()

img.onload = () => {

// get img.width

// get img.height

}

img.src = el.src

紧接着,我们可以计算长宽比,以最小的宽或高为准缩放图片

let img = new Image()

img.onload = () => {

// 以长度小的边为基准, 按比例缩放,然后偏移最长边和当前边框长度差的一半

if (img.width < img.height) {

el.style.height = Math.floor(img.height / img.width * boxWidth) + 'px'

el.style.width = boxWidth + 'px'

} else {

el.style.width = Math.floor(img.width / img.height * boxWidth) + 'px'

el.style.height = boxWidth + 'px'

}

}

img.src = el.src

最后一步居中显示,这里我通过在图片上层定义父元素,通过img的偏移长宽差一半来实现居中效果

let handleImg = (el, binding, vnode) => {

if (!el || !el.parentNode) {

return

}

// console.log('carry', el, binding, el.parentNode)

let img = new Image()

let boxWidth = el.parentNode.offsetWidth

img.onload = () => {

// 以长度小的边为基准, 按比例缩放,然后偏移最长边和当前边框长度差的一半

if (img.width < img.height) {

el.style.height = Math.floor(img.height / img.width * boxWidth) + 'px'

el.style.width = boxWidth + 'px'

el.style.marginTop = -(el.offsetHeight - boxWidth) / 2 + 'px'

} else {

el.style.width = Math.floor(img.width / img.height * boxWidth) + 'px'

el.style.height = boxWidth + 'px'

el.style.marginLeft = -(el.offsetWidth - boxWidth) / 2 + 'px'

}

}

img.src = el.src

}

值得注意的是,这里我需要获取父元素宽度,所以前面说的,在bind过程获取不到父元素,只能用inserted啦

这是个相对简单的指令应用,目前也只用了el 的操作,还有更完善的实现,就需要一起探讨学习啦

最后一提,修改头像的功能到这里差不多就没什么好讲了,只要做好显示,剩下的工作,我只是把更新的图片转成base64存在localstorage里而已,多多指教。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值