- 2022-03-04
- 如何彻底解决小程序滚动穿透问题
背景 俗话说,产品设计有三宝:弹窗、浮层加引导。可见“弹窗”在产品设计中的重要性。对于前端开发者来说,实现一个基础的模态框(Modal)并不难,可一旦模态框内部出现可滚动内容,各种意想不到的问题就会接踵而至——其中最为典型的,就是“滚动穿透”。 什么是滚动穿透? 滚动穿透指的是:当我们在顶层弹窗中执行滑动操作时,实际滚动的是底层页面的内容。这种体验非常影响交互,也常让用户感到困惑。 解决方案分析 解决滚动穿透的思路通常有两种:改变顶层或改变底层。 1. 改变顶层(不推荐) 一种自然的想法是阻止顶层的事件继续传播。例如,给遮罩层(蒙层)绑定 catchtouchmove 事件。但这种方法在部分场景下并不生效,因此并不是一个可靠的通用方案。 2. 改变底层(推荐) 既然问题是底层内容跟随滚动,那么只要在弹窗打开时禁止底层页面滚动,问题就迎刃而解。 具体实现方案 🟡 不成熟的方案 将底层页面的最外层 view 设置为 position: fixed,但这会导致页面滚动位置丢失,回到顶部。 另一种方式是:在打开弹窗前记录当前滚动位置,关闭弹窗后使用 wx.pageScrollTo 滚动回原位置。这种方法实现较为繁琐,且体验不够流畅。 ✅ 成熟的方案 方案一:使用 page-meta 组件 通过微信小程序提供的 page-meta 组件,我们可以直接控制页面根容器的样式,类似于在 H5 中设置 body { overflow: hidden; }。 我们可以动态设置 overflow 属性为 hidden 或 auto,来控制页面是否可滚动。 方案二:使用 wx.setPageStyle 方法 这是一个更灵活的 API,特别适合在组件内部(如封装好的弹窗组件)使用,无需额外编写 page-meta 结构。 [代码]wx.setPageStyle({ style: { overflow: 'hidden' // 或 'auto' } }) [代码] 代码示例 这里提供一个可供测试的代码片段,帮助你快速上手: 👉 点击查看代码片段 拓展:支付宝小程序的情况 支付宝小程序虽然也支持 page-meta 组件,但由于其 WebView 内核限制(版本 69),设置 overflow: hidden 并不能完全阻止底层滚动。 支付宝团队为此专门提供了新的 API:my.setPageScrollable,用于精确控制页面是否可滚动。 [代码]my.setPageScrollable({ scrollable: false, success: (res) => console.log(res), fail: (err) => console.log(err) }) [代码] 更新(2025.06.18) 该 API 已正式开放。不过目前测试发现:在 安卓端 设置禁止滚动后,弹窗内的可滚动区域也会被一并禁止;iOS 端 则表现正常,仅底层页面被锁定。 原因分析: 安卓与 iOS 在滚动禁止的层级控制上存在系统级差异: 安卓 采用 Webview 级别的滚动限制,生效时整个页面(包括所有弹层)均不可滚动; iOS 采用组件级控制,能智能区分层级,仅锁定底层页面而保持弹窗内部可滚动。
11-13 - 如何实现圣诞节星星飘落效果
圣诞节快到啦~🎄🎄🎄🎄咱们也试着做做小程序版本的星星✨飘落效果吧~ 先来个效果图: [图片] 484听起来很叼,看起来也就那样。 来咱们上手开始撸 页面内容wxml,先简单切个图吧。 [代码]<view class="container"> <image src="https://qiniu-image.qtshe.com/merry_001.jpg" mode="widthFix"/> <image src="https://qiniu-image.qtshe.com/merry_02.jpg" alt="" mode="widthFix"/> <image src="https://qiniu-image.qtshe.com/merry_03.jpg" alt="" mode="widthFix"/> <image src="https://qiniu-image.qtshe.com/merry_04.jpg" alt="" mode="widthFix"/> <image src="https://qiniu-image.qtshe.com/merry_05.jpg" alt="" mode="widthFix"/> <image src="https://qiniu-image.qtshe.com/merry_06.jpg" alt="" mode="widthFix"/> <image src="https://qiniu-image.qtshe.com/merry_07.jpg" alt="" mode="widthFix"/> </view> <canvas canvas-id="myCanvas" /> [代码] 页面样式wxss,因为切片用的不太熟练,图片之间有个2rpx的空隙。 [代码].container { height: 100%; box-sizing: border-box; min-height: 100vh; } image { width: 100%; display: block; margin-top: -2rpx; //不会切图造的孽 } canvas { width: 100%; min-height:100vh; position: fixed; top: 0; z-index: 888; } [代码] 重点JS: [代码]//获取应用实例 const app = getApp() // 存储所有的星星 const stars = [] // 下落的加速度 const G = 0.01 const stime = 60 // 速度上限,避免速度过快 const SPEED_LIMIT_X = 1 const SPEED_LIMIT_Y = 1 const W = wx.getSystemInfoSync().windowWidth const H = wx.getSystemInfoSync().windowHeight var starImage = '' //星星素材 wx.getImageInfo({ src: 'https://qiniu-image.qtshe.com/WechatIMG470.png', success: (res)=> { starImage = res.path } }) Page({ onLoad() { this.setAudioPlay() }, onShow() { this.createStar() }, createStar() { let starCount = 350 //星星总的数量 let starNum = 0 //当前生成星星数 let deltaTime = 0 let ctx = wx.createCanvasContext('myCanvas') let requestAnimationFrame = (() => { return (callback) => { setTimeout(callback, 1000 / stime) } })() starLoop() function starLoop() { requestAnimationFrame(starLoop) ctx.clearRect(0, 0, W, H) deltaTime = 20 //每次增加的星星数量 starNum += deltaTime if (starNum > starCount) { stars.push( new Star(Math.random() * W, 0, Math.random() * 5 + 5) ); starNum %= starCount } stars.map((s, i) => { //重复绘制 s.update() s.draw() if (s.y >= H) { //大于屏幕高度的就从数组里去掉 stars.splice(i, 1) } }) ctx.draw() } function Star(x, y, radius) { this.x = x this.y = y this.sx = 0 this.sy = 0 this.deg = 0 this.radius = radius this.ax = Math.random() < 0.5 ? 0.005 : -0.005 } Star.prototype.update = function() { const deltaDeg = Math.random() * 0.6 + 0.2 this.sx += this.ax if (this.sx >= SPEED_LIMIT_X || this.sx <= -SPEED_LIMIT_X) { this.ax *= -1 } if (this.sy < SPEED_LIMIT_Y) { this.sy += G } this.deg += deltaDeg this.x += this.sx this.y += this.sy } Star.prototype.draw = function() { const radius = this.radius ctx.save() ctx.translate(this.x, this.y) ctx.rotate(this.deg * Math.PI / 180) ctx.drawImage(starImage, -radius, -radius * 1.8, radius * 2, radius * 2) ctx.restore() } }, setAudioPlay() { let adctx = wx.createInnerAudioContext() adctx.autoplay = true adctx.loop = true adctx.src = 'https://dn-qtshe.qbox.me/Jingle%20Bells.mp3' adctx.onPlay(() => { console.log('开始播放') adctx.play() }) } }) [代码] 以上只是简单实现了一个星星飘落的效果,预览的时候需要开启不校验合法域名哦~ 目前还有更优的h5版本,使用Three.js实现的,在小程序内使用Three.js对于我来说有点打脑壳,先把效果分享出来吧。 h5版本,手机看效果最佳 h5源码可直接右键查看:https://qiniu-web.qtshe.com/merryChrimas.html
2023-12-07 - 炫酷的wxss动画效果
因为没啥事,研究了下小程序的粒子动画,最后放弃了,实在是头大。去搞了一些花里胡哨的效果,没啥实际用处,就分享玩玩,看能不能提供一些其他灵感啥的。 [图片] 代码片段如下:https://developers.weixin.qq.com/s/VQwYjYm47dgH 使用wxss绘制烟花动画 [图片] https://developers.weixin.qq.com/s/xcJdoMmW7lh3 蜡烛逼真燃烧效果: [图片] https://developers.weixin.qq.com/s/Iom47XmO7rh5 螺旋旋转效果 [图片] https://developers.weixin.qq.com/s/1BnRTXmZ7Rhj 炫酷wxss粒子动画 [图片] https://developers.weixin.qq.com/s/cRpjQXmb7khN 水文章
2020-08-03