• React.js学习(二):案例源码学习“排序动画”


    又有时间了,继续推进React的第二天。自己学习React往往会忽视许多细节,于是找来别人的项目学习学习。今天学习一个小案例,“排序动画”。

    案例的核心是“状态管理”

    一、模块划分

    阅读App.jsx文件,可以看到这个可视化的排序页面主要分为4个部分

    • NavBar 导航栏:主要功能选择排序算法
    • Controller 控制栏:生成无序数组和控制排序动画的速度、暂停和播放
    • AlgoDisplay 排序算法演示板:根据所选择的算法,演示排序的动画
    • Footer 页脚:一些补充信息,这里是作者Sadanand的署名和链接
    1. export default function App() {
    2. return (
    3. <Container>
    4. <NavBar />
    5. <Controller />
    6. <AlgoDisplay />
    7. <Footer />
    8. </Container>
    9. );
    10. }

     二、逻辑分析

    分析一下如果自己去实现,需要完成哪些信息处理逻辑。

    这里,四个核心模块都是同一级的兄弟元素,因此第一个需要考虑的是:

    • 1. 如何条理清晰的进行模块间信息传递?

    关于每一个模块:

    • 2. NavBar 需要做的处理比较简单,只需要把用户点击的算法对应的名称存入状态
    • 3. Controller的生成数组比较容易,控制动画的速度也可以通过调整动画的帧率(每一帧间隔时长)来完成,最后就是动画的暂停和播放,需要状态来切换 
    • 4. Footer 没有太多的责任,当作一个Label来完成即可

    上面的4条,其实正在关键的是第一条,因为随着项目规模的增大,麻烦的不是实现一个模块,而是井井有序的控制每个模块之间的关系。

    三、 实现细节

    1. 配置基本动画变量config.js

    1. import { getScreenWidth } from "./helper";
    2. import { BubbleSort } from "../sortFunctions/BubbleSort";
    3. import { SelectionSort } from "../sortFunctions/SelectionSort";
    4. import { InsertionSort } from "../sortFunctions/InsertionSort";
    5. import { QuickSort } from "../sortFunctions/QuickSort";
    6. import { HeapSort } from "../sortFunctions/HeapSort.js";
    7. import { MergeSort } from "../sortFunctions/MergeSort";
    8. // 演示动画中,数字不同状态时的颜色
    9. export const comparisionColor = "pink";
    10. export const swapColor = "yellow";
    11. export const sortedColor = "springgreen";
    12. export const pivotColor = "sandybrown";
    13. // 演示动画的帧数
    14. export let swapTime = 1000; // 3秒内的帧数
    15. export let compareTime = 500; // 1.5秒内的帧数
    16. // 根据屏幕宽度,设定初始的数组
    17. export let sortingArray = initArrayForScreenSize();
    18. // 所有算法信息
    19. export const sortingAlgorithms = [
    20. { component: BubbleSort, title: "Bubble", name: "BubbleSort" },
    21. { component: SelectionSort, title: "Selection", name: "SelectionSort" },
    22. { component: InsertionSort, title: "Insertion", name: "InsertionSort" },
    23. { component: HeapSort, title: "Heap", name: "HeapSort" },
    24. { component: MergeSort, title: "Merge", name: "MergeSort" },
    25. { component: QuickSort, title: "Quick", name: "QuickSort" },
    26. ];
    27. function initArrayForScreenSize() {
    28. const screenSize = getScreenWidth();
    29. if (screenSize < 460) return [4, 3, 2, 1];
    30. else if (screenSize < 720) return [8, 7, 6, 5, 4, 3, 2, 1];
    31. return [12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1];
    32. }

    2. 辅助函数

    1. // 将输入框中的字符串转换为数组字符串,意思就是当往输入框中输入非数字逗号的时候,输入的字符会被清除掉
    2. // 比如 输入 1,2,3,4 正常,当后续输入一个a的时候,输入框不会显示a
    3. export function convertInputToArrayString(string) {
    4. string = string.replaceAll(/\s/g, "");
    5. string = string.replaceAll(/\d{4}/g, "");
    6. string = string.replaceAll(/\s\s/g, " ");
    7. string = string.replaceAll(/\s,/g, ",");
    8. string = string.replaceAll(/,,/g, ",");
    9. string = string.replaceAll(/[^0-9,\s]/g, "");
    10. return string;
    11. }
    12. // 将数组字符串转变为数组
    13. export function convertArrayStringToArray(string) {
    14. return string
    15. .split(",")
    16. .filter((v) => v !== "")
    17. .map((v) => +v);
    18. }
    19. // 生成随机数组
    20. export function getRandomArray(length = generateRandomNumberInRange(5, 30)) {
    21. return Array.from(new Array(length), () => generateRandomNumberInRange());
    22. }
    23. // 获得屏幕(当前浏览器页面)宽度
    24. export function getScreenWidth(){
    25. return window.innerWidth;
    26. }
    27. // 设定延迟的函数
    28. export function delay(time) {
    29. return new Promise((resolve) => setTimeout(resolve, time));
    30. }
    31. // 生成指定范围内的数字, 范围是(lowerLimit, lowerLimit+upperLimit)
    32. function generateRandomNumberInRange(lowerLimit = 0, upperLimit = 999) {
    33. return lowerLimit + Math.floor(Math.random() * upperLimit);
    34. }

    3. zustand状态管理

     create方法会创建一个状态管理器,每次使用这个管理器,需要传入一个自定义的方法来间接控制状态,当管理器实现完后,每次传入的方法应该是很简单的方法。一般是调用状态对象的属性,属性包含了状态值或者setState方法。

    1. import create from "zustand";
    2. import { devtools } from "zustand/middleware";
    3. import {
    4. sortingArray,
    5. compareTime,
    6. swapTime,
    7. sortingAlgorithms,
    8. } from "./config";
    9. // 创建了一个全局状态管理器,其内包含各个状态以及对应的seState方法
    10. export const useControls = create(
    11. devtools((set) => ({
    12. progress: "reset", // 动画的重置、播放、暂停状态
    13. speed: 3, // 动画的速度
    14. compareTime: compareTime, // 动画数字比较一次所花费的时间
    15. swapTime: swapTime, // 交换动画一次所花费的时间
    16. doneCount: 0, // 当前执行完成的动画个数(当需要同时演示所有动画的时候)
    17. // 开始排序,则设定progress为start,以此类推
    18. startSorting: () => set({ progress: "start" }),
    19. pauseSorting: () => set({ progress: "pause" }),
    20. resetSorting: () => set({ progress: "reset", doneCount: 0 }),
    21. // 标记动画已经完成:
    22. // 如果当前选择的是最后一个,即(ALL选项)同时演示所有算法
    23. // --如果已经完成所有算法, 设置完成算法数量以及progress为done
    24. // --否则,将doenCount加一,表示当前完成了一个算法
    25. // 否则直接将doneCount加一,因为非ALL选项,都只有一个算法,完成即表示所有完成
    26. markSortngDone: () =>
    27. set((state) => {
    28. if (useData.getState().algorithm === sortingAlgorithms.length) {
    29. if (state.doneCount === sortingAlgorithms.length - 1)
    30. return { doneCount: 0, progress: "done" };
    31. else return { doneCount: state.doneCount + 1 };
    32. } else return { progress: "done" };
    33. }),
    34. setSpeed: (speed) =>
    35. set(() => {
    36. return { swapTime: 3000 / speed, compareTime: 1500 / speed, speed };
    37. }),
    38. }))
    39. );
    40. // 设置当前正在执行的算法的状态管理器,包含两个状态:算法的id和算法排序中途的数组
    41. export const useData = create(
    42. devtools((set) => ({
    43. // algorithm 表示算法id,如果实现了6种排序算法,
    44. // 则每一个算法id依次为012345 另外还有一个id=6 表示同时演示所有算法
    45. algorithm: 0,
    46. // sortingArray这个就是排序数组,表示当前时刻,此算法对数组排序的结果
    47. sortingArray: sortingArray,
    48. // 设置新的数组,可能是经过上一步的排序,得到的新数组
    49. setSortingArray: (array) => set({ sortingArray: array }),
    50. // 设置算法的id
    51. setAlgorithm: (idx) => set({ algorithm: idx }),
    52. }))
    53. );

    4.总结

     这里状态管理的代码就阅读完了,同时整个项目的实现也大致可以猜到了,就是借助全局的状态管理器,来完成兄弟元素间的信息传递,所有的模块只需要跟useData和useControls来交流即可。

  • 相关阅读:
    企业微信接入芋道SpringBoot项目
    JAVA计算机毕业设计电子病历系统Mybatis+系统+数据库+调试部署
    【数据治理】揭开主数据管理的陷阱
    李航《统计学习方法》笔记之感知机perceptron
    Websphere各版本历史
    http协议详解(一)
    批量上传文件,以input上传文件,后端以List<MultipartFile>类型接收
    【实验记录1】行人重识别
    Andorid获取原生GPS定位信息
    ECharts 饼图颜色设置教程 - 4 种方式设置饼图颜色
  • 原文地址:https://blog.csdn.net/qq_42418728/article/details/127044097