import { syncPortalFigmaStageLayout } from './portal-figma-scale'; /** * 门户 Figma 等比缩放页 * * 实现要点(参考 Vue 3 / Nuxt 3 SPA 路由切换最佳实践): * 1. 用 ResizeObserver 替代 nextTick 一次性计算 stage 高度, * 任何尺寸变化(异步数据填充、窗口缩放、字体加载)都会重算, * 不会因为单次算偏小而锁住 --home-figma-visual-height。 * 2. 路由进入前清掉全局 clamp 变量,由新页面 mount 后自己算。 * 避免旧页面残留的 visualHeight 把新页面裁切。 * 3. keep-alive 场景下 activated 重新建立观察者并立即算一次。 */ export default { mounted() { this._setupPortalFigmaScaleObserver(); }, activated() { // keep-alive 场景下,组件从缓存中恢复时不会触发 mounted, // 必须重新挂载观察者并立即算一次。 this._setupPortalFigmaScaleObserver(); }, beforeRouteUpdate() { // 路由参数变化时清掉旧视觉高度,避免 stale 值 clamp 新内容 this._resetPortalFigmaVisualHeight(); }, beforeRouteEnter(to, from, next) { // 进入路由前清掉全局 clamp 变量 if (typeof document !== 'undefined') { const root = document.documentElement; root.style.removeProperty('--home-figma-visual-height'); root.style.removeProperty('--portal-shell-min-design-height'); } next(); }, beforeDestroy() { this._teardownPortalFigmaScaleObserver(); }, deactivated() { this._teardownPortalFigmaScaleObserver(); }, methods: { _setupPortalFigmaScaleObserver() { if (typeof window === 'undefined') return; if (window.innerWidth < 768) return; this._teardownPortalFigmaScaleObserver(); this._onPortalFigmaResize = () => { window.requestAnimationFrame(() => { window.requestAnimationFrame(() => this.syncPortalFigmaStageLayout()); }); }; window.addEventListener('resize', this._onPortalFigmaResize); const stage = this.$refs.figmaStage; if (stage && typeof ResizeObserver !== 'undefined') { this._portalFigmaResizeObserver = new ResizeObserver(() => { // 防抖:短时间内多次变化只算一次 if (this._portalFigmaResizeTimer) { clearTimeout(this._portalFigmaResizeTimer); } this._portalFigmaResizeTimer = setTimeout(() => { this.syncPortalFigmaStageLayout(); }, 50); }); this._portalFigmaResizeObserver.observe(stage); } // 立即算一次,cover 首次渲染 this.$nextTick(() => this.syncPortalFigmaStageLayout()); }, _teardownPortalFigmaScaleObserver() { if (this._onPortalFigmaResize && typeof window !== 'undefined') { window.removeEventListener('resize', this._onPortalFigmaResize); this._onPortalFigmaResize = null; } if (this._portalFigmaResizeObserver) { this._portalFigmaResizeObserver.disconnect(); this._portalFigmaResizeObserver = null; } if (this._portalFigmaResizeTimer) { clearTimeout(this._portalFigmaResizeTimer); this._portalFigmaResizeTimer = null; } }, _resetPortalFigmaVisualHeight() { if (typeof document === 'undefined') return; const root = document.documentElement; root.style.removeProperty('--home-figma-visual-height'); }, syncPortalFigmaStageLayout() { if (typeof window === 'undefined') return; if (window.innerWidth < 768) return; const stage = this.$refs.figmaStage; if (!stage) return; const scale = parseFloat( getComputedStyle(document.documentElement).getPropertyValue('--home-figma-scale'), ) || (window.innerWidth / 1440); syncPortalFigmaStageLayout(stage, scale); }, }, };