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

    技术2022-07-12  79

    写在开头

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

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

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

    为什么要推荐这个项目

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

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

    技术栈

    使用原生javascript + html +css

    主要绘制是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,感觉对你有帮助,可以右下角点个在看,关注一波公众号:[前端巅峰]

    Processed: 0.016, SQL: 10