myJsAnimation.js, 这里使用了上次封装的动画方法,并进行了改造
/**
* 动画的函数
* dom 当前对象
* JSON 传入元素对象的属性 {"width": 300, "opacity": 50}
*
* -------------------- 多物体运动,同时运动 ---传入JSON-------------
*/
let speed1 = 0
export function startAnimation2(dom, JSON, fn) {
// 注意:针对于多物体运动,定时器的返回值要绑定当前的对象中。offsetWidth获取的是包括border的宽度,所以这里使用getComputed获取width
clearInterval(dom.timer)
dom.timer = setInterval(() => {
let cur = 0
let flag = true // 标杆 如果true,证明所有的属性都到达终点值
// 0 获取样式属性
for (let attr in JSON) {
switch (attr) {
case 'opacity':
cur = Math.round(parseFloat(getStyle(dom, attr)) * 100)
break;
case 'scrollTop':
cur = dom[attr]
break;
default:
cur = parseInt(getStyle(dom, attr))
break;
}
// if (attr === 'opacity') {
// // 求透明度的变化速度,注意!小数需要取整
// cur = Math.round(parseFloat(getStyle(dom, attr)) * 100)
// } else {
// // 获取dom宽度或高度等
// cur = parseInt(getStyle(dom, attr))
// }
// 1、求速度
speed1 = (JSON[attr] - cur) / 20
speed1 = JSON[attr] > cur ? Math.ceil(speed1) : Math.floor(speed1)
// 2、临界处理
if (JSON[attr] !== cur) {
flag = false
}
// 3、运动起来
switch (attr) {
case 'opacity':
dom.style[attr] = `alpha(opacity=${cur + speed1})`
dom.style[attr] = (cur + speed1) / 100
break;
case 'scrollTop':
dom[attr] = cur + speed1
break;
default:
dom.style[attr] = cur + speed1 + 'px'
break;
}
// if (attr === 'opacity') {
// dom.style[attr] = `alpha(opacity=${cur + speed1})`
// dom.style[attr] = (cur + speed1) / 100
// } else {
// dom.style[attr] = cur + speed1 + 'px'
// }
}
if (flag) {
clearInterval(dom.timer)
if (fn) {
fn()
}
return
}
}, 30)
// dom 是对象, attr 是什么属性
// 获取元素属性的方法
function getStyle(dom, attr) {
if (dom.currentStyle) {
// 针对IE浏览器
return dom.currentStyle[attr]
} else {
// 针对 Firefox浏览器
return getComputedStyle(dom, null)[attr]
}
}
}
index.vue
<script setup>
import { ref, onMounted, onUnmounted, nextTick, watch, reactive } from 'vue'
import { startAnimation2 } from './MyJSAnimation/myJsAnimation2'
// ----------------------- 08 联动效果 ---------------------
// 1、联动效果
// 2、侧边栏横幅
// 3、滚动监听
// 4、轮播图
// ------------- 1 右下角联动效果 ------------------
const adRef = ref(null)
const close = () => {
startAnimation2(adRef.value, { "height": 160 }, () => {
startAnimation2(adRef.value, { "width": 0 }, () => {
adRef.value.style.display = 'none'
})
})
}
// ---------------- 2 左侧边栏横幅 --滚动效果----------------
const asideRef = ref(null)
let aside_top = 0
const raside = ref(null)
const handleScroll = (e) => {
const lis = raside.value.querySelectorAll('li')
const scrollTop = window.scrollY || document.documentElement.scrollTop;
console.log('页面滚动距离:', scrollTop);
startAnimation2(asideRef.value, { "top": scrollTop + aside_top })
// 监听页面滚动,选中右边侧边栏
if (!list.isClick) {
// 获取页面滚动的高度
const scrollTop = window.scrollY || document.documentElement.scrollTop;
for (let i = 0; i < lis.length; i++) {
if (scrollTop >= list.box[i].offsetTop) {
list.currentType = list.items[i].type
}
}
}
}
// ----------------3 淘宝案例---------------------
const list = reactive({
items: [
{ id: 1, name: '爱逛好货', type: '1' },
{ id: 2, name: '好店主播', type: '2' },
{ id: 3, name: '品质特色', type: '3' },
{ id: 4, name: '猜你喜欢', type: '4' }
],
currentType: '1',
isClick: false, // 是否点击右侧边栏
box: null, // 所有的大盒子
})
const boxRef = ref(null)
const getStyle = () => {
// 上色
const color = ['skyblue', 'orange', 'blue', 'purple']
for (let index = 0; index < list.box.length; index++) {
list.box[index].style.backgroundColor = color[index];
}
}
// 监听右导航器按钮的点击
const handleClickItem = (item, index) => {
list.isClick = true
list.currentType = item.type
nextTick(() => {
// 文档的顶部到视口顶部的距离 = indx * 文档高度
// document.documentElement.scrollTop = index * document.body.clientHeight
// console.log(document.documentElement.scrollTop);
// 页面动画
startAnimation2(document.documentElement, { "scrollTop": index * document.body.clientHeight }, () => {
list.isClick = false
})
})
}
onMounted(() => {
aside_top = asideRef?.value?.offsetTop // 左侧边栏举例顶部的距离
list.box = boxRef.value.querySelectorAll('div')
window.addEventListener('scroll', handleScroll);
getStyle()
})
onUnmounted(() => {
window.removeEventListener('scroll', handleScroll);
});
</script>
<template>
<div class="info" id="info">
<div class="main" id="box" ref="boxRef">
<div class="current">爱逛好货</div>
<div>好店主播</div>
<div>品质特色</div>
<div>猜你喜欢</div>
</div>
<!-- 右导航器 -->
<ul class="r-aside" ref="raside">
<li v-for="(item, index) in list.items" :key="item.id"
:class="`${item.type === list.currentType ? 'active' : ''}`" @click="handleClickItem(item, index)">
<a href="javascript:void(0)">{{ item.name }}</a>
</li>
</ul>
<!-- 01 右下角广告联动效果 -->
<div id="ad" ref="adRef">
<img src="../assets/vue.svg" alt="">
<span id="close" @click="close">X</span>
</div>
<!-- 左侧边栏横幅效果 -->
<div id="aside" ref="asideRef">
<img src="../assets/vue.svg" alt="">
</div>
</div>
</template>
<style scoped lang="less">
.info {
display: flex;
flex-direction: column;
position: relative;
.main {
width: 1190px;
height: 5000px;
margin: 0 auto;
&>div {
width: 100%;
height: 100%;
text-align: center;
font-size: 30px;
}
}
// 01 联动效果
#ad {
position: fixed;
bottom: 0;
right: 0;
background-color: pink;
img {
width: 200px;
height: 200px;
}
#close {
position: absolute;
top: 0;
right: 0;
width: 20px;
height: 20px;
text-align: center;
line-height: 20px;
cursor: pointer;
// background-color: skyblue;
z-index: 5;
}
}
#aside {
position: absolute;
top: 200px;
left: 0;
// transform: translateY(-50%);
background-color: pink;
img {
width: 100px;
height: 100px;
}
}
ul {
list-style: none;
}
a {
text-decoration: none;
}
.r-aside {
position: fixed;
right: 0;
top: 50%;
transform: translateY(-50%);
width: 40px;
font-size: 16px;
font-weight: 700;
text-align: center;
li {
height: 50px;
border-bottom: 1px solid #ddd;
a {
color: peru;
}
&.active {
background-color: coral;
a {
color: #fff;
}
}
}
}
}
</style>