diff --git a/frontend/pages/square/components/BannerCarousel.vue b/frontend/pages/square/components/BannerCarousel.vue
index 0186824..c58eee5 100644
--- a/frontend/pages/square/components/BannerCarousel.vue
+++ b/frontend/pages/square/components/BannerCarousel.vue
@@ -48,9 +48,9 @@ const onTop3DataLoaded = (items) => {
diff --git a/frontend/pages/square/components/StarGalaxy/ScatteredRanks.vue b/frontend/pages/square/components/StarGalaxy/ScatteredRanks.vue
index fb5e371..086f1e2 100644
--- a/frontend/pages/square/components/StarGalaxy/ScatteredRanks.vue
+++ b/frontend/pages/square/components/StarGalaxy/ScatteredRanks.vue
@@ -27,7 +27,7 @@
:style="ringItemStyle(p)"
@click="handleClick(items[i])"
>
-
+
{{ formatLabel(p.rank) }}
+
+
@@ -91,6 +98,14 @@ function ringFrameSrc(rank) {
return `/static/square/galaxy/LV${rank}.png`;
}
+function baseSrc(rank) {
+ // 不同名次区间使用不同的底座:4-6 → dizuo1, 7-9 → dizuo2, 10-12 → dizuo3
+ if (rank >= 4 && rank <= 6) return "/static/square/galaxy/dizuo1.png";
+ if (rank >= 7 && rank <= 9) return "/static/square/galaxy/dizuo2.png";
+ if (rank >= 10 && rank <= 12) return "/static/square/galaxy/dizuo3.png";
+ return "";
+}
+
function handleClick(item) {
if (item) emit("cardClick", item);
}
@@ -125,6 +140,7 @@ function handleClick(item) {
cursor: pointer;
/* display: flex; */
/* flex-direction: column; */
+ will-change: transform;
animation: orbit 36s linear infinite;
}
@@ -169,6 +185,16 @@ function handleClick(item) {
position: absolute;
top: 0;
}
+
+.base-image {
+ position: absolute;
+ bottom: -24rpx;
+ left: -8rpx;
+ width: 96rpx; /* 比 cover (84rpx) 略宽,呈现"承托"感 */
+ height: 32rpx;
+ z-index: 0; /* 位于 cover 之下 */
+ pointer-events: none;
+}
diff --git a/frontend/pages/square/composables/useSpotlight.js b/frontend/pages/square/composables/useSpotlight.js
deleted file mode 100644
index c35e14a..0000000
--- a/frontend/pages/square/composables/useSpotlight.js
+++ /dev/null
@@ -1,156 +0,0 @@
-/**
- * useSpotlight - 滚动 spotlight / 边缘渐隐
- *
- * 行为:
- * - opacity 等于「元素在视口中可见的比例」
- * - 元素完全在视口内 → 1
- * - 元素被滚出一半 → 0.5
- * - 元素完全在视口外 → 0
- * - 元素比视口高、且完全覆盖视口 → 1
- *
- * 关键修复(针对 app-plus):
- * - app-plus 的 是原生 UIScrollView,WebView 内容本身不滚动。
- * getBoundingClientRect() 永远返回初始 layout 位置(不随原生滚动更新),
- * 所以基于 rAF/setTimeout + getBoundingClientRect 的方案在 app-plus 上完全无效。
- * - 用 uni.createSelectorQuery() 取位置 —— uni-app 官方 API,会桥到原生层,
- * 在 app-plus / H5 / 小程序上都能拿到当前可视位置。
- * - 异步结果用 reqId 防止乱序(连续触发时丢弃旧结果)。
- *
- * 用法(square.vue):
- * const { start, stop } = useSpotlight({ getElements })
- * onMounted(start); onUnmounted(stop)
- * // @scroll 事件也调一次 update(),作为双保险
- */
-import { getCurrentInstance } from 'vue'
-
-const SEEN_THRESHOLD = 0.5 // 透明度高于此值记为"首次出现"
-
-export function useSpotlight(options = {}) {
- const { getElements = () => [] } = options
-
- // app-plus / Vue3 组件内 createSelectorQuery 必须挂 in(public proxy),否则 selectAll 在 app 端返回空、opacity 从不生效
- const pageProxy = getCurrentInstance()?.proxy || null
-
- const seenSet = typeof WeakSet !== "undefined" ? new WeakSet() : new Set()
-
- const computeOpacity = (rect, vh) => {
- if (!rect) return 1
- if (rect.bottom <= 0 || rect.top >= vh) return 0
-
- if (rect.height >= vh && rect.top <= 0 && rect.bottom >= vh) return 1
-
- const visibleTop = Math.max(0, rect.top)
- const visibleBottom = Math.min(vh, rect.bottom)
- const visibleHeight = Math.max(0, visibleBottom - visibleTop)
- const totalHeight = rect.height
-
- if (totalHeight <= 0) return 1
- return Math.min(1, visibleHeight / totalHeight)
- }
-
- // 给每个 ref 对应的 DOM 节点加 data-spotlight-id,方便 SelectorQuery 结果映射回节点
- const idToNode = new Map()
- let nextId = 1
- const tagNodes = () => {
- const elements = getElements()
- elements.forEach((ref) => {
- if (!ref) return
- const node = ref.$el || ref
- if (!node || node.nodeType !== 1) return
- if (node.dataset && node.dataset.spotlightId) return
- const id = `sl${nextId++}`
- node.setAttribute("data-spotlight-id", id)
- idToNode.set(id, node)
- })
- }
-
- // SelectorQuery 异步:连续触发时用 reqId 丢弃旧结果
- let reqId = 0
-
- const applyRects = (rects, vh) => {
- if (!rects || !rects.length) return
- rects.forEach((rect) => {
- const id = rect && rect.dataset && rect.dataset.spotlightId
- if (!id) return
- const node = idToNode.get(id) || (typeof document !== "undefined" && document.querySelector(`[data-spotlight-id="${id}"]`))
- if (!node) return
-
- const opacity = computeOpacity(rect, vh)
- try {
- node.style.opacity = opacity.toFixed(3)
- } catch (e) {}
-
- if (opacity > SEEN_THRESHOLD && !seenSet.has(node)) {
- seenSet.add(node)
- try { node.classList.add("first-seen") } catch (e) {}
- }
- })
- }
-
- // 测高度
- const getVH = () => {
- try {
- if (typeof window !== "undefined" && window.innerHeight) return window.innerHeight
- } catch (e) {}
- try {
- const info = uni.getSystemInfoSync()
- return info.windowHeight || info.screenHeight || 667
- } catch (e) {
- return 667
- }
- }
-
- const update = () => {
- if (typeof uni === "undefined" || typeof uni.createSelectorQuery !== "function") return
-
- tagNodes()
- if (!idToNode.size) return
-
- const myReq = ++reqId
- const vh = getVH()
-
- try {
- const query = pageProxy ? uni.createSelectorQuery().in(pageProxy) : uni.createSelectorQuery()
- query.selectAll("[data-spotlight-id]").boundingClientRect()
- query.exec((res) => {
- if (myReq !== reqId) return // 旧请求,丢弃
- if (!res || !res[0]) return
- applyRects(res[0], vh)
- })
- } catch (e) {
- // SelectorQuery 不可用时不做事
- }
- }
-
- // setTimeout 递归轮询:兜底,@scroll 漏触发时仍能跑
- let timerId = null
- const tick = () => {
- update()
- timerId = setTimeout(tick, 50) // 50ms ≈ 20fps,SelectorQuery 开销小但不为零,频率不用太高
- }
-
- const start = () => {
- if (timerId) return
- timerId = setTimeout(tick, 50)
- }
-
- const stop = () => {
- if (timerId) {
- clearTimeout(timerId)
- timerId = null
- }
- }
-
- // 兼容:@scroll 触发时直接调一次(双保险,scroll 事件比 50ms 轮询更密)
- const bindScroll = () => {
- update()
- }
-
- return {
- update,
- bindScroll,
- start,
- stop,
- isH5: typeof window !== "undefined" && typeof document !== "undefined",
- }
-}
diff --git a/frontend/pages/square/square.vue b/frontend/pages/square/square.vue
index e72b642..c33beef 100644
--- a/frontend/pages/square/square.vue
+++ b/frontend/pages/square/square.vue
@@ -45,17 +45,14 @@
-
+
@@ -90,7 +85,6 @@
@@ -98,7 +92,7 @@
@@ -412,9 +343,7 @@ onUnmounted(() => {
}
/* 区域二(主Tab)+ 区域三(分类标签)+ 占位 + 回弹动效
- 已迁入 CreationGrid 组件内部(创建于 components/CreationGrid.vue)
- 这里的 .main-tab-section / .category-section / .tab-item / 等选择器被保留
- 是因为 .spotlight-ready 选择器需要它们,CSS specificity 才能匹配(容器外层应用 spotlight-ready)。 */
+ 已迁入 CreationGrid 组件内部(创建于 components/CreationGrid.vue)。 */
/* 热门分类区块 */
.hot-category-wrapper {
@@ -445,84 +374,4 @@ onUnmounted(() => {
opacity: 1;
}
}
-
-/* ========== 滚动 spotlight(仅 H5) ==========
- * JS 把 --scroll-opacity 写到每个 .spotlight 元素上;
- * 边缘渐隐由 CSS transition 平滑。
- * 非 H5 不写变量,默认 1,元素完全可见。 */
-
-
-
diff --git a/frontend/static/square/galaxy/LV3.png b/frontend/static/square/galaxy/LV3.png
index 701a276..0723bdc 100644
Binary files a/frontend/static/square/galaxy/LV3.png and b/frontend/static/square/galaxy/LV3.png differ
diff --git a/frontend/static/square/galaxy/dizuo3.png b/frontend/static/square/galaxy/dizuo3.png
index 6818920..cc7d561 100644
Binary files a/frontend/static/square/galaxy/dizuo3.png and b/frontend/static/square/galaxy/dizuo3.png differ
diff --git a/frontend/static/square/top/bj.png b/frontend/static/square/top/d78825f2ff1822841b630e360c1da04c0d7eca00.png
similarity index 100%
rename from frontend/static/square/top/bj.png
rename to frontend/static/square/top/d78825f2ff1822841b630e360c1da04c0d7eca00.png