读书笔记选择文本高亮功能实现

28 阅读1分钟

需求: 鼠标右键弹出标记和取消标记按钮,可以显示历史标记文本,可以多个标记及重复标记。 这里主要记录显示历史标记文本如何实现

目前历史标记接口给的数据格式是 {startIndex:'4,1,2,0,2',endIndex:'6,3,4,2,4',isMark:'1,0,1,0,1'}, isMark,0代表取消标记,1代表标记。

1,首先需要搞清楚渲染到页面的数据格式

```handleHistoryTemplate = () => {
     const {context} = this.props;
     const {text, markRange} = context;
     return markRange ? (
         <>
         {text.slice(0, markRange[0]}
         {
         Array(markRange.length/2).fill(0).map((item,index) => {
            return (
                <>
                    <span className="highlighted can-select">
                        {text.slice(markRange[2*index], markRange[2*index+1]+1}
                     </span>
                     {text.slice(markRange[2*index+1]+1, markRange[2*index+2]}
                </>
            )
         }
         </>
     ):text;
 }
**由于上面的渲染结构可知,需要的是从小到大的有序高亮下标集合 如[1,2,4,5,8,10]**

2,将从接口中拿到的字符串数组通过处理,最终生成从小到大的有序高亮下标集合
(排除交叉标记,重复标记,及后面的取消标记等情况)
从接口拿到的数据 const lists = {start_index:'4,1,2,0,2',end_index:'6,3,4,2,4',is_mark:'1,0,1,0,1'}

lists.forEach((lItem, index) => {
    const {start_index, end_index, is_mark} = lItem;
    let startIndex = start_index && start_index.split(',').map(Number);
    let endIndex = end_index && end_index.split(',').map(Number);
    let isMark = is_mark && is_mark.split(',').map(Number);
    let arr = isMark && new Array(isMark.length).fill([]) || [];
    isMark && isMark.forEach((item, index) => {
        arr[index] = [startIndex[index], endIndex[index], isMark[index]];
    })
    let mardRange = isMark && this.getMarkRanges(this.getMarkIndex(arr));
    lItem = Object.assign(lItem, mardRange)
})

// 获取标记的下标数组
getMarkIndex = (arr) => {
    const maxEnd = Math.max(...arr.map((range) => range[1]));
    const marks = Array(maxEnd + 1).fill(0);
    arr.forEach((range) => {
        const start = range[0];
        const end = range[1];
        const mark = range[2];
        if(mark == 1) {
            for(let i = start; i <= end; i++) {
                marks[i] = 1;
            }
        } else {
            for(let i = start; i <= end; i++) {
                marks[i] = 0;
            }
        }
    })
    const output = [];
    marks.map((num, index) => {
        num == 1 && output.push(index)
    })
    return output
}

// 获取标记的下标区间数组
getRanges(arr) {
  let results = [];
  let start = arr[0];
  let end = arr[0];

  for (let i = 1; i < arr.length; i++) {
    if (arr[i] !== end + 1) {
      results.push(start, end);
      start = arr[i];
      end = arr[i];
    } else {
      end = arr[i];
    }
  }

  start && results.push(start, end);
  return results;
}