J'Blog

小程序开发记录

小程序开发记录

小程序开发遇到的问题

  1. picker-view 选择日期为什么不能默认选中当前年
  2. 设置元素absolute绝对定位但是没有设置其相对于哪个元素绝对定位的话显示不了当前元素
  3. 需要考虑头部栏以及底部安全区域 wx.getMenuButtonBoundingClientRect() margin-bottom: env(safe-area-inset-bottom)
  4. 父子组件传值问题 父->子:通过属性 子->父:绑定事件 父:binddateChange="dateChange" 子:bindchange="bindChange"
    bindChange(e) {
      const val = e.detail.value

      this.triggerEvent('dateChange', {
        date: `${this.data.years[val[0]]}-${this.data.months[val[1]]}-${this.data.days[val[2]]}`,
        fieldName: this.properties.type
      })
    }
父通过e.detail
  dateChange(e) {
    this.setData({
      info: {
        ...this.data.passportInfo,
        [e.detail.fieldName + 'Label']: e.detail.date,
        [e.detail.fieldName]: new Date(e.detail.date.replace(/-/g, '/')).getTime()
      }
    })
  },
  1. data-*获取属性值dataset可能在target也可能在currentTarget
  2. ios不支持“2024-12-27”这样的格式,需要将-替换成/
  3. scroll-view滚动视图和页面原生监听滚动事件不能同时使用,如果使用了scroll-view就要使用组件内部的监听事件监听页面滚动,同时不能使用原生方法指定页面滚动到具体位置
wx.pageScrollTo({
   scrollTop: 0,
   duration: 300
})

小程序开发记录

<image>组件mode属性,widthFix设置宽度会根据固定宽度在不压缩的情况下自动匹配图片的高度
图片cdn
图片点击预览
<image src="" mode="widthFix" bindtap="previewImage" data-url="{{item.value}}" />
// 图片点击事件处理函数
previewImage: function (e) {
    const currentUrl = e.currentTarget.dataset.url
    wx.previewImage({
        current: currentUrl, // 当前显示图片的URL
        urls: this.properties.list // 需要预览的图片URL列表
    })
}

if判断 wx:if wx:elif wx:else

for循环 wx:for wx:key data-index="{{index}}" data-item="{{item}}"

绑定点击事件:bind:tap

scroll-view横向滚动案例
<scroll-view class="scroll" scroll-x show-scrollbar="{{false}}" scroll-with-animation="{{true}}" scroll-left="{{scrollLeft}}">
    <view class="list">
        <view bind:tap="activeTabHandle" class="tab-item {{activeIndex === index ? 'active' : ''}}" wx:for="{{actInfo.tabInfo}}" wx:key="index" data-index="{{index}}" data-item="{{item}}">
            <image src="{{filters.resizeImage(item.flowerImage ,{width:60}, 'jpg')}}" mode="widthFix" />
             <text>{{item.categoryName}}</text>
         </view>
    </view>
</scroll-view>
  swiperAnimation() {
    timer && clearInterval(timer)
    timer = setInterval(() => {
      const curActiveIndex = this.data.activeIndex
      const len = this.data.actInfo.tabInfo?.length || 0
      // 正向轮播还是反向轮播,轮播方向
      this.direction = curActiveIndex === len - 1 && this.direction === 1 ? -1 : (this.direction === -1 && curActiveIndex === 0 ? 1 : this.direction)

      if (this.direction === 1) {
        this.setData({
          activeIndex: curActiveIndex + 1,
          scrollLeft: len < 4 ? 0 : curActiveIndex * 160
        })
      } else {
        this.setData({
          activeIndex: curActiveIndex - 1,
          scrollLeft: len < 4 ? 0 : (len - 1) * 160 - (len - curActiveIndex + 1) * 160
        })
      }
    }, 2000)
  },
  // 切换tab
  activeTabHandle(e) {
    timer && clearInterval(timer)
    const { index } = e.currentTarget.dataset
    this.setData({
      activeIndex: index
    })

    setTimeout(() => {
      this.swiperAnimation()
    }, 3000)
  },

原生左右切屏
<view style="display: flex;transition: all 1s; transform: translateX(calc(-{{activeIndex}}*100vw));width: {{actInfo.tabInfo.length*100}}vw">
    <view class="item" wx:for="{{actInfo.tabInfo}}" wx:key="index" data-item="{{item}}" style="height: {{activeIndex === index ? 'fit-content': '0'}}">
     </view>
</view>
遇到的问题:高度会根据最高元素撑开,解决方案是非激活项高度置为0,外围宽度设置为100vw,overflow:hidden;

元素吸顶:设置position: sticky
// 页面滚动
onPageScroll: function(e) {
  // 根据滚动距离判断是否吸顶
  const scrollTop = e.scrollTop
  if (scrollTop > (this.data.menuButton.bottom)) {
    this.setData({
      opacity: (scrollTop - this.data.menuButton.bottom) / this.data.menuButton.bottom
    })
    return
  }
  this.setData({
    opacity: 0
  })
},
// 示例:吸顶tab,点击页面自动滚动到具体tab内容,监听页面滚动,滚动到具体tab内容,tab激活
1. tab吸顶设置position属性为sticky
2. 设置当前激活tab项,根据当前激活项切换tab激活样式
3. 给页面中的tab内容设置id,获取id距离页面顶部的高度,再减去顶部menuButton的高度以及tab高度(注意:当页面是动态加载的时候,需要在数据加载完成之后获取高度;当tab内容在页面的组件中时,需要调用子组件的获取元素高度的方法将元素位置信息存储在变量中),当点击tab时,使用wx.pageScrollTo自动滚动到tab内容位置
// 获取自定义组件的实例
const tabInfoComponent = this.selectComponent('#tabInfoComponent')
// 调用组件的方法
tabInfoComponent.getElementInfo()
4. 在onPageScroll方法中监听页面滚动距离顶部的距离,如果超过某个高度,则切换激活项(注意:给一个变量控制在点击tab切换激活项时不监听页面滚动距离,否则两者会冲突)
变量:
menuButton: wx.getMenuButtonBoundingClientRect(),
tabs: ['tab1', 'tab2', 'tab3', 'tab4'],
currentTab: 0,
componentPositions: [],
scrollTop: 0,
tabHeight: {},
页面tab:
<view class="tab-box" id="tab-box" style="top:{{menuButton.top+menuButton.height+10}}px">
    <view wx:for="{{tabs}}" wx:key="index" bind:tap="clickTab" data-index="{{index}}" wx:if="{{item}}" class="{{currentTab===index?'selectedTab':'unSelectedTab'}}">
        {{item}}
    </view>
</view>
在加载完数据时获取tab内容高度:
await this.getComponentHeight()
getComponentHeight() {
    let rect = []
    const query = wx.createSelectorQuery().in(this)
    this.data.tabs.forEach((tab, index) => {
      query.select(`#section${index}`).boundingClientRect()
    })
    query.exec(res => {
      rect = res.map(item => {
        if (item) return item.top + this.data.scrollTop
        else return 0
      })
      this.setData({
        componentPositions: rect
      })
    })
},
点击tab:
clickTab(e) {
    const curTabIndex = e.currentTarget.dataset.index
    this.clickTabChange = true
    this.setData({
      currentTab: curTabIndex
    })
    this.scrollToComponent(curTabIndex)
},
// 滚动到指定组件
async scrollToComponent(index) {
    if (!this.data.componentPositions[index]) return
    wx.pageScrollTo({
      scrollTop: this.data.componentPositions[index] - this.data.menuButton.top - this.data.menuButton.height - 10 - (this.data.tabHeight.height || 66),
      duration: 300,
      complete: function () {
        this.clickTabChange = false
      }
    })
    setTimeout(() => {
      this.clickTabChange = false
    }, 500)
},
getTabHeight() {
    return new Promise((resolve) => {
      const query = wx.createSelectorQuery()
      query.select('#tab-box').boundingClientRect(res => {
        resolve(res)
      }).exec()
    })
}
监听页面滚动:
onPageScroll(e) {
    const scrollTop = e.scrollTop
    this.setData({
      scrollTop
    })
    if (this.data.isPageScroll !== (scrollTop > 50)) {
      this.setData({ isPageScroll: scrollTop > 50 })
    }

    // 获取容器高度
    let containerHeight = 0
    if (wx.getWindowInfo) {
      containerHeight = wx.getWindowInfo().windowHeight
    } else {
      containerHeight = wx.getSystemInfoSync().windowHeight
    }
    if (scrollTop > containerHeight && !this.data.isShowTop) {
      this.setData({ isShowTop: true })
    }
    if (scrollTop <= containerHeight && this.data.isShowTop) {
      this.setData({ isShowTop: false })
    }

    if (this.clickTabChange) return
    const positions = this.data.componentPositions
    for (let i = positions.length - 1; i >= 0; i--) {
      if ((scrollTop >= positions[i] - this.data.menuButton.top - this.data.menuButton.height - 10 - (this.data.tabHeight.height || 66)) && positions[i]) {
        this.setData({
          currentTab: i
        })
        break
      }
    }
},


跳转到tab页面:
wx.switchTab({ url: '/pages/home/home' })

打开公众号文章
wx.openOfficialAccountArticle({
      url,
      success: res => {
      },
      fail: res => {
      }
})
打开公众号兼容问题:有些设备不支持openOfficialAccountArticle
跳转到一个新页面,用webview组件展示页面
<web-view src="{{url}}"></web-view>

跳转页面
wx.navigateTo({ url: url })

返回上一页
  back() {
    wx.navigateBack({
      delta: 1,
      fail: _ => {
        wx.switchTab({ url: '/pages/home/home' })
      }
    })
  },

分享
<button class="share" open-type="share">分享</button>
// 分享给朋友
  async onShareAppMessage(e) {
    return {
      title,
      path,
      imageUrl
    }
}

// 分享到朋友圈
  onShareTimeline() {
    return {
      title,
      query,
      imageUrl
    }
}

// 隐藏分享按钮
  wx.hideShareMenu({
    menus: ['shareTimeline']
  })

// 埋点
wx.reportEvent(string eventId, object data)

// 直播间内点击商品跳转到商品详情页面会自带用户openid,room_id
onLoad: function (options) {
  // options: { openid, room_id }
  this.setData({
    options
  })
}
// 商品详情页分享直播间,给直播间添加分享人id
let path = 'plugin-private://{appid}/pages/live-player-plugin?room_id=' + this.data.options?.room_id
const customParams = encodeURIComponent(JSON.stringify({ share_openid: this.data.options?.openid }))
path += `&custom_params=${customParams}`
// 在小程序app.js中监听路由判断是否在直播间,获取分享人id
const livePlayer = requirePlugin('live-player-plugin')
const liveInfo = await livePlayer.getShareParams()
log.info('直播间信息', liveInfo)
if (!liveInfo.share_openid) liveInfo.share_openid = liveInfo.custom_params?.share_openid
// 可以做直播分享计数等操作

// 使用微信内置地图
wx.openLocation({
   name,
   address,
   latitude,
   longitude,
   scale: 18
})

// markdown文本
<rich-text class="ich-text" nodes="" />

// 打电话
wx.makePhoneCall({
    phoneNumber: phone
})

// 收获地址
wx.chooseAddress({
    success(res) {
       // 选择收获地址回调函数  
    }
})

// open-type="contact"联系客服