成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

前端每日實(shí)戰(zhàn):165# 視頻演示如何用 Vue 創(chuàng)作一個(gè)算術(shù)訓(xùn)練程序(內(nèi)含 3 個(gè)視頻)

isaced / 544人閱讀

摘要:等式右邊的問(wèn)號(hào)和結(jié)果不應(yīng)同時(shí)顯示出來(lái),在用戶思考時(shí)應(yīng)顯示問(wèn)號(hào),思考結(jié)束后應(yīng)隱藏問(wèn)號(hào)顯示結(jié)果。為了增加一點(diǎn)加法的難度,我們把設(shè)置為略此時(shí),每刷新一次頁(yè)面,運(yùn)算數(shù)就會(huì)跟著刷新,因?yàn)槊看雾?yè)面加載都會(huì)運(yùn)行方法生成新的隨機(jī)數(shù)。

效果預(yù)覽

按下右側(cè)的“點(diǎn)擊預(yù)覽”按鈕可以在當(dāng)前頁(yè)面預(yù)覽,點(diǎn)擊鏈接可以全屏預(yù)覽。

https://codepen.io/comehope/pen/dwzRyQ

可交互視頻

此視頻是可以交互的,你可以隨時(shí)暫停視頻,編輯視頻中的代碼。

請(qǐng)用 chrome, safari, edge 打開觀看。

第 1 部分:
https://scrimba.com/p/pEgDAM/ca6wWSk

第 2 部分:
https://scrimba.com/p/pEgDAM/c7Zy2AZ

第 3 部分:
https://scrimba.com/p/pEgDAM/c9R2Gsy

源代碼下載

每日前端實(shí)戰(zhàn)系列的全部源代碼請(qǐng)從 github 下載:

https://github.com/comehope/front-end-daily-challenges

代碼解讀

本項(xiàng)目可以訓(xùn)練加、減、乘、除四則運(yùn)算。比如訓(xùn)練加法時(shí),界面給出 2 個(gè)數(shù)值表示 2 個(gè)加數(shù),小朋友心算出結(jié)果后大聲說(shuō)出,然后點(diǎn)擊“?”按鈕查看結(jié)果,根據(jù)對(duì)照的結(jié)果,如果計(jì)算正確(或錯(cuò)誤),就點(diǎn)擊綠勾(或紅叉),然后再開始下一道測(cè)驗(yàn)。界面中還會(huì)顯示已經(jīng)做過(guò)幾道題,正確率是多少。為了增強(qiáng)趣味性,加入了音效,答對(duì)時(shí)會(huì)響起小貓?zhí)鹈赖慕新?,答錯(cuò)時(shí)響起的是小貓失望的叫聲。

頁(yè)面用純 css 布局,程序邏輯用 vue 框架編寫,用 howler.js 庫(kù)播放音效。整個(gè)應(yīng)用分成 4 個(gè)步驟實(shí)現(xiàn):靜態(tài)頁(yè)面布局、加法的程序邏輯、四則運(yùn)算的程序邏輯、音效處理。

一、頁(yè)面布局

先創(chuàng)建 dom 結(jié)構(gòu),整個(gè)文檔分成 4 部分,.choose-type 是一組多選一按鈕,用于選擇四則運(yùn)算的類型,.score 是成績(jī)統(tǒng)計(jì)數(shù)據(jù),.expression 是一個(gè)算式,它也是游戲的主體部分,.judgment 用于判斷答題是否正確:

.choose-type 一共包含 4 個(gè) input[type=radio] 控件,命名為 arithmetic-type,加、減、乘、除 4 種運(yùn)算類型的值分別為 1、2、3、4,每個(gè)控件后跟隨一個(gè)對(duì)應(yīng)的label,最終我們將把 input 控件隱藏起來(lái),而讓用戶操作 label。

.score 包含 2 個(gè)數(shù)據(jù),一個(gè)是已經(jīng)做過(guò)的題目數(shù),一個(gè)是正確率:

ROUND 15 SCORE 88%

.expression 把一個(gè)表達(dá)式的各部分拆開,以便能修飾表達(dá)式各部分的樣式。.number 表示等式左邊的 2 個(gè)運(yùn)算數(shù),.operation 表示運(yùn)算符和等號(hào),.show 是一個(gè)問(wèn)號(hào),同時(shí)它也是一個(gè)按鈕,當(dāng)心算出結(jié)果后,點(diǎn)擊它,就顯示出 .result 元素,展示運(yùn)算結(jié)果:

10 + 20 = ? 30

.judgment 包含 2 個(gè)按鈕,分別是表示正確的綠勾和表示錯(cuò)誤的紅叉,顯示在結(jié)果的下方:

? ?

至此,完整的 dom 結(jié)構(gòu)如下:

ROUND 15 SCORE 88%
10 + 20 = ? 30
? ?

接下來(lái)用 css 布局。
居中顯示:

body{
    margin: 0;
    height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    background: linear-gradient(lightyellow, tan);
}

設(shè)置應(yīng)用的容器樣式,黑色漸變背景,子元素縱向排列,尺寸用相對(duì)單位 vwem,以便在窗口縮放后能自適應(yīng)新窗口尺寸:

#app {
    width: 66vmin;
    display: flex;
    flex-direction: column;
    align-items: center;
    box-shadow: 0 1em 4em rgba(0, 0, 0, 0.5);
    border-radius: 2em;
    padding: 8em 5em;
    background: linear-gradient(black, dimgray, black);
    font-family: sans-serif;
    font-size: 1vw;
    user-select: none;
}

布局 .choose-type 區(qū)域。隱藏 input 控件,設(shè)置 label 為天藍(lán)色:

.choose-type input[name=arithmetic-type] {
    position: absolute;
    visibility: hidden;
}

.choose-type label {
    font-size: 2.5em;
    color: skyblue;
    margin: 0.3em;
    letter-spacing: 0.02em;
}

label 之間加入分隔線:

.choose-type label {
    position: relative;
}

.choose-type label:not(:first-of-type)::before {
    content: "|";
    position: absolute;
    color: skyblue;
    left: -0.5em;
    filter: opacity(0.6);
}

設(shè)置 label 在鼠標(biāo)懸停時(shí)變色,當(dāng) input 控件被選中時(shí)對(duì)應(yīng)的 label 會(huì)變色、首字母變大寫并顯示下劃線,為了使視覺(jué)效果切換平滑,設(shè)置了緩動(dòng)時(shí)間。這里沒(méi)有使用 text-decoration: underline 設(shè)置下劃線,是因?yàn)橛?border 才有緩動(dòng)效果:

.choose-type label {
    transition: 0.3s;
}

.choose-type label:hover {
    color: deepskyblue;
    cursor: pointer;
}

.choose-type input[name=arithmetic-type]:checked + label {
    text-transform: capitalize;
    color: deepskyblue;
    border-style: solid;
    border-width: 0 0 0.1em 0;
}

.score 區(qū)域用銀色字,2 組數(shù)據(jù)之間留出一些間隔:

.score{
    font-size: 2em;
    color: silver;
    margin: 1em 0 2em 0;
    width: 45%;
    display: flex;
    justify-content: space-between;
}

.expression 區(qū)域用大字號(hào),各元素用不同的顏色區(qū)分:

.expression {
    font-size: 12em;
    display: flex;
    align-items: center;
}

.expression span {
    margin: 0 0.05em;
}

.expression .number{
    color: orange;
}

.expression .operation{
    color: skyblue;
}

.expression .result{
    color: gold;
}

.show 是等號(hào)右邊的問(wèn)號(hào),它同時(shí)也是一個(gè)按鈕,在這里把按鈕的樣式 .button 獨(dú)立出來(lái),因?yàn)楹竺孢€會(huì)用到 .button 樣式:

.expression .show {
    color: skyblue;
    font-size: 0.8em;
    line-height: 1em;
    width: 1.5em;
    text-align: center;
}

.button {
    background-color: #222;
    border: 1px solid #555;
    padding: 0.1em;
}

.button:hover {
    background-color: #333;
    cursor: pointer;
}

.button:active {
    background-color: #222;
}

設(shè)置 .judgment 區(qū)域 2 個(gè)按鈕的樣式,它們還共享了 .button 樣式:

.judgment {
    font-size: 8em;
    align-self: flex-end;
}

.judgment .wrong {
    color: orangered;
}

.judgment .right {
    color: lightgreen;
}

至此,靜態(tài)頁(yè)面布局完成,完整的 css 代碼如下:

body{
    margin: 0;
    height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    background: linear-gradient(lightyellow, tan);
}

#app {
    width: 66vw;
    display: flex;
    flex-direction: column;
    align-items: center;
    box-shadow: 0 1em 4em rgba(0, 0, 0, 0.5);
    border-radius: 2em;
    padding: 8em 5em;
    background: linear-gradient(black, dimgray, black);
    font-family: sans-serif;
    font-size: 1vw;
    user-select: none;
}

.choose-type input[name=arithmetic-type] {
    position: absolute;
    visibility: hidden;
}

.choose-type label {
    font-size: 2.5em;
    color: skyblue;
    margin: 0.3em;
    letter-spacing: 0.02em;
    position: relative;
    transition: 0.3s;
}

.choose-type label:not(:first-of-type)::before {
    content: "|";
    position: absolute;
    color: skyblue;
    left: -0.5em;
    filter: opacity(0.6);
}

.choose-type label:hover {
    color: deepskyblue;
    cursor: pointer;
}

.choose-type input[name=arithmetic-type]:checked + label {
    text-transform: capitalize;
    color: deepskyblue;
    border-style: solid;
    border-width: 0 0 0.1em 0;
}

.score{
    font-size: 2em;
    color: silver;
    margin: 1em 0 2em 0;
    width: 45%;
    display: flex;
    justify-content: space-between;
}

.expression {
    font-size: 12em;
    display: flex;
    align-items: center;
}

.expression span {
    margin: 0 0.05em;
}

.expression .number{
    color: orange;
}

.expression .operation{
    color: skyblue;
}

.expression .result{
    color: gold;
}

.expression .show {
    color: skyblue;
    font-size: 0.8em;
    line-height: 1em;
    width: 1.5em;
    text-align: center;
}

.judgment {
    font-size: 8em;
    align-self: flex-end;
}

.judgment .wrong {
    color: orangered;
}

.judgment .right {
    color: lightgreen;
}

.button {
    background-color: #222;
    border: 1px solid #555;
    padding: 0.1em;
}

.button:hover {
    background-color: #333;
    cursor: pointer;
}

.button:active {
    background-color: #222;
}
二、加法的程序邏輯

我們先用加法把流程跑通,再把加法擴(kuò)展為四則運(yùn)算。

引入 vue 框架:

創(chuàng)建一個(gè) Vue 對(duì)象:

let vm = new Vue({
    el: "#app",
})

定義數(shù)據(jù),round 存儲(chǔ)題目數(shù),round.all 表示總共答過(guò)了多少道題,round.right 表示答對(duì)了多少道題;numbers 數(shù)組包含 2 個(gè)元素,用于存儲(chǔ)等式左邊的 2 個(gè)運(yùn)算數(shù),用數(shù)組是為了便于后面使用解構(gòu)語(yǔ)法:

let vm = new Vue({
    ///...略
    data: {
        round: {all: 0, right: 0},
        numbers: [0, 0],
    }
    ///...略
})

定義計(jì)算屬性,operation 是操作符,目前是加號(hào),result 是計(jì)算結(jié)果,等于 2 個(gè)運(yùn)算數(shù)相加,score 是正確率,開始做第一題時(shí)正確率顯示為 100%,后續(xù)根據(jù)實(shí)際答對(duì)的題數(shù)計(jì)算正確率:

let vm = new Vue({
    ///...略
    computed: {
        operation: function() {
            return "+"
        },
        result: function() {
            return this.numbers[0] + this.numbers[1]
        },
        score: function() {
            return this.round.all == 1
                ? 100
                : Math.round(this.round.right / (this.round.all - 1) * 100)
        }
    },
    ///...略
})

把數(shù)據(jù)綁定到 html 模板中:

ROUND {{round.all - 1}} SCORE {{score}}%
{{numbers[0]}} {{operation}} {{numbers[1]}} = ? {{result}}

至此,頁(yè)面中的數(shù)據(jù)都是動(dòng)態(tài)獲取的了。

等式右邊的問(wèn)號(hào)和結(jié)果不應(yīng)同時(shí)顯示出來(lái),在用戶思考時(shí)應(yīng)顯示問(wèn)號(hào),思考結(jié)束后應(yīng)隱藏問(wèn)號(hào)顯示結(jié)果。為此,增加一個(gè) isThinking 變量,用于標(biāo)志用戶所處的狀態(tài),默認(rèn)為 true,即進(jìn)入游戲時(shí),用戶開始思考第 1 道題目:

let vm = new Vue({
    ///...略
    data: {
        round: {all: 0, right: 0},
        numbers: [0, 0],
        isThinking: true,
    },
    ///...略
})

isThinking 綁定到 html 模板中,用戶思考時(shí)只顯示問(wèn)號(hào) .show,否則顯示結(jié)果 .result 和判斷結(jié)果正確與否的按鈕 .judgment,此處請(qǐng)注意,對(duì)于占據(jù)同一個(gè)視覺(jué)位置的元素,用 v-show=false,即 display: none 隱藏,對(duì)于占據(jù)獨(dú)立視覺(jué)位置的元素,用 visibility: hidden 隱藏:

? {{result}}

接下來(lái)生成隨機(jī)運(yùn)算數(shù)。創(chuàng)建一個(gè) next() 方法用于開始下一個(gè)題目,那么在頁(yè)面載入后就應(yīng)執(zhí)行這個(gè)方法初始化第 1 道題目:

let vm = new Vue({
    ///...略
    methods: {
        next: function() {

        },
    },
})

window.onload = vm.next

next() 方法一方面要負(fù)責(zé)初始化運(yùn)算數(shù),還要把答過(guò)的題目數(shù)加1,這里獨(dú)立出來(lái)一個(gè) newRound() 方法是為了方便后面復(fù)用它:

let vm = new Vue({
    ///...略
    methods: {
        newRound: function() {
            this.numbers = this.getNumbers()
            this.isThinking = true
        },
        next: function() {
            this.newRound()
            this.round.all++
        },
    },
})

getNumbers() 方法用于生成 2 個(gè)隨機(jī)數(shù),它調(diào)用 getRandomNumber() 方法來(lái)生成一個(gè)隨機(jī)數(shù),其中 level 參數(shù)表示隨機(jī)數(shù)的取值范圍,level 為 1 時(shí),生成的隨機(jī)數(shù)介于 1 ~ 9 之間,level 為 2 時(shí),生成的隨機(jī)數(shù)介于 10 ~ 99 之間。為了增加一點(diǎn)加法的難度,我們把 level 設(shè)置為 2:

let vm = new Vue({
    ///...略
    methods: {
        getRandomNumber: function(level) {
            let min = Math.pow(10, level - 1)
            let max = Math.pow(10, level)
            return min + Math.floor(Math.random() * (max - min))
        },
        getNumbers: function() {
            let level = 2
            let a = this.getRandomNumber(level)
            let b = this.getRandomNumber(level)
            return [a, b]
        },
        newRound: function() {
            this.numbers = this.getNumbers()
            this.isThinking = true
        },
        next: function() {
            this.newRound()
            this.round.all++
        },
    },
})

此時(shí),每刷新一次頁(yè)面,運(yùn)算數(shù)就會(huì)跟著刷新,因?yàn)槊看雾?yè)面加載都會(huì)運(yùn)行 vm.next() 方法生成新的隨機(jī)數(shù)。
接下來(lái)我們來(lái)處理按鈕事件,頁(yè)面中一共有 3 個(gè)按鈕:?jiǎn)柼?hào)按鈕 .show 被點(diǎn)擊后應(yīng)顯示結(jié)果;綠勾按鈕 .right 被點(diǎn)擊后應(yīng)給答對(duì)題的數(shù)目加 1,然后進(jìn)入下一道題;紅叉按鈕 .wrong 被點(diǎn)擊后直接進(jìn)入下一道題,所以我們?cè)诔绦蛑性黾?3 個(gè)方法,getResult()answerRight()、answerWrong 分別對(duì)應(yīng)上面的 3 個(gè)點(diǎn)擊事件:

let vm = new Vue({
    ///...略
    methods: {
        ///...略
        getResult: function() {
            this.isThinking = false
        },
        answerRight: function() {
            this.round.right++
            this.next()
        },
        answerWrong: function() {
            this.next()
        },
    },
})

把事件綁定到 html 模板:

?
? ?

至此,加法程序就全部完成了,可以一道又一道題一直做下去。
此時(shí)的 html 代碼如下:

ROUND {{round.all - 1}} SCORE {{score}}%
{{numbers[0]}} {{operation}} {{numbers[1]}} = ? {{result}}
? ?

此時(shí)的 javascript 代碼如下:

let vm = new Vue({
    el: "#app",

    data: {
        round: {all: 0, right: 0},
        numbers: [0, 0],
        isThinking: true,
    },

    computed: {
        operation: function() {
            return "+"
        },
        result: function() {
            return this.numbers[0] + this.numbers[1]
        },
        score: function() {
            return this.round.all == 1
                ? 100
                : Math.round(this.round.right / (this.round.all - 1) * 100)
        }
    },
    
    methods: {
        getRandomNumber: function(level) {
            let min = Math.pow(10, level - 1)
            let max = Math.pow(10, level)
            return min + Math.floor(Math.random() * (max - min))
        },
        getNumbers: function() {
            let level = 2
            let a = this.getRandomNumber(level)
            let b = this.getRandomNumber(level)
            return [a, b]
        },
        newRound: function() {
            this.numbers = this.getNumbers()
            this.isThinking = true
        },
        next: function() {
            this.newRound()
            this.round.all++
        },
        getResult: function() {
            this.isThinking = false
        },
        answerRight: function() {
            this.round.right++
            this.next()
        },
        answerWrong: function() {
            this.next()
        },
    },
})

window.onload = vm.next
三、四則運(yùn)算的程序邏輯

我們先來(lái)評(píng)估一下四種運(yùn)算在這個(gè)程序里會(huì)在哪些方面有差異。首先,運(yùn)算符不同,加、減、乘、除的運(yùn)算符分別是“+”、“-”、“×”、“÷”;第二是運(yùn)算函數(shù)不同,這個(gè)不用多說(shuō)。根據(jù)這 2 點(diǎn),我們定義一個(gè)枚舉對(duì)象 ARITHMETIC_TYPE,用它存儲(chǔ)四種運(yùn)算的差異,每個(gè)枚舉對(duì)象有 2 個(gè)屬性,operation 代表操作符,f() 函數(shù)是運(yùn)算邏輯。另外,我們?cè)俾暶饕粋€(gè)變量 arithmeticType,用于存儲(chǔ)用戶當(dāng)前選擇的運(yùn)算類型:

let vm = new Vue({
    ///...略
    data: {
        ///...略
        ARITHMETIC_TYPE: {
            ADDITION: 1,
            SUBTRACTION: 2,
            MULTIPLICATION: 3,
            DIVISION: 4,
            properties: {
                1: {operation: "+", f: ([x, y]) => x + y},
                2: {operation: "-", f: ([x, y]) => x - y},
                3: {operation: "×", f: ([x, y]) => x * y},
                4: {operation: "÷", f: ([x, y]) => x / y}
            }
        },
        arithmeticType: 1,
    },
})

改造計(jì)算屬性中關(guān)于運(yùn)算符和計(jì)算結(jié)果的函數(shù):

let vm = new Vue({
    ///...略
    computed: {
        ///...略
        operation: function() {
            // return "+"
            return this.ARITHMETIC_TYPE.properties[this.arithmeticType].operation
        },
        result: function() {
            // return this.numbers[0] + this.numbers[1]
            return this.ARITHMETIC_TYPE.properties[this.arithmeticType].f(this.numbers)
        },
        ///...略
    },
})

因?yàn)樯厦?2 個(gè)計(jì)算屬性都用到了 arithmeticType 變量,所以當(dāng)用戶選擇運(yùn)算類型時(shí),這 2 個(gè)計(jì)算屬性的值會(huì)自動(dòng)更新。另外,為了讓 ui 邏輯更嚴(yán)密,我們令 arithmeticType 的值改變時(shí),開始一個(gè)新題目:

let vm = new Vue({
    ///...略
    watch: {
        arithmeticType: function() {
            this.newRound()
        }
    }
})

然后,把 arithmeticType 變量綁定到 html 模板中的 input 控件上:

至此,當(dāng)選擇不同的運(yùn)算類型時(shí),表達(dá)式的運(yùn)算符和計(jì)算結(jié)果都會(huì)自動(dòng)更新為匹配的值,比如選擇乘法時(shí),運(yùn)算符就變?yōu)槌颂?hào),運(yùn)算結(jié)果為 2 個(gè)運(yùn)算數(shù)的乘積。
不過(guò),此時(shí)的最明顯的問(wèn)題是,除法的運(yùn)算數(shù)因?yàn)槭请S機(jī)生成的,商經(jīng)常是無(wú)限小數(shù),為了更合理,我們規(guī)定這里的除法只做整除運(yùn)算。再延伸一下,對(duì)于減法,為了避免差為負(fù)數(shù),也規(guī)定被減數(shù)不小于減數(shù)。
解決這個(gè)問(wèn)題的辦法是在 ARITHMETIC_TYPE 枚舉中添加一個(gè) gen() 函數(shù),用于存儲(chǔ)生成運(yùn)算數(shù)的邏輯,gen() 函數(shù)接收一個(gè)包含 2 個(gè)隨機(jī)數(shù)的數(shù)組作為參數(shù),對(duì)于加法和乘法,直接返回?cái)?shù)組本身,減法的 gen() 函數(shù)為 gen: ([a, b]) => a >= b ? [a, b] : [b, a],除法的 gen() 函數(shù)為 gen: ([a, b]) => [a * b, b],經(jīng)過(guò)如此處理的運(yùn)算數(shù),就可以實(shí)現(xiàn)上面規(guī)定的邏輯了。改造后的 ARITHMETIC_TYPE 如下:

let vm = new Vue({
    ///...略
    data: {
        ///...略
        ARITHMETIC_TYPE: {
            ADDITION: 1,
            SUBTRACTION: 2,
            MULTIPLICATION: 3,
            DIVISION: 4,
            pproperties: {
                1: {operation: "+", f: (arr) => arr, gen: ([a, b]) => [a, b]},
                2: {operation: "-", f: ([x, y]) => x - y, gen: ([a, b]) => a >= b ? [a, b] : [b, a]},
                3: {operation: "×", f: (arr) => arr, gen: ([a, b]) => [a, b]},
                4: {operation: "÷", f: ([x, y]) => x / y, gen: ([a, b]) => [a * b, b]}
            }
        },
        ///...略
    },
    ///...略
})

然后,在 getNumbers() 中調(diào)用 gen() 方法:

let vm = new Vue({
    ///...略
    methods: {
        ///...略
        getNumbers: function() {
            let level = 2
            let a = this.getRandomNumber(2)
            let b = this.getRandomNumber(2)
            // return [a, b]
            return this.ARITHMETIC_TYPE.properties[this.arithmeticType].gen([a, b])
        },
        ///...略
    },
    ///...略
})

至此,減法可以保證差不為負(fù)數(shù),除法也可以保證商是整數(shù)了。
接下來(lái),我們來(lái)配置訓(xùn)練難度。對(duì)大多數(shù)人來(lái)說(shuō),2 個(gè)二位數(shù)的加減法不是很難,但是 2 個(gè)二位數(shù)的乘除法的難度就大多了。在生成隨機(jī)數(shù)時(shí),因?yàn)槎x了 level=2,所以取值范圍固定是 11 ~ 99,我們希望能夠靈活配置每個(gè)運(yùn)算數(shù)的取值范圍,為此,我們需要再為 ARITHMETIC_TYPE 枚舉中增加一個(gè) level 屬性,用于表示隨機(jī)數(shù)的取值范圍,它是一個(gè)包含 2 個(gè)元素的數(shù)組,分別表示 2 個(gè)運(yùn)算數(shù)的取值范圍,改造后的 ARITHMETIC_TYPE 如下:

let vm = new Vue({
    ///...略
    data: {
        ///...略
        ARITHMETIC_TYPE: {
            ADDITION: 1,
            SUBTRACTION: 2,
            MULTIPLICATION: 3,
            DIVISION: 4,
            properties: {
                1: {operation: "+", f: ([x, y]) => x + y, gen: (arr) => arr, level: [3, 2]},
                2: {operation: "-", f: ([x, y]) => x - y, gen: ([a, b]) => a >= b ? [a, b] : [b, a], level: [3, 2]},
                3: {operation: "×", f: ([x, y]) => x * y, gen: (arr) => arr, level: [2, 1]},
                4: {operation: "÷", f: ([x, y]) => x / y, gen: ([a, b]) => [a * b, b], level: [2, 1]}
            }
        },
        ///...略
    },
    ///...略
})

然后,把 getNumbers() 函數(shù)的 level 變量的值改為從枚舉 ARITHMETIC_TYPE 中取值:

let vm = new Vue({
    ///...略
    methods: {
        getNumbers: function() {
            let level = this.ARITHMETIC_TYPE.properties[this.arithmeticType].level
            let a = this.getRandomNumber(level[0])
            let b = this.getRandomNumber(level[1])
            return this.ARITHMETIC_TYPE.properties[this.arithmeticType].gen([a, b])
        },
        ///...略
    },
    ///...略
})

現(xiàn)在運(yùn)行程序可以看到,加減法的 2 個(gè)運(yùn)算數(shù)分別是 3 位數(shù)和 2 位數(shù),而乘除法的 2 個(gè)運(yùn)算數(shù)則分別是 2 位數(shù)和 1 位數(shù),你也可以根據(jù)自己的需要來(lái)調(diào)整訓(xùn)練難度。
至此,四則運(yùn)算的程序邏輯全部完成,此時(shí)的 javascript 代碼如下:

let vm = new Vue({
    el: "#app",

    data: {
        round: {all: 0, right: 0},
        numbers: [0, 0],
        isThinking: true,
        ARITHMETIC_TYPE: {
            ADDITION: 1,
            SUBTRACTION: 2,
            MULTIPLICATION: 3,
            DIVISION: 4,
            properties: {
                1: {operation: "+", f: ([x, y]) => x + y, gen: (arr) => arr, level: 2},
                2: {operation: "-", f: ([x, y]) => x - y, gen: ([a, b]) => a >= b ? [a, b] : [b, a], level: 2},
                3: {operation: "×", f: ([x, y]) => x * y, gen: (arr) => arr, level: 1},
                4: {operation: "÷", f: ([x, y]) => x / y, gen: ([a, b]) => [a * b, b], level: 1}
            }
        },
        arithmeticType: 1,
    },

    computed: {
        operation: function() {
            return this.ARITHMETIC_TYPE.properties[this.arithmeticType].operation
        },
        result: function() {
            return this.ARITHMETIC_TYPE.properties[this.arithmeticType].f(this.numbers)
        },
        score: function() {
            return this.round.all == 1
                ? 100
                : Math.round(this.round.right / (this.round.all - 1) * 100)
        }
    },
    
    methods: {
        getRandomNumber: function(level) {
            let min = Math.pow(10, level - 1)
            let max = Math.pow(10, level)
            return min + Math.floor(Math.random() * (max - min))
        },
        getNumbers: function() {
            let level = this.ARITHMETIC_TYPE.properties[this.arithmeticType].level
            let a = this.getRandomNumber(level[0])
            let b = this.getRandomNumber(level[1])
            return this.ARITHMETIC_TYPE.properties[this.arithmeticType].gen([a, b])
        },
        newRound: function() {
            this.numbers = this.getNumbers()
            this.isThinking = true
        },
        next: function() {
            this.newRound()
            this.round.all++
        },
        getResult: function() {
            this.isThinking = false
        },
        answerRight: function() {
            this.round.right++
            this.next()
        },
        answerWrong: function() {
            this.next()
        },
    },

    watch: {
        arithmeticType: function() {
            this.newRound()
        }
    }
})

window.onload = vm.next
四、音效處理

引入 howler 庫(kù):

聲明變量 sound,它有 2 個(gè)屬性 rightwrong,分別代表回答正確和錯(cuò)誤時(shí)的音效,屬性值是一個(gè) Howl 對(duì)象,在構(gòu)造函數(shù)中指定音頻文件的 url:

let vm = new Vue({
    ///...略
    data: {
        ///...略
        sound: {
            right: new Howl({src: ["https://freesound.org/data/previews/203/203121_777645-lq.mp3"]}),
            wrong: new Howl({src: ["https://freesound.org/data/previews/415/415209_5121236-lq.mp3"]})
        },
    },
    ///...略
})

answerRight() 方法和 answerWrong() 方法中分別調(diào)用播放聲音的 play() 方法即可:

let vm = new Vue({
    ///...略
    methods: {
        ///...略
        answerRight: function() {
            this.round.right++
            this.sound.right.play()
            this.next()
        },
        answerWrong: function() {
            this.sound.wrong.play()
            this.next()
        },
    ///...略
})

現(xiàn)在,當(dāng)點(diǎn)擊綠勾時(shí),就會(huì)響起小貓?zhí)鹈赖慕新?;?dāng)點(diǎn)擊紅叉時(shí),響起的是小貓失望的叫聲。
至此,程序全部開發(fā)完成,最終的 javascript 代碼如下:

let vm = new Vue({
    el: "#app",

    data: {
        round: {all: 0, right: 0},
        numbers: [0, 0],
        isThinking: true,
        ARITHMETIC_TYPE: {
            ADDITION: 1,
            SUBTRACTION: 2,
            MULTIPLICATION: 3,
            DIVISION: 4,
            properties: {
                1: {operation: "+", f: ([x, y]) => x + y, gen: (arr) => arr, level: [3, 2]},
                2: {operation: "-", f: ([x, y]) => x - y, gen: ([a, b]) => a >= b ? [a, b] : [b, a], level: [3, 2]},
                3: {operation: "×", f: ([x, y]) => x * y, gen: (arr) => arr, level: [2, 1]},
                4: {operation: "÷", f: ([x, y]) => x / y, gen: ([a, b]) => [a * b, b], level: [2, 1]}
            }
        },
        arithmeticType: 1,
        sound: {
            right: new Howl({src: ["https://freesound.org/data/previews/203/203121_777645-lq.mp3"]}),
            wrong: new Howl({src: ["https://freesound.org/data/previews/415/415209_5121236-lq.mp3"]})
        },
    },

    computed: {
        operation: function() {
            return this.ARITHMETIC_TYPE.properties[this.arithmeticType].operation
        },
        result: function() {
            return this.ARITHMETIC_TYPE.properties[this.arithmeticType].f(this.numbers)
        },
        score: function() {
            return this.round.all == 1
                ? 100
                : Math.round(this.round.right / (this.round.all - 1) * 100)
        }
    },
    
    methods: {
        getRandomNumber: function(level) {
            let min = Math.pow(10, level - 1)
            let max = Math.pow(10, level)
            return min + Math.floor(Math.random() * (max - min))
        },
        getNumbers: function() {
            let level = this.ARITHMETIC_TYPE.properties[this.arithmeticType].level
            let a = this.getRandomNumber(level[0])
            let b = this.getRandomNumber(level[1])
            return this.ARITHMETIC_TYPE.properties[this.arithmeticType].gen([a, b])
        },
        newRound: function() {
            this.numbers = this.getNumbers()
            this.isThinking = true
        },
        next: function() {
            this.newRound()
            this.round.all++
        },
        getResult: function() {
            this.isThinking = false
        },
        answerRight: function() {
            this.round.right++
            this.sound.right.play()
            this.next()
        },
        answerWrong: function() {
            this.sound.wrong.play()
            this.next()
        },
    },

    watch: {
        arithmeticType: function() {
            this.newRound()
        }
    }
})

window.onload = vm.next

大功告成!

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/100468.html

相關(guān)文章

  • 前端每日實(shí)戰(zhàn) 2018年10月至2019年6月項(xiàng)目匯總(共 20 個(gè)項(xiàng)目)

    摘要:過(guò)往項(xiàng)目年月份項(xiàng)目匯總共個(gè)項(xiàng)目年月份項(xiàng)目匯總共個(gè)項(xiàng)目年月份項(xiàng)目匯總共個(gè)項(xiàng)目年月份項(xiàng)目匯總共個(gè)項(xiàng)目年月份項(xiàng)目匯總共個(gè)項(xiàng)目年月份項(xiàng)目匯總共個(gè)項(xiàng)目年月至年月發(fā)布的項(xiàng)目前端每日實(shí)戰(zhàn)專欄每天分解一個(gè)前端項(xiàng)目,用視頻記錄編碼過(guò)程,再配合詳細(xì)的代碼解讀, 過(guò)往項(xiàng)目 2018 年 9 月份項(xiàng)目匯總(共 26 個(gè)項(xiàng)目) 2018 年 8 月份項(xiàng)目匯總(共 29 個(gè)項(xiàng)目) 2018 年 7 月份項(xiàng)目匯總(...

    muddyway 評(píng)論0 收藏0
  • 前端每日實(shí)戰(zhàn):163# 視頻演示何用原生 JS 創(chuàng)作個(gè)多選一場(chǎng)景的交互游戲(內(nèi)含 3 個(gè)視頻

    摘要:本項(xiàng)目將設(shè)計(jì)一個(gè)多選一的交互場(chǎng)景,用進(jìn)行頁(yè)面布局用制作動(dòng)畫效果用原生編寫程序邏輯。中包含個(gè)展示頭像的和個(gè)標(biāo)明當(dāng)前被選中頭像的。 showImg(https://segmentfault.com/img/bVbknOW?w=400&h=302); 效果預(yù)覽 按下右側(cè)的點(diǎn)擊預(yù)覽按鈕可以在當(dāng)前頁(yè)面預(yù)覽,點(diǎn)擊鏈接可以全屏預(yù)覽。 https://codepen.io/comehope/pen/L...

    pakolagij 評(píng)論0 收藏0
  • 前端每日實(shí)戰(zhàn):163# 視頻演示何用原生 JS 創(chuàng)作個(gè)多選一場(chǎng)景的交互游戲(內(nèi)含 3 個(gè)視頻

    摘要:本項(xiàng)目將設(shè)計(jì)一個(gè)多選一的交互場(chǎng)景,用進(jìn)行頁(yè)面布局用制作動(dòng)畫效果用原生編寫程序邏輯。中包含個(gè)展示頭像的和個(gè)標(biāo)明當(dāng)前被選中頭像的。 showImg(https://segmentfault.com/img/bVbknOW?w=400&h=302); 效果預(yù)覽 按下右側(cè)的點(diǎn)擊預(yù)覽按鈕可以在當(dāng)前頁(yè)面預(yù)覽,點(diǎn)擊鏈接可以全屏預(yù)覽。 https://codepen.io/comehope/pen/L...

    antz 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<