最近在开发 IM 聊天系统, UI 设计了一个很牛逼的输入框, 就是高仿飞书客户端输入框。我真想一头扎死在那,你是看我很闲吗!
一起来看看飞书的输入框长什么样
单行文字
多行文字
关于交互上是输入内容到某个宽度时就要自动跳到上面
看完飞书的输入框,再来看看我们的输入框长啥样
单行文字
多行文字
没错,除了按钮不一样,其他都一样。我真想说你怎么不找个飞书开发来呢!给我这点钱让我做飞书的活!(我是个打工人啊,默默接受)
开始干活(只讲对的思路,我猜的坑就不说了)
先从内容到一定长度将输入框移动到上面开始, 在这里的思路是先计算内容的宽度, 如果内容的宽度超出了所在位置将文本框设置到上面。
在这里我是用的flex布局, 可以想到如果内容达到一定的宽度我直接将flex布局取消,那么输入框的部分将会自动跑到上面。
那么我们怎么获取内容的宽度呢,需要添加一个专门计算内容宽度的副本 textarea (要记得将副本的文字样式和文本框设置为一样的,这样计算出来的宽度才不会有错的)
// 计算宽度的副本
<textarea class="copy-textarea fixed top-0 init-textarea overflow-x-auto whitespace-nowrap w-auto hide-scrollbar invisible" :value="textContent" :style="{ width: textareaInitWidth }" />
// 输入框,里面有一些多余的功能
<div class="bg-white rounded-8px px-5 py-12px textarea-wrapper" :class="isFlex ? 'flex' : ''">
<!-- 左边输入框 -->
<div class="flex-1 max-h-200px overflow-y-auto flex items-center">
<textarea ref="textareaRef" :value="textContent" placeholder="请输入新消息" class="init-textarea real-textarea placeholder-#C1C2C6" cols="1000" :style="{ height: textareaHeight }" @input="textareaInput" @dragenter.prevent @dragover.prevent @drop.prevent="onDropFile" @paste="onPasteImage" @keyup.enter="sendTextMsg" />
</div>
<div class="flex items-center justify-end" :class="{ 'mt-1': !isFlex }">
<CollectAudio class="cursor-pointer" @send-audio-messages="sendAudioMessages" />
<img class="w-18px h-18px mx-18px cursor-pointer" src="../../assets/images/upload-img-icon.png" alt="" @click="uploadImgsClick">
<!-- <img class="w-18px h-18px mr-18px cursor-pointer" src="../../assets/images/upload-video-icon.png" alt="" @click="uploadVideo"> -->
<div class="bg-#A3B4C9 rounded-16px h-32px leading-8 text-center w-64px text-14px text-white" :style="{ 'background-color': textContent ? '#18457A' : '#A3B4C9', 'cursor': textContent ? 'pointer' : 'not-allowed' }" @click="sendTextMsg">
发送
</div>
<div ref="loadingBox" class="absolute right-0 top-0 w-6 h-6" />
</div>
</div>
计算内容宽度,其实就是获取副本的宽度来进行比较,是否内容超出了限制
function textareaInput(event: Event) {
// @ts-expect-error xxx
textContent.value = event.target.value || ''
if (!textareaRef.value) {
return
}
// @ts-expect-error xxx
if (!event.target?.value) {
textareaHeight.value = `${initTextareaHeight.value}px`
isFlex.value = true
return
}
// 计算内容宽度
nextTick(() => {
compareWidth()
})
// 对比内容宽度是否大于初始宽度
function compareWidth() {
const totalContentWidth = document.querySelector('.copy-textarea')?.scrollWidth || 0
if (totalContentWidth > copyInitWidth.value) {
isFlex.value = false
} else {
isFlex.value = true
}
}
}
写到这不想写了, 大家去体验或者看源码吧