如何处理 h5 使用 iframe 嵌套页面,内外 viewport 不一致导致的缩放问题?

简介: 如何处理 h5 使用 iframe 嵌套页面,内外 viewport 不一致导致的缩放问题?

问题

相信不少的朋友都遇到这样的场景,在我们的系统中嵌入别人的页面,和在别人的系统中嵌入我们的页面。这里的第一原则是,不管是我们嵌入别人还是别人嵌入我们,我们都只能修改我们自己的页面,而其他别人的页面,我们无权修改,毕竟会有“我们嵌入其他系统的时候,都是好好的啊”和“其他人嵌入我们的系统的时候,都是可以的。”



解法

这里需要分开讨论。


我们的页面嵌入其他系统

这里有一个 iframe 的特性需要事先知晓,当内外 viewport 不一致时,将会使用主页面的 viewport。


比如,在 alita 项目中,我们使用的是现在比较稳定的高清方案。在项目中使用rem单位,通过获取设备的 dpr 即 window.devicePixelRatio,将项目中的 rem 值设置成真实的 px 值。

doc.documentElement.style.fontSize = 100 / 2 * dpr  + 'px';

然后通过设置 viewport 的 initial-scale 来进行缩放页面达到高清显示页面的效果。


比如 iphone6 中 dpr 是2,我们就会将 px 设置成 @2x,然后通过设置 initial-scale=0.5,来实现页面的适配。


通常出现内外 viewport 不一致的情况,是主系统没有使用类似的高清方案,viewport 的 initial-scale 一般是设置为 1。其实就算设置的是其他的值也没有关系,我们都可以通过在代码中获取到主页面真实的 viewport。

if (window != top ) {
    const meta = document.querySelector('meta[name="viewport"]');
    const metaStr = meta.getAttribute('content') || '';
    const viewport = getViewPort(metaStr);
   if (viewport['initial-scale']) {
          const dpr = window.devicePixelRatio || 1;
          const baseScale = 10 / dpr;
          window.alitaFontScale = baseScale / parseInt(`${parseFloat(viewport['initial-scale']) * 10}`, 10);
    }
}


先判断一下,我们的页面是在 iframe 中打开,然后用 js 的方法获取到当前页面的 viewport。这里需要注意我们前面提到的“当内外 viewport 不一致时,将会使用主页面的 viewport”。所以在这里获取到的是主页面的 viewport。


(这里的几个类型转换,纯属是我个人的强迫症,其实没有什么意义。)

到此为止,我们就获得了我们也需要的缩放比例,将这个值放入我们最开始的 base fontsize 的计算中,就可以处理缩放问题了。

doc.documentElement.style.fontSize = 100 / 2 * dpr * window.alitaFontScale  + 'px';


其他的页面嵌入我们的页面中。

理解了上面的逻辑,那要处理这个问题就比较容易了。如果其他页面也使用类似的高清方案或者其他适配方案,理论上他们会处理这样的异常情况。在我们实际交互场景中,我们只遇到子系统 initial-scale=1 的情况,因此我这里也仅仅针对这个情况来解决问题。


(这也仅仅是我个人的设计理念问题,做的少就是做的多。)

比如在iphone 6 上,我们使用的是 @2x 的 px 值,但是子系统用的是 @1x 。因此我们只需要将 iframe 的大小设置成我们需要的 1 半,然后通过 transform scale 放大一倍就可以解决。


const Iframe: FC<IframeProps> = (props) => {
  const stt = {
    border: 'none',
    width: `50vw`,
    height: `50vh`,
    transformOrigin: '0 0',
    transform: `scale(2)`,
  };
  return <iframe style={stt} src={url}></iframe>;
};


套娃模式

在有其他系统嵌入在我们的系统的情况下,我们的系统嵌入到其他系统中。(这里有点绕,反正就是嵌套里面又嵌套) 不知道你有没有留意到,在前面的实现中我们使用了一个全局变量,window.alitaFontScale。可以通过它来判断是否是套娃,和我们项目当前已经做出的修改情况。

import React from 'react';
import type { FC, IframeHTMLAttributes } from 'react';
interface AlitaIframeProps extends IframeHTMLAttributes<HTMLIFrameElement> {
  /**
   * 0~100
   */
  width?: number;
  /**
   * 0~100
   */
  height?: number;
}
const AlitaIframe: FC<AlitaIframeProps> = (props) => {
  const { width = 100, height = 100, style, ...rest } = props;
  (window as any).alitaFontScale = (window as any).alitaFontScale ?? 1;
  const defaultStyle = {
    border: 'none',
    ...style,
    width: `${1 / (window as any).alitaFontScale * 50 * width / 100}vw`,
    height: `${1 / (window as any).alitaFontScale * 50 * height / 100}vh`,
    transformOrigin: '0 0',
    transform: `scale(${(window as any).alitaFontScale / 0.5})`,
  };
  return <iframe style={defaultStyle} {...rest}></iframe>;
};
export default AlitaIframe;



其他

本着 alita 体系的设计理念,框架中做的越多,实际交付开发中就写的越少。

我将第一种情况内收到了 hd 的插件中,适用于 alita 项目和其他使用了 @alitajs/hd 插件的 umi 项目。框架自己兼容,用户无需理会差异。

将第二种情况,发布到了 @alitajs/iframe 组件。在项目中可以直接引入使用,用户无需理会过多的逻辑。

$ yarn add @alitajs/iframe
or
$ npm i @alitajs/iframe
import React from 'react';
import Iframe  from '@alitajs/iframe';
const otherUrl = 'http://localhost:8000/#/';
export default () => <Iframe src={otherUrl} />;



总结

解法固然重要,更有趣的是过程。其实我们在实际开发中,都会遇到一些问题,或简单或复杂,或通用或局限,但是如果能够把问题梳理清楚,将代码保留在更高一个层级的位置,服务自己,服务他人,甚至能让整个团队受益。

想象一下,以后在 React 项目中,只要使用 Iframe 打开其他人的页面,都不会再遇到缩放问题了,是不是很帅。

当然这只是理想的情况,毕竟这是我昨天写的代码,我只能保证它在昨天能够正确运行。


目录
相关文章
|
3月前
|
JavaScript
子组件获取外层组件的scrollTop,达到实时定位的效果
本文介绍了如何通过Vue的自定义事件总线(eventBus),实现子组件获取外层组件的scrollTop值,并达到实时定位的效果。
35 1
子组件获取外层组件的scrollTop,达到实时定位的效果
|
5月前
|
前端开发 JavaScript 应用服务中间件
iframe动态操作标签分享
iframe动态操作标签分享
52 0
|
7月前
|
前端开发 JavaScript 容器
纯css实现高度或者宽度变化字体发生变化【容器查询】
纯css实现高度或者宽度变化字体发生变化【容器查询】
119 0
|
前端开发 JavaScript
echarts tooltip设置正常却无法显示被遮挡设置层级堆叠顺序的问题解决方案
echarts tooltip设置正常却无法显示被遮挡设置层级堆叠顺序的问题解决方案
601 0
|
前端开发
CSS层级小技巧:在滚动时自动添加头部阴影
CSS层级小技巧:在滚动时自动添加头部阴影
251 0
CSS层级小技巧:在滚动时自动添加头部阴影
|
前端开发
iframe的运用---特别是获取父子页面的元素
iframe的运用---特别是获取父子页面的元素
512 0
|
前端开发 容器
web前端学习(二十三)——CSS3定位(position)、元素裁剪(clip)及鼠标样式(cursor)属性的相关设置
web前端学习(二十三)——CSS3定位(position)、元素裁剪(clip)及鼠标样式(cursor)属性的相关设置
web前端学习(二十三)——CSS3定位(position)、元素裁剪(clip)及鼠标样式(cursor)属性的相关设置
|
前端开发
web前端学习(二十一)——CSS3分组和嵌套选择器、尺寸属性(height、width)的相关设置
web前端学习(二十一)——CSS3分组和嵌套选择器、尺寸属性(height、width)的相关设置
web前端学习(二十一)——CSS3分组和嵌套选择器、尺寸属性(height、width)的相关设置
|
JavaScript 前端开发
js获取元素到文档区域document的(横向、纵向)坐标的两种方法
js获取元素到文档区域document的(横向、纵向)坐标的两种方法
js获取元素到文档区域document的(横向、纵向)坐标的两种方法