马黑黑 发表于 2025-11-8 12:03

玩个球

本帖最后由 马黑黑 于 2025-11-8 13:00 编辑 <br /><br /><div class="codebox" data-prev="1">
&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
&lt;meta charset="utf-8" /&gt;
&lt;title&gt;玩个球&lt;/title&gt;
&lt;style&gt;
        .parent { margin: 20px auto; padding: 10px; width: 80vw; height: 80vh; border: 1px solid gray; user-select: none; overflow: hidden; position: relative; }
        .parent::before { content: '添加的球球可以在框内自由拖动'; }
        .son { --bg1: #998d21; --bg2: #229dee; position: absolute; width: 100px; height: 100px; background: linear-gradient(var(--bg1), var(--bg2)); border-radius: 50%; cursor: move; display: grid; place-items: center; }
        .moving { animation: escape var(--du) forwards; }
        .tMid { text-align: center; }
        #btnAdd { transform: translateX(100px); }
        @keyframes escape {
                to { left: var(--xx); top: var(--yy); }
        }
&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;div id="pa" class="parent"&gt;&lt;/div&gt;
&lt;p class="tMid"&gt;&lt;input type="button" id="btnAdd" value="添加球球" /&gt;&lt;/p&gt;

&lt;script&gt;
        const pa = document.getElementById('pa');
        const sons = [];
        let dragger = null;
        let offsetX, offsetY, counter = 0;

        const startDrag = (e) =&gt; {
                const target = e.target.closest('.son');
                if (!target) return;
                dragger = target;
                offsetX = e.clientX - target.offsetLeft;
                offsetY = e.clientY - target.offsetTop;
                e.preventDefault();
        };

        const doDrag = (e) =&gt; {
                if (!dragger) return;
                let newX = e.clientX - offsetX;
                let newY = e.clientY - offsetY;
                const maxX = pa.offsetWidth - dragger.offsetWidth;
                const maxY = pa.offsetHeight - dragger.offsetHeight;
                newX = Math.max(0, Math.min(newX, maxX));
                newY = Math.max(0, Math.min(newY, maxY));
                dragger.style.left = newX + 'px';
                dragger.style.top = newY + 'px';
        };

        const stopDrag = () =&gt; dragger = null;

        const endAnimation = () =&gt; {
                sons.forEach(son =&gt; {
                        son.onanimationend = () =&gt; {
                                pa.removeChild(son);
                                const rest = document.querySelectorAll('.son');
                                if (rest.length &lt; 1) {
                                        sons.length = 0;
                                        counter = 0;
                                        btnAdd.disabled = false;
                                }
                        }
                });
        };

        pa.addEventListener('touchstart', (e) =&gt; {
                const touch = e.touches;
                const mouseEvent = new MouseEvent('mousedown', {
                        clientX: touch.clientX,
                        clientY: touch.clientY
                });
                startDrag(mouseEvent);
        });

        document.addEventListener('touchmove', (e) =&gt; {
                if (!dragger) return;
                const touch = e.touches;
                const mouseEvent = new MouseEvent('mousemove', {
                        clientX: touch.clientX,
                        clientY: touch.clientY
                });
                doDrag(mouseEvent);
                e.preventDefault();
        });

        btnAdd.addEventListener('click', () =&gt; {
                counter ++;
                if (counter &gt; 10) {
                        sons.forEach(son =&gt; {
                                son.classList.add('moving');
                        });
                        btnAdd.disabled = true;
                        endAnimation();
                        return;
                }
                const son = document.createElement('div');
                son.innerText = counter;
                son.classList.add('son');
                son.style.cssText += `
                        left: ${Math.floor(Math.random() * (pa.offsetWidth - 100))}px;
                        top: ${Math.floor(Math.random() * (pa.offsetHeight - 100))}px;
                        --bg1: #${Math.random().toString(16).substring(2,8)};
                        --bg2: #${Math.random().toString(16).substring(2,8)};
                        --du: ${Math.random() * 4 + 2}s;
                        --xx: ${(Math.random() &gt; 0.5 ? 1 : -1) * window.innerWidth}px;
                        --yy: ${(Math.random() &gt; 0.5 ? 1 : -1) * window.innerHeight}px;
                `;
                pa.appendChild(son);
                sons.push(son);
        });

        pa.addEventListener('mousedown', startDrag);
        document.addEventListener('mousemove', doDrag);
        document.addEventListener('mouseup', stopDrag);
        document.addEventListener('touchend', stopDrag);
&lt;/script&gt;

&lt;/body&gt;
&lt;/html&gt;
</div>

<script type="module">
import linenumber from 'https://638183.freep.cn/638183/web/js/linenumber.js';
linenumber();
</script>

马黑黑 发表于 2025-11-8 12:08

玩法:点击代码框右上角“预览”按钮,进入预览模态界面,在该界面点击“添加球球”按钮,主框内会添加随机渐变颜色的球,每点击一次加一个小球。所有添加上去的小球,均可以自由地在框内拖曳。加到一定数量,球球们会跑开,跑的最慢的球球跑到安全地带之后程序重置,可以接着再玩。

马黑黑 发表于 2025-11-8 12:20

实现原理:

一、拖动小球

以改变小球的位置即 left 和 top 属性值达成拖曳的目的。程序监听鼠标指针按下、移动、松开三个事件,不同的事件处理不同的业务,比如按下鼠标左键且按在任意一个小球之上,触发小球可以拖曳条件,此时若不松开按键且移动鼠标指针,小球会跟着鼠标指针走,而松开鼠标指针后小球可移动条件消失、小球不会再跟着鼠标指针走。

二、小球动画

当小球加满10个,再点击“添加球球”按钮,触发小球逃亡动画,它们会四散而去。当最后一个小球的动画运行完毕,重置数据到初始化状态,这时一切可以从头再来。

CSS设计+JS实现机制是开发类似东东的关键。不过这只是一个简单的案例,尚不具备游戏雏形状态,玩玩而已。真要开发游戏,一楼代码展现出来的思路行不通,需要复杂成百上千倍的设计。

马黑黑 发表于 2025-11-8 12:21

JS代码支持移动端操作

杨帆 发表于 2025-11-8 12:28

好玩,可以添加11个球的动画{:4_174:}

红影 发表于 2025-11-8 15:46

马黑黑 发表于 2025-11-8 12:08
玩法:点击代码框右上角“预览”按钮,进入预览模态界面,在该界面点击“添加球球”按钮,主框内会添加随机 ...

这人的和玩游戏似的,有趣{:4_173:}

红影 发表于 2025-11-8 15:47

马黑黑 发表于 2025-11-8 12:20
实现原理:

一、拖动小球


这些小球的颜色好好看,每点击一下都在猜测下一个渐变的小球是什么颜色的,会落在哪里{:4_187:}

马黑黑 发表于 2025-11-8 18:49

红影 发表于 2025-11-8 15:47
这些小球的颜色好好看,每点击一下都在猜测下一个渐变的小球是什么颜色的,会落在哪里

JS的随机本质上可以破解,但需要做大量的工作才能找到它的算法结果

马黑黑 发表于 2025-11-8 18:50

红影 发表于 2025-11-8 15:46
这人的和玩游戏似的,有趣

不过感觉对移动端拖曳支持不理想,我在这些方面基础薄弱

红影 发表于 2025-11-8 20:45

马黑黑 发表于 2025-11-8 18:49
JS的随机本质上可以破解,但需要做大量的工作才能找到它的算法结果

那就不去找了,只跟着它的算法玩呗。

红影 发表于 2025-11-8 20:46

马黑黑 发表于 2025-11-8 18:50
不过感觉对移动端拖曳支持不理想,我在这些方面基础薄弱

移动端的拖曳有另外的语句么?你能弄好电脑上也很厉害啊{:4_187:}

小辣椒 发表于 2025-11-8 21:57

这些小球以后是初中学习的内容了?

小辣椒 发表于 2025-11-8 21:58

黑黑先预览效果,后面出实题操作

马黑黑 发表于 2025-11-9 12:33

小辣椒 发表于 2025-11-8 21:58
黑黑先预览效果,后面出实题操作

这个只是练手玩玩,没有什么计划

马黑黑 发表于 2025-11-9 12:33

小辣椒 发表于 2025-11-8 21:57
这些小球以后是初中学习的内容了?

幼儿园大班吧

马黑黑 发表于 2025-11-9 12:34

红影 发表于 2025-11-8 20:46
移动端的拖曳有另外的语句么?你能弄好电脑上也很厉害啊

移动端需要另外的代码进行适配,我是放了一组适配代码,但是不好使,改天有空再研究研究,目前精力不再它那里

马黑黑 发表于 2025-11-9 12:35

红影 发表于 2025-11-8 20:45
那就不去找了,只跟着它的算法玩呗。

也行,凭空猜猜也挺好玩

小辣椒 发表于 2025-11-9 17:57

马黑黑 发表于 2025-11-9 12:33
这个只是练手玩玩,没有什么计划

小辣椒是纯欣赏{:4_205:}

小辣椒 发表于 2025-11-9 17:58

马黑黑 发表于 2025-11-9 12:33
幼儿园大班吧

那这个幼儿园大班都是以后的高材生,基础起点就这么高{:4_170:}

马黑黑 发表于 2025-11-9 19:13

小辣椒 发表于 2025-11-9 17:58
那这个幼儿园大班都是以后的高材生,基础起点就这么高

也不一定的。你看宁铂,两岁就背了30多首诗歌,是妥妥的天才,13岁入读中国科大,19岁成为讲师,可是后来并没啥成就,没到40岁就出家做和尚了。幼时聪慧不等于长大后就能成才,还是脚踏实地的好。
页: [1] 2
查看完整版本: 玩个球