Skip to content
本文总阅读量

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 */
}
  1. 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;
}