Appearance
1、与App交互
通用方法
js
/**
* 判断是安卓还是IOS环境
*/
function isAndroid() {
let u = navigator.userAgent
let isAndroid = u.indexOf('Android') > -1 || u.indexOf('Linux') > -1 //android终端或者uc浏览器
// let isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/) //ios终端
return isAndroid
}
/**
* 安卓 注册事件监听
*/
function connectWebViewJavascriptBridge(callback) {
if (window.WebViewJavascriptBridge) {
callback(WebViewJavascriptBridge)
} else {
document.addEventListener(
'WebViewJavascriptBridgeReady',
function () {
callback(WebViewJavascriptBridge)
},
false
)
}
}
/**
* ios 注册事件监听
*/
function setupWebViewJavascriptBridge(callback) {
var ua = navigator.userAgent.toLowerCase()
if (ua.match(/MicroMessenger/i) == 'micromessenger') {
//ios的ua中无miniProgram,但都有MicroMessenger(表示是微信浏览器)
} else {
if (!isAndroid()) {
if (window.WebViewJavascriptBridge) {
return callback(WebViewJavascriptBridge)
}
if (window.WVJBCallbacks) {
return window.WVJBCallbacks.push(callback)
}
window.WVJBCallbacks = [callback] // 创建一个 WVJBCallbacks 全局属性数组,并将 callback 插入到数组中
var WVJBIframe = document.createElement('iframe') // 创建一个 iframe 元素
WVJBIframe.style.display = 'none' // 不显示
WVJBIframe.src = 'https://__bridge_loaded__' // 设置 iframe 的 src 属性
document.documentElement.appendChild(WVJBIframe) // 把 iframe 添加到当前文导航上。
setTimeout(function () {
document.documentElement.removeChild(WVJBIframe)
}, 0)
}
}
}
// 安卓 ios公用执行方法
function onExecuteWebViewFn(funcName, cb, params = {}) {
if (isAndroid()) {
connectWebViewJavascriptBridge((bridge) => {
bridge.callHandler(funcName, params, cb)
})
} else {
setupWebViewJavascriptBridge(function (bridge) {
bridge.callHandler(funcName, params, cb)
})
}
}
示例:vue 中使用需要在 mounted 注册
vue
<script>
export default {
// 第一种方法挂在全局 利用postMessage通讯
mounted() {
window.addEventListener('message', this.handleMessage)
},
beforeDestroy() {
window.removeEventListener('message', this.handleMessage)
},
methods: {
handleMessage(event) {
// 处理收到的消息
const message = event.data
// 执行逻辑
},
sendMessage() {
// 父窗口对象发送消息
const data = { foo: 'bar' }
window.parent.postMessage(data, '')
/**
* 兼容部分IOS
* 用于在iOS WebView中发送消息给native代码
* window.webkit.messageHandlers是iOS WebView提供的一个对象,用于与native代码通信
*/
try {
window.webkit.messageHandlers.handleMessage.postMessage('')
} catch (err) {
console.log('执行中错误')
}
}
},
// 第二种方法 WebViewJavascriptBridge
onLoad() {
// 监听回调方法
onExecuteWebViewFn('H5Func', (res) => {
console.log('H5Func')
})
}
}
</script>
2、meta标记页面铺满屏幕
H5网页设置viewport-fit=cover的时候才生效,小程序里的viewport-fit默认是cover
safe-area-inset-left:安全区域距离左边边界的距离 safe-area-inset-right:安全区域距离右边边界的距离 safe-area-inset-top:安全区域距离顶部边界的距离 safe-area-inset-bottom:安全区域距离底部边界的距离
这两个函数都是 webkit 中 css 函数,可以直接使用变量函数,只有在 webkit 内核下才支持
env 函数 | constant 函数 |
---|---|
必须在 ios >= 11.2 才支持 | 必须 ios < 11.2 支持 |
示例:H5 index.html
html
<meta name="viewport" content="viewport-fit=cover" />
头部组件css
css
.head {
padding-top: constant(safe-area-inset-top);
padding-top: env(safe-area-inset-top);
height: 100rpx;
line-height: 100rpx;
position: fixed;
top: 0;
left: 0;
right: 0;
}
页面css
css
.main-content {
height: calc(100vh - 100rpx - constant(safe-area-inset-top));
height: calc(100vh - 100rpx - env(safe-area-inset-top));
padding-top: calc(100rpx + constant(safe-area-inset-top));
padding-top: calc(100rpx + env(safe-area-inset-top));
}
解决ios适配自定义头部组件高度
js
<meta name="viewport" content="viewport-fit=cover" />
3、IOS圆角不生效
ios中使用border-radius配合overflow:hidden出现了失效的情况: 出现此问题的原因是因为ios手机会在transform的时候导致border-radius失效
解决方法:在使用动画效果带transform的元素的上一级div元素的css加上下面语句:
-webkit-transform:rotate(0deg);
4、IOS文本省略溢出问题
两种情况,在目标元素上设置font-size = line-height
css
.text-overflow {
display: -webkit-box;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
}
.text-overflow {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
经过测试,在height = line-height = font-szie的情况下,加上padding-top: 1px可以解决这个问题
5、安卓手机按钮点击后有橙色边框
CSS
button:focus {
outline: none;
}
6、剪切板的读写访问
navigator.clipboard兼容性不是很好,低版本浏览器不支持
JS
const copyText = (text: string) => {
return new Promise(resolve => {
if (navigator.clipboard?.writeText) {
return resolve(navigator.clipboard.writeText(text))
}
// 创建输入框
const textarea = document.createElement('textarea')
document.body.appendChild(textarea)
// 隐藏此输入框
textarea.style.position = 'absolute'
textarea.style.clip = 'rect(0 0 0 0)'
// 赋值
textarea.value = text
// 选中
textarea.select()
// 复制
document.execCommand('copy', true)
textarea.remove()
return resolve(true)
})
}
7、一个uni-app构建的 H5 页面
页面中组件搜索弹窗覆盖高度 100%,想要监听 app 返回只关闭弹窗
js
<script>
export default {
data() {
return {
isShowSearch: false,
}
},
methods: {
pushHistory() {
const that = this
window.addEventListener(
"popstate",
function (e) {
that.isShowSearch = false
},
false
)
/**
调用 history.pushState() 或者 history.replaceState() 不会触发 popstate 事件。
popstate 事件只会在浏览器某些行为下触发,比如点击后退按钮(或者在 JavaScript 中调用 history.back() 方法)。
即,在同一文档的两个历史记录条目之间导航会触发该事件
*/
// 每次点击相关操作时都调用pushHistory函数,这样每次切换页面时都会添加一个新的历史记录,从而让popstate事件能够被触发
window.history.pushState(null, null, "")
}
onSelectTag() {
this.isShowSearch = true
this.pushHistory()
}
}
}
</script>
8、ios 内嵌h5 顶部盒子使用fixed在滚动的时候会出现间隙
情况会在ios有刘海的手机出现,不能设置viewport-fit=cover,排除情况2 Iphone11真机已确认盒子fixed top为0
解决办法:滚动列表不能包含顶部fixed盒子,改为absolute定位
vue
<template>
<div>
<div class="head">顶部固定区域</div>
<div class="main">滚动区域</div>
</div>
</template>
<style>
.head {
position: absolute;
top: 0;
left: 0;
height: 100px;
}
.main {
padding-top: 100px; /* head高度 */
height: 100%;
position: absolute;
top: 0;
left: 0;
overflow-y: scroll; /*使之可以滚动*/
}
</style>
9、ios导航栏定位组件安全距离
子组件
css
.common-header-box {
padding-top: env(safe-area-inset-top);
padding-top: constant(safe-area-inset-top);
height: 100rpx;
line-height: 100rpx;
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 999;
}
父页面
css
.page-content {
height: calc(100vh - 100rpx - constant(safe-area-inset-top));
height: calc(100vh - 100rpx - env(safe-area-inset-top));
padding-top: calc(100rpx + env(safe-area-inset-top));
padding-top: calc(100rpx + constant(safe-area-inset-top));
}
10、使用viewport配置,确保完美视口移动端开发首先要设置正确的viewport,这是适配的基础。
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
关键属性解析: width=device-width:将视口宽度设置为设备宽度 initial-scale=1.0:初始缩放比例为 1user-scalable=n::禁用用户缩放 viewport-fit=cover:适配刘海屏2. 使用rem实现弹性布局rem是相对于根元素(html)的字体大小的单位,可以实现整体布局的弹性缩放。// 设置 rem 基准值
js
;(function flexible() {
const docEl = document.documentElement
function setRemUnit() {
const rem = docEl.clientWidth / 10
docEl.style.fontSize = rem + 'px'
}
setRemUnit()
window.addEventListener('resize', setRemUnit)
window.addEventListener('orientationchange', setRemUnit)
})()
配套的CSS使用:
css
.container {
width: 7.5rem; /* 750px / 100 */
height: 1rem; /* 100px / 100 */
font-size: 0.28rem; /* 28px / 100 */
}
- CSS媒体查询处理不同尺寸使用媒体查询针对不同屏幕尺寸定制样式。/_ iPhone SE _/
css
@media screen and (max-width: 374px) {
.container {
font-size: 14px;
}
}
/* iPhone 6/7/8/X */
@media screen and (min-width: 375px) and (max-width: 413px) {
.container {
font-size: 16px;
}
}
/* iPhone 6/7/8 Plus */
@media screen and (min-width: 414px) {
.container {
font-size: 18px;
}
}
11、1px边框问题解决方案在高清屏幕下1px边框显示过粗的解决方案。
css
.border-1px {
position: relative;
&::after {
content: '';
position: absolute;
left: 0;
bottom: 0;
width: 100%;
height: 1px;
background-color: #000;
transform: scaleY(0.5);
transform-origin: bottom;
}
}
// 2x屏
@media (-webkit-min-device-pixel-ratio: 2) {
.border-1px::after {
transform: scaleY(0.5);
}
}
// 3x屏
@media (-webkit-min-device-pixel-ratio: 3) {
.border-1px::after {
transform: scaleY(0.33);
}
}
12、图片适配方案针对不同分辨率设备的图片适配策略。
使用srcset适配不同分辨率
js
<img
srcset="image-320w.jpg 320w,image-480w.jpg 480w,image-800w.jpg 800w"
sizes="(max-width: 320px) 280px,(max-width: 480px) 440px,800px"
src="image-800w.jpg"
alt="Responsive image"
/>
配合CSS的处理:
css
.responsive-image {
max-width: 100%;
height: auto;
display: block;
}
13、横屏适配处理处理横屏模式下的布局适配。
检测横屏
css
@media screen and (orientation: landscape) {
.landscape-container {
display: flex;
flex-direction: row;
}
}
检测竖屏
css
@media screen and (orientation: portrait) {
.portrait-container {
display: flex;
flex-direction: column;
}
}
JavaScript监听屏幕旋转:
js
window.addEventListener('orientationchange', function () {
if (window.orientation === 180 || window.orientation === 0) {
// 竖屏
console.log('竖屏')
}
if (window.orientation === 90 || window.orientation === -90) {
// 横屏
console.log('横屏')
}
})
14、软键盘弹出处理处理软键盘弹出时的页面适配问题。
js
// 监听软键盘
const originalHeight = document.documentElement.clientHeight
window.addEventListener('resize', () => {
const currentHeight = document.documentElement.clientHeight
const input = document.activeElement
if (originalHeight > currentHeight) {
// 软键盘弹出
if (input.tagName === 'INPUT' || input.tagName === 'TEXTAREA') {
input.scrollIntoView({ block: 'center' })
}
} else {
// 软键盘收起
window.scrollTo(0, 0)
}
})
CSS处理: 防止键盘顶起页面
css
.container {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
overflow: auto;
-webkit-overflow-scrolling: touch;
}