本文分享自华为云社区《没想到吧!这个可可爱爱的游戏居然是用ECharts实现的!云社区华为云》,作者:DevUI。前言 echarts是一个很强大的图表库,除了我们常见的图表功能,echarts有一个自定义图形的功能,这个功能可以让我们很简单地在画布上绘制一些非常规的图形,基于此,我们来玩一些花哨的。 FlappyBird小游戏体验地址(看看你能玩几分): foolmadao。github。ioechartflap 下面我们来一步步实现他。1在坐标系中画一只会动的小鸟 首先实例化一个echart容器,再从网上找一个像素小鸟的图片,将散点图的散点形状,用自定义图片的方式改为小鸟。constmyChartecharts。init(document。getElementById(main));option{series:〔{name:bird,type:scatter,symbolSize:50,symbol:image:bird。png,data:〔〔50,80〕〕,animation:false},〕};myChart。setOption(option); 要让小鸟动起来,就需要给一个向右的速度和向下的加速度,并在每一帧的场景中刷新小鸟的位置。而小鸟向上飞的动作,则可以靠角度的旋转来实现,向上飞的触发条件设置为空格事件。option{series:〔{xAxis:{show:false,type:value,min:0,max:200,},yAxis:{show:false,min:0,max:100},name:bird,type:scatter,symbolSize:50,symbol:image:bird。png,data:〔〔50,80〕〕,animation:false},〕};设置速度和加速度leta0。05;letvh0;letvw0。5timersetInterval((){小鸟位置和仰角调整vhvha;option。series〔0〕。data〔0〕〔1〕vh;option。series〔0〕。data〔0〕〔0〕vw;option。series〔0〕。symbolRotateoption。series〔0〕。symbolRotate?option。series〔0〕。symbolRotate5:0;坐标系范围调整option。xAxis。minvw;option。xAxis。maxvw;myChart。setOption(option);},25); 效果如下 2用自定义图形绘制障碍物 echarts自定义系列,渲染逻辑由开发者通过renderItem函数实现。该函数接收两个参数params和api,params包含了当前数据信息和坐标系的信息,api是一些开发者可调用的方法集合,常用的方法有:api。value(),意思是取出dataItem中的数值。例如api。value(0)表示取出当前dataItem中第一个维度的数值。api。coord(),意思是进行坐标转换计算。例如varpointapi。coord(〔api。value(0),api。value(1)〕)表示dataItem中的数值转换成坐标系上的点。api。size(),可以得到坐标系上一段数值范围对应的长度。api。style(),可以获取到series。itemStyle中定义的样式信息。 灵活使用上述api,就可以将用户传入的Data数据转换为自己想要的坐标系上的像素位置。 renderItem函数返回一个echarts中的graphic类,可以多种图形组合成你需要的形状,graphic类型。对于我们游戏中的障碍物只需要使用矩形即可绘制出来,我们使用到下面两个类。type:group,组合类,可以将多个图形类组合成一个图形,子类放在children中。type:rect,矩形类,通过定义矩形左上角坐标点,和矩形宽高确定图形。数据项定义为〔x坐标,下方水管上侧y坐标,上方水管下侧y坐标〕data:〔〔150,50,80〕,。。。〕renderItem:function(params,api){获取每个水管主体矩形的起始坐标点letstart1api。coord(〔api。value(0)10,api。value(1)〕);letstart2api。coord(〔api。value(0)10,100〕);获取两个水管头矩形的起始坐标点letstartHead1api。coord(〔api。value(0)12,api。value(1)〕);letstartHead2api。coord(〔api。value(0)12,api。value(2)8〕)水管头矩形的宽高letheadSizeapi。size(〔24,8〕)水管头矩形的宽高letrectapi。size(〔20,api。value(1)〕);letrect2api。size(〔20,100api。value(2)〕);坐标系配置constcommon{x:params。coordSys。x,y:params。coordSys。y,width:params。coordSys。width,height:params。coordSys。height}水管形状constrectShapeecharts。graphic。clipRectByRect({x:start1〔0〕,y:start1〔1〕,width:rect〔0〕,height:rect〔1〕},common);constrectShape2echarts。graphic。clipRectByRect({x:start2〔0〕,y:start2〔1〕,width:rect2〔0〕,height:rect2〔1〕},common)水管头形状constrectHeadShapeecharts。graphic。clipRectByRect({x:startHead1〔0〕,y:startHead1〔1〕,width:headSize〔0〕,height:headSize〔1〕},common);constrectHeadShape2echarts。graphic。clipRectByRect({x:startHead2〔0〕,y:startHead2〔1〕,width:headSize〔0〕,height:headSize〔1〕},common);返回一个group类,由四个矩形组成return{type:group,children:〔{type:rect,shape:rectShape,style:{。。。api。style(),lineWidth:1,stroke:000}},{type:rect,shape:rectShape2,style:{。。。api。style(),lineWidth:1,stroke:000}},{type:rect,shape:rectHeadShape,style:{。。。api。style(),lineWidth:1,stroke:000}},{type:rect,shape:rectHeadShape2,style:{。。。api。style(),lineWidth:1,stroke:000}}〕};}, 颜色定义,我们为了让水管具有光泽使用了echarts的线性渐变色对象。itemStyle:{渐变色对象color:{type:linear,x:0,y:0,x2:1,y2:0,colorStops:〔{offset:0,color:ddf38c0处的颜色},{offset:1,color:587d2a100处的颜色}〕,global:false缺省为false},borderWidth:3}, 另外,用一个for循环一次性随机出多个柱子的数据functioninitObstacleData(){添加minHeight防止空隙太小letminHeight20;letstart150;obstacleData〔〕;for(letindex0;index50;index){constheightMath。random()30minHeight;constobstacleStartMath。random()(90minHeight);obstacleData。push(〔start50index,obstacleStart,obstacleStartheight100?100:obstacleStartheight〕)}} 再将背景用游戏图片填充,我们就将整个游戏场景,绘制完成: 3进行碰撞检测 由于飞行轨迹和障碍物数据都很简单,所以我们可以将碰撞逻辑简化为小鸟图片的正方形中,我们判断右上和右下角是否进入了自定义图形的范围内。 对于特定坐标下的碰撞范围,因为柱子固定每格50坐标值一个,宽度也是固定的,所以,可碰撞的横坐标范围就可以简化为(x501)0。6 在特定范围内,依据Math。floor(x50)获取到对应的数据,即可判断出两个边角坐标是否和柱子区域有重叠了。在动画帧中判断,如果重叠了,就停止动画播放,游戏结束。centerCoord为散点坐标点functionjudgeCollision(centerCoord){if(centerCoord〔1〕0centerCoord〔1〕100){returnfalse;}letcoordList〔〔centerCoord〔0〕15,centerCoord〔1〕1〕,〔centerCoord〔0〕15,centerCoord〔1〕1〕,〕for(leti0;i2;i){constcoordcoordList〔i〕;constindexcoord〔0〕50;if(index10。6obstacleData〔Math。floor(index)3〕){if(obstacleData〔Math。floor(index)3〕〔1〕coord〔1〕obstacleData〔Math。floor(index)3〕〔2〕coord〔1〕){returnfalse;}}}returnfalse}functioninitAnimation(){动画设置timersetInterval((){小鸟速度和仰角调整vhvha;option。series〔0〕。data〔0〕〔1〕vh;option。series〔0〕。data〔0〕〔0〕vw;option。series〔0〕。symbolRotateoption。series〔0〕。symbolRotate?option。series〔0〕。symbolRotate5:0;坐标系范围调整option。xAxis。minvw;option。xAxis。maxvw;碰撞判断constresultjudgeCollision(option。series〔0〕。data〔0〕)if(result){产生碰撞后结束动画endAnimation();}myChart。setOption(option);},25);}总结 echarts提供了强大的图形绘制自定义能力,要使用好这种能力,一定要理解好数据坐标点和像素坐标点之间的转换逻辑,这是将数据具象到画布上的重要一步。 运用好这个功能,再也不怕产品提出奇奇怪怪的图表需求。 源码地址:github。comfoolmadaoe 点击下方,第一时间了解华为云新鲜技术 华为云博客大数据博客AI博客云计算博客开发者中心华为云