JI-UI适用于微信小程序的UI库(二)——j-local-popup点击位置弹出层
读者你好,我将在这一系列文章中逐步更新JI-UI的公共UI库。但在短期内只会更新由我创作的部分。 问题的引出 全局弹出层无疑的最常见的弹出层实现,但是它带来的问题。在我看来,不够轻型化,难以胜任某些场景。 我希望,可以实现如下的行为: 如图可以看到,我在点击选择字体颜色图标时,弹出层会跟随鼠标点击(实际中当然是跟随用户触碰)弹出。 问题的解决 要点 要获取用户点击位置;事件里会有 要获取弹出层的宽高;这里肯定要动态获取,通过this.createSelectorQuery()实现 确定弹出层的位置;要考虑到不能超出页面边界,以及从实用性的角度,应该考虑从上方弹出、从下方弹出以及从左边、从右边弹出。 弹出层怎么取消?我们考虑了如何弹出,但是也务必考虑它的注销流程。实际上我们在设计自定义组件的时候,应当考虑它的整个生命周期。简略来说,即组件的创建、工作、销毁。 解决问题 组件的wxml部分 <slot name="main" catchtouchend="openPopup"></slot> <view class="popup" catchtouchend="closePopup" wx:if="{{visible}}"> </view> <view wx:if="{{visible||ishow}}" style="top:{{top}}px;left:{{left}}px;position:fixed;opacity:0;transform:scale(0.1)" class="out-class {{show?'show':''}}"> <slot name="popup"></slot> </view> 解析一下,这段代码中中存在两个插槽,其中一个为main,另一个为popup; main是设计给被点击对象的,这样子想使用触碰弹出层的话方式会非常简单,只需要把原本的代码带上slot="main"放进组件中即可 以下边这段代码为例,我们希望点击它可以弹出“Love you”。 <i class="hello"> hello </i> 只需要: <j-local-popup> <i class="hello" slot="main"> hello </i> <view slot="popup" class="love">Love you</view> </j-local-popup> 同样的,slot名为popup的组件将作为点击后的弹出层出现。 组件的js部分 阅读了wxml部分,你应当知道组件是创建方法是openPopup,销毁它则是通过closePopup 下边是openPopup的解析,closePopup较为简单,如果有需要可以自行在git中查看。 openPopup(e){ var that=this; let detail=e.changedTouches[0]; var top=detail.clientY; var left=detail.clientX; var width=0; var height=0; new Promise((resolve,reject)=>{ that.setData({ visible:true, ishow:true },()=>{ let o = that.createSelectorQuery().select(".out-class").boundingClientRect(rect => { width=rect.width; height=rect.height; }).exec((res)=>{ resolve(res[0]) }); }) }).then(res=>{ if (that.properties.position=='top') { that.setData({ top:top-(height*10)-5, left:left-(width*10/2), show:true }) } }) } 基本上,代码由三步组成。 1. 开始渲染组件 that.setData({ visible:true, ishow:true } 2.在渲染完组件后获取popup插槽父组件的高度、宽度 that.createSelectorQuery().select(".out-class").boundingClientRect(rect => { width=rect.width; height=rect.height; }).exec((res)=>{ resolve(res[0]) }); 3. 处理top从上方弹出 that.setData({ top:top-(height*10)-5, left:left-(width*10/2), show:true }) 这里对height和width进行*10是因为初始状态通过scale(0.1)改变了组件的大小,-5则是一定程度的偏移。 组件的动画效果 演示图中,我们看到的是组件的放大缩小显隐,从我个人的角度我认为这种形式的动画会让我更舒适。 这种效果并不是很难做到。 opacity:0; transform:scale(0.1) opacity改变透明度,transform中的scale改变大小。 当我们把这两个初始值取消,并在tranistion中配置好贝塞尔曲线、影响参以及动画时间 transition:all ease .6s; 于是我们便完成了这个简单的动画 一个有趣的事情 有一位初学前端的朋友问我,为什么你在动画里不通过改变width和height实现,是因为这个函数方便么? 显然不能只是这样...... transform里几个函数都有极强的功能。细究会发现其中的translate移动和scale缩放的部分或者全部功能其实是可以通过改变其他css属性实现的。 通过直接改变width和height只会改变对应组件的长宽,子组件不会被改变,直接改变长宽适用于卷轴式的动画 通过带有top、left等的属性我们可以改变组件位置,但是为什么我们不应该这样做?因为通过这种方式创建出来的动画不流畅,可能我们的肉眼不一定能很好察觉,但是性能可以好为什么不做好点。 至于为什么通过带有top、left等的属性改变组件位置创建的动画并不流畅,这和浏览器的工作机制有关系,之后会专门说明。 Tips&Bugs tip:使用catchtouch而不是bingtouch或是bindtap,因为点击这里并不希望事件传递出去使得出现输入框失焦等问题 bug:是的,我没有做不能超出页面边界的控制这一部分。因为目前还没遇见该问题,暂且不想花费宝贵的期末复习时间。 JI-UI库gitee地址:https://gitee.com/grinzero/ji-ui文件已经上传了一部分