$$demo <h1 id="result">总共得分:0</h1> <h1 id="hit" hidden></h1>

<div id="one"> <div id="three"> <div id="five"></div> </div> </div> <script> // model const model = { // 总分 total: 0, // 击中得分 score: 0, // 靶子的坐标 left: 0, top: 0, // 是否显示靶子 isShowTarget: true, // 定时器 id timerID: null, // 更新页面 update() { view.render() }, }

// controller
const controller = {
    targetWidth: 0,
    targetHeight: 0,
    init() {
        five.onclick = this.shot(5)
        three.onclick = this.shot(3)
        one.onclick = this.shot(1)

        this.targetWidth = one.clientWidth
        this.targetHeight = one.clientHeight

        this.run()
    },
    shot(score) {
        return () => {
            // 击中得分
            model.score = score
            // 总分
            model.total += score

            // 隐藏靶子
            model.isShowTarget = false

            // 重新定时
            // 使得能每次都是同一时间后显示靶子
            clearInterval(model.timerID)
            this.run()

            // 通知数据发生了更新
            model.update()

            // 阻止冒泡
            event.stopPropagation()
        }
    },
    run() {
        model.timerID = setInterval(() => {
            model.left =
                Math.random() *
                (document.documentElement.clientWidth - this.targetWidth)
            model.top =
                Math.random() *
                (document.documentElement.clientHeight - this.targetHeight)

            // 显示靶子
            model.isShowTarget = true

            // 通知数据发生了更新
            model.update()
        }, 1000)
    },
}

// view
const view = {
    render() {
        hit.textContent = `击中${model.score}分`
        result.textContent = `总共得分:${model.total}`

        one.style.left = `${model.left}px`
        one.style.top = `${model.top}px`

        // 是否显示靶子
        one.hidden = !model.isShowTarget

        // 显示靶子则不显示得分
        hit.hidden = model.isShowTarget
    },
}

window.addEventListener("load", function (event) {
    // 在页面初始化后才能获取到靶子的宽高
    controller.init()
})

</script> <style> html { min-height: 500px; }

#result,
#hit {
    text-align: center;

    /* 禁止选中文本 */
    user-select: none;
}

#hit {
    color: green;
}

#five {
    width: 25px;
    height: 25px;

    border-radius: 50%;
}

#three,
#one {
    padding: 25px;
    border-radius: 50%;
}

#five {
    background-color: #666;
}
#three {
    background-color: #999;
}
#one {
    background-color: #bbb;
}

#one {
    position: fixed;
}

</style>

$$ $$answer

<h1 id="result">总共得分:0</h1>
<h1 id="hit" hidden></h1>

<div id="one">
    <div id="three">
        <div id="five"></div>
    </div>
</div>
<script>
    // model
    const model = {
        // 总分
        total: 0,
        // 击中得分
        score: 0,
        // 靶子的坐标
        left: 0,
        top: 0,
        // 是否显示靶子
        isShowTarget: true,
        // 定时器 id
        timerID: null,
        // 更新页面
        update() {
            view.render()
        },
    }

    // controller
    const controller = {
        targetWidth: 0,
        targetHeight: 0,
        init() {
            five.onclick = this.shot(5)
            three.onclick = this.shot(3)
            one.onclick = this.shot(1)

            this.targetWidth = one.clientWidth
            this.targetHeight = one.clientHeight

            this.run()
        },
        shot(score) {
            return () => {
                // 击中得分
                model.score = score
                // 总分
                model.total += score

                // 隐藏靶子
                model.isShowTarget = false

                // 重新定时
                // 使得能每次都是同一时间后显示靶子
                clearInterval(model.timerID)
                this.run()

                // 通知数据发生了更新
                model.update()

                // 阻止冒泡
                event.stopPropagation()
            }
        },
        run() {
            model.timerID = setInterval(() => {
                model.left =
                    Math.random() *
                    (document.documentElement.clientWidth - this.targetWidth)
                model.top =
                    Math.random() *
                    (document.documentElement.clientHeight - this.targetHeight)

                // 显示靶子
                model.isShowTarget = true

                // 通知数据发生了更新
                model.update()
            }, 1000)
        },
    }

    // view
    const view = {
        render() {
            hit.textContent = `击中${model.score}分`
            result.textContent = `总共得分:${model.total}`

            one.style.left = `${model.left}px`
            one.style.top = `${model.top}px`

            // 是否显示靶子
            one.hidden = !model.isShowTarget

            // 显示靶子则不显示得分
            hit.hidden = model.isShowTarget
        },
    }

    window.addEventListener("load", function (event) {
        // 在页面初始化后才能获取到靶子的宽高
        controller.init()
    })
</script>
<style>
    #result,
    #hit {
        text-align: center;

        /* 禁止选中文本 */
        user-select: none;
    }

    #hit {
        color: green;
    }

    #five {
        width: 25px;
        height: 25px;

        border-radius: 50%;
    }

    #three,
    #one {
        padding: 25px;
        border-radius: 50%;
    }

    #five {
        background-color: #666;
    }
    #three {
        background-color: #999;
    }
    #one {
        background-color: #bbb;
    }

    #one {
        position: fixed;
    }
</style>

$$