传奇广告查询第一站 同步54.com

转盘抽奖脚本自己撸
原创 于2026-01-05 18:07:00发布
9 阅读
0
0

demo 效果

需求

很多场景都需要做各种活动,抽奖最是司空见惯了,跑马灯的,转盘的,下面先花几分钟撸出一个转盘的吧,当然网上至少有一打的 demo 可供参考。
真的只需要一点点时间而已。

书写伪代码

实现一个东西,一般都先写伪代码,这里也不例外。
初步想法功能主要有两点:

  • 实现一个类 class,只要传入一个元素节点,就可以控制转动,
  • 转动角度和时长以及动画曲线 可 通过参数进行配置

考虑一下,这个类需要什么方法功能?
根据上面的两个需求,

  1. 第一 需要一个 设置参数的 方法
  2. 第二需要提供一个 开启动画的方法
  3. 第三,既然是动画,脱不开定时器功能,所以需要一个动画主循环

这里再提供一个 init 方法用作初始化操作,比如设置参数或者还有其他处理,增加兼容性。
下面是伪代码

class RotatePlate { constructor(options) { this.init(); } /** * 初始化操作 */ init() { this.setOptions(); } /** * 启动转动函数 */ rotate() {} /** * 设置配置参数 */ setOptions() {} /** * 动画主循环 */ _animate() {} }

实现参数 options 配置方法

为了方便使用和兼容处理,我们开发者常用的做法就是配置默认参数,然后用 调用者的参数去覆盖默认参数。所以,先给类增加一些默认配置,如下:

constructor(options) { // 缓存用户数据参数,稍后会进行默认参数覆盖,之后再做重复初始化也会很方便 this.customOps = options; // 默认参数配置 this._parameters = { angle: 0, // 元素初始角度设置 animateTo: 0, // 目标角度 step: null, // 旋转过程中 回调函数 easing: function(t, b, c, d) { return -c * ((t = t / d - 1) * t * t * t - 1) + b; }, // 缓动曲线,关于动画 缓动函数不太懂得可以自行搜索 duration: 3000, // 动画旋转时间 callback: () => {}, // 旋转完成回调 }; this._angle = 0; // 维护一个当前时刻角度的私有变量,下面setOptions就知道如何使用了 } this.init(); // 调用初始化方法

接下来实现 setOptions 方法,并且在 init 方法中进行调用,这个方法实现没什么难度,就是对象合并操作

init() { // 初始化参数 this.setOptions(Object.assign({}, this.customOps)); } /** * 设置配置参数 */ setOptions(parameters) { try { // 获取容器元素 if (typeof parameters.el === 'string') { this.el = document.querySelector(parameters.el); } else { this.el = parameters.el; } // 获取初始角度 if (typeof parameters.angle === 'number') this._angle = parameters.angle; // 合并参数 Object.assign(this._parameters, parameters); } catch (err) {} }

实现一个设置元素样式的方法

上面设置完了参数,我们还没办法验证参数是否正确。
为了实现旋转效果,我们有两种方式可供选择,第一种,利用 css3 的 transform,第二种利用 canvas 绘图。其实两种方法都比较简单,这里先选择 css3 实现一版,结尾再附上 canvas 版本的。

// 实现一个css3样式,我们需要处理兼容性,确定浏览器类型,选择对应的属性 // 这里添加一个辅助方法 /** * 判断运行环境支持的css,用作css制作动画 */ function getSupportCSS() { let supportedCSS = null; const styles = document.getElementsByTagName('head')[0].style; const toCheck = 'transformProperty WebkitTransform OTransform msTransform MozTransform'.split( ' ' ); for (var a = 0; a < toCheck.length; a++) { if (styles[toCheck[a]] !== undefined) { supportedCSS = toCheck[a]; break; } } return supportedCSS; } // 在constructor构造函数里面增加一个属性 this.supportedCSS = getSupportCSS();

然后 给类增加一个 设置样式的方法_rotate

_rotate(angle) { const el = this.el; this._angle = angle; // 更新当前角度 el.style[this.supportedCSS] = `rotate3d(0,0,1,${angle % 360}deg)`; } // 在 init里面增加 _rotate方法,初始化元素 初始角度 init() { // 初始化参数 this.setOptions(Object.assign({}, this.customOps)); // 设置一次初始角度 this._rotate(this._angle); }

在这里,就可以写一个 demo,进行测试了,当然还么有动画,只能测试初始角度 angle 设置
demo 代码,顺便看看我们的脚本代码变成了什么样子:

      Document    

实现动画主循环

写到这里,虽然说了一大推话,但是代码去掉注释,真的还没有几行。
但是我们只差一个定时器循环了,接下里实现这个主循环,不断更新 angle 值就可以了。
说起定时器,我们需要计算动画时间来 判断是否应该取消定时器等等,一些附加操作,所以增加一个_animateStart 方法清理和计时, 下面直接上代码,

 _animateStart() { if (this._timer) { clearTimeout(this._timer); } this._animateStartTime = Date.now(); this._animateStartAngle = this._angle; this._animate(); } /** * 动画主循环 */ _animate() { const actualTime = Date.now(); const checkEnd = actualTime - this._animateStartTime > this._parameters.duration; // 判断是否应该 结束 if (checkEnd) { clearTimeout(this._timer); } else { if (this.el) { // 调用缓动函数,获取当前时刻 angle值 var angle = this._parameters.easing( actualTime - this._animateStartTime, this._animateStartAngle, this._parameters.animateTo - this._animateStartAngle, this._parameters.duration ); // 设置 el 元素的样式 this._rotate(~~(angle * 10) / 10); } if (this._parameters.step) { this._parameters.step.call(this, this._angle); } // 循环调用 this._timer = setTimeout(() => { this._animate(); }, 10); } // 完成回调 if (this._parameters.callback && checkEnd) { this._angle = this._parameters.animateTo; this._rotate(this._angle); this._parameters.callback.call(this); } }

然后再 rotate 方法调用_animateStart 就好了

 rotate() { if (this._angle === this._parameters.animateTo) { this._rotate(this._angle); } else { this._animateStart(); } }

至此,一个利用 css3 实现的脚本就完成了,有木有很简单,下面贴上完整代码.

/** * 功能: 开发一个旋转插件,传入一个元素节点即可控制旋转 * 转动角度和时长以及动画曲线 可 通过参数进行配置 * * 参数列表: * - dom 需要一个容器 必选 * - angle 初始角度 非必选 * - animateTo 结束角度 非必选 * - duration 动画时长 非必选 * - easing 缓动函数 非必选 * - step 角度每次更新调用 非必选 * - callback 动画结束回调 非必选 */ class RotatePlate { constructor(options) { // 获取当前运行环境支持的样式属性 this.supportedCSS = getSupportCSS(); // 缓存用户数据 this.customOps = options; // 私有参数 this._parameters = { angle: 0, animateTo: 0, step: null, easing: function(t, b, c, d) { return -c * ((t = t / d - 1) * t * t * t - 1) + b; }, duration: 3000, callback: () => {}, }; this._angle = 0; // 当前时刻角度 this.init(); } /** * 初始化操作 */ init(newOps = {}) { // 初始化参数 this.setOptions(Object.assign({}, this.customOps, newOps)); // 设置一次初始角度 this._rotate(this._angle); } /** * 启动转动函数 */ rotate() { if (this._angle === this._parameters.animateTo) { this._rotate(this._angle); } else { this._animateStart(); } } /** * 设置配置参数 */ setOptions(parameters) { try { // 获取容器元素 if (typeof parameters.el === 'string') { this.el = document.querySelector(parameters.el); } else { this.el = parameters.el; } // 获取初始角度 if (typeof parameters.angle === 'number') this._angle = parameters.angle; // 合并参数 Object.assign(this._parameters, parameters); } catch (err) {} } _rotate(angle) { const el = this.el; this._angle = angle; // 更新当前角度 el.style[this.supportedCSS] = `rotate3d(0,0,1,${angle % 360}deg)`; } _animateStart() { if (this._timer) { clearTimeout(this._timer); } this._animateStartTime = Date.now(); this._animateStartAngle = this._angle; this._animate(); } /** * 动画主循环 */ _animate() { const actualTime = Date.now(); const checkEnd = actualTime - this._animateStartTime > this._parameters.duration; if (checkEnd) { clearTimeout(this._timer); } else { if (this.el) { var angle = this._parameters.easing( actualTime - this._animateStartTime, this._animateStartAngle, this._parameters.animateTo - this._animateStartAngle, this._parameters.duration ); this._rotate(~~(angle * 10) / 10); } if (this._parameters.step) { this._parameters.step.call(this, this._angle); } this._timer = setTimeout(() => { this._animate(); }, 10); } if (this._parameters.callback && checkEnd) { this._angle = this._parameters.animateTo; this._rotate(this._angle); this._parameters.callback.call(this); } } } /** * 判断运行环境支持的css,用作css制作动画 */ function getSupportCSS() { let supportedCSS = null; const styles = document.getElementsByTagName('head')[0].style; const toCheck = 'transformProperty WebkitTransform OTransform msTransform MozTransform'.split( ' ' ); for (var a = 0; a < toCheck.length; a++) { if (styles[toCheck[a]] !== undefined) { supportedCSS = toCheck[a]; break; } } return supportedCSS; }

下面再补充一个 canvas 实现的动画方法:

 _rotateCanvas(angle) { // devicePixelRatio 是设备像素比,为了解决canvas模糊问题设置的 // 原理把 canvas画布扩大,然后缩小显示在屏幕 this._angle = angle; const radian = ((angle % 360) * Math.PI) / 180; this._canvas.width = this.WIDTH * this.devicePixelRatio; this._canvas.height = this.HEIGHT * this.devicePixelRatio; // 解决模糊问题 this._cnv.scale(this.devicePixelRatio, this.devicePixelRatio); // 平移canvas原点 this._cnv.translate(this.WIDTH / 2, this.HEIGHT / 2); // 平移后旋转 this._cnv.rotate(radian); // 移回 原点 this._cnv.translate(-this.WIDTH / 2, -this.HEIGHT / 2); this._cnv.drawImage(this._img, 0, 0, this.WIDTH, this.HEIGHT); }

源码下载

完整源码请到 github 下载,查看

管理员
0
0
0
分享
上一篇: 传奇GOM引擎——佩戴某个物品,切割怪物指定血量
下一篇: 服务器脚本系列教程
评论
历史记录
回顶部
浏览时间 游戏名称 游戏IP 开区网址
注册GM1论坛账号
  • 上传头像
注册

已有账号,

微信扫码登录
重置密码
重置密码

注册

绑定关联手机号
关联手机号