首先来看一个简单的列子 是一个固定选项的抽奖页面 最终实现的效果是点击开始按钮之后会随机让一个单元格的背景色变成黄色 是一个很简单的抽奖 css样式部分小伙伴门可以自行调整
简单抽奖
<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>Document</title></head><body><divid="app"><buttonclass="btn">start</button><divclass="container"><divclass="basis-box">1</div><divclass="basis-box">2</div><divclass="basis-box">3</div><divclass="basis-box">4</div><divclass="basis-box">5</div><divclass="basis-box">6</div></div></div></body><script>constbtn=document.querySelector('.btn');//获取按钮//获取所有格子constcellList=document.getElementsByClassName('basis-box');letindex=0;//记录当前显示的cell索引// 添加点击事件btn.addEventListener('click',function(){cellList[index].classList.remove("highlight")index=Math.floor(Math.random()*cellList.length);cellList[index].classList.add("highlight")});</script><style>body{width:100vw;/* 占满窗口 */padding:0;}#app{width:100%;display:flex;flex-direction:column;align-items:center;}/* 按钮的样式 */.btn{width:60px;height:40px;}.container{display:flex;flex-direction:row;/* 横向排列每一个cell */font-size:28px;margin-top:10px;margin-bottom:10px;}.basis-box{display:flex;justify-content:center;/* 垂直居中 */align-items:center;/* 水平居中 */width:100px;height:100px;margin-left:10px;border:2px solid black;/* 边框 */}/* 用来表示选项的类 */.highlight{background-color:yellow;}</style></html>这样就实现了一个简单的抽奖 但是这样的没有灵魂(好看的动画效果)这里只展示js的具体逻辑效果 css 的可以自行定义
具体实现
想要实现动画效果也就是实现随着时间变化速度也跟随一起变化的效果自然需要计时器,但是计时器多了会造成严重的卡顿,故此可以使用一个计时器实现这个效果。
实现思路如下:
- 增加速度变量
- 让计时器按照一定的tick进行
- 速度可以使用时间 也可以使用计数器
本文使用计数器实现速度变化 类似于我的世界的tick设计
像是这个样子 就可以实现一个简单的往复运动的抽奖但是有个问题 这个每次都会落到一个方块上
btn.addEventListener('click',function(){updateCell();});constupdateCell=()=>{letpreIndex=0;lettickCount=0;//记录已经更新的次数letcurInterval=2;//当前间隔letpreUpdateTick=0;//上次更新的tick数// 初始化第一个高亮cellList[index].classList.remove("highlight");consttimer=setInterval(()=>{tickCount++;if(tickCount-preUpdateTick>=curInterval){preUpdateTick=tickCount;curInterval=computedInterval(tickCount);// 更新高亮显示cellList[index].classList.remove("highlight");// 使用数学公式实现往复高亮index=Math.abs((preIndex++%(2*cellList.length-2))-(cellList.length-1));cellList[index].classList.add("highlight");}if(tickCount>=maxTick){clearInterval(timer);console.log('动画结束');return;}},tick);}constcomputedInterval=(tickCount)=>{if(tickCount<maxTick/3){return2;}elseif(tickCount<(maxTick/3)*2){return5;}elseif(tickCount<maxTick){return7;}}可以给这个抽奖添加一个随机的初始位置实现随机的效果,并且可以优化一下现在的动画变动效果 现在的略显卡顿 tick也就是帧率 调小之后会变得更加流畅
改动之后的间隔计算: 增加阶段机制 让其更像是游戏里的抽奖 具体数值可以自己调整
constcomputedInterval=(tickCount)=>{constprogress=tickCount/maxTick;// 使用缓动函数实现更自然的减速效果if(progress<0.15){// 一阶段:0-15%return1+progress*20;// 1 -> 4}elseif(progress<0.35){// 二阶段:15%-35%constp=(progress-0.15)/0.2;return4+p*8;// 4 -> 12}elseif(progress<0.6){// 三阶段:中速 (35%-60%)constp=(progress-0.35)/0.25;return12+p*13;// 12 -> 25}elseif(progress<0.85){// 四阶段:慢速 (60%-85%)constp=(progress-0.6)/0.25;return25+p*20;// 25 -> 45}else{// 五阶段:85%-100%constp=(progress-0.85)/0.15;return45+p*35;// 45 -> 80}}新的更新函数 增加了一个随机初始位置 并且增加一个随机是否反向运动增加随机性 这样的抽奖看起来更加的灵动
constupdateCell=()=>{// 随机起始位置letpreIndex=Math.floor(Math.random()*cellList.length);lettickCount=0;//记录已经更新的次数letcurInterval=1;//初始间隔letpreUpdateTick=0;//上次更新的tick数// 随机决定是否反向运动letdirection=Math.random()>0.5?1:-1;if(direction===-1){preIndex=cellList.length-1-preIndex;}// 初始化第一个高亮cellList[index].classList.remove("highlight");index=preIndex;cellList[index].classList.add("highlight");consttimer=setInterval(()=>{// 同上}}扩展建议:
动态增加/删除格子
可以在抽奖过程中或开始前动态调整格子数量,让游戏更具灵活性:
<!-- 添加控制按钮 --><divclass="controls"><buttonid="addCellBtn">+ 添加格子</button><buttonid="removeCellBtn">- 删除格子</button></div>// 动态添加格子constaddCell=()=>{constnewCell=document.createElement('div');newCell.className='basis-box';newCell.textContent=cellList.length+1;container.appendChild(newCell);cellList=document.getElementsByClassName('basis-box');};// 动态删除格子(至少保留2个)constremoveCell=()=>{if(cellList.length>2){container.removeChild(container.lastElementChild);cellList=document.getElementsByClassName('basis-box');// 如果当前索引超出范围,重置为0if(index>=cellList.length){index=0;}}};document.getElementById('addCellBtn').addEventListener('click',addCell);document.getElementById('removeCellBtn').addEventListener('click',removeCell);自定义格子内容
允许用户为每个格子设置自定义文本或图片:
// 双击格子编辑内容cellList.forEach((cell,idx)=>{cell.addEventListener('dblclick',()=>{constnewText=prompt(`请输入第${idx+1}个格子的内容:`,cell.textContent);if(newText!==null&&newText.trim()!==''){cell.textContent=newText;}});});多种动画模式
提供不同的动画效果供用户选择:
constanimationModes={// 往复运动(默认)bounce:(counter,length)=>{returnMath.abs((counter%(2*length-2))-(length-1));},// 单向循环loop:(counter,length)=>{returncounter%length;},// 随机跳动random:(counter,length)=>{returnMath.floor(Math.random()*length);},// 螺旋式减速spiral:(counter,length,maxTick)=>{constprogress=counter/maxTick;constspeed=1+Math.pow(progress,3)*10;returnMath.floor(counter/speed)%length;}};// 使用示例constmode='bounce';// 可切换index=animationModes[mode](preIndex++,cellList.length,maxTick);概率控制系统
为不同格子设置不同的中奖概率:
// 定义每个格子的权重(概率)constweights=[10,20,30,15,15,10];// 数值越大,概率越高// 根据权重随机选择最终结果constweightedRandom=()=>{consttotalWeight=weights.reduce((sum,w)=>sum+w,0);letrandom=Math.random()*totalWeight;for(leti=0;i<weights.length;i++){random-=weights[i];if(random<=0){returni;}}returnweights.length-1;};// 在updateCell中使用constfinalIndex=weightedRandom();// ... 计算总步数时以finalIndex为终点