微信小程序避坑scroll-view,用tween.js实现吸附动画

背景

在开发一个小程序项目时,遇到一个需要列表滚动,松手时自动吸附的动画需求,如下(最终效果):

很自然用了scroll-view组件,但使用过程中发现scroll-view 里包含'position: fixed;top:0;'的元素时,应用scroll-with-animation=true,搭配更改scroll-top时,松手后fixed的元素会抖一下......

于是决定不用组件内置的scroll-with-animation,改用手动控制scroll-top实现吸附的效果。

思路

通常,要做动画,我们就得确定以下信息:

  • 初始状态
  • 结束状态
  • 动画时长
  • 动画过程状态如何变化(匀速/先加速后减速/...)

前三个信息不难得到,第四个在css动画里(如transition/animation)用 timing-function 指定:

在js动画里,改变css的属性可通过 Web Animations API 里的 easing 属性指定:

而如果需要动画的状态不是css的属性呢(例如上面的scrollTop)?这就要用到补间/插值工具了,tween.js登场!

关于 tween.js

tween翻译有'补间'的意思

补间(动画)(来自 in-between)是一个概念,允许你以平滑的方式更改对象的属性。你只需告诉它哪些属性要更改,当补间结束运行时它们应该具有哪些最终值,以及这需要多长时间,补间引擎将负责计算从起始点到结束点的值。

简单点就是tweenjs可以指定状态从初始值到结束值该怎么变化,下面是简单的使用例子:

js 复制代码
const box = document.getElementById('box') // 获取我们想要设置动画的元素。
const coords = {x: 0, y: 0} // 从 (0, 0) 开始

const tween = new TWEEN.Tween(coords, false) // 创建一个修改"坐标"的新 tween。
	.to({x: 300, y: 200}, 1000) // 在 1 秒内移动到 (300, 200)。
	.easing(TWEEN.Easing.Quadratic.InOut) // 使用缓动函数使动画流畅。
	.onUpdate(() => {
		// 在 tween.js 更新"坐标"后调用。
		// 使用 CSS transform 将 'box' 移动到 'coords' 描述的位置。
		box.style.setProperty('transform', 'translate(' + coords.x + 'px, ' + coords.y + 'px)')
	})
	.start() // 立即开始 tween。

	// 设置动画循环。
	function animate(time) {
		tween.update(time)
		requestAnimationFrame(animate)
	}
	requestAnimationFrame(animate)

在微信小程序里使用tween.js

导入适配

下载 github.com/tweenjs/twe... 文件,修改下'now'的实现,把performance.now()改成Date.now() 即可在小程序里使用:

动画循环

小程序里没有直接支持requestAnimationFrame,这个可以用canvas组件的requestAnimationFrame方法代替:

js 复制代码
    // ...
    wx.createSelectorQuery()
      .select("#canvas")
      .fields({
        node: true,
      })
      .exec((res) => {
        this.canvas = res[0].node;
      });
    // ...
    
    // ...
    const renderLoop = () => {
      TWEEN.update();
      this.canvas.requestAnimationFrame(renderLoop);
    };
    renderLoop();

其他

锁帧

手动改scrolltop还是得通过setData方法,频繁调用可能会导致动画卡顿,requestAnimationFrame一般1s跑60次,也就是60fps,根据需要可以增加锁帧逻辑:

js 复制代码
const fps = 30; // 锁30fps
const interval = 1000/30;
let lastTime = Date.now();
const renderLoop = () => {
    this.canvas.requestAnimationFrame(renderLoop);
    
    const now = Date.now();
    if(now - lastTime > interval){
        // 真正的动作在这里运行
        TWEEN.update();
        lastTime = now;
    }
};
renderLoop();

官方支持?

要是 scrollView 组件支持 wxs 更改scrollTop就好了 developers.weixin.qq.com/community/d...

相关推荐
水獭比特21 分钟前
工作流都公开了,为什么还不能继承 Owner 权限?
javascript·人工智能·node.js
ly76893 小时前
Vue 3 从入门到工程实践:基础语法、组件化、路由、状态管理与项目部署
前端·javascript·vue.js
四千岁3 小时前
抓包LangChain-了解LangChain的原理
前端·javascript·后端
用户298698530143 小时前
使用 JavaScript 在 React 中实现 Word 转 PDF
前端·javascript·react.js
悟空瞎说4 小时前
UICollectionViewLayout 全套源码 + 逐行中文注释 + 使用场景说明
javascript
编程小橘子6 小时前
基于微信小程序健康心理测试、设计与实现 (源码+lw+参考文档+核心代码讲解等)
微信小程序·小程序·毕业设计·毕设
铁皮饭盒6 小时前
浏览器AI肯定离不开onnxruntime, 微软开源
前端·javascript·后端
码视野7 小时前
基于 Vue3 + Element Plus 的【智慧社区洗车养车与上门流动洗车预约调度系统】设计与实现(含PRD/三端源码/大屏)
前端·javascript·vue.js·人工智能·vue3
码视野7 小时前
基于 Vue3 + Element Plus 的【无人值守共享自习室与空间智能预约系统】设计与实现(含PRD/三端源码/大屏)
前端·javascript·vue.js·人工智能·vue3
CarIise7 小时前
JavaScript基础语法与DOM操作实战课堂笔记
开发语言·javascript·笔记