大数跨境

微信小程序Skyline 实战:实现文章列表 + 返回顶部按钮

微信小程序Skyline 实战:实现文章列表 + 返回顶部按钮 wordpress知识
2026-10-27
6
本文分享微信小程序 Skyline 渲染引擎的实战代码,核心采用原生虚拟列表结合 Worklet 渲染线程进行滚动监听。功能涵盖下拉刷新、上拉加载以及滚动时返回顶部按钮的淡入淡出效果。

完整代码实现

index.json
{  "usingComponents": {}}
index.wxml
<view class="nav"  style="height:{{statusBarHeight + 44}}px;padding-top:{{statusBarHeight}}px">  <view class="nav-title">商品列表</view></view><scroll-view  class="scroll"  style="padding-top:{{statusBarHeight + 44}}px"  scroll-y  scroll-top="{{scrollValue}}"  refresher-enabled  refresher-triggered="{{refreshing}}"  bindrefresherrefresh="onRefresh"  bindscrolltolower="loadMore"  lower-threshold="150"  worklet:onscrollupdate="onScrollUpdate">  <block wx:for="{{goodsList}}" wx:key="id">    <view class="goods-item" bindtap="tapGoods" data-id="{{item.id}}">      <image class="goods-img" src="{{item.img}}" mode="aspectFill"></image>      <view class="goods-info">        <view class="goods-name">{{item.title}}</view>        <view class="goods-price">¥{{item.price}}</view>      </view>    </view>  </block>  <view wx:if="{{isLoading && !refreshing}}" class="loading-wrap">    <view class="dot dot1"></view><view class="dot dot2"></view><view class="dot dot3"></view>  </view>  <view wx:if="{{noMore}}" class="nomore">没有更多了</view></scroll-view><view class="back-top" bindtap="tapBackTop">  <view class="back-top-icon">↑</view></view>
index.wxss
page {  background: #f5f5f5;}.nav {  position: fixed;  top: 0;  left: 0;  width: 100%;  z-index: 99;  display: flex;  align-items: center;  justify-content: center;  background: #ffffff;  box-sizing: border-box;}.nav-title {  font-size: 32rpx;  font-weight: 500;  color: #333;}.scroll {  width: 100%;  height: 100vh;  padding-left: 20rpx;  padding-right: 20rpx;  padding-bottom: env(safe-area-inset-bottom);  box-sizing: border-box;}.goods-item {  background: #fff;  border-radius: 16rpx;  margin-bottom: 20rpx;  display: flex;  padding: 20rpx;}.goods-img {  width: 160rpx;  height: 160rpx;  border-radius: 12rpx;  flex-shrink: 0;}.goods-info {  flex: 1;  margin-left: 20rpx;  display: flex;  flex-direction: column;  justify-content: space-between;}.goods-name {  font-size: 28rpx;  color: #333;}.goods-price {  color: #f56c6c;  font-size: 30rpx;  font-weight: bold;}.loading-wrap {  height: 80rpx;  display: flex;  align-items: center;  justify-content: center;  gap: 10rpx;}.dot {  width: 12rpx;  height: 12rpx;  border-radius: 50%;  background: #999;  animation: jump 0.6s infinite ease-in-out;}.dot1 { animation-delay: 0s; }.dot2 { animation-delay: 0.2s; }.dot3 { animation-delay: 0.4s; }@keyframes jump {  0%, 100% { transform: translateY(0); }  50% { transform: translateY(-12rpx); }}.nomore {  text-align: center;  padding: 30rpx 0;  color: #999;  font-size: 26rpx;}.back-top {  position: fixed;  right: 30rpx;  bottom: calc(140rpx + env(safe-area-inset-bottom));  width: 80rpx;  height: 80rpx;  border-radius: 50%;  background: #fff;  box-shadow: 0 4rpx 20rpx rgba(0,0,0,0.15);  display: flex;  align-items: center;  justify-content: center;  z-index: 100;  opacity: 0;}.back-top-icon {  font-size: 40rpx;  color: #333;  font-weight: bold;}
index.js
const { shared, runOnJS } = wx.workletPage({  data: {    goodsList: [],    refreshing: false,    loading: false,    noMore: false,    page: 1,    statusBarHeight: 0  },  onLoad() {    const info = wx.getSystemInfoSync()    this.setData({      statusBarHeight: info.statusBarHeight    })    // 返回顶部按钮透明度共享变量,渲染线程驱动    this.backTopOpacity = shared(0)    this.applyAnimatedStyle('.back-top', () => {      'worklet'      return {        opacity: this.backTopOpacity.value      }    })  },  onReady() {    this.loadData()  },  logScroll(scrollTop, opacity) {    console.log('scrollTop=', scrollTop, 'opacity=', opacity)  },  onScrollUpdate(evt) {    'worklet'    const scrollTop = evt.detail.scrollTop    const showProgress = Math.min(Math.max((scrollTop - 500) / 100, 0), 1)    this.backTopOpacity.value = showProgress    runOnJS(this.logScroll.bind(this))(scrollTop, showProgress)  },  // 下拉刷新  onRefresh() {    this.setData({      page: 1,      noMore: false,      refreshing: true,      goodsList: []    }, () => {      this.loadData()    })  },  // 上拉加载  loadMore() {    if (this.data.loading || this.data.noMore) return    this.loadData()  },  loadData() {    this.setData({ loading: true })    setTimeout(() => {      const page = this.data.page      const mock = []      for (let i = 0; i < 10; i++) {        mock.push({          id: `${page}_${i}`,          title: 'Skyline商品测试',          price: (Math.random() * 100).toFixed(2),          img: 'https://picsum.photos/id/237/200/200'        })      }      const list = [...this.data.goodsList, ...mock]      const noMore = page >= 5      this.setData({        goodsList: list,        page: page + 1,        loading: false,        refreshing: false,        noMore      })    }, 600)  },  // 返回顶部(稳定版本,type=list虚拟列表,瞬时回顶)  tapBackTop() {    const query = this.createSelectorQuery()    query.select('.scroll').node(res => {      if (res?.node) {        res.node.scrollTo({          top: 0        })      }    }).exec()  },  goDetail(e) {    const id = e.currentTarget.dataset.id    wx.showToast({ title: '商品id:' + id })  }})

核心要点

  • type=list:Skyline 原生虚拟列表,自动回收节点,长列表性能更强

  • worklet:onscrollupdate 在渲染线程监听滚动,不会阻塞主线程

  • shared + applyAnimatedStyle 驱动返回顶部按钮淡入淡出动画

踩坑小结

  • worklet 函数第一行必须写 'worklet',Nightly 编译器容错率较低

  • type=list 虚拟列表中,node.scrollTo 不支持 duration 参数,只能实现瞬时回顶

  • worklet 内不能直接调用 setData,与主线程通信需使用 runOnJS

#Skyline #前端实战 #虚拟列表 #Worklet

【声明】内容源于网络
0
0
wordpress知识
各类跨境出海行业相关资讯
内容 385
粉丝 0
wordpress知识 各类跨境出海行业相关资讯
总阅读14.9k
粉丝0
内容385