uni 中使用 vue3 写法 IntersectionObserver简单用法

我们当前需要用到的方法是uni.createIntersectionObserver,创建后会包含4个方法

  • relativeTo(selector,[margins]) 使用选择器指定一个节点,作为参照区域之一

  • relativeToViewport([margins]) 指定页面显示区域作为参照区域之一

  • observe(selector,[callback]) 指定目标节点并开始监听相交状态变化情况。回调函数 callback 包含一个参数 result

  • disconnect() 停止监听。回调函数将不再触发

完整项目demo:https://gitee.com/hackchen/demo-collection/tree/master/front-end/uniapp/intersection-observer-demo

当前示例,我们用到3个方法,分别为relativeTo、observe、disconnect

完整代码如下:

<script setup>
import { ref,getCurrentInstance,onMounted } from 'vue'
import {onLoad, onUnload} from '@dcloudio/uni-app'

const observer = ref(null);
const appear = ref(false);
const instance = ref(null);
onMounted(() => {
  instance.value = getCurrentInstance();
  console.log('onMounted',instance.value)

  observer.value = uni.createIntersectionObserver(instance.value);
  // 如果 .ball 出现在 .scroll-view 可视区域,则执行回调
  observer.value
      .relativeTo('.scroll-view') // 第二个参数 定义相交边界 left top right bottom
      .observe('.ball', (res) => {
    console.log('createIntersectionObserver res',res)
    // res.intersectionRatio 0-1 相交比例 大于0 小球出现
    if (res.intersectionRatio > 0 && !appear.value) {
      appear.value = true;
    } else if (!res.intersectionRatio > 0 && appear.value) {
      appear.value = false;
    }
  })
})
onUnload(() => {
  observer.value.disconnect();
})
</script>

<template>
  <view class="container">
    <text>{{ appear ? '小球出现' : '小球消失' }}</text>
    <view class="page-section">
      <scroll-view class="scroll-view" scroll-y>
        <view class="scroll-area">
          <text class="notice">向下滚动让小球出现</text>
          <view class="ball"></view>
        </view>
      </scroll-view>
    </view>
  </view>
</template>

<style lang="scss" scoped>
.container {
  display: flex;
  flex-direction: column;
}

.scroll-view {
  height: 400rpx;
  background: #fff;
  border: 1px solid #ccc;
  box-sizing: border-box;
}

.scroll-area {
  height: 1800rpx;
  display: flex;
  flex-direction: column;
  align-items: center;
  transition: .5s;
}

.notice {
  margin: 150rpx 0 400rpx 0;
}

.ball {
  width: 200rpx;
  height: 200rpx;
  background: #1AAD19;
  border-radius: 50%;
}
</style>

<script lang="ts" setup> import { custContactListApi, delCustomerContactApi, getCustPermissionApi } from &#39;@/subpkgCRM/api/customer/contact&#39; // import { Type } from &#39;../types&#39; import { TriggerEvent, exTriggerEvent } from &#39;@/utils/crm/tracker&#39; import CellPhonePopup from &#39;@/subpkgCRM/pages/customer/components/CellPhonePopup/index.vue&#39; import { getCrmUrl } from &#39;@/utils/crm/index&#39; interface Props { custName: string custQuery: string isShow: boolean cellPhone: (custCode: string) => void } const props = withDefaults(defineProps<Props>(), {}) const _this: any = getCurrentInstance() const observer = uni.createIntersectionObserver(_this) const viewTop = ref(0) const oldViewTop = ref(0) const scrollView = (e: any) => { oldViewTop.value = e } const setMainViewTop = (val: number) => { viewTop.value = oldViewTop.value nextTick(() => { viewTop.value = val }) } const list = ref<any>([]) const pageData = ref({ limit: 10, page: 1 }) const total = defineModel(&#39;total&#39;, { default: 0 }) /** * 获取客户列表参数 */ const getListParams = () => { return { custCode: urlData.value.custCode, ...pageData.value } } /** * 获取客户列表 * @param type 类型:push:拉取下一页数据; &#39;&#39;:拉取第一页数据 */ const getList = async (type = &#39;&#39;) => { console.log("🚀 ~ getList1111 ~ type:", type) if (type === &#39;push&#39;) { if (total.value && total.value <= list.value.length) { return } pageData.value.page++ } else { pageData.value.page = 1 } try { console.log("🚀 ~ getList2222 ~ type:", type) const { data }:any = await custContactListApi(getListParams(), type === &#39;push&#39;) total.value = data.total if (type === &#39;push&#39;) { list.value.push(...data.list) } else { list.value = data.list setMainViewTop(0) } } catch (error) { console.error(&#39;getLegalPageListApi--&#39;, error) } } /** * 点击列表项 * @param item 被点击的项 */ const handleItem = (item: any) => { console.log(&#39;handleItem--&#39;, item) uni.navigateTo({ url: `/subpkgCRM/pages/customer/channelCustomer/channelContactDetail/index?urlId=${item.id}` }) } // 组件引用 const cellPhonePopupRef = ref() const cellPhoneContactList = ref<any[]>([]) /** * 点击拨号图标 * @param item 被点击的项 */ const handleCellPhone = (item: any) => { let list = [] if(item.contactMobile){ list.push({ moblie: item.contactMobile }) } if(item.contactTelephone){ list.push({ moblie: item.contactTelephone }) } cellPhoneContactList.value = [...list] cellPhonePopupRef.value?.open() } const storageUserInfo = uni.getStorageSync(&#39;userInfo&#39;) const dialogRef = ref() const dialogPrimaryText = ref(&#39;确定&#39;) const dialogCancelText = ref(&#39;取消&#39;) const dialogTitle = ref(&#39;&#39;) const dialogConfirm = ref(() => {}) const dialogCancel = ref(() => {}) /** * 点击删除 * @param item 要删除的列表项 */ const handleDel = (item: any) => { exTriggerEvent(TriggerEvent.CustClickDelCust, { custCode: item.custCode, custName: item.custName }) dialogTitle.value = &#39;是否确认删除此客户?&#39; dialogRef.value.show() dialogConfirm.value = async () => { exTriggerEvent(TriggerEvent.CustClickConfirmDelCust, { custCode: item.custCode, custName: item.custName }) const params = { custCode: item.custCode, id: item.id, source: &#39;2&#39;, confirm: true } await delCustomerContactApi(params) uni.showToast({ title: &#39;删除成功&#39;, icon: &#39;none&#39;, mask: true }) getList() } } /** * 点击编辑 * @param item 要编辑的项 */ const handleEdit = (item: any) => { console.log(&#39;handleEdit--&#39;, item) exTriggerEvent(TriggerEvent.CustClickUpdateCust, { custCode: item.custCode, custName: item.custName }) const itemStr = encodeURIComponent(JSON.stringify(item)) uni.navigateTo({ url: `/subpkgCRM/pages/customer/channelCustomer/addChannelContact/index?data=${itemStr}&custCode=${urlData.value.custCode}` }) } const canAdd = ref(false) // 获取联系人列表权限 const getCustPermission = async () => { console.log("🚀 ~ getCustPermission ~ params.urlData.value.custCode:", urlData.value.custCode) try { const params = { custType: &#39;channel&#39;, custCode: urlData.value.custCode } const res: any = await getCustPermissionApi(params) if(res.data && res.data==&#39;OWNER&#39;){ canAdd.value = true } console.log("🚀 ~ getCustPermission ~ res:", res) } catch (err) { console.log("🚀 ~ getCustPermission ~ err:", err) } } // 跳转新增 const addContact = () => { uni.navigateTo({ url: `/subpkgCRM/pages/customer/channelCustomer/addChannelContact/index?custCode=${urlData.value.custCode}` }) } onMounted(() => { exTriggerEvent(TriggerEvent.ViewCustMangeChannelList) }) onReady(() => { observer.relativeToViewport({ bottom: 40 }).observe(&#39;.sentinel&#39;, (res: any) => { console.log("🚀 ~ observer.relativeToViewport ~ res:", res) if (res.intersectionRatio > 0) { console.log("🚀 ~ observer.relativeToViewport ~ res.intersectionRatio:", res.intersectionRatio) //上拉加载 console.log(&#39;11111111111111111111111111111111111111111111111111111111111111111111111&#39;); getList(&#39;push&#39;) } }) }) onUnload(() => { observer.disconnect() }) // 当前客户信息 const urlData = ref<any>({}) // 页面生命周期 onLoad((options: any) => { if (options?.data) { urlData.value = JSON.parse(decodeURIComponent(options.data)) console.log("🚀 ~ onLoad ~ urlData.value:", urlData.value) // 设置导航栏标题 uni.setNavigationBarTitle({ title: urlData.value.custName || &#39;渠道联系人信息-列表&#39; }) getCustPermission() } }) onShow(()=>{ if(urlData.value.custCode){ getList() } }) </script> <template> <view class="channel-container"> <view class="top-box"> <text class="top-box-title">联系人信息</text> <text class="top-box-num">(共{{ total }}人)</text> <view class="top-box-add" v-if="canAdd" @click="addContact"> <uni-icons class="icon-add" type="plus" color="#0F56D5" size="16"></uni-icons>新增联系人 </view> </view> <scroll-view v-if="total > 0" scroll-y :scroll-top="viewTop" class="scroll-view" @scroll="scrollView"> <view class="list"> <view class="list-item" v-for="item in list" :key="item.custCode" @click="handleItem(item)"> <view class="list-item-content"> <view class="list-item-name-and-phone"> <view class="contact-name"> <span class="contact-name-text">{{ item.contactName }}</span> <span class="contact-name-position">{{ item.contactPost }}</span> </view> <view v-if="storageUserInfo.oneId != item.ownId" class="contact-belong" > <span class="contact-belong-title">归属</span> <span class="contact-belong-name">{{ item.owner }}</span> </view> <view v-if="item.contactMobile||item.contactTelephone" class="list-item-phone" @click.stop="handleCellPhone(item)"> <!-- <uni-icons type="dianhua" color="#5b8ff9" size="14" /> --> <uni-icons class="icon-call" type="dianhua" color="#1677FF" size="14"></uni-icons> </view> </view> </view> <view class="list-item-footer" v-if="storageUserInfo.oneId == item.ownId"> <view class="list-item-footer-tag list-item-footer-tag-deleted" @click.stop="handleDel(item)" > <text>删除</text> </view> <view class="list-item-footer-tag list-item-footer-tag-deleted" @click.stop="handleEdit(item)" > <text>修改</text> </view> </view> </view> </view> <view :style="{ display: list && list.length && total > list.length ? &#39;flex&#39; : &#39;none&#39; }" class="sentinel"> <text>加载中</text> <view class="sentinel-loding"> <view class="sentinel-loding-item" v-for="item in 3" :key="item"></view> </view> </view> <view :style="{ display: total > list.length || !(list && list.length) ? &#39;none&#39; : &#39;block&#39; }" class="loading-out" >没有更多了</view > </scroll-view> <!-- 无数据状态 --> <view v-else class="no-data-box"> <view class="no-data-wrap"> <view class="no-data-img"> <img :src="getCrmUrl(&#39;biz-crm-market/staticView/resources/crm_market_nodata.png&#39;)" alt="" /> </view> <view class="no-data-text"> <text v-if="!canAdd">暂无数据</text> <text v-if="canAdd">还没有联系人,</text> <text v-if="canAdd" class="no-data-add" @click="addContact">立即添加</text> </view> </view> </view> <zy-dialog ref="dialogRef" showPrimary :primaryText="dialogPrimaryText" :cancelText="dialogCancelText" :title="dialogTitle" @primary="dialogConfirm" @cancel="dialogCancel" /> <!-- 弹窗组件 --> <CellPhonePopup ref="cellPhonePopupRef" :list="cellPhoneContactList" /> </view> </template> <style lang="scss" scoped> .channel-container { height: 100%; .scroll-view { height: calc(100% - var(--status-bar-height) - 50px); /* 减去状态栏和标题栏 */ } .list { padding-top: $uni-spacing-col-base; padding-bottom: $uni-spacing-col-base; &-item { background-color: $uni-text-color-inverse; box-shadow: 0 2px 10px 0 rgba(197, 197, 197, 0.22); border-radius: $uni-border-radius-sm; margin: $uni-spacing-col-base $uni-spacing-row-base; margin-top: 0; &:last-child { margin-bottom: 0; } &-name-and-phone { display: flex; } &-name { flex: 1; font-size: $uni-font-size-15; color: rgba(0, 0, 0, 0.9); font-weight: 500; padding-right: $uni-spacing-row-base; } &-phone { flex: 0 0 none; } &-tag-box { display: flex; padding-top: $uni-spacing-col-xs; } &-tag { margin-left: $uni-spacing-row-xs; background: #f2f7ff; border-radius: 2px; font-size: $uni-font-size-12; color: $uni-text-color-primary; text-align: center; font-weight: 400; padding: 2px 4px; &:first-child { margin-left: 0; } } &-projectManagers { font-size: $uni-font-size-12; color: $uni-text-color-tertiary; font-weight: 400; padding-top: $uni-spacing-col-xs; } &-footer { display: flex; justify-content: flex-end; align-items: center; position: relative; &::before { content: &#39;&#39;; position: absolute; top: 0; width: 100%; height: 1px; } } &-footer-tag { font-size: $uni-font-size-12; color: $uni-color-primary; font-weight: 400; box-sizing: border-box; border-radius: $uni-border-radius-sm; background-color: #FFFFFF; width: 46px; height: 26px; line-height: 26px; text-align: center; & + .list-item-footer-tag { margin-left: $uni-spacing-row-xs; background: #FFFFFF; border-radius: 4px; } &-deleted { color: $uni-text-color-secondary; font-size: 13px; color: #333333; } & > text { padding-left: 2px; } } } } @keyframes bounce { 0%, 80%, 100% { transform: scale(0); } 40% { transform: scale(1); } } .sentinel { display: flex; align-items: center; justify-content: center; height: 43px; width: calc(100% - 2 * $uni-spacing-row-base); font-size: $uni-font-size-13; color: $uni-text-color-tertiary; font-weight: 400; margin: -$uni-spacing-row-base $uni-spacing-row-base 0; &-loding { display: flex; align-items: center; padding-left: $uni-spacing-row-xs; gap: 9px; &-item { $size: 4px; width: $size; height: $size; background-color: $uni-text-color-tertiary; border-radius: 1px; animation: bounce 1.4s infinite ease-in-out both; &:nth-child(1) { animation-delay: -0.32s; } &:nth-child(2) { animation-delay: -0.16s; } } } } .loading-out { font-size: $uni-font-size-13; height: 59px; line-height: 59px; text-align: center; color: $uni-text-color-extra; font-weight: 400; margin: -$uni-spacing-row-base $uni-spacing-row-base 0; } } .top-box { flex-shrink: 0; background-color: #fff; line-height: 50px; padding: 0 12px; .top-box-title { font-weight: 500; color: #333; } .top-box-num { margin-left: 7px; color: #666; } .top-box-add { float: right; color: #0F56D5; font-size: 14px; } } .contact-name { .contact-name-text { font-size: 15px; color: #333; font-weight: 500; line-height: 15px; } .contact-name-position { font-size: 13px; color: #666; margin-left: 8px; line-height: 15px; } } .list-item-footer{ height: 50px; background: #F8F8F8; box-shadow: 0 2px 4px 0 rgba(204,204,204,0.12); border-radius: 0 0 6px 6px; padding-right: 16px; } .icon-call { position: absolute; right: 19px; top: 50%; transform: translateY(-50%); } .contact-belong { margin-top: 3px; line-height: 17px; .contact-belong-title { border: 0.5px solid #d1d1d1; border-radius: 2px; padding: 0 4px; font-size: 12px; color: #666; } .contact-belong-name { font-size: 12px; color: #999; margin-left: 2px; } } .list-item-name-and-phone{ min-height: 64px; max-height: 70px; position: relative; padding: 17px 20px; box-sizing: border-box; justify-content: center; flex-direction: column; } .no-data-box { height: 85vh; width: 100%; display: flex; justify-content: center; align-items: center; .no-data-wrap { width: 151px; .no-data-img { width: 151px; height: 90px; image, img { width: 100%; height: 100%; } } .no-data-text { text-align: center; font-size: 14px; color: #666; line-height: 26px; width: max-content; .no-data-add { color: #0F56D5; } } } } </style> 当前代码向下滚动未能触发onReady(() => { observer.relativeToViewport({ bottom: 40 }).observe(&#39;.sentinel&#39;, (res: any) => { console.log("🚀 ~ observer.relativeToViewport ~ res:", res) if (res.intersectionRatio > 0) { console.log("🚀 ~ observer.relativeToViewport ~ res.intersectionRatio:", res.intersectionRatio) //上拉加载 console.log(&#39;11111111111111111111111111111111111111111111111111111111111111111111111&#39;); getList(&#39;push&#39;) } }) })
最新发布
10-29
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值