vue3中自制了一个页面滚动功能和一个暂停、滚动按钮,目前出现的问题是暂停、滚动按钮有时好使有时失效,并且页面自动滚动偶尔会出现页面抖动的问题,求帮忙解决一下
以下是代码
<template>
<div class="home" v-on:scroll="onScroll" ref="scrollContainer">
<div class="header">仓库看板div>
<div class="home-table">
<table class="homeTable1" border="1" cellspacing="0">
<thead class="table-head">
<th class="bgPrimary">库区th>
<th class="bgPrimary">层数th>
<th class="bgPrimary" v-for="(item, index) in 22" :key="index">
{{ (item = item < 10 ? "00" + item : "0" + item) }}
th>
thead>
<tbody v-for="(item, index) in filteredList" :key="index">
<tr v-for="(items, indexs) in item.list" :key="indexs">
<th
:rowspan="item.list.length"
class="bgPrimary"
v-if="indexs == 0"
>
{{ item.name }}
th>
<th class="bgPrimary">{{ item.list.length - indexs }}th>
<td
v-for="(itemss, indexss) in items.info"
:key="indexss"
:class="
itemss.volmueType == 0
? 'colorEmpty'
: itemss.volmueType > 0 && itemss.volmueType < 50
? 'colorMore'
: itemss.volmueType >= 50 && itemss.volmueType <= 100
? 'colorMore'
: itemss.volmueType > 100
? 'colorMore'
: 'colorPrimary'
"
:style="itemss.volmueType == '-1' ? 'border:none' : ''"
>
{{ itemss.MatName }}
td>
tr>
<div class="space">div>
tbody>
table>
div>
<div class="home-table">
<table class="homeTable1" border="1" cellspacing="0">
<thead>
<th class="bgPrimary">库区th>
<th class="bgPrimary">层数th>
<th class="bgPrimary" v-for="(item, index) in 10" :key="index">
{{ (item = item < 10 ? "00" + item : "0" + item) }}
th>
thead>
<tbody v-for="(item, index) in filteredElseList" :key="index">
<tr v-for="(items, indexs) in item.list" :key="indexs">
<th
:rowspan="item.list.length"
class="bgPrimary"
v-if="indexs == 0"
>
{{ item.name }}
th>
<th class="bgPrimary">{{ item.list.length - indexs }}th>
<td
v-for="(itemss, indexss) in items.info"
:key="indexss"
:class="
itemss.volmueType == 0
? 'colorEmpty'
: itemss.volmueType > 0 && itemss.volmueType < 50
? 'colorMore'
: itemss.volmueType >= 50 && itemss.volmueType <= 100
? 'colorMore'
: itemss.volmueType > 100
? 'colorMore'
: 'colorPrimary'
"
>
{{ itemss.MatName }}
td>
tr>
<div class="space">div>
tbody>
table>
div>
<el-button type="primary" class="floatBtn" @click="handleOnscroll">{{
isPaused ? "开始滚动" : "暂停滚动"
}}el-button>
div>
template>
<script>
import { onMounted, ref, onUnmounted, computed } from "vue";
import { ExecJSON2 } from "@/api/mainPage";
export default {
name: "mainPage",
setup() {
const tableData = ref([]);
const rowstyles = ref();
const areaData = ref(new Array());
const timer = ref();
const scrolling = ref(false);
const timeoutId = ref(null);
const scrollContainer = ref(null);
const intervalId = ref(null);
const isPaused = ref(false);
const filteredList = computed(() => {
if (!tableData.value) {
return [];
}
return tableData.value.filter((item) => item.list.length > 1);
});
const filteredElseList = computed(() => {
return tableData.value.filter((item) => item.list.length == 1);
});
onMounted(() => {
getAllData();
onScroll();
timer.value = setInterval(() => {
getAllData();
location.reload();
}, 30 * 60 * 1000);
});
onUnmounted(() => {
clearInterval(timer);
});
const getAllData = () => {
const param = {
Code: "KB_01",
Data: [
{
WarehouseCode: "01",
},
],
};
ExecJSON2(param)
.then(async (res) => {
if (res.Success == "01") {
tableData.value = await bubbleSort(res.Data1);
}
})
.catch((err) => {
console.log(err, 19);
});
};
const bubbleSort = async (array) => {
for (let i = 0; i < array.length; i++) {
array[i].info = JSON.parse(array[i].info);
let newDataList = [];
for (let j = 0; j < array[i].info.length; j++) {
newDataList.push(array[i].info[j]);
let curNum = parseInt(array[i].info[j].WarehouseLocation, 10);
if (array[i].info[j + 1]) {
let nextNum = parseInt(array[i].info[j + 1].WarehouseLocation, 10);
if (nextNum - curNum > 1) {
for (let j = 1; j < nextNum - curNum; j++) {
newDataList.push({
MatName: "",
WarehouseLocation: (curNum + j).toString().padStart(3, "0"),
WarehouseLayers: array[i].info[j].WarehouseLayers,
WarehouseArea: array[i].info[j].WarehouseArea,
Volume: "0.00",
NewVolume: "0.00",
Quantity: "0",
SafetyStock: "0",
volmueType: "-1",
});
}
}
}
array[i].info[j].volmuePersent =
parseInt(array[i].info[j].NewVolume) == "0"
? " "
: Math.round(
(parseFloat(array[i].info[j].Volume) /
parseFloat(array[i].info[j].NewVolume)) *
10000
) /
100 +
"%";
array[i].info[j].volmueType =
parseInt(array[i].info[j].NewVolume) == "0"
? 0
: Math.round(
(parseFloat(array[i].info[j].Volume) /
parseFloat(array[i].info[j].NewVolume)) *
10000
) / 100;
}
array[i].info = newDataList;
}
array = await groupArr(array, "WarehouseArea");
return array;
};
const groupArr = (list, field) => {
var obj = {};
for (var i = 0; i < list.length; i++) {
for (let item in list[i]) {
if (item == field) {
obj[list[i][item]] = {
list: obj[list[i][field]] ? obj[list[i][field]].list : [],
name: list[i][field],
};
}
}
obj[list[i][field]].list.push(list[i]);
}
var att = [];
for (let item in obj) {
att.push({
list: obj[item].list,
name: obj[item].name,
});
}
return att;
};
const scrollDirection = ref("down");
const isAutoScrolling = ref(true);
const onScroll = () => {
if (isPaused.value) return;
// 停止自动滚动
clearTimeout(timeoutId.value);
// 设置滚动状态
scrolling.value = true;
// 滚动页面
const windowHeight = window.innerHeight;
const contentHeight = document.documentElement.scrollHeight;
let position = window.pageYOffset;
let reachedTop = false;
intervalId.value = setInterval(() => {
if (scrollDirection.value == "down") {
position += 1; // 每次滚动1px
window.scrollTo(0, position);
if (position + windowHeight >= contentHeight) {
clearInterval(intervalId.value);
scrolling.value = false;
scrollDirection.value = "up";
// 两秒后重新开始滚动
timeoutId.value = setTimeout(() => {
onScroll();
}, 2000);
}
} else {
if (position === 0) {
reachedTop = true;
}
if (!reachedTop) {
position -= 1; // 每次滚动1px
window.scrollTo(0, position);
}
if (reachedTop && position === 0) {
clearInterval(intervalId);
scrolling.value = false;
scrollDirection.value = "down";
reachedTop = false;
// 两秒后重新开始滚动
timeoutId.value = setTimeout(() => {
onScroll();
}, 2000);
}
}
}, 20); // 每20毫秒滚动一次
};
const handleOnscroll = () => {
if (isPaused.value) {
// 继续滚动
isPaused.value = false;
onScroll();
} else {
// 暂停滚动
isPaused.value = true;
clearTimeout(timeoutId.value);
clearInterval(intervalId.value);
}
console.log("isPaused.value:" + isPaused.value);
console.log("scrolling.value:" + scrolling.value);
console.log("intervalId.value:" + intervalId.value);
console.log("timeoutId.value:" + timeoutId.value);
};
return {
getAllData,
bubbleSort,
tableData,
rowstyles,
areaData,
groupArr,
filteredList,
filteredElseList,
timer,
scrolling,
timeoutId,
onScroll,
scrollContainer,
scrollDirection,
isAutoScrolling,
handleOnscroll,
intervalId,
isPaused,
};
},
mounted() {
this.$refs.scrollContainer.addEventListener("scroll", this.onScroll);
},
beforeUnmount() {
this.$refs.scrollContainer.removeEventListener("scroll", this.onScroll);
},
};
script>
<style lang="less" scoped>
.home {
width: 100%;
.header {
font-size: 1.2rem;
color: #409eff;
position: sticky;
top: 0;
background: #f2f6fc;
}
.home-table {
display: flex;
table {
table-layout: fixed;
height: 100%;
width: 100%;
}
.table-head {
position: sticky;
top: 1.5rem;
}
}
table,
th {
text-align: center;
border: 0.5px solid #f5f6f9;
border-collapse: collapse;
color: #ffffff;
font-size: 0.7rem;
background: #f2f6fc;
}
.space {
width: 100%;
height: 3rem;
}
td {
color: black;
height: 3rem;
}
table td {
margin: 0 auto;
}
.colorPrimary {
background: #f2f6fc !important;
}
.bgPrimary {
background: #409eff !important;
}
.colorEmpty {
background: #ffffff !important;
}
.colorSimple {
background: #e3f6f5 !important;
}
.colorMore {
background: #bae8e8 !important;
}
.colorOverload {
background: #2c698d !important;
}
.floatBtn {
position: fixed;
right: 1rem;
bottom: 3rem;
}
}
style>
v3 用v2写法??
根据你提供的代码,初步判断可能出现问题的地方有:
暂停、滚动按钮的状态管理不够清晰,可能会出现状态混乱的情况,导致按钮失效。建议使用 computed 计算属性来管理 isPaused 状态,这样方便在模板中直接引用,同时也不容易出现状态混乱的问题。
页面抖动的问题可能是因为滚动事件触发的频率过高,建议将 intervalId 的时间间隔调大一些,比如 50 毫秒。
另外,建议在开发环境下打开浏览器的 Console,查看控制台是否有报错信息,有可能也会影响页面的滚动效果。
1、div标签不能放在tbody标签中,移出去吧
2、没有定义事件处理函数,v-on:scroll="onScroll"
3、你用isPaused这个变量来控制暂停/滚动,但是没有看到你在事件处理函数中修改isPaused的值啊
现在我们把各种函数都放在了 setup 中,这样做肯定没有出错,但是这样会让我们的 setup 函数非常长,如果项目做到后面我们要在里面找某一个函数或者变量的时候,都很麻烦,如果把关于登录逻辑的数据和方法都放在 setup 外面的一个函数中,关于注册逻辑的数据和方法放在另一个函数中,这样再把这些函数在 setup 中接收,在 setup 中我们只关心整个页面的实现逻辑就行,这样整个代码的维护性和可读性都大大提高了。
在登录页面我们把向后端发送登录请求的相关逻辑从 setup 中抽离出来:
const useLoginEffect = () => {
const router = useRouter()
let username = ref('')
let password = ref('')
let handleLogin = async () => {
if (username.value === '' || password.value === '') {
alertmessage('输入内容不能为空', 'warning', 1500)
return
}
try {
const result = await post('/api/user/login', {
username: username,
password: password
})
if (result.data.errno === 0) {
localStorage.isLogin = true
alertmessage('登录成功欢迎您', 'success', 2000)
setTimeout(() => {
router.push({ name: 'home' })
}, 2000)
} else {
alertmessage('登录失败', 'error', 2000)
}
} catch (e) {
alertmessage('请求失败', 'error', 2000)
}
}
return { username, password, handleLogin }
}
这里我们重新定义了一个useLoginEffect 函数,然后把需要用到的数据和方法都放进来,最后通过 return 把数据和方法返回出来,以便在 setup 中接收。
我们再把点击注册这个函数的相关逻辑抽离出来:
const useRegisterEffect = () => {
const router = useRouter()
let handleRegisterClick = () => {
ElNotification({
title: '尊敬的用户您好',
message: h('i', { style: 'color: teal' }, '正在跳转到注册页面'),
duration: 800
})
setTimeout(() => {
router.push({ name: 'register' })
}, 900)
}
return { handleRegisterClick }
}
这样我们就把登录页面相关的功能都从 setup 里面抽离了出来,现在再看 setup 里的代码就优雅了许多,浅显易懂:
setup () {
const { username, password, handleLogin } = useLoginEffect()
const { handleRegisterClick } = useRegisterEffect()
return {
handleLogin,
username,
password,
handleRegisterClick
}
}
在 setup 里我们很清晰的知道这个页面的实现逻辑,如果想修改跳转登录这个函数就去对应的函数里修改就可以,方便了很多。
在注册页面的代码拆分和登录页面的相同,这里就不过多阐述。在本文中我们暂时完成了项目首页和登陆注册页面的样式,实现了登陆注册时向后端发送请求获取数据的功能,最后通过代码拆分增加逻辑可维护性。下一篇文章我们会实现商家展示功能的开发,大家记得关注哦!
项目代码地址:
https://gitee.com/jie_shao1112/jingdong-homehttps://gitee.com/jie_shao1112/jingdong-home
检查按钮事件绑定是否正确:确保事件绑定的元素和按钮选择器匹配,事件处理函数没有出现语法错误。可以在控制台中打印出事件绑定的元素,检查选择器是否匹配。
检查页面元素是否出现变化:如果你在页面中动态添加或删除了元素,那么选择器可能需要相应的修改,否则事件绑定将会失败。你可以检查一下DOM结构,确认选择器是否仍然有效。
对于页面抖动的问题,可能是由于页面自动滚动的时候,页面高度发生变化,导致滚动条出现或消失,进而导致页面抖动。你可以尝试以下几个方面来解决这个问题:
确保页面高度不会发生变化:在页面自动滚动的时候,确保页面中的元素不会发生高度变化。例如,如果你在滚动过程中动态加载了图片,可以在图片加载前占位,避免页面高度的变化。
使用固定高度的容器:如果你使用了自定义滚动功能,可以使用一个固定高度的容器来实现滚动。这样可以避免滚动条的出现或消失,进而避免页面抖动。
使用CSS动画:如果你使用了自定义滚动功能,可以考虑使用CSS动画来实现页面的滚动,这样可以避免使用滚动条,进而避免页面抖动。
问题的出现可能是由于滚动和暂停按钮的事件绑定存在问题,或者滚动的逻辑实现不够完善。建议首先检查滚动和暂停按钮的事件绑定是否正确,可以尝试使用Vue提供的@click事件来替换原有的@click.native事件。另外,可以尝试优化滚动逻辑,例如使用requestAnimationFrame来代替setInterval实现更平滑的滚动效果,并添加一些滚动结束时的判断,避免页面抖动等问题的出现。以下是代码示例:
<template>
<div class="home" ref="scrollContainer">
...
<el-button type="primary" class="floatBtn" @click="handleScroll">{{ isPaused ? '开始滚动' : '暂停滚动' }}</el-button>
</div>
</template>
<script>
import { onMounted, ref, onUnmounted, computed } from "vue";
import { ExecJSON2 } from "@/api/mainPage";
export default {
name: "mainPage",
setup() {
const tableData = ref([]);
const isPaused = ref(false);
const scrollContainer = ref(null);
let animationId;
const filteredList = computed(() => {
if (!tableData.value) {
return [];
}
return tableData.value.filter((item) => item.list.length > 1);
});
onMounted(() => {
getAllData();
onScroll();
animationId = requestAnimationFrame(animate);
});
onUnmounted(() => {
cancelAnimationFrame(animationId);
});
const getAllData = () => {
...
};
const animate = () => {
if (!isPaused.value) {
scrollContainer.value.scrollTop += 1;
}
animationId = requestAnimationFrame(animate);
};
const handleScroll = () => {
isPaused.value = !isPaused.value;
};
return {
getAllData,
tableData,
filteredList,
isPaused,
scrollContainer,
handleScroll,
};
},
};
</script>
在这个示例中,使用了requestAnimationFrame替换了setInterval来实现滚动效果,并且添加了一个animate函数来控制滚动的行为。另外,将滚动容器的引用绑定到了scrollContainer变量上,方便后续的操作。在handleScroll函数中,通过改变isPaused的值来控制滚动的暂停和继续。
引用chatGPT作答,根据提供的代码和问题描述,可能有以下问题:
1.暂停/滚动按钮的失效 - 代码中的 handleOnscroll 方法没有提供,无法确定问题所在。但可能的原因包括方法未正确定义、按钮状态没有正确更新、按钮与滚动事件之间的同步问题等。建议检查 handleOnscroll 方法的定义并确保其正确更新按钮状态。
2.页面抖动 - 这可能是因为滚动事件触发了重新渲染组件,导致页面抖动。为了解决这个问题,可以使用 requestAnimationFrame 方法替代 setInterval 实现滚动,并使用 Vue 的异步更新机制来避免频繁的组件重新渲染。可以在 onMounted 生命周期钩子中定义一个滚动函数,在函数内使用 requestAnimationFrame 方法实现页面滚动,并使用 Vue.nextTick() 方法在下一次更新时异步更新组件。
3.页面滚动的同步问题 - 可能会出现滚动事件与数据更新之间的同步问题。当滚动事件触发时,页面应该滚动到相应的位置,以显示当前数据。因此,建议在 onMounted 生命周期钩子中为滚动容器添加一个 ref 引用,并在 onScroll 方法中使用该引用来确定当前滚动位置,并根据该位置更新数据。