最近突发奇想,用 3D 的堆叠柱图,做了一个搭积木的小游戏。

主要思路

  1. 用一个几乎透明的 series-bar3D 铺满整个 grid3D,作为操作区,监听鼠标点击事件、完成堆积木的操作;
  2. 用多层数据为 0 的 series-bar3D 放在操作层 bar3D 下方,堆积木时,按照从下向上的顺序,更新其数据 series-bar3D.data(包括数值和样式,即 value 和 itemStyle);
  3. 用一个 series-heatmap 制作菜单,也是监听鼠标点击事件,实现撤销、重做、重置、修改积木样式(高度、颜色和透明度)等功能。

效果演示

家里的笔记本屏幕小,菜单按钮上的文字几乎全都显示不全了……

关键代码

  • 生成数据的部分
generateData = (length) => {let ret = {x: [],y: [],boxWidth: length,boxDepth: length,boxHeight: length,operatingSeriesData: [],brickSeriesData: []};let brickSeriesDataItem = [];for (let i = 0; i < length; i++) {ret.x.push('x_' + i);ret.y.push('y_' + i);for (let j = 0; j < length; j++) {ret.operatingSeriesData.push([i, j, 1]);brickSeriesDataItem.push({value: [i, j, 0]});}}for (let i = 0; i < length; i++) {ret.brickSeriesData[i] = JSON.parse(JSON.stringify(brickSeriesDataItem));}return ret;
};

由于一开始对 3D 堆叠柱图的堆叠机制了解不够深入(自以为是,没仔细看配置项手册[1],大家不要学我哈- -),所以一上来就把所有可能用到的砖块数据都生成出来了……也不管最终是否会用到。这里还有优化的空间……

  • series-heatmap.data 生成部分
generateMenuData = (colorList, sizeList) => {let ret = [];for (let i = 0; i < sizeList.length; i++) {ret.push({value: [i, 1, sizeList[i]],name: 'size',label: {show: true,color: 'black'},itemStyle: {color: 'gray'}});}for (let i = 0; i < colorList.length + 1; i++) {if (i === colorList.length) {ret.push({value: [i, 0, 1],name: 'empty',label: {show: true,color: 'black'},itemStyle: {color: '#FFF',opacity: 0.1}});continue;}ret.push({value: [i, 0, 1],name: 'color',label: {show: true,color: 'black'},itemStyle: {color: colorList[i]}});}ret.push({value: [0, 2, 1],name: 'undo',label: {show: true,color: 'black'},itemStyle: {color: 'gray'}}, {value: [1, 2, 1],name: 'redo',label: {show: true,color: 'black'},itemStyle: {color: 'gray'}}, {value: [2, 2, 1],name: 'reset',label: {show: true,color: 'black'},itemStyle: {color: 'gray'}}, {value: [3, 2, 1],name: 'save',label: {show: true,color: 'black'},itemStyle: {color: 'gray'}}, {value: [4, 2, 1],name: 'load',label: {show: true,color: 'black'},itemStyle: {color: 'gray'}});return ret;
};

  • option.series 生成
generateSeries = (src) => {ret = [];for (let i = 0; i < src.boxHeight; i++) {ret.push({type: 'bar3D',name: 'bricks',color: 'LawnGreen',data: src.brickSeriesData[i],bevelSize: i === 0 ? 0 : 0.2,bevelSmoothness: i === 0 ? 0 : 2,barSize: [1, 1],stack: 'stack',silent: true,shading: 'lambert',itemStyle: {opacity: i === 0? 1: 0}});}ret.push({type: 'bar3D',name: 'operatingSeries',data: src.operatingSeriesData,barSize: [1, 1],stack: 'stack',color: '#FFA',shading: 'lambert',label: {emphasis: {show: false}},itemStyle: {opacity: 0.01},emphasis: {itemStyle: {opacity: 1}}});ret.push({type: 'heatmap',name: 'menu',tooltip: {formatter: params => {if (params.name === 'color') {return `点击更换“积木”颜色为 ${params.color}`;}if (params.name === 'size') {return `点击更换“积木”高度为 ${params.value[2]}`;}return {undo: '撤销', redo: '重做', reset: '清空', save: '导出游戏数据,<br />供下次赋值给 loadData 使用', load: '功能开发中…' }[params.name];   }},label: {normal:{formatter: params => { if (params.name === 'color') {return params.color;}if (params.name === 'size') {return params.value[2];                        }return params.name;}}},itemStyle: {borderColor: '#AAA',borderWidth: 4},data: generateMenuData(menuConfig.colorList, menuConfig.sizeList)});return ret;
};

通过 tooltip.formatter 和 label.normal.formatter 定义按钮的文字和提示框内容

  • 撤销、重做函数定义
// 撤销
undo = () => {if (history.undoList.length === 0) {alert('操作历史记录为空,撤销未执行…');return console.log('操作历史记录为空,撤销未执行…');}// undoList 最后一条记录“剪切”到 redoListlet historyObj = history.undoList.pop();history.redoList.push(historyObj);// 将上一步操作/重做的 series[seriesIndex].data[dataIndex] 重置为初始值let val = series[historyObj.seriesIndex].data[historyObj.dataIndex].value;val[2] = 0;series[historyObj.seriesIndex].data[historyObj.dataIndex] = {value: val};myChart.setOption({series: series});console.log('撤销成功');
};// 重做
redo = () => {if (history.redoList.length === 0) {alert('操作历史记录为空,重做未执行…');return console.log('操作历史记录为空,重做未执行…');}// redoList 最后一条记录“剪切”到 undoListlet historyObj = history.redoList.pop();history.undoList.push(historyObj);// 将上一步重置的 series[seriesIndex].data[dataIndex] 重设为撤销前的状态series[historyObj.seriesIndex].data[historyObj.dataIndex].value[2] = historyObj.brickConfig.size;series[historyObj.seriesIndex].data[historyObj.dataIndex].itemStyle = {color: historyObj.brickConfig.color,opacity: historyObj.brickConfig.opacity};myChart.setOption({series: series});console.log('重做成功');
};// 撤销/重做 所用的操作历史记录
let history = {undoList: [],redoList: []
};

  • 鼠标单击事件监听处理
// 监听鼠标点击事件
myChart.on('click', params => {// 菜单操作处理if (params.seriesName === 'menu') {if (params.name === 'color') {brickConfig.color = params.color;brickConfig.opacity = 1;myChart.setOption({title:{subtext: `当前颜色:${brickConfig.color}n当前尺寸:${brickConfig.size}n当前透明度:${brickConfig.opacity}`}});return console.log(`砖块颜色更换为${params.color}`);}if (params.name === 'empty') {brickConfig.color = params.color;brickConfig.opacity = 0;myChart.setOption({title:{subtext: `当前颜色:${brickConfig.color}n当前尺寸:${brickConfig.size}n当前透明度:${brickConfig.opacity}`}});return console.log(`砖块颜色更换为透明`);}if (params.name === 'size') {brickConfig.size = params.value[2];myChart.setOption({title:{subtext: `当前颜色:${brickConfig.color}n当前尺寸:${brickConfig.size}n当前透明度:${brickConfig.opacity}`}});return console.log(`砖块 size 更换为${params.value[2]}`);}if (params.name === 'load') {// loadalert('开发中…');return console.log('开发中…');}if (params.name === 'reset') {data = generateData(xLength);series = generateSeries(data);myChart.setOption({series: series});return console.log('清空数据成功');}if (params.name === 'save') {let uri = 'data:application/json;base64,';//console.log(data);window.location.href = uri + base64(JSON.stringify(data));return console.log('导出数据成功');}if (params.name === 'undo') {return undo();}if (params.name === 'redo') {return redo();}}//alert(`正在 (${params.data[0]}, ${params.data[1]}) 处堆积一个砖块`);// 堆积木(砖块)操作处理for (let i in series) {if (series[i].name === 'bricks' && series[i].data[params.data[0] * xLength + params.data[1]].value[2] === 0) {series[i].data[params.data[0] * xLength + params.data[1]].value[2] = brickConfig.size;series[i].data[params.data[0] * xLength + params.data[1]].itemStyle = {color: brickConfig.color,opacity: brickConfig.opacity};history.undoList.push({seriesIndex: i, dataIndex: params.data[0] * xLength + params.data[1], brickConfig: JSON.parse(JSON.stringify(brickConfig)) // 深拷贝});history.redoList = [];return myChart.setOption({series: series}); }}
});

主要就是通过 echartsInstance.on 绑定事件处理函数,也就是 myChart.on('click', function(){}) 的形式。

ECharts Gallery​gallery.echartsjs.com

参考

  1. ^柱状图堆叠,相同 stack 值的柱状图系列数据会有叠加。注意不同系列需要叠加的数据项在数组中的索引必须是一样的。 https://echarts.apache.org/zh/option-gl.html#series-bar3D.stack

echarts树点击label事件_ECharts 堆积木(砖块)游戏相关推荐

  1. echarts中树图的label的点击_ECharts 堆积木(砖块)游戏

    最近突发奇想,用 3D 的堆叠柱图,做了一个搭积木的小游戏. 主要思路 用一个几乎透明的 series-bar3D 铺满整个 grid3D,作为操作区,监听鼠标点击事件.完成堆积木的操作: 用多层数据 ...

  2. echarts y轴添加点击事件_ECharts中的事件和行为

    用户在操作 Echarts 的图表时会触发相应的事件,这些事件由开发者监听,然后回调函数做出相应的处理,可以弹出一个对话框.跳转到一个地址或者做数据下钻等等. ECharts 3 中绑定事件跟 Ech ...

  3. echarts 地图去除点击事件_ECharts地图区域处理事件

    ECharts 中有两种方式的地图区域处理: 一种是在 geo 中: 另一种是在 series-map 中 geo 的情况下 : events.geoselectchanged   |  Event ...

  4. 【Echarts图例点击事件】自定义Echarts图例legend点击事件(已解决)

    目录 先睹为快(效果) 1.实现Echarts多条曲线 2.点击echarts触发接口请求 2.1 先默认隐藏部分数据 2.2 自定义legend图例点击事件 3.源码下载地址(解压即用) **[写在 ...

  5. 关于Echarts柱状图点击事件的实现方法charsjs柱状图点击事件

    关于Echarts柱状图点击事件的实现方法 charsjs柱状图点击事件 ajax请求数据 var chartDom = document.getElementById('riskmain'); va ...

  6. label 阻止冒泡 防止点击label 触发2次事件

    // 必须要把 jnput的外面的label加上事件阻止冒泡,否则点击label的时候,会冒泡到input上 再次触发input的点击事件$('.xt_order_cleft_modb_rl_dx') ...

  7. echarts点击图表事件和鼠标悬浮事件

    在使用echarts开发过程中,经常会遇到一些需求就是点击图表或者鼠标悬浮在图表上有接下来的相关动态操作,只需将获取的echarts实例添加监听事件即可实现. 鼠标点击echarts图表 const ...

  8. 如何实现echarts树点击节点名称也可以实现展开收起(直接更改options即可)

    产品有个需求,需要用echarts画一颗树,原型大概这样 然后看了下官网,echarts的树图大概如下,呃呃呃,差好多,那就改呗 然后一顿操作页面出来差不多了,思路大概如下,左边那个灰色按钮是自己写的 ...

  9. Echarts数据可视化event图表事件的相关操作,开发全解+完美注释

    全栈工程师开发手册 (作者:栾鹏) Echarts数据可视化开发代码注释全解 Echarts数据可视化开发参数配置全解 6大公共组件详解(点击进入): title详解. tooltip详解.toolb ...

最新文章

  1. 使用深度学习方法实现面部表情包识别
  2. python背景虚化
  3. 福利|PMCAFF问答专家群-社区大咖聚集地
  4. 跳一跳python源码_使用Python实现跳一跳自动跳跃功能
  5. Linux测试端口的连通性的四种方法
  6. c调用python脚本如何获取结果_使用C++调用Python代码的方法详解
  7. Nginx SSL+tomcat集群配置SSL,ngnix配置SSL后js/css访问出现404
  8. xbox360链接pc_如何将实时电视从Xbox One流式传输到Windows PC,iPhone或Android Phone
  9. 程序员必备工具包(实物)
  10. HDU1225 字符串
  11. 安卓学习第一课——电话拨号器
  12. 使用pagehelper踩的坑PageHelper cannot be cast
  13. 90%做tiktok运营都存在的误区?
  14. [zz]linux之sed用法
  15. ExtJS 在grid中想要取消checkbox选中的方法
  16. 【经验心得】每刷新一次页面就顺序更换一张图片的js特效
  17. 现代信号处理——参数估计理论(估计量的性能)
  18. 阐述游戏关卡设计中的色彩理论
  19. Java中new一个对象的过程
  20. Win10应用程序添加到右键中

热门文章

  1. 【技能提升】delegate为什么要用weak
  2. Oracle技术之Data Pump介绍
  3. 财务一体化项目,进度与计划17
  4. mysql text 最大长度 报错 Row size too large. The maximum row size for the used table type
  5. cpu密集型 计算密集型 io密集型 简介
  6. linux centos 修改主机名
  7. 漏洞高危 中危 低危的划分标准
  8. 高效的密码攻击方法:彩虹表
  9. 如何将WinDBG中命令的输出保存到文本文件中
  10. VS2010 VS2012 的快捷键