diff --git a/components/hover-nav/hover-nav.vue b/components/hover-nav/hover-nav.vue index c523182..0878c7b 100644 --- a/components/hover-nav/hover-nav.vue +++ b/components/hover-nav/hover-nav.vue @@ -172,7 +172,7 @@ function getLangShortCode(langValue, customMap) { // 切回首页时按钮突然上/下跳。缓存上次算出的避让值, 让所有页面位置恒定。 let cachedAvoidBottom = null; -// H5 端需要隐藏悬浮按钮的页面路由(仅 H5, 微信小程序保持显示) +// 需要隐藏悬浮按钮的页面路由(全平台生效: H5 + 小程序) // /pages_goods/category 商品分类 // /pages_tool/contact/contact 电子名片(名片) const H5_HIDDEN_ROUTES = [ @@ -271,19 +271,14 @@ export default { fbConfig() { return (this.$store && this.$store.state && this.$store.state.floatingButtonConfig) || null; }, - // H5 端在指定页面隐藏悬浮按钮(商品分类 / 电子名片) - h5HiddenOnRoute() { - // #ifdef H5 + // 指定页面隐藏悬浮按钮(商品分类 / 电子名片), 全平台生效 + hiddenOnRoute() { const r = this.currentRoute; return H5_HIDDEN_ROUTES.some((p) => r === p || r.indexOf(p + '/') === 0); - // #endif - // #ifndef H5 - return false; - // #endif }, // 总开关 + 强制隐藏 + 页面范围 判定结果 fbVisible() { - if (this.h5HiddenOnRoute) return false; // H5 指定页面直接隐藏 + if (this.hiddenOnRoute) return false; // 指定页面直接隐藏 return shouldShowOnPage(this.fbConfig, this.currentRoute, true); }, // 最终要渲染的按钮 (已按 button_order 排序 + 显隐过滤); null = 用老行为 @@ -430,20 +425,35 @@ export default { containerBottom() { const space = 18; const base = this.layoutTopBottom + this.layoutTopSize + space; - // 避让公告栏(diy-notice 跑马灯): 把悬浮按钮整体放到公告栏"正下方", - // 按钮 top 紧贴公告栏底边之下,互不遮挡。公告栏是页面流/固定底部的 DIY - // 组件,位置与高度动态,这里用实时测量的视口坐标计算其底边距视口底部的距离。 + // 避让公告栏(diy-notice 跑马灯): 把悬浮按钮整体放到公告栏"正下方"。 + // 关键:用「文档坐标」而非「视口坐标」算 bottom —— + // 公告栏随页面内容滚动, 其视口坐标(rect.bottom)随滚动变化; + // 若直接 (winH - rect.bottom) 算, 滚动时按钮会跟着公告栏上下移动。 + // 这里把 rect 加上当前滚动距离得到"公告栏在文档中的底边", + // 再算 (winH - 文档底边): 该值与滚动无关, 按钮固定在"页面顶部时公告栏正下方", + // 滚动时按钮静止(公告栏自行滚走), 不再来回滑动。 const rect = this.noticeRect; let target = null; if (rect && this.winW > 0 && this.winH > 0 && rect.bottom > 0 && rect.top < this.winH) { + // 当前滚动距离(H5: window.pageYOffset; 小程序无 window 时回退 0) + let scrollY = 0; + try { + if (typeof window !== 'undefined' && 'pageYOffset' in window) { + scrollY = window.pageYOffset || 0; + } else if (typeof document !== 'undefined' && document.documentElement) { + scrollY = document.documentElement.scrollTop || 0; + } + } catch (e) { scrollY = 0; } const scaleInv = 750 / this.winW; // px → 设计稿 rpx const gap = 12; // 按钮与公告栏之间的间距(设计稿 rpx) - const distFromBottom = (this.winH - rect.bottom) * scaleInv; // 公告栏底边距视口底部 - // 按钮 bottom = 公告栏底边距 + 间距 - 按钮自身高度 → 按钮 top 落在公告栏底边下方 gap 处 + // 公告栏底边在文档中的坐标 = 视口坐标 + 滚动距离(滚动无关, 恒定) + const docBottom = rect.bottom + scrollY; + const distFromBottom = (this.winH - docBottom) * scaleInv; + // 按钮 bottom = 文档底边距视口底部 + 间距 - 按钮自身高度 const below = distFromBottom - gap - this.containerHeightNum; if (Number.isFinite(below)) { target = Math.max(0, below); - cachedAvoidBottom = target; // 缓存: 跨页面/重挂载复用, 保证位置恒定不跳动 + cachedAvoidBottom = target; // 缓存: 跨页面/重挂载复用, 位置恒定 } } // 无公告栏的页面(如分类页)复用上一次算出的避让值, 使悬浮按钮在所有页面位置一致 @@ -634,9 +644,10 @@ export default { setTimeout(() => this.measureNotice(), 800), ]; // #ifdef H5 - // 只在挂载时测量一次公告栏位置, 算出固定的 bottom 后: - // - 不监听 scroll —— 否则每次滚动都重测并重算 containerBottom, 按钮会上下跳动 - // - 仅监听 resize(旋转/尺寸变化,极少触发), 让按钮在视口尺寸变化时重新避让 + // 不再监听 scroll: containerBottom 已改用「文档坐标」(视口坐标 + 滚动距离)计算, + // 得到与滚动无关的恒定 bottom —— 按钮固定在"页面顶部时公告栏正下方", + // 滚动时按钮静止、公告栏自行滚走, 不再来回滑动。 + // 仅监听 resize(旋转/尺寸变化), 让按钮在视口尺寸变化时重新避让一次。 this._scrollHandler = this._onScroll.bind(this); window.addEventListener('resize', this._scrollHandler); // #endif @@ -784,6 +795,10 @@ export default { 'min-height': s, 'max-width': s, 'max-height': s, + // 内部图标(.iconfont/.icox)通过该变量跟随按钮尺寸: + // 图标盒子里原本锁死 72rpx, 按钮调大后图标仍 72rpx, + // 又因列 flex 横轴默认贴左 → 图标偏在按钮左上角 + '--btn-size': s, 'aspect-ratio': '1', // border-radius 保留作 clip-path 不支持时的 fallback 'border-radius': '50%', @@ -1117,6 +1132,9 @@ export default { .btn-item { display: flex; justify-content: center; + /* 横向居中: 列布局下横轴是 align-items, 固定宽度的子元素 + * (如锁死尺寸的 .iconfont 图标盒)默认贴左, 必须显式居中 */ + align-items: center; text-align: center; flex-direction: column; line-height: 1; @@ -1182,11 +1200,11 @@ export default { display: flex; justify-content: center; align-items: center; - /* 不再设 width: 100% / height: 100% 再覆盖为 72rpx 的写法, - * 微信小程序/H5 对 width: 100% 在 flex 列布局里的高度计算有差异, - * 直接固定 72x72 更稳。 */ - width: 72rpx; - height: 72rpx; + /* 尺寸跟随所在按钮: 按钮内联样式写入 --btn-size(=按钮实际尺寸), + * 图标盒不再锁死 72rpx —— 否则按钮调大后图标偏小且贴左上。 + * 72rpx 兜底 = 按钮未走 btnItemStyle 内联样式时的默认尺寸。 */ + width: var(--btn-size, 72rpx); + height: var(--btn-size, 72rpx); margin: 0; border-radius: 50%; padding: 0;