换一换
实现百度换一换和自定义分页的功能,下面包含了完整的代码演示教程
在本教程中,我们将使用 Vue 3 和 Composition API 实现一个“换一换”和自定义分页的功能。用户可以通过点击按钮来显示一组新的数据。我们将使用 <transition>
元素和动画效果,让用户在获取新数据时能够看到转圈动画,增强用户体验。🚀
创建组件 🛠️
在 src/views
目录下,创建一个个组件对应的 .vue
文件。📁
我们可以使用以下步骤来实现自定义分页的功能:
page
属性,表示当前页码。📄getNewList
函数中,根据页码和每页显示数量,将原数组分割并计算新数组。🔄getNewList
函数获取新的数据。👇示例代码:
首先,我们需要创建自定义分页的 HTML 结构。对于本文中实现自定义分页功能的 Vue 3 项目,我们需要展示以下内容:
html<template>
<div class="container">
<div class="change" @click="changePage">
<div class="change-btn">换一换</div>
</div>
<div class="list">
<div class="item" v-for="item in newArr" :key="item.id">
<span class="title-content-index"> {{ item.id }}</span>
<div class="title-content-title">{{ item.value }}</div>
</div>
</div>
</div>
</template>
在自定义分页组件中,我们需要实现以下功能:
下面是 JavaScript 代码的示例:👇
jsx<script>
import { reactive, toRefs, onMounted, computed } from "vue";
export default {
setup() {
// 计算总页数
const totalPage = computed(() => Math.ceil(status.arr.length / PAGE_SIZE));
// 定义响应式状态和方法
const status = reactive({
arr: [
{ id: 1, value: "今日头条" },
{ id: 2, value: "百度搜索" },
{ id: 3, value: "知乎" },
{ id: 4, value: "淘宝" },
{ id: 5, value: "京东" },
{ id: 6, value: "微信" },
{ id: 7, value: "抖音" },
{ id: 8, value: "快手" },
{ id: 9, value: "网易云音乐" },
{ id: 10, value: "哔哩哔哩" },
{ id: 11, value: "饿了么" },
{ id: 12, value: "美团外卖" },
{ id: 13, value: "斗地主" },
{ id: 14, value: "QQ音乐" },
],
newArr: [],
page: 1, // 添加分页属性
totalPage,
});
const PAGE_SIZE = 6; // 每页显示的数量
const generatePages = (page = 1) => {
// 根据分页属性,切分数组
let startIndex = (page - 1) * PAGE_SIZE; // 计算起始索引
let endIndex = page * PAGE_SIZE; // 计算结束索引
status.page = page; // 更新分页属性
let currentArr = status.arr.slice(startIndex, endIndex); // 切分原始数组
status.newArr = currentArr; // 更新新数组
};
onMounted(() => {
// 初始化时生成第一页
generatePages();
});
const changePage = () => {
if (status.page + 1 > status.totalPage) {
generatePages(1);
} else {
generatePages(status.page + 1);
}
status.isRotate = true;
};
return {
...toRefs(status), // 使用 toRefs 将状态对象转为响应式对象
changePage,
};
},
};
</script>
这段代码中的主要注意点如下:
setup
函数返回响应式状态和方法。👍reactive
包装原始数据,并使用 toRefs
将响应式状态转为普通对象,以便在模板中访问。🔍computed
计算总页数,将其作为状态存储,并在模板中展示。🔢PAGE_SIZE
常量表示每页显示的数量。📏generatePages
函数,根据分页属性切分数组,并将切分后的新数组存储到状态对象中。在第一次渲染组件时生成第一页数据。🔨changePage
函数中根据当前页数和总页数更新分页数据,并使用 isRotate
状态来展示动画效果。如果当前页是最后一页,则下一页为第一页,否则下一页为当前页加 1。💫jsx<style lang="scss" scoped>
.container {
width: 420px;
margin: 0 auto;
padding: 20px;
background: #fff;
border-radius: 4px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
.list {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
width: 420px;
}
.item {
width: 200px;
margin-bottom: 10px;
display: flex;
text-align: left;
}
.item .title-content-index {
margin-right: 4px;
width: 22px;
font-family: Arial, sans-serif;
font-size: 18px;
line-height: 18px;
position: relative;
top: 1px;
}
.item .title-content-title {
font-family: Arial, sans-serif;
font-size: 16px;
line-height: 18px;
color: #626675;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 160px;
}
.change {
height: 18px;
display: flex;
align-items: center;
margin-bottom: 10px;
}
.change-btn {
font-size: 14px;
color: #626675;
margin-left: 2px;
font-weight: bold;
cursor: pointer;
}
.change:hover .change-btn {
color: #315efb;
}
</style>
首先,我们需要创建换一换的 HTML 结构。对于本文中实现自定义分页功能的 Vue 3 项目,我们需要展示以下内容:
下面是 HTML 结构的示例代码:👇
html<template>
<div class="container">
<div
class="change"
@mouseover="isHovered = true"
@mouseout="isHovered = false"
@click="changePage"
>
<img
:src="
isHovered
? 'https://image.myxuechao.com/blog/refresh-icon-color.png'
: 'https://image.myxuechao.com/blog/refresh-icon.png'
"
class="refresh-icon"
:class="{ 'is-rotating': isRotate }"
alt=""
@transitionend="onRotateEnd"
/>
<div class="change-btn">换一换</div>
</div>
<div class="list">
<div class="item" v-for="item in newArr" :key="item.id">
<span class="title-content-index"> {{ item.id }}</span>
<div class="title-content-title">{{ item.value }}</div>
</div>
</div>
</div>
</template>
在自定义分页组件中,我们需要实现以下功能:
换一换的实现只需要修改 getNewList
这个函数,下面是 JavaScript 代码的示例:👇
换一换的实现只需要修改 getNewList
这个函数就可以了。下面是 JavaScript 代码的示例:👇
jsxconst getNewList = (page = 1) => {
// 根据分页属性,切分数组
let startIndex = (page - 1) * PAGE_SIZE; // 计算起始索引
let endIndex = page * PAGE_SIZE; // 计算结束索引
status.page = page; // 更新分页属性
let currentArr = status.arr.slice(startIndex, endIndex); // 切分原始数组
let midIndex = Math.ceil(currentArr.length / 2); // 获取数组 currentArr 的中间元素下标,向上取整
let newArr = [];
// 组合新的数组
for (let i = 0; i < midIndex; i++) {
if (currentArr[i]) {
// 通过 if 语句过滤掉 undefined 和 null
newArr.push(currentArr[i]); // 将位于中间下标左侧的元素添加到新数组中
}
if (currentArr[midIndex + i]) {
// 组合右侧的元素
newArr.push(currentArr[midIndex + i]);
}
}
status.newArr = newArr; // 更新新数组
};
**点击“换一换”按钮时,会触发旋转动画效果。**💫
jsx<script>
import { reactive, toRefs, onMounted,computed} from "vue";
export default {
setup() {
// 计算总页数
const totalPage = computed(() => Math.ceil(status.arr.length / PAGE_SIZE));
const status = reactive({
arr: [
/*...*/
],
newArr: [],
page: 1, // 添加分页属性
isHovered: false,
isRotate: false,
totalPage,
});
const PAGE_SIZE = 6; // 每页显示的数量
const getNewList = (page = 1) => {
/*省略代码。上面有*/
};
onMounted(() => {
getNewList();
});
const changePage = () => {
if (status.page + 1 > status.totalPage) {
getNewList(1);
} else {
getNewList(status.page + 1);
}
status.isRotate = true;
};
const onRotateEnd = () => {
console.log("动画结束!");
status.isRotate = false;
};
const handleMouseover = () => {
// 鼠标滑过事件触发时的处理
console.log("Mouse over");
status.isHovered = true;
};
const handleMouseout = () => {
// 鼠标离开事件触发时的处理
console.log("Mouse out");
status.isHovered = false;
};
return {
...toRefs(status), // 使用 toRefs 将状态对象转为响应式对象
changePage,
handleMouseover,
handleMouseout,
onRotateEnd,
};
},
};
</script>
css<style lang="scss" scoped>
.refresh-icon {
width: 22px;
height: 22px;
}
.is-rotating {
transition: transform 0.3s;
transform: rotate(360deg);
}
</style>
这段代码中的主要注意点如下:
getNewList
函数接收一个 page
参数,表示当前的分页属性,如果没有传入,则默认为 1。startIndex
和结束索引 endIndex
,用来切割原始数组。page
值更新到状态对象 status
中的 page
属性中。slice
方法对原始数组进行切割,得到 currentArr
数组。currentArr
数组的中间元素下标 midIndex
,并向上取整。currentArr
数组的前面一半元素,将存在的元素依次添加到 newArr
数组的末尾。currentArr
数组的后面一半元素,将存在的元素依次添加到 newArr
数组的末尾。newArr
更新到状态对象 status
中的 newArr
属性中。setup
函数中返回响应式状态和方法。🚀totalPage
响应式状态,用于计算分页总数,随着 status.arr
数组长度的变化而实时更新。🔢reactive
函数进行响应式处理,并使用 toRefs
方法将响应式状态转换为标准对象,方便在模板中直接引用。👀handleMouseover
响应式方法,在用户鼠标滑过“换一换”按钮时,将 isHovered
状态设置为 true
,从而实现鼠标滑过效果。👆handleMouseout
响应式方法,在用户鼠标离开“换一换”按钮时,将 isHovered
状态设置为 false
,从而实现鼠标离开效果。👋onRotateEnd
响应式方法,在“换一换”按钮转圈动画结束时触发,输出一条 console 日志,并将 isRotate
状态设置为 false
,从而实现动画结束后的处理。💫jsx<template>
<div class="container">
<div
class="change"
@mouseover="isHovered = true"
@mouseout="isHovered = false"
@click="changePage"
>
<img
:src="
isHovered
? 'https://image.myxuechao.com/blog/refresh-icon-color.png'
: 'https://image.myxuechao.com/blog/refresh-icon.png'
"
class="refresh-icon"
:class="{ 'is-rotating': isRotate }"
alt=""
@transitionend="onRotateEnd"
/>
<div class="change-btn">换一换</div>
</div>
<div class="list">
<div class="item" v-for="item in newArr" :key="item.id">
<span class="title-content-index"> {{ item.id }}</span>
<div class="title-content-title">{{ item.value }}</div>
</div>
</div>
</div>
</template>
jsx<script>
import { reactive, toRefs, onMounted, computed } from "vue";
export default {
setup() {
// 计算总页数
const totalPage = computed(() => Math.ceil(status.arr.length / PAGE_SIZE));
const status = reactive({
arr: [
{ id: 1, value: "今日头条" },
{ id: 2, value: "百度搜索" },
{ id: 3, value: "知乎" },
{ id: 4, value: "淘宝" },
{ id: 5, value: "京东" },
{ id: 6, value: "微信" },
{ id: 7, value: "抖音" },
{ id: 8, value: "快手" },
{ id: 9, value: "网易云音乐" },
{ id: 10, value: "哔哩哔哩" },
{ id: 11, value: "饿了么" },
{ id: 12, value: "美团外卖" },
{ id: 13, value: "斗地主" },
{ id: 14, value: "QQ音乐" },
],
newArr: [],
page: 1, // 添加分页属性
isHovered: false,
isRotate: false,
totalPage,
});
const PAGE_SIZE = 6; // 每页显示的数量
const getNewList = (page = 1) => {
// 根据分页属性,切分数组
let startIndex = (page - 1) * PAGE_SIZE; // 计算起始索引
let endIndex = page * PAGE_SIZE; // 计算结束索引
status.page = page; // 更新分页属性
let currentArr = status.arr.slice(startIndex, endIndex); // 切分原始数组
let midIndex = Math.ceil(currentArr.length / 2); // 获取数组 currentArr 的中间元素下标,向上取整
let newArr = [];
// 组合新的数组
for (let i = 0; i < midIndex; i++) {
if (currentArr[i]) {
// 通过 if 语句过滤掉 undefined 和 null
newArr.push(currentArr[i]); // 将位于中间下标左侧的元素添加到新数组中
}
if (currentArr[midIndex + i]) {
// 组合右侧的元素
newArr.push(currentArr[midIndex + i]);
}
}
status.newArr = newArr; // 更新新数组
};
onMounted(() => {
getNewList();
});
const changePage = () => {
if (status.page + 1 > status.totalPage) {
getNewList(1);
} else {
getNewList(status.page + 1);
}
status.isRotate = true;
};
const onRotateEnd = () => {
console.log("动画结束!");
status.isRotate = false;
};
const handleMouseover = () => {
// 鼠标滑过事件触发时的处理
console.log("Mouse over");
status.isHovered = true;
};
const handleMouseout = () => {
// 鼠标离开事件触发时的处理
console.log("Mouse out");
status.isHovered = false;
};
return {
...toRefs(status), // 使用 toRefs 将状态对象转为响应式对象
changePage,
handleMouseover,
handleMouseout,
onRotateEnd,
};
},
};
</script>
jsx<style lang="scss" scoped>
.container {
width: 420px;
margin: 0 auto;
padding: 20px;
background: #fff;
border-radius: 4px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
.list {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
width: 420px;
}
.item {
width: 200px;
margin-bottom: 10px;
display: flex;
text-align: left;
}
.item .title-content-index {
margin-right: 4px;
width: 22px;
font-family: Arial, sans-serif;
font-size: 18px;
line-height: 18px;
position: relative;
top: 1px;
}
.item .title-content-title {
font-family: Arial, sans-serif;
font-size: 16px;
line-height: 18px;
color: #626675;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 160px;
}
.change {
height: 18px;
display: flex;
align-items: center;
margin-bottom: 10px;
}
.change-btn {
font-size: 14px;
color: #626675;
margin-left: 2px;
font-weight: bold;
cursor: pointer;
}
.change:hover .change-btn {
color: #315efb;
}
.refresh-icon {
width: 22px;
height: 22px;
}
.is-rotating {
transition: transform 0.3s;
transform: rotate(360deg);
}
</style>
本文作者:LiuXueChao
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!