整合營(yíng)銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          如何實(shí)現(xiàn)一個(gè)基于自然流布局的可視化拖拽搭建平臺(tái)?

          如何實(shí)現(xiàn)一個(gè)基于自然流布局的可視化拖拽搭建平臺(tái)?

          owCode 是高效、高性能的拖拽式低代碼開(kāi)發(fā)平臺(tái). 也是筆者最近一直在研究的方向, 對(duì)于可視化搭建平臺(tái)的實(shí)現(xiàn)方案筆者之前寫過(guò)很多文章, 這里帶大家探索一個(gè)新方向——基于自然流布局的可視化搭建平臺(tái).

          在我們之前實(shí)現(xiàn)的 h5-dooring 搭建平臺(tái)中, 我們采用了網(wǎng)格布局的方式來(lái)實(shí)現(xiàn)拖拽生成H5頁(yè)面或者Web app, 其好處就是靈活簡(jiǎn)單, 用戶基本沒(méi)有任何使用成本, 在前端層也能做一定的橫向擴(kuò)展, 但是存在幾個(gè)缺陷:

          • 實(shí)現(xiàn)嵌套組件比較復(fù)雜
          • 沒(méi)有層的概念

          雖然通過(guò)改造可以實(shí)現(xiàn)層和嵌套的問(wèn)題, 最近也在努力往這個(gè)方向?qū)崿F(xiàn)(雖然和設(shè)計(jì)初衷相悖, dooring的初衷是抹去層和嵌套的概念, 讓搭建扁平化和智能化, 所以沒(méi)有采用自由布局的方案)

          但是如果一定要實(shí)現(xiàn)嵌套和層的功能, 有沒(méi)有另一種更簡(jiǎn)單的方案呢? 筆者目前想到了兩種解決方案:

          • 將智能布局改為自由布局, 即可以采用類似 react-resizable 的這種方案
          • 基于自然流來(lái)實(shí)現(xiàn), 也就是抹去定位的概念, 完全基于元素在文檔的順序, 層級(jí)和定位的選擇權(quán)交給用戶

          因?yàn)榈谝环N方案筆者在dooring的早期已經(jīng)實(shí)現(xiàn)過(guò)一版, 最后棄用采用了網(wǎng)格布局, 所以說(shuō)我們來(lái)探討一下第二種方案的實(shí)現(xiàn).

          基于自然流布局實(shí)現(xiàn)拖拽生成頁(yè)面

          自然流布局的好處就是我們不用通過(guò)定位的方式來(lái)限定元素的位置等信息, 而是以html文檔流的方式來(lái)布局元素, 并且用戶可以靈活的設(shè)置元素的層級(jí)(layer)和偏移(transform), 接下來(lái)我們來(lái)看看簡(jiǎn)單的實(shí)現(xiàn)效果.

          1. demo效果

          H5建站, 頁(yè)面制作

          H5制作, H5編輯器

          由上圖的demo我們可以發(fā)現(xiàn)組件在畫布中的布局完全是默認(rèn)的文檔流的方式, 所以我們有更靈活的布局實(shí)現(xiàn).

          2. 實(shí)現(xiàn)思路

          具體實(shí)現(xiàn)思路主要分以下幾個(gè)部分:

          • 組件區(qū)拖拽至畫布
          • 畫布區(qū)拖拽
          • 組件編輯器和更新機(jī)制

          第一點(diǎn)和第三點(diǎn)我們?cè)?H5-dooring中已經(jīng)實(shí)現(xiàn)了, 感興趣的可以看我之前的文章, 我們這里重點(diǎn)來(lái)實(shí)現(xiàn)畫布區(qū)拖拽, 也是比較核心的環(huán)節(jié).

          2.1 H5拖放api基本介紹

          拖放(Dragdrop)是 HTML5 標(biāo)準(zhǔn)的組成部分, 早已被大多數(shù)瀏覽器支持. 我們目前使用的拖放插件基本上基于 H5 拖放 API 來(lái)實(shí)現(xiàn)的, 其實(shí)實(shí)現(xiàn)第一點(diǎn)組件區(qū)拖拽至畫布我們完全可以用原生來(lái)實(shí)現(xiàn), 這里筆者簡(jiǎn)單來(lái)介紹以下.

          首先我們來(lái)看看一個(gè)完整的拖放過(guò)程:

          1. 首先要設(shè)置一個(gè)元素可拖放(比如<img draggable="true" />)
          2. 設(shè)計(jì)拖動(dòng)的時(shí)候會(huì)發(fā)生什么(需要用到ondragstart事件 和 setData(你要傳遞的數(shù)據(jù)))
          3. 放到何處,也就是目標(biāo)容器(通常在目標(biāo)容器上綁定ondragoverondrop事件)

          有了以上3個(gè)步驟, 我們就能實(shí)現(xiàn)第一點(diǎn)的需求, 筆者寫個(gè)簡(jiǎn)單demo來(lái)給大家參考一下:

          <script type="text/javascript">
            function allowDrop(ev) {
              ev.preventDefault();
            }
          
            function drag(ev){
              ev.dataTransfer.setData("Text",ev.target.id);
            }
          
            function drop(ev){
              ev.preventDefault();
              let data=ev.dataTransfer.getData("Text");
              ev.target.appendChild(document.getElementById(data));
            }
          </script>
          
          <div id="box" ondrop="drop(event)" ondragover="allowDrop(event)"></div>
          <img id="drag" src="dooring.png" draggable="true" ondragstart="drag(event)" width="336" height="69" />

          也就是對(duì)應(yīng)的我們的組件拖放區(qū)域, 如下圖所示:

          2.2 畫布區(qū)拖拽布局實(shí)現(xiàn)

          因?yàn)橹暗陌姹疚覀儾捎昧司W(wǎng)格布局來(lái)實(shí)現(xiàn)智能拖拽, 由于內(nèi)部定位機(jī)制采用的是絕對(duì)定位(absolute), 所以是實(shí)現(xiàn)層級(jí)和固定組件比較困難, 如果組件的呈現(xiàn)完全脫離了定位的束縛, 我們就可以實(shí)現(xiàn)以上的困境了. 所以這里我們調(diào)研了一種方案——拖拽排序機(jī)制.

          自然流布局的規(guī)律就是默認(rèn)情況下html頁(yè)面是基于dom出現(xiàn)的順序來(lái)排列的, 也就是我們說(shuō)的堆疊.

          H5制作

          我們可以遵循這樣的設(shè)計(jì), 通過(guò)排序的方式改變組件的位置從而實(shí)現(xiàn)自然流布局的頁(yè)面搭建.

          那么我們?cè)倩氐缴厦嬲f(shuō)的布局問(wèn)題, 比如說(shuō)要想實(shí)現(xiàn)柵格化布局, 我們只需要定義一個(gè)flex容器, 將組件拖拽到容器里就好了, 這樣也就解決了嵌套的問(wèn)題. 同時(shí)我們還可以設(shè)計(jì)嵌套容器的柵格數(shù), 這樣就可以實(shí)現(xiàn)類似如下的效果:

          H5編輯器

          拖拽排序的庫(kù)我們可以使用: sortable Vue.Draggable * react-dnd

          還有很多優(yōu)秀的庫(kù), 這里就不一一舉例了.

          3. 如何實(shí)現(xiàn)層級(jí)和嵌套

          其實(shí)在上面的實(shí)現(xiàn)思路中我們已經(jīng)解決了嵌套的問(wèn)題了, 即提供拖放的容器組件, 利用筆者在上文中介紹的拖放api即可實(shí)現(xiàn). 對(duì)于組件層級(jí)來(lái)說(shuō), 因?yàn)槲覀儾捎玫氖亲匀涣鞑季? 所以我們可以輕松的設(shè)置元素的定位屬性, 比如我們提供一個(gè)定位的設(shè)置:

          拖拽搭建HTML5


          關(guān)于如何設(shè)計(jì)一個(gè)動(dòng)態(tài)的屬性編輯器, 筆者之前文章中也就詳細(xì)地介紹, 大家可以參考:

          • 表單編輯器實(shí)現(xiàn)(FormEditor)

          以上就是自然流布局的基本實(shí)現(xiàn)方式, 后續(xù)筆者也會(huì)在github上同步我們最新的成果.

          H5-Dooring編輯器wiki: https://github.com/MrXujiang/h5-Dooring/wiki

          最后

          覺(jué)得有用 ?喜歡就收藏,順便點(diǎn)個(gè)贊吧,你的支持是我最大的鼓勵(lì)!搜 “趣談前端”,發(fā)現(xiàn)更多有趣的H5游戲, webpack,node,gulp,css3,javascript,nodeJS,canvas數(shù)據(jù)可視化等前端知識(shí)和實(shí)戰(zhàn).

          話不說(shuō),先上效果吧。

          Axure軟件其實(shí)是一款原型設(shè)計(jì)工具,可以設(shè)置網(wǎng)頁(yè)、app等原型,由于它基于html構(gòu)架,其中又包含了一些基本的函數(shù),所以我們可以通過(guò)制作一些游戲更加熟練地使用、了解這些函數(shù)。本文之后將根據(jù)我的另一篇文章中《以「用戶為中心」的產(chǎn)品設(shè)計(jì)詳述》提到的「五大用戶體驗(yàn)要素」一一講解構(gòu)建過(guò)程。

          戰(zhàn)略層

          通過(guò)使用Axure制作一些小游戲,將多種函數(shù)及邏輯關(guān)系設(shè)定其中,達(dá)到增強(qiáng)自己邏輯思維能力及熟練使用Axure的目的。

          本例準(zhǔn)備制作一款「人機(jī)乒乓球」游戲,基本游戲規(guī)則是:玩家通過(guò)移動(dòng)桌面底部球拍,是乒乓球在下落時(shí)正好落到球拍上,然后反彈乒乓球。

          范圍層

          本例要實(shí)現(xiàn)的基本功能包括:

          • 設(shè)置乒乓球移動(dòng)速度:玩家可以自由選擇多種速度等級(jí),等級(jí)越高速度越快,每回合分?jǐn)?shù)越高。
          • 設(shè)置玩家名稱:玩家可以自由輸入自己的名稱,在游戲中顯示。
          • 多種移動(dòng)球拍方式:鼠標(biāo)直接拖動(dòng)球拍、點(diǎn)擊鍵盤「←」、「→」按鈕、點(diǎn)擊信息欄「左」、「右」圖標(biāo)均可控制球拍移動(dòng)
          • 游戲暫停及恢復(fù)功能:游戲時(shí)可以隨時(shí)暫停及恢復(fù)游戲
          • 實(shí)時(shí)顯示分?jǐn)?shù)及難度
          • 游戲結(jié)束后可以重新開(kāi)始

          結(jié)構(gòu)層

          流程梳理

          通過(guò)「范圍層」的梳理,我們可以簡(jiǎn)單設(shè)置出整個(gè)游戲的基本流程圖:

          難點(diǎn)分析

          在游戲中,乒乓球移動(dòng)是最重要的,所以我們第一個(gè)考慮的是「循環(huán)」,通過(guò)獲得一個(gè)恒定的循環(huán)時(shí)間,控制乒乓球恒定速度移動(dòng),但是因?yàn)槲覀冇挚梢赃x擇乒乓球移動(dòng)速度,所以我們需要得到一個(gè)基準(zhǔn)速度v,然后在基準(zhǔn)速度上直接按倍數(shù)增加移動(dòng)速度。

          首先在頁(yè)面載入時(shí)設(shè)置動(dòng)態(tài)面板「bit_time」和「bit_ball」向后循環(huán),循環(huán)間隔1毫秒,動(dòng)態(tài)面板「database」中的「time_begin」獲得系統(tǒng)載入時(shí)的時(shí)間戳。

          在動(dòng)態(tài)面板「bit_time」中,我們?cè)O(shè)置「database」動(dòng)態(tài)面板中的「time_now」獲得系統(tǒng)現(xiàn)在時(shí)刻的時(shí)間戳,「time_bit」=time_now-time_begin。

          此時(shí)「time_bit」就是一個(gè)以毫秒為單位不斷增長(zhǎng)的數(shù)值,它代表著系統(tǒng)現(xiàn)在時(shí)刻與之前time_begin的時(shí)間差值。

          之后我們?cè)凇竧ime_ball」動(dòng)態(tài)面板設(shè)置觸發(fā)條件:當(dāng)「time_bit」>=level(level為小球速度等級(jí),默認(rèn)50)時(shí),「time_begin」重新賦值為當(dāng)前系統(tǒng)時(shí)間。這時(shí)「time_ball」就形成了一個(gè)每50毫秒自動(dòng)循環(huán)運(yùn)行的程序,如果level為100時(shí),「time_ball」就會(huì)每100毫秒自動(dòng)出發(fā)一次。

          以上我們就獲得了一個(gè)可以控制的定時(shí)循環(huán)機(jī)制,其它功能都是在這個(gè)機(jī)制上實(shí)現(xiàn)的。

          功能

          1. 加載初始化實(shí)現(xiàn)

          第一步永遠(yuǎn)是最難的,我在這里設(shè)置了6個(gè)全局變量,方便對(duì)整個(gè)游戲的配置。

          • Level:游戲難度等級(jí),初始為50
          • location_x:乒乓球在移動(dòng)時(shí)X軸方向的位移量
          • game_status:游戲狀態(tài),包括暫停、首頁(yè)、設(shè)置、游戲中、結(jié)束等,初始化為首頁(yè)
          • angle:乒乓球移動(dòng)速度,初始化為100
          • score:游戲難度對(duì)應(yīng)基準(zhǔn)分?jǐn)?shù)倍數(shù),分別為1,2,3,5,10,初始化為3
          • score1:乒乓球移動(dòng)時(shí)基準(zhǔn)分,初始化為0

          看了上述這些全局變量你可能還不明白,但請(qǐng)你一定要記住,因?yàn)槊恳粋€(gè)都非常重要之后我會(huì)詳細(xì)介紹。

          除了初始化全局變量外,還需要初始化以下數(shù)據(jù):

          • 游戲桌面動(dòng)態(tài)模板:初始化為顯示首頁(yè)
          • 設(shè)置動(dòng)態(tài)模板:初始化為隱藏
          • 信息欄動(dòng)態(tài)模板:初始化禁用所有功能

          2. 設(shè)置游戲難度

          游戲難度體現(xiàn)在乒乓球移動(dòng)速度上,當(dāng)難度越高時(shí)速度越快,本例中共有5個(gè)等級(jí),默認(rèn)為中間等級(jí)。當(dāng)玩家選擇對(duì)應(yīng)等級(jí)時(shí),系統(tǒng)將等級(jí)賦值給全局變量Level,通過(guò)上文的「time_ball」控制乒乓球移動(dòng)速度,難度越高,Level值越低,則「time_bit」越短,乒乓球越快。

          3. 游戲?qū)崟r(shí)顯示分?jǐn)?shù)

          游戲分?jǐn)?shù)需要實(shí)時(shí)更新,筆者開(kāi)始通過(guò)判斷乒乓球接觸球拍且能成功返回時(shí),分?jǐn)?shù)增加,但是實(shí)現(xiàn)起來(lái)有些bug,所以舍棄。

          本例中通過(guò)計(jì)算乒乓球移動(dòng)桌面的次數(shù)實(shí)現(xiàn)分?jǐn)?shù)增加,當(dāng)乒乓球從最上面到最下面時(shí)或從最下面移到最上面時(shí)(恒定移動(dòng)6次,后文詳述),score1自增,分?jǐn)?shù)=score*score1/6.

          4. 游戲暫停及恢復(fù)

          通過(guò)全局變量game_status的值控制。

          當(dāng)game_status=begin時(shí),所有與游戲進(jìn)行的相關(guān)功能才可以使用,例如乒乓球移動(dòng)、分?jǐn)?shù)增加、移動(dòng)球拍等,所以當(dāng)點(diǎn)擊「暫停」時(shí),game_status=pause,乒乓球即自動(dòng)停止運(yùn)動(dòng)。

          5. 乒乓球移動(dòng)速度

          整個(gè)桌面大小為600*600.為了制作方便,乒乓球固定每次在y軸移動(dòng)angle==100距離,通過(guò)每次移動(dòng)的時(shí)間間隔控制乒乓球移動(dòng)速度。如初始等級(jí)正常,level==50時(shí),乒乓球每次移動(dòng)時(shí)間間隔為50毫秒。

          6. 乒乓球隨機(jī)移動(dòng)方向

          為了使游戲逼真,每次從上桌面彈出的乒乓球角度均為隨機(jī)值,本例為了制作方便,設(shè)置乒乓球初始從(300,0)坐標(biāo)開(kāi)始向下移動(dòng),如果碰到球拍,乒乓球按照原方向返回(300,0)坐標(biāo)。

          本例目前控制乒乓球初始移動(dòng)角度不會(huì)彈到左、右桌壁(即初始角度較小)。

          本例通過(guò)函數(shù)location_x=[[Math.tan((Math.random()*40-20)/57)]]得到初始移動(dòng)X軸方向移動(dòng)位移,乒乓球每次X軸的移動(dòng)距離為location_x*angle.

          7. 球拍觸碰乒乓球反彈

          球拍的寬度恒定為120,所以判斷當(dāng)乒乓球移動(dòng)到桌面底部時(shí),如果「-120<球拍.x-乒乓球.x<120」時(shí),則判斷此次成功碰到乒乓球,改變乒乓球y軸移動(dòng)全局變量angle=-angle,乒乓球反彈。

          框架層

          根據(jù)之前邏輯梳理,我們需要制作以下幾個(gè)動(dòng)態(tài)面板:

          游戲桌面面板:包括首頁(yè)、游戲中、游戲結(jié)束狀態(tài)

          設(shè)置面板:

          信息欄面板:

          bit_time:獲得基準(zhǔn)時(shí)間值

          bit_ball:控制小球移動(dòng)

          bit_down:判斷小球接觸球拍時(shí)成功或失敗

          bit_status_up:小球觸碰上端桌面時(shí)隨便方向向下移動(dòng)

          bit_score:獲得實(shí)時(shí)分?jǐn)?shù)

          表現(xiàn)層

          以下為最初設(shè)計(jì)的三個(gè)頁(yè)面效果圖

          總結(jié)

          雖然看似是一個(gè)很簡(jiǎn)單的小游戲,但在制作過(guò)程中卻遇到了很多難題,有的可以直接解決,有的卻要花費(fèi)很大精力繞道實(shí)現(xiàn)。不過(guò)當(dāng)完成這款游戲后,相信你對(duì)各種通過(guò)循環(huán)功能實(shí)現(xiàn)的頁(yè)面輪播圖等功能實(shí)現(xiàn)會(huì)輕松自如了,而通過(guò)全局變量去控制各種邏輯狀態(tài)、通過(guò)鍵盤控制面板移動(dòng)等也是信手拈來(lái)了。

          本游戲目前還十分簡(jiǎn)單,如果有時(shí)間,我近期會(huì)做如下改進(jìn):

          自動(dòng)識(shí)別屏幕,使桌面寬度為屏幕寬度,且乒乓球及球拍大小也會(huì)對(duì)應(yīng)變換

          乒乓球可以撞擊桌面左、右兩邊

          通過(guò)中繼器儲(chǔ)存每次稱號(hào)及分?jǐn)?shù),并在結(jié)束后實(shí)時(shí)顯示

          優(yōu)化速度控制方式,使手機(jī)端更加流暢

          增加更多不確定因素

          歡迎大家隨時(shí)交流,謝謝!

          本文由 @escher 原創(chuàng)發(fā)布于人人都是產(chǎn)品經(jīng)理。未經(jīng)許可,禁止轉(zhuǎn)載。

          題圖來(lái)自 Pixabay,基于 CC0 協(xié)議

          去年曾寫過(guò)一個(gè)類似的拼拼樂(lè)小游戲,技術(shù)棧采用自己的Xuery框架和原生javascript實(shí)現(xiàn)的,腳手架采用gulp來(lái)實(shí)現(xiàn),為了滿足對(duì)vue的需求,這次我使用vue生態(tài)將其重構(gòu),腳手架采用比較火的vue-cli

          前言

          為了加深大家對(duì)vue的了解和vue項(xiàng)目實(shí)戰(zhàn),筆者采用vue生態(tài)來(lái)重構(gòu)此項(xiàng)目,方便大家學(xué)習(xí)和探索。技術(shù)棧如下:

          • vue-cli4 基于vue的腳手架
          • Xuery 筆者基于原生js二次封裝的dom庫(kù)
          • vue mvvm庫(kù)

          因?yàn)樵搼?yīng)用屬于H5游戲,為了輕量化項(xiàng)目, 我沒(méi)有采用第三方ui庫(kù), 如果大家想采用基于vue的第三方移動(dòng)端ui庫(kù),推薦如下:

          • Mint 餓了么推出的移動(dòng)端ui庫(kù)
          • NutUI 一套京東風(fēng)格的移動(dòng)端組件庫(kù)
          • muse-ui 基于MaterialUI風(fēng)格的移動(dòng)端UI組件
          • cube-ui 滴滴團(tuán)隊(duì)開(kāi)發(fā)的移動(dòng)端UI組件庫(kù)
          • vant 有贊團(tuán)隊(duì)的電商風(fēng)格的移動(dòng)端組件庫(kù)
          • atom-design atom風(fēng)格的移動(dòng)端ui組件庫(kù) *
          • mand-mobile 滴滴團(tuán)隊(duì)研發(fā)的基于金融場(chǎng)景的移動(dòng)端ui組件庫(kù)

          以上筆者推薦的都是社區(qū)比較完善,bug比較少的組件庫(kù),大家可以感受一下。

          回到我們的小游戲開(kāi)發(fā),我們更多的是javascriptcss3的掌握程度,在學(xué)習(xí)完這篇文章之后相信大家對(duì)javascriptcss3的編程能力都會(huì)有極大的提升,后面還會(huì)介紹如何使用canvas實(shí)現(xiàn)生成戰(zhàn)績(jī)海報(bào)圖的功能。

          正文

          我們先來(lái)看看游戲的預(yù)覽界面:

          本文的算法實(shí)現(xiàn)方式在之前的拼拼樂(lè)文章中已經(jīng)說(shuō)明,這里主要介紹核心算法, 至于vue-cli的使用方法,筆者之前也寫過(guò)對(duì)應(yīng)的文章,大家可以研究學(xué)習(xí)一下。vue-cli搭建項(xiàng)目方式如下:

          // 安裝
          yarn global add @vue/cli
          
          // 創(chuàng)建項(xiàng)目
          vue create pinpinle
          
          // 進(jìn)入項(xiàng)目并啟動(dòng)
          cd pinpinle && yarn start
          

          關(guān)于vue-cli3配置實(shí)戰(zhàn),可以移步我之前的文章: 一張圖教你快速玩轉(zhuǎn)vue-cli3

          H5游戲核心功能介紹

          目前我主要整理了如下核心功能,接下來(lái)會(huì)一一帶大家實(shí)現(xiàn):

          • 實(shí)現(xiàn)純javascript上傳預(yù)覽圖片
          • 實(shí)現(xiàn)拼圖分割功能
          • 實(shí)現(xiàn)洗牌算法
          • 實(shí)現(xiàn)生成戰(zhàn)績(jī)海報(bào)功能

          1. 實(shí)現(xiàn)純javascript上傳預(yù)覽圖片

          文件上傳預(yù)覽主要采用FileReader API實(shí)現(xiàn),原理就是將file對(duì)象傳給FileReader的readAsDataURL然后轉(zhuǎn)化為data:URL格式的字符串(base64編碼)以表示所讀取文件的內(nèi)容。 具體代碼如下:

          // 2.文件上傳解析
          let file=$('#file');
          file.on('change', function(e){
              var file=this.files[0];
              var fileReader=new FileReader();
              // 讀取完成觸發(fā)的事件
              fileReader.onload=function(e) {
                  $('.file-wrap')[0].style.backgroundImage='url(' + fileReader.result + ')';
                  imgSrc=fileReader.result;
              }
          
              file && fileReader.readAsDataURL(file);
          })
          

          2. 實(shí)現(xiàn)拼圖分割功能

          一般我們處理這種拼圖游戲都會(huì)有如下方案: 用canvas分割圖片 采用n張不同的切好的切片圖片(方法簡(jiǎn)單,但是會(huì)造成多次請(qǐng)求) * 動(dòng)態(tài)背景分割

          經(jīng)過(guò)權(quán)衡,筆者想出了第三種方法,也是自認(rèn)為比較優(yōu)雅的方法,即動(dòng)態(tài)背景分割,我們只需要使用1張圖片,然后利于css的方式切割圖片,有點(diǎn)經(jīng)典的雪碧圖的感覺(jué),如下:

          本質(zhì)就是我們?cè)O(shè)置九個(gè)div,每個(gè)div都使用同一張圖片,并且圖片大小等于游戲畫布大小,但是我們通過(guò)backgroundPosition(背景定位)的方式來(lái)實(shí)現(xiàn)切割圖片。這樣做的另一個(gè)好處是方便我們實(shí)現(xiàn)洗牌邏輯

          3. 實(shí)現(xiàn)洗牌算法

          洗牌邏輯依托于隨機(jī)算法,這里我們結(jié)合坐標(biāo)系,實(shí)現(xiàn)一個(gè)隨機(jī)生成二維坐標(biāo)系的邏輯,然后通過(guò)改變每個(gè)切片的translate位置,配合過(guò)渡動(dòng)畫,即可實(shí)現(xiàn)洗牌功能和洗牌動(dòng)畫。

          3.1 數(shù)組亂序算法

          數(shù)組亂序比較簡(jiǎn)單,代碼如下:

          // 數(shù)組亂序
          function upsetArr(arr) {
              arr.sort(function(a,b){
                  return Math.random() > 0.5 ? -1 : 1
              })
          }

          3.2 洗牌邏輯

          洗牌邏輯基于數(shù)組亂序,具體邏輯如下:

          // 洗牌方法
          function shuffle(els, arr) {
              upsetArr(arr);
              for(var i=0, len=els.length; i< len; i++) {
                  var el=els[i];
                  el.setAttribute('index', i);  // 將打亂后的數(shù)組索引緩存到元素中
                  el.style.transform='translate(' + arr[i].x + 'vw,' + arr[i].y + 'vh'+ ')';
              }
          }

          3.3 生成n緯矩陣坐標(biāo)

          n維矩陣主要用來(lái)做洗牌和計(jì)算成功率的,具體實(shí)現(xiàn)如下:

          // 生成n維矩陣坐標(biāo)
          function generateMatrix(n, dx, dy) {
              var arr=[], index=0;
              for(var i=0; i< n; i++) {
                  for(var j=0; j< n; j++) {
                      arr.push({x: j*dx, y: i*dy, index: index});
                      index++;
                  }
              }
              return arr
          }
          

          3.4 置換算法

          置換算法主要用來(lái)切換拼圖的,比如用戶想移動(dòng)拼圖,可以用置換來(lái)實(shí)現(xiàn):

          // 數(shù)組置換
          function swap(arr, indexA, indexB) {
              let cache=arr[indexA];
              arr[indexA]=arr[indexB];
              arr[indexB]=cache;
          }

          4. 實(shí)現(xiàn)生成戰(zhàn)績(jī)海報(bào)功能

          生成戰(zhàn)績(jī)海報(bào)筆者采用canvas來(lái)實(shí)現(xiàn),對(duì)于canvas的api不熟悉的可以查看MDN,講的比較詳細(xì)。這里筆者簡(jiǎn)單實(shí)現(xiàn)一個(gè)供大家參考:

          function generateImg() {
              var canvas=document.createElement("canvas");
          
              if(canvas.getContext) {
                  var winW=window.innerWidth,
                      winH=window.innerHeight,
                  ctx=canvas.getContext('2d');
                  canvas.width=winW;
                  canvas.height=winH;
          
                  // 繪制背景
                  // ctx.fillStyle='#06c';
                  var linear=ctx.createLinearGradient(0, 0, 0, winH);
                  linear.addColorStop(0, '#a1c4fd');
                  linear.addColorStop(1, '#c2e9fb');
                  ctx.fillStyle=linear;
                  ctx.fillRect(0, 0, winW, winH);
                  ctx.fill();
          
                  // 繪制頂部圖像
                  var imgH=0;
                  img=new Image();
                  img.src=imgSrc;
                  img.onload=function(){
                      // 繪制的圖片寬為.7winW, 根據(jù)等比換算繪制的圖片高度為 .7winW*imgH/imgW
                      imgH=.6*winW*this.height/this.width;
                      ctx.drawImage(img, .2*winW, .1*winH, .6*winW, imgH);
          
                      drawText();
                      drawTip();
                      drawCode();
                  }
          
                  // 繪制文字
                  function drawText() {
                      ctx.save();
                      ctx.fillStyle='#fff';
                      ctx.font=20 + 'px Helvetica';
                      ctx.textBaseline='hanging';
                      ctx.textAlign='center';
                      ctx.fillText('我只用了' + (180 -dealtime) + 's,' + '快來(lái)挑戰(zhàn)!', winW/2, .15*winH + imgH);
                      ctx.restore();
                  }
          
                  // 繪制提示文字
                  function drawTip() {
                      ctx.save();
                      ctx.fillStyle='#000';
                      ctx.font=14 + 'px Helvetica';
                      ctx.textBaseline='hanging';
                      ctx.textAlign='center';
                      ctx.fillText('關(guān)注下方二維碼開(kāi)始游戲', winW/2, .25*winH + imgH);
                      ctx.restore();
                  }
          
          
                  // 繪制二維碼
                  function drawCode() {
                      var imgCode=new Image();
                      imgCode.src='/piecePlay/images/logo.png';
                      imgCode.onload=function(){
                          ctx.drawImage(imgCode, .35*winW, .3*winH + imgH, .3*winW, .3*winW);
          
                          // 生成預(yù)覽圖
                          var img=new Image();
                          img.src=convertCanvasToImage(canvas, 1).src;
                          img.className='previewImg';
                          img.onload=function(){
                              $('.preview-page')[0].appendChild(this);
                              startDx=startDx - 100;
                              transformX(wrap, startDx + 'vw');
                          }
                      }
                  }  
              } else {
                  alert('瀏覽器不支持canvas!')
              }
          }
          

          H5拼圖小游戲筆者已在github開(kāi)源, 感興趣的可以學(xué)習(xí)參考。以上的邏輯部分的代碼可以直接整合到vue項(xiàng)目中即可,由于實(shí)現(xiàn)比較簡(jiǎn)單, 這里筆者就不詳細(xì)介紹了。

          H5可視化編輯器H5-Dooring

          目前我也在持續(xù)更新H5編輯器H5-Dooring, 它是一款輕松拖拽式制作H5頁(yè)面的工具, 功能非常強(qiáng)大, 感興趣的朋友也可以搜索體驗(yàn)一下.

          最后

          如果想學(xué)習(xí)更多H5游戲, webpack,node,gulp,css3,javascript,nodeJS,canvas數(shù)據(jù)可視化等前端知識(shí)和實(shí)戰(zhàn),歡迎在《趣談前端》學(xué)習(xí)討論,共同探索前端的邊界。


          主站蜘蛛池模板: 老熟女高潮一区二区三区| 日本免费电影一区| 一区二区三区高清在线| 无码精品一区二区三区免费视频| 一区在线观看视频| 日韩一区在线视频| 亚洲国产av一区二区三区| 国产高清在线精品一区二区三区| 日本精品一区二区三区在线视频一| 国产精品视频免费一区二区三区| 美女福利视频一区二区| 国产成人综合精品一区| 人妻无码第一区二区三区| 亚洲天堂一区在线| 三级韩国一区久久二区综合| 精品一区二区无码AV| 国产成人精品一区二区A片带套 | 亚洲精品色播一区二区| 精品一区二区三区无码视频| 久久影院亚洲一区| 无码精品不卡一区二区三区| 中文日韩字幕一区在线观看| 亚洲一区二区三区无码国产| 国产视频一区二区在线播放| 亚洲蜜芽在线精品一区| 一区二区高清在线| 玩弄放荡人妻一区二区三区| 国产精品视频第一区二区三区| 中文字幕av人妻少妇一区二区| 久久99精品免费一区二区| 精品人妻无码一区二区色欲产成人| 中文字幕一区二区三区久久网站| 风间由美性色一区二区三区| 日韩欧国产精品一区综合无码| 好吊妞视频一区二区| 人妻体体内射精一区二区| 夜夜嗨AV一区二区三区| 一区二区三区无码高清视频| 国产成人午夜精品一区二区三区| 亚洲电影国产一区| 国产精品毛片VA一区二区三区|