react-native的输入框中实现插入高亮文本图片的做法

20797次浏览

背景

上一篇文章我们介绍了rn中如何实现文本高亮,但是输入框中实现文本高亮如何做呢?输入框中插入图片等如何实现呢?这篇文章将普及一下这方面的知识,分享给大家。

输入框高亮文本

其实假如你要实现输入框高亮文本,那么可以通过如下方式实现。

    <TextInput maxlength={200} onChange={onInputEventDetail} onFocus={() => showBar()} onBlur={() => (Platform.OS == 'ios' ? hideKeyboard() : null)} multiline={true} textAlign={'left'} style={{ textAlignVertical: 'top' }} onKeyPress={onKeyPressfn} onTextInput={onTextInput} className={Style['publish-textarea']} placeholder='haorooms发布帖子测试'>
      <Text>
        {matchAtData?.length > 0 ? (
          <>
            {textData
              .filter((li) => li.trim())
              .map((item, index) => {
                if (matchAtData.join('').indexOf(item) != -1) {
                  return item ? (
                    <Text className={Style['bbs_text_at']} key={index}>
                      {item}&nbsp;
                    </Text>
                  ) : null
                } else {
                  return <Text key={index}>{item}</Text>
                }
              })}
          </>
        ) : (
          textValue
        )}
      </Text>
    </TextInput>

这种方式可以实现文本框里面高亮文本了。

输入框里面插入图片如何实现呢?

假如需要插入图片,上面方式ios不兼容,插入多张图片的时候都不兼容,如何解决呢?只能利用react-native-webview 通过富文本的形式实现。假如通过富文本实现,那么出来的数据结构是如下的:

'<div>你好haorooms博客
<span style="color:#006ff6;" contenteditable="false" data-id="1">@haorooms博客</span> 
<span style="color:#006ff6;" contenteditable="false" data-id="2">#文本高亮话题</span>
这里是rn的渲染</div>';

rn里面出现这种富文本,后端存储2种方式,一种直接存储,这样的话,前台展示的时候也要通过富文本渲染出来。第二种我们可以通过转换为json字符串的形式来存储。例如可以转换为如下:

[{
    type: 'div',
    text: '你好haorooms博客 '
}, {
    type: 'span',
    text: '@haorooms博客 ',
    id: '1'
}, {
    type: 'span',
    text: '#文本高亮话题',
    id: '2'
}, {
    type: 'div',
    text: '这里是rn的渲染'
}]

将html转换为json字符串,需要一些编译解析。我是这么解析的:

1、html转为josn

const operateHtmlToData = (html) => {
    // 改进的正则表达式
    const regex = /<(\w+)([^>]*)?>([^<]*)<\/\1>|<(\w+)([^>]*)?>([^<]*)|([^<]+)/g
    const matches = html.matchAll(regex)

    const result = []

    for (const match of matches) {
      const [, tagName, tagAttrs, tagText, inlineTagName, inlineTagAttrs, inlineTagText, plainText] = match

      if (tagName) {
        let _tagText = tagText.trim()
        _tagText = _tagText.replace(/<br>/g, '\n')
        _tagText = _tagText.replace(/&nbsp;/g, ' ')
        const obj = {
          type: tagName,
          text: _tagText
        }

        if (tagAttrs) {
          const attrs = tagAttrs.split(' ')
          for (const attr of attrs) {
            const [key, values] = attr.split('=')
            if (key === 'data-id') {
              obj.id = values.replace(/("|')/g, '')
            }
          }
        }

        if (obj.text || obj.id) {
          // 只添加有内容或id的对象
          result.push(obj)
        }
      } else if (inlineTagName) {
        let _inlineTagText = inlineTagText.trim()
        _inlineTagText = _inlineTagText.replace(/<br>/g, '\n')
        _inlineTagText = _inlineTagText.replace(/&nbsp;/g, ' ')
        const obj = {
          type: inlineTagName,
          text: _inlineTagText
        }

        if (inlineTagAttrs) {
          const attrs = inlineTagAttrs.split(' ')
          for (const attr of attrs) {
            const [key, values] = attr.split('=')
            if (key === 'data-id') {
              obj.id = values.replace(/("|')/g, '')
            }
          }
        }

        if (obj.text || obj.id) {
          // 只添加有内容或id的对象
          result.push(obj)
        }
      } else if (plainText) {
        // 对于不在任何HTML标签内的纯文本
        let trimmedText = plainText.trim()
        trimmedText = trimmedText.replace(/<br>/g, '\n')
        trimmedText = trimmedText.replace(/&nbsp;/g, ' ')
        if (trimmedText && plainText != '/div>') {
          // 只添加非空文本
          result.push({
            type: 'text',
            text: trimmedText
          })
        }
      }
    }
    return result
  }

2、json转为html

  const renderElement = (element) => {
    switch (element.type) {
      case 'div':
        return `<div>${element.text}</div>`
      case 'at':
        return `<span style='color:#006ff6;' contenteditable="false" data-id=${element.id}>${element.text}</span>`
      case 'text':
        return element.text
      default:
        return null
    }
  }
  const renderData = value.map((item) => renderElement(item)).join('')
  setTextValue(renderData)

这种方式存储,列表也展示也是可以通过循环数组直接展示出来。但是需要遇到div的时候强制换行,span的时候不换行,需要在rn里面实现这个功能。那么可以利用view包裹一层text来实现这个功能。

今天文章暂时分享到这里。

相关文章: