转盘抽奖脚本html,转盘抽奖脚本自己撸

需求

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

真的只需要一点点时间而已。

书写伪代码

实现一个东西,一般都先写伪代码,这里也不例外。

初步想法功能主要有两点:

实现一个类 class,只要传入一个元素节点,就可以控制转动,

转动角度和时长以及动画曲线 可 通过参数进行配置

考虑一下,这个类需要什么方法功能?

根据上面的两个需求,

第一 需要一个 设置参数的 方法

第二需要提供一个 开启动画的方法

第三,既然是动画,脱不开定时器功能,所以需要一个动画主循环

这里再提供一个 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

.ball {

width: 100px;

height: 100px;

background: red;

margin: 40px auto;

}

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() {

// 初始化参数

this.setOptions(Object.assign({}, this.customOps));

// 设置一次初始角度

this._rotate(this._angle);

}

/**

* 启动转动函数

*/

rotate() {}

/**

* 设置配置参数

*/

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)`;

}

/**

* 动画主循环

*/

_animate() {}

}

/**

* 判断运行环境支持的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;

}

const r = new RotatePlate({

el: document.querySelector('#ball'),

angle: 60,

animateTo: 1800,

})

实现动画主循环

写到这里,虽然说了一大推话,但是代码去掉注释,真的还没有几行。

但是我们只差一个定时器循环了,接下里实现这个主循环,不断更新 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);

}

源码下载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值