网站建设参考网站建设公司开发
文章目录
- 22-首页主体-补充-vue动画
- 23-首页主体-面板骨架效果
- 24-首页主体-组件数据懒加载
- 25-首页主体-热门品牌
22-首页主体-补充-vue动画
目标: 知道vue中如何使用动画,知道Transition组件使用。
当vue中,显示隐藏,创建移除,一个元素或者一个组件的时候,可以通过transition实现动画。
如果元素或组件离开,完成一个淡出效果:
<transition name="fade"><p v-if="show">100</p>
</transition>
.fade-leave {opacity: 1
}
.fade-leave-active {transition: all 1s;
}
.fade-leave-to {opcaity: 0
}
- 进入(显示,创建)
- v-enter 进入前 (vue3.0 v-enter-from)
- v-enter-active 进入中
- v-enter-to 进入后
- 离开(隐藏,移除)
- v-leave 进入前 (vue3.0 v-leave-from)
- v-leave-active 进入中
- v-leave-to 进入后
多个transition使用不同动画,可以添加nam属性,name属性的值替换v即可。
23-首页主体-面板骨架效果
目的: 加上面板的骨架加载效果
定义一个骨架布局组件:
src/views/home/components/home-skeleton.vue
<template><div class='home-skeleton'><div class="item" v-for="i in 4" :key="i" :style="{backgroundColor:bg}"><XtxSkeleton bg="#e4e4e4" width="306px" height="306px" animated /><XtxSkeleton bg="#e4e4e4" width="160px" height="24px" animated /><XtxSkeleton bg="#e4e4e4" width="120px" height="24px" animated /></div></div>
</template><script>
export default {name: 'HomeSkeleton',props: {bg: {type: String,default: '#fff'}}
}
</script><style scoped lang='less'>
.home-skeleton {width: 1240px;height: 406px;display: flex;justify-content: space-between;.item {width: 306px;.xtx-skeleton ~ .xtx-skeleton{display: block;margin: 16px auto 0;}}
}
</style>
在 home-hot
home-new
组件分别使用
<HomePanel title="人气推荐" sub-title="人气爆款 不容错过">
+ <div style="position: relative;height: 426px;">
+ <Transition name="fade">
+ <ul v-if="goods.length" ref="pannel" class="goods-list"><li v-for="item in goods" :key="item.id"><RouterLink to="/"><img :src="item.picture" alt=""><p class="name">{{item.title}}</p><p class="desc">{{item.alt}}</p></RouterLink></li></ul>
+ <HomeSkeleton v-else />
+ </Transition>
+ </div></HomePanel>
<template><HomePanel title="新鲜好物" sub-title="新鲜出炉 品质靠谱"><template v-slot:right><XtxMore /></template>
+ <div style="position: relative;height: 406px;">
+ <Transition name="fade">
+ <ul v-if="goods.length" ref="pannel" class="goods-list"><li v-for="item in goods" :key="item.id"><RouterLink to="/"><img :src="item.picture" alt=""><p class="name">{{item.name}}</p><p class="price">¥{{item.price}}</p></RouterLink></li></ul>
+ <HomeSkeleton bg="#f0f9f4" v-else />
+ </Transition>
+ </div></HomePanel>
</template>
在 src/assets/styles/common.less
定义动画
.fade{&-leave {&-active {position: absolute;width: 100%;transition: opacity .5s .2s;z-index: 1;}&-to {opacity: 0;}}
}
注意:
- 动画的父容器需要是定位,防止定位跑偏。
24-首页主体-组件数据懒加载
目的: 实现当组件进入可视区域在加载数据。
我们可以使用 @vueuse/core
中的 useIntersectionObserver
来实现监听进入可视区域行为,但是必须配合vue3.0的组合API的方式才能实现。
大致步骤:
- 理解
useIntersectionObserver
的使用,各个参数的含义 - 改造 home-new 组件成为数据懒加载,掌握
useIntersectionObserver
函数的用法 - 封装
useLazyData
函数,作为数据懒加载公用函数 - 把
home-new
和home-hot
改造成懒加载方式
落的代码:
1.先分析下这个useIntersectionObserver
函数:
// stop 是停止观察是否进入或移出可视区域的行为
const { stop } = useIntersectionObserver(// target 是观察的目标dom容器,必须是dom容器,而且是vue3.0方式绑定的dom对象target,// isIntersecting 是否进入可视区域,true是进入 false是移出// observerElement 被观察的dom([{ isIntersecting }], observerElement) => {// 在此处可根据isIntersecting来判断,然后做业务},
)
2.开始改造 home-new 组件:src/views/home/components/home-new.vue
- 进入可视区后获取数据
<div ref="box" style="position: relative;height: 406px;">
// 省略。。。
<script>
import HomePanel from './home-panel'
import HomeSkeleton from './home-skeleton'
import { findNew } from '@/api/home'
import { ref } from 'vue'
import { useIntersectionObserver } from '@vueuse/core'
export default {name: 'HomeNew',components: { HomePanel, HomeSkeleton },setup () {const goods = ref([])const box = ref(null)const { stop } = useIntersectionObserver(box,([{ isIntersecting }]) => {if (isIntersecting) {stop()findNew().then(data => {goods.value = data.result})}})return { goods, box }}
}
</script>
3.由于首页面板数据加载都需要实现懒数据加载,所以封装一个钩子函数,得到数据。
src/hooks/index.js
// hooks 封装逻辑,提供响应式数据。
import { useIntersectionObserver } from '@vueuse/core'
import { ref } from 'vue'
// 数据懒加载函数
export const useLazyData = (apiFn) => {// 需要// 1. 被观察的对象// 2. 不同的API函数const target = ref(null)const result = ref([])const { stop } = useIntersectionObserver(target,([{ isIntersecting }], observerElement) => {if (isIntersecting) {stop()// 调用API获取数据apiFn().then(data => {result.value = data.result})}})// 返回--->数据(dom,后台数据)return { target, result }
}
4.再次改造 home-new
组件:src/views/home/components/home-new.vue
import { findNew } from '@/api/home'
+import { useLazyData } from '@/hooks'
export default {name: 'HomeNew',components: { HomePanel, HomeSkeleton },setup () {
+ const { target, result } = useLazyData(findNew)
+ return { goods: result, target }}
}
+ <div ref="target" style="position: relative;height: 426px;">
5.然后改造 home-hot
组件:src/views/home/components/home-hot.vue
+ <div ref="target" style="position: relative;height: 426px;">
import { findHot } from '@/api/home'
import HomePanel from './home-panel'
import HomeSkeleton from './home-skeleton'
+import { useLazyData } from '@/hooks'
export default {name: 'HomeHot',components: { HomePanel, HomeSkeleton },setup () {
+ const { target, result } = useLazyData(findHot)
+ return { target, list: result }}
}
25-首页主体-热门品牌
目的: 实现品牌的展示,和切换品牌效果。
基本步骤:
- 准备基础布局组件
- 获取数据实现渲染,完成切换效果
- 加上骨架效果和数据懒加载
落的代码:
.基础结构:
src/views/home/components/home-brand.vue`
<template><HomePanel title="热门品牌" sub-title="国际经典 品质保证"><template v-slot:right><a href="javascript:;" class="iconfont icon-angle-left prev"></a><a href="javascript:;" class="iconfont icon-angle-right next"></a></template><div class="box" ref="box"><ul class="list" ><li v-for="i in 10" :key="i"><RouterLink to="/"><img src="http://zhoushugang.gitee.io/erabbit-client-pc-static/uploads/brand_goods_1.jpg" alt=""></RouterLink></li></ul></div></HomePanel>
</template><script>
import HomePanel from './home-panel'
export default {name: 'HomeBrand',components: { HomePanel }
}
</script><style scoped lang='less'>
.home-panel {background:#f5f5f5
}
.iconfont {width: 20px;height: 20px;background: #ccc;color: #fff;display: inline-block;text-align: center;margin-left: 5px;background: @xtxColor;&::before {font-size: 12px;position: relative;top: -2px}&.disabled {background: #ccc;cursor: not-allowed;}
}
.box {display: flex;width: 100%;height: 345px;overflow: hidden;padding-bottom: 40px;.list {width: 200%;display: flex;transition: all 1s;li {margin-right: 10px;width: 240px;&:nth-child(5n) {margin-right: 0;}img {width: 240px;height: 305px;}}}
}
</style>
2.使用组件:src/views/home/index.vue
<!-- 人气推荐 --><HomeHot /><!-- 热门品牌 -->
+ <HomeBrand />
+import HomeBrand from './components/home-brand'
export default {name: 'xtx-home-page',
+ components: { HomeCategory, HomeBanner, HomeNew, HomeHot, HomeBrand }
}
2.获取数据和切换效果:
- 由于最后会使用到数据懒加载,那么我们也会使用组合API实现。
- 业务上,只有两页数据切换,0—>1 或者 1—>0 的方式。
<template><HomePanel title="热门品牌" sub-title="国际经典 品质保证"><template v-slot:right><a @click="toggle(-1)" :class="{disabled:index===0}" href="javascript:;" class="iconfont icon-angle-left prev"></a><a @click="toggle(1)" :class="{disabled:index===1}" href="javascript:;" class="iconfont icon-angle-right next"></a></template><div class="box"><ul v-if="brands.length" class="list" :style="{transform:`translateX(${-index*1240}px)`}"><li v-for="item in brands" :key="item.id"><RouterLink to="/"><img :src="item.picture" alt=""></RouterLink></li></ul></div></HomePanel>
</template><script>
import { ref } from 'vue'
import HomePanel from './home-panel'
import { findBrand } from '@/api/home'
import { useLazyData } from '@/hooks'
export default {name: 'HomeBrand',components: { HomePanel },setup () {// 获取数据const brands = ref([])findBrand(10).then(data => {brands.value = data.result})// 切换效果,前提只有 0 1 两页const index = ref(0)// 1. 点击上一页// 2. 点击下一页const toggle = (step) => {const newIndex = index.value + stepif (newIndex < 0 || newIndex > 1) returnindex.value = newIndex}return { brands, toggle, index }}
}
</script>
3.加上数据懒加载和骨架效果
<template><HomePanel title="热门品牌" sub-title="国际经典 品质保证"><template v-slot:right><a @click="toggle(-1)" :class="{disabled:index===0}" href="javascript:;" class="iconfont icon-angle-left prev"></a><a @click="toggle(1)" :class="{disabled:index===1}" href="javascript:;" class="iconfont icon-angle-right next"></a></template>
+ <div ref="target" class="box">
+ <Transition name="fade">
+ <ul v-if="brands.length" class="list" :style="{transform:`translateX(${-index*1240}px)`}"><li v-for="item in brands" :key="item.id"><RouterLink to="/"><img :src="item.picture" alt=""></RouterLink></li></ul>
+ <div v-else class="skeleton">
+ <XtxSkeleton class="item" v-for="i in 5" :key="i" animated bg="#e4e4e4" width="240px" height="305px"/>
+ </div>
+ </Transition></div></HomePanel>
</template><script>
import { ref } from 'vue'
import HomePanel from './home-panel'
import { findBrand } from '@/api/home'
+import { useLazyData } from '@/hooks'
export default {name: 'HomeBrand',components: { HomePanel },setup () {// 获取数据// const brands = ref([])// findBrand(10).then(data => {// brands.value = data.result// })
+ // 注意:useLazyData需要的是API函数,如果遇到要传参的情况,自己写函数再函数中调用API
+ const { target, result } = useLazyData(() => findBrand(10))// 切换效果,前提只有 0 1 两页const index = ref(0)// 1. 点击上一页// 2. 点击下一页const toggle = (step) => {const newIndex = index.value + stepif (newIndex < 0 || newIndex > 1) returnindex.value = newIndex}
+ return { brands: result, toggle, index, target }}
}
</script>
.skeleton {width: 100%;display: flex;.item {margin-right: 10px;&:nth-child(5n) {margin-right: 0;}}}
总结: 注意下useLazyData传参的情况。