600字范文,内容丰富有趣,生活中的好帮手!
600字范文 > 如何优雅的实现一个九宫格抽奖

如何优雅的实现一个九宫格抽奖

时间:2020-11-12 11:00:37

相关推荐

如何优雅的实现一个九宫格抽奖

作者:黄鹏

如何优雅的实现一个九宫格抽奖

九宫格抽奖是在移动端常见开发功能点之一,那如何实现一个高度可复用的九宫格逻辑就显的特别重要了。接下来我们来分析下如何实现一个优雅的抽奖功能。

功能分析

image.png

图片1

实现功能:

按照右图箭头的方向进行旋转。

旋转到某一个 格子 可以进行一定的操作。比如到达当前的 格子 进行高亮, 等功能。

旋转的速度由 停 -> 加速 -> 匀速 -> 减速 -> 停。

方案:

分析完了九宫格需要实现的功能,现在介绍一下实现方案。

方案一

基于setTimeout 的旋转方式。通过不断的改变setTimeout 的时间来控制旋转速度的快慢,实现代码如下:

// count 代表旋转的圈数,id, 表示停止的id, cb 表示动画结束后执行的函数。functionstart(count,id,cb){varsortArr=[1,2,3,6,9,8,7,4];//旋转的顺序vari=0;//最大速度150vartimeout=200;//走一格所花的时间,时间越大,速度越慢。varthat=this;for(varm=0;m<sortArr.length;m++){if(that.imgdata[sortArr[m]-1].id==id){break;}}varmiddle=sortArr.length*count+m+1;//计算需要走多少格functionrotate(){//结束条件if(i>sortArr.length*count&&that.imgdata[that.current-1].id==id){clearInterval(that.time);that.time=null;cb();return;}//控制加,减速度。if(i<middle/count){timeout=timeout-160/middle*count;}if(i>(count-1)*middle/count){timeout=timeout+500/middle*count;}if(i==middle-2){timeout=600;}if(i==middle-1){timeout=1000;}//获取当前停留格子的数据,that.current=sortArr[i++%sortArr.length];setTimeout(rotate,timeout);}this.time=setTimeout(rotate,timeout);}}

优点:动画效果是比较好看的。有加速,匀速减速过程。

缺点:和页面相关太密切。不利于复用。(pass)

方案二

基于sass 实现。通过先写出所有小方格的css,(每一个停留区间实现一套css。八个就写八次。)然后js 中只需要不停的替换class名称就行了。代码如下:

@mixin qianzhuai($animationname,$name){#{$animationname}: $name;-moz-#{$animationname}: $name;-o-#{$animationname}: $name;-ms-#{$animationname}: $name;-webkit-#{$animationname}: $name;}.choosed {position: absolute;width: 100%;height: 100%;top: 0;left: 0;// animation-name: loader1;@include qianzhuai(animation-name,loader1);@include qianzhuai(animation-duration,3s);@include qianzhuai(animation-timing-function,steps(1,end));@include qianzhuai(animation-iteration-count,1);@include qianzhuai(animation-fill-mode,forwards);}@mixin transform($transforms) {transform: $transforms;-moz-transform: $transforms;-o-transform: $transforms;-ms-transform: $transforms;-webkit-transform: $transforms;}@function sqrt ($x) {@if $x < 0 {@warn "Argument for `sqrt()` must be a positive number.";@return null;}$ret: 1;@for $i from 1 through 24 {$ret: $ret - ($ret * $ret - $x) / (2 * $ret);}@return $ret;}// translate@mixin translate ($x, $y) {@include transform(translate($x, $y));}$positions: (-100%,-100%) (0%,-100%) (100%,-100%) (100%,0%) (100%,100%) (0%,100%) (-100%,100%) (-100%,0%);// rotate@mixin calc($positions,$rotatetimes,$stopposition) {$arrrlen: length($positions);$alllength: length($positions) * $rotatetimes + $stopposition ;@for $i from 0 through ($arrrlen *$rotatetimes + $stopposition) {$item: nth($positions, ($i % $arrrlen)+1);$first: nth($item, 1);$second: nth($item,2);$index:1;@if $i <= $arrrlen {$index:20/sqrt($arrrlen)*sqrt(($i));}@else if $i <= ($alllength - $arrrlen + 5) {$index:20 + 25/($alllength - 2*$arrrlen) * ($i - $arrrlen) ;}@else{$index:45 + 55/(4 * 4)* (($i - ($alllength - $arrrlen + 4 ))*($i - ($alllength - $arrrlen + 4 )))}#{$index}% {@include translate($first, $second);}}}@mixin allrotate($name,$positions,$rotatetimes:4, $stopposition:0) {@keyframes #{$name}{@include calc($positions,$rotatetimes, $stopposition);}// @-o-keyframes #{$name}{//@include calc($positions,$rotatetimes, $stopposition);// }}@include allrotate('rewards0',$positions,4,0);@include allrotate('rewards1',$positions,4,1);@include allrotate('rewards2',$positions,4,2);@include allrotate('rewards3',$positions,4,3);@include allrotate('rewards4',$positions,4,4);@include allrotate('rewards5',$positions,4,5);@include allrotate('rewards6',$positions,4,6);@include allrotate('rewards7',$positions,4,7);

我们可以看到,基本上是将js部分的代码搬到sass 中,减少js 代码的冗余,并通过css 3d 加速,使用页面效果更加流畅。

总结

优点:因为用css写的,效果看起来比js写起来的顺滑许多。

缺点:兼容代码过去。且不易扩展。如果不是九宫格,是十宫格,十一宫格。修改的地方感觉就多了。(pass)

**

方案三 (终极方案):

通过上诉的两种方案,发现都不是十分通用。那么如何写出更加通用的代码。便于大家使用呢?

经过多次尝试,发现九宫格有如下特点:

虽然是旋转效果,但是旋转方式各有不同。因此不应该过多依赖于css(方案二的弊端)。

九宫格,抽奖是抽八个奖。但是有可能抽九个奖,十个奖项。

旋转的方向也不同。(方向也应该自定义)

定义合理的数据结构。(采用链表的形式)

服务端返回的奖品信息列表的一般形式,可能会有其他信息,这里就不列出来了。

//服务端返回的奖品信息列表constprizeList=[{id:1},{id:2},{id:3},{id:4},{id:5},{id:6},{id:7},{id:8},];

将我们的数据变成链表的形式:

varrewaridList=[{id:1,next:prizeList[2]},{id:2,next:prizeList[3]},{id:3,next:prizeList[5]},{id:4,next:prizeList[1]},{id:5,next:prizeList[8]},{id:6,next:prizeList[4]},{id:7,next:prizeList[6]},{id:8,next:prizeListp[7]}];

这样子的形式,我们旋转起来就会十分方便,我们拿到数组中的任意一个,我们就知道他接下来旋转的方向了。

注意:next 对应的值更换的话,就代表其旋转方向的变化。

如何控制旋转速度:requestAnimationFrame;

我们将按照的时间片的方式,给格子分发时间片的多少,来实现视觉上速度的快慢。

接下来贴出源码:

classLuckDraw{constructor(DataArr,RotateDir,cycleNumber,minSpeed){this.DataArr=JSON.parse(JSON.stringify(DataArr));//最大速度this.maxSpeed=4;//全速this.cycleNumber=cycleNumber||2;this.myReq;//最小速度this.defaultSpeed=minSpeed||15;for(vari=0;i<RotateDir.length;i++){let{index,next}=RotateDir[i];if(typeofthis.DataArr[index].next!=="undefined"){console.error(`RotateDiriserror`);return;}this.DataArr[index].next=this.DataArr[next]}}run(id,running,runend){varcounter=0;//计数器varcurrent=0;//当前数字值varn=0;varcurrentObj=this.DataArr[0];vartem=this.DataArr[0];while(true){if(n>this.DataArr.length){console.error(`${id}不存在`);return;}if(tem.id==id){break;}tem=tem.next;n++;}varallCount=this.cycleNumber*this.DataArr.length+n;//加速区间varaddSpeed=this.defaultSpeed-this.maxSpeed;//减速区间varreduceSpeed=allCount-(this.defaultSpeed-this.maxSpeed);this.running=running;this.runend=runend;var_this=this;this.running(currentObj);this.myReq=requestAnimationFrame(step);functionstep(){//current++;//加速环节if(counter<addSpeed){if(current<Math.pow(_this.defaultSpeed-counter,2)){current=current+_this.defaultSpeed/2;}else{current=0;//往前移动一个;counter++;currentObj=currentObj.next;_this.running(currentObj);}}//匀速环节if(counter>=addSpeed&&counter<reduceSpeed){if(current<_this.maxSpeed){current++;}else{//计数清零current=0;//往前移动一个;counter++;currentObj=currentObj.next;_this.running(currentObj);}}//减速环节if(counter>=reduceSpeed&&counter<allCount){if(Math.sqrt(current)<=(_this.defaultSpeed-(allCount-counter))){current=current+2;}else{//计数清零current=0;//往前移动一个;counter++;currentObj=currentObj.next;_this.running(currentObj);}}//停止if(counter>=allCount){_this.runend(currentObj);cancelAnimationFrame(_this.myReq);return;}_this.myReq=requestAnimationFrame(step);}}}

使用方式:

//服务端返回的奖品信息列表constprizeList=[{id:1},{id:2},{id:3},{id:4},{id:5},{id:6},{id:7},{id:8},];//旋转规则数组constrotateDir=[{index:0,next:1},{index:1,next:2},{index:2,next:3},{index:3,next:4},{index:4,next:5},{index:5,next:6},{index:6,next:7},{index:7,next:0},]//初始化抽奖,3代表圈数,8代表速度,也代表时间片的个数constluckDrawFn=LuckDraw(prizeList,rotateDir,3,8);//中奖id,请求服务端接口拿到constid=7;luckDrawFn.run(id,//中奖idparams=>{//停留在当前格子的回调函数//拿到当前active状态的idthis.rewardId=params.id;},params=>{//最终停止的回调函数//最后转盘停止的地方,可以弹出奖励弹框或其他操作this.rewardId=params.id;})

总结:这样子实现,我们每次更改的只有,prizeList, rotateDir,操作逻辑可以写在两个提供的回调函数中,实现。这样子就可以完美的提供给任何人用了,还可以兼容各种复杂的抽奖情况。

好友招贤纳士:

❤️爱心三连击1.看到这里了就点个在看支持下吧,你的「在看」是我创作的动力。2.关注公众号程序员成长指北,「带你一起学Node」!3.特殊阶段,带好口罩,做好个人防护。4.添加微信【ikoala520】,拉你进技术交流群一起学习。“在看转发”是最大的支持

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。