- 微信小程序ios端触摸事件获取clientY不准确,经常触发负值或超大值的情况?
wxml: <view class="index-list-keys" catch:touchstart="scrollTo" catchtouchmove='scrollTo' catchtouchend='removeTouching' ></view> js: const clientY = e.changedTouches[0].clientY; 最近在做通讯录索引的需求,右侧可以滑动并索引,想要根据clientY来计算当前滑动到的位置,发现了如果在连续触发触摸事件时在ios端,clientY会出现负值,或者超大值;比如从开始到结束:324,327,330,-100,1700,333,336,339 [图片]
08-14 - IOS video组件静音失效(在小程序内时接到微信语音通话)
1.ios端微信进入小程序播放一下视频。 2.在不退出小程序的情况下,其他人跟你发起语音通话并且接通2s,然后挂断。播放视频发现静音失效。
2022-03-14 - 小程序列表倒计时最佳实践
你的列表倒计时卡顿吗?批量倒计时在运行,性能差吗?如果觉得下文繁琐,建议直接看小程序代码片段: https://developers.weixin.qq.com/s/v6J3SomH7nr2 一. 什么是ticker? tick本来的意思是钟表的滴答声。Ticker类为游戏开发提供了一个主要的定时类。它主要的目的就是把stage渲染的工作集中起来,也就是说定时调用stage.update()这个方法。Ticker设置的频率也就是游戏的帧数了。 我们把Ticker应用到小程序开发中,频率设置为1s。 Ticker的使用如下,初始化Ticker对象,添加侦听tick事件,启动ticker。 [代码]const ticker = new Ticker() // 参数为Object类型,必须有tick方法 ticker.addTick({ tick: (delta) => { ... } }) ticker.start() [代码] 这里不细说Ticker的实现,详情请看Ticker.js源码。 二. 小程序倒计时的烦恼 假如我们都在页面onShow设置setTimeout。 1、onHide取消clearTimeout。假如首页有个倒计时在倒数100S,进入二级页面后,触发onHide,取消clearTimeout。过了10S返回首页,又重新启动setTimeout,那么应该是从100S还是90S开始倒数呢? 那肯定是90S开始呀,可是setTimeout都停了,怎么记录到过去了10S呢? 2、onUnload 取消clearTimeout。onHide之后,其实倒计时还在后台执行,setData也在重新渲染。如果有多级页面,无疑是非常浪费性能。 三. Ticker实现countdown解决方案 在Page的生命周期函数中,添加tick处理。 [代码]import ticker from './utils/ticker' Page({ countdown: 100, // 添加当前页面对象到ticker队列 onLoad () { ticker.addTick(this) }, // 恢复当前页面对象tick onShow () { ticker.resume(this) }, // 暂停当前页面对象tick onHide () { ticker.pause(this) }, // 移除当前页面对象tick从ticker队列 onUnload () { ticker.removeTick(this) }, // 需要计时的页面添加tick方法 tick (delta) { countdown -= delta this.setData({ countdown }) } }) [代码] 统一处理Page的tick 每个需要用ticker的页面,都需要在各自的生命周期函数里面添加对应的操作。重复的工作交给代码,来重写Page构造函数。interceptor.js [代码]// 生命周期函数集合 const Interceptor = { onLoad: [], onReady: [], onShow: [], onHide: [], onUnload: [] // onShareAppMessage: [] } // 组件生命周期函数集合 const ComponentInterceptor = { attached: [], show: [], hide: [], detached: [] } /** * 组合函数,依次执行 * @param {...Function} args 被组合的函数 */ function compose(interceptorList, sourceMethod, keyName) { return function () { ;[...interceptorList, sourceMethod].forEach((fn) => { typeof fn === 'function' && fn.apply(this, arguments) }) } } /** * 小程序Page方法的替代实现 */ const wxPage = Page /** * 重写Page构造函数 * @param pageObject - 传入的页面对象 */ Page = function (pageObject) { Object.keys(Interceptor).forEach((keyName) => { const sourceMethod = pageObject[keyName] pageObject[keyName] = compose(Interceptor[keyName], sourceMethod, keyName) }) return wxPage(pageObject) } /** * 增加对Page生命周期方法的拦截器 * @param methodName * @param handler */ export function addInterceptor(methodName, handler) { Interceptor[methodName] && Interceptor[methodName].push(handler) } /** * 小程序Component方法的替代实现 */ const wxComponent = Component /** * 重写Conponent构造函数 * @param componentObject - 传入的页面对象 */ Component = function (componentObject) { componentObject.pageLifetimes = componentObject.pageLifetimes || {} Object.keys(ComponentInterceptor).forEach((keyName) => { if (['show', 'hide'].includes(keyName)) { const sourceMethod = componentObject.pageLifetimes[keyName] componentObject.pageLifetimes[keyName] = compose(ComponentInterceptor[keyName], sourceMethod, keyName) } else { // 兼容Component.lifetimes.attached和Component.attached,detached同理 let lifetimes = componentObject if (componentObject.lifetimes && componentObject.lifetimes[keyName]) { lifetimes = componentObject.lifetimes } const sourceMethod = lifetimes[keyName] lifetimes[keyName] = compose(ComponentInterceptor[keyName], sourceMethod) } }) return wxComponent(componentObject) } /** * 增加对Component生命周期方法的拦截器 * @param methodName * @param handler */ export function addComponentInterceptor(methodName, handler) { ComponentInterceptor[methodName] && ComponentInterceptor[methodName].push(handler) } [代码] 小程序入口文件app.js,给页面生命周期函数全局注入ticker对应的方法。 [代码]import * as Interceptor from './utils/interceptor' import ticker from './utils/ticker' Interceptor.addInterceptor('onLoad', function () { ticker.addTick(this) }) Interceptor.addInterceptor('onShow', function () { ticker.resume(this) }) Interceptor.addInterceptor('onHide', function () { ticker.pause(this) }) Interceptor.addInterceptor('onUnload', function () { ticker.removeTick(this) }) App({ onLaunch () { } }) [代码] 页面只需要添加tick方法,利用delta计算倒数时间,无需操作ticker逻辑。page.js: [代码]import formatTime from '../../utils/formatTime' Page({ countdown: 1000, data: { countdownStr: '' }, tick (delta) { console.log('index tick') let countdownStr = formatTime(this.countdown -= delta) this.setData({ countdownStr }) } }); [代码] done 小程序代码片段: https://developers.weixin.qq.com/s/v6J3SomH7nr2
2021-06-28 - 小程序页面(Page)扩展,为所有页面添加公共的生命周期、事件处理等函数
背景 在小程序的原生开发中,页面中经常会用到一些公共方法,例如在页面onLoad中验证权限、所有页面都需要onShareAppMessage设置分享等 假设我们在编码时每个页面都写一遍,显然不是一个高级程序员会干的事情,太Low了。如果我们定义一个公共文件,导出这些公共方法,每个页面都引入,然后再生命周期或者事件处理函数中调用,虽然看起来很方便,但不够优雅,达不到我们最终的目的(偷懒)。 下面给大家介绍一种相对比较优雅的实现方式,扩展Page来实现以上的操作。 Page(页面) 需要传入的是一个 [代码]object[代码] 类型的参数,那么我们重载一个 [代码]Page[代码] 函数,将这个 [代码]object[代码] 参数拦截改掉就可以了,下面直接上代码。 实现 1、在根目录新建一个 [代码]page-extend.js[代码] 文件,公共的逻辑都写在这里面 [代码]/** * * Page扩展函数 * * @param {*} Page 原生Page */ const pageExtend = Page => { return object => { // 导出原生Page传入的object参数中的生命周期函数 // 由于命名冲突,所以将onLoad生命周期函数命名成了onLoaded const { onLoaded } = object // 公共的onLoad生命周期函数 object.onLoad = function (options) { // 在onLoad中执行的代码 ... // 执行onLoaded生命周期函数 if (typeof onLoaded === 'function') { onLoaded.call(this, options) } } // 公共的onShareAppMessage事件处理函数 object.onShareAppMessage = () => { return { title: '分享标题', imageUrl: '分享封面' } } return Page(object) } } // 获取原生Page const originalPage = Page // 定义一个新的Page,将原生Page传入Page扩展函数 Page = pageExtend(originalPage) [代码] 2、在 [代码]app.js[代码] 中引入 [代码]page-extend.js[代码] 文件 [代码]require('./page-extend') App({ // 其他代码 ... }) [代码] 代码片段 https://developers.weixin.qq.com/s/Cyx8iGmV7Ldp 本文内容及评论未经允许,禁止任何形式的转载与复制(代码可在程序中使用)
2019-12-24