整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

用html和js做的抽奖效果仅仅是一个效果哦

单的html和js做的抽奖效果,自己也做过抽奖程序,用作年会上面的抽奖,效果不错,赛光时代小编会后续的分享出来供大家使用得。近些年互联网飞速发展,周边的产业不断的完善微信、手机等等都需要我们企业不断的扩展和适应。

抽奖效果

<!doctype html>

<html>

<head>

<meta charset="utf-8">

<title>抽奖程序-saiguangw.com</title>

<script src="http://libs.baidu.com/jquery/1.11.1/jquery.min.js"></script>

<style>

* {

margin:0;

padding:0;

}

a {

text-decoration:none;

}

img {

border:none;

}

li {

list-style:none outside none;

}

body {

background:#c9c9c9;

font-size:14px;

font-family:"宋体";

}

.myBox {

margin:50px auto 0;

}

.myBox ul {

margin:0 auto 0;

position:relative;

width:500px;

height:100px;

overflow:hidden;

}

.myBox li {

width:100px;

height:100px;

text-align:center;

line-height:100px;

font-size:40px;

color:#fff;

background:rgba(222,122,155,0.5);

}

.myBox li.on {

background:rgba(66,56,222,0.5);

}

.text {

height:50px;

overflow:hidden;

width:500px;

margin:20px auto 0;

font-size:20px;

line-height:50px;

text-align:center;

}

.bt,.jg,.zt {

float:left;

width:200px;

}

.bt {

width:100px;

height:50px;

background:rgb(200,100,55);

color:#fff;

cursor:pointer;

}

em {

font-size:30px;

font-style:normal;

color:red;

}

</style>

</head>

<body>

<div class="myBox">

<ul class="cj2">

<li>1</li>

</ul>

<div class="text">

<div class="bt bt2">点我抽奖</div>

<div class="jg jg2">中奖者为"<em></em>"号</div>

</div>

</div><script>

//by zyp

;(function($, window, document, undefined) {

var LuckDraw = function(ele, opt) {

this.$element = ele,

this.defaults = {

row: 4, //行

column: 4, //列

spacing: 0,

click: null,

time: 3,

end: function(e) {}

},

this.target,

this.options = $.extend({}, this.defaults, opt);

}

LuckDraw.prototype = {

init: function() {

var $this = this.$element;

var row = this.options.row;

var col = this.options.column;

var spacing = this.options.spacing;

var click = this.options.click;

var allNumber = 2 * (row + col) - 4;

var line = row - 2; //除去上下de行数

var length = $this.children('li').length;

var options = this.options;

if (length < allNumber) {

for (var i = length; i <= (allNumber - length); i++) {

$this.append("<li>" + (i + 1) + "</li>");

}

}

var children = $this.children('li');

var width = children.eq(0).width() || 0;

var height = children.eq(0).height() || 0;

//元素初始化

$this.css({

position: 'relative',

width: col * width + (col - 1) * spacing,

height: row * height + (row - 1) * spacing

});

children.css({

position: 'absolute'

});

if (line == 0) {

initOne();

} else {

initTwo();

}

//初始化函数

//此时分成4个部分,上、右、下、左

//上: 0 ~ col-1

//右: col ~ col+line

//下: col+line+1 ~ 2*col+line-1

//左: else

//如果只有两行

//此时分成4个部分,上、右、下、左

function initOne() {

children.each(function(index) {

if (index >= 0 && index <= (col - 1)) {

$(this).css({

top: 0,

left: index * width + index * spacing

});

} else {

$(this).css({

bottom: 0,

right: index % col * width

});

}

});

}

//如果大于两行

function initTwo() {

children.each(function(index) {

if (index >= 0 && index <= (col - 1)) {

$(this).css({

top: 0,

left: index * width + index * spacing

});

} else if (index >= col && index <= (col + line - 1)) {

$(this).css({

top: ((index + 1 - col)) * (height + spacing),

right: 0

});

} else if (index >= (col + line) && index <= (2 * col + line - 1)) {

$(this).css({

bottom: 0,

right: (index - ((col + line))) * (width + spacing)

});

} else {

$(this).css({

left: 0,

bottom: (index - (2 * col + line - 1)) * (height + spacing)

});

}

});

}

var target = $this.target || Math.floor(Math.random() * allNumber + 1); //目标,指定或随机

var ix = 0; //位置

var stop;

var flg = false; //抽奖是否正在运行

/*

加速度公式

v1 = v0 + a*t;注意加速度的v代表时间

此时的t可以我们自己定义,所以是常量,所以只需要求a的值

*/

var a = -25.0;

var v0 = 500.0;

var t = 0.0,

v;

var time = this.options.time * 1000; //匀速运行的时间,单位秒

$(click).on('click', function() {

if (!flg) {

flg = true;

target = $this.target || Math.floor(Math.random() * allNumber + 1);

speedUp();

} else {

return;

}

});

//加速

function speedUp() {

runner(ix);

if (v <= 50) {

clearTimeout(stop);

v = 50;

t = 0.0;

uniform(); //跳转到匀速

} else {

t++;

v = v0 + a * t;

stop = setTimeout(speedUp, v);

}

}

//匀速

function uniform() {

stop = setTimeout(uniform, v);

if (t == time / 50) {

clearTimeout(stop);

t = 0.0;

speedDown();

} else {

t++;

}

runner(ix);

}

//减速

function speedDown() {

var stop3 = setTimeout(speedDown, v);

if (v >= 500) {

v = 500;

if (ix == target - 1) {

clearTimeout(stop3);

options.end(target);

flg = false;

}

} else {

t++;

v = v - a * t;

}

runner(ix);

}

//ix++

function runner(i) {

children.removeClass('on').eq(ix).addClass('on');

i++;

if (i == allNumber) {

ix = 0;

} else {

ix = i;

}

}

},

setTarget: function(options) {

var $this = this.$element;

$this.target = options;

}

}

$.fn.myLuckDraw = function(options, target) {

var Ld = new LuckDraw(this, options);

Ld.setTarget(target);

Ld.init();

return this;

}

})(jQuery, window, document);

$(function() {

var tar = 5;

$('.cj2').myLuckDraw({

row: 3, //行

column: 5, //列

spacing: 5, //空隙

click: '.bt2', //点击触发

time: 3, //匀速运动的时间

end: function(e) {

//抽奖执行完毕的回调函数,参数e为获奖编号

//因为这里是指定的,所以e == 12

$('.jg2 em').text(e);

}

});

});</script>

</body>

</html>

.0版本的时候没有前端页面,会使这个系统看起来很简陋,但由于自己前端水平实在有限,所以在github上找了一个抽奖的前端,直接套用一下。抽奖也改用前端实现,只有抽奖名单和中间名单使用了后台。

跨域处理

引入前端界面的同时,引入了另一个问题:跨域!

什么是跨域呢?

出于浏览器的同源策略限制。同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)

简单来说就是当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域。

origins = [
    "*",
    "http://localhost",
    "http://localhost:8080",
]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)


新增数据上传接口

新增数据model,中奖描述,还有中奖名单,因为没有其他业务逻辑,这里就直接通过pymysql 插入数据,不做其他的处理了



class Info(BaseModel):
    description: str
    name: str


@app.post("/push/lottery")
async def push_lottery(info: Info):


    db = pymysql.connect(host='localhost', port=3306, user='root', password='root', database='test_data_platform',
                         charset='utf8')
    cursor = db.cursor()
    sql = "INSERT INTO user_info(name,description)values('%s','%s')" % (info.description, info.name.rstrip(','))
    try:
        cursor.execute(sql)
        db.commit()
    except Exception as e:
        db.rollback()
        print("push error:"+e)
    db.close()
    return {"message": "push success"}

前端页面

github代码地址,上面有这个动态效果的源代码https://github.com/fouber/lottery ,将其中的部分代码替换为请求后台数据

member.js

window.member;
$(document).ready(function(){
  $.get("http://127.0.0.1:8000/lottery/employees",function(data,status){
    localStorage.setItem('member', JSON.stringify(data.data));
  });

});

前端显示界面

<!DOCTYPE html>
<html>
<head>
    <meta name="screen-orientation" content="portrait">
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=0"/>
    <title>年会抽奖小程序</title>
    <link rel="stylesheet" type="text/css" href="css/reset.css">
    <link rel="stylesheet" type="text/css" href="css/wall.css">
   
</head>
<body>
<div id="main" class="wall">
    <div class="result-btn">
       <a href="./result.html" target="_blank">获奖名单</a>
    </div>
</div>
<div id="result" class="result">
</div>

<div id="tools" class="tools">
       <form-item label="抽奖人数">
        <input type="textarea" v-model="btns"></input>
      </form-item>
    <button
       class="pure-button"
       @click="toggle"
       :class="{'button-secondary': !running,
               'button-success': running}">{{running?'停!':'开始'}}</button>
    <button class="pure-button button-warning" @click="reset">重置</button>
</div>
<script type="text/javascript" src="js/zepto.js"></script>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript" src="js/tagcanvas.js"></script>
<script type="text/javascript" src="js/member.js"></script>
<script type="text/javascript">
    (function(){
        var employees = JSON.parse(localStorage.getItem('member'))
        var choosed = JSON.parse(localStorage.getItem('choosed')) || {};
        // 转动效果
        var speed = function(){
            return [0.1 * Math.random() + 0.01, -(0.1 * Math.random() + 0.01)];
        };
        var getKey = function(item){
            return item.name + '-' + item.phone;
        };
        var createHTML = function(){
            var html = [ '<ul>' ];
                employees.forEach(function(item, index){
                item.index = index;
                var key = getKey(item);
                var color = choosed[key] ? 'yellow' : 'white';
                html.push('<li><a href="#" style="color: ' + color + ';">' + item.name + '</a></li>');
            });
            html.push('</ul>');
            return html.join('');
        };
        var lottery = function(count){
            var list = canvas.getElementsByTagName('a');
            var color = 'yellow';
            var names = "";
            var ret = employees
                .filter(function(m, index){
                    m.index = index;
                    return !choosed[getKey(m)];
                })
                .map(function(m){
                    return Object.assign({
                      score: Math.random()
                    }, m);
                })
                .sort(function(a, b){
                    return a.score - b.score;
                })
                .slice(0, count)
                .map(function(m){
                  choosed[getKey(m)] = 1;
                  list[m.index].style.color = color;
                  names = m.name +","+names
                  return m.name + '<br/>'; 
                });
            localStorage.setItem('choosed', JSON.stringify(choosed));
            var info = {"name":names,"description":"一等奖"}
            $.ajax({
                url:'http://127.0.0.1:8000/push/lottery',
                type:'post',
                dateType:'json',
                headers:{
                    'Content-Type':'application/json'
                },
                data:JSON.stringify(info)
            })
            return ret;
        };
        var canvas = document.createElement('canvas');
        canvas.id = 'myCanvas';
        canvas.width = document.body.offsetWidth;
        canvas.height = document.body.offsetHeight;
        document.getElementById('main').appendChild(canvas);
        new Vue({
            el: '#tools',
            data: {
                selected: 30,
                running: false,
                btns: ''
            },
            mounted () {
                canvas.innerHTML = createHTML();
                TagCanvas.Start('myCanvas', '', {
                    textColour: null,
                    initial: speed(),
                    dragControl: 1,
                    textHeight: 14
                });
            },
            methods: {
                reset: function(){
                    if(confirm('确定要重置么?所有之前的抽奖历史将被清除!')){
                        localStorage.clear();
                        location.reload(true);
                    }
                },

                toggle: function(){
                    if(this.running){
                        TagCanvas.SetSpeed('myCanvas', speed());
                        var ret = lottery(this.btns);
                        if (ret.length === 0) {
                            $('#result').css('display', 'block').html('<span>已抽完</span>');
                            return
                        }
                        $('#result').css('display', 'block').html('<span>' + ret.join('</span><span>') + '</span>');
                        TagCanvas.Reload('myCanvas');
                        setTimeout(function(){
                            localStorage.setItem(new Date().toString(), JSON.stringify(ret));
                            $('#main').addClass('mask');
                        }, 300);
                    } else {
                        $('#result').css('display', 'none');
                        $('#main').removeClass('mask');
                        TagCanvas.SetSpeed('myCanvas', [5, 1]);
                    }
                    this.running = !this.running;
                }
            }
        });
    })();
</script>
</body>
</html>

抽奖效果

抽奖效果

结果记录

上传抽奖记录到数据库

前后端所有代码已上传 https://github.com/627886474/lottery

套系统开发没有任何难度,唯一需要注意高并发下性能和数据问题,静态资源放到cdn,避免带宽成为瓶颈,把mysql操作变成redis操作,解决io问题。

需求出现

年会将近,而年会抽奖环节必不可少,但是抽奖系统却还没有。所以某一天,PM走过来说:小伙,手头的需求修完成了吧!在年会开始之前必须做出一个抽奖系统。这个系统很简单,后台可以设置总金额,然后每个用户可以获得的金额范围,金额派完则显示很遗憾没有中奖,还要设置抽奖活动时间。

需求分析

一看这东西,就觉得非常简单。最简单的一个方案,活动时间放在一个数据表,总金额和已经使用金额存放在一个表,已经派送的日志一个表。后台提供一个接口,客户端手动点击按钮,则发送一个请求。账号体系直接使用微信的oauth,接口首先判断活动有没有开始,如果开始则随机一个金额,然后判断如果派送该金额会不会超预算,如果不超预算,则调用微信的现金接口发放零钱。

并发问题

这个简单方案存在一个致命的问题,就是并发下,可能导致超预算的问题。如果采用加锁的方式,面对1000多员工同时请求,系统100%瘫痪。(因为抽奖系统的服务器是最普通的1核1G 1M带宽的服务器)


那么不加锁的情况,又能如何避免并发造成的派送超过预算的问题呢? 一个简单的办法,把分配派送金额的操作从并行变成串行。那么就需要异步的编程方法。最简单的处理方法,把任务写入mysql,然后启动一个独立的进程来一个任务一个任务的串行处理。异步的话,客户端如何知道服务器已经处理了呢?最简单就是采用轮询的方法了,客户端每隔几秒就请求服务器一次。

性能问题

由于抽奖是短时间大量用户请求的,如果直接让请求落到mysql,类似DDOS攻击,一般的数据库是扛不住的。而redis是1种基于内存的高并发NoSQL,在很多公司广泛使用,由于其性能非常好,并且其丰富的数据接口完全可以胜任抽奖任务需求。 这个时候,你可能有这样的疑问,我们的系统设计是怎么样的呢?
抽奖系统相关配置存储在redis的一个key值,直接使用json格式
客户端请求的时候判断,时间是否在活动时间范围内
客户端请求如果时间在活动范围内,则把用户添加到一个redis集合,用于防止用户重复请求,只有第一次请求才会添加到集合后,再添加到一个redis列表。
后台一个独立的进程,从redis列表pop第一位用户,然后分配一个金额,然后把金额和用户信息压入另一个redis列表B,同时写入redis的hash结构,标示用户获得多少现金。一直循环该过程。
后台另一个独立的进程,从redis列表B pop第一位用户,然后调用发送现金接口,一直循环该过程。
客户端不停轮询获取用户金额的接口,该接口从哪个hash结构获取用户金额,然后没有数据,则告诉客户端若干秒后再次请求。

前端优化

由于参与活动的人数较多,而且服务器是放在外网的,所以需要考虑带宽的问题。
第一步,把静态资源放到cdn。
第二步,抽奖页面静态化,同时也放到cdn,这样子服务器只需要承受用户请求和登录即可。
第三步,由于采用了微信登录,所以登录系统采用一个独立的进程,并且使用异步框架来处理高并发。
第四步,前端发送请求队列化处理,避免用户不停点击,造成大量请求。


本文转自:https://www.linuxprobe.com/develop-company.html