读者你好,我将在这一系列文章中逐步更新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 文件已经上传了一部分
源心锁 
![[爱了]](/js/img/d1.gif)
![[尴尬]](/js/img/d16.gif)