textarea多行输入框遮盖问题

1,363 阅读5分钟

uni-app textarea多行输入框遮盖问题

一.问题:

1. textarea在获取焦点时为了保持焦点会上推页面,使定位(fixed,sticky)位置不准确 image-20210924175637984.png 2.textarea获取焦点时不上推页面,内容过多时,textarea输入的内容会被键盘挡住,正在的输入内容无法一直保持在键盘上方

image-20210924180800650.png

二.解决思路: 获取软键盘弹起后页面可视高度作为内容的高度,设置内容隐藏滚动,获取焦点位置滚动内容使焦点保持在可视范围
三.步骤:

1. 获取页面可视高度作为 scroll-view组件的高度, 输入内容超过scroll-view组件时会滚动显示, 使焦点一直保持在键盘弹起后可视范围

// 页面可视高度      屏幕高度       键盘高度   固定内容高度
 surplusHeight = windowHeight - height - footerHeight;

image-20210926184831328.png 2. 通过scroll-view组件的 @scroll事件监听获取页面滚动距离,再获取点击textarea的位置,键盘位置,和固定内容(footer)位置,通过计算点击位置被键盘和固定内容(footer)挡住的多少,设置scroll-view组件上的scroll-top属性可以使焦点滚动到可视范围

// 挡住高度           页面可视高度      点击页面位置
focalOcclusionTop =  surplusHeight - clickPosition;
//滚动高度    页面滚动高度     挡住高度          手指大小问题获取点击位置有区别,加20
scroll-top =  scrollHeight + focalOcclusionTop +  20

image-20210924184802573.png

四.兼容处理:

1. 安卓软键盘消失是不失去焦点留白,ios已经失去焦点也会在页面留白,需要监听键盘高度为0时手动失去焦点

this.isFocus = false;

image-20210927114150089.png 2. 全面屏ios底部有安全距离,安全距离是会占键盘高度和可使用窗口高度的,这个安全距离会影响内容固定到软键盘上

/* 1. mounted生命周期获取安全距离 */
// 获取底部安全距离
const { screenHeight, windowHeight } = uni.getSystemInfoSync();// 获取屏幕高度 ,可使用窗口高度
// 底部安全距离 = 屏幕高度 - 可使用窗口高度
this.safearea = screenHeight -windowHeight;

/* 2. 在监听键盘高度变化事件,键盘高度大于0是 计算 */
			// 监听键盘高度变化
			keyboardheightchange(e) {
				// 键盘高度
				let {
					height
				} = e.detail;
				// 全面屏底部的安全距离会占软键盘高度和屏幕高度,会影响底部内容固定到软键盘位置,要计算出底部安全距离,再用获取的软键盘高度减去安全距离
				if(height > 0){
					// 键盘高度 =  键盘高度 - 底部安全距离
					height -= this.safearea
				}else{
					// 在安卓软键盘显示没有失去焦点,导致页面留白,需要手动失去焦点
					this.isFocus = false;
				}
				// 赋值
				this.keyHight = height;
			}
/* 3. 获取焦点事件(focus) 中获取软键盘弹起的可视高度 要加上底部安全距离 */
// 获取页面可视范围(软键盘弹起后的可视距离) = 可使用窗口高度 - 键盘高度 - 底部固定内容高度 + 安全距离
  this.visualHeight = windowHeight - height - this.footerHeight + this.safearea;

image-20210927115115154.png 3. 使用原生导航栏页面页面可使用窗口高度是不包括导航栏和状态栏高度,所以在计算底部安全距离时要减去导航栏和状态栏高度

mounted() {
			this.$nextTick(() => {
				// 获取底部内容高度
				const query = uni.createSelectorQuery().in(this);
				query.select('.footer').boundingClientRect(data => {
					this.footerHeight = data.height;
				}).exec();
                // 获取设备参数
				const {
					screenHeight,
					windowHeight,
					platform,
					statusBarHeight
				} = uni.getSystemInfoSync(); // 获取屏幕高度 ,可使用窗口高度 , 客户端平台 ,状态栏高度
				/* 使用原生导航栏 */
				// 如果使用的原生导航栏:可使用窗口高度不包括状态栏的高度 和导航栏高度,需要减去
				//ios(44),android(50)原生导航栏高度是固定的
				let safearea;
				if (platform === 'ios') {
					// 底部安全 = 屏幕高度 - 可使用窗口高度  - 状态栏的高度 -ios导航栏高度
					safearea = screenHeight - windowHeight - statusBarHeight - 44
				} else {
					// 底部安全 = 屏幕高度 - 可使用窗口高度  - 状态栏的高度 - android导航栏高度
					safearea = screenHeight - windowHeight - statusBarHeight - 50
				}
				// 赋值
				this.safearea = safearea;
			})
		}
五.全部代码:
<template>
	<view @touchmove.stop.prevent="noTouching">
		<scroll-view :style="{height: isFocus?surplusHeight+ 'px': '100vh'}" scroll-with-animation
			:scroll-top="scrollTop" scroll-y @scroll="scroll">
			<view class="content">
				<view style="height: 50px; background-color: #4CD964;">
					占位1
				</view>
				<view style="height: 50px; background-color: #FF0000;">
					占位2
				</view>
				<textarea :focus="isFocus" :auto-height="true" :adjust-position="false" :value="value" :maxlength="-1"
					placeholder="请输入内容..." @focus="focus" @keyboardheightchange="keyboardheightchange" @input="input"
					@tap.stop="getClickPosition" />
			</view>
		</scroll-view>
		<view @touchend.prevent="handleTouch" @touchmove.stop.prevent="noTouching" class="footer"
			:style="{bottom: height + 'px'}">
			footer
		</view>
	</view>
</template>
<script>
	export default {
		data() {
			return {
				height: 0, //键盘高度,
				isFocus: false, //焦点
				value: '内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容', //内容
				scrollHeight: 0, // scroll-view滚动高度
				clickPosition: 0, // 点击屏幕位置
				footerHeight: 0, //底部高度
				surplusHeight: 0, // 页面可视高度: 屏幕高度 - 键盘高度 - 底部内容高度
				scrollTop: 0, //获取焦点时页面滚动距离
				safearea: 0, //全面屏底部安全距离
			}
		},
		mounted() {
			this.$nextTick(() => {
				// 获取底部内容高度
				const query = uni.createSelectorQuery().in(this);
				query.select('.footer').boundingClientRect(data => {
					this.footerHeight = data.height;
				}).exec();

				// 获取设备参数
				const {
					screenHeight,
					windowHeight,
					platform,
					statusBarHeight
				} = uni.getSystemInfoSync(); // 获取屏幕高度 ,可使用窗口高度 , 客户端平台 ,状态栏高度
				/* 使用原生导航栏 */
				// 如果使用的原生导航栏:可使用窗口高度不包括状态栏的高度 和导航栏高度,需要减去
				//ios(44),android(50)原生导航栏高度是固定的
				// let safearea;
				// if (platform === 'ios') {
				// 	// 底部安全 = 屏幕高度 - 可使用窗口高度  - 状态栏的高度 -ios导航栏高度
				// 	safearea = screenHeight - windowHeight - statusBarHeight - 44
				// } else {
				// 	// 底部安全 = 屏幕高度 - 可使用窗口高度  - 状态栏的高度 - android导航栏高度
				// 	safearea = screenHeight - windowHeight - statusBarHeight - 50
				// }
				// // 赋值
				// this.safearea = safearea;
				
				/* 使用自定义导航栏 */
				// 底部安全 = 屏幕高度 - 可使用窗口高度
				this.safearea = screenHeight - windowHeight;
			})
		},
		methods: {
			// scroll-view 内容滚动高度
			scroll(e) {
				this.scrollHeight = e.detail.scrollTop;
			},
			// 监听内容变化事件
			input(e) {
				this.value = e.detail.value;
			},
			// footer内容点击
			handleTouch() {
				console.log('footer')
			},
			// 禁止触摸固定内容
			noTouching() {
				return
			},
			// 获取点击在屏幕位置
			getClickPosition(event) {
				this.clickPosition = event.detail.y;
			},
			// 监听键盘高度
			keyboardheightchange(e) {
				// 键盘高度
				let {
					height
				} = e.detail;
				// 全面屏底部的安全距离会占软键盘高度和屏幕高度,会影响底部内容固定到软键盘位置,要计算出底部安全距离,再用获取的软键盘高度减去安全距离
				if (height > 0) {
					// 键盘高度 =  键盘高度 - 底部安全距离
					height -= this.safearea;
				} else {
					// 在安卓软键盘显示没有失去焦点,导致页面留白,需要手动失去焦点
					this.isFocus = false;
				}
				// 赋值
				this.height = height;
			},
			// 监听获取焦点事件 
			focus(e) {
				// 获取点击屏幕位置方法比focus执行慢, 为了确保拿到当前点击评论位置, 加个定时器
				setTimeout(() => {
					const {
						height
					} = e.detail; // 键盘高度
					let {
						windowHeight
					} = uni.getSystemInfoSync(); // 屏幕高度
					this.isFocus = true;
					// 获取页面可视范围(软键盘弹起后的可视距离) = 可使用窗口高度 - 键盘高度 - 底部固定内容高度 + 安全距离
					this.surplusHeight = windowHeight - height - this.footerHeight + this.safearea;
					// 焦点被遮挡了
					if (this.clickPosition > this.surplusHeight) {
						// 焦点被遮挡距离 =  焦点位置  - 页面可视范围
						const focalOcclusionTop = this.clickPosition - this.surplusHeight;
						// 获取焦点时页面滚动距离 =  页面滚动距离 + 焦点被遮挡距离
						this.scrollTop = this.scrollHeight + focalOcclusionTop + 20;
					}
				}, 300)
			}
		}
	}
</script>
<style>
	textarea {
		padding-bottom: 100upx;
		width: 100%;
		background-color: #C0C0C0;
	}
	.defaultClass{
		padding-bottom: 100upx;
		min-height: 500upx;
	}
	.footer {
		box-sizing: border-box;
		position: fixed;
		left: 0;
		bottom: 0;
		width: 100%;
		height: 100upx;
		background-color: #007AFF;
	}
</style>