欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

使用Canvas实现一个在线发牌游戏 [纯前端、附源码]

程序员文章站 2024-01-23 08:04:04
...

写在开头

  • 一位作者开源了这个游戏,纯前端实现,原生Canvas

  • 希望大家给他点个star,源码地址:https://github.com/leeseean/sic-bo

  • 这个项目克隆很慢,因为比较大,如果你想知道怎么克隆快,可以看今天公众号第二条推文 《如何把github的clone速度提升到1MB/S》

为什么要推荐这个项目

  • 在我看来,这个作者是有一定技术实力的,对canvas理解和使用,以及浏览器渲染机制,都是比较了解的,还有原生dom操作能力都可以

  • 里面大量使用了canvas的路径绘制、填充,以及requestAnimationFrame

技术栈

  • 使用原生javascript + html +css

  • 主要绘制是canvas实现

项目初始状态

使用Canvas实现一个在线发牌游戏 [纯前端、附源码]

怎么玩这个游戏

  • 点击想押注的地方

  • 筹码会有一个动画飞向你押注的区域

  • 使用Canvas实现一个在线发牌游戏 [纯前端、附源码]

  • 押注完成后,定时开始摇骰子,开奖

使用Canvas实现一个在线发牌游戏 [纯前端、附源码]

大概实现

  • 初始调用 init函数,生成canvas画布,挂载onclick事件

   init() {
        let _this = this;
        _this.loadImage();
        _this.scale = screen.width < 1500 ? screen.width / 1920 : 1;
        let scale = _this.scale;
        //拿到画布
        let canvas_fly = document.getElementById('canvas_fly'); //渲染飞出去的筹码
        canvas_fly.width = document.body.clientWidth;
        canvas_fly.height = 912 * scale || 800;
        _this.ctxFly = canvas_fly.getContext('2d');
        let canvas_stop = document.getElementById('canvas_stop'); //渲染未确认**放桌面上的筹码
        canvas_stop.width = document.body.clientWidth;
        canvas_stop.height = 912 * scale || 800;
        _this.ctxStop = canvas_stop.getContext('2d');
        let canvas_betted = document.getElementById('canvas_betted'); //渲染确认**的筹码
        canvas_betted.width = document.body.clientWidth;
        canvas_betted.height = 912 * scale || 800;
        _this.ctxBetted = canvas_betted.getContext('2d');

        //拿到chipsImgObj对象
        let img = new Image();
        img.onload = function () {
            _this.chipsImgObj = this; //拿到chipsImgObj对象
        }
        img.src = './images/chips.png';

        //确定所用筹码
        $('.chips>.chip').off('click').on('click', function (e) {
            $(this).addClass('on').siblings('.chip').removeClass('on');
            _this.priceNum = +$(this).attr('priceNum');
        });
        $('.chips>.chip10').trigger('click'); //默认筹码10

        const pieceIntervalOver = { //连续点击翻倍生不生效的flag
            'betFor': false,
            'betted': false,
            'pieceCount': 0,
        };
        const cancelOk = {
            ok: false,
            count: 0,
        }; //取消完毕,默认false
        const resetOk = {
            ok: false,
            count: 0,// 防止重复点击
        }
        //点击桌面选号
        $('[rel="selectCode"]').off('click').on('click', function (e) {
            if (cancelOk.ok || cancelOk.count === 0) {
                cancelOk.count = 0;
            } else {
                return; //没取消完毕不准过去
            }

            _this.flyState = 'flyTo';
            let code = $(this).attr('value');
            let method = $(this).attr('method');
            let startPos = {
                x: $(`.chips .chip${_this.priceNum}`).offset().left,
                y: $(`.chips .chip${_this.priceNum}`).offset().top,
            };
            let endPos = {
                x: $(this).offset().left + $(this)[0].offsetWidth * scale / 2 - $('.chips>.chip').width() * scale / 2,
                y: $(this).offset().top + $(this)[0].offsetHeight * scale / 2 - $('.chips>.chip').height() * scale / 2,
            };
            _this.eachBetCount[code] = _this.eachBetCount[code] || 0;
            _this.eachBetCount[code] += _this.priceNum;
            _this.betOrderRecords[code] = { //记录order
                method: method,
                code: code,
                price: _this.priceNum,
                amount: _this.eachBetCount[code],
                piece: _this.eachBetCount[code],
            };
            let clickedElemOption = { //被点击元素的相关数据
                priceNum: _this.priceNum,
                code: code,
                position: {
                    x: $(this).offset().left,
                    y: $(this).offset().top,
                },
                width: $(this).outerWidth(),
                height: $(this).outerHeight(),
            };
            _this.betForRecords.push({ //记录**
                elemOption: copyJSON(clickedElemOption),
                priceNum: _this.priceNum,
                startPos,
                endPos,
            });

            _this.chipFly(_this.ctxFly, _this.ctxStop, _this.chipsImgObj, _this.priceNum, startPos, endPos, clickedElemOption, 10);
            $('.betMoneyAmount').text(_this.calculateBetMoney());
        });
        //取消**
        $('.cancelButton').off('click').on('click', function (e) {
            if (cancelOk.ok || cancelOk.count === 0) {
                cancelOk.ok = false;
            } else {
                return; //没取消完毕不准过去
            }
            if ((pieceIntervalOver.betFor && pieceIntervalOver.betted) || pieceIntervalOver.pieceCount === 0) {

            } else {
                return; //上次翻倍全部渲染结束才能进行下一次点击操作,没结束操作无效                
            }
            cancelOk.count += 1;

            pieceIntervalOver.pieceCount = 0;
            if (_this.betForRecords.length === 0) {
                return;
            }
            _this.flyState = 'flyBack';
            //timechunk 分时函数
            let i = 0;
            let interval = setInterval(() => {
                if (i === _this.betForRecords.length) {
                    cancelOk.ok = true;
                    cancelOk.count = 0; //回到0
                    _this.betForRecords.length = 0;
                    _this.betOrderRecords = {};
                    _this.eachBetCount = {};
                    _this.ctxStop.clearRect(0, 0, document.body.clientWidth, document.body.clientHeight);
                    $('.betMoneyAmount').text(_this.calculateBetMoney());
                    return clearInterval(interval);
                }
                let record = _this.betForRecords[i];
                _this.chipFly(_this.ctxFly, _this.ctxStop, _this.chipsImgObj, record.priceNum, record.endPos, record.startPos, record.elemOption, 10);
                i++;
            }, 10);
        });
        //重置**
        $('.resetButton').off('click').on('click', function (e) {
            if (resetOk.ok || resetOk.count === 0) {
                resetOk.ok = false;
            } else {
                return; //没取消完毕不准过去
            }
            if ((pieceIntervalOver.betFor && pieceIntervalOver.betted) || pieceIntervalOver.pieceCount === 0) {

            } else {
                return; //上次翻倍全部渲染结束才能进行下一次点击操作,没结束操作无效                
            }
            resetOk.count += 1;

            pieceIntervalOver.pieceCount = 0;
            if (_this.betForRecords.length === 0 && _this.bettedRecords.length === 0) {
                return;
            }
            _this.flyState = 'flyBack';
            //timechunk 分时函数
            let i = 0;
            let interval = setInterval(() => {
                if (i === _this.betForRecords.length) {
                    _this.betForRecords.length = 0;
                    _this.betOrderRecords = {};
                    _this.eachBetCount = {};
                    _this.ctxStop.clearRect(0, 0, document.body.clientWidth, document.body.clientHeight);
                    $('.betMoneyAmount').text(_this.calculateBetMoney());
                    clearInterval(interval);
                    let j = 0;
                    const _interval = setInterval(() => {
                        if (j === _this.bettedRecords.length) {
                            resetOk.ok = true;
                            resetOk.count = 0; //回到0
                            _this.bettedRecords.length = 0;
                            _this.ctxBetted.clearRect(0, 0, document.body.clientWidth, document.body.clientHeight);
                            $('.betMoneyAmount').text(_this.calculateBetMoney());
                            return clearInterval(_interval);
                        }
                        console.log(j)
                        const bettedRecord = _this.bettedRecords[j];
                        _this.chipFly(_this.ctxFly, _this.ctxBetted, _this.chipsImgObj, bettedRecord.priceNum, bettedRecord.endPos, bettedRecord.startPos, bettedRecord.elemOption, 10);
                        j++;
                    }, 10);
                    return;
                }
                const betForRecord = _this.betForRecords[i];
                _this.chipFly(_this.ctxFly, _this.ctxStop, _this.chipsImgObj, betForRecord.priceNum, betForRecord.endPos, betForRecord.startPos, betForRecord.elemOption, 10);
                i++;
            }, 10);

        });
        //确认**
        $('.betButton').off('click').on('click', function (e) {
            if (_this.betForRecords.length === 0) {
                alert('请先**!');
                return;
            }
            if (cancelOk.ok || cancelOk.count === 0) {
            } else {
                return; //没取消完毕不准过去
            }
            if ((pieceIntervalOver.betFor && pieceIntervalOver.betted) || pieceIntervalOver.pieceCount === 0) {

            } else {
                return; //上次翻倍全部渲染结束才能进行下一次点击操作,没结束操作无效                
            }
            _this.flyState = 'betted';
            _this.bettedRecords = _this.bettedRecords.concat(_this.betForRecords);
            _this.betForRecords.forEach((record) => {
                _this.chipFly(_this.ctxFly, _this.ctxBetted, _this.chipsImgObj, record.priceNum, record.endPos, record.endPos, record.elemOption, 30);
            });
            _this.ctxStop.clearRect(0, 0, document.body.clientWidth, document.body.clientHeight);
            _this.betForRecords.length = 0;
            _this.betOrderRecords = {};
            _this.eachBetCount = {};
        });
        //翻倍** 
        $('.pieceButtoon').off('click').on('click', function (e) {
            let bettedLen = _this.bettedRecords.length;
            let betForLen = _this.betForRecords.length;
            if (bettedLen === 0 && betForLen === 0) {
                return;
            }
            if (cancelOk.ok || cancelOk.count === 0) {
            } else {
                return; //没取消完毕不准过去
            }
            if ((pieceIntervalOver.betFor && pieceIntervalOver.betted) || pieceIntervalOver.pieceCount === 0) {
                pieceIntervalOver.betFor = false;
                pieceIntervalOver.betted = false;
                pieceIntervalOver.pieceCount += 1;
            } else {
                return; //上次翻倍全部渲染结束才能进行下一次点击操作,没结束操作无效                
            }

            //timechunk分时函数,防止短时间多次触发卡死浏览器
            let i = 0;
            let interval_bet = setInterval(() => {
                if (i === bettedLen) {
                    pieceIntervalOver.betted = true;
                    return clearInterval(interval_bet);
                }
                let code = _this.bettedRecords[i]['elemOption']['code'];
                _this.priceNum = _this.bettedRecords[i]['priceNum'];
                $(`[rel="selectCode"][value="${code}"]`).trigger('click'); //自动桌面选号点击
                i++;
            }, 10);
            let j = 0;
            let interval_betted = setInterval(() => {
                if (j === betForLen) {
                    pieceIntervalOver.betFor = true;
                    return clearInterval(interval_betted);
                }
                let code = _this.betForRecords[j]['elemOption']['code'];
                _this.priceNum = _this.betForRecords[j]['priceNum'];
                $(`[rel="selectCode"][value="${code}"]`).trigger('click'); //自动桌面选号点击
                j++;
            }, 10);
        });
    },
  • init函数初始化中将canvas画笔挂载到this中

_this.ctxStop = canvas_stop.getContext('2d');
_this.ctxBetted = canvas_betted.getContext('2d');
...
  • 确认**后,清空未确认**放桌面上的筹码画布

 $('.betButton').off('click').on('click'、、、
 _this.ctxStop.clearRect(0, 0, document.body.clientWidth, document.body.clientHeight);
  • 如果没有**,就提示:

  if (_this.betForRecords.length === 0) {
                alert('请先**!');
                return;
            }

最后,希望大家多看看这个源码,了解canvas使用

  • 记得给作者点个star哦,这是一个入门canvas的一个很好的开源项目,点击左下角的阅读原文就可以进入到源码地址啦:https://github.com/leeseean/sic-bo

  • 如果深入点的话,可以再学习一下canvas的像素控制,或者pixijs的使用

  • 在线体验这个游戏的地址是:

  • https://leeseean.github.io/sic-bo/
    

如果感觉写得不错,关注下微信公众号 [前端巅峰]

  • 我是Peter,架构设计过20万人端到端加密超级群功能的桌面IM软件,我的微信:CALASFxiaotan

  • 另外欢迎收藏我的资料网站:前端生活社区:https://qianduan.life,感觉对你有帮助,可以右下角点个在看,关注一波公众号:[前端巅峰]