思路:
1.监听键盘高度的变化
要监听键盘高度的变化,我们得先看看在键盘展开或收起的时候,分别会触发哪些浏览器事件:
-
iOS 和部分 Android 浏览器 展开:键盘展示时会依次触发 visualViewport resize -> focusin
-> visualViewport scroll,部分情况下手动调用 input.focus 不触发 focusin 收起:键盘收起时会依次触发 visualViewport resize -> focusout -> visualViewport scroll-其他 Android 浏览器 展开:键盘展示的时候会触发一段连续的 window resize,约过 200 毫秒稳定
收起:键盘收起的时候会触发一段连续的 window resize,约过 200 毫秒稳定,但是部分手机上有些异常的 case:键盘收起时viewport 会先变小,然后变大,最后再变小
if (window.visualViewport) {window.visualViewport?.addEventListener("resize", listener);window.visualViewport?.addEventListener("scroll", listener);
} else {window.addEventListener("resize", listener);
}
window.addEventListener("focusin", listener);
window.addEventListener("focusout", listener);
2.获取「键盘顶部距离视口顶部的高度」
键盘顶部距离视口顶部的高度 = 视口当前的高度 + 视口滚动上去高度
3.设置 input 的位置
键盘距离页面顶部高度」再减去「元素高度」,从而获得「当前元素的位移」
代码
import EventEmitter from "eventemitter3";// 默认屏幕高度
const DEFAULT_HEIGHT = window.innerHeight;
const MIN_KEYBOARD_HEIGHT = 200;export const KeyboardEvent = {Show: "keyboardShow",Hide: "keyboardHide",PositionChange: "keyboardPositionChange",
};class KeyboardObserver extends EventEmitter {constructor() {super();this.inited = false;this.lastWinHeight = DEFAULT_HEIGHT;this.canChangeStatus = true;this._unbind = () => { };}// 键盘初始化init() {if (this.inited) {return;}const listener = () => this.adjustPos();if (window.visualViewport) {window.visualViewport.addEventListener("resize", listener);window.visualViewport.addEventListener("scroll", listener);} else {window.addEventListener("resize", listener);}window.addEventListener("focusin", listener);window.addEventListener("focusout", listener);this._unbind = () => {if (window.visualViewport) {window.visualViewport.removeEventListener("resize", listener);window.visualViewport.removeEventListener("scroll", listener);} else {window.removeEventListener("resize", listener);}window.removeEventListener("focusin", listener);window.removeEventListener("focusout", listener);};this.inited = true;}unbind() {this.inited = false;this._unbind();}// 调整元素位置adjustPos() {// 获取当前视口高度const height = window.visualViewport? window.visualViewport.height: window.innerHeight;// 获取键盘高度const keyboardHeight = DEFAULT_HEIGHT - height;// 获取键盘顶部距离视口顶部的距离const top = height + (window.visualViewport?.pageTop || 0);this.emit(KeyboardEvent.PositionChange, { top });const diffHeight = height - this.lastWinHeight;this.lastWinHeight = height;// 如果高度减少,且减少高度大于 200,则视为键盘弹起if (diffHeight < 0 && keyboardHeight > MIN_KEYBOARD_HEIGHT) {this.onKeyboardShow({ height: keyboardHeight, top });} else if (diffHeight > 0) {this.onKeyboardHide({ height: keyboardHeight, top });}}onKeyboardShow(keyboardInfo) {if (this.canChangeStatus) {this.emit(KeyboardEvent.Show, keyboardInfo);this.setCanChangeStatus();}}onKeyboardHide(keyboardInfo) {if (this.canChangeStatus) {this.emit(KeyboardEvent.Hide, keyboardInfo);this.setCanChangeStatus();}}setCanChangeStatus() {this.canChangeStatus = false;const timer = setTimeout(() => {clearTimeout(timer);this.canChangeStatus = true;}, 200);}
}const keyboardObserver = new KeyboardObserver();export default keyboardObserver;
组件
<template><div><button @click="focusInput">点击我</button><!-- <div>{{ statusText }}</div> --><inputref="elRef"class="block":style="{ transform: `translateY(${top}px)` }"/></div>
</template><script>
import keyboardObserver, { KeyboardEvent } from "./keyboard";export default {name: 'App',data() {return {// statusText: '',top: window.innerHeight,};},created() {this.initKeyboardObserver();},beforeDestroy() {keyboardObserver.unbind();},methods: {initKeyboardObserver() {keyboardObserver.init();keyboardObserver.on(KeyboardEvent.PositionChange, this.onPositionChange);// keyboardObserver.on(KeyboardEvent.Show, this.onKeyboardShow);// keyboardObserver.on(KeyboardEvent.Hide, this.onKeyboardHide);},onPositionChange({ top }) {this.top = top - this.$refs.elRef.clientHeight;},// onKeyboardShow() {// this.statusText = 'show';// },// onKeyboardHide() {// this.statusText = 'hide';// },focusInput() {this.$refs.elRef.focus();},},
};
</script><style>
html,
body {margin: 0;padding: 0;height: 100%;width: 100%;
}#app {display: flex;align-items: center;justify-content: center;height: 100%;width: 100%;
}.block {position: fixed;top: 0;left: 0;width: 100%;height: 50px;display: flex;align-items: center;justify-content: center;transition: 0.2s all;background-color: #000;color: #fff;
}</style>
使用
页面初始化时输入框获取焦点,并平移键盘弹起的平移高度
初始化监听initKeyboardObserver
参考:
https://juejin.cn/post/7338335869709385780