104 lines
3.8 KiB
JavaScript
104 lines
3.8 KiB
JavaScript
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);
|
||
},
|
||
},
|
||
};
|