赞
踩
实际效果如下:
注意事项:
animate.css需要添加样式兼容微信小程序;
微信小程序滚动时boundingClientRect获取不到标签信息
1、HBuilderX打开uniapp创建的vue3项目,在编辑器下方打开终端输入npm install animate.css --save 安装模块
animate.css官网地址
参考官方文档安装使用animate.css
npm install animate.css --save
2、main.js中引入animate.css
import 'animate.css';
3、app.vue中添加全局样式兼容微信小程序
项目中我已将改代码放入base.scss文件中
//animate.css 兼容小程序
page {
--animate-duration: 1s;
--animate-delay: 1s;
--animate-repeat: 1;
}
4、根据animate.css官网文档给标签添加动画,然后再通过js监听屏幕滚动当标签出现在屏幕可视区时给标签切换class,页面全部代码如下:
main-layout 是自定义组件,除去即可
<template> <view class="home-wrap"> //<main-layout title="首页"> <view class="animate-item animate__animated " v-for="(item,index) in domList" :id="'item'+item.id" :class="item.show?item.showClass:item.hideClass">{{item.text}}</view> //</main-layout> </view> </template> <script lang="ts"> import { defineComponent, computed, onMounted, watch, ref, getCurrentInstance, reactive, nextTick, toRefs, toRef } from 'vue'; export default { setup(props,context){ //dom节点列表 let domList: { id: String | Number, text: String, show: Boolean, showClass: String, hideClass: String, siteInfo: Object } [] = ref([{ id: 1, text: '1', show: true, showClass: 'animate__fadeInLeftBig', hideClass: 'animate__fadeOutLeftBig', siteInfo: {} }, { id: 2, text: '2', show: true, showClass: 'animate__fadeInRightBig', hideClass: 'animate__fadeOutRightBig', siteInfo: {} }, { id: 3, text: '3', show: true, showClass: 'animate__fadeInLeftBig', hideClass: 'animate__fadeOutLeftBig', siteInfo: {} }, { id: 4, text: '4', show: true, showClass: 'animate__fadeInRightBig', hideClass: 'animate__fadeOutRightBig', siteInfo: {} }, { id: 5, text: '5', show: true, showClass: 'animate__fadeInLeftBig', hideClass: 'animate__fadeOutLeftBig', siteInfo: {} }, { id: 6, text: '6', show: true, showClass: 'animate__fadeInRightBig', hideClass: 'animate__fadeOutRightBig', siteInfo: {} }, { id: 7, text: '7', show: true, showClass: 'animate__fadeInLeftBig', hideClass: 'animate__fadeOutLeftBig', siteInfo: {} }, { id: 8, text: '8', show: true, showClass: 'animate__fadeInRightBig', hideClass: 'animate__fadeOutRightBig', siteInfo: {} }, { id: 9, text: '9', show: true, showClass: 'animate__fadeInLeftBig', hideClass: 'animate__fadeOutLeftBig', siteInfo: {} }]) // 获取上下文this const instance = getCurrentInstance(); // 获取屏幕高度 const sysInfo = uni.getSystemInfoSync(); // 屏幕滚动防抖定时器 let scrollTimer = null; const screenScroll = (): void => { //屏幕滚动停止后获取dom节点信息,执行动画 if (scrollTimer) clearTimeout(scrollTimer); scrollTimer = setTimeout(() => { domList.value.forEach(async (t, i) => { await getNodeInfo('item' + t.id).then(res => { domList.value[i].siteInfo = res; }) if (i == domList.value.length - 1) { loadAni(); } }) }, 100) } const getNodeInfo = (id: String): any => { // 获取位置信息并返回 return new Promise(resolve => { const query = uni.createSelectorQuery().in(instance); query.select('#' + id).boundingClientRect(data => { // console.log("得到布局位置信息" + JSON.stringify(data)); // console.log("节点离页面顶部的距离为" + data.top); resolve({ domInfo: data ? data.height : 100, domTop: data ? data.top : 100 }) }).exec(); }); } const loadAni = (): void => { const screenH: String | Number = sysInfo.screenHeight; domList.value.forEach((t, i) => { if ((t.siteInfo.domTop > 0) && (t.siteInfo.domTop < screenH)) { domList.value[i].show = true; } else { domList.value[i].show = false; } }) } onMounted(() => { screenScroll(); }) return{ domList, screenScroll } }, onPageScroll() { this.screenScroll(); }, onReachBottom() { console.log('bottom') } } </script> <style lang="scss"> .home-wrap{ overflow: hidden; .animate-item{ border: 1px solid #bbb; margin: 10px 10px 100px; text-align: center; padding: 10px; } } </style>
1、pages.json中这只导航栏样式为自定义
"globalStyle": {
"navigationStyle": "custom"
},
2、编写头部组件代码
<template> <view class="main-wrap" :style="'padding-top:'+(statusBarHeight+titleBarHeight)+'px;'"> <view class="head-bar"> //状态栏占位 <view class="status-bar" :style="'height:'+statusBarHeight+'px'"> </view> <view class="title-bar" :style="'height:'+titleBarHeight+'px'"> //导航左侧返回键 <view class="title-bar-left"> <uni-icons type="back" size="30" v-if="back" @click="backPage"></uni-icons> </view> //导航栏中间标题 <view class="title-bar-center"> {{cTitle}} </view> //导航栏右侧插槽 <view class="title-bar-right"> <slot name="titleRight"></slot> </view> </view> </view> //导航栏下方页面内容区域插槽 <view class="main"> <slot></slot> </view> </view> </template> <script lang="ts"> import { defineComponent, computed, onMounted, watch,ref,getCurrentInstance,reactive,nextTick ,toRefs} from 'vue' export default { name:"MainLayout", props:{ title:{ type:String||Number, default:'' }, back:{ type:Boolean, default:false } }, created() { this.setHeaderHeight(); }, setup(props, context){ let statusBarHeight = ref<Number>(0); let titleBarHeight = ref<Number>(0); let tabBarHeight = ref<Number>(60); const setHeaderHeight =():void=>{ uni.getSystemInfo({ success:e=>{ let statusBar = 0 //状态栏高度 let customBar = 0 // 状态栏高度 + 导航栏高度 let navbar = 0 // 自定义标题与胶囊对齐高度 // #ifdef MP statusBar = e.statusBarHeight customBar = e.statusBarHeight + 45 if (e.platform === 'android') { customBar = e.statusBarHeight + 50 } // #endif // #ifdef MP-WEIXIN statusBar = e.statusBarHeight const custom = wx.getMenuButtonBoundingClientRect() customBar = custom.bottom + custom.top - e.statusBarHeight navbar = (custom.top - e.statusBarHeight) * 2 + custom.height // #endif // #ifdef MP-ALIPAY statusBar = e.statusBarHeight customBar = e.statusBarHeight + e.titleBarHeight // #endif // #ifdef APP-PLUS console.log('app-plus', e) statusBar = e.statusBarHeight customBar = e.statusBarHeight + 45 // #endif // #ifdef H5 statusBar = 0 customBar = e.statusBarHeight + 45 // #endif titleBarHeight.value = navbar||customBar; statusBarHeight.value = statusBar; } }) } const cTitle = computed({ get:()=>{ return props.title ; }, set:()=>{} }) const backPage = ():void=>{ uni.navigateBack({ //关闭当前页面,返回上一页面或多级页面。 delta:1 }); } return { setHeaderHeight, statusBarHeight, titleBarHeight, tabBarHeight, cTitle, backPage } } } </script> <style lang="scss"> //@import "@/styles/base.scss"; .main-wrap { display: block; position: relative; width:100%; .head-bar{ position: fixed; top: 0; left: 0; right: 0; z-index:999; .status-bar{ background-color: #fff; } .title-bar{ display: flex; align-items: center; justify-content: space-between; background-color: $uni-bg-color-nav; .title-bar-left,.title-bar-right{ width: 50px; } .title-bar-center{ white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: calc(100% - 100px); } } } } </style>
3、 main.js全局引用该组件
import MainLayout from "./layout/index.vue"
//vue2
Vue.component("main-layout", MainLayout);
//vue3
app.component("main-layout", MainLayout);
4、页面上直接使用该组件
<main-layout title="首页" back>
<template v-slot:titleRight>
<text>右插槽</text>
</template>
<text>页面内容</text>
</main-layout>
结语:有用点赞,无用留言批评
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。