这篇文章主要为大家详细介绍了Vue实现美团app的影院推荐选座功能【推荐】,具有一定的参考价值,可以用来参考一下。
感兴趣的小伙伴,下面一起跟随四海网的小编两巴掌来看看吧!
经常用美团app买电影票,不禁对它的推荐选座功能产生了好奇,于是打算自己实现一个类似的算法,美团app的推荐选座界面如下
【图片暂缺】
最多可以选5个座位,本demo的选座界面如下图
【图片暂缺】
上图是点击 推荐选座5人
后选出的座位(绿色),这个demo和美团app不同的地方在于可以连续进行推荐选座,美团app点击了推荐选座就必须买票才能继续选择。
本demo采用Vue-cli搭建,github地址点此 ,clone后直接npm start即可进行具体操作
对于这个推荐座位算法,我尝试了不同场次的电影进行推荐选座,总结出以下几点
【图片暂缺】
可以确定是这个逻辑,试了其他几个场次也是一样
【图片暂缺】
这一点也是大多数情况是对的,有些情况不对,很奇怪
可以肯定的是用一个二维数组 seatArray 代表影院座位,注意到影院座位分布是不规则的,因此需要确定一个 seatRow 和 seatCol 来确定影院座位的数组尺寸,分别代表行列数,对于那些没有座位的地方, seatArray 对应的位置填-1,下面是座位具体的值和代表的含义
-1 非座位
0 可选座位 (白色)
1 已选座位 (绿色)
2 已购票座位 (红色)
然后在mounted中初始化座位,初始值都为0(可选座位),如下代码
代码如下:
//初始座位数组
initSeatArray: function(){
let seatArray = Array(this.seatRow).fill(0).map(()=>Array(this.seatCol).fill(0));
this.seatArray = seatArray;
//均分父容器宽度作为座位的宽度
this.seatSize = this.$refs.innerSeatWrapper
? parseInt(parseInt(window.getComputedStyle(this.$refs.innerSeatWrapper).width,10) / this.seatCol,10)
:0;
//初始化不是座位的地方
this.initNonSeatPlace();
},
//初始化不是座位的地方
initNonSeatPlace: function(){
for(let i=0;i<9;i++){
this.seatArray[i][0]=-1;
}
for(let i=0;i<8;i++){
this.seatArray[i][this.seatArray[0].length-1]=-1;
this.seatArray[i][this.seatArray[0].length-2]=-1;
}
for(let i=0;i<9;i++){
this.seatArray[i][this.seatArray[0].length-3]=-1;
}
for(let i=0;i<this.seatArray[0].length;i++){
this.seatArray[2][i]=-1;
}
}
初始化好之后用一个二重循环来构建html结构,2个v-for嵌套循环出整个结构,如下代码
代码如下:
<div class="inner-seat-wrapper" ref="innerSeatWrapper" >
<div v-for="row in seatRow">
<!--这里的v-if很重要,如果没有则会导致报错,因为seatArray初始状态为空-->
<div v-for="col in seatCol"
v-if="seatArray.length>0"
class="seat"
:style="{width:seatSize+'px',height:seatSize+'px'}">
<div class="inner-seat"
@click="handleChooseSeat(row-1,col-1)"
v-if="seatArray[row-1][col-1]!==-1"
:class="seatArray[row-1][col-1]===2?'bought-seat':(seatArray[row-1][col-1]===1?'selected-seat':'unselected-seat')">
</div>
</div>
</div>
</div>
上述的inner-seat类的div就是具体的座位div,v-if说明了如果是-1也就是是过道之类的就不渲染,然后:class一句控制了该座位对应状态的类的值,一个嵌套三目运算符来控制,对于每个座位绑定点击事件 handleChooseSeat(row-1,col-1) 进行状态切换
代码如下:
//处理座位选择逻辑
handleChooseSeat: function(row,col){
let seatValue = this.seatArray[row][col];
let newArray = this.seatArray;
//如果是已购座位,直接返回
if(seatValue===2) return
//如果是已选座位点击后变未选
if(seatValue === 1){
newArray[row][col]=0
}else if(seatValue === 0){
newArray[row][col]=1
}
//必须整体更新二维数组,Vue无法检测到数组某一项更新,必须slice复制一个数组才行
this.seatArray = newArray.slice();
},
这里注意vue中改变data中的二维数组必须先缓存二维数组,修改后,最终将二维数组重新赋值,否则修改不生效,因为Vue无法侦测到数组内的变动。
首先给每个推荐座位的按钮绑定事件smartChoose
【图片暂缺】
代码如下
代码如下:
//推荐选座,参数是推荐座位数目
smartChoose: function(num){
//找到影院座位水平垂直中间位置的后一排
let rowStart = parseInt((this.seatRow-1)/2,10)+1;
//先从中间排往后排搜索
let backResult = this.searchSeatByDirection(rowStart,this.seatRow-1,num);
if(backResult.length>0){
this.chooseSeat(backResult);
return
}
//再从中间排往前排搜索
let forwardResult = this.searchSeatByDirection(rowStart-1,0,num);
if(forwardResult.length>0) {
this.chooseSeat(forwardResult);
return
}
//提示用户无合法位置可选
alert('无合法位置可选!')
},
第一步是找到影院座位水平垂直中间位置的后一排,然后调用 this.searchSeatByDirection
进行该方向的搜索,先从中间排往后排搜索,再从中间排往前排搜索。如果任意一个方向搜索到结果,直接返回,否则提示用户无合法位置, chooseSeat 用于改变座位的状态
重点就是 searchSeatByDirection 的实现,代码如下
代码如下:
//向前后某个方向进行搜索的函数,参数是起始行,终止行,推荐座位个数
searchSeatByDirection: function(fromRow,toRow,num){
/*
* 推荐座位规则
* (1)初始状态从座位行数的一半处的后一排的中间开始向左右分别搜索,取离中间最近的,如果满足条件,
* 记录下该结果离座位中轴线的距离,后排搜索完成后取距离最小的那个结果作为最终结果,优先向后排进行搜索,
* 后排都没有才往前排搜,前排逻辑同上
* (2)只考虑并排且连续的座位,不能不在一排或者一排中间有分隔
* */
/*
* 保存当前方向搜索结果的数组,元素是对象,result是结果数组,offset代表与中轴线的偏移距离
* {
* result:Array([x,y])
* offset:Number
* }
*/
let currentDirectionSearchResult = [];
//确定行数的大小关系,从小到大进行遍历
let largeRow = fromRow>toRow?fromRow:toRow,
smallRow = fromRow>toRow?toRow:fromRow;
//逐行搜索
for(let i=smallRow;i<=largeRow;i++){
//每一排的搜索,找出该排里中轴线最近的一组座位
let tempRowResult = [],
minDistanceToMidLine=Infinity;
for(let j=0;j<=this.seatCol - num;j++){
//如果有合法位置
if(this.checkRowSeatContinusAndEmpty(i,j,j+num-1)){
//计算该组位置距离中轴线的距离:该组位置的中间位置到中轴线的距离
let resultMidPos = parseInt((j+num/2),10);
let distance = Math.abs(parseInt(this.seatCol/2) - resultMidPos);
//如果距离较短则更新
if(distance<minDistanceToMidLine){
minDistanceToMidLine = distance;
//该行的最终结果
tempRowResult = this.generateRowResult(i,j,j+num-1)
}
}
}
//保存该行的最终结果
currentDirectionSearchResult.push({
result:tempRowResult,
offset:minDistanceToMidLine
})
}
//处理后排的搜索结果:找到距离中轴线最短的一个
//注意这里的逻辑需要区分前后排,对于后排是从前往后,前排则是从后往前找
let isBackDir = fromRow < toRow;
let finalReuslt = [],minDistanceToMid = Infinity;
if(isBackDir){
//后排情况,从前往后
currentDirectionSearchResult.forEach((item)=>{
if(item.offset < minDistanceToMid){
finalReuslt = item.result;
minDistanceToMid = item.offset;
}
});
}else{
//前排情况,从后往前找
currentDirectionSearchResult.reverse().forEach((item)=>{
if(item.offset < minDistanceToMid){
finalReuslt = item.result;
minDistanceToMid = item.offset;
}
})
}
//直接返回结果
return finalReuslt
},
代码有点长,不过逻辑不难,就是前面那几条规则的实现,对于每一行的搜索,是可能存在多个合理的座位结果的
【图片暂缺】
我这里采用的是从左往右遍历,如果是推荐5个座位,先判断1-5位置是否合理,如果合理则记录下其中间位置(3号)到中轴线的距离以及座位结果数组,然后再右移一位检查2-6位置是否合理,如果合理则比较2-6位置的中间位置(4号)距离中轴线的距离和之前的距离,取最短的一个,同时更新座位结果数组。 这样遍历下来,该行的最终结果就能确定, 每一行的最佳结果会存放在currentDirectionSearchResult
数组中
然后后排方向的所有排遍历完后,就得到了由每一行最佳结果组成的数组currentDirectionSearchResult
,再遍历这个数组根据规则取距离中轴线最近的一个作为 最终返回的结果
这个算法可以优化,直接从中间向2边找,找到就返回,不过写起来有点麻烦,但是效率肯定高。需要注意的是前排情况下要 currentDirectionSearchResult.reverse()
反向数组一下,因为对于前排部分是优先选择前排的后面部分的(谁都不想坐第一排!),同后排相反
这个算法不过有点问题,如下图
【图片暂缺】
最左边的2个绿色座位是最后一次点击推荐选座2人的结果,不过该位置却不如另外一个箭头处那2个位置合理,说明该算法其实不完美,可能上面的分析不到位,其实美团的算法也有问题,如下图
【图片暂缺】
这个推荐的合理位置应该是4个位置往左移动一格,这才是正中央位置,这个推荐的有偏移量,不知道是为啥,网上也没有搜到具体的算法逻辑,只能靠猜想和实验。
以上所述是小编给大家介绍的Vue实现一个美团app的影院推荐选座功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对四海网网站的支持!
本文来自:http://www.q1010.com/184/5733-0.html
注:关于Vue实现美团app的影院推荐选座功能【推荐】的内容就先介绍到这里,更多相关文章的可以留意四海网的其他信息。
关键词:vue.js
四海网收集整理一些常用的php代码,JS代码,数据库mysql等技术文章。