1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        如何優(yōu)雅的實現(xiàn)一個九宮格抽獎

        共 2887字,需瀏覽 6分鐘

         ·

        2020-09-22 16:38

        作者:黃鵬

        如何優(yōu)雅的實現(xiàn)一個九宮格抽獎

        九宮格抽獎是在移動端常見開發(fā)功能點之一,那如何實現(xiàn)一個高度可復(fù)用的九宮格邏輯就顯的特別重要了。接下來我們來分析下如何實現(xiàn)一個優(yōu)雅的抽獎功能。

        功能分析

        image.png

        圖片1

        實現(xiàn)功能:

        1. 按照右圖箭頭的方向進(jìn)行旋轉(zhuǎn)。
        2. 旋轉(zhuǎn)到某一個 格子 可以進(jìn)行一定的操作。比如到達(dá)當(dāng)前的 格子 進(jìn)行高亮, 等功能。
        3. 旋轉(zhuǎn)的速度由 ?停 -> 加速 -> 勻速 -> 減速 -> 停。


        方案:

        分析完了九宮格需要實現(xiàn)的功能,現(xiàn)在介紹一下實現(xiàn)方案。


        方案一

        基于setTimeout 的旋轉(zhuǎn)方式。通過不斷的改變setTimeout 的時間來控制旋轉(zhuǎn)速度的快慢,實現(xiàn)代碼如下:

        // count 代表旋轉(zhuǎn)的圈數(shù),id, 表示?停止的id, cb 表示動畫結(jié)束后執(zhí)行的函數(shù)。
        function?start(count,?id,?cb)?{
        ???var?sortArr?=?[1,?2,?3,?6,?9,?8,?7,?4];?//?旋轉(zhuǎn)的順序
        ???var?i?=?0;
        ???//?最大速度?150
        ???var?timeout?=?200;?//?走一格所花的時間,時間越大,速度越慢。
        ???var?that?=?this;
        ???for?(var?m?=?0;?m??????if?(that.imgdata[sortArr[m]?-?1].id?==?id)?{
        ???????break;
        ?????}
        ???}
        ???var?middle?=?sortArr.length?*?count?+?m?+?1;//?計算需要走多少格
        ???function?rotate()?{
        ?????//?結(jié)束條件
        ?????if?(i?>?sortArr.length?*?count?&&?that.imgdata[that.current?-?1].id?==?id)?{
        ???????clearInterval(that.time);
        ???????that.time?=?null;
        ???????cb();
        ???????return;
        ?????}
        ?????//?控制加,減速度。
        ?????if?(i????????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;
        ?????}
        ?????//?獲取當(dāng)前停留格子的數(shù)據(jù),
        ?????that.current?=?sortArr[i++?%?sortArr.length];
        ?????setTimeout(rotate,?timeout);
        ???}
        ???this.time?=?setTimeout(rotate,?timeout);
        ?}
        }


        優(yōu)點:動畫效果是比較好看的。有加速,勻速減速過程。
        缺點:和頁面相關(guān)太密切。不利于復(fù)用。(pass)


        方案二

        基于sass 實現(xiàn)。通過先寫出所有小方格的css,(每一個停留區(qū)間實現(xiàn)一套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 加速,使用頁面效果更加流暢。
        總結(jié)
        優(yōu)點:因為用css寫的,效果看起來比js寫起來的順滑許多。
        缺點:兼容代碼過去。且不易擴(kuò)展。如果不是九宮格,是十宮格,十一宮格。修改的地方感覺就多了。(pass)
        **

        方案三 (終極方案):

        通過上訴的兩種方案,發(fā)現(xiàn)都不是十分通用。那么如何寫出更加通用的代碼。便于大家使用呢?
        經(jīng)過多次嘗試,發(fā)現(xiàn)九宮格有如下特點:

        1. 雖然是旋轉(zhuǎn)效果,但是旋轉(zhuǎn)方式各有不同。因此不應(yīng)該過多依賴于css(方案二的弊端)。
        2. 九宮格,抽獎是抽八個獎。但是有可能抽九個獎,十個獎項。
        3. 旋轉(zhuǎn)的方向也不同。(方向也應(yīng)該自定義)




        定義合理的數(shù)據(jù)結(jié)構(gòu)。(采用鏈表的形式)
        ?服務(wù)端返回的獎品信息列表的一般形式,可能會有其他信息,這里就不列出來了。

        ?//?服務(wù)端返回的獎品信息列表
        const?prizeList?=?[
        ??{?id:?1?},
        ??{?id:?2?},
        ??{?id:?3?},
        ??{?id:?4?},
        ??{?id:?5?},
        ??{?id:?6?},
        ??{?id:?7?},
        ??{?id:?8?},
        ];

        將我們的數(shù)據(jù)變成鏈表的形式:

        var?rewaridList?=?[
        ??{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]}
        ];

        這樣子的形式,我們旋轉(zhuǎn)起來就會十分方便,我們拿到數(shù)組中的任意一個,我們就知道他接下來旋轉(zhuǎn)的方向了。
        注意:next 對應(yīng)的值更換的話,就代表其旋轉(zhuǎn)方向的變化。

        如何控制旋轉(zhuǎn)速度:requestAnimationFrame;


        我們將按照的時間片的方式,給格子分發(fā)時間片的多少,來實現(xiàn)視覺上速度的快慢。

        接下來貼出源碼:

        class?LuckDraw?{
        ????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?(var?i?=?0;?i?????????let?{?index,?next?}?=?RotateDir[i];
        ????????if?(typeof?this.DataArr[index].next?!==?"undefined")?{
        ??????????console.error(`RotateDir?is?error`);
        ??????????return;
        ????????}
        ????????this.DataArr[index].next?=?this.DataArr[next]
        ??????}
        ????}

        ????run(id,?running,?runend)?{
        ??????var?counter?=?0;?//?計數(shù)器
        ??????var?current?=?0;?//?當(dāng)前數(shù)字值
        ??????var?n?=?0;
        ??????var?currentObj?=?this.DataArr[0];
        ??????var?tem?=?this.DataArr[0];
        ??????while?(true)?{
        ????????if?(n?>?this.DataArr.length)?{
        ??????????console.error(`${id}不存在`);
        ??????????return;
        ????????}
        ????????if?(tem.id?==?id)?{
        ??????????break;
        ????????}
        ????????tem?=?tem.next;
        ????????n++;
        ??????}
        ??????var?allCount?=?this.cycleNumber?*?this.DataArr.length?+?n;
        ??????//?加速區(qū)間
        ??????var?addSpeed?=?this.defaultSpeed?-?this.maxSpeed;
        ??????//?減速區(qū)間
        ??????var?reduceSpeed?=?allCount?-?(this.defaultSpeed?-?this.maxSpeed);
        ??????this.running?=?running;
        ??????this.runend?=?runend;
        ??????var?_this?=?this;
        ??????this.running(currentObj);
        ??????this.myReq?=?requestAnimationFrame(step);
        ??????function?step()?{
        ????????//?current++;
        ????????//?加速環(huán)節(jié)
        ????????if?(counter???????????if?(current?Math.pow(_this.defaultSpeed?-?counter,?2))?{
        ????????????current?=?current?+?_this.defaultSpeed?/?2;
        ??????????}?else?{
        ????????????current?=?0;
        ????????????//?往前移動一個;
        ????????????counter++;
        ????????????currentObj?=?currentObj.next;
        ????????????_this.running(currentObj);
        ??????????}
        ????????}
        ????????//?勻速環(huán)節(jié)
        ????????if?(counter?>=?addSpeed?&&?counter???????????if?(current?????????????current++;
        ??????????}?else?{
        ????????????//?計數(shù)清零
        ????????????current?=?0;
        ????????????//?往前移動一個;
        ????????????counter++;
        ????????????currentObj?=?currentObj.next;
        ????????????_this.running(currentObj);
        ??????????}
        ????????}
        ????????//?減速環(huán)節(jié)
        ????????if?(counter?>=?reduceSpeed?&&?counter???????????if?(Math.sqrt(current)?<=?(_this.defaultSpeed?-?(allCount?-?counter)))?{
        ????????????current?=?current?+?2;
        ??????????}?else?{
        ????????????//?計數(shù)清零
        ????????????current?=?0;
        ????????????//?往前移動一個;
        ????????????counter++;
        ????????????currentObj?=?currentObj.next;
        ????????????_this.running(currentObj);
        ??????????}
        ????????}
        ????????//?停止
        ????????if?(counter?>=?allCount)?{
        ??????????_this.runend(currentObj);
        ??????????cancelAnimationFrame(_this.myReq);
        ??????????return;
        ????????}
        ????????_this.myReq?=?requestAnimationFrame(step);
        ??????}
        ????}
        ??}

        使用方式:

        //?服務(wù)端返回的獎品信息列表
        const?prizeList?=?[
        ??{?id:?1?},
        ??{?id:?2?},
        ??{?id:?3?},
        ??{?id:?4?},
        ??{?id:?5?},
        ??{?id:?6?},
        ??{?id:?7?},
        ??{?id:?8?},
        ];

        //?旋轉(zhuǎn)規(guī)則數(shù)組?
        const?rotateDir?=?[
        ??{?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代表圈數(shù),?8代表速度,也代表時間片的個數(shù)
        const?luckDrawFn?=?LuckDraw(prizeList,?rotateDir,?3,?8);

        //?中獎id,請求服務(wù)端接口拿到
        const?id?=?7;

        luckDrawFn.run(
        ??id,?//中獎id
        ??params?=>?{?//?停留在當(dāng)前格子的回調(diào)函數(shù)
        ????//?拿到當(dāng)前?active?狀態(tài)的?id
        ????this.rewardId?=?params.id;
        ??},
        ??params?=>?{?//?最終停止的回調(diào)函數(shù)
        ????//最后轉(zhuǎn)盤停止的地方,可以彈出獎勵彈框或其他操作
        ????this.rewardId?=?params.id;
        ??})


        總結(jié):這樣子實現(xiàn),我們每次更改的只有,prizeList, rotateDir,操作邏輯可以寫在兩個提供的回調(diào)函數(shù)中,實現(xiàn)。這樣子就可以完美的提供給任何人用了,還可以兼容各種復(fù)雜的抽獎情況。

        掃碼關(guān)注公眾號,訂閱更多精彩內(nèi)容。


        “在看和轉(zhuǎn)發(fā)”就是最大的支持
        瀏覽 59
        點贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            天堂一区在线观看 | 丁香五月婷婷中文字幕 | 有坂深雪av一区二区精品 | 无码人妻视频 | 国产h在线观看 | 国产草草影院 | 黄色一级大片在线免费看产 | 亚洲碰在线 | 青青久久精品 | 动漫爽 又黄 免费视频 |