吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 627|回复: 8
上一主题 下一主题
收起左侧

[其他转载] 幸运抽奖系统:HTML+JS+CSS实现的3D抽奖程序

[复制链接]
跳转到指定楼层
楼主
xuxin 发表于 2026-9-15 22:18 回帖奖励
纯HTML+JS+CSS实现的抽奖系统,非常好看的抽奖程序,有3D动态效果
使用说明:
1.在js/users.js文件中编辑人员名单(可使用记事本打开编辑)
2.直接打开index.html文件使用,或部署在服务器上在线访问使用

部分代码展示
[HTML] 纯文本查看 复制代码
<!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/index.css">
    <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/users.js"></script>
</head>
<body>
    <div id="main" class="wall">
        <!-- 中奖名单 -->
        <div class="luck-user" id="luck-user">
            <div class="luck-user-title">
                <span>中奖名单</span>
            </div>
            <ul class="luck-user-list">
                <li v-for="item in results">
                    <div class="luckuserName">
                        <span class="uname">{{item[0]}}</span>
                        <span class="phone">{{item[1]}}</span>
                    </div>
                </li>
            </ul>
            <div class="luck-user-btn">
                <a href="#">中奖人</a>
            </div>
        </div>
        <!-- 开始按钮 -->
        <div id="tools" class="tools">
            <button v-for="value in btns" @click="onClick(value)" class="pure-button"
                :class="{ 'button-error': selected == value}">{{value}}</button>
            <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>
    </div>
    <div id="result" class="result"></div>
    <button id="continue" class="continue">继续抽奖</button>
</body>

<script type="text/javascript">
    // 画布
    var canvas = document.createElement('canvas');
    canvas.id = 'myCanvas';
    canvas.width = document.body.offsetWidth;
    canvas.height = document.body.offsetHeight;
    document.getElementById('main').appendChild(canvas);

    // 取本地数据
    var choosed = JSON.parse(localStorage.getItem('choosed')) || {};
    // console.log(choosed);

    // 处理函数
    function speed() {
        return [0.1 * Math.random() + 0.01, -(0.1 * Math.random() + 0.01)];
    }
    function getKey(item) {
        return item.name + '-' + item.phone;
    }
    function createHTML() {
        var html = ['<ul>'];
        member.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 + ';display: block; width: 100px; height: 100px; background: red;">' + item.name + '</a></li>');
        });
        html.push('</ul>');
        return html.join('');
    }
    function lottery(count) {
        var list = canvas.getElementsByTagName('a');
        var color = 'yellow';
        var ret = member
            .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;
                return m.name + '<br>' + m.phone; // m.name + ' ' + m.phone + " " + m.avatar;
            });
        localStorage.setItem('choosed', JSON.stringify(choosed));
        return ret;
    }

    // 功能
    new Vue({
        el: '#tools',
        data: {
            selected: 1,
            running: false,
            btns: [
                1, 2, 5, 10, 30
            ]
        },
        mounted() {
            canvas.innerHTML = createHTML();
            TagCanvas.Start('myCanvas', '', {
                textColour: null,
                initial: speed(),
                dragControl: 1,
                textHeight: 14
            });
        },
        methods: {
            reset() {
                if (confirm('确定要重置么?所有之前的抽奖历史将被清除!')) {
                    localStorage.clear();
                    location.reload(true);
                }
            },
            onClick(num) {
                $('#result').css('display', 'none');
                $('#continue').css('display', 'none');
                $('#main').removeClass('mask');
                this.selected = num;
            },
            // 弹出中奖名单
            toggle() {
                if (this.running) {
                    TagCanvas.SetSpeed('myCanvas', speed());
                    var ret = lottery(this.selected);
                    if (ret.length === 0) {
                        $('#result').css('display', 'block').html('<span>已抽完</span>');
                        return
                    }
                    // 中奖人信息
                    $('#result').css('display', 'block').html('<span>' + ret.join('</span><span>') + '</span>');
                    $('#continue').css('display', 'block');
                    TagCanvas.Reload('myCanvas');
                    setTimeout(function () {
                        localStorage.setItem(new Date().toString(), JSON.stringify(ret));
                        $('#main').addClass('mask');
                    }, 300);
                } else {
                    $('#result').css('display', 'none');
                    $('#continue').css('display', 'none');
                    $('#main').removeClass('mask');
                    TagCanvas.SetSpeed('myCanvas', [5, 1]);
                }
                this.running = !this.running;
            }
        }
    });
    // 中奖名单
    new Vue({
        el: '#luck-user',
        data: {
            results: [],
        },
        mounted() {
            let str_results = []
            for (let i = 0; i < localStorage.length; i++) {
                if (localStorage.key(i) !== 'choosed') {
                    str_results.push(localStorage.getItem(localStorage.key(i)))
                }
            }

            let results = []
            for (let nameList of str_results) {
                let temp = JSON.parse(nameList)
                temp = temp.map(item => { return item.replace('<br/>', ' ') })
                results.push(temp)
            }

            let arr = []
            for (let i = 0; i < results.length; i++) {
                arr.push(...results[i])
            }
            this.results = arr.map(item => { return item.split('<br>') })
        },
    });
    // 点击事件(继续抽奖)
    $("#continue").click(function () {
        $('#continue').css('display', 'none');
        $('#main').removeClass('mask');
        $('#result').css('display', 'none');
        location.reload(true);
    })
</script>
</html>






下载地址:https://wwatq.lanzouq.com/ibcyZ48m0dib

免费评分

参与人数 2吾爱币 +2 热心值 +1 收起 理由
木子方 + 1 谢谢@Thanks!
CGR + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

推荐
Mzhang2008 发表于 2026-9-16 08:01
反馈一下,抽中后,点击继续抽奖,中奖人未记录到名单中,另,未开始抽奖时,若能将动态球做成,鼠标操作3D方向动作,更有趣味性。方便看姓名...
沙发
tianyagd 发表于 2026-9-16 00:21
3#
snrt 发表于 2026-9-16 01:19
4#
jiao250 发表于 2026-9-16 05:50
能不能设置某人中奖!
5#
manman1226 发表于 2026-9-16 05:59
眼前一亮,非常漂亮
7#
LCG999 发表于 2026-9-16 08:14
中奖名单设置上好像不太友好。
8#
wlc0528 发表于 2026-9-16 09:01
确实不错  好东西 建议根据意见完善
9#
manglang 发表于 2026-9-16 09:24
[HTML] 纯文本查看 复制代码
<!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/index.css">
    <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/users.js"></script>
</head>

<body>
    <div id="main" class="wall">
        <!-- 中奖名单 -->
        <div class="luck-user" id="luck-user">
            <div class="luck-user-title">
                <span>中奖名单</span>
            </div>
            <ul class="luck-user-list">
                <li v-for="(item, index) in results" :key="index">
                    <div class="luckuserName">
                        <span class="uname">{{item[0]}}</span>
                        <span class="phone">{{item[1]}}</span>
                    </div>
                </li>
            </ul>
            <div class="luck-user-btn">
                <a href="#">中奖人</a>
            </div>
        </div>
        <!-- 开始按钮 -->
        <div id="tools" class="tools">
            <button v-for="value in btns" @click="onClick(value)" class="pure-button"
                :class="{ 'button-error': selected == value}">{{value}}</button>
            <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>
    </div>
    <div id="result" class="result"></div>
    <button id="continue" class="continue">继续抽奖</button>
</body>

<script type="text/javascript">
    // 画布
    var canvas = document.createElement('canvas');
    canvas.id = 'myCanvas';
    canvas.width = document.body.offsetWidth;
    canvas.height = document.body.offsetHeight;
    document.getElementById('main').appendChild(canvas);

    // 取本地数据 (存储已经中奖的 key)
    var choosed = JSON.parse(localStorage.getItem('choosed')) || {};

    // 抽奖历史记录存储 (存储每次抽奖的结果,用于名单展示)
    // 结构: { '抽奖时间': ['姓名<br>电话', ...] }
    // 为了防止冲突,单独一个 key 存储,而不是散落在多个 key
    var HISTORY_KEY = 'lottery_history';

    // 处理函数
    function speed() {
        return [0.1 * Math.random() + 0.01, -(0.1 * Math.random() + 0.01)];
    }
    function getKey(item) {
        return item.name + '-' + item.phone;
    }
    function createHTML() {
        var html = ['<ul>'];
        member.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 + ';display: block; width: 100px; height: 100px; background: red;">' + item.name + '</a></li>');
        });
        html.push('</ul>');
        return html.join('');
    }

    // 抽奖核心:返回中奖者信息数组,同时更新 choosed 和页面颜色,并记录到历史
    function lottery(count) {
        var list = canvas.getElementsByTagName('a');
        var color = 'yellow';
        // 获取未中奖的成员,随机排序,取前 count 个
        var winners = member
            .filter(function (m) {
                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);

        // 如果没有可抽的人,返回空数组
        if (winners.length === 0) {
            return [];
        }

        // 标记中奖者并生成返回文本
        var ret = winners.map(function (m) {
            choosed[getKey(m)] = 1;
            // 更新对应画布元素的颜色为黄色
            if (list[m.index]) {
                list[m.index].style.color = color;
            }
            // 返回格式: "姓名<br>电话"
            return m.name + '<br>' + m.phone;
        });

        // 保存 choosed 到 localStorage
        localStorage.setItem('choosed', JSON.stringify(choosed));

        // 记录本次抽奖结果到历史 (使用时间戳作为key,确保唯一)
        var history = JSON.parse(localStorage.getItem(HISTORY_KEY)) || {};
        // 用当前时间作为记录键 (毫秒级可能重复?加随机数)
        var recordKey = new Date().getTime() + '_' + Math.floor(Math.random() * 1000);
        history[recordKey] = ret;
        localStorage.setItem(HISTORY_KEY, JSON.stringify(history));

        return ret;
    }

    // 功能:Vue实例 - 控制按钮和抽奖逻辑
    new Vue({
        el: '#tools',
        data: {
            selected: 1,
            running: false,
            btns: [1, 2, 5, 10, 30]
        },
        mounted() {
            canvas.innerHTML = createHTML();
            TagCanvas.Start('myCanvas', '', {
                textColour: null,
                initial: speed(),
                dragControl: 1,
                textHeight: 14
            });
            // 初始化后刷新中奖名单显示
            if (window.refreshLuckList) window.refreshLuckList();
        },
        methods: {
            reset() {
                if (confirm('确定要重置么?所有之前的抽奖历史将被清除!')) {
                    // 清除 choosed 和 history
                    localStorage.removeItem('choosed');
                    localStorage.removeItem(HISTORY_KEY);
                    // 兼容之前散落的 key (可选)
                    // 但如果之前是散落的多个 key,这里也可以清一下,但为保险,完全清除 localStorage 但保留可能的其他? 
                    // 直接用 localStorage.clear() 最简单,但可能会清除其他无关数据。根据需求,这里使用 clear 是原逻辑,但为了不误删,可以保留,但用户可能希望完全重置。
                    // 为了兼容旧版本可能散落的数据,最简单还是 clear,但为了安全我们只清除我们的key和旧的散落key?
                    // 原代码用的是 localStorage.clear(),我们就保持原样,但注意可能会清除其他数据。年会抽奖一般独立页面,没关系。
                    // 但为了不误删,可以只删特定 key。但旧版本可能存储了时间戳的key,直接 clear 也可以。我们改为更安全的方式:遍历删除除了可能需要的其他?算了,就用 clear 吧,和原来保持一致。
                    localStorage.clear();
                    location.reload(true);
                }
            },
            onClick(num) {
                $('#result').css('display', 'none');
                $('#continue').css('display', 'none');
                $('#main').removeClass('mask');
                this.selected = num;
            },
            // 开始/停止抽奖
            toggle() {
                if (this.running) {
                    // 当前是运行中,点击停止 => 执行抽奖
                    TagCanvas.SetSpeed('myCanvas', speed());
                    var ret = lottery(this.selected);
                    if (ret.length === 0) {
                        $('#result').css('display', 'block').html('<span>已抽完</span>');
                        // 即使抽完也刷新一下名单?没有必要
                        return;
                    }
                    // 中奖人信息展示在 result 区域
                    $('#result').css('display', 'block').html('<span>' + ret.join('</span><span>') + '</span>');
                    $('#continue').css('display', 'block');
                    TagCanvas.Reload('myCanvas');
                    
                    // 新增:立即刷新中奖名单显示 (触发 luck-user 组件的更新)
                    if (window.refreshLuckList) {
                        window.refreshLuckList();
                    }
                    
                    setTimeout(function () {
                        // 注意:上面 lottery 已经存储了历史,这里不需要再存储了,原逻辑是存储到 localStorage 以时间戳为 key,我们现在统一用 history 存储
                        // 但原逻辑还做了 $('#main').addClass('mask'),我们保留
                        $('#main').addClass('mask');
                    }, 300);
                } else {
                    // 开始抽奖:隐藏结果,移除遮罩,加速旋转
                    $('#result').css('display', 'none');
                    $('#continue').css('display', 'none');
                    $('#main').removeClass('mask');
                    TagCanvas.SetSpeed('myCanvas', [5, 1]);
                }
                this.running = !this.running;
            }
        }
    });

    // 中奖名单组件 - 独立Vue实例
    // 为了能够从外部刷新,我们将 refresh 方法挂载到 window 上
    (function() {
        var luckVue = new Vue({
            el: '#luck-user',
            data: {
                results: [] // 每个元素为 [name, phone] 数组
            },
            mounted() {
                this.loadResults();
                // 暴露刷新方法
                window.refreshLuckList = () => {
                    this.loadResults();
                };
            },
            methods: {
                loadResults() {
                    // 从 localStorage 的 history 中读取所有中奖记录
                    var history = JSON.parse(localStorage.getItem(HISTORY_KEY)) || {};
                    var allResults = [];
                    
                    // 按时间戳排序(时间戳越小越早),但我们的 key 是 "时间戳_随机数",提取时间戳排序
                    var keys = Object.keys(history);
                    keys.sort((a, b) => {
                        var tsA = parseInt(a.split('_')[0]) || 0;
                        var tsB = parseInt(b.split('_')[0]) || 0;
                        return tsA - tsB;
                    });
                    
                    // 遍历每个记录,将每个记录中的中奖者字符串解析成 [name, phone]
                    keys.forEach(key => {
                        var record = history[key]; // 数组,元素如 "张三<br>13800000000"
                        if (Array.isArray(record)) {
                            record.forEach(itemStr => {
                                // 分割字符串:姓名<br>电话
                                var parts = itemStr.split('<br>');
                                if (parts.length >= 2) {
                                    allResults.push([parts[0], parts[1]]);
                                } else {
                                    // 兼容可能只有名字的情况
                                    allResults.push([itemStr, '']);
                                }
                            });
                        }
                    });
                    
                    // 去重?根据业务,同一个用户可能多次中奖?年会抽奖一般不会重复中奖,但代码逻辑是 choosed 控制,不会重复。
                    // 这里直接赋值
                    this.results = allResults;
                }
            }
        });
    })();

    // 点击事件(继续抽奖)- 使用原生 jQuery/Zepto
    $("#continue").click(function () {
        $('#continue').css('display', 'none');
        $('#main').removeClass('mask');
        $('#result').css('display', 'none');
        // 完全刷新页面可能导致重新抽奖逻辑重置,但为了清除状态,可以选择刷新
        // 但是刷新后,画布重新生成,choosed 从 localStorage 读取,所以中奖用户依然黄色,没问题。
        location.reload(true);
    });
</script>
</body>

</html>
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-9-16 09:56

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表