canvas实现H5接物类小游戏

6 篇文章 0 订阅

在vue中实现接物类小游戏

先放张效果图
在这里插入图片描述
在这里插入图片描述

<template>
    <div id="container">
        <div id="guidePanel"></div>
        <div id="gamepanel">
            <div class="score-wrap">
                <div class="heart"></div>
                <span id="score">0</span>
            </div>
            <canvas v-show="isShowCanvas" id="stage"></canvas>
        </div>
        
        <div id="gameoverPanel"></div>
        <div id="resultPanel">
            <!-- <div class="weixin-share"></div> -->
            <a href="javascript:void(0)" class="replay"></a>
            <div id="fenghao"></div>
            <!-- <div id="scorecontent">
                您在<span id="stime" class="lighttext">2378</span>秒内抢到了<span id="sscore" class="lighttext">21341</span>个月饼<br>超过了<span id="suser" class="lighttext">31%</span>的用户!
            </div> -->
            <!-- <div class="textc">
                <span class="btn1 share">请小伙伴吃月饼</span>
            </div> -->
        </div>
        <audio style="display: none;" id="media" src="https://ppt-mp3cdn.hrxz.com/d/file/filemp3/hrxz.com-owjof3jyx1p46949.mp3"></audio>
        <img :class="{ 'isRotate': isRotateMusic }" class="music_btn" :src="isPlayMusic ? '/static/image/active/musicOn.png' : '/static/image/active/musicOff.png'" alt="音乐播放" @click="playPause">
    </div>
</template>

<script>
export default {
    name: 'activeTest',
    data () {
        return {
            isShowCanvas: true,
            isRotateMusic: false,
            isPlayMusic: true
        }
    },
    mounted () {
        // 
        const self = this
        // 实现canvas铺满整个屏幕
        var canvas = document.getElementById('stage')
		canvas.setAttribute('width', window.innerWidth)
		canvas.setAttribute('height', window.innerHeight)
        // 接物类
        function Ship (ctx) {
            // 预加载图片
            // gameMonitor.im.loadImage(['/static/game/static/img/player.png'])
            this.width = 70
            this.height = 70
            this.left = gameMonitor.w/2 - this.width/2
            this.top = gameMonitor.h - this.height
            // this.player = gameMonitor.im.createImage('/static/game/static/img/player.png')
            let myImg = new Image()
            myImg.src = '/static/game/static/img/player.png'
            this.player = myImg
            this.paint = function () {
                // 取消上下移动
                ctx.drawImage(this.player, this.left, gameMonitor.h - this.height, this.width, this.height)
            }
            // 保存接收物移动的位置
            this.setPosition = function (event) {
                var tarL = event.changedTouches[0].clientX
                // 取消上下移动
                // var tarT = event.changedTouches[0].clientY
                // 测试减少计算 能否减少移动飞船时的重影
                this.left = tarL - 35
                // this.top = tarT - this.height/2
                if (this.left < 0) {
                    this.left = 0
                }
                if (this.left > gameMonitor.w - this.width) {
                    this.left = gameMonitor.w - this.width
                }
                // if (this.top > gameMonitor.h - this.height) {
                //     this.top = gameMonitor.h - this.height
                // }
                // if (this.top < gameMonitor.h - this.height) {
                //     this.top = gameMonitor.h - this.height
                // }
                this.paint()
            }
            // 监听touch时间,来调用setPosition来计算位置
            this.controll = function () {
                const _this = this
                const stage = $('#gamepanel')
                let move = false
                stage.on(gameMonitor.eventType.start, function (event) {
                    _this.setPosition(event)
                    move = true
                }).on(gameMonitor.eventType.end, function () {
                    move = false
                }).on(gameMonitor.eventType.move, function (event) {
                    event.preventDefault()
                    if(move) {
                        _this.setPosition(event)	
                    }
                })
            }
            // 每次帧刷新时做碰撞检测(两圆心的距离小于半径之和,则认为发生了碰撞)
            this.eat = function (foodlist) {
                for(let i = foodlist.length-1; i >= 0; i--) {
                    let f = foodlist[i]
                    if (f) {
                        const l1 = this.top+this.height/2 - (f.top+f.height/2)
                        const l2 = this.left+this.width/2 - (f.left+f.width/2)
                        const l3 = Math.sqrt(l1*l1 + l2*l2)
                        if (l3 <= this.height/2 + f.height/2) {
                            foodlist[f.id] = null
                            if (f.type == 0) {
                                gameMonitor.stop()
                                $('#gameoverPanel').show()
                                self.isShowCanvas = false
                                setTimeout(function () {
                                    $('#gameoverPanel').hide()
                                    $('#resultPanel').show()
                                    gameMonitor.getScore()
                                }, 2000)
                            } else {
                                document.querySelector('#media').currentTime = 0
                                document.querySelector('#media').play()
                                $('#score').text(++gameMonitor.score)
                                $('.heart').removeClass('hearthot').addClass('hearthot')
                                setTimeout(function () {
                                    $('.heart').removeClass('hearthot')
                                }, 200)
                            }
                        }
                    }
                }
            }
        }
        /**
         * 掉落物类
         * type 0 == 炸弹 1 == 好东西
         */
        function Food (type, left, id) {
            // 加速的时间间隔
            this.speedUpTime = 600
            this.id = id
            this.type = type
            this.width = 50
            this.height = 50
            this.left = left
            this.top = -50
            // 掉落时间
            this.speed = 0.01 * Math.pow(1.2, Math.floor(gameMonitor.time/this.speedUpTime))
            console.log(this.speed);
            this.loop = 0
            const p = this.type == 0 ? '/static/game/static/img/food1.png' : '/static/game/static/img/food2.png'
            this.pic = gameMonitor.im.createImage(p)
        }
        Food.prototype.paint = function (ctx) {
            ctx.drawImage(this.pic, this.left, this.top, this.width, this.height)
        }
        Food.prototype.move = function (ctx) {
            if (gameMonitor.time % this.speedUpTime == 0) {
                this.speed *= 1.2
            }
            this.top += ++this.loop * this.speed
            if (this.top>gameMonitor.h) {
                gameMonitor.foodList[this.id] = null
            } else {
                // 去掉上下移动
                // this.paint(ctx)
            }
        }

    // 图片加载器,让浏览器预加载图片,方便直接调用
    function ImageMonitor () {
        let imgArray = []
        return {
            // 返回当前数组中对应的图片,如果不存在该图片,则new一个来返回
            createImage: function (src) {
                return typeof imgArray[src] != 'undefined' ? imgArray[src] : (imgArray[src] = new Image(), imgArray[src].src = src, imgArray[src])
            },
            // 接收一个数组和一个回调函数,把数组中的图片路径逐一加载,保存到一个数组中,最后一张图片加载完后执行一个回调函数
            loadImage: function (arr, callback) {
                for(let i = 0, l = arr.length; i < l; i++) {
                    let img = arr[i]
                    imgArray[img] = new Image()
                    imgArray[img].onload = function () {
                        if(i == l-1 && typeof callback == 'function') {
                            callback()
                        }
                    }
                    imgArray[img].src = img
                }
            }
        }
    }


    var gameMonitor = {
        w: window.innerWidth,
        h: window.innerHeight,
        bgWidth: window.innerWidth,
        bgHeight: window.innerHeight,
        time: 0,
        timmer: null,
        bgSpeed: 2,
        bgloop: 0,
        score: 0,
        im: new ImageMonitor(),
        foodList: [],
        bgDistance: 0,//背景位置
        eventType: {
            start : 'touchstart',
            move : 'touchmove',
            end : 'touchend'
        },
        init: function(){
            var _this = this;
            var canvas = document.getElementById('stage')
            var ctx = canvas.getContext('2d')

            //绘制背景
            var bg = new Image()
            _this.bg = bg
            bg.onload = function(){
                ctx.drawImage(bg, 0, 0, _this.bgWidth, _this.bgHeight);         	
            }
            bg.src = '/static/game/static/img/bg.jpg'

            _this.initListener(ctx)


        },
        initListener: function (ctx) {
            const _this = this
            const body = $(document.body)
            $(document).on(gameMonitor.eventType.move, function(event){
                event.preventDefault()
            })
            body.on(gameMonitor.eventType.start, '.replay, .playagain', function(){
                $('#resultPanel').hide()
                self.isShowCanvas = true
                const canvas = document.getElementById('stage')
                const ctx = canvas.getContext('2d')
                _this.ship = new Ship(ctx)
                _this.ship.controll()
                _this.reset()
                _this.run(ctx)
            })

            body.on(gameMonitor.eventType.start, '#frontpage', function(){
                $('#frontpage').css('left', '-100%')
            })

            body.on(gameMonitor.eventType.start, '#guidePanel', function(){
                $(this).hide()
                self.isRotateMusic = true
                document.querySelector('#media').playbackRate = 1
                _this.ship = new Ship(ctx)
                _this.ship.paint()
                _this.ship.controll()
                gameMonitor.run(ctx)
            })
        },
        // 生成滚动的背景
        rollBg: function(ctx){
            // if(this.bgDistance>=this.bgHeight){
            //     this.bgloop = 0
            // }
            // this.bgDistance = ++this.bgloop * this.bgSpeed
            // ctx.drawImage(this.bg, 0, this.bgDistance-this.bgHeight, this.bgWidth, this.bgHeight)
            ctx.drawImage(this.bg, 0, 0, this.bgWidth, this.bgHeight)
        },
        run: function(ctx){
            var _this = gameMonitor
            ctx.clearRect(0, 0, _this.bgWidth, _this.bgHeight)
            _this.rollBg(ctx)
            //绘制飞船
            _this.ship.paint()
            _this.ship.eat(_this.foodList)


            //产生月饼
            _this.genorateFood()

            //绘制月饼
            for (let i = _this.foodList.length-1; i>=0; i--) {
                var f = _this.foodList[i]
                if (f) {
                    f.paint(ctx)
                    f.move(ctx)
                }
            }
            // 通过setTimeout来控制帧率
            _this.timmer = setTimeout(function () {
                gameMonitor.run(ctx)
            }, Math.round(1000/200))
            _this.time++
        },
        stop: function () {
            var _this = this
            $('#stage').off(gameMonitor.eventType.start + ' ' +gameMonitor.eventType.move)
            setTimeout(function () {
                clearTimeout(_this.timmer)
            }, 0)
        },
        // 生成食物
        genorateFood: function () {
            var genRate = 70 //产生月饼的频率
            var random = Math.random()
            if (random*genRate>genRate-1) {
                var left = Math.random()*(this.w - 50)
                // 随机生成食物类型 bad good
                var type = Math.floor(left) % 2 == 0 ? 0 : 1
                var id = this.foodList.length
                var f = new Food(type, left, id)
                this.foodList.push(f)
            }
        },
        // 点击在玩一次后 重置所有数据
        reset: function(){
            this.foodList = []
            this.bgloop = 0
            this.score = 0
            this.timmer = null
            this.time = 0
            $('#score').text(this.score)
        },
        getScore: function () {
            // var time = Math.floor(this.time/60)
            // var score = this.score
            // var user = 1
            // if(score==0){
            //     $('#scorecontent').html('真遗憾,您竟然<span class="lighttext">一个</span>月饼都没有抢到!')
            //     $('.btn1').text('大侠请重新来过').removeClass('share').addClass('playagain')
            //     $('#fenghao').removeClass('geili yinhen').addClass('yinhen')
            //     return
            // }
            // else if(score<10){
            //     user = 2
            // }
            // else if(score>10 && score<=20){
            //     user = 10
            // }
            // else if(score>20 && score<=40){
            //     user = 40
            // }
            // else if(score>40 && score<=60){
            //     user = 80;
            // }
            // else if(score>60 && score<=80){
            //     user = 92
            // }
            // else if(score>80){
            //     user = 99
            // }
            $('#fenghao').removeClass('geili yinhen').addClass('geili')
            // $('#scorecontent').html('您在<span id="stime" class="lighttext">2378</span>秒内抢到了<span id="sscore" class="lighttext">21341</span>个月饼<br>超过了<span id="suser" class="lighttext">31%</span>的用户!')
            // $('#stime').text(time)
            // $('#sscore').text(score)
            // $('#suser').text(user+'%')
            // $('.btn1').text('请小伙伴吃月饼').removeClass('playagain').addClass('share')
        },
    }
    gameMonitor.init()
    },
    methods: {
        playPause () {
            const music = document.querySelector('#media')
            this.isRotateMusic = !this.isRotateMusic
            this.isPlayMusic = !this.isPlayMusic
            document.querySelector('#media').muted = !this.isPlayMusic
        }
    }
}
</script>

<style scoped>
#container{
    position: relative;
	overflow: hidden;
	width: 100%;
    height: 100%;
}
#startgame{
	position: absolute;
	right: 20px;
	bottom: 20px;
}
#gamepanel{
	width: 100%;
	height: 100%;
}
#stage{
	background-color: #CCC;
}
.score-wrap {
	background: url('/static/game/static/img/scorebg.png') no-repeat;
	background-size: 100%;
	color: #FFF;
	/*display: none;*/
	font-family: "Helvetica","Microsoft YaHei",sans-serif;
	font-style: italic;
	font-size: 17px;
	font-weight: 700;
	height: 32px;
	letter-spacing: 2px;
	padding: 7px 10px;
	position: absolute;
	right: 20px;
	text-align: right;
	text-shadow: 1.5px 0 0 #613209,-1.5px 0 0 #613209,0 1px 0 #613209,0 -1.5px 0 #613209,1px 1px 0 #613209,-1px 1px 0 #613209,1px -1px 0 #613209,-1px -1px 0 #613209;
	top: 10px;
	width: 105px;
	z-index: 1005
}

.score-wrap div {
	background: url('/static/game/static/img/heart.png') no-repeat;
	background-size: 100%;
	height: 26px;
	left: 2px;
	position: absolute;
	top: 2px;
	width: 26px;
	z-index: 1009
}

div.hearthot {
	-webkit-animation: fire .2s linear;
	-o-animation: fire .2s linear;
	animation: fire .2s linear
}

@-webkit-keyframes fire {
	0% {
		opacity: 1;
		-webkit-transform: scale(1.1);
		-moz-transform: scale(1.1);
		-ms-transform: scale(1.1);
		-o-transform: scale(1.1);
		transform: scale(1.1)
	}

	100% {
		opacity: 0;
		-webkit-transform: scale(3.0);
		-moz-transform: scale(3.0);
		-ms-transform: scale(3.0);
		-o-transform: scale(3.0);
		transform: scale(3.0)
	}
}

@keyframes fire {
	0% {
		-webkit-transform: scale(1.1);
		-moz-transform: scale(1.1);
		-ms-transform: scale(1.1);
		-o-transform: scale(1.1);
		transform: scale(1.1)
	}

	100% {
		-webkit-transform: scale(1.0);
		-moz-transform: scale(1.0);
		-ms-transform: scale(1.0);
		-o-transform: scale(1.0);
		transform: scale(1.0)
	}
}
#guidePanel {
	background: rgba(0,0,0,0.6) url('/static/game/static/img/startbg.png') center 50% no-repeat;
	background-size: 219px 369px;
	height: 100%;
	left: 0;
	position: absolute;
	top: 0;
	width: 100%;
	z-index: 10000
}
#gameoverPanel {
	background: rgba(0,0,0,0.8) url('/static/game/static/img/gameover.jpeg') center 30% no-repeat;
	background-size: 230px 260px;
	top: 0
}

#gameoverPanel,#resultPanel {
	display: none;
	height: 100%;
	position: absolute;
	width: 100%;
	z-index: 10000
}

#resultPanel{
	background:url('/static/game/static/img/endpage.jpg') center top no-repeat;
    background-size: 100% 100%;
}

#resultPanel,#resultPanel .weixin-share {
	left: 0;
	top: 0
}
#resultPanel .weixin-share {
	background: rgba(0,0,0,.8) url('/static/game/static/img/weixin.png') right top no-repeat;
	background-size: 212px 196px;
	display: none;
	height: 100%;
	position: absolute;
	width: 100%;
	z-index: 100
}

#resultPanel .replay {
	background: url('/static/game/static/img/replay.png') 0 0 no-repeat;
	height: 36px;
	line-height: 36px;
	right: 24px;
	overflow: hidden;
	position: absolute;
	top: 11px;
	width: 86px;
	z-index: 10;
	color: #E44324;
	text-align: right;
	padding-right: 6px;
	font-weight: 700;
	font-size: 12px;
}
#resultPanel .panel,#scoreBoard .score-result {
	display: none;
	height: 100%;
	left: 0;
	position: absolute;
	top: 0;
	width: 100%
}
#fenghao{
	height: 68px;
	margin-top: 90px;
}
#scorecontent{
	font-size: 16px;
	font-weight: 700;
	color: #FFF;
	text-align: center;
	line-height: 1.8em;
	margin-top: 5px;
}
.lighttext{
	color: #F6DE0A;
}
.geili{
	background: url('/static/game/static/img/geili.png') center no-repeat;
}
.yinhen{
	background: url('/static/game/static/img/yinhen.png') center no-repeat;
}
.textc{
	text-align: center;
}
.btn1, .btn2{
	display: inline-block;
	width: 196px;
	height: 54px;
	line-height: 54px;
	color: #FFF;
	font-size: 20px;
	border-radius: 5px;
	text-align: center;
}
.btn1{
	margin-top: 22px;
	background-color: #E8722C;
}
.btn2{
	margin-top: 12px;
	border: 1px solid #6A6B6D;

}
#container .music_btn {
    position: absolute;
    left: 20px;
    top: 20px;
    width: 36px;
    height: 36px;
    display: block;
}
#container .isRotate {
    animation: rotating 1.2s linear infinite;
}
@keyframes rotating {
    0% {
        transform: rotate(0);
    }
    100% {
        transform: rotate(360deg);
    }
}
</style>

这里是原文链接,不是vue的

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是一个简单的 h5 canvas 益智类填色画游戏的代码示例: HTML 代码: ``` <canvas id="gameCanvas" width="400" height="400"></canvas> ``` JavaScript 代码: ``` var canvas = document.getElementById("gameCanvas"); var context = canvas.getContext("2d"); // 定义游戏区域大小和格子大小 var gridSize = 20; var gameWidth = canvas.width - gridSize; var gameHeight = canvas.height - gridSize; // 定义游戏数据 var gameData = []; for (var i = 0; i < gameWidth / gridSize; i++) { gameData[i] = []; for (var j = 0; j < gameHeight / gridSize; j++) { gameData[i][j] = "#FFFFFF"; // 初始颜色为白色 } } // 绘制游戏区域 function drawGameArea() { context.fillStyle = "#CCCCCC"; // 背景颜色为灰色 context.fillRect(0, 0, canvas.width, canvas.height); for (var i = 0; i < gameWidth / gridSize; i++) { for (var j = 0; j < gameHeight / gridSize; j++) { context.fillStyle = gameData[i][j]; context.fillRect(i * gridSize, j * gridSize, gridSize, gridSize); } } } // 获取点击位置对应的格子坐标 function getGridPosition(event) { var x = event.pageX - canvas.offsetLeft; var y = event.pageY - canvas.offsetTop; var gridX = Math.floor(x / gridSize); var gridY = Math.floor(y / gridSize); return { x: gridX, y: gridY }; } // 填色 function fillGrid(event) { var grid = getGridPosition(event); var color = gameData[grid.x][grid.y]; // 随机生成新颜色 var newColor = "#" + Math.floor(Math.random() * 16777215).toString(16); // 如果颜色相同,不做处理 if (color === newColor) { return; } // 填充同色区域 var fillQueue = [{ x: grid.x, y: grid.y }]; while (fillQueue.length > 0) { var currentGrid = fillQueue.shift(); gameData[currentGrid.x][currentGrid.y] = newColor; // 上方格子 if (currentGrid.y > 0 && gameData[currentGrid.x][currentGrid.y - 1] === color) { fillQueue.push({ x: currentGrid.x, y: currentGrid.y - 1 }); } // 下方格子 if (currentGrid.y < gameHeight / gridSize - 1 && gameData[currentGrid.x][currentGrid.y + 1] === color) { fillQueue.push({ x: currentGrid.x, y: currentGrid.y + 1 }); } // 左侧格子 if (currentGrid.x > 0 && gameData[currentGrid.x - 1][currentGrid.y] === color) { fillQueue.push({ x: currentGrid.x - 1, y: currentGrid.y }); } // 右侧格子 if (currentGrid.x < gameWidth / gridSize - 1 && gameData[currentGrid.x + 1][currentGrid.y] === color) { fillQueue.push({ x: currentGrid.x + 1, y: currentGrid.y }); } } drawGameArea(); } // 初始化游戏 function initGame() { drawGameArea(); canvas.addEventListener("click", fillGrid); } initGame(); ``` 在这段代码中,我们首先定义了游戏区域大小和格子大小。然后,我们定义了游戏数据,即包含每个格子的颜色。初始时,每个格子的颜色都是白色。 着,我们编写了绘制游戏区域的函数 `drawGameArea`。该函数先绘制背景颜色,然后根据游戏数据绘制每个格子。 我们还编写了一个函数 `getGridPosition`,用于获取点击位置对应的格子坐标。 下来是填色的核心逻辑。当用户点击某个格子时,我们首先获取该格子的颜色。然后,我们随机生成一个新的颜色,如果新颜色和原来的颜色相同,就不做处理。否则,我们使用广度优先搜索算法,将所有和该格子颜色相同的格子都填充成新颜色。 最后,我们编写了初始化游戏的函数 `initGame`,该函数绑定了点击事件,并调用 `drawGameArea` 函数绘制游戏区域。 这是一个非常简单的 h5 canvas 益智类填色画游戏的代码示例,仅供参考。实际上,填色游戏实现方法有很多种,您可以根据自己的需求进行修改和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值