我们 UI 让我写一个飞书的输入框

284 阅读2分钟

体验地址 vercel

最近在开发 IM 聊天系统, UI 设计了一个很牛逼的输入框, 就是高仿飞书客户端输入框。我真想一头扎死在那,你是看我很闲吗!

一起来看看飞书的输入框长什么样

单行文字

image.png

多行文字

image.png

关于交互上是输入内容到某个宽度时就要自动跳到上面

image.png

看完飞书的输入框,再来看看我们的输入框长啥样

单行文字

image.png

多行文字

image.png

没错,除了按钮不一样,其他都一样。我真想说你怎么不找个飞书开发来呢!给我这点钱让我做飞书的活!(我是个打工人啊,默默接受)

开始干活(只讲对的思路,我猜的坑就不说了)

image.png

先从内容到一定长度将输入框移动到上面开始, 在这里的思路是先计算内容的宽度, 如果内容的宽度超出了所在位置将文本框设置到上面。

在这里我是用的flex布局, 可以想到如果内容达到一定的宽度我直接将flex布局取消,那么输入框的部分将会自动跑到上面。

image.png

那么我们怎么获取内容的宽度呢,需要添加一个专门计算内容宽度的副本 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
    }
  }
}

写到这不想写了, 大家去体验或者看源码吧