txw/txw-mhzc-web/src/pages/index/utils/portal-figma-scale-mixin.js
2026-06-02 23:28:48 +08:00

104 lines
3.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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);
},
},
};