前言
-echarts实现一个饼图下钻的图表,带一个层级导航点击层级到指定的内容
使用到这些工具/框架库
vue3
https://cn.vuejs.orgvite
https://vitejs.cn/vite3-cn/guide/echarts
https://echarts.apache.org/zh/option.html#series-pieant design vue 4.x
https://www.antdv.com/components/breadcrumb-cn
演示视频
饼图下钻
实际用呢,用来跳转页面的,后面介绍跳过跳转页面;
有兴趣可查看demo仓库 gitee; 用到了vite插件vite-plugin-md
将.md
文件作为页面
一、准备
1.vite创建 vue3项目
- 打开
cmd
运行npm create vite@latest
回车 - 键盘按下
y
,输入项目名称echarts-demo
- 上下箭头选择
Vue
回车 - 上下箭头选择
TypeScript
回车 - 接着
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');
二、页面的布局
- 顶部有
层级导航面包屑
\饼图/矩形树图切换按钮
- 下方就是
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>
二、布局所需的变量,事件
- 层级导航通过变量
pathLog
渲染- 层级导航点击事件,通过下标 截取记录变量
pathLog
以更新记录 - 首先获取最后一个记录,用来判断当前点击和最后一项是否相等如果是则不再截取
- 判断是否为
root
这里表示根层级不在截取同时将记录变量pathLog
长度设置为1
- 最后截取当前点击的下标向后的所有元素,以更新记录
- 层级导航点击事件,通过下标 截取记录变量
chartType
用来表示图表类型是饼图\矩形树图
- 通过点击切换按钮更新该值
<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
chartTypeConfig
echarts饼图\矩形树图
配置信息对象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
- 声明
initChart
函数 - 通过ref获取容器元素,并实例化echarts实例
myChart
- 声明
echarts
图表数据变量data
’computedChartData
函数设置饼图数值- 默认每一个都是1,
children
的长度值 - 主要用来表示占比
- 默认每一个都是1,
- 这里初始层级就是根层级的每一项加上😊用来表示根
- 设置饼图每一项
label
样式
- 将跟层级添加到记录数组变量
pathLog
中 - 通过图表类型变量
chartType
和常量chartTypeConfig
获取指定图表类型配置信息 - 声明
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.图表中每一项的事件添加
- 声明一变量
isRoot
用来表示是否点击了设置😊的根图元素label
- 加该变量主要是因为 点击
label
同样触发myChart.on
事件这里做判断使用
- 加该变量主要是因为 点击
myChart.getZr().on
绑定视图任意位置的点击事件- 通过rich设置的样式,用来判断是否为
label
- 点击带有😊的直接跳转该类型页面,不用跳转到下级子页面
- 通过rich设置的样式,用来判断是否为
- 设置图表 每一项的点击事件
- 判断变量
isRoot
以判断终止下方代码执行
- 判断变量
- 如果点击的为图表每一项而不是
带😊的label
- 获取事件对象的
event.data
结构出该对象下的children, name, computedDataLabel_k, key
- 调用该方法
computedChartData
传入children
设置占比数值 isClickType
变量 用来表示点击那个根目录下的子集,用来跳转路由使用- 判断记录变量
pathLog
内是否含有了当前的computedDataLabel_k
- 没有则将该key的对象加入到记录中
- 通过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>