刷到抖音有人这样玩,就写了一个这样的小游戏练习一下H5的知识点。
小游戏预览
w(゚Д゚)w 不开挂越急越完成不了,👿确认15次也没全对…
知识点
获取坐标位置的DOM元素,感觉应该是新的吧,以前的时候没什么印象有这个方法。兼容性不晓得可以自己查下~
document.elementFromPoint(x, y)
源码
注释不多,比较简单的,还是比较好理解的。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="./assets/global.css">
<style>
.block {
width: 65px;
height: 65px;
/* position: absolute; */
background-size: 300px;
display: inline-block;
user-select: none;
background-position: calc(var(--x, 0) * (65px + 13.5px) * -1) calc(var(--y, 0) * (65px + 13.5px) * -1);
}
.title {
margin-top: 40px;
font-size: 40px;
font-weight: bold;
text-align: center;
}
.container {
margin: 0 20px;
}
.tips {
margin: 20px;
font-size: 20px;
color: rgba(0, 0, 0, .5);
}
.btn {
margin: 20px;
color: #fff;
background-color: green;
width: 120px;
height: 40px;
line-height: 40px;
text-align: center;
}
.btn:active {
opacity: .7;
}
.abs {
position: absolute;
z-index: 2;
pointer-events: none;
}
.op5 {
opacity: .5;
}
.billiard-container .block {
pointer-events: none;
width: 32.5px;
height: 32.5px;
background-size: 150px;
background-position: calc(var(--x, 0) * (32px + 7.5px) * -1) calc(var(--y, 0) * (32px + 7.5px) * -1)
/* transform: scale(-50%); */
}
.result {
display: flex;
flex-direction: column-reverse
}
.result-item {
display: flex;
align-items: center;
padding: 10px 20px 0;
}
.result-item .index {
font-size: 20px;
font-weight: bold;
margin-right: 20px;
}
.result-item .billiard-container {
flex: 1;
}
.result-item .right-count {
color: #12be77;
}
.win {
margin: 20px 20px 0;
font-size: 20px;
}
</style>
</head>
<body>
<div class="title">猜位置</div>
<div class="tips">拖动更换位置,点击确认获取结果,位置都正确获取游戏胜利。</div>
<div class="container"></div>
<div class="win">🐂🍺! 🎉游戏胜利!🎉</div>
<div class="btn">确定</div>
<div class="result"></div>
<script type="module">
let winDom = document.querySelector('.win')
winDom.hidden = true;
import { Maths, Randoms, Animation, cloneDeep, InterchangeFlag } from "https://unpkg.com/@3r/tool"
let containerDom = document.querySelector('.container')
let btnDom = document.querySelector('.btn')
let resultDom = document.querySelector('.result')
let billiardConfig = {
sprite: './assets/taiqiu.png',
blocks: [
{
id: '1',
position: { x: 0, y: 0 }
},
{
id: '2',
position: { x: 1, y: 0 }
},
{
id: '3',
position: { x: 2, y: 0 }
},
{
id: '4',
position: { x: 3, y: 0 }
},
{
id: '5',
position: { x: 0, y: 1 }
},
{
id: '6',
position: { x: 1, y: 1 }
},
{
id: '7',
position: { x: 2, y: 1 }
},
{
id: '8',
position: { x: 3, y: 1 }
},
{
id: '9',
position: { x: 0, y: 2 }
},
{
id: '10',
position: { x: 1, y: 2 }
},
{
id: '11',
position: { x: 2, y: 2 }
},
{
id: '12',
position: { x: 3, y: 2 }
},
{
id: '13',
position: { x: 0, y: 3 }
},
{
id: '14',
position: { x: 1, y: 3 }
},
{
id: '15',
position: { x: 2, y: 3 }
},
{
id: '白球',
position: { x: 3, y: 3 }
}
]
}
let allBlocks = cloneDeep(billiardConfig.blocks) // 所有的数据
let selBlockDom = null // 移动前不动的球
let movBlockDom = null // 当前移动的球
let hovBlockDom = null // 不表浮动到哪个球上面
let curBlocks = Randoms.getDisorganizeArray(cloneDeep(allBlocks)).slice(0, 5) // 记录当前记录
let resDataIds = Randoms.getDisorganizeArray(curBlocks.map(b => b.id)) // 记录本轮结果
let hisList = [] // 记录历史
document.body.addEventListener("touchmove", handleMoving)
document.body.addEventListener("touchend", handleMoveEnd)
document.body.addEventListener("touchcancel", handleMoveEnd)
document.body.addEventListener("mousemove", handleMoving)
document.body.addEventListener("mouseup", handleMoveEnd)
function handleMoveStart(ev) {
// console.log("handleMoveStart", ev);
let x, y;
if (ev.type == 'touchstart') {
selBlockDom = ev.target;
movBlockDom = ev.target.cloneNode()
x = ev.touches[0].clientX
y = ev.touches[0].clientY
}
if (ev.type == 'mousedown') {
x = ev.x
y = ev.y
selBlockDom = ev.target;
movBlockDom = ev.target.cloneNode()
}
if (!movBlockDom) return;
movBlockDom.classList.add('abs')
movBlockDom.classList.add('op5')
movBlockDom.style.left = `${x}px`
movBlockDom.style.top = `${y}px`
document.body.appendChild(movBlockDom)
}
function handleMoving(ev) {
// console.log("handleMoving", ev);
let x, y;
if (ev.type == 'touchmove') {
x = ev.touches[0].clientX
y = ev.touches[0].clientY
}
if (ev.type == 'mousemove') {
x = ev.x
y = ev.y
}
x = Math.floor(x)
y = Math.floor(y)
hovBlockDom?.classList.remove('op5')
hovBlockDom = null;
let tmpHovBlockDom = document.elementFromPoint(x, y)
if (tmpHovBlockDom.classList.contains('block')) {
tmpHovBlockDom.classList.add('op5')
hovBlockDom = tmpHovBlockDom;
}
if (!movBlockDom) return;
movBlockDom.style.left = `${x}px`
movBlockDom.style.top = `${y}px`
}
function handleMoveEnd(ev) {
if (!movBlockDom) return;
if (hovBlockDom) {
// 交换位置
let dataId = hovBlockDom.getAttribute('data-id')
let style = hovBlockDom.getAttribute('style');
hovBlockDom.setAttribute('data-id', selBlockDom.getAttribute('data-id'))
hovBlockDom.setAttribute('style', selBlockDom.getAttribute('style'))
selBlockDom.setAttribute('data-id', dataId)
selBlockDom.setAttribute('style', style)
let idx1 = curBlocks.findIndex(b => b.id == selBlockDom.getAttribute('data-id'))
let idx2 = curBlocks.findIndex(b => b.id == hovBlockDom.getAttribute('data-id'))
// 下标交换
Maths.interchange(curBlocks, idx1, idx2, InterchangeFlag.Change)
}
hovBlockDom?.classList.remove('op5')
document.body.removeChild(movBlockDom)
hovBlockDom = null;
movBlockDom = null;
selBlockDom = null;
}
// 生成球
function generateBilliardItemDom(blocks) {
let blockDomList = []
for (const block of blocks) {
let blockDom = document.createElement('div')
blockDom.classList.add('block')
// let px = Math.round(block.position.x * billiardConfig.width + billiardConfig.marginRight * block.position.x) * -1
// let py = Math.round(block.position.y * billiardConfig.height + billiardConfig.marginBottom * block.position.y) * -1
// let backgroundPosition = `background-position: ${px}px ${py}px;`
// blockDom.style = `background-image: url(${billiardConfig.sprite});${backgroundPosition}`
blockDom.setAttribute('style', `--x: ${block.position.x}; --y: ${block.position.y}`);
blockDom.style.backgroundImage = `url(${billiardConfig.sprite})`
blockDom.setAttribute('data-id', block.id)
blockDom.addEventListener("mousedown", handleMoveStart)
blockDom.addEventListener("touchstart", handleMoveStart)
blockDomList.push(blockDom)
// containerDom.appendChild(blockDom)
}
return blockDomList
}
// 生成历史结果
function generateResultDom(result) {
let resultItemDom = document.createElement('div')
resultItemDom.classList.add('result-item')
let indexDom = document.createElement('div')
indexDom.classList.add('index')
indexDom.textContent = `第${hisList.length + 1}次`
let billiardDom = document.createElement('div')
billiardDom.classList.add('billiard-container')
let rightCountDom = document.createElement('div')
rightCountDom.classList.add('right-count')
rightCountDom.textContent = `✔ × ${result.rightCount}`
generateBilliardItemDom(result.blocks).forEach(item => {
billiardDom.appendChild(item)
});
resultItemDom.appendChild(indexDom)
resultItemDom.appendChild(billiardDom)
resultItemDom.appendChild(rightCountDom)
resultDom.appendChild(resultItemDom)
}
// 计算结果
function calculateResult() {
let curDataIds = curBlocks.map(b => b.id)
let rightCount = 0;
for (let i = 0; i < curDataIds.length; i++) {
if (curDataIds[i] == resDataIds[i]) {
rightCount++;
}
}
// 判断是否游戏胜利✌
if (rightCount == curBlocks.length) {
winDom.hidden = false;
btnDom.hidden = true;
}
let result = {
rightCount,
blocks: cloneDeep(curBlocks)
}
generateResultDom(result)
hisList.push(result)
}
btnDom.addEventListener('click', calculateResult)
generateBilliardItemDom(curBlocks).forEach(item => {
containerDom.appendChild(item)
});
</script>
</body>
</html>
源码地址 https://github.com/linyisonger/H5.Examples
在线试玩 https://linyisonger.github.io/H5.Examples/?name=./066.%E7%8C%9C%E4%BD%8D%E7%BD%AE.html