摸鱼的时候玩一个小游戏吧。

来自:    更新日期:早些时候
~ 前言

因为最近在忙着找工作和做些其他有的没的事情,东西多了容易忘记,但写代码还是每天的事,再熟悉了一段时间的公司架构代码之后,撑着闲暇空隙做了这个小游戏锻炼一下玩玩。起因是因为在一次的面试过程中,一个面试官拿着个这个游戏跟我说这个游戏里面的实现和思路,当时也说了一部分,很明显的被遭到了鄙视,但这都无所谓,我也只是简单的讲了一下,具体还是要做的时候去思考,临场的说辞也不能说明什么,只能说思维不敏捷,思考不周到,我始终觉得做过就有印象,即使看的再多没有实战和手写一步步实现,没过多久肯定会忘记。面的时候问了一些无关紧要的问题,我也感觉到各自相互看不上,也就不提了,但这个他拿出来的这个游戏我算是记住了玩法,刚好前几天在刷短视频的时候看到这个小程序,让我想起之前的插曲,撑着这几天的五一放假写了这个小游戏。

了解小游戏玩法

先看封面动图,了解一下玩法内容;

随机创建四个瓶子并且随机的颜色块

点击瓶子时把当前颜色块的最上一个放到另外一个瓶子的最上面

如果当前的颜色大于2是一样的则叠加倒入到另一个瓶子的最上面

可以返回上一步历史记录操作

切换难度0(普通),1(中等),2(困难),3(非常难)

记录多少步长

整理上面的需求和思路,可以试着写出来。

拆分需求内容

面对上面给出来的问题我这边简单的做一个需求的拆分,以便写起来的时候不会没有思路。首先想到的是这个游戏的核心内容就是颜色的替换和转变,但本质上还是考验数组之间的变化和获取,知道这个了其实不难,难在第一步如果处理数组和获取数组中的数据。

当游戏开始时生成一个数组constcolorArr=[]维护多个试管(图中就四个,三个有颜色的一个空的),然后再根据固定的颜色(可以是外边传的颜色,也可以写成固定多个颜色)生成十二种颜色,当然这个些颜色需要四个四个一样的,加上最后的透明色transparent总共十二种(对于动图而言),再把全部的颜色打乱之后在切割数组成四份,拿到数据就可以做一个Dom的循环渲染了,渲染的过程中加上各个Dom元素的监听事件和change事件,当点击瓶子时可以做一个Flag判断是否是第一次点击和第二次点击,在第二次点击的时候再做数组的替换和改变Dom重新渲染页面。差不多总体上就这么简单了,具体的功能处理下面细说。

初始化操作

这里我用的Es6的class写的插件,具体看代码;

class?ColorSort?{??//?默认参数??static?DEFAULT_OPTIONS?=?{????el:?null,?//?游戏的容器????level:?0,?//?难度????num:?4??//?初始化的颜色块数量??}??constructor(options)?{????this.options?=?Object.assign({},?ColorSort.DEFAULT_OPTIONS,?options)????this.colorArr?=?[]????this.init()??}??init()?{????//?创建?DOM?元素渲染????this.options.el.innerHTML?=?`????????<div?class="color-sort__btns">??????????<div?class="btn?color-sort__prev">上一步</div>??????????<div?class="btn?color-sort__reset">重置</div>??????????<div?class="btn?color-sort__restart">新开</div>??????????<div?class="btn?color-sort__level">????????????<select?class="color-sort__select">??????????????<option?value="0">初级</option>??????????????<option?value="1">中级</option>??????????????<option?value="2">困难</option>??????????????<option?value="3">特难</option>????????????</select>??????????</div>????????</div>????????<div?class="color-sort__wrapper"></div>????????<div?class="color-sort__step">0</div>???????`????//?创建随机颜色????this.colorArr?=?this.randomColorArr()????//?渲染界面????this.render()??}}

获取随机颜色

根据分析可知,在渲染Dom之前需要去随机获取颜色数据操作,每个颜色随机并分布到三个瓶子内排列,通过拿到的数据可以循环渲染。

//?重新渲染?colorArr?数组randomColorArr()?{??//?保存所有瓶子颜色的栈??let?arr?=?[]??//?根据等级创建多个瓶子,并且保证最后一个瓶子为空??for?(let?i?=?0;?i?<?this.options.level?+?3;?i++)?{????const?color?=?this._createEmpty()????arr?=?arr.concat(color)??}??//?打乱颜色??const?colorShuffle?=?this._shuffle(arr)??//?重组颜色??const?colorArr?=?this._rebuild(colorShuffle)??//?在后面添加一个空的瓶子??colorArr.push(this._createEmpty('transparent'))??return?colorArr}

获取随机的颜色操作,根据等级this.options.level创建多个瓶子,并且保证最后一个瓶子为空,通过createEmpty方法创建随机的颜色,然后再_shuffle打乱颜色。

//?打乱颜色_shuffle(arr)?{??//?复制一份数组??let?_arr?=?arr.slice()??for?(let?i?=?0;?i?<?_arr.length;?i++)?{????let?j?=?this._getRandomInt(0,?i)????let?t?=?_arr[i]????_arr[i]?=?_arr[j]????_arr[j]?=?t??}??return?_arr}//?获取一个随机数,区间是?[min,?max)_getRandomInt(min,?max)?{??return?Math.floor(Math.random()?*?(max?-?min?+?1)?+?min)}

拿到打乱之后的颜色再根据一个瓶子内有多少颜色数用slice()方法进行切割(这里传的是num为4),通过_rebuild方法切割之后重组。这里可以说一个知识点,拿到的数组需要进行slice()或者...方法做一个拷贝,避免传来的数据污染,但是slice()里面如果还有引用类型的话就可以用JSON.parse(JSON.stringify(arr))做一个拷贝处理,然后再返回一个新的重组之后的值。

//?重组颜色_rebuild(arr)?{??const?result?=?[]??const?_arr?=?arr.slice()??const?group?=?_arr.length?/?this.options.num??//?做一下数据切割,保证每个瓶子有?5?个颜色??for?(let?i?=?0;?i?<?group;?i++)?{????result.push(_arr.slice(i?*?this.options.num,?(i?+?1)?*?this.options.num))??}??return?result}

重组之后的值还缺少一个空的数据瓶子,所以需要在后面需要加一个transparent的空瓶子。做好数据的初次处理之后,就开始下面的页面渲染。

页面渲染

先把Dom区域通过querySelector('.color-sort__wrapper')获取一下,然后wrapper.appendChild()插入到区域内,最后再插入到传进来的el中。

在做渲染之前思考一个问题,怎么让瓶子按照一行3个的排列方式往下布局?

其实很简单,可以通过%取余的方式拿到当前循环的索引取余数是否等于0,当结果等于0时,可以做一个外部变量index++当然第一次的索引不计算在内,得到的余数的值可以作为left的偏移量计算,看下面具体实现。

render()?{??const?colorArr?=?this.colorArr??//?一行有?3?个??const?col?=?3??//?用于计算的变量值??let?index?=?0??for?(let?i?=?0;?i?<?colorArr.length;?i++)?{????const?calc?=?i?%?col????if?(calc?===?0?&&?i?!==?0)?{??????index++????}????//?创建瓶子?????const?item?=?document.createElement('div')????item.className?=?'color-sort__item'????item.style.height?=?`${height}px`????//?...??}}

在上面的图上也可以看到,到了颜色的过程当中是有一个动画的效果的,就是比喻“瓶子的水减少和增加”的动画。一开始在做的时候没考虑那么多,用的position:absolute定位,然后加上瓶内的单个颜色根据top值做一个定位排布,但是那样做完之后就发现,在做动画的时候出现了比较麻烦的事,解决方法就是新创建一个dom元素覆盖到颜色上,在把原本的颜色设置为透明或者去掉,再通过上面的dom的高度递减到height:0即可,这样的话就需要多操作几步还比较麻烦,所以想了想,换种思路。

可以通过z-index加上bottom:0定位到底部的方式一开始把高度都写好,然后再去高度递减不就可以了嘛,看下面的图的解释效果。

这样之后需要改变对应的第一个的高度即可实现动画效果,如果使用第一种方案的话操作起来相对麻烦。

//?单个色块的?宽度static?WIDTH?=?40//?单个色块的?高度static?HEIGHT?=?30//?渲染render()?{??const?colorArr?=?this.colorArr??const?wrapper?=?this.options.el.querySelector('.color-sort__wrapper')??//?计算需要有多少列,如果超出颜色数组长度大于?6?就改成一行?4?个(纯粹是为了好看点)??const?col?=?colorArr.length?>?6???4?:?3??//?单个瓶子的间距??const?space?=?40??//?单个瓶子上方的空白间隙??const?padding?=?10??//?计算一个瓶子的高度??const?height?=?this.options.num?*?ColorSort.HEIGHT?+?padding??//?用于计算的变量值??let?index?=?0??//?创建瓶子??for?(let?i?=?0;?i?<?colorArr.length;?i++)?{????const?calc?=?i?%?col????if?(calc?===?0?&&?i?!==?0)?{??????index++????}????//?创建一个瓶子????const?item?=?document.createElement('div')????item.className?=?'color-sort__item'????item.style.height?=?`${height}px`????item.style.top?=?`${(height?*?index)?+?(space?*?index)}px`????item.style.left?=?`${(calc?*?ColorSort.WIDTH)?+?(calc?*?space)}px`????//?绑定点击事件????this._addEvent(item,?'click',?this.handleClick.bind(this,?item,?i))????for?(let?j?=?0;?j?<?this.options.num;?j++)?{??????const?color?=?document.createElement('div')??????color.className?=?'color'??????color.style.height?=?`${(this.options.num?-?j)?*?ColorSort.HEIGHT}px`??????color.style.zIndex?=?j??????color.style.backgroundColor?=?colorArr[i][j]??????item.appendChild(color)????}????wrapper.appendChild(item)??}??//?给父级元素添加宽高??wrapper.style.width?=?`${ColorSort.WIDTH?*?col?+?(col?-?1)?*?space}px`??wrapper.style.height?=?`${height?*?(index?+?1)?+?index?*?space}px`??//?最后插入到?dom?中??this.options.el.appendChild(wrapper)??//?结束动画状态??ColorSort.isMoving?=?false}

最后在给其父级加上宽高实现动态的上下左右居中,当然父级还要加上下面css代码;

.color-sort?.color-sort__wrapper?{??position:?absolute;??top:?50%;??left:?50%;??transform:?translate(-50%,?-50%);}

上面在创建瓶子的dom时就做了绑定事件handleClick的处理,接下来开始做点击切换数据的操作了,针对第一次点击和第二次点击分别做不同的处理。

点击瓶子操作

分析一下需求点击操作:

判断第二次点的瓶子是否满格(满格为this.options.num次),如果满格,置换成第一次点击

第一次点击的最上面的瓶子内颜色应该,加入到第二次点击的瓶子最上面的颜色中

如果第一次点击的瓶子上面同时有两个或者两个以上的相同的颜色,则再第二次点击过程中把相同的颜色都倒入到下个瓶子上面

点击之后数据改变,重新渲染操作

保存历史记录

//?点击的次数,是否是第一次点击(为?0)或者第二次点击(为?1)static?flag?=?0static?firstDom?=?nullstatic?secondDom?=?nullstatic?firstIndex?=?0static?secondIndex?=?0static?firstPostion?=?{}//?点击事件handleClick(dom,?index)?{??//?动画正在运行时不能点击??if?(ColorSort.isMoving)?return??//?第一次点击??if?(ColorSort.flag?===?0)?{????//?如果点击的是空瓶子,则不做任何操作????let?isOk?=?this.colorArr[index].find((item)?=>?item?!==?'transparent')????if?(!isOk)?{??????alert('第一次不能点击的是空瓶子哦~')??????return????}????this.setFirstCommon(dom,?index)??}??//?第二次点击??else?if?(ColorSort.flag?===?1)?{????ColorSort.isMoving?=?true????ColorSort.secondIndex?=?index????ColorSort.flag?=?0????ColorSort.secondDom?=?dom????ColorSort.firstDom.style.transform?=?'scale(1)'????//?...??}}//?设置初始属性setFirstCommon(dom,?index)?{??ColorSort.isMoving?=?false??ColorSort.firstIndex?=?index??ColorSort.flag?=?1??ColorSort.firstDom?=?dom??//?保存第一次点击的位置??ColorSort.firstPostion?=?{????top:?dom.offsetTop,????left:?dom.offsetLeft,??}??ColorSort.firstDom.style.transform?=?'scale(1.08)'}

处理第二次点击的时候单独说一下,在确保可以点击的条件有一个,第二次点击的瓶子内透明色块的个数一定是大于或者等于第一次点击的瓶子内首个有颜色的个数值且颜色相同,除此之外的所有情况都可点击,当然不包括边界条件(动画中或者成功后)。

如果想要处理颜色之间的转换,就需要找到两个瓶子中从上到下首次出现色块的位置,即色块的索引值,相同色块的个数和色块的颜色,三个关键的数据。

简单来说,如何要在一个数组里面找出第一个相同的数据那一项?

比如constarr=['transparent','red','red','pink','green'],找出red项位于数组的多少索引,后面跟了多少个red的个数,以及red这个颜色。

//?获取点击时瓶子内的最上层颜色(**关键代码**)_getColor(arr)?{??const?obj?=?{????index:?arr.length,????count:?arr.length,????color:?'transparent'??}??for?(let?i?=?0;?i?<?arr.length;)?{????if?(arr[i]?===?'transparent')?{??????i++??????continue????}????let?count?=?0????for?(let?j?=?i;?j?<?arr.length;?j++)?{??????if?(arr[i]?===?arr[j])?{????????count++??????}?else?{????????break??????}????}????obj.color?=?arr[i]?//?透明色之后出现的第一个颜色值????obj.count?=?count?//?连续出现相同颜色的次数????obj.index?=?i?//?可以代表透明色之后出现的第一个颜色的索引,也可以代表透明的颜色的个数????break??}??return?obj}

有了上面的工具函数之后就可以做下面的数据操作了

//?取出第一次点击的数组const?firstArr?=?this.colorArr[ColorSort.firstIndex]//?取出第二次点击的数组const?secondArr?=?this.colorArr[ColorSort.secondIndex]//?第一次点击的数组下首个有颜色的色块的?索引,个数和颜色值const?firstColor?=?this._getColor(firstArr)const?secondColor?=?this._getColor(secondArr)if?(ColorSort.firstIndex?===?ColorSort.secondIndex)?{??this.setFirstCommon(dom,?index)??return}//?当第一次点击的颜色个数大于第二次点击的瓶子内的透明颜色个数时,判断是超出的,则重新设置成第一次点击的瓶子if?(firstColor.count?>?secondColor.index)?{??this.setFirstCommon(dom,?index)??return}//?先取出第二次点击的透明颜色的个数,即,等于第一次点击的相同颜色的个数const?transparentArr?=?secondArr.slice(0,?firstColor.count)const?firstArrDel?=?firstArr.splice(??firstColor.index,??firstColor.count,??...transparentArr)secondArr.splice(??secondColor.index?-?firstColor.count,??firstColor.count,??...firstArrDel)//?给第一次的操作的数据修改this.colorArr.splice(ColorSort.secondIndex,?1,?firstArr)//?给第二次的操作的数据修改this.colorArr.splice(ColorSort.secondIndex,?1,?secondArr)//?添加动画this.animate(firstColor,?secondColor)

至此所有点击操作相关的切换数据已完成,然后再加上瓶子移动的动画和颜色高度为0的动画处理。

执行动画

第二次可以点击的瓶子的条件是必须包含有一个或者多个transparent色块,想要添加新的色块到第二个瓶子中有三种情况;

第一,相同颜色的添加,就是第一次和第二次的瓶子内第一个颜色都相同

第二,不同颜色的添加

第三,添加到空瓶中

第一中的做法很简单就是在第二个瓶子中找到第一个颜色块,给其高度改变就可以,第二种如果要改变高度的话颜色就对不上了,第三个需要重新设置颜色和高度。所以综合考虑的想还是直接去第二次点击瓶子中的最开始的一项transparent给其高度设置为0,然后设置颜色,再延迟设置高度改变实现动画。

//?重新渲染?colorArr?数组randomColorArr()?{??//?保存所有瓶子颜色的栈??let?arr?=?


摸鱼的时候玩一个小游戏吧。视频

相关评论:
  • 13978053699这款流行了30年的小游戏,蕴含着大智慧
    山哪心时至今日,这四款承载了无数人办公室摸鱼和休闲 娱乐 的 游戏 ,成为了全球最流行的电子 游戏 之一。而当初开发windows纸牌的程序员也确实是想给自己找点乐子(摸鱼)才开发的这款 游戏 。结果无心插柳柳成荫。要说《纸牌》并不是微软首创,在我小的时候我外婆就经常拿着扑克牌在那里玩。玩法嘛有...

  • 13978053699啫喱游戏怎么玩如何下载
    山哪心一款全新的休闲小游戏,将社交与手游集为一体,采用全新的玩法,为用户带来了新鲜趣味的游戏体验,在这里用户可以自由与好友组队打造独特的空间,用装饰的物品说话,与好友一起度过休闲娱乐的时间,整体玩起来趣味性十足,喜欢的朋友可以来下载试试~官方介绍啫喱是只属于你和你最好朋友的“友情公寓”,在...

  • 13978053699爸爸妈妈小时候玩什么游戏
    山哪心爸爸妈妈小时候常见的游戏是玩《抓石子》。游戏玩法:8颗石子(或用布做的小沙包)全部抓在一个手中,往上抛,此时手掌迅速翻过来,让一颗石子落于手背上(如果落在手背上有多颗石子,就把其它的石子抖落到地上;手背上只剩一颗石子),然后手背把石子往上抛,此时手背迅速翻过来,用手心接着石子,...

  • 13978053699为什么公司大了,摸鱼的人就多了?
    山哪心要说真是公司冗余的人员太多吧,你又解释不了,这些大公司一边抱怨公司人效下降,上班摸鱼的人多了;但是又一边拼命的高薪招聘,公司规模不但没有缩减,反而还在迅速的扩张。那么为啥会出现这个现象呢?核心岗位是稀缺的 第一个原因就是:无论公司规模多小,还是多大,真正的核心岗位就那么几个,是非常...

  • 13978053699几款周末宅家必备APP精致女孩值得拥有
    山哪心打工人一定懂得竖屏的游戏有多么的神仙!!。类似于QQ农场的种地小游戏,里面的农作物的 成熟时间是比较快的,不会说你今天种的东西到 了第二天才能够摘取,这样摸鱼才有乐趣。而且画风治愈可爱,看着宠物和植物一点点长 大,有了陪伴和收获,也是成就感满满。【虚拟试衣间】让自己成为穿搭形象设计师...

  • 13978053699适当摸鱼会有助于提升学习\/工作的效率么?
    山哪心“摸鱼”源自于成语浑水摸鱼,原本指在水中捉鱼,代指趁乱获取利益。现在,摸鱼多指该做正事的时候不做正事,悄悄的偷懒。表面上是在上网课,其实手机显示着游戏界面;表面上在认真听老师讲课,其实脑子里已经把好吃的来回想了遍,思考着中午要哪一个老板的生意……然而这些事,都只能偷偷做,如果有一天,...

  • 13978053699你上班划水最喜欢干什么?
    山哪心与同事聊天,增进感情 在工作中,有时候与同事闲聊一下也是再正常不过的事情了。有了自由的小聊天时间,我与同事之间产生了更多的共鸣和默契。我们可以谈论工作,也可以谈论感兴趣的话题。通过与同事交流,我发现我们更容易形成团队合作的默契,这对于工作的开展有着极为积极的影响。综述:每个人在工作中都...

  • 13978053699十大耐玩的单机手游哪个好玩
    山哪心愤怒的小鸟游戏一款最初始的休闲益智的游戏,这是很多九零后的都会游玩的经典小游戏,在这款版本是绝对的是还原最初始的玩法和关卡,玩家要控制小鸟发射击败所有的小猪,玩家在快速的体验最棒的射击经典玩法,快来一起体验这款游戏吧!美味星球正版是一款非常能释放缓解压力的很好玩的小游戏。美味星球正版这...

  • 13978053699在小游戏《武林外传》同福奇缘中,小贝的童心怎么增加啊?
    山哪心去那个打烊之后也不走的人那儿,买糖葫芦卷;十八里铺淘宝如果淘到玩偶偶像就可以加童心;"到处逛逛'去后院或者大堂,然后点播放,可能会出现由小贝颁发的"诺贝尔XX奖"。其实买过一次糖葫芦后就可以摸鱼了,建议你多摸鱼!找小朋友玩也可以

  • 13978053699生活中的小游戏
    山哪心我记得我大学时玩过两个小游戏:1.你在心中想好一个规则,让别人来猜。例如:我想好了一个饮料的规则“名称带有颜色的饮料”(比如:白酒、绿茶、黄瓜汁都符合规则,而其他什么康师傅、苹果汁、酒都不行),可以让你的同学挨个报一种饮料,你根据自己定的规则回答他“是”或者“不是”,大家一起猜...

  • 相关主题精彩

    版权声明:本网站为非赢利性站点,内容来自于网络投稿和网络,若有相关事宜,请联系管理员

    Copyright © 喜物网