2023-04-17
Vue3.0
00

目录

目标特性 🎯
步骤一:实现自定义分页 📖
HTML 🌐
JS
CSS
步骤二:实现换一换功能🔄
HTML 🌐
JS
CSS
完整代码示例
HTML部分
以下是 JavaScript 代码示例:
CSS部分

换一换

实现百度换一换和自定义分页的功能,下面包含了完整的代码演示教程

在本教程中,我们将使用 Vue 3 和 Composition API 实现一个“换一换”和自定义分页的功能。用户可以通过点击按钮来显示一组新的数据。我们将使用 <transition> 元素和动画效果,让用户在获取新数据时能够看到转圈动画,增强用户体验。🚀

https://image.myxuechao.com/blog/refresh-gif.gif

目标特性 🎯

  • 点击按钮“换一换”时,每次都会展示一组新数据。
  • 点击“换一换”按钮时,会触发转圈动画效果。💫
  • 鼠标悬停在“换一换”按钮上时,按钮图标颜色会变化,用户能直接看到按钮的交互状态。👀

创建组件 🛠️

在 src/views 目录下,创建一个个组件对应的 .vue 文件。📁

步骤一:实现自定义分页 📖

我们可以使用以下步骤来实现自定义分页的功能:

  1. 在状态对象中添加一个 page 属性,表示当前页码。📄
  2. 在 getNewList 函数中,根据页码和每页显示数量,将原数组分割并计算新数组。🔄
  3. 在页面渲染时,根据新数组中的数据渲染页面。🎨
  4. 在按钮的点击事件中,根据当前页码和总页数,计算出下一页的页码,并调用 getNewList 函数获取新的数据。👇

示例代码:

HTML 🌐

首先,我们需要创建自定义分页的 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>

JS

在自定义分页组件中,我们需要实现以下功能:

  1. 获取原始数据。
  2. 指定分页长度和当前页码,并计算出总页数。
  3. 根据当前页数和分页数将数据分割。
  4. 定义响应式状态和方法,以供切换分页中使用。

下面是 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>

这段代码中的主要注意点如下:

  1. 使用了 Vue 3 的 Composition API,通过 setup 函数返回响应式状态和方法。👍
  2. 使用了 reactive 包装原始数据,并使用 toRefs 将响应式状态转为普通对象,以便在模板中访问。🔍
  3. 使用 computed 计算总页数,将其作为状态存储,并在模板中展示。🔢
  4. 定义 PAGE_SIZE 常量表示每页显示的数量。📏
  5. 定义 generatePages 函数,根据分页属性切分数组,并将切分后的新数组存储到状态对象中。在第一次渲染组件时生成第一页数据。🔨
  6. 在 changePage 函数中根据当前页数和总页数更新分页数据,并使用 isRotate 状态来展示动画效果。如果当前页是最后一页,则下一页为第一页,否则下一页为当前页加 1。💫

CSS

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 🌐

首先,我们需要创建换一换的 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>

JS

在自定义分页组件中,我们需要实现以下功能:

  1. 已经在“步骤一”中实现了分页。
  2. 基于当前分页的数据进行数据分割和重新排序。
  3. 点击“换一换”按钮时,会触发旋转动画效果。
  4. 鼠标悬停在“换一换”按钮上时,按钮图标的颜色会变化,以便用户直接看到交互状态。

换一换的实现只需要修改 getNewList 这个函数,下面是 JavaScript 代码的示例:👇

换一换的实现只需要修改 getNewList 这个函数就可以了。下面是 JavaScript 代码的示例:👇

jsx
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; // 更新新数组 };

**点击“换一换”按钮时,会触发旋转动画效果。**💫

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

css
<style lang="scss" scoped> .refresh-icon { width: 22px; height: 22px; } .is-rotating { transition: transform 0.3s; transform: rotate(360deg); } </style>

这段代码中的主要注意点如下:

  1. getNewList 函数接收一个 page 参数,表示当前的分页属性,如果没有传入,则默认为 1。
  2. 计算起始索引 startIndex 和结束索引 endIndex,用来切割原始数组。
  3. 将计算好的 page 值更新到状态对象 status 中的 page 属性中。
  4. 使用 slice 方法对原始数组进行切割,得到 currentArr 数组。
  5. 计算 currentArr 数组的中间元素下标 midIndex,并向上取整。
  6. 遍历 currentArr 数组的前面一半元素,将存在的元素依次添加到 newArr 数组的末尾。
  7. 遍历 currentArr 数组的后面一半元素,将存在的元素依次添加到 newArr 数组的末尾。
  8. 将组合好的新数组 newArr 更新到状态对象 status 中的 newArr 属性中。
  9. 使用了 Vue 3 的 Composition API,在 setup 函数中返回响应式状态和方法。🚀
  10. 定义了一个 totalPage 响应式状态,用于计算分页总数,随着 status.arr 数组长度的变化而实时更新。🔢
  11. 所有状态对象均采用了 reactive 函数进行响应式处理,并使用 toRefs 方法将响应式状态转换为标准对象,方便在模板中直接引用。👀
  12. 在触发分页更新时,根据分页属性和每页显示的数量,对原始数组进行切割和组合,得到新的数组并更新状态。🤖
  13. 定义了一个 handleMouseover 响应式方法,在用户鼠标滑过“换一换”按钮时,将 isHovered 状态设置为 true,从而实现鼠标滑过效果。👆
  14. 定义了一个 handleMouseout 响应式方法,在用户鼠标离开“换一换”按钮时,将 isHovered 状态设置为 false,从而实现鼠标离开效果。👋
  15. 定义了一个 onRotateEnd 响应式方法,在“换一换”按钮转圈动画结束时触发,输出一条 console 日志,并将 isRotate 状态设置为 false,从而实现动画结束后的处理。💫

完整代码示例

HTML部分

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>

以下是 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, // 添加分页属性 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>

CSS部分

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>
如果对你有用的话,可以打赏哦
打赏
ali pay
wechat pay

本文作者:LiuXueChao

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!