- 小程序EventChannel的一些理解和使用实践
EventChannel是什么通过官方文档和示例,实际可以发现EventChannel借助wx.navigateTo方法,在两个页面之间构建起了数据通道,互相可以通过“派发事件”及“注册这些事件的监听器”来实现基于事件的页面通信。 具体看下下面的官方示例: wx.navigateTo({ url: 'test?id=1', events: { // 为指定事件添加一个监听器,获取被打开页面传送到当前页面的数据 acceptDataFromOpenedPage: function(data) { console.log(data) }, someEvent: function(data) { console.log(data) } ... }, success: function(res) { // 通过 eventChannel 向被打开页面传送数据 res.eventChannel.emit('acceptDataFromOpenerPage', { data: 'test' }) } }) events: 注册将在目标页面触发(派发)的同名事件的监听器 success:跳转后进行可通过res.eventChannel 触发自定义事件 //test.js Page({ onLoad: function(option){ console.log(option.query) const eventChannel = this.getOpenerEventChannel() eventChannel.emit('acceptDataFromOpenedPage', {data: 'test'}); eventChannel.emit('someEvent', {data: 'test'}); // 监听 acceptDataFromOpenerPage 事件,获取上一页面通过 eventChannel 传送到当前页面的数据 eventChannel.on('acceptDataFromOpenerPage', function(data) { console.log(data) }) } }) 通过this.getOpenerEventChannel()获取eventChannel对象, 使用eventChannel.emit() 可以在任意时刻触发(派发)事件。当前页面即可执行对应的事件处理函数。 使用eventChannel.on() 即可监听上个页面emit的事件。 使用示例A页面为订单页面,B页面为填写发票信息页面,A页面跳转B页面携带开票金额(amount),A页面如果已填写,再次跳转B页面,需要携带发票信息(invoice),方便直接编辑B页面填写后返回更新A页面的发票信息(invoice)简单的实现是 A到B的传值使用query实现,B到A的传值使用globalData实现。 // A页面 onShow(){ if(globalData.temp_invoice_info){ this.setData({ invoice:globalData.temp_invoice_info },()=>{ globalData.temp_invoice_info=null; }) } }, //跳转填写发票表单信息 toFillOutInvoiceInfo() { wx.navigateTo({ url: `/pages/invoice/invoice-info-form/invoice-info-form?amount=${ this.data.detail.amount }&invoice=${ this.data.invoice.invoice_title ? JSON.stringify(this.data.invoice) : "" }`, }); }, //B页面 onLoad(options){ this.data.options = options; if (this.data.options.invoice) { this.setData({ invoice: JSON.parse(this.data.options.invoice) }) } }, submit(){ globalData.temp_invoice_info = this.data.invoice; wx.navigateBack(); } 如下EventChannel的实现 //A页面(注意这里需要使用箭头函数(访问this)) //跳转填写发票表单信息 toFillOutInvoiceInfo() { wx.navigateTo({ url: `/pages/invoice/invoice-info-form/invoice-info-form`, events:{ updateInvoice:(result)=>{ this.setData({invoice:result}) } }, success:(res)=>{ const params = { amount: this.data.amount, invoice:this.data.invoice.invoice_title ? JSON.stringify(this.data.invoice) : "" } res.eventChannel.emit('sendQueryParams',params) } }); }, //B页面 onLoad(){ this.getOpenerEventChannel().once('sendQueryParams',(params)=>{ const {amount,invoice} = parmas; this.setData({amount,invoice}) }) } submit(){ this.getOpenerEventChannel().emit('updateInvoice',this.data.invoice); wx.navigateBack(); } 还有一个额外的地方就是EventChannel可以在A-B-C多个页面直接建立数据通道。官方示例。 //注意this.getOpenerEventChannel() 只能在navigateTo模板页面使用,其他更多页面使用时, //可以保存在getApp()全局实例中以备其他页面使用 // 保留AB通道事件,已备C页面给A页面发送数据 // B页面 const eventChannel = this.getOpenerEventChannel() getApp().pageBEventChannel = eventChannel //C页面 getApp().pageBEventChannel.emit('PageAacceptDataFromPageC', { data: 'Page C->A' }); 有不对或扩展的地方欢迎大家批评交流。
2022-08-11 - 小程序自定义导航栏完整适配方案
写这篇博客的背景 临近节日,产品想给小程序首页头部设置图片背景,这个只能自定义导航栏来实现 [图片] 当然除了自定义背景图,还可以放置其他组件,按钮、搜索框等 实践部分设备状态栏、胶囊、间距的高度(仅供参考)(单位px) 状态栏 胶囊 上下间距 整个导航栏高度 iPhone 5 20 32 4 60 iPhone 6/7/8 20 32 4 60 iPhone 6/7/8 Plus 20 32 4 60 iPhone X/XR/XS 44 32 4 84 小米6 (非刘海) 24 29 7 67 华为 nova 7 Pro(刘海) 42 29 7 85 后续遇到其他设备再补充 @[toc] 步骤 1.隐藏小程序自带的导航栏 小程序配置 [代码]// 1.全局配置 // app.json { ... "navigationStyle": "custom" ... } // 2.页面配置 // pages.json { ... "navigationStyle": "custom" ... } [代码] 每一个小程序页面也可以使用 .json 文件来对本页面的窗口表现进行配置。页面中配置项在当前页面会覆盖 app.json 的 window 中相同的配置项。 [图片] 2.编写自定义导航栏 导航栏的组成部分(主要是状态栏和标题栏) 1.状态栏(时间和电量显示那一栏) + 2.状态栏和标题栏之间的间距 + 3.标题栏(小程序胶囊按钮那一栏) + 4.标题栏和正文区域之间的间距 刘海屏 [图片] 非刘海屏 [图片] 计算各部分的高度 获取系统信息api获取状态栏高度 [代码]// 状态栏高度 wx.getSystemInfoSync().statusBarHeight; [代码] 获取胶囊按钮信息api获取胶囊按钮的宽高和位置 [代码]/** * 获取微信小程序菜单栏(胶囊)信息 * 菜单按键宽度:width * 菜单按键高度:height * 菜单按键上边界坐标:top * 菜单按键右边界坐标:right * 菜单按键下边界坐标:bottom * 菜单按键左边界坐标:left */ wx.getMenuButtonBoundingClientRect(); > 重点: 此api返回的是胶囊按钮在页面中的的上下左右坐标的绝对位置 > 注意:在模拟器使用时记得把视图百分比调为100%,否则可能会导致获取数据不准确 [代码] 因为整个小程序的导航栏高度是不变的,我们可以把高度信息放在全局,方便使用。一般会在小程序的app.js(如果使用的uni-app,就是App.vue) 的 onLaunch生命周期进行获取和计算。 [代码]//app.js App({ onLaunch() { this.calcNavBarInfo() }, globalData: { //全局数据管理 navBarHeight: 0, // 导航栏高度 menuBottom: 0, // 胶囊距底部间距(顶部间距也是这个) menuHeight: 0, // 胶囊高度 }, /** * @description 计算导航栏信息 */ calcNavBarInfo () { // 获取系统信息 const systemInfo = wx.getSystemInfoSync(); // 胶囊按钮位置信息 const menuButtonInfo = wx.getMenuButtonBoundingClientRect(); // 导航栏高度 = 状态栏到胶囊的间距(胶囊上坐标位置-状态栏高度) * 2 + 胶囊高度 + 状态栏高度 this.globalData.navBarHeight = (menuButtonInfo.top - systemInfo.statusBarHeight) * 2 + menuButtonInfo.height + systemInfo.statusBarHeight; // 状态栏和菜单按钮(标题栏)之间的间距 // 等同于菜单按钮(标题栏)到正文之间的间距(胶囊上坐标位置-状态栏高度) this.globalData.menuBottom = menuButtonInfo.top - systemInfo.statusBarHeight; // 菜单按钮栏(标题栏)的高度 this.globalData.menuHeight = menuButtonInfo.height; } }) [代码] 到此各个部分的元素高度都已拿到, 而且是根据不同设备的屏幕信息动态设置,无论是刘海屏还是非刘海屏,安卓还是ios,样式皆可统一。 3.如何使用 [代码] <view class="nav" style="height:{{navBarHeight}}px; background: url();"> <!-- 胶囊区域 --> <view class="capsule-box" style="height:{{menuHeight}}px; min-height:{{menuHeight}}px; line-height:{{menuHeight}}px; bottom:{{menuBottom}}px;"> <view class="nav-handle"> <view class="back"> <!-- 返回按钮 --> <image src=""></image> </view> <view class="home"> <!-- 首页按钮 --> <image src=""></image> </view> </view> <view class="nav-title">导航标题</view> </view> </view> // js Page({ data: { navBarHeight: getApp().globalData.navBarHeight, menuBottom: getApp().globalData.menuBotton, menuHeight: getApp().globalData.menuHeight } }) // style // 导航栏 .nav { position: relative; } // 胶囊栏 .capsule-box { position: absolute; display: flex; align-items: center; } // 标题文字 .nav-title { height: 100%; width: 50%; margin: 0 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } [代码] 最近使用wx.getMenuButtonBoundingClientRect(),在ios端偶尔值全都是0,导致无法正确自定义导航栏高度 想了一下,ios端的所有设备胶囊按钮信息都是一样的,android端各个品牌都或多或少的有差距,根据这个规律作如下改造: 我这边后期用的uni-app ,所以api都换成了uni [代码]uni.getSystemInfo({ success: res => { let menuButtonInfo = {} if (res.platform === 'ios') { // ios设备的胶囊按钮都是固定的 menuButtonInfo = { width: 87, height: 32, left: res.screenWidth - 7 - 87, right: res.screenWidth - 7, top: res.statusBarHeight + 4, bottom: res.statusBarHeight + 4 + 32 } } else { // 安卓通过api获取 menuButtonInfo = uni.getMenuButtonBoundingClientRect() } console.log('获取胶囊信息:', menuButtonInfo); // 导航栏高度 = 状态栏到胶囊的间距(胶囊距上未知-状态栏高度)* 2 + 胶囊高度 + 状态栏高度 this.$options.globalData.navHeight = (menuButtonInfo.top - res.statusBarHeight) * 2 + menuButtonInfo.height + res.statusBarHeight; console.log('navHeight:', this.$options.globalData.navHeight); // 按钮上下边距高度 this.$options.globalData.menuBottom = menuButtonInfo.top - res.statusBarHeight; // 导航栏右边到屏幕边缘的距离 this.$options.globalData.menuRight = res.screenWidth - menuButtonInfo.right; // 导航栏高度 this.$options.globalData.menuHeight = menuButtonInfo.height; }, fail(err) {} }) [代码]
2022-03-04