echarts饼图下钻-导航指定层级

前言

-echarts实现一个饼图下钻的图表,带一个层级导航点击层级到指定的内容

使用到这些工具/框架库

  • vue3 https://cn.vuejs.org
  • vite https://vitejs.cn/vite3-cn/guide/
  • echarts https://echarts.apache.org/zh/option.html#series-pie
  • ant design vue 4.x https://www.antdv.com/components/breadcrumb-cn

演示视频

饼图下钻

jvideo

实际用呢,用来跳转页面的,后面介绍跳过跳转页面;

有兴趣可查看demo仓库 gitee; 用到了vite插件vite-plugin-md.md文件作为页面

一、准备

1.vite创建 vue3项目

  1. 打开cmd运行 npm create vite@latest 回车
  2. 键盘按下 y,输入项目名称echarts-demo
  3. 上下箭头选择Vue回车
  4. 上下箭头选择TypeScript回车
  5. 接着cd echarts-demo 之后运行npm install下载依赖
    在这里插入图片描述

2.安装echarts,ant-design-vue

npm install echarts ant-design-vue

3.挂载ant-design-vue

用编辑器打开该项目 在src/main.ts中加入以下代码

import { createApp } from 'vue';
import Antd from 'ant-design-vue';
import App from './App';
import 'ant-design-vue/dist/reset.css';
const app = createApp(App);
app.use(Antd).mount('#app');

二、页面的布局

在这里插入图片描述

  1. 顶部有层级导航面包屑 \ 饼图/矩形树图切换按钮
  2. 下方就是echarts容器
<template>
    <div class="home">
        <div class="home_path">
            <div class="home_path_box">
                <a-breadcrumb>
                    <a-breadcrumb-item v-for="item, key in pathLog">
                        <span class="home_path_box_item" @click="handleClickPathItem(item, key)">
                            {{ item.path }}
                        </span>
                    </a-breadcrumb-item>
                </a-breadcrumb>
            </div>
            <div class="home_switch">
                <div class="home_switch_box" @click="handleClickSwitch">
                    <div class="home_switch_box_icon" :class="{ home_switch_box_iconShow: chartType == 'pie' }">
                        <img src="./images/pie.png">
                    </div>
                    <div class="home_switch_box_icon" :class="{ home_switch_box_iconShow: chartType == 'treemap' }">
                        <img src="./images/areaTree.png">
                    </div>
                </div>
            </div>
        </div>
        <div class="chart" ref="chart"></div>
    </div>
</template>

二、布局所需的变量,事件

  1. 层级导航通过变量pathLog渲染
    1. 层级导航点击事件,通过下标 截取记录变量pathLog以更新记录
    2. 首先获取最后一个记录,用来判断当前点击和最后一项是否相等如果是则不再截取
    3. 判断是否为 root 这里表示根层级不在截取同时将记录变量pathLog长度设置为1
    4. 最后截取当前点击的下标向后的所有元素,以更新记录
  2. chartType用来表示图表类型是饼图\矩形树图
    1. 通过点击切换按钮更新该值
<script lang='ts' setup>
import { onMounted, ref, onActivated, onDeactivated } from 'vue';
let pathLog = ref<Record<String,any>[]>([]);
const handleClickPathItem = (item: any, key: number) => {
    let last = pathLog.value[pathLog.value.length - 1];
    if (item.path === 'root') {
        pathLog.value.length = 1;
    };
    if (pathLog.value.length > 1 && item.computedDataLabel_k == last.computedDataLabel_k) return;
    pathLog.value.splice(key + 1, Infinity);
}

let chartType = ref<string>('pie');
const handleClickSwitch = () => {
    chartType.value = chartType.value === 'pie' ? 'treemap' : 'pie';
}
</script>

三、准备树结构变量 chartData

有点小多压缩了一下结构就是:

let data = {
    name: 'CSS艺术之美',
    routerKey: 'CSS',
    children: []
}
const _Study:any=[{name:'CSS艺术之美',routerKey:'CSS',children:[{name:'基础篇',routerKey:'基础',children:[{name:'剧中',routerKey:'剧中',},{name:'单位',routerKey:'单位',},{name:'颜色',routerKey:'颜色',},{name:'变量',routerKey:'变量',},{name:'选择器',routerKey:'选择器',},{name:'Sass',routerKey:'SASS',},{name:'Flex布局',routerKey:'Flex',},{name:'Grid布局',routerKey:'Grid',},{name:'数学函数',routerKey:'数学函数',},{name:'Flex vs Grid',routerKey:'FlexVsGrid',}]},{name:'进阶篇',routerKey:'进阶',children:[{name:'框架',routerKey:'框架',},{name:'技巧',routerKey:'技巧',},{name:'优化',routerKey:'优化',},{name:'方法论',routerKey:'方法论',},{name:'容器查询',routerKey:'容器查询',},{name:'网页置灰',routerKey:'网页置灰',},{name:'样式重置',routerKey:'样式重置',},{name:'文本换行',routerKey:'文本换行',},{name:'隐藏元素',routerKey:'隐藏元素',},{name:'溢出问题',routerKey:'溢出问题',},{name:'父选择器',routerKey:'父选择器',},{name:'高级选择器',routerKey:'高级选择器',},{name:'选择器性能',routerKey:'选择器性能',},{name:'背景图vs<img>',routerKey:'背景图vsImg',},{name:'Tailwind css',routerKey:'TailwindCSS',},]},{name:'效果篇',routerKey:'效果',children:[{name:'实现三角形',routerKey:'三角形',},{name:'实现九宫格',routerKey:'九宫格',},{name:'实现3D骰子',routerKey:'3D骰子',},]},{name:'最新动态',routerKey:'最新',children:[{name:'可扩展Css的演变',routerKey:'CSS演变',},{name:'新特性',routerKey:'新特性',},]}]},{name:'JavaScript',routerKey:'JavaScript',children:[{name:'现代深拷贝',routerKey:'现代深拷贝',},{name:'基础篇',routerKey:'基础',children:[{name:'数组',routerKey:'数组',},{name:'JSON',routerKey:'JSON',},{name:'字符串',routerKey:'字符串'},{name:'操作符',routerKey:'操作符'},{name:'Infinity',routerKey:'Infinity'},{name:'数据类型',routerKey:'数据类型'},{name:'类型转换',routerKey:'类型转换'},{name:'循环遍历',routerKey:'循环遍历'},{name:'引号区别',routerKey:'引号区别'},{name:'错误类型',routerKey:'错误类型'},{name:'深浅拷贝',routerKey:'深浅拷贝'},{name:'结构赋值',routerKey:'结构赋值'},{name:'变量提升',routerKey:'变量提升'},{name:'对象不变性',routerKey:'对象不变性'},{name:'正则表达式',routerKey:'正则表达式'},{name:'ES6-ES13',routerKey:'ES6+'},]},{name:'进阶篇',routerKey:'进阶',children:[{name:'this',routerKey:'this'},{name:'闭包',routerKey:'闭包'},{name:'reduce',routerKey:'reduce'},{name:'二进制',routerKey:'二进制'},{name:'时间日期',routerKey:'时间日期'},{name:'字符编码',routerKey:'字符编码'},{name:'错误处理',routerKey:'错误处理'},{name:'内存泄漏',routerKey:'内存泄漏'},{name:'异步编程',routerKey:'异步编程'},{name:'事件循环',routerKey:'事件循环'},{name:'竞态条件',routerKey:'竞态条件'},{name:'web api',routerKey:'webApi'},{name:'前端模块化',routerKey:'前端模块化'},{name:'V8执行原理',routerKey:'V8执行原理'},{name:'虚拟键盘Api',routerKey:'虚拟键盘Api'},{name:'迭代器&生成器',routerKey:'迭代器和迭代器'},]},{name:'技巧篇',routerKey:'技巧',children:[{name:'对象处理',routerKey:'对象处理'},{name:'条件语句',routerKey:'条件语句'},{name:'调试技巧',routerKey:'调试技巧'},{name:'命名约定',routerKey:'命名约定'},{name:'开发技巧',routerKey:'开发技巧'},{name:'单行代码',routerKey:'单行代码'},{name:'工具函数',routerKey:'工具函数'},{name:'Eslint异步',routerKey:'Eslint异步'},]},{name:'最新动态',routerKey:'最新',children:[{name:'ECMAScript 2022',routerKey:'ECMAScript2022'},]}]},{name:'精通React',routerKey:"React",children:[{name:'深入理解 JSX:从零开始实现一个 JSX 解析器',routerKey:"JSX",},{name:'React实践',routerKey:"React实践",},{name:'React18如何提高性能?',routerKey:"React18提高性能",},{name:'探索主流前端框架的响应式原理',routerKey:"前端框架响应式原理",},{name:'React api和代码重用的演变',routerKey:"ReactApi演变",},{name:'2023年的React生态',routerKey:"React2023生态",},{name:'2023年如何搭建新的React项目',routerKey:"React2023如何搭建项目",},{name:'React常见错误,如何避免?',routerKey:"React常见错误",},{name:'React渲染的未来',routerKey:"React渲染的未来",},{name:'React Router',routerKey:'ReactRouter',},{name:'如何在React中应用SOLID原则',routerKey:'React应用SOLID'},{name:'如何使用React Hook重构类组件',routerKey:'ReactHook'},{name:'如何组织 React 组件代码结构',routerKey:'React组件代码结构'},{name:'在 React 中实现条件渲染的7种方式',routerKey:'7中条件渲染'},{name:'React + TypeScript:如何处理常见事件',routerKey:'与ts处理常见事件'},{name:'React 中的浅比较是如何工作的',routerKey:'浅比较是如何工作的'},{name:'如何编写干净的 React 代码',routerKey:'如何编写干净的react代码'},{name:'如何编写高性能的 React 代码:规则、模式、注意事项',routerKey:'规则、模式、注意事项'},{name:'如何优雅地在 React 中使用TypeScript',routerKey:'react使用TypeScript',},{name:'React 中五种常见的样式策略',routerKey:'5种常见样式策略',},{name:'使用 React Hooks 时要避免的6个错误',routerKey:'Hooks避免的6哥错误',},{name:'React Hooks 初探',routerKey:'Hooks初探',},{name:'React 基础入门',routerKey:'React入门',},{name:'React 组件实践',routerKey:'React组件',},{name:'React 组件化开发',routerKey:'React组件化开发',},{name:'React 生命周期',routerKey:'React生命周期',},{name:'React 组件通信',routerKey:'React组件通信',},{name:'React 纯函数组件',routerKey:'纯函数组件',},{name:'React 组件设计模式',routerKey:'组件设计模式',},{name:'React 渲染流程',routerKey:'渲染流程',},]},{name:'TypeScript',routerKey:'TypeScript',children:[{name:'基础篇',routerKey:'基础',children:[{name:'简史',routerKey:'简史',},{name:'快速入门',routerKey:'快速入门'},{name:'数据类型',routerKey:'数据类型'},{name:'枚举类型',routerKey:'枚举类型'},{name:'函数类型',routerKey:'函数类型'},{name:'类类型',routerKey:'类类型'},{name:'接口类型',routerKey:'接口类型'},]},{name:'进阶篇',routerKey:'进阶',children:[{name:'泛型',routerKey:'泛型'},{name:'装饰器',routerKey:'装饰器'},{name:'严格模式',routerKey:'严格模式'},{name:'工具类型',routerKey:'工具类型'},{name:'映射类型',routerKey:'映射类型'},{name:'高级类型',routerKey:'高级类型'},{name:'tsconfig',routerKey:'tsconfig'},{name:'JS迁移TS',routerKey:'JS迁移TS'},{name:'标准库类型',routerKey:'标准库类型'},{name:'类型兼容性',routerKey:'类型兼容性'},]}]},{name:'前端工程化',routerKey:'工程化',children:[{name:'Monorepo',routerKey:'Monorepo',},{name:'VS Code技巧',routerKey:'VsCode技巧',},{name:'在 Git 中撤消更改的 6 种方法',routerKey:'Git6种撤销',},{name:'如何配置 Git 提交代码检查',routerKey:'Git配置提交检查',},{name:'学习 Git,看这一篇就够了',routerKey:'学习Git',},{name:'SPA vs MPA vs PWA',routerKey:'SPAvsMPAvsPWA',},{name:'package.json',routerKey:'packageJSON',},{name:'常见的编程方式',routerKey:'常见的编程方式',},{name:'前端模块化规范',routerKey:'前端模块化规范',},{name:'前端发展和演进史',routerKey:'前端发展和演进史',},{name:'邂逅Webpack',routerKey:'邂逅Webpack',},]},{name:'浏览器原理',routerKey:'浏览器原理',children:[{name:'前端安全',routerKey:'前端安全'},{name:'浏览器存储',routerKey:'浏览器存储'},{name:'进程与线程',routerKey:'进程与线程'},{name:'浏览器事件机制',routerKey:'浏览器事件机制'},{name:'同源策略和跨域',routerKey:'同源策略和跨域'},{name:'浏览器渲染原理',routerKey:'浏览器渲染原理'},]},{name:'计算机网络',routerKey:'计算机网络',children:[{name:'DNS',routerKey:'DNS'},{name:'网络模型',routerKey:'网络模型'},{name:'TCP与UDP',routerKey:'TCP与UDP'},{name:'Websocket',routerKey:'Websocket'},{name:'HTTP概述',routerKey:'HTTP概述'},{name:'HTTPS概述',routerKey:'HTTPS概述'},{name:'HTTP状态码',routerKey:'HTTP状态码'},]},{name:'前端设计模式',routerKey:'前端设计模式',children:[{name:'前端设计模式',routerKey:'前端设计模式'},{name:'单例模式',routerKey:'单例模式'},{name:'工厂模式',routerKey:'工厂模式'},{name:'状态模式',routerKey:'状态模式'},{name:'策略模式',routerKey:'策略模式'},{name:'代理模式',routerKey:'代理模式'},{name:'装饰器模式',routerKey:'装饰器模式'},{name:'适配器模式',routerKey:'适配器模式'},{name:'迭代器模式',routerKey:'迭代器模式'},{name:'观察者模式/发布-订阅模式',routerKey:'观察者模式'},]},{name:'前端性能优化',routerKey:'前端性能优化',children:[{name:'什么是性能优化',routerKey:'什么是性能优化'},{name:'图像优化',routerKey:'图像优化'},{name:'加载优化',routerKey:'加载优化'},{name:'雅虎35条军规',routerKey:'雅虎35条军规'},]},{name:'前端宝藏',routerKey:'前端宝藏',children:[{name:'开源库',routerKey:'开源库',children:[{name:'词云',routerKey:'词云'},{name:'3D 库',routerKey:'3D库'},{name:'甘特图',routerKey:'甘特图'},{name:'国际化',routerKey:'国际化'},{name:'测试库',routerKey:'测试库'},{name:'图标库',routerKey:'图标库'},{name:'动画库',routerKey:'动画库'},{name:'Vue 库',routerKey:'Vue库'},{name:'React 库',routerKey:'React库'},{name:'代码高亮',routerKey:'代码高亮'},{name:'快捷键库',routerKey:'快捷键库'},{name:'产品引导',routerKey:'产品引导'},{name:'数据处理',routerKey:'数据处理'},{name:'视频播放器',routerKey:'视频播放器'},{name:'拖拽排序库',routerKey:'拖拽排序库'},{name:'文件处理库',routerKey:'文件处理库'},{name:'Node.js 框架',routerKey:'Nodejs框架'},{name:'前端代码规范',routerKey:'前端代码规范'},{name:'富文本编辑器',routerKey:'富文本编辑器'},{name:'数据可视化库',routerKey:'数据可视化库'},{name:'移动端组件库',routerKey:'移动端组件库'},{name:'React 图标库',routerKey:'React图表库'},{name:'Vue UI 组件库',routerKey:'VueUI组件库'},{name:'混用Vue&React',routerKey:'混用VueReact'},{name:'React Hooks 库',routerKey:'ReactHooks库'},{name:'中后台管理模版',routerKey:'中后台管理模版'},{name:'React UI 组件库',routerKey:'ReactUI组件库'},{name:'Vue 3 UI 组件库',routerKey:'Vue3UI组件库'},{name:'微信小程序UI组件库',routerKey:'微信小程序UI组件库'},]},{name:'实用工具',routerKey:'实用工具',children:[{name:'单词练习',routerKey:'单词练习'},{name:'简历制作',routerKey:'简历制作'},{name:'ChatGPT',routerKey:'ChatGPT'},{name:'调试工具',routerKey:'调试工具'},{name:'画图工具',routerKey:'画图工具'},{name:'技术博客',routerKey:'技术博客'},{name:'前端工具',routerKey:'前端工具'},{name:'技术选型',routerKey:'技术选型'},{name:'检查依赖包',routerKey:'检查依赖包'},{name:'性能测试工具',routerKey:'性能测试工具'},{name:'Chrome扩展',routerKey:'Chrome扩展'},{name:'RSS订阅工具',routerKey:'RSS订阅工具'},{name:'VS Code 插件',routerKey:'VSCode插件'},{name:'VS Code 主题',routerKey:'VsCode主题'},{name:'在线代码编辑器',routerKey:'在线代码编辑器'},{name:'WebStorm主题',routerKey:'WebStorm主题'},{name:'VS Code 摸鱼插件',routerKey:'VSCode摸鱼插件'},{name:'JS框架性能基准工具',routerKey:'JS框架性能基准工具'},]},{name:'使用技巧',routerKey:'使用技巧',children:[{name:'Linux 命令',routerKey:'Linux命令',},{name:'npm 使用技巧',routerKey:'npm使用技巧'},{name:'Google 搜索技巧',routerKey:'Google搜索技巧'},{name:'console 调试技巧',routerKey:'console调试技巧'},{name:'VS Code 快捷方式',routerKey:'Vscode快捷方式'},{name:'DevTools 调试技巧',routerKey:'DevTools调试技巧',children:[{name:'DevTools[1]',routerKey:'DevTools1'},{name:'DevTools[2]',routerKey:'DevTools2'},]},{name:'DevTools 实用功能',routerKey:'DevTools实用功能',children:[{name:'DevTools[1]',routerKey:'DevTools1'},{name:'DevTools[2]',routerKey:'DevTools2'},]},{name:'删除未使用的Css代码',routerKey:'删除未使用的Css代码'}]},{name:'学习资源',routerKey:'学习资源',children:[{name:'Rust 资源',routerKey:'Rust资源'},{name:'3D 项目',routerKey:'3D项目'},{name:'前端大会',routerKey:'前端大会'},{name:'前端书籍',routerKey:'前端书籍'},{name:'热门项目',routerKey:'热门项目'},{name:'Vue 项目',routerKey:'Vue项目'},{name:'Windows',routerKey:'Windows'},{name:'Awesome',routerKey:'Awesome'},{name:'Web3 项目',routerKey:'Web3项目'},{name:'React 资源',routerKey:'React资源'},{name:'React 项目',routerKey:'React项目'},{name:'低代码项目',routerKey:'低代码项目'},{name:'Vue3.0项目',routerKey:'Vue3项目'},{name:'Node.js项目',routerKey:'Nodejs项目'},{name:'Canvas 项目',routerKey:'Canvas项目'},{name:'前端高仿项目',routerKey:'前端高仿项目'},{name:'国外前端博主',routerKey:'国外前端博主'},{name:'前端学习资源',routerKey:'前端学习资源'},{name:'Nuxt 官方项目',routerKey:'Nuxt官方项目'},{name:'跨平台桌面项目',routerKey:'跨平台桌面项目'},{name:'微信小程序项目1',routerKey:'微信小程序项目1'},{name:'微信小程序项目2',routerKey:'微信小程序项目2'},{name:'微信小程序项目3',routerKey:'微信小程序项目3'},{name:'数据结构与算法',routerKey:'数据结构与算法'},{name:'TypeScript资源',routerKey:'TypeScript资源'},{name:'数据可视化大屏项目',routerKey:'数据可视化大屏项目'},{name:'前端框架源码解析项目',routerKey:'前端框架源码解析项目'},]}]}]const computedDataLabel=(data:any,routerKey=null)=>{data.map((item:any)=>{let _routerKey=item.routerKey;if(routerKey){_routerKey=routerKey+'_'+item.routerKey}item['key']=_routerKey;item['label']=item.name item['title']=item.name if(item.children&&item.children.length)computedDataLabel(item.children,_routerKey)})}computedDataLabel(_Study);export const Study=_Study export const _Question=[{name:'互联网公司大盘点',routerKey:'面试题_互联网公司大盘点',},{name:'前端学习路线',routerKey:'面试题_前端学习路线'},{name:'前端面试准备',routerKey:'面试题_前端面试准备'},{name:'前端面试题之HTML篇',routerKey:'面试题_前端面试题之HTML篇'},{name:'前端面试题之CSS篇',routerKey:'面试题_前端面试题之CSS篇'},{name:'前端面试题之JavaScript篇',routerKey:'面试题_前端面试题之JavaScript篇'},{name:'前端面试题之Vue篇',routerKey:'面试题_前端面试题之Vue篇'},{name:'前端面试题之React篇',routerKey:'面试题_前端面试题之React篇'},{name:'前端面试题之性能优化篇',routerKey:'面试题_前端面试题之性能优化篇'},{name:'前端面试题之前端工程化篇',routerKey:'面试题_前端面试题之前端工程化篇'},{name:'前端面试题之计算机网络篇',routerKey:'面试题_前端面试题之计算机网络篇'},{name:'前端面试题之浏览器原理篇',routerKey:'面试题_前端面试题之浏览器原理篇'},{name:'前端面试题之手写代码篇',routerKey:'面试题_前端面试题之手写代码篇'},{name:'前端面试题之代码输出篇',routerKey:'面试题_前端面试题之代码输出篇'},{name:'LeetCode 高频题目分类列表',routerKey:'面试题_LeetCode'},]computedDataLabel(_Question);export const Question=_Question;export const Echarts=[{name:'echarts',routerKey:'echarts',children:[]},]export const chartData=[{name:'知识',routerKey:'知识',children:Study},{name:'面试',routerKey:'面试',children:Question},{name:'echarts',routerKey:'echarts',isVue:true,}]

四、实现渲染方法

1.常量chartTypeConfig colors

  1. chartTypeConfig echarts饼图\矩形树图配置信息对象
  2. colors echarts系列颜色
export  const chartTypeConfig:any = {
    'pie':{
        type: 'pie',
        id: 'chart',
        width: '100%',
        height: '100%',
        label: {
            fontSize: 14,
            fontWeight: 600,
            color: "#888",
        },
        labelLine: {
            length: '2%',
            length2: '1%',
        },
        itemStyle: {
            borderRadius: 6,
            shadowColor: '#1677ff7a',
            shadowBlur: 4,
            borderWidth: 0,
        },
        emphasis: {
            itemStyle: {
                color: 'inherit',
                shadowBlur: 10,
                shadowColor: '#1677ff9a'
            },
            label: {
                show: true
            }
        },
    },
    'treemap':{
        type: 'treemap',
        width: '80%',
        height: '80%',
        left: 'center',
        top: 'middle',
        id: 'chart',
        leafDepth: 1,
        nodeClick: false,
        breadcrumb: {
            show: false
        },
        label: {
            fontSize: 14,
            fontWeight: 600,
            color: "#888",
        },
        roam: false,
    }
}

export const colors=['#FF9843','#FFDD95','#86A7FC','#3468C0','#D9EDBF','#FFB996','#FFCF81','#FDFFAB','#80BCBD','#AAD9BB','#D5F0C1','#F9F7C9','#7BD3EA','#A1EEBD','#F6F7C4','#F6D6D6','#FFF78A','#FFE382','#FFC47E','#FFAD84','#7071E8','#C683D7','#ED9ED6','#FFC7C7','#FF8F8F','#EEF296','#9ADE7B','#508D69','#FFC5C5','#FFEBD8','#C7DCA7','#89B9AD','#D0A2F7','#39A7FF','#A2C579','#a5f1e9','#7fe9de','#80bcbd','#aad9bb','#d5f0c1','#f9f7c9','#756ab6','#ac87c5','#e0aed0','#ffe5e5','#7bd3ea','#ff90bc','#8acdd7','#ffc5c5','#ffebd8','#ff8080','#00a9ff','#89cff3','#a0e9ff','#cdf5fd','#45ffca','#feffac','#ffb6d9','#d67bff','#ffacac','#ffbfa9','#ffebb4','#fbffb1','#b9f3e4','#ea8fea','#ffaacf','#f6e6c2','#cde990','#aacb73','#eac7c7','#f8f988']

2.渲染方法 initChart

这里多次使用到树形结构变量 chartData

  1. 声明initChart函数
  2. 通过ref获取容器元素,并实例化echarts实例myChart
  3. 声明echarts图表数据变量data
    1. computedChartData 函数设置饼图数值
      • 默认每一个都是1,children的长度值
      • 主要用来表示占比
    2. 这里初始层级就是根层级的每一项加上😊用来表示根
    3. 设置饼图每一项label样式
  4. 将跟层级添加到记录数组变量pathLog
  5. 通过图表类型变量chartType和常量chartTypeConfig获取指定图表类型配置信息
  6. 声明option变量放入配置项,渲染图表
const initChart = () => {
  let myChart = echarts.init(chart.value);
  let data  = computedChartData(chartData)
  data.map((item: any) => {
    item['name'] = '🙂' + item.name;
    item['label'] = {
      position: ['50%', '50%'],
      formatter: (v: any) => {
        return `{text|${v.name}}`
      },
      rich: {
        text: {
          fontSize: 14,
          fontWeight: 600,
          color: "#666",      //颜色区分用于 getZr的事件判断
        },
      }
    }
  })
  pathLog.value.push({
    path: 'root',
    computedDataLabel_k: 'root',
    data
  });
  let optionConfig = chartTypeConfig[chartType.value];
  let option = {
    color: colors,
    series: [
      {
        type: chartType.value,
        id: 'chart',
        ...optionConfig,
        data: data
      }
    ]
  };
  myChart.setOption(option);
  window.addEventListener('resize', resizeEcharts);
  chartEventMethods(myChart);
}

const computedChartData = (_d: any) => {
  return _d.map((v: any) => {
    return {
      ...v,
      value: v.children ? v.children.length : 1
    }
  })
}

3.图表大小随屏幕变化调整

const resizeEcharts = () => {
  let myChart = echarts.getInstanceByDom(chart.value);
  if (myChart) {
    myChart.resize();
  }
}

4.图表中每一项的事件添加

  1. 声明一变量isRoot用来表示是否点击了 设置😊的根图元素label
    1. 加该变量主要是因为 点击label同样触发myChart.on事件这里做判断使用
  2. myChart.getZr().on 绑定视图任意位置的点击事件
    1. 通过rich设置的样式,用来判断是否为label
    2. 点击带有😊的直接跳转该类型页面,不用跳转到下级子页面
  3. 设置图表 每一项的点击事件
    1. 判断变量isRoot以判断终止下方代码执行
  4. 如果点击的为图表每一项而不是带😊的label
    1. 获取事件对象的event.data结构出该对象下的children, name, computedDataLabel_k, key
    2. 调用该方法computedChartData传入children设置占比数值
    3. isClickType变量 用来表示点击那个根目录下的子集,用来跳转路由使用
    4. 判断记录变量pathLog内是否含有了当前的computedDataLabel_k
    5. 没有则将该key的对象加入到记录中
    6. 通过echarts实例再次渲染图表更新内容
const chartEventMethods = (myChart: any) => {
  let isRoot = false;
  myChart.getZr().on('click', (e: any) => {
    if (!e.target?.style) return;
    const {fill, text} = e.target.style;
    if (fill == '#666') {
      let routerPath = '';
      if (text == '🙂面试') {
        routerPath = '/question'
      } else if (text == '🙂知识') {
        routerPath = '/study'
      } else if (text == 'echarts') {
        routerPath = '/echarts'
      }
      router.push({
        path: routerPath
      })
      isRoot = true;
    }
  })
  myChart.on('click', (event: any) => {
    if (isRoot) {
      isRoot = false;
      return
    }
    ;

    const {children, name, computedDataLabel_k, key} = event.data;
    if (pathLog.value.length === 1) {
      isClickType.value = name;
    }
    if (children && children.length) {
      let cdata = computedChartData(children);
      let pathLog_computedDataLabel_ks = pathLog.value.map((v: any) => {
        return v.computedDataLabel_k;
      });
      if (!pathLog_computedDataLabel_ks.includes(computedDataLabel_k)) {
        pathLog.value.push({
          path: name,
          computedDataLabel_k,
          data: cdata
        });
      }
      myChart.setOption({
        series: [
          {
            id: 'chart',
            data: cdata
          }
        ]
      })
      return;
    }
    let routerPath = '/study/' + encodeURI(key);
    if (isClickType.value === '🙂知识') routerPath = '/study/' + encodeURI(key);
    else if (isClickType.value === '🙂面试') routerPath = '/question/' + encodeURI(key);
    else if (isClickType.value === '🙂echarts') routerPath = '/echarts'
    router.push({
      path: routerPath
    })
  })
}

5.记录点击事件的handleClickSwitchMethods方法

  • 获取图表实例,根据传入的数组,重新渲染图表;
  • 同时兼顾更新图表类型饼图\矩形树图
const handleClickSwitchMethods = (_d = []) => {
  let myChart = echarts.getInstanceByDom(chart.value);
  let option_ = JSON.parse(JSON.stringify(myChart?.getOption()))
  let data_ = option_.series[0].data;
  if (_d && _d.length) data_ = _d;
  option_.series[0] = {...chartTypeConfig[chartType.value], data: data_}
  myChart?.setOption(option_);
}

五、完整代码

src/views/Home · 春秋半夏/webMds-template - 码云 - 开源中国 (gitee.com)

./config

export const chartTypeConfig:any={'pie':{type:'pie',id:'chart',width:'100%',height:'100%',label:{fontSize:14,fontWeight:600,color:"#888",},labelLine:{length:'2%',length2:'1%',},itemStyle:{borderRadius:6,shadowColor:'#1677ff7a',shadowBlur:4,borderWidth:0,},emphasis:{itemStyle:{color:'inherit',shadowBlur:10,shadowColor:'#1677ff9a'},label:{show:true}},},'treemap':{type:'treemap',width:'80%',height:'80%',left:'center',top:'middle',id:'chart',leafDepth:1,nodeClick:false,breadcrumb:{show:false},label:{fontSize:14,fontWeight:600,color:"#888",},roam:false,}}export const colors=['#FF9843','#FFDD95','#86A7FC','#3468C0','#D9EDBF','#FFB996','#FFCF81','#FDFFAB','#80BCBD','#AAD9BB','#D5F0C1','#F9F7C9','#7BD3EA','#A1EEBD','#F6F7C4','#F6D6D6','#FFF78A','#FFE382','#FFC47E','#FFAD84','#7071E8','#C683D7','#ED9ED6','#FFC7C7','#FF8F8F','#EEF296','#9ADE7B','#508D69','#FFC5C5','#FFEBD8','#C7DCA7','#89B9AD','#D0A2F7','#39A7FF','#A2C579','#a5f1e9','#7fe9de','#80bcbd','#aad9bb','#d5f0c1','#f9f7c9','#756ab6','#ac87c5','#e0aed0','#ffe5e5','#7bd3ea','#ff90bc','#8acdd7','#ffc5c5','#ffebd8','#ff8080','#00a9ff','#89cff3','#a0e9ff','#cdf5fd','#45ffca','#feffac','#ffb6d9','#d67bff','#ffacac','#ffbfa9','#ffebb4','#fbffb1','#b9f3e4','#ea8fea','#ffaacf','#f6e6c2','#cde990','#aacb73','#eac7c7','#f8f988']

/@/router/mds

const _Study:any=[{name:'CSS艺术之美',routerKey:'CSS',children:[{name:'基础篇',routerKey:'基础',children:[{name:'剧中',routerKey:'剧中',},{name:'单位',routerKey:'单位',},{name:'颜色',routerKey:'颜色',},{name:'变量',routerKey:'变量',},{name:'选择器',routerKey:'选择器',},{name:'Sass',routerKey:'SASS',},{name:'Flex布局',routerKey:'Flex',},{name:'Grid布局',routerKey:'Grid',},{name:'数学函数',routerKey:'数学函数',},{name:'Flex vs Grid',routerKey:'FlexVsGrid',}]},{name:'进阶篇',routerKey:'进阶',children:[{name:'框架',routerKey:'框架',},{name:'技巧',routerKey:'技巧',},{name:'优化',routerKey:'优化',},{name:'方法论',routerKey:'方法论',},{name:'容器查询',routerKey:'容器查询',},{name:'网页置灰',routerKey:'网页置灰',},{name:'样式重置',routerKey:'样式重置',},{name:'文本换行',routerKey:'文本换行',},{name:'隐藏元素',routerKey:'隐藏元素',},{name:'溢出问题',routerKey:'溢出问题',},{name:'父选择器',routerKey:'父选择器',},{name:'高级选择器',routerKey:'高级选择器',},{name:'选择器性能',routerKey:'选择器性能',},{name:'背景图vs<img>',routerKey:'背景图vsImg',},{name:'Tailwind css',routerKey:'TailwindCSS',},]},{name:'效果篇',routerKey:'效果',children:[{name:'实现三角形',routerKey:'三角形',},{name:'实现九宫格',routerKey:'九宫格',},{name:'实现3D骰子',routerKey:'3D骰子',},]},{name:'最新动态',routerKey:'最新',children:[{name:'可扩展Css的演变',routerKey:'CSS演变',},{name:'新特性',routerKey:'新特性',},]}]},{name:'JavaScript',routerKey:'JavaScript',children:[{name:'现代深拷贝',routerKey:'现代深拷贝',},{name:'基础篇',routerKey:'基础',children:[{name:'数组',routerKey:'数组',},{name:'JSON',routerKey:'JSON',},{name:'字符串',routerKey:'字符串'},{name:'操作符',routerKey:'操作符'},{name:'Infinity',routerKey:'Infinity'},{name:'数据类型',routerKey:'数据类型'},{name:'类型转换',routerKey:'类型转换'},{name:'循环遍历',routerKey:'循环遍历'},{name:'引号区别',routerKey:'引号区别'},{name:'错误类型',routerKey:'错误类型'},{name:'深浅拷贝',routerKey:'深浅拷贝'},{name:'结构赋值',routerKey:'结构赋值'},{name:'变量提升',routerKey:'变量提升'},{name:'对象不变性',routerKey:'对象不变性'},{name:'正则表达式',routerKey:'正则表达式'},{name:'ES6-ES13',routerKey:'ES6+'},]},{name:'进阶篇',routerKey:'进阶',children:[{name:'this',routerKey:'this'},{name:'闭包',routerKey:'闭包'},{name:'reduce',routerKey:'reduce'},{name:'二进制',routerKey:'二进制'},{name:'时间日期',routerKey:'时间日期'},{name:'字符编码',routerKey:'字符编码'},{name:'错误处理',routerKey:'错误处理'},{name:'内存泄漏',routerKey:'内存泄漏'},{name:'异步编程',routerKey:'异步编程'},{name:'事件循环',routerKey:'事件循环'},{name:'竞态条件',routerKey:'竞态条件'},{name:'web api',routerKey:'webApi'},{name:'前端模块化',routerKey:'前端模块化'},{name:'V8执行原理',routerKey:'V8执行原理'},{name:'虚拟键盘Api',routerKey:'虚拟键盘Api'},{name:'迭代器&生成器',routerKey:'迭代器和迭代器'},]},{name:'技巧篇',routerKey:'技巧',children:[{name:'对象处理',routerKey:'对象处理'},{name:'条件语句',routerKey:'条件语句'},{name:'调试技巧',routerKey:'调试技巧'},{name:'命名约定',routerKey:'命名约定'},{name:'开发技巧',routerKey:'开发技巧'},{name:'单行代码',routerKey:'单行代码'},{name:'工具函数',routerKey:'工具函数'},{name:'Eslint异步',routerKey:'Eslint异步'},]},{name:'最新动态',routerKey:'最新',children:[{name:'ECMAScript 2022',routerKey:'ECMAScript2022'},]}]},{name:'精通React',routerKey:"React",children:[{name:'深入理解 JSX:从零开始实现一个 JSX 解析器',routerKey:"JSX",},{name:'React实践',routerKey:"React实践",},{name:'React18如何提高性能?',routerKey:"React18提高性能",},{name:'探索主流前端框架的响应式原理',routerKey:"前端框架响应式原理",},{name:'React api和代码重用的演变',routerKey:"ReactApi演变",},{name:'2023年的React生态',routerKey:"React2023生态",},{name:'2023年如何搭建新的React项目',routerKey:"React2023如何搭建项目",},{name:'React常见错误,如何避免?',routerKey:"React常见错误",},{name:'React渲染的未来',routerKey:"React渲染的未来",},{name:'React Router',routerKey:'ReactRouter',},{name:'如何在React中应用SOLID原则',routerKey:'React应用SOLID'},{name:'如何使用React Hook重构类组件',routerKey:'ReactHook'},{name:'如何组织 React 组件代码结构',routerKey:'React组件代码结构'},{name:'在 React 中实现条件渲染的7种方式',routerKey:'7中条件渲染'},{name:'React + TypeScript:如何处理常见事件',routerKey:'与ts处理常见事件'},{name:'React 中的浅比较是如何工作的',routerKey:'浅比较是如何工作的'},{name:'如何编写干净的 React 代码',routerKey:'如何编写干净的react代码'},{name:'如何编写高性能的 React 代码:规则、模式、注意事项',routerKey:'规则、模式、注意事项'},{name:'如何优雅地在 React 中使用TypeScript',routerKey:'react使用TypeScript',},{name:'React 中五种常见的样式策略',routerKey:'5种常见样式策略',},{name:'使用 React Hooks 时要避免的6个错误',routerKey:'Hooks避免的6哥错误',},{name:'React Hooks 初探',routerKey:'Hooks初探',},{name:'React 基础入门',routerKey:'React入门',},{name:'React 组件实践',routerKey:'React组件',},{name:'React 组件化开发',routerKey:'React组件化开发',},{name:'React 生命周期',routerKey:'React生命周期',},{name:'React 组件通信',routerKey:'React组件通信',},{name:'React 纯函数组件',routerKey:'纯函数组件',},{name:'React 组件设计模式',routerKey:'组件设计模式',},{name:'React 渲染流程',routerKey:'渲染流程',},]},{name:'TypeScript',routerKey:'TypeScript',children:[{name:'基础篇',routerKey:'基础',children:[{name:'简史',routerKey:'简史',},{name:'快速入门',routerKey:'快速入门'},{name:'数据类型',routerKey:'数据类型'},{name:'枚举类型',routerKey:'枚举类型'},{name:'函数类型',routerKey:'函数类型'},{name:'类类型',routerKey:'类类型'},{name:'接口类型',routerKey:'接口类型'},]},{name:'进阶篇',routerKey:'进阶',children:[{name:'泛型',routerKey:'泛型'},{name:'装饰器',routerKey:'装饰器'},{name:'严格模式',routerKey:'严格模式'},{name:'工具类型',routerKey:'工具类型'},{name:'映射类型',routerKey:'映射类型'},{name:'高级类型',routerKey:'高级类型'},{name:'tsconfig',routerKey:'tsconfig'},{name:'JS迁移TS',routerKey:'JS迁移TS'},{name:'标准库类型',routerKey:'标准库类型'},{name:'类型兼容性',routerKey:'类型兼容性'},]}]},{name:'前端工程化',routerKey:'工程化',children:[{name:'Monorepo',routerKey:'Monorepo',},{name:'VS Code技巧',routerKey:'VsCode技巧',},{name:'在 Git 中撤消更改的 6 种方法',routerKey:'Git6种撤销',},{name:'如何配置 Git 提交代码检查',routerKey:'Git配置提交检查',},{name:'学习 Git,看这一篇就够了',routerKey:'学习Git',},{name:'SPA vs MPA vs PWA',routerKey:'SPAvsMPAvsPWA',},{name:'package.json',routerKey:'packageJSON',},{name:'常见的编程方式',routerKey:'常见的编程方式',},{name:'前端模块化规范',routerKey:'前端模块化规范',},{name:'前端发展和演进史',routerKey:'前端发展和演进史',},{name:'邂逅Webpack',routerKey:'邂逅Webpack',},]},{name:'浏览器原理',routerKey:'浏览器原理',children:[{name:'前端安全',routerKey:'前端安全'},{name:'浏览器存储',routerKey:'浏览器存储'},{name:'进程与线程',routerKey:'进程与线程'},{name:'浏览器事件机制',routerKey:'浏览器事件机制'},{name:'同源策略和跨域',routerKey:'同源策略和跨域'},{name:'浏览器渲染原理',routerKey:'浏览器渲染原理'},]},{name:'计算机网络',routerKey:'计算机网络',children:[{name:'DNS',routerKey:'DNS'},{name:'网络模型',routerKey:'网络模型'},{name:'TCP与UDP',routerKey:'TCP与UDP'},{name:'Websocket',routerKey:'Websocket'},{name:'HTTP概述',routerKey:'HTTP概述'},{name:'HTTPS概述',routerKey:'HTTPS概述'},{name:'HTTP状态码',routerKey:'HTTP状态码'},]},{name:'前端设计模式',routerKey:'前端设计模式',children:[{name:'前端设计模式',routerKey:'前端设计模式'},{name:'单例模式',routerKey:'单例模式'},{name:'工厂模式',routerKey:'工厂模式'},{name:'状态模式',routerKey:'状态模式'},{name:'策略模式',routerKey:'策略模式'},{name:'代理模式',routerKey:'代理模式'},{name:'装饰器模式',routerKey:'装饰器模式'},{name:'适配器模式',routerKey:'适配器模式'},{name:'迭代器模式',routerKey:'迭代器模式'},{name:'观察者模式/发布-订阅模式',routerKey:'观察者模式'},]},{name:'前端性能优化',routerKey:'前端性能优化',children:[{name:'什么是性能优化',routerKey:'什么是性能优化'},{name:'图像优化',routerKey:'图像优化'},{name:'加载优化',routerKey:'加载优化'},{name:'雅虎35条军规',routerKey:'雅虎35条军规'},]},{name:'前端宝藏',routerKey:'前端宝藏',children:[{name:'开源库',routerKey:'开源库',children:[{name:'词云',routerKey:'词云'},{name:'3D 库',routerKey:'3D库'},{name:'甘特图',routerKey:'甘特图'},{name:'国际化',routerKey:'国际化'},{name:'测试库',routerKey:'测试库'},{name:'图标库',routerKey:'图标库'},{name:'动画库',routerKey:'动画库'},{name:'Vue 库',routerKey:'Vue库'},{name:'React 库',routerKey:'React库'},{name:'代码高亮',routerKey:'代码高亮'},{name:'快捷键库',routerKey:'快捷键库'},{name:'产品引导',routerKey:'产品引导'},{name:'数据处理',routerKey:'数据处理'},{name:'视频播放器',routerKey:'视频播放器'},{name:'拖拽排序库',routerKey:'拖拽排序库'},{name:'文件处理库',routerKey:'文件处理库'},{name:'Node.js 框架',routerKey:'Nodejs框架'},{name:'前端代码规范',routerKey:'前端代码规范'},{name:'富文本编辑器',routerKey:'富文本编辑器'},{name:'数据可视化库',routerKey:'数据可视化库'},{name:'移动端组件库',routerKey:'移动端组件库'},{name:'React 图标库',routerKey:'React图表库'},{name:'Vue UI 组件库',routerKey:'VueUI组件库'},{name:'混用Vue&React',routerKey:'混用VueReact'},{name:'React Hooks 库',routerKey:'ReactHooks库'},{name:'中后台管理模版',routerKey:'中后台管理模版'},{name:'React UI 组件库',routerKey:'ReactUI组件库'},{name:'Vue 3 UI 组件库',routerKey:'Vue3UI组件库'},{name:'微信小程序UI组件库',routerKey:'微信小程序UI组件库'},]},{name:'实用工具',routerKey:'实用工具',children:[{name:'单词练习',routerKey:'单词练习'},{name:'简历制作',routerKey:'简历制作'},{name:'ChatGPT',routerKey:'ChatGPT'},{name:'调试工具',routerKey:'调试工具'},{name:'画图工具',routerKey:'画图工具'},{name:'技术博客',routerKey:'技术博客'},{name:'前端工具',routerKey:'前端工具'},{name:'技术选型',routerKey:'技术选型'},{name:'检查依赖包',routerKey:'检查依赖包'},{name:'性能测试工具',routerKey:'性能测试工具'},{name:'Chrome扩展',routerKey:'Chrome扩展'},{name:'RSS订阅工具',routerKey:'RSS订阅工具'},{name:'VS Code 插件',routerKey:'VSCode插件'},{name:'VS Code 主题',routerKey:'VsCode主题'},{name:'在线代码编辑器',routerKey:'在线代码编辑器'},{name:'WebStorm主题',routerKey:'WebStorm主题'},{name:'VS Code 摸鱼插件',routerKey:'VSCode摸鱼插件'},{name:'JS框架性能基准工具',routerKey:'JS框架性能基准工具'},]},{name:'使用技巧',routerKey:'使用技巧',children:[{name:'Linux 命令',routerKey:'Linux命令',},{name:'npm 使用技巧',routerKey:'npm使用技巧'},{name:'Google 搜索技巧',routerKey:'Google搜索技巧'},{name:'console 调试技巧',routerKey:'console调试技巧'},{name:'VS Code 快捷方式',routerKey:'Vscode快捷方式'},{name:'DevTools 调试技巧',routerKey:'DevTools调试技巧',children:[{name:'DevTools[1]',routerKey:'DevTools1'},{name:'DevTools[2]',routerKey:'DevTools2'},]},{name:'DevTools 实用功能',routerKey:'DevTools实用功能',children:[{name:'DevTools[1]',routerKey:'DevTools1'},{name:'DevTools[2]',routerKey:'DevTools2'},]},{name:'删除未使用的Css代码',routerKey:'删除未使用的Css代码'}]},{name:'学习资源',routerKey:'学习资源',children:[{name:'Rust 资源',routerKey:'Rust资源'},{name:'3D 项目',routerKey:'3D项目'},{name:'前端大会',routerKey:'前端大会'},{name:'前端书籍',routerKey:'前端书籍'},{name:'热门项目',routerKey:'热门项目'},{name:'Vue 项目',routerKey:'Vue项目'},{name:'Windows',routerKey:'Windows'},{name:'Awesome',routerKey:'Awesome'},{name:'Web3 项目',routerKey:'Web3项目'},{name:'React 资源',routerKey:'React资源'},{name:'React 项目',routerKey:'React项目'},{name:'低代码项目',routerKey:'低代码项目'},{name:'Vue3.0项目',routerKey:'Vue3项目'},{name:'Node.js项目',routerKey:'Nodejs项目'},{name:'Canvas 项目',routerKey:'Canvas项目'},{name:'前端高仿项目',routerKey:'前端高仿项目'},{name:'国外前端博主',routerKey:'国外前端博主'},{name:'前端学习资源',routerKey:'前端学习资源'},{name:'Nuxt 官方项目',routerKey:'Nuxt官方项目'},{name:'跨平台桌面项目',routerKey:'跨平台桌面项目'},{name:'微信小程序项目1',routerKey:'微信小程序项目1'},{name:'微信小程序项目2',routerKey:'微信小程序项目2'},{name:'微信小程序项目3',routerKey:'微信小程序项目3'},{name:'数据结构与算法',routerKey:'数据结构与算法'},{name:'TypeScript资源',routerKey:'TypeScript资源'},{name:'数据可视化大屏项目',routerKey:'数据可视化大屏项目'},{name:'前端框架源码解析项目',routerKey:'前端框架源码解析项目'},]}]}]const computedDataLabel=(data:any,routerKey=null)=>{data.map((item:any)=>{let _routerKey=item.routerKey;if(routerKey){_routerKey=routerKey+'_'+item.routerKey}item['key']=_routerKey;item['label']=item.name item['title']=item.name if(item.children&&item.children.length)computedDataLabel(item.children,_routerKey)})}computedDataLabel(_Study);export const Study=_Study export const _Question=[{name:'互联网公司大盘点',routerKey:'面试题_互联网公司大盘点',},{name:'前端学习路线',routerKey:'面试题_前端学习路线'},{name:'前端面试准备',routerKey:'面试题_前端面试准备'},{name:'前端面试题之HTML篇',routerKey:'面试题_前端面试题之HTML篇'},{name:'前端面试题之CSS篇',routerKey:'面试题_前端面试题之CSS篇'},{name:'前端面试题之JavaScript篇',routerKey:'面试题_前端面试题之JavaScript篇'},{name:'前端面试题之Vue篇',routerKey:'面试题_前端面试题之Vue篇'},{name:'前端面试题之React篇',routerKey:'面试题_前端面试题之React篇'},{name:'前端面试题之性能优化篇',routerKey:'面试题_前端面试题之性能优化篇'},{name:'前端面试题之前端工程化篇',routerKey:'面试题_前端面试题之前端工程化篇'},{name:'前端面试题之计算机网络篇',routerKey:'面试题_前端面试题之计算机网络篇'},{name:'前端面试题之浏览器原理篇',routerKey:'面试题_前端面试题之浏览器原理篇'},{name:'前端面试题之手写代码篇',routerKey:'面试题_前端面试题之手写代码篇'},{name:'前端面试题之代码输出篇',routerKey:'面试题_前端面试题之代码输出篇'},{name:'LeetCode 高频题目分类列表',routerKey:'面试题_LeetCode'},]computedDataLabel(_Question);export const Question=_Question;export const Echarts=[{name:'echarts',routerKey:'echarts',children:[]},]export const chartData=[{name:'知识',routerKey:'知识',children:Study},{name:'面试',routerKey:'面试',children:Question},{name:'echarts',routerKey:'echarts',isVue:true,}]

./components/codeRain.vue

<template>
    <div class="codebox" ref="codeBoxRef">
        <canvas class="code" ref="codeCavans"></canvas>
    </div>
</template>

<script setup lang="ts">
import { ref, onMounted, onUnmounted, onActivated, onDeactivated } from 'vue';
const randomText = ():string => {
    let text = 'You make your brother really angry'
    return text[Math.floor(Math.random() * text.length)];
}
const randomColor = (): string => {
    let colors = [
        '#c678bb7a',
        '#2d7de37a',
        '#e06c697a',
        '#98c3797a',
        '#b09a667a',
    ];
    return colors[Math.floor(Math.random() * colors.length)];
}
interface Rain {
    width: number;
    height: number;
    columnCount: number;
    columnWidth: number;
    fontSize: number;
    timer: any;
    ctx: CanvasRenderingContext2D | null;
    nextChar: number[];
}
let rain = ref<Rain>({
    width: 0,
    height: 0,
    columnCount: 0,
    columnWidth: 0,
    fontSize: 14,
    timer: null,
    ctx:null,
    nextChar: []
})

let codeBoxRef = ref();
let codeCavans = ref();
const initRain = () => {
    let canBox: HTMLDivElement = codeBoxRef.value;
    let can: HTMLCanvasElement = codeCavans.value;
    rain.value.width = canBox.clientWidth;
    rain.value.height = canBox.clientHeight;
    can.width = rain.value.width;
    can.height = rain.value.height;
    rain.value.ctx = can?.getContext('2d');

    rain.value.columnWidth = rain.value.fontSize;
    rain.value.columnCount = Math.ceil(rain.value.width / rain.value.columnWidth);
    rain.value.nextChar = new Array(rain.value.columnCount).fill(0);
}

const drawRain = () => {
    const { ctx ,width,height,columnCount,fontSize,columnWidth,nextChar} = rain.value;
    if(!ctx) return;
    ctx.fillStyle = 'rgba(255,255,255,0.1)';
    ctx.fillRect(0, 0, width, height);
    for (let i = 0; i < columnCount; i++) {
        ctx.fillStyle = randomColor();
        const char = randomText();
        ctx.font = `${fontSize}px 'Roboto Mono'`;
        let s = nextChar[i];
        const x = i * columnWidth;
        const y = (s + 1) * fontSize;
        ctx.fillText(char, x, y);
        // 超出下一个的位置超出容器高度归零; 使用逻辑与如果超了;开始随机的分段加入随机判断归零
        if (y > height && Math.random() > 0.99) {
            nextChar[i] = 0;
        } else {
            nextChar[i]++;
        }
    }
}
const play = () =>{
    rain.value.timer = setInterval(drawRain, 30);
}
const suspend = () =>{
    clearInterval(rain.value.timer);
}

let isOnActivated = ref(true);
onMounted(() => {
    initRain();
    play();
    window.addEventListener('resize', initRain);
})
onUnmounted(() => {
    window.removeEventListener('resize', initRain, true);
})
onActivated(() => {
    if (isOnActivated.value) {
        isOnActivated.value = false;
        return;
    }
    play();
    window.addEventListener('resize', initRain);
})
onDeactivated(() => {
    suspend();
    window.removeEventListener('resize', initRain, true);
})
</script>

<style lang="less" scoped>
.codebox {
    width: 100%;
    height: 100%;

    canvas {
        display: block;
        width: 100%;
        height: 100%;
    }
}
</style>

App.vue

<template>
  <div class="home">
    <div class="home_tip" v-if="isInitShowTip" @animationend="handleAnimationEnd">
      <nav>点击扇形下钻以及跳转页面</nav>
      <nav>点击root层级的🙂label可直接进入</nav>
    </div>

    <div class="home_path">
      <div class="home_path_box">
        <a-breadcrumb>
          <a-breadcrumb-item v-for="item, key in pathLog">
                        <span class="home_path_box_item" @click="handleClickPathItem(item, key)">
                            {{ item.path }}
                        </span>
          </a-breadcrumb-item>
        </a-breadcrumb>
        <a-button v-if="false" type="link" :size="'small'" @click="handleGoBack" style="margin-left:10px;">Back
        </a-button>
      </div>
      <div class="home_switch">
        <div class="home_switch_box" @click="handleClickSwitch">
          <div class="home_switch_box_icon" :class="{ home_switch_box_iconShow: chartType == 'pie' }">
            <img src="./images/pie.png">
          </div>
          <div class="home_switch_box_icon" :class="{ home_switch_box_iconShow: chartType == 'treemap' }">
            <img src="./images/areaTree.png">
          </div>
        </div>
      </div>
    </div>
    <div class="chart" ref="chart"></div>
    <codeRain class="home_code"></codeRain>
  </div>
</template>

<script lang="ts" setup>
import {colors, chartTypeConfig} from './config';
import codeRain from './components/codeRain.vue';
import {onMounted, ref, onActivated, onDeactivated} from 'vue';
import * as echarts from 'echarts';
import {useRouter} from 'vue-router';

const router = useRouter();
import {chartData} from '/@/router/mds';

let chart = ref();
let computedDataLabel_k = 0;
const computedDataLabel = (data: any) => {
  data.map((item: any) => {
    if (item.children && item.children.length) {
      computedDataLabel(item.children)
    } else {
      item['value'] = 1;
    }
    computedDataLabel_k++
    item['computedDataLabel_k'] = computedDataLabel_k;
  })
}
computedDataLabel(chartData);
let pathLog: any = ref([]);
let isClickType = ref();
const computedChartData = (_d: any) => {
  return _d.map((v: any) => {
    return {
      ...v,
      value: v.children ? v.children.length : 1
    }
  })
}

let chartType = ref<string>('pie');
const handleClickSwitch = () => {
  chartType.value = chartType.value === 'pie' ? 'treemap' : 'pie';
  handleClickSwitchMethods();
}
const handleClickSwitchMethods = (_d = []) => {
  let myChart = echarts.getInstanceByDom(chart.value);
  let option_ = JSON.parse(JSON.stringify(myChart?.getOption()))
  let data_ = option_.series[0].data;
  if (_d && _d.length) data_ = _d;
  option_.series[0] = {...chartTypeConfig[chartType.value], data: data_}
  myChart?.setOption(option_);
}

// is 用来判断 内容页面返回后是否重新渲染
const initChart = () => {
  let myChart = echarts.init(chart.value);
  let data  = computedChartData(chartData)
  data.map((item: any) => {
    item['name'] = '🙂' + item.name;
    item['label'] = {
      position: ['50%', '50%'],
      formatter: (v: any) => {
        return `{text|${v.name}}`
      },
      rich: {
        text: {
          fontSize: 14,
          fontWeight: 600,
          color: "#666",      //颜色区分用于 getZr的事件判断
        },
      }
    }
  })
  pathLog.value.push({
    path: 'root',
    computedDataLabel_k: 'root',
    data
  });
  let optionConfig = chartTypeConfig[chartType.value];
  let option = {
    color: colors,
    series: [
      {
        type: chartType.value,
        id: 'chart',
        ...optionConfig,
        data: data
      }
    ]
  };
  myChart.setOption(option);
  window.addEventListener('resize', resizeEcharts);
  chartEventMethods(myChart);
}
const chartEventMethods = (myChart: any) => {
  let isRoot = false;
  myChart.getZr().on('click', (e: any) => {
    if (!e.target?.style) return;
    const {fill, text} = e.target.style;
    if (fill == '#666') {
      let routerPath = '';
      if (text == '🙂面试') {
        routerPath = '/question'
      } else if (text == '🙂知识') {
        routerPath = '/study'
      } else if (text == 'echarts') {
        routerPath = '/echarts'
      }
      router.push({
        path: routerPath
      })
      isRoot = true;
    }
  })
  myChart.on('click', (event: any) => {
    if (isRoot) {
      isRoot = false;
      return
    }
    ;

    const {children, name, computedDataLabel_k, key} = event.data;
    if (pathLog.value.length === 1) {
      isClickType.value = name;
    }
    if (children && children.length) {
      let cdata = computedChartData(children);
      let pathLog_computedDataLabel_ks = pathLog.value.map((v: any) => {
        return v.computedDataLabel_k;
      });
      if (!pathLog_computedDataLabel_ks.includes(computedDataLabel_k)) {
        pathLog.value.push({
          path: name,
          computedDataLabel_k,
          data: cdata
        });
      }
      myChart.setOption({
        series: [
          {
            id: 'chart',
            data: cdata
          }
        ]
      })
      return;
    }
    let routerPath = '/study/' + encodeURI(key);
    if (isClickType.value === '🙂知识') routerPath = '/study/' + encodeURI(key);
    else if (isClickType.value === '🙂面试') routerPath = '/question/' + encodeURI(key);
    else if (isClickType.value === '🙂echarts') routerPath = '/echarts'
    router.push({
      path: routerPath
    })
  })
}
const handleGoBack = () => {
  let len = pathLog.value.length;
  if (len <= 1) return;
  let backDetail = pathLog.value[len - 1 - 1];
  pathLog.value.splice(len - 1, 1);
  const {data} = backDetail;
  handleClickSwitchMethods(data);
}
const handleClickPathItem = (item: any, key: number) => {
  let last = pathLog.value[pathLog.value.length - 1];
  if (item.path === 'root') {
    pathLog.value.length = 1;
  }
  ;
  if (pathLog.value.length > 1 && item.computedDataLabel_k == last.computedDataLabel_k) return;
  pathLog.value.splice(key + 1, Infinity);
  handleClickSwitchMethods(item.data);
}
const resizeEcharts = () => {
  let myChart = echarts.getInstanceByDom(chart.value);
  if (myChart) {
    myChart.resize();
  }
}
// 用来判断是否第一次触发 onActivated 而渲染echarts;和onMounted冲突所加
let isOnActivated = ref(true);
onMounted(() => {
  initChart();
})
onDeactivated(() => {
  window.removeEventListener('resize', resizeEcharts, true);
})
onActivated(() => {
  if (isOnActivated.value) {
    isOnActivated.value = false;
    return;
  }
  let myChart = echarts.getInstanceByDom(chart.value);
  myChart?.clear();
  myChart?.dispose();
  chart.value.removeAttribute('style');
  chart.value.removeAttribute('_echarts_instance_');
  chart.value.removeAttribute('aria-label');
  requestAnimationFrame(() => {
    initChart(true);
  })
})

let isInitShowTip = ref(true);
const handleAnimationEnd = () => {
  isInitShowTip.value = false;
}
</script>

<style lang="less" scoped>
@keyframes hideTip {
  0% {
    opacity: 1;
  }

  80% {
    opacity: 1;
  }

  100% {
    opacity: 0;
  }
}

.home {
  width: 100%;
  height: 100vh;
  position: relative;

  .home_code {
    position: absolute;
    left: 0px;
    top: 0px;
  }

  .home_switch {
    width: 32px;
    height: 32px;
    border-radius: 6px;
    box-shadow: 0px 0px 8px #1677ff;
    z-index: 999999;
    font-size: 14px;
    background-color: white;
    margin-left: 1em;

    .home_switch_box {
      position: relative;
      width: 100%;
      height: 100%;
      border-radius: 6px;
      overflow: hidden;

      .home_switch_box_icon {
        position: absolute;
        z-index: 9;
        opacity: 0;
        transition: all .3s ease-in-out;
        transform: scale(0);
        display: flex;
        width: 100%;
        height: 100%;
        justify-content: center;
        align-items: center;

        img {
          width: 80%;
          display: block;
        }
      }

      .home_switch_box_iconShow {
        opacity: 1;
        transform: scale(1);
      }
    }
  }

  .home_tip {
    position: absolute;
    z-index: 999;
    font-size: 14px;
    font-weight: 600;
    color: #1677ff;
    top: 10px;
    left: 10px;
    animation: hideTip ease-in 5s forwards;

    nav {
      width: 100%;
      height: 20px;
      line-height: 20px;
    }
  }

  .home_path {
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
    z-index: 999;
    top: calc(50px - 32px);
    display: flex;
    justify-content: center;

    .home_path_box {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      background-color: white;
      height: 32px;
      padding: 0px 6px;
      border-radius: 6px;
      box-shadow: 0px 0px 8px #1677ff;

      .home_path_box_item {
        vertical-align: top;
        font-size: 14px;
        cursor: pointer;
        transition: all .3s linear;
        padding: 0px 2px;
        border-radius: 4px;
        font-weight: 600;
        padding-bottom: 2px;
      }

      .home_path_box_item:hover {
        background-color: #f0f0f0;
      }

      button {
        font-weight: 600;
      }
    }
  }


  .home_back {
    position: absolute;
    left: 50%;
    top: 2%;
    transform: translateX(-50%);
    cursor: pointer;
    z-index: 999999999999;
  }
}

.chart {
  width: 100vw;
  height: calc(100vh - 50px);
  position: absolute;
  bottom: 0px;
  z-index: 9999;
}
</style>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值