3

React-native中高亮文本实现方案

 6 months ago
source link: https://www.haorooms.com/post/rn_texthighlight
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

React-native中高亮文本实现方案

2024年3月3日 373次浏览

React-native中高亮文本实现方案,rn中文本高亮并不像h5那样,匹配正则,直接添加标签实现,rn中一般是循环实现了。一般是一段文本,拆分出关键词,然后关键词高亮。

const markKeywords = (text, highlight) => {
    if (!text || !highlight) return { value: [text], highlight: [] }

    for (let index = 0; index < highlight.length; index++) {
        const reg = new RegExp(highlight[index], 'g');
        text = text.replace(reg, `**${highlight[index]}**`)
    }

    return {
        markKeywordList: text.split('**').filter(item => item),
        hightList: highlight.map(item => item)
    }
} 

上面可以拆分出可以循环的文本,和要高亮的文本。

const title = 'haorooms前端博文章高亮测试一下'
const highLightWords = ['前端博文', '文章高亮']

因为打上星号标记的原因,文章高亮 在被标记成 前端博文 章高亮 后,并不能被 文章高亮 匹配,而且即使能匹配也不能把 前端博文章高亮 拆成 前端博文文章高亮,如果能拆成 前端博文章高亮 就好了。

function sort(letter, substr) {
  letter = letter.toLocaleUpperCase()
  substr = substr.toLocaleUpperCase()
  var pos = letter.indexOf(substr)
  var positions = []
  while(pos > -1) {
     positions.push(pos)
     pos = letter.indexOf(substr, pos + 1)
  }

  return positions.map(item => ([item, item + substr.length]))
}

// 高亮词第一次遍历索引
function format (text, hight) {
  var arr = []
  // hight.push(hight.reduce((prev, curr) => prev+curr), '')
  hight.forEach((item, index) => {
    arr.push(sort(text, item))
  })

  return arr.reduce((acc, val) => acc.concat(val), []);
}

// 合并索引区间
var merge = function(intervals) {
  const n = intervals.length;

  if (n <= 1) {
      return intervals;
  }

  intervals.sort((a, b) => a[0] - b[0]);

  let refs = [];
  refs.unshift([intervals[0][0], intervals[0][1]]);

  for (let i = 1; i < n; i++) {
      let ref = refs[0];

      if (intervals[i][0] < ref[1]) {
          ref[1] = Math.max(ref[1], intervals[i][1]);
      } else {
          refs.unshift([intervals[i][0], intervals[i][1]]);
      }
  }

  return refs.sort((a,b) => a[0] - b[0]);
}

function getHightLightWord (text, hight) {
  var bj = merge(format(text, hight))
  const c = text.split('')
  var bjindex = 0
  try {
    bj.forEach((item, index) => {
      item.forEach((_item, _index) => {
          c.splice(_item + bjindex, 0, '**')
          bjindex+=1
      })
    })
  } catch (error) {
  }
  return c.join('').split('**')
}

export const markKeywords = (text, keyword) => {

  if (!text || !keyword || keyword.length === 0 ) {
    return { value: [text], keyword: [] }
  }
  if (Array.isArray(keyword)) {
    keyword = keyword.filter(item => item)
  }
  let obj = { value: [text], keyword };
  obj = {
    value: getHightLightWord(text, keyword).filter((item) => item),
    keyword: keyword.map((item) => item),
  };
  return obj;
};

述方法中我们先使用了下标匹配的方式,得到一个下标值的映射,然后通过区间合并的方式把连着的词做合并处理,最后再用合并后的下标值映射去打 ** 标记即可。

简单组件封装

function TextHighLight(props) {
    const { title = '', highLightWords = [] } = props
    const { numberOfLines, ellipsizeMode } = props
    const { style } = props

    const { markKeywordList, hightList } = markKeywords(title, highLightWords)

    return <Text
            numberOfLines={numberOfLines}
            ellipsizeMode={ellipsizeMode}
            style={style}
        >
            {
                markKeywordList ?
                    markKeywordList.map((item,index) => (
                        (hightList && hightList.some(i => (i.toLocaleUpperCase().includes(item) || i.toLowerCase().includes(item))))
                            ? <Text key={index} style={{ color: '#FF6300' }}>{item}</Text>
                            : item
                    ))
                    : null
            }
        </Text>
}

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK