首页 / 应用软件 / 详细指南,开发网站线上活动抽奖与实时弹幕互动展示系统

详细指南,开发网站线上活动抽奖与实时弹幕互动展示系统

详细指南:开发网站线上活动抽奖与实时弹幕互动展示系统

摘要

在当今数字化时代,线上活动已成为企业与用户互动的重要方式。本文将详细介绍如何通过WordPress程序的代码二次开发,实现一个集线上抽奖与实时弹幕互动展示于一体的系统。我们将从系统设计、技术选型、代码实现到部署测试,全面解析这一常用互联网小工具功能的开发过程,帮助您为网站增添互动性与趣味性。


第一章:系统概述与需求分析

1.1 项目背景与意义

随着互联网技术的快速发展,线上活动已成为企业营销、社区互动和用户参与的重要手段。抽奖活动能够有效提升用户参与度,而实时弹幕互动则能增强活动的趣味性和即时互动性。将这两种功能结合,可以为线上活动带来全新的体验。

传统的线上活动工具往往功能单一,且与网站集成度不高。通过WordPress二次开发实现这一系统,不仅可以充分利用WordPress庞大的用户基础和成熟的生态系统,还能根据具体需求进行深度定制,实现与网站的无缝集成。

1.2 系统功能需求

本系统需要实现以下核心功能:

  1. 抽奖系统功能

    • 支持多种抽奖模式(随机抽取、按条件筛选抽取)
    • 灵活的奖品设置与管理
    • 参与资格验证与限制
    • 中奖结果实时展示与通知
    • 中奖记录管理与导出
  2. 实时弹幕互动功能

    • 用户实时发送弹幕消息
    • 弹幕样式自定义(颜色、大小、位置)
    • 弹幕内容审核与过滤
    • 弹幕数据统计与分析
    • 弹幕显示控制(速度、密度、显示区域)
  3. 管理后台功能

    • 活动创建与配置
    • 参与用户管理
    • 中奖记录查看与操作
    • 弹幕内容审核与管理
    • 数据统计与报表生成

1.3 技术选型与架构设计

技术栈选择

  • 后端:WordPress + PHP 7.4+
  • 前端:HTML5 + CSS3 + JavaScript (ES6+)
  • 实时通信:WebSocket (Ratchet或Swoole)
  • 数据库:MySQL 5.7+
  • 缓存:Redis (可选,用于提升性能)

系统架构
采用前后端分离的设计思路,前端通过AJAX与WordPress REST API交互,实时功能通过WebSocket实现。抽奖逻辑在服务器端执行确保公平性,弹幕数据通过WebSocket广播实现实时展示。


第二章:开发环境搭建与准备工作

2.1 WordPress环境配置

首先需要搭建一个标准的WordPress开发环境:

// 推荐使用本地开发环境
// 1. 安装XAMPP/MAMP/WAMP等集成环境
// 2. 下载最新版WordPress
// 3. 创建数据库并完成WordPress安装
// 4. 启用调试模式,在wp-config.php中添加:

define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', false);

// 5. 安装必要的开发插件:
// - Query Monitor (性能调试)
// - Show Current Template (模板调试)
// - Advanced Custom Fields (字段管理)

2.2 创建自定义插件

为保持代码的可维护性和可移植性,我们将所有功能封装为一个独立的WordPress插件:

/*
Plugin Name: 线上活动抽奖与弹幕系统
Plugin URI: https://yourwebsite.com/
Description: 提供线上抽奖与实时弹幕互动功能
Version: 1.0.0
Author: Your Name
License: GPL v2 or later
Text Domain: lottery-danmaku
*/

// 防止直接访问
if (!defined('ABSPATH')) {
    exit;
}

// 定义插件常量
define('LD_PLUGIN_DIR', plugin_dir_path(__FILE__));
define('LD_PLUGIN_URL', plugin_dir_url(__FILE__));
define('LD_VERSION', '1.0.0');

// 初始化插件
require_once LD_PLUGIN_DIR . 'includes/class-core.php';
require_once LD_PLUGIN_DIR . 'includes/class-database.php';
require_once LD_PLUGIN_DIR . 'includes/class-websocket.php';

function ld_init() {
    $plugin = Lottery_Danmaku_Core::get_instance();
    $plugin->init();
}
add_action('plugins_loaded', 'ld_init');

2.3 数据库设计

创建必要的数据库表来存储活动、参与记录、弹幕等数据:

// includes/class-database.php
class Lottery_Danmaku_Database {
    
    public function create_tables() {
        global $wpdb;
        
        $charset_collate = $wpdb->get_charset_collate();
        
        // 活动表
        $table_activities = $wpdb->prefix . 'ld_activities';
        $sql_activities = "CREATE TABLE IF NOT EXISTS $table_activities (
            id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
            title VARCHAR(200) NOT NULL,
            description TEXT,
            type ENUM('lottery', 'danmaku', 'both') DEFAULT 'both',
            start_time DATETIME NOT NULL,
            end_time DATETIME NOT NULL,
            status ENUM('draft', 'active', 'ended', 'archived') DEFAULT 'draft',
            settings LONGTEXT,
            created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
            PRIMARY KEY (id)
        ) $charset_collate;";
        
        // 抽奖奖品表
        $table_prizes = $wpdb->prefix . 'ld_prizes';
        $sql_prizes = "CREATE TABLE IF NOT EXISTS $table_prizes (
            id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
            activity_id BIGINT(20) UNSIGNED NOT NULL,
            name VARCHAR(200) NOT NULL,
            description TEXT,
            quantity INT(11) NOT NULL DEFAULT 1,
            level INT(11) DEFAULT 1,
            probability DECIMAL(5,4) DEFAULT 0.0,
            remaining INT(11) NOT NULL DEFAULT 0,
            PRIMARY KEY (id),
            FOREIGN KEY (activity_id) REFERENCES $table_activities(id) ON DELETE CASCADE
        ) $charset_collate;";
        
        // 参与记录表
        $table_participants = $wpdb->prefix . 'ld_participants';
        $sql_participants = "CREATE TABLE IF NOT EXISTS $table_participants (
            id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
            activity_id BIGINT(20) UNSIGNED NOT NULL,
            user_id BIGINT(20) UNSIGNED,
            user_email VARCHAR(100),
            user_name VARCHAR(100),
            joined_at DATETIME DEFAULT CURRENT_TIMESTAMP,
            ip_address VARCHAR(45),
            user_agent TEXT,
            is_winner TINYINT(1) DEFAULT 0,
            prize_id BIGINT(20) UNSIGNED,
            PRIMARY KEY (id),
            INDEX activity_user (activity_id, user_id),
            FOREIGN KEY (activity_id) REFERENCES $table_activities(id) ON DELETE CASCADE
        ) $charset_collate;";
        
        // 弹幕消息表
        $table_danmaku = $wpdb->prefix . 'ld_danmaku';
        $sql_danmaku = "CREATE TABLE IF NOT EXISTS $table_danmaku (
            id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
            activity_id BIGINT(20) UNSIGNED NOT NULL,
            user_id BIGINT(20) UNSIGNED,
            content TEXT NOT NULL,
            color VARCHAR(7) DEFAULT '#FFFFFF',
            size INT(11) DEFAULT 24,
            position ENUM('top', 'bottom', 'fly') DEFAULT 'fly',
            status ENUM('pending', 'approved', 'rejected') DEFAULT 'pending',
            sent_at DATETIME DEFAULT CURRENT_TIMESTAMP,
            displayed TINYINT(1) DEFAULT 0,
            PRIMARY KEY (id),
            INDEX activity_status (activity_id, status),
            FOREIGN KEY (activity_id) REFERENCES $table_activities(id) ON DELETE CASCADE
        ) $charset_collate;";
        
        require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
        dbDelta($sql_activities);
        dbDelta($sql_prizes);
        dbDelta($sql_participants);
        dbDelta($sql_danmaku);
    }
}

第三章:抽奖系统核心功能实现

3.1 活动管理模块

创建活动管理后台界面,使用WordPress的Settings API和Custom Post Type:

// includes/class-admin.php
class Lottery_Danmaku_Admin {
    
    public function register_post_types() {
        // 注册活动自定义文章类型
        $args = array(
            'labels' => array(
                'name' => __('活动管理', 'lottery-danmaku'),
                'singular_name' => __('活动', 'lottery-danmaku'),
            ),
            'public' => false,
            'show_ui' => true,
            'show_in_menu' => true,
            'menu_position' => 30,
            'menu_icon' => 'dashicons-megaphone',
            'supports' => array('title', 'editor'),
            'capability_type' => 'post',
            'capabilities' => array(
                'create_posts' => 'manage_options',
            ),
            'map_meta_cap' => true,
        );
        register_post_type('ld_activity', $args);
        
        // 添加活动管理页面
        add_menu_page(
            __('抽奖弹幕系统', 'lottery-danmaku'),
            __('抽奖弹幕', 'lottery-danmaku'),
            'manage_options',
            'lottery-danmaku',
            array($this, 'admin_page'),
            'dashicons-awards',
            30
        );
    }
    
    public function admin_page() {
        ?>
        <div class="wrap">
            <h1><?php echo esc_html(get_admin_page_title()); ?></h1>
            
            <div class="ld-admin-container">
                <div class="ld-tabs">
                    <button class="ld-tab active" data-target="activities">活动管理</button>
                    <button class="ld-tab" data-target="prizes">奖品管理</button>
                    <button class="ld-tab" data-target="participants">参与记录</button>
                    <button class="ld-tab" data-target="danmaku">弹幕审核</button>
                    <button class="ld-tab" data-target="settings">系统设置</button>
                </div>
                
                <div class="ld-tab-content active" id="activities">
                    <!-- 活动管理界面 -->
                    <?php $this->render_activities_table(); ?>
                </div>
                
                <div class="ld-tab-content" id="prizes">
                    <!-- 奖品管理界面 -->
                    <?php $this->render_prizes_table(); ?>
                </div>
                
                <!-- 其他标签页内容 -->
            </div>
        </div>
        <?php
    }
}

3.2 抽奖算法实现

实现公平、高效的抽奖算法:

// includes/class-lottery.php
class Lottery_Engine {
    
    /**
     * 执行抽奖
     * @param int $activity_id 活动ID
     * @param int $user_id 用户ID
     * @return array 抽奖结果
     */
    public function draw($activity_id, $user_id) {
        global $wpdb;
        
        // 验证活动状态
        $activity = $this->get_activity($activity_id);
        if (!$activity || $activity->status !== 'active') {
            return array('success' => false, 'message' => '活动未开始或已结束');
        }
        
        // 验证用户参与资格
        if (!$this->check_eligibility($activity_id, $user_id)) {
            return array('success' => false, 'message' => '您不符合参与条件');
        }
        
        // 检查用户是否已参与
        if ($this->has_participated($activity_id, $user_id)) {
            return array('success' => false, 'message' => '您已参与过本次活动');
        }
        
        // 获取奖品列表
        $prizes = $this->get_available_prizes($activity_id);
        if (empty($prizes)) {
            return array('success' => false, 'message' => '奖品已抽完');
        }
        
        // 执行抽奖算法
        $result = $this->calculate_prize($prizes);
        
        if ($result['prize_id']) {
            // 用户中奖
            $this->record_winner($activity_id, $user_id, $result['prize_id']);
            $prize_info = $this->get_prize_info($result['prize_id']);
            
            return array(
                'success' => true,
                'is_winner' => true,
                'prize' => $prize_info,
                'message' => '恭喜您中奖!'
            );
        } else {
            // 用户未中奖
            $this->record_participant($activity_id, $user_id);
            
            return array(
                'success' => true,
                'is_winner' => false,
                'message' => '很遗憾,您未中奖'
            );
        }
    }
    
    /**
     * 概率算法计算中奖奖品
     * @param array $prizes 奖品列表
     * @return array 抽奖结果
     */
    private function calculate_prize($prizes) {
        $total_probability = 0;
        $prize_list = array();
        
        // 构建奖品概率数组
        foreach ($prizes as $prize) {
            if ($prize->remaining > 0) {
                $total_probability += $prize->probability;
                $prize_list[] = array(
                    'id' => $prize->id,
                    'probability' => $prize->probability,
                    'cumulative' => $total_probability
                );
            }
        }
        
        // 生成随机数
        $random = mt_rand() / mt_getrandmax();
        $selected_prize = 0;
        
        // 根据概率选择奖品
        foreach ($prize_list as $prize) {
            if ($random <= $prize['cumulative']) {
                $selected_prize = $prize['id'];
                break;
            }
        }
        
        return array('prize_id' => $selected_prize);
    }
    
    /**
     * 记录中奖者
     */
    private function record_winner($activity_id, $user_id, $prize_id) {
        global $wpdb;
        
        $table = $wpdb->prefix . 'ld_participants';
        
        // 记录参与信息
        $wpdb->insert($table, array(
            'activity_id' => $activity_id,
            'user_id' => $user_id,
            'is_winner' => 1,
            'prize_id' => $prize_id,
            'joined_at' => current_time('mysql')
        ));
        
        // 减少奖品剩余数量
        $prize_table = $wpdb->prefix . 'ld_prizes';
        $wpdb->query($wpdb->prepare(
            "UPDATE $prize_table SET remaining = remaining - 1 WHERE id = %d AND remaining > 0",
            $prize_id
        ));
        
        // 发送中奖通知
        $this->send_winner_notification($user_id, $prize_id);
    }
}

3.3 前端抽奖界面

创建用户参与抽奖的前端界面:

<!-- templates/lottery-frontend.php -->
<div class="lottery-container" data-activity-id="<?php echo $activity_id; ?>">
    <div class="lottery-header">
        <h2><?php echo esc_html($activity_title); ?></h2>
        <p class="lottery-description"><?php echo esc_html($activity_description); ?></p>
        <div class="lottery-timer" id="lottery-timer">
            <span>活动倒计时: </span>
            <span class="countdown" data-end="<?php echo $end_time; ?>"></span>
        </div>
    </div>
    
    <div class="lottery-prizes">
        <h3>活动奖品</h3>
        <div class="prizes-grid">
            <?php foreach ($prizes as $prize): ?>
            <div class="prize-item" data-level="<?php echo $prize->level; ?>">
                <div class="prize-icon">🏆</div>
                <h4><?php echo esc_html($prize->name); ?></h4>
                <p><?php echo esc_html($prize->description); ?></p>
                <div class="prize-quantity">
                    剩余: <span class="remaining"><?php echo $prize->remaining; ?></span>/<?php echo $prize->quantity; ?>
                </div>
            </div>
            <?php endforeach; ?>
        </div>
    </div>
    
    <div class="lottery-action">
        <?php if (is_user_logged_in()): ?>
            <button class="btn-draw" id="btn-draw" <?php echo $can_participate ? '' : 'disabled'; ?>>
                <?php echo $can_participate ? '立即抽奖' : '已参与'; ?>
            </button>
            <div class="lottery-result" id="lottery-result"></div>
        <?php else: ?>
            <div class="login-required">
                <p>请先登录参与抽奖</p>
                <a href="<?php echo wp_login_url(get_permalink()); ?>" class="btn-login">登录</a>
            </div>
        <?php endif; ?>
    </div>
    
    <div class="lottery-winners">
        <h3>中奖名单</h3>
        <div class="winners-list" id="winners-list">
            <!-- 通过AJAX动态加载中奖名单 -->
        </div>
    </div>
</div>

<script>
jQuery(document).ready(function($) {
    // 抽奖按钮点击事件

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

    const $btn = $(this);
    const activityId = $('.lottery-container').data('activity-id');
    
    if ($btn.hasClass('processing')) return;
    
    $btn.addClass('processing').text('抽奖中...');
    
    // 发送抽奖请求
    $.ajax({
        url: ld_ajax.ajax_url,
        type: 'POST',
        data: {
            action: 'ld_perform_draw',
            activity_id: activityId,
            nonce: ld_ajax.nonce
        },
        success: function(response) {
            if (response.success) {
                if (response.data.is_winner) {
                    // 显示中奖动画
                    showWinnerAnimation(response.data.prize);
                    // 更新中奖名单
                    loadWinnersList();
                } else {
                    $('#lottery-result').html(
                        '<div class="result-message not-winner">' + 
                        '<p>很遗憾,您未中奖</p>' +
                        '<p>感谢参与!</p>' +
                        '</div>'
                    );
                }
                $btn.prop('disabled', true).text('已参与');
            } else {
                alert(response.data.message);
                $btn.removeClass('processing').text('立即抽奖');
            }
        },
        error: function() {
            alert('抽奖失败,请稍后重试');
            $btn.removeClass('processing').text('立即抽奖');
        }
    });
});

// 加载中奖名单
function loadWinnersList() {
    $.ajax({
        url: ld_ajax.ajax_url,
        type: 'GET',
        data: {
            action: 'ld_get_winners',
            activity_id: activityId
        },
        success: function(response) {
            if (response.success) {
                $('#winners-list').html(response.data.html);
            }
        }
    });
}

// 中奖动画效果
function showWinnerAnimation(prize) {
    const $result = $('#lottery-result');
    $result.html(`
        <div class="winner-animation">
            <div class="confetti"></div>
            <div class="prize-reveal">
                <h3>🎉 恭喜您中奖了! 🎉</h3>
                <div class="prize-details">
                    <h4>${prize.name}</h4>
                    <p>${prize.description}</p>
                </div>
                <p class="winner-instructions">请查看您的注册邮箱获取领奖方式</p>
            </div>
        </div>
    `);
    
    // 触发WebSocket广播中奖消息
    if (window.ldWebSocket && window.ldWebSocket.readyState === WebSocket.OPEN) {
        const message = {
            type: 'winner_announcement',
            data: {
                prize: prize.name,
                timestamp: new Date().toISOString()
            }
        };
        window.ldWebSocket.send(JSON.stringify(message));
    }
}

});
</script>


---

## 第四章:实时弹幕系统实现

### 4.1 WebSocket服务器搭建

使用PHP Ratchet实现WebSocket服务器:

// includes/class-websocket-server.php
use RatchetMessageComponentInterface;
use RatchetConnectionInterface;
use RatchetServerIoServer;
use RatchetHttpHttpServer;
use RatchetWebSocketWsServer;

class DanmakuWebSocket implements MessageComponentInterface {


protected $clients;
protected $activityConnections;

public function __construct() {
    $this->clients = new SplObjectStorage;
    $this->activityConnections = [];
}

public function onOpen(ConnectionInterface $conn) {
    $this->clients->attach($conn);
    echo "新连接: {$conn->resourceId}n";
}

public function onMessage(ConnectionInterface $from, $msg) {
    $data = json_decode($msg, true);
    
    if (!$data || !isset($data['type'])) {
        return;
    }
    
    switch ($data['type']) {
        case 'subscribe':
            // 订阅特定活动
            $activityId = $data['activity_id'];
            if (!isset($this->activityConnections[$activityId])) {
                $this->activityConnections[$activityId] = [];
            }
            $this->activityConnections[$activityId][$from->resourceId] = $from;
            $from->activityId = $activityId;
            break;
            
        case 'danmaku':
            // 处理弹幕消息
            $this->handleDanmaku($from, $data);
            break;
            
        case 'heartbeat':
            // 心跳检测
            $from->send(json_encode(['type' => 'pong']));
            break;
    }
}

private function handleDanmaku($from, $data) {
    global $wpdb;
    
    $activityId = $data['activity_id'];
    $content = sanitize_text_field($data['content']);
    $userId = isset($data['user_id']) ? intval($data['user_id']) : 0;
    
    // 保存到数据库
    $table = $wpdb->prefix . 'ld_danmaku';
    $wpdb->insert($table, [
        'activity_id' => $activityId,
        'user_id' => $userId,
        'content' => $content,
        'color' => $data['color'] ?? '#FFFFFF',
        'size' => $data['size'] ?? 24,
        'position' => $data['position'] ?? 'fly',
        'status' => 'pending', // 需要审核
        'sent_at' => current_time('mysql')
    ]);
    
    $danmakuId = $wpdb->insert_id;
    
    // 如果是管理员或自动审核通过的消息,立即广播
    if ($this->shouldAutoApprove($userId)) {
        $this->broadcastDanmaku($activityId, [
            'id' => $danmakuId,
            'content' => $content,
            'color' => $data['color'] ?? '#FFFFFF',
            'size' => $data['size'] ?? 24,
            'position' => $data['position'] ?? 'fly',
            'timestamp' => time(),
            'user' => $this->getUserInfo($userId)
        ]);
        
        // 更新状态为已批准
        $wpdb->update($table, 
            ['status' => 'approved'], 
            ['id' => $danmakuId]
        );
    }
}

private function broadcastDanmaku($activityId, $danmaku) {
    if (!isset($this->activityConnections[$activityId])) {
        return;
    }
    
    $message = json_encode([
        'type' => 'danmaku',
        'data' => $danmaku
    ]);
    
    foreach ($this->activityConnections[$activityId] as $client) {
        $client->send($message);
    }
}

public function onClose(ConnectionInterface $conn) {
    $this->clients->detach($conn);
    
    // 从活动连接中移除
    if (isset($conn->activityId)) {
        unset($this->activityConnections[$conn->activityId][$conn->resourceId]);
    }
    
    echo "连接关闭: {$conn->resourceId}n";
}

public function onError(ConnectionInterface $conn, Exception $e) {
    echo "错误: {$e->getMessage()}n";
    $conn->close();
}

}

// WebSocket服务器启动脚本
class WebSocketServer {


public function start() {
    $port = get_option('ld_websocket_port', 8080);
    $server = IoServer::factory(
        new HttpServer(
            new WsServer(
                new DanmakuWebSocket()
            )
        ),
        $port
    );
    
    echo "WebSocket服务器运行在端口 {$port}n";
    $server->run();
}

}


### 4.2 前端弹幕展示系统

创建弹幕展示前端:

<!-- templates/danmaku-display.php -->
<div class="danmaku-container" data-activity-id="<?php echo $activity_id; ?>">

<div class="danmaku-stage" id="danmaku-stage">
    <!-- 弹幕将在这里显示 -->
</div>

<div class="danmaku-controls">
    <div class="danmaku-input-area">
        <input type="text" 
               id="danmaku-input" 
               placeholder="输入弹幕内容..." 
               maxlength="100">
        
        <div class="danmaku-style-controls">
            <div class="color-picker">
                <label>颜色:</label>
                <input type="color" id="danmaku-color" value="#FFFFFF">
            </div>
            
            <div class="size-selector">
                <label>大小:</label>
                <select id="danmaku-size">
                    <option value="20">小</option>
                    <option value="24" selected>中</option>
                    <option value="28">大</option>
                </select>
            </div>
            
            <div class="position-selector">
                <label>位置:</label>
                <select id="danmaku-position">
                    <option value="fly" selected>滚动</option>
                    <option value="top">顶部</option>
                    <option value="bottom">底部</option>
                </select>
            </div>
        </div>
        
        <button id="send-danmaku" class="btn-send">
            <span class="dashicons dashicons-arrow-right-alt"></span> 发送
        </button>
    </div>
    
    <div class="danmaku-settings">
        <label>
            <input type="checkbox" id="danmaku-auto-scroll" checked> 自动滚动
        </label>
        <label>
            速度: <input type="range" id="danmaku-speed" min="1" max="10" value="5">
        </label>
        <label>
            透明度: <input type="range" id="danmaku-opacity" min="0.1" max="1" step="0.1" value="0.8">
        </label>
        <button id="clear-danmaku" class="btn-clear">清屏</button>
    </div>
</div>

<div class="danmaku-stats">
    <span>在线人数: <span id="online-count">0</span></span>
    <span>弹幕数量: <span id="danmaku-count">0</span></span>
</div>

</div>

<script>
class DanmakuDisplay {

constructor(container, activityId) {
    this.container = container;
    this.activityId = activityId;
    this.stage = container.querySelector('#danmaku-stage');
    this.danmakuPool = [];
    this.activeDanmaku = [];
    this.onlineCount = 0;
    this.danmakuCount = 0;
    this.settings = {
        speed: 5,
        opacity: 0.8,
        autoScroll: true,
        maxDanmaku: 100
    };
    
    this.initWebSocket();
    this.initControls();
    this.loadHistory();
    this.startRenderLoop();
}

initWebSocket() {
    const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
    const host = window.location.hostname;
    const port = ld_settings.websocket_port || 8080;
    const wsUrl = `${protocol}//${host}:${port}`;
    
    this.ws = new WebSocket(wsUrl);
    
    this.ws.onopen = () => {
        console.log('WebSocket连接已建立');
        // 订阅活动
        this.ws.send(JSON.stringify({
            type: 'subscribe',
            activity_id: this.activityId
        }));
        
        // 开始心跳
        this.startHeartbeat();
    };
    
    this.ws.onmessage = (event) => {
        const data = JSON.parse(event.data);
        this.handleMessage(data);
    };
    
    this.ws.onclose = () => {
        console.log('WebSocket连接关闭');
        setTimeout(() => this.initWebSocket(), 3000);
    };
}

handleMessage(data) {
    switch (data.type) {
        case 'danmaku':
            this.addDanmaku(data.data);
            break;
        case 'online_count':
            this.updateOnlineCount(data.count);
            break;
        case 'winner_announcement':
            this.showWinnerAnnouncement(data.data);
            break;
    }
}

addDanmaku(danmaku) {
    // 创建弹幕元素
    const element = document.createElement('div');
    element.className = `danmaku-item ${danmaku.position}`;
    element.textContent = danmaku.content;
    element.style.color = danmaku.color;
    element.style.fontSize = `${danmaku.size}px`;
    element.style.opacity = this.settings.opacity;
    element.dataset.id = danmaku.id;
    
    // 添加到舞台
    this.stage.appendChild(element);
    
    // 添加到活动弹幕列表
    this.activeDanmaku.push({
        element: element,
        data: danmaku,
        position: 0
    });
    
    // 更新计数
    this.danmakuCount++;
    this.updateStats();
    
    // 限制弹幕数量
    if (this.activeDanmaku.length > this.settings.maxDanmaku) {
        const oldest = this.activeDanmaku.shift();
        oldest.element.remove();
    }
}

startRenderLoop() {
    const render = () => {
        const stageWidth = this.stage.offsetWidth;
        const stageHeight = this.stage.offsetHeight;
        
        this.activeDanmaku.forEach((danmaku, index) => {
            const element = danmaku.element;
            const speed = this.settings.speed;
            
            if (danmaku.data.position === 'fly') {
                // 滚动弹幕
                if (danmaku.position === 0) {
                    // 初始位置在右侧
                    const elementWidth = element.offsetWidth;
                    element.style.left = `${stageWidth}px`;
                    element.style.top = `${Math.random() * (stageHeight - 30)}px`;
                    danmaku.position = stageWidth;
                }
                
                // 向左移动
                danmaku.position -= speed;
                element.style.transform = `translateX(${danmaku.position}px)`;
                
                // 移出屏幕后移除
                if (danmaku.position < -element.offsetWidth) {
                    element.remove();
                    this.activeDanmaku.splice(index, 1);
                }
            } else {
                // 固定位置弹幕
                if (!element.style.left) {
                    element.style.left = '50%';
                    element.style.transform = 'translateX(-50%)';
                    element.style.top = danmaku.data.position === 'top' ? '10px' : 'auto';
                    element.style.bottom = danmaku.data.position === 'bottom' ? '10px' : 'auto';
                    
                    // 3秒后移除
                    setTimeout(() => {
                        element.remove();
                        this.activeDanmaku.splice(index, 1);
                    }, 3000);
                }
            }
        });
        
        requestAnimationFrame(render);
    };
    
    render();
}

sendDanmaku(content, style = {}) {
    if (!content.trim() || !this.ws || this.ws.readyState !== WebSocket.OPEN) {
        return;
    }
    
    const message = {
        type: 'danmaku',
        activity_id: this.activityId,
        content: content,
        color: style.color || '#FFFFFF',
        size: style.size || 24,
        position: style.position || 'fly',
        user_id: ld_settings.current_user_id || 0
    };
    
    this.ws.send(JSON.stringify(message));
}

initControls() {
    const sendBtn = this.container.querySelector('#send-danmaku');
    const input = this.container.querySelector('#danmaku-input');
    
    sendBtn.addEventListener('click', () => {
        const content = input.value.trim();
        if (content) {
            const style = {
                color: this.container.querySelector('#danmaku-color').value,
                size: this.container.querySelector('#danmaku-size').value,
                position: this.container.querySelector('#danmaku-position').value
            };
            
            this.sendDanmaku(content, style);
            input.value = '';
        }
    });
    
    input.addEventListener('keypress', (e) => {
        if (e.key === 'Enter') {
            sendBtn.click();
        }
    });
    
    // 设置控件
    this.container.querySelector('#danmaku-speed').addEventListener('input', (e) => {
        this.settings.speed = parseInt(e.target.value);
    });
    
    this.container.querySelector('#danmaku-opacity').addEventListener('input', (e) => {
        this.settings.opacity = parseFloat(e.target.value);
        this.activeDanmaku.forEach(d => {
            d.element.style.opacity = this.settings.opacity;
        });
    });
    
    this.container.querySelector('#clear-danmaku').addEventListener('click', () => {
        this.activeDanmaku.forEach(d => d.element.remove());
        this.activeDanmaku = [];
    });
}

loadHistory() {
    // 加载历史弹幕
    fetch(`${ld_ajax.ajax_url}?action=ld_get_danmaku_history&activity_id=${this.activityId}`)
        .then(response => response.json())
        .then(data => {
            if (data.success) {
                data.data.forEach(danmaku => {
                    this.addDanmaku(danmaku);
                });
            }
        });
}

startHeartbeat() {
    setInterval(() => {
        if (this.ws.readyState === WebSocket.OPEN) {
            this.ws.send(JSON.stringify({ type: 'heartbeat' }));
        }
    }, 30000);
}

updateStats() {
    this.container.querySelector('#danmaku-count').textContent = this.danmakuCount;
}

updateOnlineCount(count) {
    this.onlineCount = count;
    this.container.querySelector('#online-count').textContent = count;
}

showWinnerAnnouncement(data) {
    // 显示中奖公告弹幕
    this.addDanmaku({
        id: 'winner_' + Date.now(),
       
本文来自网络,不代表柔性供应链服务中心立场,转载请注明出处:https://mall.org.cn/5361.html

EXCHANGES®作者

上一篇
下一篇

为您推荐

发表回复

联系我们

联系我们

18559313275

在线咨询: QQ交谈

邮箱: vip@exchanges.center

工作时间:周一至周五,9:00-17:30,节假日休息
返回顶部