最近突發(fā)奇想,用 3D 的堆疊柱圖,做了一個(gè)搭積木的小游戲。
主要思路
用一個(gè)幾乎透明的 series-bar3D 鋪滿整個(gè) grid3D,作為操作區(qū),監(jiān)聽鼠標(biāo)點(diǎn)擊事件、完成堆積木的操作;
用多層數(shù)據(jù)為 0 的 series-bar3D 放在操作層 bar3D 下方,堆積木時(shí),按照從下向上的順序,更新其數(shù)據(jù) series-bar3D.data(包括數(shù)值和樣式,即 value 和 itemStyle);
用一個(gè) series-heatmap 制作菜單,也是監(jiān)聽鼠標(biāo)點(diǎn)擊事件,實(shí)現(xiàn)撤銷、重做、重置、修改積木樣式(高度、顏色和透明度)等功能。
效果演示
家里的筆記本屏幕小,菜單按鈕上的文字幾乎全都顯示不全了……
關(guān)鍵代碼
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;};
柱狀圖堆疊,相同 stack 值的柱狀圖系列數(shù)據(jù)會有疊加。注意不同系列需要疊加的數(shù)據(jù)項(xiàng)在數(shù)組中的索引必須是一樣的。
https://echarts.apache.org/zh/option-gl.html#series-bar3D.stack
由于一開始對 3D 堆疊柱圖的堆疊機(jī)制了解不夠深入(自以為是,沒仔細(xì)看配置項(xiàng)手冊,大家不要學(xué)我哈-? -),所以一上來就把所有可能用到的磚塊數(shù)據(jù)都生成出來了……也不管最終是否會用到。這里還有優(yōu)化的空間……
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;};
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 `點(diǎn)擊更換“積木”顏色為 ${params.color}`; } if (params.name === 'size') { return `點(diǎn)擊更換“積木”高度為 ${params.value[2]}`; } return {undo: '撤銷', redo: '重做', reset: '清空', save: '導(dǎo)出游戲數(shù)據(jù),
供下次賦值給 loadData 使用', load: '功能開發(fā)中…' }[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 定義按鈕的文字和提示框內(nèi)容
// 撤銷undo = () => { if (history.undoList.length === 0) { alert('操作歷史記錄為空,撤銷未執(zhí)行…'); return console.log('操作歷史記錄為空,撤銷未執(zhí)行…'); } // undoList 最后一條記錄“剪切”到 redoList let 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('操作歷史記錄為空,重做未執(zhí)行…'); return console.log('操作歷史記錄為空,重做未執(zhí)行…'); } // redoList 最后一條記錄“剪切”到 undoList let historyObj = history.redoList.pop(); history.undoList.push(historyObj); // 將上一步重置的 series[seriesIndex].data[dataIndex] 重設(shè)為撤銷前的狀態(tài) 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: []};
// 監(jiān)聽鼠標(biāo)點(diǎn)擊事件myChart.on('click', params => { // 菜單操作處理 if (params.seriesName === 'menu') { if (params.name === 'color') { brickConfig.color = params.color; brickConfig.opacity = 1; myChart.setOption({title:{subtext: `當(dāng)前顏色:${brickConfig.color}\n當(dāng)前尺寸:${brickConfig.size}\n當(dāng)前透明度:${brickConfig.opacity}`}}); return console.log(`磚塊顏色更換為${params.color}`); } if (params.name === 'empty') { brickConfig.color = params.color; brickConfig.opacity = 0; myChart.setOption({title:{subtext: `當(dāng)前顏色:${brickConfig.color}\n當(dāng)前尺寸:${brickConfig.size}\n當(dāng)前透明度:${brickConfig.opacity}`}}); return console.log(`磚塊顏色更換為透明`); } if (params.name === 'size') { brickConfig.size = params.value[2]; myChart.setOption({title:{subtext: `當(dāng)前顏色:${brickConfig.color}\n當(dāng)前尺寸:${brickConfig.size}\n當(dāng)前透明度:${brickConfig.opacity}`}}); return console.log(`磚塊 size 更換為${params.value[2]}`); } if (params.name === 'load') { // load alert('開發(fā)中…'); return console.log('開發(fā)中…'); } if (params.name === 'reset') { data = generateData(xLength); series = generateSeries(data); myChart.setOption({series: series}); return console.log('清空數(shù)據(jù)成功'); } if (params.name === 'save') { let uri = 'data:application/json;base64,'; //console.log(data); window.location.href = uri + base64(JSON.stringify(data)); return console.log('導(dǎo)出數(shù)據(jù)成功'); } if (params.name === 'undo') { return undo(); } if (params.name === 'redo') { return redo(); } } //alert(`正在 (${params.data[0]}, ${params.data[1]}) 處堆積一個(gè)磚塊`); // 堆積木(磚塊)操作處理 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 綁定事件處理函數(shù),也就是 myChart.on('click', function(){}) 的形式。
?閱讀原文查看 ECharts Gallery 例子,強(qiáng)烈建議 PC?查看
總結(jié)
以上是生活随笔為你收集整理的echarts中树图的label的点击_ECharts 堆积木(砖块)游戏的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。