vue模拟抖音

自适应调整页面-rem和vh/vw

简介使用rem适应不同终端的变化,调整页面元素的大小。
使用动态计算vh的目的是,因为移动端的地址栏的出现与消失会影响也页面视窗的高度即vh。因此需要动态计算。
图 2

核心问题是移动浏览器(Chrome 和 Safari)有一个“有用”的功能,地址栏有时可见,有时隐藏,从而改变视口的可见大小。这些浏览器并没有随着视口高度的变化而将高度调整100vh为屏幕的可见部分,而是将100vh地址栏设置为隐藏地址的浏览器高度。结果是,当地址栏可见时,屏幕的底部将被切断,从而违背了100vh最初的目的。

rem

rem 的值是根据根元素 html 字体大小的来计算的,即1rem = html font-szie
如果 html 元素没有指定字体大小,那么浏览器默认的字体大小是 16px ,所以 1rem = 16px
如果 html 元素指定 font-size: 1px ,那么 1rem = 1px
同理 html 元素指定 font-size: 3px ,那么 1rem = 3px
同理 html 元素指定 font-size: 1000000px ,那么 1rem = 1000000px
同理 html 元素指定 font-size: 0.000001px ,那么 1rem = 0.000001px

rem和em的区别

em 是以父元素的字体大小来计算; rem 顾名思义,就是 rootem, 所以它是以html的字体大小来计算

不同屏幕自适应

不同用户的设备屏幕宽度不同,若每个用户对应的 html 元素 font-size 值相同的话,用户看到的显示效果也是不同的。

可以用 JavaScript 来根据用户的屏幕宽度,动态更改 html 元素上的 font-size 值,从而使实际显示的内容比例始终保持不变,不同用户看到的效果也会保持一致。

比如,设计稿的宽度为 400px ,里面显示了一个宽度为 40px 的盒子。某用户以 800px 宽度的设备来访问,看到的盒子宽度应该为 80px。那么此时在 html 元素的 font-size 值设置为 2px ,然后盒子的宽度采用 rem 单位,设置为 40rem ,那么就能显示出 80px 的盒子了。保持用户看到的和设计稿中的效果比例一致。

所以,html元素的font-size计算公式为:

1
2
// 用户设备宽度 / 设计稿标准宽度
document.documentElement.style.fontSize = document.documentElement.clientWidth / 375 + 'px'

vh

vhvw 都是相对于视窗的宽高度,“视窗”所指为浏览器内部的可视区域大小,即window.innerWidth/window.innerHeight大小,不包含任务栏标题栏以及底部工具栏的浏览器区域大小。可以简单理解为屏幕百分比,1vh = 屏幕的1%

移动端 100vh 显示 bug

vh 在移动端的Chrome 和 Safari上会显示溢出 ,如下图

图 3

当地址栏处于视图中时,元素底部被裁剪(右),但我们想要的是元素能完整的占据一屏(左)。
造成这种现象的原因就在于移动端浏览器对于 vh 单位的计算,是不包含地址栏的,也就是说 100vh 的高度会使带有地址栏的视图溢出。

解决方法

使用 window.innerHeight 获取当前视窗的的高度,将高度按 100 份等分,得到视窗的单位高度, 然后通过 js 设置成一个 css 的变量 --vh

1
document.documentElement.style.setProperty('--vh', `${vh}px`)

css中使用

1
2
3
4
5
//表示100vh
height: calc(var(--vh, 1vh) * 100);

//100vh - 60rem
height: calc(var(--vh, 1vh) * 100 - 60rem);

代码

1
2
3
4
5
6
7
8
9
10
11
function resetVhAndPx() {
let vh = window.innerHeight * 0.01
document.documentElement.style.setProperty('--vh', `${vh}px`)
document.documentElement.style.fontSize = document.documentElement.clientWidth / 375 + 'px'
}

onMounted(() => {
resetVhAndPx()
// 监听resize事件 视图大小发生变化就重新计算1vh的值
window.addEventListener('resize',resetVhAndPx)
})

实现原理

无限滑动的原理和虚拟滚动的原理差不多,要保持 SlideList 里面永远只有 N 个 SlideItem,就要在滑动时不断的删除和增加 SlideItem。
滑动时调整 SlideList 的偏移量 translateY 的值,以及列表里那几个 SlideItem 的 top 值,就可以了

为什么要调整 SlideList 的偏移量 translateY 的值同时还要调整 SlideItem 的 top 值呢?
因为 translateY 只是将整个列表移动,如果我们列表里面的元素是固定的,不会变多和减少,那么没关系,只调整 translateY 值就可以了,上滑了几页就减几页的高度,下滑同理

但是如果整个列表向前移动了一页,同时前面的 SlideItem 也少了一个,,那么最终效果就是移动了两页…因为 塌陷 了一页
这显然不是我们想要的,所以我们还需要同时调整 SlideItem 的 top 值,加上前面少的 SlideItem 的高度,这样才能显示出正常的内容