100vh问题
**原因:**safari浏览器的100vh包含地址栏和底部栏的高度,而其他浏览器只是视口高度
解决方案:
方案一:
npm i vh-check
import vhCheck from 'vh-check'
// 设置一个css变量
vhCheck('brwoser-address-bar')
height: calc(100vh - var(--browser-address-bar, 0px))
calc(100vh - var(--browser-address-bar, 0px)) 表示100vh的实际高度
方案二:
使用js动态改变盒子高度,即使用window.innerHeight来获取实际的屏幕高度,然后赋值给需要改变的元素 即xxxx.style.height = window.innerHeight +'px’
参考文章
移动端100vh的问题与解决方案 - 夏如眷 - 博客园 (cnblogs.com)
(28条消息) 踩坑记录,移动端开发使用height=100vh_sha虫剂的博客-CSDN博客_移动端高度100%
1px问题
原因
做移动端页面时一般都会设置meta viewport的content=“width=device-width”,但css最低只支持1px,不足1px 会显示为1px。而如果设计稿是750px,750px的1px,在375px的屏幕上,则需要显示为0.5px,而css不支持0.5px,会将其显示为1px(但ios新版本支持0.5px,安卓暂不支持),所以兼容问题就产生了
js获取dpr
const dpr = devicePixelRatio >= 3? 3: devicePixelRatio >= 2? 2: 1;
document.documentElement.setAttribute('data-dpr', dpr);
解决方案
方案一
和设计师沟通,让设计稿中避免出现1px的值 优先沟通,从问题的根源解决
方案二
通过各种方式去兼容
方式一:伪元素 + transform: scale()缩放
原理: 在目标元素的后面追加一个 ::after 伪元素,让这个元素布局为 absolute 之后、整个伸展开铺在目标元素上,然后把它的宽和高都设置为目标元素的两倍,border值设为 1px。接着借助 CSS 动画特效中的放缩能力,把整个伪元素缩小为原来的 50%。此时,伪元素的宽高刚好可以和原有的目标元素对齐,而 border 也缩小为了 1px 的二分之一,间接地实现了 0.5px 的效果。
单边
/* 下边框 */
.div::after {
content: '';
box-sizing: border-box;
position: absolute;
z-index: 1;
left: 0;
bottom: 0;
width: 100%;
height: 2px;
border-bottom: 1px solid #bfbfbf;
transform: scaleY(1/2);
transform-origin: left bottom;
}
/* 右边框 */
.div::before {
content: '';
box-sizing: border-box;
position: absolute;
z-index: 1;
right: 0;
bottom: 0;
width: 2px;
height: 100%;
border-right: 1px solid #bfbfbf;
transform: scaleX(1/2);
transform-origin: right bottom;
}
所有边
.div::after {
content: '';
box-sizing: border-box;
position: absolute;
z-index: 1;
left: 0;
top: 0;
width: 300%;
height: 300%;
border: 1.5px solid #bfbfbf;
transform: scale(1/1.5/2);
transform-origin: left top;
border-radius: 6px;
}
参考文章
1px像素问题(一):真正原因_醉逍遥neo的博客-CSDN博客
1px像素问题(二):解决方法_醉逍遥neo的博客-CSDN博客
吃透移动端 1px|从基本原理到开源解决方案 - 知乎 (zhihu.com)
解决移动端1px边框问题的几种方法 - 掘金 (juejin.cn)
点击事件300ms延迟问题
原因
移动端要判断是否是双击,所以单击之后不能够立刻触发click,要等300ms,直到确认不是双击了才触发click。所以就导致了click有延迟。
解决方案
<meta name="viewport" content="width=device-width, initial-scale=1.0">
注意
采用此解决方案,将不会再有浏览器默认的双击缩放功能
参考文章
关于h5端点击事件300ms延迟问题 - 掘金 (juejin.cn)
关闭IOS键盘首字母大写
解决方案
<input type='text' autocapitalize="false">
参考文章
autocapitalize - HTML(超文本标记语言) | MDN (mozilla.org)
让Chrome支持小于12px的文字
解决方案:
通过 transform: scale(x) 实现
div{
font-size: 100px;
}
div span{
display: inline-block;
/*
这个span会继承div的字体大小,这里再设置为缩放为0.1倍,那就是10px。
注意:这里缩放的是盒子的大小,来间接达到显示小字体大小的效果,
实际并不是直接控制了字体大小。因此实际还是要避免设计稿中出现小于12px的字体大小
*/
transform: scale(0.1);
}
去除IOS中被触摸元素的半透明遮罩
解决方案
button,
a,
textarea,
input {
tap-highlight-color: rgba(0, 0, 0, 0);
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
禁止ios长按触发系统菜单,禁止IOS & Android 长按时下载图片
解决方案
html,body{
touch-callout: none;
-webkit-touch-callout: none;
}
禁止 IOS & Android 用户选中文字
解决方案
html,body{
user-select: none;
-webkit-user-select: none;
}
边框支持渐变
CSS实现渐变色边框(Gradient borders)的5种方法 - 掘金 (juejin.cn)
ios浏览器不支持yyyy-MM-dd HH:mm:ss或yyyy-MM-dd格式对Date进行初始化
解决方案
使用第三方工具(如:dayjs)进行日期对象和字符串的相互转换
不同设备使用不同的多倍图
问题描述
- 适用普通屏的图片在 retina 屏中,图片展示就会显得模糊;
- 适用 retina 屏的图片在普通屏中,图片展示就会缺少色差、没有锐利度,并且浪费带宽; 所以如果对性能、美观要求很高的场景,需要根据 dpr 区分使用对应的图片
原因
我们最常用的iphone6,7,8来说,他们屏幕1 css 像素就 = 2个物理像素,不仅如此,随着屏幕分辨率的提高,物理像素比也跟着提高。因此,如果我们需要在移动端上显示一张50 css像素的图片,则需要准备一张100物理像素或者更高的图片,再手动的设置为50px,才能在移动端上清楚的显示出来,否则就会出现下列效果。
scss方式
@mixin bg-image($url) {
background-image: url($url + ".png");
@media (-webkit-min-device-pixel-ratio:2),(min-device-pixel-ratio:2){
background-image: url($url + "@2x.png")
}
@media (-webkit-min-device-pixel-ratio:3),(min-device-pixel-ratio:3){
background-image: url($url + "@3x.png")
}
}
使用示例
div{
width:30px;
height:20px;
background-size:30px 20px;
background-repeat:no-repeat;
@include bg-image('special_1');
}
css方式
/*默认大小*/
.photo {background-image: url(image100.png);}
/* 如果设备像素大于等于2,则用2倍图 */
@media screen and (-webkit-min-device-pixel-ratio: 2),
screen and (min--moz-device-pixel-ratio: 2) {
.photo {
background-image: url(image200.png);
background-size: 100px 100px;
}
}
/* 如果设备像素大于等于3,则用3倍图 */
@media screen and (-webkit-min-device-pixel-ratio: 3),
screen and (min--moz-device-pixel-ratio: 3) {
.photo {
background-image: url(image300.png);
background-size: 100px 100px;
}
}
.photo {width:100px;height:100px;}
安全区适配
安全区域指的是一个可视窗口范围,处于安全区域的内容不受圆角(corners)、齐刘海(sensor housing)、小黑条(Home Indicator)影响,如下图蓝色区域:
也就是说,我们要做好适配,必须保证页面可视、可操作区域是在安全区域内。(上图的蓝色区域)
适配步骤
设置网页在可视窗口的布局方式
<meta name="viewport" content="width=device-width, viewport-fit=cover">
只有设置了 viewport-fit=cover,才能使用 env()
可以通过加内边距 padding 扩展高度
{
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
或者通过计算函数 calc 覆盖原来高度
{
height: calc(60px(假设值) + constant(safe-area-inset-bottom));
height: calc(60px(假设值) + env(safe-area-inset-bottom));
}
注意,这个方案需要吸底条必须是有背景色的,因为扩展的部分背景是跟随外容器的,否则出现镂空情况。
通过新增一个新的元素
通过新增一个新的元素(空的颜色块,主要用于小黑条高度的占位),然后吸底元素可以不改变高度只需要调整位置,像这样:
{
margin-bottom: constant(safe-area-inset-bottom);
margin-bottom: env(safe-area-inset-bottom);
}
空的颜色块:
{
position: fixed;
bottom: 0;
width: 100%;
height: constant(safe-area-inset-bottom);
height: env(safe-area-inset-bottom);
background-color: #fff;
}
部分奇特的 Android 手机
很多 Android 手机也会按照 iOS 的标准来实现安全区域,因此上面的属性在大部分 Android 手机上也能正常使用。
但是,我们在测试的过程中发现,有几个奇特的手机,会出现下图的状况:
通过 Chrome 查看样式,发现他会识别 safe-area-inset-top 等预定义变量,但又将其解析为 0。
这就导致即使我们设置了兜底的数据,也无法使用。例如我们在不支持安全区域属性时,使用兜底的 padding-top: 25PX 样式(大写的 PX 是为了不被插件转义成 vw 或 rem),但上述的 Android 设备中,兜底样式也不会生效:
body {
/* prettier-ignore */
padding-top: 25PX;
padding-top: constant(safe-area-inset-top);
padding-top: env(safe-area-inset-top);
}
解决方案
首先我们向页面中插入一个看不见的 div,将 div 的高度设置为安全距离的高度,然后再通过 js 获取其高度,若高度为 0,则说明没有生效。
let status = 0; // 0:还没数据,-1:不支持,1:支持
/**
* 判断当前设置是否支持constant(safe-area-inset-top)或env(safe-area-inset-top);
* 部分Android设备,可以认识safa-area-inset-top,但会将其识别为0
* @returns {boolean} 当前设备是否支持安全距离
*/
const supportSafeArea = (): boolean => {
if (status !== 0) {
// 缓存数据,只向 body 插入一次 dom 即可
return status === 1;
}
const div = document.createElement('div');
const id = 'test-check-safe-area';
const styles = [
'position: fixed',
'z-index: -1',
'height: constant(safe-area-inset-top)',
'height: env(safe-area-inset-top)',
];
div.style.cssText = styles.join(';');
div.id = id;
document.body.appendChild(div);
const areaDiv = document.getElementById(id);
if (areaDiv) {
status = areaDiv.offsetHeight > 0 ? 1 : -1; // 该 div 的高度是否为 0
areaDiv.parentNode?.removeChild(areaDiv);
}
return status === 1;
};
那么在已经设置了安全区域属性的地方,都需要额外执行下 supportSafeArea()方法:
const SignTaskDetail = () => {
const [safaArea, setSafeArea] = useState(true); // 当前页面是否支持 safe-area-inset-top
useEffect(() => {
setSafeArea(supportSafeArea());
}, []);
return (
<div
className={classNames('task-page', {
'task-page-not-safearea': !safaArea, // 不支持时,需要额外设置属性
})}
></div>
);
};
参考文章
2022 年移动端适配方案指南 — 全网最新最全 - 掘金 (juejin.cn)
(37条消息) iPhoneX安全区域与H5引发的问题(Safe Area)_余光、的博客-CSDN博客
javascript - iPhoneX安全区域(Safe Area)底部小黑条在微信小程序和H5的屏幕适配_个人文章 - SegmentFault 思否
如何解决移动端的安全区域为0的问题 - 知乎 (zhihu.com)