diff --git a/src/components/cards/PluginAppCard.vue b/src/components/cards/PluginAppCard.vue index e5b1f9a5..be9779dc 100644 --- a/src/components/cards/PluginAppCard.vue +++ b/src/components/cards/PluginAppCard.vue @@ -2,7 +2,7 @@ import api from '@/api' import type { Plugin } from '@/api/types' import { getLogoUrl } from '@/utils/imageUtils' -import { getDominantColor } from '@/@core/utils/image' +import { getCardAccentRgbFromImage } from '@/composables/useCardAccentColor' import { isNullOrEmptyObject } from '@/@core/utils' import { formatDownloadCount } from '@/@core/utils/formatters' import { useToast } from 'vue-toastification' @@ -35,8 +35,8 @@ const $toast = useToast() const createConfirm = useConfirm() -// 背景颜色 -const backgroundColor = ref('#28A9E1') +// 卡片头部染色所用的图标主色(CSS 变量可直接消费的 RGB 通道值) +const accentRgb = ref('40, 169, 225') // 图片对象 const imageRef = ref() @@ -76,8 +76,8 @@ function closeInstallProgress() { async function imageLoaded() { isImageLoaded.value = true const imageElement = imageRef.value?.$el.querySelector('img') as HTMLImageElement - // 从图片中提取背景色 - backgroundColor.value = await getDominantColor(imageElement) + // 从图标中提取主色,作为卡片头部染色玻璃的色相来源 + accentRgb.value = await getCardAccentRgbFromImage(imageElement, '#28A9E1') } // 计算图标路径 @@ -236,15 +236,13 @@ onUnmounted(() => { :width="props.width" :height="props.height" @click="showPluginDetail" - class="app-hover-lift-card flex flex-col h-full" + class="plugin-card app-hover-lift-card flex flex-col h-full" :class="{ 'app-hover-lift-card--hovering': hover.isHovering, }" + :style="{ '--plugin-card-accent-rgb': accentRgb }" > -
+
() @@ -98,8 +98,8 @@ watch( async function imageLoaded() { isImageLoaded.value = true const imageElement = imageRef.value?.$el.querySelector('img') as HTMLImageElement - // 从图片中提取背景色 - backgroundColor.value = await getDominantColor(imageElement) + // 从图标中提取主色,作为卡片头部染色玻璃的色相来源 + accentRgb.value = await getCardAccentRgbFromImage(imageElement, '#28A9E1') } // 显示更新日志 @@ -573,17 +573,15 @@ watch( :width="props.width" :height="props.height" @click="handleCardClick" - class="app-hover-lift-card flex flex-col h-full" + class="plugin-card app-hover-lift-card flex flex-col h-full" :class="{ 'app-hover-lift-card--hovering': hover.isHovering && !props.sortable, 'cursor-move': props.sortable, }" + :style="{ '--plugin-card-accent-rgb': accentRgb }" :ripple="!props.sortable" > -
+
* { + position: relative; + } + + // 磨砂材质透光更强,染色需要同步减弱以免头部压过卡片本体。 + &[data-glass-appearance='frosted'] .plugin-card__banner { + --plugin-card-banner-scrim: linear-gradient( + rgba(11, 19, 34, calc(0.24 - var(--glass-transparency, 0.5) * 0.12)) 0%, + rgba(11, 19, 34, calc(0.34 - var(--glass-transparency, 0.5) * 0.14)) 100% + ); + --plugin-card-banner-tint: linear-gradient( + 135deg, + rgba(var(--plugin-card-accent-rgb), 0.32) 0%, + rgba(var(--plugin-card-accent-rgb), 0.18) 58%, + rgba(var(--plugin-card-accent-rgb), 0.08) 100% + ); + } + + // 色调材质本身带主色语义,头部染色向主色收敛保持整页同一色温。 + &[data-glass-appearance='tinted'] .plugin-card__banner { + --plugin-card-banner-tint: linear-gradient( + 135deg, + color-mix(in srgb, rgba(var(--plugin-card-accent-rgb), 0.42) 74%, rgba(var(--v-theme-primary), 0.42)) 0%, + color-mix(in srgb, rgba(var(--plugin-card-accent-rgb), 0.24) 74%, rgba(var(--v-theme-primary), 0.24)) 58%, + rgba(var(--plugin-card-accent-rgb), 0.12) 100% + ); + } + + // 文件夹卡片保留用户自选渐变作为色相,只降低不透明度让卡片本体的玻璃透出来。 + .plugin-folder-card__bg { + opacity: calc(0.5 - var(--glass-transparency, 0.5) * 0.14); + } + + .plugin-folder-card__bg::after { + position: absolute; + background-image: var(--glass-sheen); + box-shadow: inset 0 1px 0 var(--glass-highlight); + content: ''; + inset: 0; + pointer-events: none; + } + + .plugin-folder-card__overlay { + background: linear-gradient( + rgba(11, 19, 34, calc(0.34 - var(--glass-transparency, 0.5) * 0.14)) 0%, + rgba(11, 19, 34, calc(0.46 - var(--glass-transparency, 0.5) * 0.16)) 100% + ); + } + // 捷径菜单的嵌套卡片会被通用弹层模糊覆盖,需要显式跟随玻璃材质。 .shortcut-card-hover-area > .v-card { -webkit-backdrop-filter: var(--glass-surface-backdrop-filter) !important;