项目场景:
参考这篇文章
https://juejin.cn/post/7169763455946194952
在开发 Vue 应用时,我们可能会遇到需要在输入框聚焦时调整页面滚动位置的需求。这在移动设备上尤其常见,因为软键盘的弹出可能会遮挡住输入框。本文将介绍如何在 Vue 组件中处理这个问题。
问题描述
在 Vue 组件中,我们需要在输入框聚焦或窗口大小变化时(通常是由于软键盘的弹出或收起引起的),调整页面的滚动位置,以确保当前聚焦的输入框始终在可视区域内。
解决方案:
focusinPage
方法用于处理页面中所有输入框的聚焦事件。当任何一个输入框聚焦时,这个方法会被调用。它会记录当前聚焦的输入框,并调用 handleFocus 方法。
calcScrollTop
方法用于计算并设置新的滚动位置。它接受一个参数:visualHeight(可视区域的高度)。这个方法首先获取当前输入框的顶部相对于视口的位置和页面的垂直滚动位置,然后根据这些信息计算出新的滚动位置,并使用 scrollTo 方法设置新的滚动位置。
handleFocus
方法用于处理IOS设备的输入框聚焦事件。它会调用 calcScrollTop 方法来调整页面的滚动位置,以确保当前聚焦的输入框始终在可视区域内。
handleResize
方法用于处理安卓的软键盘移动。
实例代码:
<template>
<div class="main">
<div class="body1">
聊天内容省略...
</div>
<div class="chat-input">
<input
@focus="focusinPage"
ref="chat-input"
class="input-control input-text" />
<button/></button>
</div>
</div>
<!-- 隐藏的输入元素 -->
</template>
<script>
export default {
data() {
return {
lastInnerHeight: window.innerHeight,
currentInput: null,
};
},
created() {
},
watch: {
},
async mounted() {
const chatInput = document.querySelector('.chat-input .input-text');
//如果是ios系统,监听键盘弹出事件
if (/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent)) {
chatInput.addEventListener('focus', this.handleFocus);
}
//如果是安卓系统,监听键盘弹出事件
if (/(Android)/i.test(navigator.userAgent)) {
chatInput.addEventListener('resize', this.handleResize);
}
},
destroyed() {
const chatInput = document.querySelector('.chat-input .input-text');
chatInput.removeEventListener('focus', this.handleFocus);
chatInput.removeEventListener('resize', this.handleResize);
},
methods: {
focusinPage(e) {
if (e && e.target) {
this.currentInput = e.target;
setTimeout(() => {
this.calcScrollTop(window.visualViewport.height);
}, 300);
} else {
console.error('No target in focus event');
}
},
calcScrollTop(visualHeight, scrollObj) {
scrollObj = scrollObj || window;
var currentInputTop = this.currentInput.getBoundingClientRect().top;
var yOffset = window.pageYOffset;
if (scrollObj !== window) {
yOffset = scrollObj.scrollTop;
}
if (currentInputTop > visualHeight) {
scrollObj.scrollTo(0, yOffset + currentInputTop - visualHeight / 2 + this.currentInput.offsetHeight / 2);
} else if (currentInputTop < 0) {
scrollObj.scrollTo(0, yOffset - currentInputTop - visualHeight / 2 + this.currentInput.offsetHeight / 2);
}
},
handleFocus() {
console.log(this.currentInput.getBoundingClientRect());
this.calcScrollTop(window.visualViewport.height)
},
handleResize() {
var vm = this;
var resizeHeight = this.lastInnerHeight - window.innerHeight // 本次变动量
this.lastInnerHeight = window.innerHeight // 记录目前的可视窗口高度,以便下次计算resize变动量
console.log(resizeHeight)
console.log(this.currentInput)
// 弹出软键盘
if (resizeHeight > 200 && this.currentInput) {
setTimeout(function () {
// 第二个参数,如果当前布局结构是页面容器设置了高度和可视视口高度一样,滚动条是属于这个H5容器的而不是window,如例子中的div.page,
// 则第二个参数要传滚动容器的dom对象,如 document.querySelector('.page')
vm.calcScrollTop(window.visualViewport.height)
}, 150)
}
},
}
};
</script>
注意事项:
@focus=“focusinPage” 是一个事件监听器,用于监听 focus 事件。当用户聚焦到这个输入框时,focus 事件就会被触发,需要确保 calcScrollTop 方法在调用 getBoundingClientRect 方法之前检查 this.currentInput
在使用这个解决方案时,需要注意 this 的上下文。在 handleResize 方法中,我们使用了 var vm = this; 来保存 this 的值,以确保在 setTimeout 的回调函数中可以正确地访问 Vue 组件实例。