From 7ba7b34ca75f8d2854bd6cac4a990ef9270d2791 Mon Sep 17 00:00:00 2001 From: pllx Date: Thu, 30 Apr 2026 11:07:46 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A1=A5=E5=85=85=E5=BA=A7=E9=A9=BE=E5=8A=A8?= =?UTF-8?q?=E7=94=BB=E6=A0=87=E9=A2=98=E7=94=A8=E6=88=B7=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Events/EffectBroadcast.php | 3 +++ app/Http/Controllers/ChatController.php | 3 +++ app/Services/RideService.php | 1 + resources/js/chat-room/chat-events.js | 1 + resources/js/effects/99a.js | 17 +++++++++++------ resources/js/effects/df5c.js | 17 +++++++++++------ resources/js/effects/fujian.js | 17 +++++++++++------ resources/js/effects/j35.js | 17 +++++++++++------ tests/Feature/ChatControllerTest.php | 4 ++++ 9 files changed, 56 insertions(+), 24 deletions(-) diff --git a/app/Events/EffectBroadcast.php b/app/Events/EffectBroadcast.php index e9bf8a9..a7e65cb 100644 --- a/app/Events/EffectBroadcast.php +++ b/app/Events/EffectBroadcast.php @@ -41,6 +41,7 @@ class EffectBroadcast implements ShouldBroadcastNow * @param string|null $giftMessage 附带赠言 * @param string|null $effectTitle 特效画面标题 * @param string|null $rideName 座驾名称 + * @param string|null $effectUserInfo 特效画面用户身份信息 */ public function __construct( public readonly int $roomId, @@ -50,6 +51,7 @@ class EffectBroadcast implements ShouldBroadcastNow public readonly ?string $giftMessage = null, public readonly ?string $effectTitle = null, public readonly ?string $rideName = null, + public readonly ?string $effectUserInfo = null, ) {} /** @@ -79,6 +81,7 @@ class EffectBroadcast implements ShouldBroadcastNow 'gift_message' => $this->giftMessage, 'effect_title' => $this->effectTitle, 'ride_name' => $this->rideName, + 'effect_user_info' => $this->effectUserInfo, ]; } } diff --git a/app/Http/Controllers/ChatController.php b/app/Http/Controllers/ChatController.php index 828b052..2bf829d 100644 --- a/app/Http/Controllers/ChatController.php +++ b/app/Http/Controllers/ChatController.php @@ -251,6 +251,7 @@ class ChatController extends Controller 'ride_key' => $ridePresencePayload['ride_key'], 'ride_name' => $ridePresencePayload['ride_name'], 'effect_title' => $ridePresencePayload['effect_title'], + 'effect_user_info' => $ridePresencePayload['effect_user_info'], 'sent_at' => now()->toDateTimeString(), ]; @@ -263,11 +264,13 @@ class ChatController extends Controller $user->username, effectTitle: $ridePresencePayload['effect_title'], rideName: $ridePresencePayload['ride_name'], + effectUserInfo: $ridePresencePayload['effect_user_info'], ))->toOthers(); $initialRideEffect = $ridePresencePayload['ride_key']; $initialRideEffectOptions = [ 'effect_title' => $ridePresencePayload['effect_title'], + 'effect_user_info' => $ridePresencePayload['effect_user_info'], 'ride_name' => $ridePresencePayload['ride_name'], 'operator' => $user->username, ]; diff --git a/app/Services/RideService.php b/app/Services/RideService.php index 5059c50..661e11d 100644 --- a/app/Services/RideService.php +++ b/app/Services/RideService.php @@ -242,6 +242,7 @@ class RideService 'ride_name' => $item->name, 'ride_icon' => (string) ($item->icon ?? '🚘'), 'effect_title' => "{$user->username} 乘坐【{$item->name}】闪亮登场", + 'effect_user_info' => $identitySummary['inline'], 'identity_text' => ChatContentSanitizer::htmlText($identitySummary['inline']), 'welcome_text' => ChatContentSanitizer::htmlText($rendered), ]; diff --git a/resources/js/chat-room/chat-events.js b/resources/js/chat-room/chat-events.js index 1578209..b47b7bc 100644 --- a/resources/js/chat-room/chat-events.js +++ b/resources/js/chat-room/chat-events.js @@ -484,6 +484,7 @@ export function bindChatEvents() { const myName = window.chatContext?.username; const effectOptions = { effect_title: e.detail?.effect_title, + effect_user_info: e.detail?.effect_user_info, ride_name: e.detail?.ride_name, operator, }; diff --git a/resources/js/effects/99a.js b/resources/js/effects/99a.js index 653370e..9bc4a1e 100644 --- a/resources/js/effects/99a.js +++ b/resources/js/effects/99a.js @@ -447,8 +447,9 @@ const Type99AEffect = (() => { * @param {number} h 画布高度 * @param {number} progress 播放进度 * @param {string} title 入场标题 + * @param {string} userInfo 用户身份信息 */ - function drawHud(ctx, w, h, progress, title) { + function drawHud(ctx, w, h, progress, title, userInfo) { const enter = Math.min(1, Math.max(0, (progress - 0.14) / 0.2)); const leave = Math.min(1, Math.max(0, (1 - progress) / 0.16)); const alpha = easeInOutSine(enter) * leave; @@ -462,16 +463,19 @@ const Type99AEffect = (() => { ctx.fillStyle = "rgba(28,25,23,0.66)"; ctx.strokeStyle = "rgba(253,230,138,0.72)"; ctx.lineWidth = 2; - roundRect(ctx, w * 0.5 - 320, y - 46, 640, 96, 18); + roundRect(ctx, w * 0.5 - 340, y - 56, 680, 120, 18); ctx.fill(); ctx.stroke(); ctx.fillStyle = "#fef3c7"; ctx.font = "700 16px serif"; - ctx.fillText("ZTZ-99A ARMORED FORCE", w * 0.5, y - 12); + ctx.fillText("ZTZ-99A ARMORED FORCE", w * 0.5, y - 24); + ctx.fillStyle = "#fde68a"; + ctx.font = "700 18px serif"; + ctx.fillText(userInfo, w * 0.5, y + 8, 620); ctx.fillStyle = "#ffffff"; - ctx.font = "900 38px serif"; - ctx.fillText(title, w * 0.5, y + 28, 590); + ctx.font = "900 34px serif"; + ctx.fillText(title, w * 0.5, y + 45, 620); ctx.restore(); } @@ -513,6 +517,7 @@ const Type99AEffect = (() => { const h = canvas.height; const dust = createDust(w, h); const title = String(options.effect_title || "99A主战坦克 重装入场").trim() || "99A主战坦克 重装入场"; + const userInfo = String(options.effect_user_info || "").trim(); const startTime = performance.now(); let animId = null; let finished = false; @@ -556,7 +561,7 @@ const Type99AEffect = (() => { drawDust(ctx, dust, w, progress); drawShockwave(ctx, w, h, progress); drawTank(ctx, tankX, tankY, scale, progress); - drawHud(ctx, w, h, progress, title); + drawHud(ctx, w, h, progress, title, userInfo); if (progress < 1) { animId = requestAnimationFrame(animate); diff --git a/resources/js/effects/df5c.js b/resources/js/effects/df5c.js index 35bf29a..8632602 100644 --- a/resources/js/effects/df5c.js +++ b/resources/js/effects/df5c.js @@ -270,8 +270,9 @@ const Df5cEffect = (() => { * @param {number} h 画布高度 * @param {number} progress 播放进度 * @param {string} title 入场标题 + * @param {string} userInfo 用户身份信息 */ - function drawHud(ctx, w, h, progress, title) { + function drawHud(ctx, w, h, progress, title, userInfo) { const enter = Math.min(1, Math.max(0, (progress - 0.1) / 0.18)); const leave = Math.min(1, Math.max(0, (1 - progress) / 0.14)); const alpha = easeInOutCubic(enter) * leave; @@ -283,17 +284,20 @@ const Df5cEffect = (() => { ctx.fillStyle = "rgba(15,23,42,0.68)"; ctx.strokeStyle = "rgba(248,113,113,0.72)"; ctx.lineWidth = 2; - roundRect(ctx, w * 0.5 - 330, y - 46, 660, 96, 18); + roundRect(ctx, w * 0.5 - 350, y - 56, 700, 120, 18); ctx.fill(); ctx.stroke(); ctx.shadowColor = "rgba(248,113,113,0.95)"; ctx.shadowBlur = 22; ctx.fillStyle = "#fee2e2"; ctx.font = "700 16px serif"; - ctx.fillText("DF-5C STRATEGIC LAUNCH PREVIEW", w * 0.5, y - 12); + ctx.fillText("DF-5C STRATEGIC LAUNCH PREVIEW", w * 0.5, y - 24); + ctx.fillStyle = "#fecaca"; + ctx.font = "700 18px serif"; + ctx.fillText(userInfo, w * 0.5, y + 8, 640); ctx.fillStyle = "#ffffff"; - ctx.font = "900 38px serif"; - ctx.fillText(title, w * 0.5, y + 28, 610); + ctx.font = "900 34px serif"; + ctx.fillText(title, w * 0.5, y + 45, 640); ctx.restore(); } @@ -335,6 +339,7 @@ const Df5cEffect = (() => { const h = canvas.height; const particles = createParticles(120); const title = String(options.effect_title || "东风-5C 洲际导弹 升空").trim() || "东风-5C 洲际导弹 升空"; + const userInfo = String(options.effect_user_info || "").trim(); const startTime = performance.now(); let animId = null; let finished = false; @@ -378,7 +383,7 @@ const Df5cEffect = (() => { drawLaunchPad(ctx, w, h, progress); drawExhaust(ctx, particles, tailX, tailY, progress); drawMissile(ctx, launchX, launchY, scale, progress); - drawHud(ctx, w, h, progress, title); + drawHud(ctx, w, h, progress, title, userInfo); if (progress < 1) { animId = requestAnimationFrame(animate); diff --git a/resources/js/effects/fujian.js b/resources/js/effects/fujian.js index 508b394..10e1b75 100644 --- a/resources/js/effects/fujian.js +++ b/resources/js/effects/fujian.js @@ -394,8 +394,9 @@ const FujianEffect = (() => { * @param {number} h 画布高度 * @param {number} progress 播放进度 * @param {string} title 入场标题 + * @param {string} userInfo 用户身份信息 */ - function drawHud(ctx, w, h, progress, title) { + function drawHud(ctx, w, h, progress, title, userInfo) { const enter = Math.min(1, Math.max(0, (progress - 0.12) / 0.2)); const leave = Math.min(1, Math.max(0, (1 - progress) / 0.14)); const alpha = easeInOutCubic(enter) * leave; @@ -407,17 +408,20 @@ const FujianEffect = (() => { ctx.fillStyle = "rgba(15,23,42,0.68)"; ctx.strokeStyle = "rgba(103,232,249,0.72)"; ctx.lineWidth = 2; - roundRect(ctx, w * 0.5 - 320, y - 46, 640, 96, 18); + roundRect(ctx, w * 0.5 - 340, y - 56, 680, 120, 18); ctx.fill(); ctx.stroke(); ctx.shadowColor = "rgba(103,232,249,0.95)"; ctx.shadowBlur = 20; ctx.fillStyle = "#cffafe"; ctx.font = "700 16px serif"; - ctx.fillText("FUJIAN AIRCRAFT CARRIER PREVIEW", w * 0.5, y - 12); + ctx.fillText("FUJIAN AIRCRAFT CARRIER PREVIEW", w * 0.5, y - 24); + ctx.fillStyle = "#a5f3fc"; + ctx.font = "700 18px serif"; + ctx.fillText(userInfo, w * 0.5, y + 8, 620); ctx.fillStyle = "#ffffff"; - ctx.font = "900 38px serif"; - ctx.fillText(title, w * 0.5, y + 28, 590); + ctx.font = "900 34px serif"; + ctx.fillText(title, w * 0.5, y + 45, 620); ctx.restore(); } @@ -459,6 +463,7 @@ const FujianEffect = (() => { const h = canvas.height; const waves = createWaves(w, h); const title = String(options.effect_title || "福建舰 航母入场").trim() || "福建舰 航母入场"; + const userInfo = String(options.effect_user_info || "").trim(); const startTime = performance.now(); let animId = null; let finished = false; @@ -500,7 +505,7 @@ const FujianEffect = (() => { drawBackdrop(ctx, w, h, progress); drawWaves(ctx, waves, w, progress, carrierX, carrierY, scale); drawCarrier(ctx, carrierX, carrierY, scale, progress); - drawHud(ctx, w, h, progress, title); + drawHud(ctx, w, h, progress, title, userInfo); if (progress < 1) { animId = requestAnimationFrame(animate); diff --git a/resources/js/effects/j35.js b/resources/js/effects/j35.js index 528edc1..51adc4a 100644 --- a/resources/js/effects/j35.js +++ b/resources/js/effects/j35.js @@ -337,8 +337,9 @@ const J35Effect = (() => { * @param {number} h 画布高度 * @param {number} progress 播放进度 * @param {string} title 入场标题 + * @param {string} userInfo 用户身份信息 */ - function drawHud(ctx, w, h, progress, title) { + function drawHud(ctx, w, h, progress, title, userInfo) { const enter = Math.min(1, Math.max(0, (progress - 0.13) / 0.18)); const leave = Math.min(1, Math.max(0, (1 - progress) / 0.16)); const alpha = easeInOutSine(enter) * leave; @@ -352,16 +353,19 @@ const J35Effect = (() => { ctx.fillStyle = "rgba(2,6,23,0.62)"; ctx.strokeStyle = "rgba(56,189,248,0.72)"; ctx.lineWidth = 2; - roundRect(ctx, w * 0.5 - 320, y - 46, 640, 96, 18); + roundRect(ctx, w * 0.5 - 340, y - 56, 680, 120, 18); ctx.fill(); ctx.stroke(); ctx.fillStyle = "#bae6fd"; ctx.font = "700 16px serif"; - ctx.fillText("STEALTH FIGHTER ARRIVAL", w * 0.5, y - 12); + ctx.fillText("STEALTH FIGHTER ARRIVAL", w * 0.5, y - 24); + ctx.fillStyle = "#e0f2fe"; + ctx.font = "700 18px serif"; + ctx.fillText(userInfo, w * 0.5, y + 8, 620); ctx.fillStyle = "#ffffff"; - ctx.font = "900 38px serif"; - ctx.fillText(title, w * 0.5, y + 28, 590); + ctx.font = "900 34px serif"; + ctx.fillText(title, w * 0.5, y + 45, 620); ctx.restore(); } @@ -403,6 +407,7 @@ const J35Effect = (() => { const h = canvas.height; const speedLines = createSpeedLines(w, h); const title = String(options.effect_title || "中国歼-35 破空入场").trim() || "中国歼-35 破空入场"; + const userInfo = String(options.effect_user_info || "").trim(); const startTime = performance.now(); let animId = null; let finished = false; @@ -446,7 +451,7 @@ const J35Effect = (() => { drawSpeedLines(ctx, speedLines, w, progress); drawSonicRing(ctx, w, h, progress); drawJet(ctx, jetX, jetY, scale, progress); - drawHud(ctx, w, h, progress, title); + drawHud(ctx, w, h, progress, title, userInfo); if (progress < 1) { animId = requestAnimationFrame(animate); diff --git a/tests/Feature/ChatControllerTest.php b/tests/Feature/ChatControllerTest.php index 4b4328c..ecea5fb 100644 --- a/tests/Feature/ChatControllerTest.php +++ b/tests/Feature/ChatControllerTest.php @@ -1133,10 +1133,12 @@ class ChatControllerTest extends TestCase $this->assertSame('座驾播报', $rideMessage['from_user']); $this->assertSame('j35', $rideMessage['ride_key']); $this->assertSame("{$user->username} 乘坐【歼-35测试座驾】闪亮登场", $rideMessage['effect_title']); + $this->assertSame('部门 无部门 · 职务 无职务 · 会员 普通会员', $rideMessage['effect_user_info']); $this->assertStringContainsString($user->username, $rideMessage['content']); $this->assertSame('j35', $response->viewData('initialRideEffect')); $this->assertSame([ 'effect_title' => "{$user->username} 乘坐【歼-35测试座驾】闪亮登场", + 'effect_user_info' => '部门 无部门 · 职务 无职务 · 会员 普通会员', 'ride_name' => '歼-35测试座驾', 'operator' => $user->username, ], $response->viewData('initialRideEffectOptions')); @@ -1210,8 +1212,10 @@ class ChatControllerTest extends TestCase $this->assertNotNull($rideMessage); $this->assertNull($vipPresenceMessage); $this->assertStringContainsString('部门 战备部 · 职务 🛡️ 试飞官 · 会员 👑 至尊会员', $rideMessage['content']); + $this->assertSame('部门 战备部 · 职务 🛡️ 试飞官 · 会员 👑 至尊会员', $rideMessage['effect_user_info']); $this->assertStringContainsString($user->username, $rideMessage['content']); $this->assertSame('99a', $response->viewData('initialRideEffect')); + $this->assertSame('部门 战备部 · 职务 🛡️ 试飞官 · 会员 👑 至尊会员', $response->viewData('initialRideEffectOptions')['effect_user_info']); $this->assertNull($response->viewData('initialPresenceTheme')); }