微信小程序Skyline搜索框吸顶到胶囊位置,解决官方scroll-view的sticky-header内的input,点击input页面会自动回顶问题,丝滑Q弹动画

进入下面小程序可以体验效果

以下皆基于官方原版demo修改

官方原版demo是使用sticky-header做的吸顶,如果是不涉及input普通使用其实这篇内容可以放弃了,直接用官方即可。

如是需要用到input框吸顶,且需要避免点击input框时scroll-view会自动回顶的问题。那么请看下面代码!

WXML:

<view class="search-container">
  <scroll-tab class="in-bar"
  style="position: absolute;height: 34px;"
  iconDirect="left" 
  bind:tapIcon="tapIcon"></scroll-tab>
  <view class="search-p">
    <view class="search">
      <view class="search-icon-wrp">
        <image class="search-icon" src="/assets/image/search.png" />
      </view>
      <input class="search-text"
        confirm-type="search" 
        value="{{searchValue}}" 
        bindinput="searchBlur" bindconfirm="toSearch" placeholder="你想要的都能搜到哟~" >搜索</input>
    </view>
  </view>
</view>
<scroll-view
  class="scroll-area"
  type="custom"
  enable-back-to-top="{{false}}"
  scroll-y
  scroll-with-animation="{{true}}"
  show-scrollbar="{{false}}"
  bindscrolltolower="lower"
  lower-threshold='150'
  worklet:onscrollend="handleScrollEnd"
  worklet:onscrollupdate="handleScrollUpdate"
  >
    <view class="fake-nav-bar" style="height: {{menuBottom+60}}px;"/>
    <grid-builder type="masonry" 
      list="{{gridList[0]}}"
      child-count="{{gridList[0]}}"
      cross-axis-count="{{2}}" cross-axis-gap="{{10}}" main-axis-gap="{{10}}" padding="{{[4,10,0,10]}}">
          <block slot:item slot:index>
            <view style="width: 100%;height: {{item.height}}px;border: 1px solid gainsboro;">
              <image style="width: 100%;height: 100%;" mode="aspectFill" src="https://img-blog.csdnimg.cn/direct/25e4d10c5187409d9190a2f47297503e.jpeg"></image>
            </view>
          </block>
    </grid-builder>
    <view style="height: 310rpx;margin-bottom:calc(80rpx + env(safe-area-inset-bottom) / 2);"></view>
  </scroll-view>

js:

const app = getApp() //这段代码需要放在page页面JS的最顶部
const { shared, Easing,spring} = wx.worklet
const lerp = function (begin, end, t) {
  'worklet'
  return begin + (end - begin) * t
}

const clamp = function (cur, lowerBound, upperBound) {
  'worklet'
  if (cur > upperBound) return upperBound
  if (cur < lowerBound) return lowerBound
  return cur
}

Page({

  /**
   * 页面的初始数据
   */
  data: {
    paddingTop:app.globalData.menu.top,
    menuBottom:app.globalData.menu.bottom,
    gridList:[[]],
    searchValue:''
  },
  searchBlur: function (e) {
    this.setData({
        searchValue: e.detail.value,
    })
    this.currentName=e.detail.value
  },
  /**
   * 搜索头像
   */
  toSearch(e){
     this.setData({
        gridList:[[]]
     })
     this.loadData();
  },
  lower(e){
    //触底加载数量
    const that = this;
    // 加载数据
    setTimeout(function() {
      that.loadData();
    }, 1000);
},
/**
 * 加载瀑布数据
 * 分类壁纸
 */
loadData(){
    let nowList = `gridList[0]`
    var list = [];
    for(var i=0;i<50;i++){
        list.push({
          "height":this.getRadomHeight()
        })
    }
    let newList = this.data.gridList[0].concat(list)
    this.setData({
        [nowList]:newList
    })
  },
  /**
     * 生成随机(100, 400)高度
     */
    getRadomHeight() {
      return parseInt(Math.random()*100 + 100)
    },
  /**
   * 生命周期函数--监听页面加载
   */
  onLoad(options) {
    this.loadData();
    this.applyStyle()
  },
  /**
   * 生命周期函数--监听页面初次渲染完成
   */
  onReady() {
    
  },

  /**
   * 应用搜索框动画
   */
  applyStyle(){
    this.saftPadding = shared(app.globalData.menu.top)
    //用于计算输入框的宽度到胶囊左边的距离
    this.menuWidth =shared((((app.globalData.menu.left)/app.globalData.system.windowWidth)*100)-15)
    this.menuBottom =shared(app.globalData.menu.height+15)
    this.menuBottom2 =shared(app.globalData.menu.height+15)
    this.searchBarheight = shared(app.globalData.menu.height)
    this.searchBarWidth = shared(100)
    this.scrollBarWidth = shared(72)
    this.searchBarLeft = shared(0)
    this.navBarOpactiy = shared(1)
    this.wnavBarOpactiy = shared(1)
    this.inBarOpactiy = shared(0)
    this.paddingLeft = shared(10)
    this.paddingRight = shared(10)
    this.navBarZindex = shared(10)
    this.navBarLeft = shared(app.globalData.system.windowWidth-app.globalData.menu.right)
    this.isTop = shared(false) //是否触顶了
    this.isBottom = shared(false) //是否触底了
    this.isExc = shared(false) //是否执行动画了
    this.isScrolling = shared(false) //是否正在滑动

    this.applyAnimatedStyle('.nav-bar', () => {
      'worklet'
      return {
        marginTop: `${this.saftPadding.value}px`,
        height: `${this.searchBarheight.value}px`,
        width: `${this.scrollBarWidth.value}%`,
        opacity: this.wnavBarOpactiy.value,
        zIndex:this.navBarZindex.value,
        marginLeft:`${this.navBarLeft.value}px`
      }
  })

    this.applyAnimatedStyle('.in-bar', () => {
        'worklet'
        return {
          height: `${this.searchBarheight.value}px`,
          width: `${this.scrollBarWidth.value}%`,
          opacity: this.inBarOpactiy.value,
          marginLeft:`${this.navBarLeft.value}px`
        }
    })
    this.applyAnimatedStyle('.search', () => {
      'worklet'
      return {
        width: `${this.searchBarWidth.value}%`,
        height: `${this.searchBarheight.value}px`,
        marginTop: `${this.menuBottom.value}px`
      }
    })
    this.applyAnimatedStyle('.search-p', () => {
      'worklet'
      return {
        paddingLeft:`${this.paddingLeft.value}px`,
        paddingRight:`${this.paddingRight.value}px`,
        left: `${this.searchBarLeft.value}%`,
      }
    })
      this.applyAnimatedStyle('.search-container', () => {
        'worklet'
        return {
          backgroundColor: (this.wnavBarOpactiy.value > 0.1) ? 'transparent' : '#fff',
          paddingTop: `${this.saftPadding.value}px`,
          position:'absolute',
          width:'100%',
          zIndex:99
        }
      })
  },
  /**
   * 滚动列表Worklet动画事件
   */
  handleScrollUpdate(evt) {
    'worklet'
    const maxw = 100;
    const minw = this.menuWidth.value;
    const maxDistance = 60
    const scrollTop = clamp(evt.detail.scrollTop, 0, maxDistance)
    const progress = scrollTop / maxDistance
    const EasingFn = Easing.cubicBezier(0.4, 0.0, 0.2, 1.0)
    this.searchBarLeft.value = lerp(0, 10, EasingFn(progress))
    this.inBarOpactiy.value = lerp(0, 1, progress)
    this.navBarOpactiy.value = lerp(1, 0, progress)
    const t1= lerp(maxw, minw, EasingFn(progress)) //45参数不能动
    this.menuBottom.value = lerp(this.menuBottom2.value, 0, EasingFn(progress))
    this.paddingLeft.value = lerp(10, 0, EasingFn(progress))
    this.paddingRight.value = lerp(10, 0, EasingFn(progress))
    this.navBarZindex.value = lerp(10, 0, EasingFn(progress))
    // this.scrollBarWidth.value = t2
    if(t1>minw&&t1<maxw){
        //这个范围表示正在滑行
        this.wnavBarOpactiy.value=0
        this.isScrolling.value=true
        this.isBottom.value=false
        this.isTop.value=false
    }
    if((t1>minw&&t1<maxw)&&(!this.isExc.value||this.isScrolling.value)){
        //这个表示没有执行动画或者正在化学且范围内,将宽度计算减少1.5为了弹力效果
        this.searchBarWidth.value = t1-1.5
    }
    if(t1===minw&&evt.detail.scrollTop>minw&&!this.isTop.value){
        //靠顶了
        this.isTop.value=true
        this.isBottom.value=false
        this.searchBarWidth.value = t1-2
        this.searchBarWidth.value = spring(t1)
        this.isExc.value=true
        this.isScrolling.value=false
    }else if(t1===maxw&&evt.detail.scrollTop<1&&!this.isBottom.value){
        //靠底了
        this.wnavBarOpactiy.value=1
        this.isBottom.value=true
        this.isTop.value=false
        this.searchBarWidth.value = t1-2
        this.searchBarWidth.value = spring(t1)
        this.isExc.value=true
        this.isScrolling.value=false
    }
  },
  /**
   * 滑动手离开后,停止了
   */
  handleScrollEnd(evt){
    'worklet'
    this.isScrolling.value=false
    this.isExc.value=false
  },

  
/**
   * 生命周期函数--监听页面显示
   */
  onShow(){
  },

  /**
   * 生命周期函数--监听页面隐藏
   */
  onHide() {

  },

  /**
   * 生命周期函数--监听页面卸载
   */
  onUnload() {

  },

  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */
  onPullDownRefresh() {

  },

  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom() {

  },

  /**
   * 用户点击右上角分享
   */
  onShareAppMessage() {

  }
})

WXSS: 

.fake-nav-bar {
  height: 60px;
}

.search-container {
  padding: 0 0px 10px 0px;
}
.search {
  display: flex;
  flex-direction: row;
  box-sizing: border-box;
  width: 100%;
  height: 40px;
  border-radius: 20px;
  border: 1px solid #dae0dd;
  position: relative;
  align-items: center;
  background-color: #fff;
}

.search-text {
  color: #1b1b1b;
  font-size: 16px;
  width: 100%;
  height: 100%;
}

.search-icon-wrp {
  display: flex;
  width: 30px;
  height: 100%;
  flex-direction: row;
  align-items: center;
  justify-content: center;
}

.search-icon {
  width: 16px;
  height: 16px;
}

.search-btn {
  position: absolute;
  right: 0;
  width: 60px;
  height: 100%;
  border-radius: 20px;
  background-color: #07c160;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #FFF;
  font-size: 16px;
  /* font-weight: bold; */
}



.scroll-area {
  height: 100vh;
}





.grids {
  width: 100%;
  margin-bottom: 390rpx;
  display: flex;
  flex-flow: wrap;
}

.grids-cell {
  width: 30%;
  border-radius: 100px;
  margin: 15rpx 7.5rpx;
  text-align: center;
  background: #f1f1f1;
  padding: 15rpx 10rpx;
  font-size: 28rpx;
  font-family: fangsong;
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Qensq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值