diff --git a/TUICallKit-Vue3/tongji/components/FoodTileIcon.vue b/TUICallKit-Vue3/tongji/components/FoodTileIcon.vue index 73451ef4..eae5d02f 100644 --- a/TUICallKit-Vue3/tongji/components/FoodTileIcon.vue +++ b/TUICallKit-Vue3/tongji/components/FoodTileIcon.vue @@ -5,7 +5,7 @@ v-if="src && !imageFailed" class="food-tile-icon__img" :src="src" - :mode="size === 'tile' ? 'aspectFill' : 'aspectFit'" + mode="aspectFit" @error="onImageError" /> @@ -71,6 +71,11 @@ function onImageError() { z-index: 2; } +/* 玻璃格上的食材图片:投影增强立体感(参考稿) */ +.food-tile-icon--tile .food-tile-icon__img { + filter: drop-shadow(0 6rpx 10rpx rgba(0, 0, 0, 0.16)); +} + .food-tile-icon--goal { width: 40rpx; height: 40rpx; diff --git a/TUICallKit-Vue3/tongji/pages/game.vue b/TUICallKit-Vue3/tongji/pages/game.vue index be42795c..c5bfc7eb 100644 --- a/TUICallKit-Vue3/tongji/pages/game.vue +++ b/TUICallKit-Vue3/tongji/pages/game.vue @@ -125,30 +125,28 @@ - + - + + + + + + + + 第 {{ levelConfig.id }} 关 + + + - - - - - - - - - - 第 {{ levelConfig.id }} 组 - - {{ scoreText }} - + @@ -156,49 +154,40 @@ - - - - 上次消除 - - - {{ lastClearedFood.giShort }} - - - - + + + + + + - - 认糖 - 点食物看含糖 · 消掉红色「高」即过关 - @@ -265,8 +254,8 @@ - - + + - {{ booster.count }} - - + + + + + {{ booster.count }} {{ booster.label }} @@ -336,8 +327,8 @@ const COL_AXES = Array.from({ length: COLS }, (_, i) => i) const BOARD_INSET_RPX = 4 /** 棋盘与底部道具栏之间的安全间隙,避免最后一行被遮挡 */ const BOARD_FOOTER_GAP_RPX = 28 -const GRID_PAD_RPX = 8 -const GRID_GAP_RPX = 8 +const GRID_PAD_RPX = 18 +const GRID_GAP_RPX = 14 const SWAP_MS = 280 const MATCH_MS = 400 const GRAVITY_MS = 300 @@ -360,6 +351,21 @@ const activeFoods = ref([]) const sessionHighFood = ref(null) const goalDefs = ref([]) +/** 解锁高糖三连所需的有效操作次数(约 8 步内才有机会消除红色) */ +const HIGH_UNLOCK_MOVES = 7 +/** 解锁前棋盘固定保留的高糖数量(<3 无法三连,故无法消除) */ +const HIGH_COUNT_LOCKED = 2 +/** 解锁后棋盘至少保留的高糖数量(够凑三连) */ +const HIGH_COUNT_UNLOCKED = 3 +/** 解锁后高糖数量上限,避免刷屏 */ +const HIGH_COUNT_MAX = 6 +/** 解锁后每次补格生成高糖的概率 */ +const HIGH_SPAWN_P = 0.16 +/** 非高糖棋子的最小种类数,保证棋盘有足够变化 */ +const MIN_NON_HIGH_TYPES = 4 +/** 本局玩家有效操作次数(成功消除的交换计为一次) */ +const playerMoves = ref(0) + function pickRandom(pool, n) { const arr = [...pool] for (let i = arr.length - 1; i > 0; i--) { @@ -379,13 +385,26 @@ function selectSessionFoods() { if (Array.isArray(fixedKeys) && fixedKeys.length) { const fixed = fixedKeys.map((k) => getFoodByKey(k)).filter(Boolean) const fixedHighs = fixed.filter((f) => f.gi === 'high') + const fixedNonHighs = fixed.filter((f) => f.gi !== 'high') + // 每关只允许一种高糖:从固定高糖里随机取 1 种 const fixedHighPick = pickRandom(fixedHighs, 1) sessionHighFood.value = fixedHighPick[0] ? cloneFoodType(fixedHighPick[0]) : null - activeFoods.value = fixed - warmUpFoodImgs(fixed) + // 非高糖种类不足时,用随机低糖补足,保证棋盘有变化 + const nonHigh = [...fixedNonHighs] + if (nonHigh.length < MIN_NON_HIGH_TYPES) { + const used = new Set([...nonHigh, ...fixedHighPick].map((f) => f.key)) + const extraLows = FOOD_LIBRARY.filter((f) => f.gi === 'low' && !used.has(f.key)) + nonHigh.push(...pickRandom(extraLows, MIN_NON_HIGH_TYPES - nonHigh.length)) + } - const goalFoods = pickRandom(fixed, 2) + const active = [...nonHigh, ...fixedHighPick] + activeFoods.value = active + warmUpFoodImgs(active) + + // 收集目标从非高糖里挑(高糖以“消除即过关”驱动) + const goalPool = nonHigh.length ? nonHigh : active + const goalFoods = pickRandom(goalPool, Math.min(2, goalPool.length)) const fixedTargets = levelConfig.value.goalTargets || [10, 12] goalDefs.value = goalFoods.map((f, i) => ({ key: f.key, food: cloneFoodType(f), target: fixedTargets[i] ?? 10 })) @@ -659,10 +678,6 @@ const goalList = computed(() => }) ) -const allGoalsDone = computed(() => - goalList.value.length > 0 && goalList.value.every((g) => g.done) -) - const glucoseStatusText = computed(() => { if (glucoseLevel.value < 30) return '血糖偏低' if (glucoseLevel.value > 70) return '血糖过高' @@ -744,7 +759,15 @@ function updateGridMetrics() { function randomFood() { const pool = activeFoods.value.length ? activeFoods.value : FOOD_LIBRARY - const pick = pool[Math.floor(Math.random() * pool.length)] + const highFood = sessionHighFood.value + // 解锁后高糖才会随补格生成;解锁前一律生成非高糖,由 ensureHighPresence 控制高糖数量 + const afterUnlock = highFood && playerMoves.value >= HIGH_UNLOCK_MOVES + if (afterUnlock && Math.random() < HIGH_SPAWN_P) { + return cloneFoodType(highFood) + } + const nonHigh = pool.filter((f) => f.gi !== 'high') + const src = nonHigh.length ? nonHigh : pool + const pick = src[Math.floor(Math.random() * src.length)] return cloneFoodType(pick) } @@ -759,6 +782,91 @@ function boardHasHighGiFood() { return false } +/** 该位置若变为高糖,是否会与现有高糖形成三连(注入时尽量避免,让玩家自己凑) */ +function wouldFormHighRun(r, c) { + const isHigh = (rr, cc) => { + const t = board.value[rr]?.[cc] + return t && !t.isRemoved && t.type.gi === 'high' + } + let h = 1 + for (let cc = c - 1; cc >= 0 && isHigh(r, cc); cc--) h++ + for (let cc = c + 1; cc < COLS && isHigh(r, cc); cc++) h++ + if (h >= 3) return true + let v = 1 + for (let rr = r - 1; rr >= 0 && isHigh(rr, c); rr--) v++ + for (let rr = r + 1; rr < ROWS && isHigh(rr, c); rr++) v++ + return v >= 3 +} + +function shuffleInPlace(arr) { + for (let i = arr.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [arr[i], arr[j]] = [arr[j], arr[i]] + } + return arr +} + +/** + * 维持棋盘上高糖(红色)食物的数量,是“约 8 步才能消除红色”的核心: + * - 解锁前(playerMoves < HIGH_UNLOCK_MOVES)固定保留 2 个,永远无法三连 → 无法消除; + * - 解锁后至少保留 3 个(散开、避免直接成行),玩家凑三连即可消除过关;上限 HIGH_COUNT_MAX。 + * 高糖只在此注入/回收,randomFood 解锁前不生成高糖。 + */ +function ensureHighPresence() { + const highFood = sessionHighFood.value + if (!highFood) return + const before = playerMoves.value < HIGH_UNLOCK_MOVES + const minTarget = before ? HIGH_COUNT_LOCKED : HIGH_COUNT_UNLOCKED + const maxCap = before ? HIGH_COUNT_LOCKED : HIGH_COUNT_MAX + + const highTiles = [] + const nonHighTiles = [] + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + const t = board.value[r]?.[c] + if (!t || t.isRemoved) continue + if (t.type.gi === 'high') highTiles.push(t) + else nonHighTiles.push(t) + } + } + + if (highTiles.length < minTarget) { + let need = minTarget - highTiles.length + const candidates = shuffleInPlace([...nonHighTiles]) + // 优先选不会立即成三连的位置 + for (const t of candidates) { + if (need <= 0) break + if (t.type.gi === 'high') continue + if (wouldFormHighRun(t.r, t.c)) continue + t.type = cloneFoodType(highFood) + need-- + } + // 仍不够再放宽限制 + for (const t of candidates) { + if (need <= 0) break + if (t.type.gi === 'high') continue + t.type = cloneFoodType(highFood) + need-- + } + bumpLayout() + } else if (highTiles.length > maxCap) { + const lowMidPool = activeFoods.value.filter((f) => f.gi !== 'high') + let remove = highTiles.length - maxCap + const shuffled = shuffleInPlace([...highTiles]) + for (const t of shuffled) { + if (remove <= 0) break + const repl = lowMidPool.length + ? lowMidPool[Math.floor(Math.random() * lowMidPool.length)] + : null + if (repl) { + t.type = cloneFoodType(repl) + remove-- + } + } + bumpLayout() + } +} + function wouldCreateInitialMatch(r, c, type, currentBoard, currentRow) { const key = type.key if (r >= 2 && currentBoard[r - 1][c]?.type.key === key && currentBoard[r - 2][c]?.type.key === key) { @@ -813,6 +921,8 @@ function initBoard() { } ensureBoardFull(next) commitBoard(next) + // 注入本关唯一高糖(解锁前固定 2 个,无法三连) + ensureHighPresence() startHintTimer() } @@ -874,6 +984,7 @@ function setupLevel(levelId) { highClearAlertVisible.value = false clearedHighFoodName.value = '' lastClearedFood.value = null + playerMoves.value = 0 tileTouchStart = null clearDragPreview() selectSessionFoods() @@ -1467,6 +1578,7 @@ function recordLastClearedFood(groups) { gi, giShort: GI_SHORT[gi] || '低糖', eatHint: formatEatLabel(t), + tip: getFoodTip(t), color: GI_COLOR[gi] || '#64748b', food: cloneFoodType(t) } @@ -1513,11 +1625,7 @@ function showLearnFx(groups) { function checkGameEnd() { if (gamePhase.value !== 'playing') return - // 无失败惩罚:认全目标或已消除场上全部高糖食物即通关 - if (allGoalsDone.value) { - endGame('won', { reason: 'goals' }) - return - } + // 唯一通关条件:消除本关唯一的高糖(红色)食物 if (sessionHighFood.value && !boardHasHighGiFood()) { endGame('won', { reason: 'high_cleared', @@ -1640,11 +1748,14 @@ async function processSwap(t1, t2, opts = {}) { const matches = findMatches() if (matches.length > 0) { movesLeft.value = Math.max(0, movesLeft.value - 1) + playerMoves.value += 1 const endedByHigh = await clearAndRefill() if (endedByHigh) { isProcessing.value = false return } + // 按解锁进度维持高糖数量(到第 7 步起补到可消除的 3 个) + ensureHighPresence() // 所有连消结束后,检查棋盘是否死锁并自动重排 await reshuffleIfStuck() } else { @@ -1887,6 +1998,8 @@ async function onBooster(booster) { return } } + // 恢复本关高糖数量,避免纤维清空高糖直接跳过“8 步”门槛 + ensureHighPresence() // fiber 改变了棋盘,保险检查是否死锁 await reshuffleIfStuck() @@ -2003,6 +2116,8 @@ async function onBooster(booster) { await delay(GRAVITY_MS) clearDropFlags() + // 维持本关高糖数量 + ensureHighPresence() // meal 自带洗牌已保证有解,但再做一次轻量检查保险 await reshuffleIfStuck() @@ -2051,7 +2166,7 @@ function initLayout() { paddingTop: `${paddingTop}px`, paddingLeft: '32rpx', paddingRight: '32rpx', - paddingBottom: '10rpx' + paddingBottom: '18rpx' } navStyle.value = { paddingRight: `${Math.max(0, headerPaddingRight - uni.upx2px(32))}px` @@ -2071,19 +2186,25 @@ function recalcBoardSize() { const query = uni.createSelectorQuery().in(instance?.proxy) query.select('.gg-header').boundingClientRect() query.select('.gg-footer').boundingClientRect() + query.select('.gg-board-hero').boundingClientRect() query.exec((res) => { try { const sys = uni.getSystemInfoSync() const header = res?.[0] const footer = res?.[1] + const hero = res?.[2] const headerBottom = Math.ceil(header?.bottom ?? header?.height ?? uni.upx2px(360)) + // 棋盘真正可用区从进度条/信息横幅之下(board-hero 顶部)算起 + const boardAreaTop = Math.ceil(hero?.top ?? headerBottom) // 底部道具栏较高,测量失败时用更保守的兜底高度,避免最后一行被遮挡 const footerTop = Math.floor(footer?.top ?? (sys.windowHeight - uni.upx2px(300))) const inset = uni.upx2px(BOARD_INSET_RPX) const footerGap = uni.upx2px(BOARD_FOOTER_GAP_RPX) - const zoneW = sys.windowWidth + // gg-main 左右内边距各 20rpx,棋盘可用宽度需相应收窄 + const sidePad = uni.upx2px(20) + const zoneW = sys.windowWidth - sidePad * 2 // 预留底部安全间隙,棋盘不贴到道具栏 - const zoneH = Math.max(0, footerTop - headerBottom - footerGap) + const zoneH = Math.max(0, footerTop - boardAreaTop - footerGap) // 5 行 × 4 列:宽高分别约束,取较小单格像素 cellSizePx.value = calcMaxCellSize(zoneW - inset * 2, zoneH - inset * 2) diff --git a/TUICallKit-Vue3/tongji/styles/game-stitch.scss b/TUICallKit-Vue3/tongji/styles/game-stitch.scss index a99b4666..6020ad01 100644 --- a/TUICallKit-Vue3/tongji/styles/game-stitch.scss +++ b/TUICallKit-Vue3/tongji/styles/game-stitch.scss @@ -5,8 +5,10 @@ .game-page { height: 100vh; height: 100dvh; - background: #f4fbf4; /* VitalMint background */ - color: #161d19; /* VitalMint text color */ + /* 参考稿:顶部高光的薄荷渐变背景 */ + background: #e9f0e9; + background-image: radial-gradient(circle at 50% -20%, #ffffff 0%, #e9f0e9 60%, #d5dcd5 100%); + color: #171d19; overflow: hidden; position: relative; display: flex; @@ -24,68 +26,72 @@ } .game-page .gg-glass { - background: rgba(255, 255, 255, 0.75); - backdrop-filter: blur(16px); - -webkit-backdrop-filter: blur(16px); - border: 1.5rpx solid rgba(255, 255, 255, 0.6); + background: rgba(255, 255, 255, 0.6); + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); + border: 1px solid rgba(255, 255, 255, 0.8); } -/* ===== Header HUD ===== */ +/* ===== Header / TopAppBar(参考稿玻璃顶栏) ===== */ .game-page .gg-header { position: fixed; top: 0; left: 0; right: 0; z-index: 50; - border-radius: 0 0 24rpx 24rpx; - box-shadow: 0 4rpx 20rpx rgba(0, 108, 73, 0.04); - background: rgba(255, 255, 255, 0.88); - backdrop-filter: blur(16px); - -webkit-backdrop-filter: blur(16px); - border-bottom: 1rpx solid rgba(0, 108, 73, 0.06); + background: rgba(255, 255, 255, 0.4); + backdrop-filter: blur(24px); + -webkit-backdrop-filter: blur(24px); + border-bottom: 1px solid rgba(255, 255, 255, 0.6); + box-shadow: 0 4rpx 24rpx rgba(0, 0, 0, 0.02); } .game-page .gg-hud-nav { display: flex; align-items: center; + justify-content: space-between; position: relative; - height: var(--menu-height, 64rpx); + min-height: var(--menu-height, 64rpx); margin-bottom: 0; } .game-page .gg-hud-control-group { display: flex; align-items: center; - gap: 10rpx; + gap: 16rpx; } +/* 玻璃圆形图标按钮 */ .game-page .gg-hud-btn { - width: var(--menu-height, 64rpx); - height: var(--menu-height, 64rpx); + width: 84rpx; + height: 84rpx; border-radius: 50%; - background: rgba(255, 255, 255, 0.85); - border: 1rpx solid rgba(0, 108, 73, 0.08); + background: rgba(255, 255, 255, 0.7); + border: 1px solid rgba(255, 255, 255, 0.9); display: flex; align-items: center; justify-content: center; flex-shrink: 0; - box-shadow: 0 4rpx 12rpx rgba(0, 108, 73, 0.03); - transition: all 0.2s ease; + box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.04), inset 0 1px 2px rgba(255, 255, 255, 0.8); + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); + transition: all 0.15s ease; } .game-page .gg-hud-btn--pressed { - opacity: 0.75; transform: scale(0.94); + background: rgba(255, 255, 255, 0.95); } .game-page .gg-hud-btn.is-muted { - opacity: 0.45; + opacity: 0.55; } .game-page .gg-hud-level-center { position: absolute; left: 50%; - transform: translateX(-50%); + top: 50%; + transform: translate(-50%, -50%); display: flex; flex-direction: column; align-items: center; @@ -93,25 +99,29 @@ } .game-page .gg-hud-level-text { - font-size: 30rpx; + font-size: 44rpx; font-weight: 800; - color: #475569; + color: #006c4b; line-height: 1.1; - letter-spacing: 1rpx; + letter-spacing: 2rpx; + text-shadow: 0 2rpx 6rpx rgba(0, 108, 75, 0.12); } .game-page .gg-hud-score { display: flex; align-items: baseline; - gap: 4rpx; + gap: 6rpx; + margin-top: 2rpx; } .game-page .gg-hud-score-val { - font-size: 44rpx; - font-weight: 900; - color: #006c49; + font-size: 68rpx; + font-weight: 800; + color: #006c4b; font-variant-numeric: tabular-nums; - line-height: 1.2; + line-height: 1; + letter-spacing: -2rpx; + text-shadow: 0 2rpx 6rpx rgba(0, 108, 75, 0.12); } .game-page .gg-hud-score.is-pop .gg-hud-score-val { @@ -119,9 +129,136 @@ } .game-page .gg-hud-score-unit { - font-size: 26rpx; + font-size: 28rpx; font-weight: 700; - color: #64748b; + color: #3d4a42; +} + +/* ===== 信息横幅(参考稿玻璃卡 + 绿色 info 图标) ===== */ +.game-page .gg-info-banner { + flex-shrink: 0; + width: 100%; + max-width: 750rpx; + margin: 0 auto 20rpx; + padding: 20rpx 24rpx; + box-sizing: border-box; + display: flex; + align-items: center; + gap: 20rpx; + border-radius: 28rpx; + background: rgba(255, 255, 255, 0.6); + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); + border: 1px solid rgba(255, 255, 255, 0.8); + box-shadow: 0 12rpx 32rpx rgba(0, 0, 0, 0.06), inset 0 1px 2px rgba(255, 255, 255, 0.9); +} + +.game-page .gg-info-banner-icon { + flex-shrink: 0; + width: 80rpx; + height: 80rpx; + border-radius: 22rpx; + display: flex; + align-items: center; + justify-content: center; + background: linear-gradient(135deg, #00a676, #006c4b); + border: 1px solid rgba(255, 255, 255, 0.2); + box-shadow: 0 8rpx 18rpx rgba(0, 108, 75, 0.28); +} + +.game-page .gg-info-banner-body { + flex: 1; + min-width: 0; +} + +.game-page .gg-info-banner-text { + font-size: 28rpx; + font-weight: 500; + color: #171d19; + line-height: 1.5; +} + +.game-page .gg-info-banner-kw { + font-size: 32rpx; + font-weight: 800; + color: #006c4b; + letter-spacing: 1rpx; +} + +/* —— 消除后:该食物的含糖 + 建议 —— */ +.game-page .gg-info-banner.is-food { + gap: 18rpx; + animation: gg-info-food-in 0.28s cubic-bezier(0.22, 1, 0.36, 1); +} +.game-page .gg-info-banner.is-food.is-low { border-color: rgba(22, 163, 74, 0.28); } +.game-page .gg-info-banner.is-food.is-mid { border-color: rgba(234, 88, 12, 0.28); } +.game-page .gg-info-banner.is-food.is-high { border-color: rgba(220, 38, 38, 0.3); } + +/* 食物图标:白底 + GI 彩色描边(覆盖默认绿色渐变) */ +.game-page .gg-info-banner-icon.is-food { + background: #ffffff; + box-shadow: 0 4rpx 12rpx rgba(15, 23, 42, 0.08), inset 0 1px 2px rgba(255, 255, 255, 0.9); + border-width: 2rpx; + border-style: solid; +} +.game-page .gg-info-banner-icon.is-food.is-low { border-color: rgba(22, 163, 74, 0.4); } +.game-page .gg-info-banner-icon.is-food.is-mid { border-color: rgba(234, 88, 12, 0.4); } +.game-page .gg-info-banner-icon.is-food.is-high { border-color: rgba(220, 38, 38, 0.45); } + +.game-page .gg-info-banner-icon.is-food .food-tile-icon--goal { + width: 60rpx; + height: 60rpx; +} + +.game-page .gg-info-food-head { + display: flex; + align-items: center; + gap: 14rpx; + margin-bottom: 8rpx; +} + +.game-page .gg-info-food-name { + font-size: 34rpx; + font-weight: 900; + color: #171d19; + line-height: 1.2; + max-width: 320rpx; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + +.game-page .gg-info-food-gi { + flex-shrink: 0; + display: inline-flex; + align-items: center; + padding: 4rpx 16rpx; + border-radius: 999rpx; +} +.game-page .gg-info-food-gi-text { + font-size: 24rpx; + font-weight: 800; + color: #ffffff; + letter-spacing: 1rpx; +} +.game-page .gg-info-food-gi.is-low { background: linear-gradient(135deg, #5bdda8, #00a676); box-shadow: 0 2rpx 8rpx rgba(0, 166, 118, 0.32); } +.game-page .gg-info-food-gi.is-mid { background: linear-gradient(135deg, #fbbf24, #d97706); box-shadow: 0 2rpx 8rpx rgba(217, 119, 6, 0.32); } +.game-page .gg-info-food-gi.is-high { background: linear-gradient(135deg, #f87171, #ba1a1a); box-shadow: 0 2rpx 8rpx rgba(186, 26, 26, 0.34); } + +.game-page .gg-info-food-tip { + display: block; + font-size: 26rpx; + font-weight: 600; + color: #3d4a42; + line-height: 1.4; +} +.game-page .gg-info-food-tip.is-low { color: #15803d; } +.game-page .gg-info-food-tip.is-mid { color: #c2410c; } +.game-page .gg-info-food-tip.is-high { color: #b91c1c; } + +@keyframes gg-info-food-in { + from { opacity: 0.35; transform: translateY(-6rpx); } + to { opacity: 1; transform: translateY(0); } } /* ===== 次:上次消除(名称 + 含糖量),GI 三色编码学习卡 ===== */ @@ -545,8 +682,8 @@ align-items: center; justify-content: flex-start; box-sizing: border-box; - overflow: hidden; - padding: 8rpx 1rpx 0; + overflow: visible; + padding: 14rpx 20rpx 0; } /* gg-hint-banner 已移除,提示直接显示在棋盘食物上 */ @@ -580,17 +717,16 @@ .game-page .game-grid { display: flex; flex-direction: column; - border-radius: 36rpx; - background: rgba(255, 255, 255, 0.92); - backdrop-filter: blur(24px); - -webkit-backdrop-filter: blur(24px); + border-radius: 48rpx; + background: rgba(255, 255, 255, 0.6); + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); box-shadow: - 0 20rpx 56rpx rgba(0, 108, 73, 0.14), - 0 4rpx 16rpx rgba(15, 23, 42, 0.06), - inset 0 2rpx 12rpx rgba(255, 255, 255, 0.85); - border: 2rpx solid rgba(0, 108, 73, 0.12); + 0 12rpx 32rpx rgba(0, 0, 0, 0.06), + inset 0 1px 2px rgba(255, 255, 255, 0.9); + border: 1px solid rgba(255, 255, 255, 0.8); box-sizing: border-box; - overflow: hidden; + overflow: visible; flex-shrink: 0; } @@ -614,14 +750,17 @@ inset: 0; width: 100%; height: 100%; - border-radius: 0; + border-radius: 28rpx; display: flex; align-items: stretch; justify-content: stretch; - background: transparent; - box-shadow: none; - border: none; - transition: transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.2s; + background: linear-gradient(135deg, rgba(255, 255, 255, 0.9), rgba(244, 251, 245, 0.6)); + box-shadow: + 0 10rpx 20rpx rgba(0, 108, 75, 0.08), + inset 0 2rpx 4rpx rgba(255, 255, 255, 0.8), + inset 0 -2rpx 6rpx rgba(0, 0, 0, 0.02); + border: 1px solid rgba(255, 255, 255, 0.9); + transition: transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.25s ease, opacity 0.2s; z-index: 1; box-sizing: border-box; overflow: visible; @@ -654,13 +793,13 @@ } .game-page .game-tile.is-selected { - transform: scale(1.12); + transform: scale(1.08); z-index: 20; - border: none; - box-shadow: none; - outline: 4rpx solid #006c49; - outline-offset: -2rpx; - border-radius: 8rpx; + border: 2px solid #00a676; + border-radius: 28rpx; + box-shadow: + 0 0 20rpx rgba(0, 166, 118, 0.35), + inset 0 0 10rpx rgba(0, 166, 118, 0.12); } .game-page .game-tile--pressed { @@ -863,31 +1002,39 @@ /* ===== 棋子含糖等级常驻角标(认知核心:一眼看出高/中/低糖) ===== */ .game-page .gg-tile-gi { position: absolute; - top: 2rpx; - left: 2rpx; - z-index: 3; - min-width: 30rpx; - height: 30rpx; - padding: 0 6rpx; + top: -12rpx; + left: 50%; + transform: translateX(-50%); + z-index: 5; + min-width: 40rpx; + padding: 6rpx 16rpx; border-radius: 999rpx; display: flex; align-items: center; justify-content: center; font-size: 22rpx; - font-weight: 900; + font-weight: 800; color: #ffffff; line-height: 1; + letter-spacing: 1rpx; pointer-events: none; - box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.25); - border: 2rpx solid rgba(255, 255, 255, 0.9); + border: 1px solid rgba(255, 255, 255, 0.6); + text-shadow: 0 1rpx 2rpx rgba(0, 0, 0, 0.2); + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); } -.game-page .gg-tile-gi.is-low { background: #16a34a; } -.game-page .gg-tile-gi.is-mid { background: #e67e22; } +.game-page .gg-tile-gi.is-low { + background: linear-gradient(135deg, rgba(91, 221, 168, 0.95), rgba(0, 166, 118, 0.95)); + box-shadow: 0 4rpx 12rpx rgba(0, 166, 118, 0.4), inset 0 2rpx 4rpx rgba(255, 255, 255, 0.4); +} +.game-page .gg-tile-gi.is-mid { + background: linear-gradient(135deg, rgba(251, 191, 36, 0.95), rgba(217, 119, 6, 0.95)); + box-shadow: 0 4rpx 12rpx rgba(217, 119, 6, 0.4), inset 0 2rpx 4rpx rgba(255, 255, 255, 0.4); +} .game-page .gg-tile-gi.is-high { - background: #dc2626; - box-shadow: 0 2rpx 10rpx rgba(220, 38, 38, 0.45); - font-size: 22rpx; + background: linear-gradient(135deg, rgba(248, 113, 113, 0.95), rgba(186, 26, 26, 0.95)); + box-shadow: 0 4rpx 12rpx rgba(186, 26, 26, 0.4), inset 0 2rpx 4rpx rgba(255, 255, 255, 0.4); } /* ===== 点击食物的大字科普卡 ===== */ @@ -980,97 +1127,96 @@ right: 0; bottom: 0; z-index: 40; - border-radius: 48rpx 48rpx 0 0; - border-top: 1rpx solid rgba(0, 108, 73, 0.06); - box-shadow: 0 -4rpx 24rpx rgba(0, 108, 73, 0.04); - padding-top: 12rpx; + border-radius: 64rpx 64rpx 0 0; + border-top: 1px solid #ffffff; + box-shadow: 0 -12rpx 40rpx rgba(0, 0, 0, 0.08); + padding-top: 24rpx; padding-left: 24rpx; padding-right: 24rpx; - background: rgba(255, 255, 255, 0.78); -} - -.game-page .gg-footer--sub .gg-booster-btn { - padding: 16rpx 0; - width: 152rpx; - border-radius: 32rpx; - background: rgba(255, 255, 255, 0.75); - border-color: rgba(0, 108, 73, 0.08); - box-shadow: 0 4rpx 12rpx rgba(0, 108, 73, 0.04); -} - -.game-page .gg-footer--sub .gg-booster-label { - font-size: 24rpx; - color: #64748b; + background: rgba(255, 255, 255, 0.7); + backdrop-filter: blur(28px); + -webkit-backdrop-filter: blur(28px); } .game-page .gg-boosters { display: flex; justify-content: space-around; - align-items: center; + align-items: flex-start; max-width: 750rpx; margin: 0 auto; } +/* 单个道具:图标卡 + 角标 + 文字 */ .game-page .gg-booster-btn { - position: relative; display: flex; flex-direction: column; align-items: center; - padding: 22rpx 0; - width: 176rpx; - background: rgba(255, 255, 255, 0.85); - border: 1.5rpx solid rgba(0, 108, 73, 0.12); - border-radius: 40rpx; - box-shadow: 0 8rpx 20rpx rgba(0, 108, 73, 0.03), inset 0 2rpx 4rpx rgba(255, 255, 255, 0.8); - transition: all 0.25s cubic-bezier(0.34, 1.56, 0.64, 1); + padding: 8rpx 0; + width: 184rpx; + background: transparent; + border: none; + transition: transform 0.2s ease; } -.game-page .gg-booster-btn--pressed { +.game-page .gg-booster-tile { + position: relative; + width: 112rpx; + height: 112rpx; + border-radius: 36rpx; + display: flex; + align-items: center; + justify-content: center; + background: rgba(255, 255, 255, 0.85); + border: 1px solid #ffffff; + box-shadow: 0 8rpx 18rpx rgba(0, 0, 0, 0.08), inset 0 1px 2px rgba(255, 255, 255, 0.9); + transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.2s ease; +} + +.game-page .gg-booster-btn--pressed .gg-booster-tile { transform: scale(0.92); - background: rgba(255, 255, 255, 0.65); + box-shadow: 0 4rpx 10rpx rgba(0, 0, 0, 0.06); } .game-page .gg-booster-btn.is-disabled { - opacity: 0.35; + opacity: 0.4; filter: grayscale(100%); pointer-events: none !important; } .game-page .gg-booster-count { position: absolute; - top: -10rpx; - right: -10rpx; - width: 52rpx; - height: 52rpx; - border-radius: 50%; + top: -12rpx; + right: -12rpx; + min-width: 50rpx; + height: 50rpx; + padding: 0 10rpx; + border-radius: 26rpx; color: #fff; - font-size: 30rpx; - font-weight: 900; + font-size: 28rpx; + font-weight: 800; display: flex; align-items: center; justify-content: center; - border: 4rpx solid #fff; - box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.12); + border: 3rpx solid #ffffff; + box-shadow: 0 4rpx 10rpx rgba(0, 0, 0, 0.18); + text-shadow: 0 1rpx 1rpx rgba(0, 0, 0, 0.2); z-index: 2; } -.game-page .gg-booster-count.is-green { background: #10b981; } -.game-page .gg-booster-count.is-orange { background: #f97316; } -.game-page .gg-booster-count.is-rose { background: #f43f5e; } +.game-page .gg-booster-count.is-green { background: linear-gradient(135deg, #00a676, #006c4b); box-shadow: 0 4rpx 10rpx rgba(0, 166, 118, 0.3); } +.game-page .gg-booster-count.is-orange { background: linear-gradient(135deg, #f59e0b, #d97706); box-shadow: 0 4rpx 10rpx rgba(217, 119, 6, 0.3); } +.game-page .gg-booster-count.is-rose { background: linear-gradient(135deg, #ef4444, #ba1a1a); box-shadow: 0 4rpx 10rpx rgba(186, 26, 26, 0.3); } .game-page .gg-booster-icon { - margin-bottom: 8rpx; - transition: transform 0.25s ease; -} - -.game-page .gg-booster-btn--pressed .gg-booster-icon { - transform: scale(0.85); + transition: transform 0.2s ease; } .game-page .gg-booster-label { + margin-top: 14rpx; font-size: 30rpx; font-weight: 700; - color: #2b3a32; + color: #3d4a42; + letter-spacing: 1rpx; } /* ===== 高糖消除红色警示弹窗 ===== */