前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ECharts 堆积木(砖块)游戏

ECharts 堆积木(砖块)游戏

作者头像
ZXand618
发布2022-04-10 10:14:22
5010
发布2022-04-10 10:14:22
举报

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

主要思路

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

效果演示

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

关键代码

  • 生成数据的部分
代码语言:javascript
复制
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 生成部分
代码语言:javascript
复制
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 生成
代码语言:javascript
复制
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 定义按钮的文字和提示框内容

  • 撤销、重做函数定义
代码语言:javascript
复制
// 撤销
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: []
};
  • 鼠标单击事件监听处理
代码语言:javascript
复制
// 监听鼠标点击事件
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(){}) 的形式。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-06-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 ZXand618的ECharts之旅 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
图像处理
图像处理基于腾讯云深度学习等人工智能技术,提供综合性的图像优化处理服务,包括图像质量评估、图像清晰度增强、图像智能裁剪等。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档