使用react hooks實現微信聊天輸入框組件

先看一下效果

微信聊天輸入框的特色

  1. 隨着文字輸入的增長,輸入框的行數增長
  2. 輸入框最大5行
  3. 超過5行則開始滾動

實現方式

使用textarea標籤,經過設置rows屬性來更改輸入行數css

難點分析

  1. 如何檢測文字到達邊界,改變textarea的rows屬性

如何解決

使用相同寬度的textarea,將該textarea設置爲不可見,rows屬性爲1,readonly。react

經過textarea的clientHeight和ScrollHeight來判斷行數。ios

其它注意點

  1. 去除文字首尾的空白字符
  2. 去除空白字符後無其它字符,則不容許發送

代碼實現

// InputArea.js

import React, { useState, useMemo, useRef, useEffect } from 'react'

import './InputArea.scss'

export default function InputArea(props) {
  const [content, setContent] = useState('')

  // 默認textarea的最高行數
  const [defaultMaxRows] = useState(5)

  const [rows, setRows] = useState(1)

  const hiddenTextarea = useRef(null)

  const maxRows = useMemo(() => props.maxRows || defaultMaxRows, [props.maxRows])

  useEffect(() => {
    return () => {
      let r = hiddenTextarea.current.scrollHeight / hiddenTextarea.current.clientHeight
      if (r > maxRows) r = maxRows
      setRows(r)
    }
  }, [maxRows, content])

  // 用戶輸入內容是否能夠發送
  // 去除輸入內容的收尾兩端空格
  const disable = useMemo(() => content.replace(/(^\s+)|(\s+$)/g, '') === '', [content])

  // 發送消息
  function sendMessage() {
    // 調用接口發送消息
    props.sendMessage && props.sendMessage()

    // 發送成功後
    setContent('')
  }

  function onChange(e) {
    // console.log('onchange', e.target.value)
    setContent(e.target.value)
  }

  function onBlur(e) {
    // 鍵盤收起
    setTimeout(() => {
      // safari on ios9 一下不支持window.scrollTo 好在這個兼容性問題出如今ios12的微信裏
      window.scrollTo && window.scrollTo(0, 99999999)
    }, 100)
  }

  function onFocus(e) {
    // 鍵盤彈出
    setTimeout(() => {
      // safari on ios9 一下不支持window.scrollTo 好在這個兼容性問題出如今ios12的微信裏
      window.scrollTo && window.scrollTo(0, 99999999)
    }, 100)
  }

  return (
    <div className="m-input-area__wrapper">
      <div className="m-input-area__content">
        <textarea
          spellCheck={false}
          placeholder="輸入聊天內容..."
          rows={rows}
          value={content}
          onChange={onChange}
          onBlur={onBlur}
          onFocus={onFocus}
        />

        {/* 不可見的輸入框 */}
        <textarea readOnly rows={1} value={content} ref={hiddenTextarea} />
      </div>
      <input
        className={`m-input-area__btn ${disable ? 'disable' : ''}`}
        type="button"
        value="發送"
        disabled={disable}
        onClick={sendMessage}
      />
    </div>
  )
}

複製代碼
.m-input-area__wrapper {
  display: flex;
  justify-content: space-between;
  background: #F6F6F6;
  padding: 3px 6px 4px 6px;
  // border-top: solid 1px #f1f1f1;
  box-shadow: 0 -1px 4px #ddd;
}

.m-input-area__content {
  flex-grow: 1;
  position: relative;
  // height: fit-content;
  border-radius: 2px;
  margin-right: 6px;
  padding: 4px 6px;
  background: #fff;
  // font-size: 0; // 解決textarea後面會添加空白字符 致使的高度無故撐開3px 其它解決方式看筆記

  textarea {
    display: block; // 解決textarea後面會添加空白字符 致使的高度無故撐開3px 其它解決方式看筆記
    font-size: 14px;
    padding: 0;
    border: 0;
    line-height: 22px;
    color: #71777c;
    resize: none; // 不容許調整元素大小
    width: 100%;
    outline: none;

    &::placeholder {
      color: #CECECE;
    }

    &:nth-child(2) {
      visibility: hidden;
      position: absolute;
      left: 0;
      top: 0;
      padding: 0 6px;
      box-sizing: border-box;
    }
  }
}

.m-input-area__btn {
  width: 60px;
  height: 30px;
  font-size: 14px;
  // text-align: center;
  color: #fff;
  background: #007fff;
  // border: solid 1px #007fff;
  border: none;
  border-radius: 2px;
  font-weight: 600;
  cursor: pointer;
  letter-spacing: 4px;
  text-indent: 4px;

  &.disable {
    // border-color: #CECECE;
    background: #cecece;
    color: #fff;
  }
}
複製代碼
相關文章
相關標籤/搜索