├── .eslintignore
├── .gitignore
├── examples
├── images
│ ├── dial_bg.png
│ ├── scratch_no.png
│ ├── tiger_bg.png
│ ├── dial_page_bg.jpg
│ ├── dial_pointer.png
│ ├── placeholder.jpg
│ ├── scratch_ceiling.jpg
│ ├── scratch_page_bg.jpg
│ ├── tiger_awards_1.png
│ ├── tiger_awards_2.png
│ ├── tiger_awards_3.png
│ └── tiger_page_bg.jpg
├── card.html
├── dial.html
├── tiger.html
└── css
│ └── style.css
├── .eslintrc.js
├── src
├── modules
│ ├── assign.js
│ ├── getPrefix.js
│ ├── animationEnd.js
│ ├── requestAnimationFrame.js
│ └── events.js
├── dial.js
├── tiger.js
└── card.js
├── package.json
├── dist
├── dial.js
├── tiger.js
└── card.js
└── README.md
/.eslintignore:
--------------------------------------------------------------------------------
1 | /dist
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .idea
3 | npm-debug.log
4 | node_modules
5 |
--------------------------------------------------------------------------------
/examples/images/dial_bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/94cstyles/lottery/HEAD/examples/images/dial_bg.png
--------------------------------------------------------------------------------
/examples/images/scratch_no.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/94cstyles/lottery/HEAD/examples/images/scratch_no.png
--------------------------------------------------------------------------------
/examples/images/tiger_bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/94cstyles/lottery/HEAD/examples/images/tiger_bg.png
--------------------------------------------------------------------------------
/examples/images/dial_page_bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/94cstyles/lottery/HEAD/examples/images/dial_page_bg.jpg
--------------------------------------------------------------------------------
/examples/images/dial_pointer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/94cstyles/lottery/HEAD/examples/images/dial_pointer.png
--------------------------------------------------------------------------------
/examples/images/placeholder.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/94cstyles/lottery/HEAD/examples/images/placeholder.jpg
--------------------------------------------------------------------------------
/examples/images/scratch_ceiling.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/94cstyles/lottery/HEAD/examples/images/scratch_ceiling.jpg
--------------------------------------------------------------------------------
/examples/images/scratch_page_bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/94cstyles/lottery/HEAD/examples/images/scratch_page_bg.jpg
--------------------------------------------------------------------------------
/examples/images/tiger_awards_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/94cstyles/lottery/HEAD/examples/images/tiger_awards_1.png
--------------------------------------------------------------------------------
/examples/images/tiger_awards_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/94cstyles/lottery/HEAD/examples/images/tiger_awards_2.png
--------------------------------------------------------------------------------
/examples/images/tiger_awards_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/94cstyles/lottery/HEAD/examples/images/tiger_awards_3.png
--------------------------------------------------------------------------------
/examples/images/tiger_page_bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/94cstyles/lottery/HEAD/examples/images/tiger_page_bg.jpg
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: {
4 | browser: true,
5 | node: true
6 | },
7 | extends: 'standard',
8 | plugins: [
9 | 'html'
10 | ],
11 | globals: {
12 | 'Vue': false
13 | },
14 | 'rules': {
15 | 'arrow-parens': 0,
16 | 'prefer-const': ['error', {
17 | 'destructuring': 'any',
18 | 'ignoreReadBeforeAssign': false
19 | }],
20 | 'space-before-function-paren': ['error', {
21 | 'anonymous': 'ignore',
22 | 'named': 'ignore',
23 | 'asyncArrow': 'ignore'
24 | }]
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/modules/assign.js:
--------------------------------------------------------------------------------
1 | Object.assign = Object.assign ||
2 | function (target) {
3 | // We must check against these specific cases.
4 | if (target === undefined || target === null) {
5 | throw new TypeError('Cannot convert undefined or null to object')
6 | }
7 |
8 | const output = Object(target)
9 | for (let index = 1; index < arguments.length; index++) {
10 | const source = arguments[index]
11 | if (source !== undefined && source !== null) {
12 | for (const nextKey in source) {
13 | if (source.hasOwnProperty(nextKey)) {
14 | output[nextKey] = source[nextKey]
15 | }
16 | }
17 | }
18 | }
19 | return output
20 | }
21 |
22 | export default Object.assign
23 |
--------------------------------------------------------------------------------
/src/modules/getPrefix.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 获取css属性前缀 null:不支持该属性
3 | * @param el 用于校验的element
4 | * @param property css属性
5 | * @param value 样式值 设置该值是会进行赋值
6 | * @returns {string || null}
7 | */
8 | export default function (el, property, value) {
9 | function camelCase (str) {
10 | return str.replace(/-([a-z])/ig, function (all, letter) {
11 | return letter.toUpperCase()
12 | })
13 | }
14 |
15 | if (el.style[property] === undefined) {
16 | for (const vendor of ['webkit', 'ms', 'moz', 'o', null]) {
17 | if (!vendor) return null
18 | property = camelCase(vendor + '-' + property)
19 | if (el.style[property] !== undefined) {
20 | break
21 | }
22 | }
23 | }
24 | if (value) el.style[property] = value
25 |
26 | return property
27 | }
28 |
--------------------------------------------------------------------------------
/src/modules/animationEnd.js:
--------------------------------------------------------------------------------
1 | const animationEvent = function () {
2 | const el = document.createElement('div')
3 | const animations = {
4 | 'animation': 'animationend',
5 | 'webkitAnimation': 'webkitAnimationEnd',
6 | 'msAnimation': 'MSAnimationEnd',
7 | 'oAnimation': 'oanimationend'
8 | }
9 |
10 | for (const t in animations) {
11 | if (el.style[t] !== undefined) {
12 | return animations[t]
13 | }
14 | }
15 |
16 | return null
17 | }()
18 |
19 | /**
20 | * 处理animate动画结束时间
21 | * @param el 绑定事件目标元素
22 | * @param callback 回调函数
23 | * @param animateTime 当不支持animationend使用settimeout处理 延迟时间
24 | */
25 | export default function (el, callback, animateTime = 0) {
26 | function bind () {
27 | callback()
28 | el.removeEventListener(animationEvent, bind)
29 | }
30 |
31 | animationEvent ? el.addEventListener(animationEvent, bind) : setTimeout(() => callback(), animateTime)
32 | }
33 |
--------------------------------------------------------------------------------
/src/modules/requestAnimationFrame.js:
--------------------------------------------------------------------------------
1 | let lastTime = 0
2 | export const requestAnimationFrame = window.requestAnimationFrame ||
3 | window['msRequestAnimationFrame'] ||
4 | window['mozRequestAnimationFrame'] ||
5 | window['webkitRequestAnimationFrame'] ||
6 | window['oRequestAnimationFrame'] ||
7 | function (callback) {
8 | const currTime = new Date().getTime()
9 | const timeToCall = Math.max(0, 16 - (currTime - lastTime))
10 | const id = window.setTimeout(() => {
11 | callback(currTime + timeToCall) // eslint-disable-line
12 | }, timeToCall)
13 |
14 | lastTime = currTime + timeToCall
15 | return id
16 | }
17 |
18 | export const cancelAnimationFrame = window.cancelAnimationFrame ||
19 | window['msCancelAnimationFrame'] ||
20 | window['mozCancelAnimationFrame'] ||
21 | window['webkitCancelAnimationFrame'] ||
22 | window['oCancelAnimationFrame'] ||
23 | function (id) {
24 | clearInterval(id)
25 | }
26 |
--------------------------------------------------------------------------------
/src/modules/events.js:
--------------------------------------------------------------------------------
1 | export default class Events {
2 | constructor () {
3 | this._queue = []
4 | }
5 |
6 | on (key, callback) {
7 | this._queue[key] = this._queue[key] || []
8 | this._queue[key].push(callback)
9 | return this
10 | }
11 |
12 | off (key, callback) {
13 | if (this._queue[key]) {
14 | const index = typeof (callback) === 'undefined' ? -2 : this._queue[key].indexOf(callback)
15 | if (index === -2) {
16 | delete this._queue[key]
17 | } else if (index !== -1) {
18 | this._queue[key].splice(index, 1)
19 | }
20 | if (this._queue[key] && this._queue[key].length === 0) delete this._queue[key]
21 | }
22 | return this
23 | }
24 |
25 | has (key) {
26 | return !!this._queue[key]
27 | }
28 |
29 | trigger (key, ...args) {
30 | if (this._queue[key]) {
31 | this._queue[key].forEach((callback) => callback.apply(this, args))
32 | }
33 | return this
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "lottery",
3 | "version": "1.0.0",
4 | "description": "移动端抽奖插件[大转盘,老虎机,刮刮卡]",
5 | "main": "./dist/lottery.js",
6 | "scripts": {
7 | "dev": "NODE_ENV=development node build/index.js",
8 | "release": "NODE_ENV=production node build/index.js"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "git+https://github.com/94cstyles/lottery.git"
13 | },
14 | "author": "cstyles",
15 | "license": "ISC",
16 | "bugs": {
17 | "url": "https://github.com/94cstyles/lottery/issues"
18 | },
19 | "homepage": "https://github.com/94cstyles/lottery#readme",
20 | "devDependencies": {
21 | "babel-preset-es2015-loose-rollup": "^7.0.0",
22 | "eslint": "^3.19.0",
23 | "eslint-config-standard": "^10.2.1",
24 | "eslint-plugin-html": "^2.0.1",
25 | "eslint-plugin-import": "^2.2.0",
26 | "eslint-plugin-node": "^4.2.2",
27 | "eslint-plugin-promise": "^3.5.0",
28 | "eslint-plugin-standard": "^3.0.1",
29 | "rollup": "^0.41.6",
30 | "rollup-plugin-babel": "^2.7.1",
31 | "rollup-plugin-uglify": "^1.0.2"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/examples/card.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | 刮刮卡
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/examples/dial.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | 大转盘
11 |
12 |
13 |
14 |
19 |
20 |
21 |
40 |
41 |
--------------------------------------------------------------------------------
/examples/tiger.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | 老虎机
11 |
12 |
13 |
14 |
15 |
16 |
17 | -
18 |
19 |
20 | -
21 |
22 |
23 | -
24 |
25 |
26 |
27 |
28 |
29 |
30 | -
31 |
32 |
33 | -
34 |
35 |
36 | -
37 |
38 |
39 |
40 |
41 |
42 |
43 | -
44 |
45 |
46 | -
47 |
48 |
49 | -
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
71 |
72 |
--------------------------------------------------------------------------------
/src/dial.js:
--------------------------------------------------------------------------------
1 | import './modules/assign'
2 | import Events from './modules/events'
3 | import getPrefix from './modules/getPrefix'
4 | import { requestAnimationFrame, cancelAnimationFrame } from './modules/requestAnimationFrame'
5 |
6 | class LotteryDial extends Events {
7 | constructor (pointer, options) {
8 | super()
9 |
10 | this.options = Object.assign({
11 | speed: 30, // 每帧速度
12 | areaNumber: 8, // 奖区数量
13 | deviation: 2 // 随机结果角度偏差值 为了防止出现指针和扇区分割线无限重合 单位:°
14 | }, options)
15 | this.pointer = pointer
16 |
17 | this.init()
18 | }
19 |
20 | init () {
21 | // 初始化样式设定
22 | this._transform = getPrefix(this.pointer, 'transform', 'translate3d(0,0,0)')
23 | getPrefix(this.pointer, 'backfaceVisibility', 'hidden')
24 | getPrefix(this.pointer, 'perspective', '1000px')
25 |
26 | this._raf = null
27 | this._runAngle = 0
28 | this._targetAngle = -1
29 | }
30 |
31 | reset (event = 'reset') {
32 | if (!this._raf) return
33 | cancelAnimationFrame(this._raf)
34 | this._raf = null
35 | this._runAngle = 0
36 | this._targetAngle = -1
37 | this.trigger(event)
38 | if (event === 'reset') getPrefix(this.pointer, this._transform, 'translate3d(0,0,0) rotate(0deg)')
39 | }
40 |
41 | setResult (index) {
42 | // 得到中奖结果 index:中奖奖区下标
43 | const singleAngle = 360 / this.options.areaNumber // 单个奖区角度值
44 | let endAngle = Math.random() * singleAngle // 随机得出结果在扇区内的角度
45 |
46 | endAngle = Math.max(this.options.deviation, endAngle)
47 | endAngle = Math.min(singleAngle - this.options.deviation, endAngle)
48 | endAngle = Math.ceil(endAngle + (index * singleAngle))
49 |
50 | this._runAngle = 0
51 | this._targetAngle = endAngle + (Math.floor(Math.random() * 4) + 4) * 360 // 随机旋转几圈再停止
52 | }
53 |
54 | step () {
55 | // 如果没有设置结束点 就匀速不停旋转
56 | // 如果设置了结束点 就减速到达结束点
57 | if (this._targetAngle === -1) {
58 | this._runAngle += this.options.speed
59 | } else {
60 | this._angle = (this._targetAngle - this._runAngle) / this.options.speed
61 | this._angle = this._angle > this.options.speed ? this.options.speed : this._angle < 0.5 ? 0.5 : this._angle
62 | this._runAngle += this._angle
63 | this._runAngle = this._runAngle > this._targetAngle ? this._targetAngle : this._runAngle
64 | }
65 | // 指针旋转
66 | getPrefix(this.pointer, this._transform, 'translate3d(0,0,0) rotate(' + (this._runAngle % 360) + 'deg)')
67 |
68 | if (this._runAngle === this._targetAngle) {
69 | this.reset('end')
70 | } else {
71 | this._raf = requestAnimationFrame(() => this.step())
72 | }
73 | }
74 |
75 | draw () {
76 | if (this._raf) return
77 | if (this.has('start')) this.trigger('start')
78 | this._angle = 0
79 | this._raf = requestAnimationFrame(() => this.step())
80 | }
81 | }
82 |
83 | export default LotteryDial
84 |
--------------------------------------------------------------------------------
/src/tiger.js:
--------------------------------------------------------------------------------
1 | import './modules/assign'
2 | import Events from './modules/events'
3 | import animationEnd from './modules/animationEnd'
4 |
5 | class LotteryTigerRoller {
6 | constructor (elem) {
7 | this.elem = elem
8 | this.items = elem.children
9 |
10 | // 克隆第一个节点 用于制作无限滚动效果
11 | this.elem.appendChild(this.items[0].cloneNode(true))
12 | }
13 |
14 | resize () {
15 | this.height = this.items[0].clientHeight
16 | if (!this.elem.classList.contains('fx-roll') && this.index > 0) this.elem.style.marginTop = -this.index * this.height + 'px'
17 | }
18 |
19 | reset () {
20 | this.elem.classList.remove('fx-roll')
21 | this.elem.classList.remove('fx-bounce')
22 | this.elem.style.marginTop = 0
23 | this.state = 0
24 | }
25 |
26 | start (timeout = 0) {
27 | if (this.state === 1) return
28 | this.state = 1
29 | setTimeout(() => {
30 | if (this.state !== 1) return
31 | this.elem.classList.add('fx-roll')
32 | this.elem.style.marginTop = 0
33 | }, timeout)
34 | }
35 |
36 | stop (index, callback, timeout = 0) {
37 | if (!this.height) this.height = this.items[0].clientHeight
38 | setTimeout(() => {
39 | if (this.state !== 1) return
40 | this.elem.classList.remove('fx-roll')
41 | this.elem.classList.add('fx-bounce')
42 | this.elem.style.marginTop = -index * this.height + 'px'
43 | animationEnd(this.elem, () => {
44 | this.state = 0
45 | this.elem.classList.remove('fx-bounce')
46 | if (callback) callback.call(this, index)
47 | })
48 | }, timeout)
49 | }
50 | }
51 |
52 | class LotteryTiger extends Events {
53 | constructor (toggle, rollers, options) {
54 | super()
55 |
56 | this.options = Object.assign({
57 | interval: 300, // 每个roller间动画间隔
58 | aniMinTime: 6000, // 动画执行最少时间
59 | resize: true // roller大小是否是可变的
60 | }, options)
61 | this.toggle = toggle
62 |
63 | // 初始化滚轴
64 | this.rollerQueue = []
65 | for (let i = 0; i < rollers.length; i++) {
66 | this.rollerQueue.push(new LotteryTigerRoller(rollers[i]))
67 | }
68 |
69 | // 如果大小是可变的就绑定resize事件
70 | if (this.options.resize) {
71 | window.addEventListener('onorientationchange' in document ? 'orientationchange' : 'resize', () => {
72 | this.rollerQueue.forEach((roller) => roller.resize())
73 | })
74 | }
75 | }
76 |
77 | reset () {
78 | this.toggle.classList.remove('z-active')
79 | for (let i = 0, l = this.rollerQueue.length; i < l; i++) {
80 | this.rollerQueue[i].reset()
81 | }
82 | this.trigger('reset')
83 | }
84 |
85 | setResult (ret) {
86 | // 保证动画执行时间
87 | const endTime = (new Date()).getTime()
88 | setTimeout(() => {
89 | for (let i = 0, l = this.rollerQueue.length; i < l; i++) {
90 | this.rollerQueue[i].stop(ret[i], (i === l - 1 ? () => {
91 | this.toggle.classList.remove('z-active')
92 | this.trigger('end')
93 | } : null), i * this.options.interval)
94 | }
95 | }, endTime - this._startTime > this.options.aniMinTime ? 0 : this.options.aniMinTime - (endTime - this._startTime))
96 | }
97 |
98 | draw () {
99 | if (this.toggle.classList.contains('z-active')) return
100 | if (this.has('start')) this.trigger('start')
101 | this._startTime = (new Date()).getTime()
102 |
103 | this.toggle.classList.add('z-active')
104 | for (let i = 0, l = this.rollerQueue.length; i < l; i++) {
105 | this.rollerQueue[i].start(i * this.options.interval)
106 | }
107 | }
108 | }
109 |
110 | export default LotteryTiger
111 |
--------------------------------------------------------------------------------
/dist/dial.js:
--------------------------------------------------------------------------------
1 | !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.LotteryDial=e()}(this,function(){"use strict";Object.assign=Object.assign||function(t){if(void 0===t||null===t)throw new TypeError("Cannot convert undefined or null to object");for(var e=Object(t),n=1;n1?n-1:0),r=1;r0&&void 0!==arguments[0]?arguments[0]:"reset";this._raf&&(a(this._raf),this._raf=null,this._runAngle=0,this._targetAngle=-1,this.trigger(t),"reset"===t&&r(this.pointer,this._transform,"translate3d(0,0,0) rotate(0deg)"))},o.prototype.setResult=function(t){var e=360/this.options.areaNumber,n=Math.random()*e;n=Math.max(this.options.deviation,n),n=Math.min(e-this.options.deviation,n),n=Math.ceil(n+t*e),this._runAngle=0,this._targetAngle=n+360*(Math.floor(4*Math.random())+4)},o.prototype.step=function(){var t=this;-1===this._targetAngle?this._runAngle+=this.options.speed:(this._angle=(this._targetAngle-this._runAngle)/this.options.speed,this._angle=this._angle>this.options.speed?this.options.speed:this._angle<.5?.5:this._angle,this._runAngle+=this._angle,this._runAngle=this._runAngle>this._targetAngle?this._targetAngle:this._runAngle),r(this.pointer,this._transform,"translate3d(0,0,0) rotate("+this._runAngle%360+"deg)"),this._runAngle===this._targetAngle?this.reset("end"):this._raf=s(function(){return t.step()})},o.prototype.draw=function(){var t=this;this._raf||(this.has("start")&&this.trigger("start"),this._angle=0,this._raf=s(function(){return t.step()}))},o}(i)});
2 |
--------------------------------------------------------------------------------
/dist/tiger.js:
--------------------------------------------------------------------------------
1 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.LotteryTiger=t()}(this,function(){"use strict";Object.assign=Object.assign||function(e){if(void 0===e||null===e)throw new TypeError("Cannot convert undefined or null to object");for(var t=Object(e),i=1;i1?i-1:0),o=1;o2&&void 0!==arguments[2]?arguments[2]:0;o?e.addEventListener(o,i):setTimeout(function(){return t()},n)},s=function(){function t(i){e(this,t),this.elem=i,this.items=i.children,this.elem.appendChild(this.items[0].cloneNode(!0))}return t.prototype.resize=function(){this.height=this.items[0].clientHeight,!this.elem.classList.contains("fx-roll")&&this.index>0&&(this.elem.style.marginTop=-this.index*this.height+"px")},t.prototype.reset=function(){this.elem.classList.remove("fx-roll"),this.elem.classList.remove("fx-bounce"),this.elem.style.marginTop=0,this.state=0},t.prototype.start=function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0;1!==this.state&&(this.state=1,setTimeout(function(){1===e.state&&(e.elem.classList.add("fx-roll"),e.elem.style.marginTop=0)},t))},t.prototype.stop=function(e,t){var i=this,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;this.height||(this.height=this.items[0].clientHeight),setTimeout(function(){1===i.state&&(i.elem.classList.remove("fx-roll"),i.elem.classList.add("fx-bounce"),i.elem.style.marginTop=-e*i.height+"px",r(i.elem,function(){i.state=0,i.elem.classList.remove("fx-bounce"),t&&t.call(i,e)}))},n)},t}();return function(n){function o(t,r,u){e(this,o);var l=i(this,n.call(this));l.options=Object.assign({interval:300,aniMinTime:6e3,resize:!0},u),l.toggle=t,l.rollerQueue=[];for(var a=0;athis.options.aniMinTime?0:this.options.aniMinTime-(i-this._startTime))},o.prototype.draw=function(){if(!this.toggle.classList.contains("z-active")){this.has("start")&&this.trigger("start"),this._startTime=(new Date).getTime(),this.toggle.classList.add("z-active");for(var e=0,t=this.rollerQueue.length;e
6 | start => (next) 抽奖前触发,主要用于抽奖信息处理,传递回调函数next,需进行调用。
7 | end => () 抽奖结束触发
8 | reset => () 抽奖重置触发,主要出现调用了lottery.reset()
9 |
10 | lottery都包含了3个操作函数 [setResult,reset,draw]
11 | setResult => (result) 设置抽奖结果
12 | reset => () 重置抽奖
13 | draw => () 开始抽奖(注:刮刮卡调用此方法后会直接显示结果)
14 |
15 | ### 刮刮卡
16 |
17 | ```html
18 |
19 |
20 | ```
21 |
22 | ```javascript
23 | var lottery = new LotteryCard(document.getElementById('js_lottery'),{
24 | size: 20, //滑动区域大小
25 | percent: 50, //激活百分比到谋个值 就全显示
26 | resize: true, //canvas的大小是否是可变的
27 | cover: null //img or color string, default gray
28 | });
29 | lottery.on('start',function(){
30 | //中奖结果,传递是中奖结果图片地址
31 | lottery.setResult('...imageSrc');
32 | }).on('end',function(){}).on('reset',function(){});
33 | ```
34 |
35 | ### 大转盘
36 |
37 | ```html
38 |
43 | ```
44 |
45 | ```javascript
46 | var lottery = new LotteryDial(document.getElementById('js_pointer'), {
47 | speed: 30, //每帧速度
48 | areaNumber: 8, //奖区数量
49 | deviation: 2 //随机结果角度偏差值 为了防止出现指针和扇区分割线无限重合 单位:°
50 | });
51 | var index = -1;
52 | lottery.on('start', function () {
53 | //请求获取中奖结果
54 | index = Math.round(Math.random() * 7);
55 | //中奖结果,传递停留奖区下标0开始
56 | lottery.setResult(index);
57 | }).on('end', function () {
58 | console.log('中奖奖区:' + index);
59 | }).on('reset',function(){});
60 | ```
61 |
62 | ### 老虎机
63 |
64 | ```html
65 |
66 |
67 |
68 |
69 | -
70 |
71 |
72 | -
73 |
74 |
75 | -
76 |
77 |
78 |
79 |
80 |
81 |
82 | -
83 |
84 |
85 | -
86 |
87 |
88 | -
89 |
90 |
91 |
92 |
93 |
94 |
95 | -
96 |
97 |
98 | -
99 |
100 |
101 | -
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | ```
110 |
111 | ```javascript
112 | //老虎机动画因为性能问题采用css3 所以需要完成2个动画fx-bounce fx-roll 详细查看examples
113 | var lottery = new LotteryTiger(document.getElementById('js_toggle'), document.querySelectorAll('.roller'), {
114 | interval: 300, //每个roller间动画间隔
115 | aniMinTime: 6000, //动画执行最少时间
116 | resize: true //roller大小是否是可变的
117 | });
118 | lottery.on('start', function () {
119 | setTimeout(function () {
120 | var ret = [Math.round(Math.random() * 2), Math.round(Math.random() * 2), Math.round(Math.random() * 2)];
121 | //中奖结果,传递每个roller停留下标0开始
122 | lottery.setResult(ret);
123 | }, 1000);
124 | }).on('end', function(){}).on('reset',function(){});
125 | ```
126 |
127 |
128 | ## 浏览器支持
129 |
130 |  |  |  |  | 
131 | --- | --- | --- | --- | --- |
132 | Latest ✔ | Latest ✔ | 10+ ✔ | Latest ✔ | Latest ✔ |
--------------------------------------------------------------------------------
/dist/card.js:
--------------------------------------------------------------------------------
1 | !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.LotteryCard=e()}(this,function(){"use strict";Object.assign=Object.assign||function(t){if(void 0===t||null===t)throw new TypeError("Cannot convert undefined or null to object");for(var e=Object(t),i=1;i1?i-1:0),o=1;o=this.options.percent&&(this.ctx.clearRect(0,0,this.width,this.height),this._state="end",this.trigger("end"))}},o.prototype.onResize=function(){this._touch=!1,this.options.resize&&"end"!==this._state?this.init():this.getCanvasInfo()},o}(n)});
2 |
--------------------------------------------------------------------------------
/src/card.js:
--------------------------------------------------------------------------------
1 | import './modules/assign'
2 | import Events from './modules/events'
3 |
4 | class LotteryCard extends Events {
5 | constructor (canvas, options) {
6 | super()
7 |
8 | this.options = Object.assign({
9 | size: 20, // 滑动区域大小
10 | percent: 50, // 激活百分比 到该值就显示结果
11 | resize: true, // canvas大小是否是可变的
12 | cover: null
13 | }, options)
14 |
15 | this.canvas = canvas
16 | this.ctx = canvas.getContext('2d')
17 |
18 | this._first = true
19 | this._touch = false
20 | this.init()
21 | this.bind()
22 | }
23 |
24 | getCanvasInfo () {
25 | const info = this.canvas.getBoundingClientRect()
26 | const scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0
27 | const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeftp || 0
28 |
29 | this.width = info.width
30 | this.height = info.height
31 | this.offsetX = Math.round(info.left + scrollLeft)
32 | this.offsetY = Math.round(info.top + scrollTop)
33 | this.canvas.width = info.width
34 | this.canvas.height = info.height
35 | }
36 |
37 | bind () {
38 | const SUPPORT_ONLY_TOUCH = ('ontouchstart' in window) && /mobile|tablet|ip(ad|hone|od)|android/i.test(navigator.userAgent) // 是否支持touch
39 |
40 | this.canvas.addEventListener(SUPPORT_ONLY_TOUCH ? 'touchstart' : 'mousedown', this.onTouchStart.bind(this), false)
41 | this.canvas.addEventListener(SUPPORT_ONLY_TOUCH ? 'touchmove' : 'mousemove', this.onTouchMove.bind(this), false)
42 | document.addEventListener(SUPPORT_ONLY_TOUCH ? 'touchend' : 'mouseup', this.onTouchEnd.bind(this))
43 | window.addEventListener('onorientationchange' in document ? 'orientationchange' : 'resize', this.onResize.bind(this))
44 | }
45 |
46 | init () {
47 | this._state = 'init'
48 | this.getCanvasInfo()
49 |
50 | // 绘制遮罩层
51 | this.ctx.closePath()
52 | this.ctx.globalCompositeOperation = 'source-over'
53 | const cover = this.options.cover
54 | if (cover instanceof Image) {
55 | this.ctx.fillStyle = this.ctx.createPattern(cover, 'repeat')
56 | this.ctx.rect(0, 0, this.width, this.height)
57 | } else {
58 | this.ctx.fillStyle = typeof cover === 'string' ? cover : 'gray'
59 | this.ctx.fillRect(0, 0, this.width, this.height)
60 | }
61 | this.ctx.fill()
62 | this.ctx.globalCompositeOperation = 'destination-out'
63 | }
64 |
65 | reset () {
66 | this._first = true
67 | this._touch = false
68 | this.canvas.style.backgroundImage = null
69 | this.init()
70 | this.trigger('reset')
71 | }
72 |
73 | setResult (url) {
74 | this.canvas.style.backgroundImage = 'url(' + url + ')' // 设置结果
75 | }
76 |
77 | draw () {
78 | if (this._state === 'end') return
79 | this._state = 'end'
80 | this.ctx.clearRect(0, 0, this.width, this.height)
81 | this.trigger('end')
82 | }
83 |
84 | scratchPercent () {
85 | const imageData = this.ctx.getImageData(0, 0, this.width, this.height)
86 | let hits = 0
87 |
88 | for (let i = 0, ii = imageData.data.length; i < ii; i = i + 4) {
89 | if (imageData.data[i] === 0 && imageData.data[i + 1] === 0 && imageData.data[i + 2] === 0 && imageData.data[i + 3] === 0) {
90 | hits++
91 | }
92 | }
93 |
94 | return (hits / (this.width * this.height)) * 100
95 | }
96 |
97 | getEventXY (e) {
98 | e = e.changedTouches ? e.changedTouches[0] : e
99 | return {
100 | x: e.pageX - this.offsetX,
101 | y: e.pageY - this.offsetY
102 | }
103 | }
104 |
105 | onTouchStart (e) {
106 | e.preventDefault()
107 | if (this._state === 'end') return
108 | if (this.has('start') && this._first) this.trigger('start')
109 |
110 | // 绘制起点
111 | const point = this.getEventXY(e)
112 | this._state = 'start'
113 | this._touch = true
114 | this._first = false
115 |
116 | this.ctx.beginPath()
117 | this.ctx.arc(point.x, point.y, this.options.size / 2, 0, Math.PI * 2, true)
118 | this.ctx.closePath()
119 | this.ctx.fill()
120 |
121 | this.ctx.beginPath()
122 | this.ctx.lineWidth = this.options.size
123 | this.ctx.moveTo(point.x, point.y)
124 | }
125 |
126 | onTouchMove (e) {
127 | e.preventDefault()
128 | if (!this._touch) return
129 | // 绘制路线
130 | const point = this.getEventXY(e)
131 | this.ctx.lineTo(point.x, point.y)
132 | this.ctx.stroke()
133 | }
134 |
135 | onTouchEnd (e) {
136 | if (!this._touch) return
137 | this._touch = false
138 |
139 | // 绘制终点
140 | const point = this.getEventXY(e)
141 | this.ctx.closePath()
142 | this.ctx.beginPath()
143 | this.ctx.arc(point.x, point.y, this.options.size / 2, 0, Math.PI * 2, true)
144 | this.ctx.closePath()
145 | this.ctx.fill()
146 |
147 | // 计算已经刮掉的面积
148 | if (this.scratchPercent() >= this.options.percent) {
149 | this.ctx.clearRect(0, 0, this.width, this.height)
150 | this._state = 'end'
151 | this.trigger('end')
152 | }
153 | }
154 |
155 | onResize () {
156 | this._touch = false
157 | if (this.options.resize) {
158 | if (this._state !== 'end') {
159 | this.init()
160 | } else {
161 | this.getCanvasInfo()
162 | }
163 | } else {
164 | this.getCanvasInfo()
165 | }
166 | }
167 | }
168 |
169 | export default LotteryCard
170 |
--------------------------------------------------------------------------------
/examples/css/style.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0;
3 | padding: 0
4 | }
5 |
6 | html {
7 | font-size: 20px;
8 | -webkit-text-size-adjust: 100%;
9 | -ms-text-size-adjust: 100%
10 | }
11 |
12 | html, body {
13 | width: 100%;
14 | height: 100%;
15 | -webkit-user-select: none;
16 | -ms-user-select: none;
17 | -moz-user-select: none;
18 | user-select: none
19 | }
20 |
21 | audio, canvas, progress, video {
22 | display: inline-block;
23 | vertical-align: baseline
24 | }
25 |
26 | img, video {
27 | max-width: 100%;
28 | height: auto;
29 | border: 0 none;
30 | vertical-align: bottom
31 | }
32 |
33 | li {
34 | list-style: none
35 | }
36 |
37 | svg:not(:root) {
38 | overflow: hidden
39 | }
40 |
41 | a {
42 | text-decoration: none
43 | }
44 |
45 | a, img, button, input[type='button'], input[type='submit'], input[type='reset'] {
46 | outline: none;
47 | -webkit-tap-highlight-color: rgba(255, 255, 255, 0)
48 | }
49 |
50 | input:-ms-clear {
51 | display: none
52 | }
53 |
54 | input[type='search']::-webkit-search-cancel-button {
55 | display: none
56 | }
57 |
58 | input, textarea {
59 | -webkit-user-modify: read-write-plaintext-only;
60 | -webkit-appearance: none;
61 | outline: none
62 | }
63 |
64 | button, input, textarea {
65 | border: 0 none
66 | }
67 |
68 | body, button, input, textarea, select, option, pre, optgroup {
69 | line-height: 1.5;
70 | font-family: "Microsoft YaHei", "微软雅黑", Helvetica, Arial;
71 | color: #333;
72 | font-size: 0.6rem
73 | }
74 |
75 | .f-cb:after, .f-cb li:after {
76 | content: '';
77 | display: table;
78 | clear: both
79 | }
80 |
81 | .f-wsn {
82 | word-wrap: normal;
83 | white-space: nowrap
84 | }
85 |
86 | .f-wwb {
87 | white-space: normal;
88 | word-wrap: break-word;
89 | word-break: break-all
90 | }
91 |
92 | .f-toe {
93 | overflow: hidden;
94 | word-wrap: normal;
95 | white-space: nowrap;
96 | text-overflow: ellipsis
97 | }
98 |
99 | .f-ma {
100 | margin-left: auto;
101 | margin-right: auto
102 | }
103 |
104 | .f-box {
105 | -webkit-box-sizing: border-box;
106 | -moz-box-sizing: border-box;
107 | box-sizing: border-box
108 | }
109 |
110 | .m-alert {
111 | position: fixed;
112 | top: 0;
113 | left: 0;
114 | z-index: 10000;
115 | width: 100%;
116 | height: 100%;
117 | background-color: transparent
118 | }
119 |
120 | .m-alert .text {
121 | width: 11rem;
122 | padding: 1.25rem 0.75rem;
123 | text-align: center;
124 | line-height: 1.4rem;
125 | font-size: 0.8rem;
126 | color: #fff;
127 | background-color: rgba(0, 0, 0, 0.7);
128 | white-space: normal;
129 | word-wrap: break-word;
130 | word-break: break-all;
131 | position: absolute;
132 | top: 50%;
133 | left: 50%;
134 | -webkit-transform: translate(-50%, -50%);
135 | transform: translate(-50%, -50%);
136 | border-radius: 0.25rem
137 | }
138 |
139 | .m-alert.fx-fadeIn, .m-alert.fx-fadeOut {
140 | -webkit-animation-duration: 0.3s;
141 | animation-duration: 0.3s;
142 | -webkit-animation-fill-mode: ease;
143 | animation-fill-mode: ease
144 | }
145 |
146 | [class*='fx-'] {
147 | -webkit-animation-duration: 1s;
148 | animation-duration: 1s;
149 | -webkit-animation-fill-mode: both;
150 | animation-fill-mode: both
151 | }
152 |
153 | .fx-bug {
154 | -webkit-transform-style: preserve-3d;
155 | transform-style: preserve-3d;
156 | -webkit-backface-visibility: visible;
157 | backface-visibility: visible
158 | }
159 |
160 | .fx-gpu, .m-lottery-tiger .game .item .roller {
161 | -webkit-transform: "translateZ(0)";
162 | transform: "translateZ(0)";
163 | -webkit-backface-visibility: hidden;
164 | backface-visibility: hidden;
165 | -webkit-perspective: 1000px;
166 | perspective: 1000px
167 | }
168 |
169 | .fx-infinite {
170 | -webkit-animation-iteration-count: infinite;
171 | animation-iteration-count: infinite
172 | }
173 |
174 | .fx-fadeIn {
175 | -webkit-animation-name: fadeIn;
176 | animation-name: fadeIn
177 | }
178 |
179 | @-webkit-keyframes fadeIn {
180 | 0% {
181 | opacity: 0
182 | }
183 | 100% {
184 | opacity: 1
185 | }
186 | }
187 |
188 | @keyframes fadeIn {
189 | 0% {
190 | opacity: 0
191 | }
192 | 100% {
193 | opacity: 1
194 | }
195 | }
196 |
197 | .fx-fadeOut {
198 | -webkit-animation-name: fadeOut;
199 | animation-name: fadeOut
200 | }
201 |
202 | @-webkit-keyframes fadeOut {
203 | 0% {
204 | opacity: 1
205 | }
206 | 100% {
207 | opacity: 0
208 | }
209 | }
210 |
211 | @keyframes fadeOut {
212 | 0% {
213 | opacity: 1
214 | }
215 | 100% {
216 | opacity: 0
217 | }
218 | }
219 |
220 | @media screen and (min-width: 320px) {
221 | html {
222 | font-size: 20px
223 | }
224 | }
225 |
226 | @media screen and (min-width: 360px) {
227 | html {
228 | font-size: 22.5px
229 | }
230 | }
231 |
232 | @media screen and (min-width: 375px) {
233 | html {
234 | font-size: 23.4375px
235 | }
236 | }
237 |
238 | @media screen and (min-width: 384px) {
239 | html {
240 | font-size: 24px
241 | }
242 | }
243 |
244 | @media screen and (min-width: 400px) {
245 | html {
246 | font-size: 25px
247 | }
248 | }
249 |
250 | @media screen and (min-width: 412px) {
251 | html {
252 | font-size: 25.75px
253 | }
254 | }
255 |
256 | @media screen and (min-width: 414px) {
257 | html {
258 | font-size: 25.875px
259 | }
260 | }
261 |
262 | @media screen and (min-width: 533px) {
263 | html {
264 | font-size: 33.3125px
265 | }
266 | }
267 |
268 | @media screen and (min-width: 600px) {
269 | html {
270 | font-size: 37.5px
271 | }
272 | }
273 |
274 | @media screen and (min-width: 768px) {
275 | html {
276 | font-size: 48px
277 | }
278 | }
279 |
280 | .m-ui-dial {
281 | position: relative;
282 | width: 499px;
283 | height: 499px;
284 | margin: 0 auto;
285 | background: url("../images/dial_bg.png") no-repeat center;
286 | background-size: 100%
287 | }
288 |
289 | .m-ui-dial .pointer {
290 | margin-top: -139px !important;
291 | background: url("../images/dial_pointer.png") no-repeat center;
292 | background-size: 100%;
293 | position: absolute;
294 | top: 50%;
295 | left: 50%;
296 | display: block;
297 | width: 150px;
298 | height: 238px;
299 | margin: -119px 0 0 -75px;
300 | -webkit-transform-origin: 75px 139px;
301 | transform-origin: 75px 139px
302 | }
303 |
304 | .m-ui-dial .btn {
305 | position: absolute;
306 | top: 64px;
307 | left: 0;
308 | display: block;
309 | width: 150px;
310 | height: 150px;
311 | border-radius: 75px
312 | }
313 |
314 | .m-ui-scratch {
315 | display: block;
316 | width: 300px;
317 | height: 172.5px;
318 | margin: 20px auto;
319 | background: no-repeat center;
320 | background-size: 100%
321 | }
322 |
323 | .m-ui-button {
324 | display: block;
325 | width: 160px;
326 | height: 32px;
327 | margin: 0 auto;
328 | text-align: center;
329 | line-height: 32px;
330 | font-size: 12px;
331 | color: #fff;
332 | background-color: #f94804;
333 | cursor: pointer;
334 | border-radius: 5px
335 | }
336 |
337 | .m-ui-tiger {
338 | position: relative;
339 | width: 640px;
340 | height: 432px;
341 | margin: 20px auto;
342 | background: url("../images/tiger_bg.png") no-repeat
343 | }
344 |
345 | .m-ui-tiger .toggle {
346 | position: absolute;
347 | top: 306px;
348 | left: 119px;
349 | display: block;
350 | width: 404px;
351 | height: 50px
352 | }
353 |
354 | .m-ui-tiger .item {
355 | position: absolute;
356 | top: 77px;
357 | left: 139px;
358 | display: block;
359 | width: 110px;
360 | height: 135px;
361 | overflow: hidden
362 | }
363 |
364 | .m-ui-tiger .item:nth-child(2) {
365 | left: 265px
366 | }
367 |
368 | .m-ui-tiger .item:nth-child(3) {
369 | left: 391px
370 | }
371 |
372 | .m-ui-tiger .roller {
373 | position: relative
374 | }
375 |
376 | .m-ui-tiger .roller li {
377 | width: 110px;
378 | height: 135px;
379 | overflow: hidden
380 | }
381 |
382 | .m-ui-tiger .roller li:last-child {
383 | position: absolute;
384 | top: 100%;
385 | left: 0
386 | }
387 |
388 | .m-ui-tiger .roller img {
389 | display: block;
390 | width: 110px;
391 | height: 135px
392 | }
393 |
394 | .m-ui-tiger .roller.fx-roll {
395 | -webkit-filter: blur(3px);
396 | filter: blur(3px);
397 | -webkit-animation: fx-roll 0.5s 0s infinite linear;
398 | animation: fx-roll 0.5s 0s infinite linear
399 | }
400 |
401 | .m-ui-tiger .roller.fx-bounce {
402 | -webkit-animation-duration: 0.3s;
403 | animation-duration: 0.3s
404 | }
405 |
406 | .m-lottery-scratch {
407 | position: relative;
408 | width: 16rem;
409 | min-height: 100%;
410 | margin: 0 auto;
411 | padding-top: 9rem;
412 | background: url("../images/scratch_page_bg.jpg") no-repeat #ff5e01;
413 | background-size: 100% auto
414 | }
415 |
416 | .m-lottery-scratch .game {
417 | position: relative;
418 | display: block;
419 | width: 10rem;
420 | margin: 0 auto;
421 | height: 5.75rem
422 | }
423 |
424 | .m-lottery-scratch .game canvas {
425 | position: absolute;
426 | top: 0;
427 | left: 0;
428 | display: block;
429 | width: 100%;
430 | height: 100%;
431 | background-size: 100% 100%
432 | }
433 |
434 | .m-lottery-scratch .game button {
435 | display: block;
436 | width: 5rem;
437 | height: 1.5rem;
438 | margin: 0 auto;
439 | text-align: center;
440 | line-height: 1.4rem;
441 | font-size: 0.7rem;
442 | color: #fff;
443 | background-color: #ff5e01;
444 | border: 0 none;
445 | cursor: pointer;
446 | border-radius: 0.3rem
447 | }
448 |
449 | .m-lottery-scratch .game button.z-dis {
450 | background-color: #bcbcbc
451 | }
452 |
453 | .m-lottery-scratch .game .ceiling {
454 | position: absolute;
455 | top: 0;
456 | left: 0;
457 | z-index: 3;
458 | width: 100%;
459 | height: 4rem;
460 | padding: 0.5rem 0 1.25rem;
461 | background: url("../images/scratch_ceiling.jpg") no-repeat;
462 | background-size: 100% 100%;
463 | overflow: hidden
464 | }
465 |
466 | .m-lottery-scratch .game .ceiling .tip {
467 | display: block;
468 | height: 2.5rem;
469 | line-height: 2.5rem;
470 | text-align: center;
471 | font-size: 0.8rem;
472 | color: #ff5e01;
473 | overflow: hidden
474 | }
475 |
476 | .m-lottery-scratch .des {
477 | margin-top: 2.35rem;
478 | padding: 0 1.1rem 0.5rem;
479 | line-height: 1.15rem;
480 | font-size: 0.6rem;
481 | color: #fff
482 | }
483 |
484 | .m-lottery-scratch .des dt {
485 | line-height: 1.2rem;
486 | font-size: 0.7rem;
487 | font-weight: 700
488 | }
489 |
490 | .m-lottery-dial {
491 | position: relative;
492 | width: 16rem;
493 | min-height: 100%;
494 | margin: 0 auto;
495 | padding-top: 7rem;
496 | background: url("../images/dial_page_bg.jpg") no-repeat #f94804;
497 | background-size: 100% auto
498 | }
499 |
500 | .m-lottery-dial .game-dial {
501 | position: relative;
502 | width: 12.5rem;
503 | height: 12.5rem;
504 | margin: 0 auto
505 | }
506 |
507 | .m-lottery-dial .game-dial .dial {
508 | width: 100%;
509 | height: 100%;
510 | background-size: 100% auto
511 | }
512 |
513 | .m-lottery-dial .game-dial .pointer {
514 | position: absolute;
515 | top: 50%;
516 | left: 50%;
517 | width: 3.75rem;
518 | height: 5.95rem;
519 | margin: -3.4rem 0 0 -1.875rem;
520 | background-size: 100% auto;
521 | -webkit-transform-origin: 1.875rem 3.4rem;
522 | transform-origin: 1.875rem 3.4rem
523 | }
524 |
525 | .m-lottery-dial .game-dial .pointer span {
526 | position: absolute;
527 | top: 1.6rem;
528 | left: 0;
529 | display: block;
530 | width: 3.75rem;
531 | height: 3.75rem;
532 | cursor: pointer
533 | }
534 |
535 | .m-lottery-dial .des {
536 | margin-top: 4.2rem;
537 | padding: 0 1.1rem 0.5rem;
538 | line-height: 1.15rem;
539 | font-size: 0.6rem;
540 | color: #fff
541 | }
542 |
543 | .m-lottery-dial .des dt {
544 | line-height: 1.2rem;
545 | font-size: 0.7rem;
546 | font-weight: 700
547 | }
548 |
549 | .m-lottery-tiger {
550 | position: relative;
551 | width: 16rem;
552 | min-height: 100%;
553 | margin: 0 auto;
554 | padding-top: 5.5rem;
555 | background: url("../images/tiger_page_bg.jpg") no-repeat #ffeec6;
556 | background-size: 100% auto
557 | }
558 |
559 | .m-lottery-tiger .game {
560 | position: relative
561 | }
562 |
563 | .m-lottery-tiger .game .back {
564 | width: 16rem;
565 | height: 10.8rem;
566 | background-size: 100%;
567 | background-repeat: no-repeat
568 | }
569 |
570 | .m-lottery-tiger .game .item {
571 | position: absolute;
572 | top: 1.925rem;
573 | width: 2.75rem;
574 | height: 3.375rem;
575 | overflow: hidden
576 | }
577 |
578 | .m-lottery-tiger .game .item:nth-child(1) {
579 | left: 3.475rem
580 | }
581 |
582 | .m-lottery-tiger .game .item:nth-child(2) {
583 | left: 6.625rem
584 | }
585 |
586 | .m-lottery-tiger .game .item:nth-child(3) {
587 | left: 9.775rem
588 | }
589 |
590 | .m-lottery-tiger .game .item .roller {
591 | position: relative
592 | }
593 |
594 | .m-lottery-tiger .game .item .roller li.z-last {
595 | position: absolute;
596 | top: 100%;
597 | left: 0
598 | }
599 |
600 | .m-lottery-tiger .game .item .roller.fx-bounce {
601 | -webkit-animation-duration: 0.3s;
602 | animation-duration: 0.3s
603 | }
604 |
605 | .m-lottery-tiger .game .item .roller.fx-roll {
606 | -webkit-filter: blur(3px);
607 | filter: blur(3px);
608 | -webkit-animation: fx-roll 0.3s 0s infinite linear;
609 | animation: fx-roll 0.3s 0s infinite linear
610 | }
611 |
612 | .m-lottery-tiger .game .item img {
613 | display: block;
614 | width: 2.75rem;
615 | height: 3.375rem
616 | }
617 |
618 | .m-lottery-tiger .game .btn {
619 | position: absolute;
620 | left: 50%;
621 | top: 7.65rem;
622 | width: 10.1rem;
623 | height: 1.25rem;
624 | margin-left: -5.05rem;
625 | background-size: 100%;
626 | background-repeat: no-repeat
627 | }
628 |
629 | .m-lottery-tiger .game .btn span {
630 | display: block;
631 | width: 100%;
632 | height: 100%;
633 | cursor: pointer
634 | }
635 |
636 | .m-lottery-tiger .des {
637 | padding: 0 1.1rem 0.5rem;
638 | line-height: 1.15rem;
639 | font-size: 0.6rem;
640 | color: #b26722
641 | }
642 |
643 | .m-lottery-tiger .des dt {
644 | line-height: 1.2rem;
645 | font-size: 0.7rem;
646 | font-weight: 700
647 | }
648 |
649 | @-webkit-keyframes fx-roll {
650 | 0% {
651 | -webkit-transform: translate3d(0, 0, 0);
652 | transform: translate3d(0, 0, 0)
653 | }
654 | 100% {
655 | -webkit-transform: translate3d(0, -100%, 0);
656 | transform: translate3d(0, -100%, 0)
657 | }
658 | }
659 |
660 | @keyframes fx-roll {
661 | 0% {
662 | -webkit-transform: translate3d(0, 0, 0);
663 | transform: translate3d(0, 0, 0)
664 | }
665 | 100% {
666 | -webkit-transform: translate3d(0, -100%, 0);
667 | transform: translate3d(0, -100%, 0)
668 | }
669 | }
670 |
671 | .fx-bounce {
672 | -webkit-animation-name: bounce;
673 | animation-name: bounce;
674 | -webkit-transform-origin: center bottom;
675 | transform-origin: center bottom
676 | }
677 |
678 | @-webkit-keyframes bounce {
679 | 0%, 20%, 53%, 80%, 100% {
680 | -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
681 | animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
682 | -webkit-transform: translate3d(0, 0, 0);
683 | transform: translate3d(0, 0, 0)
684 | }
685 | 40%, 43% {
686 | -webkit-animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
687 | animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
688 | -webkit-transform: translate3d(0, -30px, 0);
689 | transform: translate3d(0, -30px, 0)
690 | }
691 | 70% {
692 | -webkit-animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
693 | animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
694 | -webkit-transform: translate3d(0, -15px, 0);
695 | transform: translate3d(0, -15px, 0)
696 | }
697 | 90% {
698 | -webkit-transform: translate3d(0, -4px, 0);
699 | transform: translate3d(0, -4px, 0)
700 | }
701 | }
702 |
703 | @keyframes bounce {
704 | 0%, 20%, 53%, 80%, 100% {
705 | -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
706 | animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
707 | -webkit-transform: translate3d(0, 0, 0);
708 | transform: translate3d(0, 0, 0)
709 | }
710 | 40%, 43% {
711 | -webkit-animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
712 | animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
713 | -webkit-transform: translate3d(0, -30px, 0);
714 | transform: translate3d(0, -30px, 0)
715 | }
716 | 70% {
717 | -webkit-animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
718 | animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
719 | -webkit-transform: translate3d(0, -15px, 0);
720 | transform: translate3d(0, -15px, 0)
721 | }
722 | 90% {
723 | -webkit-transform: translate3d(0, -4px, 0);
724 | transform: translate3d(0, -4px, 0)
725 | }
726 | }
727 |
--------------------------------------------------------------------------------