ECharts 堆積木(磚塊)遊戲

最近突發奇想,用 3D 的堆疊柱圖,作了一個搭積木的小遊戲。html

 

主要思路apache

 

  1. 用一個幾乎透明的 series-bar3D 鋪滿整個 grid3D,做爲操做區,監聽鼠標點擊事件、完成堆積木的操做;json

  2. 用多層數據爲 0 的 series-bar3D 放在操做層 bar3D 下方,堆積木時,按照從下向上的順序,更新其數據 series-bar3D.data(包括數值和樣式,即 value 和 itemStyle);api

  3. 用一個 series-heatmap 製做菜單,也是監聽鼠標點擊事件,實現撤銷、重作、重置、修改積木樣式(高度、顏色和透明度)等功能。數組

 

 

效果演示微信

 

家裏的筆記本屏幕小,菜單按鈕上的文字幾乎全都顯示不全了……app

 

關鍵代碼echarts

 

  • 生成數據的部分函數







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 值的柱狀圖系列數據會有疊加。注意不一樣系列須要疊加的數據項在數組中的索引必須是同樣的。優化

https://echarts.apache.org/zh/option-gl.html#series-bar3D.stack

因爲一開始對 3D 堆疊柱圖的堆疊機制瞭解不夠深刻(自覺得是,沒仔細看配置項手冊,你們不要學我哈-  -),因此一上來就把全部可能用到的磚塊數據都生成出來了……也無論最終是否會用到。這裏還有優化的空間……

 

  • 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 最後一條記錄「剪切」到 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('操做歷史記錄爲空,重作未執行…'); return console.log('操做歷史記錄爲空,重作未執行…'); }  // redoList 最後一條記錄「剪切」到 undoList let 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') { // load alert('開發中…'); 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 例子,強烈建議 PC 查看

本文分享自微信公衆號 - ZXand618的ECharts之旅(ZXand618)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索