diff --git a/app/Services/DecorationService.php b/app/Services/DecorationService.php index 7e7a3da..b736866 100644 --- a/app/Services/DecorationService.php +++ b/app/Services/DecorationService.php @@ -9,6 +9,7 @@ * - bubble : 消息气泡边框样式 * - name_color : 昵称颜色效果 * - avatar_frame: 头像装饰边框 + * - text_color : 消息文字颜色特效 * * @author ChatRoom Laravel * @@ -37,6 +38,7 @@ class DecorationService 'msg_bubble' => 'bubble', 'msg_name_color' => 'name_color', 'avatar_frame' => 'avatar_frame', + 'msg_text_color' => 'text_color', ]; /** @@ -173,11 +175,11 @@ class DecorationService /** * 获取消息广播 payload 需要携带的装扮字段。 * - * 消息广播只需要气泡样式(msg_bubble)和昵称颜色(msg_name_color), + * 消息广播只需要气泡样式(msg_bubble)、昵称颜色(msg_name_color)和文字颜色特效(msg_text_color), * 头像框不需要在消息中展示。 * * @param User $user 消息发送者 - * @return array{msg_bubble?:string, msg_name_color?:string} + * @return array{msg_bubble?:string, msg_name_color?:string, msg_text_color?:string} */ public function getDecorationsForMessage(User $user): array { @@ -190,6 +192,9 @@ class DecorationService if (! empty($decorations['name_color']['style'])) { $result['msg_name_color'] = $decorations['name_color']['style']; } + if (! empty($decorations['text_color']['style'])) { + $result['msg_text_color'] = $decorations['text_color']['style']; + } return $result; } diff --git a/database/migrations/2026_04_27_000002_add_msg_text_color_type_to_shop_items.php b/database/migrations/2026_04_27_000002_add_msg_text_color_type_to_shop_items.php new file mode 100644 index 0000000..1ae8f0f --- /dev/null +++ b/database/migrations/2026_04_27_000002_add_msg_text_color_type_to_shop_items.php @@ -0,0 +1,38 @@ + '鎏金流光气泡', 'slug' => 'msg_bubble_golden', 'icon' => '🟡', 'description' => '浅金底纹、左侧金线和流光扫过,发言更醒目但不刺眼。', - 'price' => 300, 'type' => 'msg_bubble', 'duration_days' => 1, 'sort_order' => 50], + 'price' => 3000, 'type' => 'msg_bubble', 'duration_days' => 1, 'sort_order' => 50], ['name' => '樱语花笺气泡', 'slug' => 'msg_bubble_sakura', 'icon' => '🌸', 'description' => '粉白信笺底色配花点纹理,适合温柔、浪漫的发言氛围。', - 'price' => 500, 'type' => 'msg_bubble', 'duration_days' => 3, 'sort_order' => 51], + 'price' => 3000, 'type' => 'msg_bubble', 'duration_days' => 1, 'sort_order' => 51], ['name' => '星河微光气泡', 'slug' => 'msg_bubble_star', 'icon' => '🌌', 'description' => '淡蓝星河底纹和微光星点,保留清爽阅读感。', - 'price' => 800, 'type' => 'msg_bubble', 'duration_days' => 7, 'sort_order' => 52], + 'price' => 5000, 'type' => 'msg_bubble', 'duration_days' => 1, 'sort_order' => 52], ['name' => '霓虹彩带气泡', 'slug' => 'msg_bubble_rainbow', 'icon' => '🌈', 'description' => '顶部流动彩带和浅色底框,发言有动态高光。', - 'price' => 1500, 'type' => 'msg_bubble', 'duration_days' => 7, 'sort_order' => 53], + 'price' => 8000, 'type' => 'msg_bubble', 'duration_days' => 1, 'sort_order' => 53], ['name' => '皇冠礼赞气泡', 'slug' => 'msg_bubble_crown', 'icon' => '👑', 'description' => '金色礼赞底纹、皇冠角标和侧边金线,突出尊贵发言。', - 'price' => 3000, 'type' => 'msg_bubble', 'duration_days' => 30, 'sort_order' => 54], + 'price' => 8000, 'type' => 'msg_bubble', 'duration_days' => 1, 'sort_order' => 54], // ── 昵称颜色装扮 ────────────────────────── ['name' => '金色昵称', 'slug' => 'msg_name_golden', 'icon' => '🥇', @@ -113,27 +113,44 @@ class ShopItemSeeder extends Seeder 'price' => 200, 'type' => 'msg_name_color', 'duration_days' => 1, 'sort_order' => 60], ['name' => '渐变昵称', 'slug' => 'msg_name_rainbow', 'icon' => '🎨', 'description' => '彩虹渐变色昵称,五彩斑斓。', - 'price' => 500, 'type' => 'msg_name_color', 'duration_days' => 3, 'sort_order' => 61], + 'price' => 500, 'type' => 'msg_name_color', 'duration_days' => 1, 'sort_order' => 61], ['name' => '发光昵称', 'slug' => 'msg_name_glow', 'icon' => '✨', 'description' => '昵称带柔和发光效果,暗夜中最亮的星。', - 'price' => 800, 'type' => 'msg_name_color', 'duration_days' => 7, 'sort_order' => 62], + 'price' => 800, 'type' => 'msg_name_color', 'duration_days' => 1, 'sort_order' => 62], ['name' => '火焰昵称', 'slug' => 'msg_name_flame', 'icon' => '🔥', 'description' => '火焰色脉动昵称,热情似火。', - 'price' => 1500, 'type' => 'msg_name_color', 'duration_days' => 7, 'sort_order' => 63], + 'price' => 1500, 'type' => 'msg_name_color', 'duration_days' => 1, 'sort_order' => 63], + + // ── 消息文字颜色特效装扮 ───────────────── + ['name' => '七彩文字', 'slug' => 'msg_text_rainbow', 'icon' => '🌈', + 'description' => '文字色彩在彩虹七色间平滑流动,发言自带虹光特效。', + 'price' => 5000, 'type' => 'msg_text_color', 'duration_days' => 1, 'sort_order' => 65], + ['name' => '流光文字', 'slug' => 'msg_text_shimmer', 'icon' => '💫', + 'description' => '一道流光从左到右扫过文字表面,如金属般闪亮。', + 'price' => 5000, 'type' => 'msg_text_color', 'duration_days' => 1, 'sort_order' => 66], + ['name' => '霓虹文字', 'slug' => 'msg_text_neon', 'icon' => '💜', + 'description' => '紫蓝霓虹光晕在文字周围脉动呼吸,像灯牌一样醒目。', + 'price' => 8000, 'type' => 'msg_text_color', 'duration_days' => 1, 'sort_order' => 67], + ['name' => '火焰文字', 'slug' => 'msg_text_flame', 'icon' => '🔥', + 'description' => '橙红烈火在文字中上下跃动,热情燃烧般的视觉冲击。', + 'price' => 8000, 'type' => 'msg_text_color', 'duration_days' => 1, 'sort_order' => 68], + ['name' => '冰蓝文字', 'slug' => 'msg_text_ice', 'icon' => '❄️', + 'description' => '冰蓝晶光在文字表面流转,如同冰晶折射出的冷艳光泽。', + 'price' => 8000, 'type' => 'msg_text_color', 'duration_days' => 1, 'sort_order' => 69], // ── 头像框装扮 ──────────────────────────── ['name' => '月银守护头像框', 'slug' => 'avatar_frame_silver', 'icon' => '🥈', 'description' => '银白金属光泽外框,低调但比普通头像更精致。', - 'price' => 500, 'type' => 'avatar_frame', 'duration_days' => 7, 'sort_order' => 70], + 'price' => 5000, 'type' => 'avatar_frame', 'duration_days' => 1, 'sort_order' => 70], ['name' => '金辉勋章头像框', 'slug' => 'avatar_frame_gold', 'icon' => '🥇', 'description' => '金色勋章质感外框,带柔和光晕。', - 'price' => 1000, 'type' => 'avatar_frame', 'duration_days' => 7, 'sort_order' => 71], + 'price' => 5000, 'type' => 'avatar_frame', 'duration_days' => 1, 'sort_order' => 71], ['name' => '星轨环绕头像框', 'slug' => 'avatar_frame_star', 'icon' => '⭐', 'description' => '星轨渐变环绕头像旋转,适合高调展示。', - 'price' => 2000, 'type' => 'avatar_frame', 'duration_days' => 14, 'sort_order' => 72], + 'price' => 8000, 'type' => 'avatar_frame', 'duration_days' => 1, 'sort_order' => 72], ['name' => '龙焰御守头像框', 'slug' => 'avatar_frame_dragon', 'icon' => '🐉', 'description' => '红金御守质感外框,带虚线纹理和强烈光晕。', - 'price' => 5000, 'type' => 'avatar_frame', 'duration_days' => 30, 'sort_order' => 73], + 'price' => 8000, 'type' => 'avatar_frame', 'duration_days' => 1, 'sort_order' => 73], ]; foreach ($items as $item) { diff --git a/resources/js/chat-room/shop-controls.js b/resources/js/chat-room/shop-controls.js index bf6ad75..b8e5f50 100644 --- a/resources/js/chat-room/shop-controls.js +++ b/resources/js/chat-room/shop-controls.js @@ -58,6 +58,11 @@ const DECORATION_GROUPS = [ desc: "同类型只保留最新购买", type: "msg_name_color", }, + { + label: "🌈 文字颜色", + desc: "同类型只保留最新购买", + type: "msg_text_color", + }, { label: "🖼️ 头像框", desc: "同类型只保留最新购买", @@ -68,6 +73,7 @@ const DECORATION_GROUPS = [ const DECORATION_TYPE_TO_SLOT = { msg_bubble: "bubble", msg_name_color: "name_color", + msg_text_color: "text_color", avatar_frame: "avatar_frame", }; diff --git a/resources/views/chat/partials/scripts.blade.php b/resources/views/chat/partials/scripts.blade.php index 56e9bb8..75fb0a6 100644 --- a/resources/views/chat/partials/scripts.blade.php +++ b/resources/views/chat/partials/scripts.blade.php @@ -170,6 +170,82 @@ 50% { text-shadow: 0 0 10px #fbbf24, 0 0 16px #ef4444; } } + /* ========== 消息文字颜色特效 ========== */ + .msg-text--rainbow { + background: linear-gradient(90deg, + #ef4444, #f97316, #eab308, #22c55e, #06b6d4, #3b82f6, #a855f7, #ef4444); + background-size: 300% 100%; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + font-weight: 600; + animation: text-rainbow 3.5s linear infinite; + } + @keyframes text-rainbow { + 0% { background-position: 0% 50%; } + 100% { background-position: 300% 50%; } + } + + .msg-text--shimmer { + background: linear-gradient(110deg, + #6b7280 0%, #9ca3af 18%, #f3f4f6 28%, #d1d5db 36%, + #6b7280 52%, #9ca3af 66%, #f9fafb 74%, #6b7280 100%); + background-size: 300% 100%; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + font-weight: 600; + animation: text-shimmer 4s ease-in-out infinite; + } + @keyframes text-shimmer { + 0% { background-position: 100% 50%; } + 100% { background-position: -200% 50%; } + } + + .msg-text--neon { + color: #a855f7 !important; + font-weight: 600; + text-shadow: + 0 0 7px #a855f7, + 0 0 14px #7c3aed, + 0 0 28px #6366f1, + 0 0 42px #4f46e5; + animation: text-neon 2s ease-in-out infinite alternate; + } + @keyframes text-neon { + 0% { text-shadow: 0 0 7px #a855f7, 0 0 14px #7c3aed, 0 0 28px #6366f1; } + 100% { text-shadow: 0 0 14px #c084fc, 0 0 28px #a855f7, 0 0 48px #7c3aed, 0 0 64px #6366f1; } + } + + .msg-text--flame { + background: linear-gradient(180deg, #fef3c7 0%, #f59e0b 28%, #ea580c 60%, #dc2626 100%); + background-size: 100% 200%; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + font-weight: 700; + animation: text-flame 1.2s ease-in-out infinite alternate; + } + @keyframes text-flame { + 0% { background-position: 0% 0%; } + 100% { background-position: 0% 100%; } + } + + .msg-text--ice { + background: linear-gradient(135deg, #e0f2fe 0%, #7dd3fc 25%, #38bdf8 50%, #bae6fd 75%, #e0f2fe 100%); + background-size: 200% 200%; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + font-weight: 600; + animation: text-ice 3s ease-in-out infinite; + filter: drop-shadow(0 0 4px rgba(56, 189, 248, 0.5)); + } + @keyframes text-ice { + 0%, 100% { background-position: 0% 50%; } + 50% { background-position: 100% 50%; } + } + /* ========== 头像框 ========== */ .avatar-frame-wrapper { position: relative; @@ -1630,15 +1706,16 @@ /** * 构建普通聊天消息的正文区域,支持缩略图与过期占位渲染。 */ - function buildChatMessageContent(msg, fontColor) { + function buildChatMessageContent(msg, fontColor, textColorClass) { const rawContent = msg.content || ''; if (msg.message_type === 'image' && !isExpiredChatImageMessage(msg)) { const fullUrl = escapeHtml(msg.image_url || ''); const thumbUrl = escapeHtml(msg.image_thumb_url || ''); const imageName = escapeHtml(msg.image_original_name || '聊天图片'); + const captionColorStyle = textColorClass ? '' : `color:${fontColor};`; const captionHtml = rawContent ? - `${rawContent}` : + `${rawContent}` : ''; return ` @@ -1654,8 +1731,9 @@ } if (msg.message_type === 'expired_image' || isExpiredChatImageMessage(msg)) { + const captionColorStyle = textColorClass ? '' : `color:${fontColor};`; const captionHtml = rawContent ? - `${rawContent}` : + `${rawContent}` : ''; return ` @@ -2368,6 +2446,11 @@ nameClass = ' msg-name--' + msg.msg_name_color.replace(/^msg_name_/, ''); } + var textColorClass = ''; + if (msg.msg_text_color) { + textColorClass = ' msg-text--' + msg.msg_text_color.replace(/^msg_text_/, ''); + } + // 系统用户名列表(不可被选为聊天对象) const systemUsers = ['钓鱼播报', '星海小博士', '系统传音', '系统公告', '送花播报', '系统', '欢迎', '系统播报', '神秘箱子']; @@ -2464,7 +2547,7 @@ } const headImg = ``; - const messageBodyHtml = buildChatMessageContent(msg, fontColor); + const messageBodyHtml = buildChatMessageContent(msg, fontColor, textColorClass); let html = ''; @@ -2559,7 +2642,7 @@ return '【' + clickableUser(uName, '#000099') + '】'; }); html = - `${headImg}${clickableUser(msg.from_user, fontColor, nameClass)}:${parsedContent}`; + `${headImg}${clickableUser(msg.from_user, fontColor, nameClass)}:${parsedContent}`; } else { // 自动升级播报 / 赠礼通知 / 婚恋广播 等非游戏系统传音:金色左边框,轻量提示样式,不喧宾夺主 div.style.cssText = @@ -2593,7 +2676,7 @@ }); html = - `${headImg}${clickableUser(msg.from_user, fontColor, nameClass)}:${parsedContent}${giftHtml}`; + `${headImg}${clickableUser(msg.from_user, fontColor, nameClass)}:${parsedContent}${giftHtml}`; } } else if (msg.is_secret) { if (msg.from_user === '系统') { @@ -2610,7 +2693,7 @@ buildActionStr(msg.action, fromHtml, toHtml, '悄悄说') : `${fromHtml}对${toHtml}悄悄说:`; html = - `${headImg}${verbStr}${messageBodyHtml}`; + `${headImg}${verbStr}${messageBodyHtml}`; } } else if (msg.to_user && msg.to_user !== '大家') { // 对特定对象说话 @@ -2619,14 +2702,14 @@ const verbStr = msg.action ? buildActionStr(msg.action, fromHtml, toHtml) : `${fromHtml}对${toHtml}说:`; - html = `${headImg}${verbStr}${messageBodyHtml}`; + html = `${headImg}${verbStr}${messageBodyHtml}`; } else { // 对大家说话 const fromHtml = clickableUser(msg.from_user, '#000099', nameClass); const verbStr = msg.action ? buildActionStr(msg.action, fromHtml, '大家') : `${fromHtml}对大家说:`; - html = `${headImg}${verbStr}${messageBodyHtml}`; + html = `${headImg}${verbStr}${messageBodyHtml}`; } if (!timeStrOverride) {