⭐️⭐️border-radius 画出的圆在移动端有毛边
解决方案
给元素添加 overflow: hidden 属性。
.elem { overflow: hidden; }
⭐️⭐️安卓上去掉语音输入按钮
input::-webkit-input-speech-button { display: none; }
⭐️⭐️Vue 单页应用在 iOS 上微信分享失效,图片,标题和描述均未正常显示,安卓上分享正常
原因
我们一般在 APP.vue 的 mounted 生命周期中初始化微信 SDK,此时页面的地址 hash 是#/,而首页的 hash 是#/home,导致初始化微信 SDK 时传入的分享 url 和用户实际触发分享操作时页面的 url 不一致,致使在 iOS 上分享失败。
解决方案
初始化微信分享 SDK 时传入的地址,和实际触发分享时页面的地址保持一致。
⭐️⭐️iOS safari 被点击元素会出现半透明灰色遮罩
解决方案
给 html 或者 body 加入以下 css 代码。
body { -webkit-tap-highlight-color: rgba(0,0,0,0); -webkit-user-modify: read-write-plaintext-only; }
⭐️⭐️iOS 禁止保存或拷贝图像
解决方案
长按图片保存场景下,禁止 IOS 默认识别图像行为。
img { -webkit-touch-callout: none; }
⭐️⭐️⭐️iOS 端微信 H5 页面上下滑动时卡顿
解决方案
给滚动元素加上-webkit-overflow-scrolling
属性。
body { -webkit-overflow-scrolling:touch; }
⭐️⭐️iOS 默认输入框内阴影重置
解决方案
阻止 iOS 默认的美化页面的策略-webkit-appearance:none;
input { border: 0; -webkit-appearance:none; }
⭐️⭐️⭐️对非可点击元素(div,span 等)监听 click 事件,部分 ios 版本不会触发事件
解决方案
- 添加 css 属性 cursor: pointer;
- 换成 button 元素。
cursor: pointer; <button></button>
⭐️⭐️⭐️手机底部刘海存在背景,和页面背景色不一致
解决方案
通过指定 body 的背景色来解决。
body { background-color: #fff; // or 暗色模式 // background-color: #000; }
⭐️⭐️对于带有 hash 的 H5 链接,部分手机厂商的 webview 打开 H5 页面会加载两次
解决方案
这是部分 webview 对于特殊 url 有独特的解析和加载逻辑,去掉 hash 即可
https://www.example.com/a/b#/
⭐️⭐️body存在默认背景色
解决方案
body 标签在大部分浏览器中的默认背景色是白色,但在极少数浏览器中的背景颜色是淡绿色或者其他颜色。通过指定 body 背景色为#fff,来兼容更多设备。
body { background-color: #fff; }
⭐️⭐️旋转屏幕的时候,字体大小调整的问题
css body { -webkit-text-size-adjust: 100%; }
⭐️⭐️IOS解析日期问题
在某些情况下,苹果系统上解析 YYYY-MM-DD HH:mm:ss
格式的日期会报错 Invalid Date
,而安卓系统则没有这个问题。解决这个问题的一种方法是将日期字符串中的 -
替换为 /
。
const dateString = "2023-07-16 00:00:00"; const fixedDateString = dateString.replace(/-/g, "/"); const date = new Date(fixedDateString);
⭐️⭐️⭐️⭐️⭐️滚动穿透
现象
滚动穿透(scrolling through)是指在一个固定区域内滚动时,滚动事件透过该区域继续传递到其下方的元素,导致同时滚动两个区域的现象。滚动穿透可能会对用户体验产生负面影响,因为用户可能意外地滚动到不相关的内容。
解决方案
这个问题一直很无解,只能hack去兼容
overflow: hidden
1.先锁住body
.modal-open { &, body { overflow: hidden; height: 100%; } }
2.还原body滚动区域
// 获取滚动区域的容器元素 const container = document.querySelector('.container'); // 获取滚动区域的内容元素 const content = document.querySelector('.content'); // 记录滚动位置 let scrollTop = 0; // 禁止滚动穿透 function disableScroll() { // 记录当前滚动位置 scrollTop = window.pageYOffset || document.documentElement.scrollTop; // 设置滚动区域容器的样式,将其高度设置为固定值,并设置滚动条样式 container.style.height = '100%'; container.style.overflow = 'hidden'; // 阻止窗口滚动 document.body.classList.add('no-scroll'); document.body.style.top = `-${scrollTop}px`; } // 启用滚动穿透 function enableScroll() { // 恢复滚动区域容器的样式 container.style.height = ''; container.style.overflow = ''; // 允许窗口滚动 document.body.classList.remove('no-scroll'); document.body.style.top = ''; // 恢复滚动位置 window.scrollTo(0, scrollTop); } // 示例使用,当某个事件触发时禁止滚动穿透 function disableScrollEvent() { disableScroll(); } // 示例使用,当某个事件触发时启用滚动穿透 function enableScrollEvent() { enableScroll(); }
ant-mobile组件库解决方式
思想思路:
- 针对触摸滑动事件
touchmove
,通过监听滑动方向和滚动元素的状态,决定是否阻止默认的滑动行为,从而防止滚动穿透。 - 在需要锁定滚动的情况下,给
document
添加touchstart
和touchmove
事件的监听器,通过捕获触摸滑动事件,并根据情况阻止默认行为,从而避免滚动穿透。 - 在解锁滚动时,从
document
移除对触摸事件的监听器,恢复默认的滑动行为。
// 移植自vant:https://github.com/youzan/vant/blob/HEAD/src/composables/use-lock-scroll.ts export function useLockScroll( rootRef: RefObject<HTMLElement>, shouldLock: boolean | 'strict' ) { const touch = useTouch() const onTouchMove = (event: TouchEvent) => { touch.move(event) const direction = touch.deltaY.current > 0 ? '10' : '01' const el = getScrollParent( event.target as Element, rootRef.current ) as HTMLElement if (!el) return // This has perf cost but we have to compatible with iOS 12 if (shouldLock === 'strict') { const scrollableParent = getScrollableElement(event.target as HTMLElement) if ( scrollableParent === document.body || scrollableParent === document.documentElement ) { event.preventDefault() return } } const { scrollHeight, offsetHeight, scrollTop } = el let status = '11' if (scrollTop === 0) { status = offsetHeight >= scrollHeight ? '00' : '01' } else if (scrollTop + offsetHeight >= scrollHeight) { status = '10' } if ( status !== '11' && touch.isVertical() && !(parseInt(status, 2) & parseInt(direction, 2)) ) { if (event.cancelable) { event.preventDefault() } } } const lock = () => { document.addEventListener('touchstart', touch.start) document.addEventListener( 'touchmove', onTouchMove, supportsPassive ? { passive: false } : false ) if (!totalLockCount) { document.body.classList.add(BODY_LOCK_CLASS) } totalLockCount++ } const unlock = () => { if (totalLockCount) { document.removeEventListener('touchstart', touch.start) document.removeEventListener('touchmove', onTouchMove) totalLockCount-- if (!totalLockCount) { document.body.classList.remove(BODY_LOCK_CLASS) } } } useEffect(() => { if (shouldLock) { lock() return () => { unlock() } } }, [shouldLock]) }