H5 台球猜位置小游戏

刷到抖音有人这样玩,就写了一个这样的小游戏练习一下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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/558996.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Aws Nat Gateway

要点 NAT网关要能访问外网&#xff0c;所以需要部署在有互联网网关的Public子网中。 关键&#xff1a; NAT网关创建是选择子网&#xff0c;一定要选择公有子网&#xff08;有互联网网关子网&#xff09; 特别注意&#xff1a; 新建nat网关的时候&#xff0c;选择的子网一定…

jeecgflow之camunda工作流-并行网关

引言 书接上回&#xff0c;继续三国流程系列教程。 本文主要讲解并行网关。 并行网关允许流程中的多个任务同时执行&#xff0c;从而提高流程的执行效率。 并行网关会忽视序列流上的条件设置。 并行网关分为两部分。 Fork: 用于任务开始 Join:用于任务结束 体验文章demo演示站…

【Camera Framework笔记】二、Camera Native Framework架构①

一、总体架构&#xff1a; service -> opencamera -> client&#xff08;api1/api2&#xff09; -> device3&#xff08;hal3&#xff09; | | &#xff08;不opencamera…

kubernetes学习

1、应用部署方式演变 2、kubernetes介绍 3、kubernetes组件 4、kubernetes概念 5、环境搭建-环境规划 集群类型&#xff1a; 安装方式&#xff1a; 主机规划&#xff1a; 6、环境搭建-主机安装 使用虚拟机安装三台centos7&#xff08;一主二从&#xff09;&#xff0c;然后在…

相机摄影入门技巧,数码摄影技巧大全

一、资料前言 本套数码相机摄影资料&#xff0c;大小1.08G&#xff0c;共有42个文件。 二、资料目录 《aking人像摄影技巧分享》.pdf 《Nikon.D90数码单反摄影技巧大全》FUN视觉.全彩版.pdf 《不可不学的摄影技巧》.pdf 《常用场景摄影》.pdf 《单反数码摄影专家技法》.…

ASP.NET基于Web的招投标系统的设计与实现

摘 要 招标拍卖的历史悠久&#xff0c;在近两千年的发展历程中&#xff0c;人们对拍卖的理论和技术做了大量的探讨。随着计算机网络技术的迅猛发展和日益成熟&#xff0c;为了提高招投标及采购工作的效率&#xff0c;为廉政建设和防止腐败提供技术保障&#xff0c;传统的拍…

JS -关于对象相关介绍

在JS中&#xff0c;除去基本的数据类型&#xff0c;还有包含对象这种复合数据类型&#xff0c;他可以储存多个键值对&#xff0c;并且每个键都是唯一的&#xff0c;并且在对象中可以包含各种数据类型的值&#xff0c;包括其他对象&#xff0c;数组&#xff0c;函数等。对象是Ja…

[AI]-(第0期):认知深度学习

深度学习是一种人工智能&#xff08;AI&#xff09;方法&#xff0c;用于教计算机以受人脑启发的方式处理数据。 深度学习模型可以识别图片、文本、声音和其他数据中的复杂模式&#xff0c;从而生成准确的见解和预测。 您可以使用深度学习方法自动执行通常需要人工智能完成的…

linux 基础命令docker及防火墙iptables详解

应用场景&#xff1a; web应用自动打包和发布 自动化测试&#xff0c;持续集成、发布 在服务环境中部署后台应用 搭建paaS平台 安装应用 apt install docker.io#kali中 配置docker源&#xff0c;文件位置/etc/docker/daemon.json { "registry-mirrors": [ "h…

Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单人脸检测/识别实战案例 之一 简单人脸识别

Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单人脸检测/识别实战案例 之一 简单人脸识别 目录 Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单人脸检测/识别实战案例 之一 简单人脸识别 一、简单介绍 二、简单人脸识别实现原理 三、简单人脸识别案例实现简单步…

ContextMenuStrip内容菜单源对象赋值学习笔记(含源码)

一、前言 MetroTileItem属于第三方控件,无法定义ContextMenuStrip属性 想实现某子项点击菜单时,与源控件(按钮metroTileItem)的某值对应,用于动态控制按钮的状态或方法 1.1 效果 二、实现方法 2.1 方法1 (代码,说明见注释) private void metroTileItem_MouseDown(o…

基于Springboot的小区物业管理系统

基于SpringbootVue的小区物业管理系统的设计与实现 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringbootMybatis工具&#xff1a;IDEA、Maven、Navicat 系统展示 用户登录 首页 用户管理 员工管理 业主信息管理 费用信息管理 楼房信息管理 保修信息…

攻防世界---reverse_re3

1.下载附件&#xff0c;先查壳&#xff1a;无壳 2.在IDA中分析&#xff0c;shiftfnf5&#xff0c;看到一串长得很像flag的flag 3.根据提示我们需要找到输入&#xff0c;再进行md5转换才能得到flag flag{md5(your input)} 4.双击这个句话&#xff0c;点进去想查看信息&#xff0…

MongoDB学习【一】MongoDB简介和部署

MongoDB简介 MongoDB是一种开源的、面向文档的、分布式的NoSQL数据库系统&#xff0c;由C语言编写而成。它的设计目标是为了适应现代Web应用和大数据处理场景的需求&#xff0c;提供高可用性、横向扩展能力和灵活的数据模型。 主要特点&#xff1a; 文档模型&#xff1a; Mon…

西宁市初中生地会考报名照片尺寸要求及手机自拍方法

西宁市初中生地会考即将到来&#xff0c;对于参加考试的同学们来说&#xff0c;准备一张符合规格的报名照片是整个报名流程中不可或缺的一环。一张规范的证件照不仅展示了学生的精神面貌&#xff0c;同时也是顺利报名的重要条件之一。本文将详细介绍西宁市初中生地会考报名所需…

SSDReporter for Mac:全面检测SSD健康,预防数据丢失,让您的Mac运行更稳定

SSDReporter for Mac是一款专为Mac用户设计的固态硬盘&#xff08;SSD&#xff09;健康状况检测工具&#xff0c;旨在帮助用户全面了解并监控其Mac设备中SSD的工作状态&#xff0c;从而确保数据的完整性和设备的稳定性。 这款软件具有多种强大的功能。首先&#xff0c;它能够定…

【分治】Leetcode 库存管理 III

题目讲解 LCR 159. 库存管理 III 本题的含义就是让求出最小的k个数 算法讲解 class Solution { public:void my_qsort(vector<int>& nums, int l, int r){if(l > r) return ;int i l, left l-1, right r1;int key nums[rand() % (r - l 1) l];//完成分三…

深度学习基础之《TensorFlow框架(12)—图片数据》

一、图像基本知识 1、如何转换图片文件 回忆&#xff1a;之前我们在特征抽取中讲过如何将文本处理成数据 思考&#xff1a;如何将图片文件转换成机器学习算法能够处理的数据&#xff1f; 我们经常接触到的图片有两种&#xff0c;一种是黑白图片&#xff08;灰度图&#xff09;…

C++ - STL详解—vector类

一. vector的概念 向量&#xff08;Vector&#xff09;是一个封装了动态大小数组的顺序容器&#xff08;Sequence Container&#xff09;。跟任意其它类型容器一样&#xff0c;它能够存放各种类型的对象。可以简单的认为&#xff0c;向量是一个能够存放任意类型的动态数组。 …

阿里云ECS服务器安装docker

首先查看阿里云ECS的服务器的版本 cat /etc/redhat-release如果是Alibaba Cloud Linux release 3,请执行以下命令 添加docker-ce的dnf源。 sudo dnf config-manager --add-repohttps://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo安装Alibaba Cloud Linux 3专…