寫在開頭
-
一位作者開源了這個遊戲,純前端實現,原生
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/