思路:
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> -->
<input
ref="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