• 前端合并单元格算法-遁地龙卷风


    0.要求

      用户点击A单元格作为起始点,点击B单元格最为终止点,要根据A、B两个点算出四个边界值,用来组成出一个矩形。

      

      上图红色为终止点,绿色为起始点。

    1.算法

      关键点1:合并单元格是通过rowspan,colspan来实现,意味一个单元格代替多个单元格,算法中计算出的单元格位置需要与在视图中看到的一致,所以和在左上角单元格(在边界值组成的矩形中)在同一行的单元格删除,不再同一行的隐藏。

       

      我们用left、top、right、bottom四个属性来表示一个单元格的位置,上图中被蓝矩形标记的单元格位置为,left:1、top:1、right:3、bottom:2,上述做法主要为了确保left的获取。

      第一步:分别计算A、B两个单元格的left、top、bottom、right,取得四个方向的最值来初步画出一个矩形。关键代码如下

      

    getLeft($tr,$td){
            let $tds = $tr.find("td");
            let count = 0;
            for(let i=0;i<$tds.length;i++)
            {
                let $td_temp = $tds.eq(i);
                if($td_temp[0] ==  $td[0])
                {
                    return count;
                }
                let colspan = +$td_temp.attr("colspan") || 1;
                count += colspan;
            }
        }

      得到单元格在所属行的left属性,right根据left属性加1或colspan属性值,top等同于所属行的位置,bottom根据top属性加上1或rowspan属性值。

      关键点2:当被合并的单元格中有已合并单元格的时候,可能出现某个单元格的四个属性超出已划定矩形的情况,一个边界的变化又会影响到其他的边界。所以需要循环确认最终矩形,直到无单元格位置溢出。

      如下例:

      

      当选择绿色矩形标记的单元格为起始点,红色标记的单元格为终止点,黑色边框是第一步组成的矩形,但蓝色标记单元格的右边界溢出,当调整矩形如下时

      

      黄色标记的单元格bottom属性溢出,最终为:

      

      推算左边界的值“   

    get_tdPosLeft($tr,pos){
            let $tds = $tr.find("td");
            let count = 0;
            let colspan = 0;
            for(let i=0;i<$tds.length;i++)
            {
                let $td_temp = $tds.eq(i);
    
                if(count == pos){
                    return count;
                }
                else if(count > pos){
                    return count - colspan;
                }
                colspan = +$td_temp.attr("colspan") || 1;
                count += colspan;
            }
    
        }
    //pos的的值等于划定的左边界值,如果单元格的left属性等于pos属性,说明单元格left属性没有溢出,如果大于pos,则左边界往左减1或者减去它的colspan属性值

      推算右边界的值:

    get_tdPosRight($tr,pos){
            let $tds = $tr.find("td");
            let count = 0;
            for(let i=0;i<$tds.length;i++)
            {
                let $td_temp = $tds.eq(i);
    
                let colspan = +$td_temp.attr("colspan") || 1;
                count += colspan;
    
                if(count == pos){
                    return count;
                }
                else if(count > pos){
                    return count;
                }
            }
        }
    //
    //pos的值等于划定的右边界值,如果单元格的right属性等于pos属性,说明单元格right属性没有溢出,如果大于pos,则右边界溢出,返回单元格右边界的值。
    
    

      推算上边界:

    hasRowCollposed_top($tr,left,right){
            let $tds = $tr.find("td");
            let count = 0;
            for(let i=0;i<$tds.length;i++){
                let $td = $tds.eq(i);
    
                if($td.hasClass("hidden_rwospan")){
    
                    if(count >= left && count < right){
                        return true;
                    }
                    else{
                        break;
                    }
                }
                count +=  +$td.attr("colspan") || 1; 
            }
            return false;
        }
    //这里的left、right等于以推算过后的左边界值和右边界值,如果在left和right之间有td元素含有
    //hidden_rwospan类(因合并单元格而添加到元素上),说明上边界溢出,返回true
    
    
    .hidden_rwospan{
      display:none;
    }
    如下图:


    第二行和第三行的html结构如下:
    <tr>
                        <td><span class="tab">  </span>3</td>
                        <td rowspan="2"><span class="tab">  </span>3</td>
                        <td><span class="tab">  </span>3</td>
                        <td><span class="tab">  </span>4</td>
                    </tr>
                    <tr>
                        <td  ><span class="tab">  </span>3</td>
                        <td class="hidden_rwospan"><span class="tab">  </span>3</td>
                        <td><span class="tab">  </span>3</td>
                        <td><span class="tab">  </span>4</td>
                    </tr>
    
    

    推算下边界:下边界有一个特殊情况,当发现在左右边界内存在拥hidden_rwospan类的单元格时,需要判定是否存在穿透的情况,如下图

    黄色标记的单元格穿透了最初划定的边界。
    hasRowCollposed_bottom($tr,left,right){

    let $tds = $tr.find("td"); let count = 0; for(let i=0;i<$tds.length;i++){ let $td = $tds.eq(i); if(!$td.hasClass("hidden_rwospan")){ if(count >= left && count < right){ let rowspan = +$td.attr("rowspan") || 0; if(rowspan > 1){ return true; } } else if(count>=right){ break; } } else{ // 因为rowspan而隐藏的单元格 if(count >= left && count < right){ //如果隐藏 可能发生上面单元格的rowspan穿透了当前单元格 let right_pos = count + (+$td.attr("colspan") || 1); if (this.isCrossRow_collposed($td,$tr.next(),count,right_pos,left,right)) { return true; } } else if(count>=right){ break; } } count += +$td.attr("colspan") || 1; } return false; }
      //没被隐藏的单元格如果在范围内有单元格拥有rowspan属性且值大于1,则下边界往下推,在范围内拥有
    hidden_rwospan的元素,则需要判定是否发生穿透
     

      判定是否发生穿透:

    
    
    isCrossRow_collposed($td_compare,$tr_next,left_pos,right_pos,left,right){
            //如果在左右边界内有单元格拥有hidden_rwospan类,且left属性和right属性和left_pos、right_pos相同,则bottom需要往下推
           //left_pos 对比单元格左边的位置,right_pos 右边的位置,left 组成矩形的左边界,right 组成矩形右边界
           
            let $tds = $tr_next.find("td");
            let count = 0;
            for(let i=0;i<$tds.length;i++){
                let $td = $tds.eq(i);
                if($td.hasClass("hidden_rwospan")){
                    if(count >= left && count < right){
                        if(count == left_pos ){
                            let colspan = left_pos + ( +$td.attr("colspan") || 1);
                            if(colspan == right_pos){
                                return true;
                            }
                        }
                        else if(count > left_pos){
                            break;
                        }
                    }
                     
                }
                if(count>=right){
                    break;
                }
                count += +$td.attr("colspan") || 1; 
            }
            return  false;
        }
    
    

      核心代码(Esl class语法):

    getArra_collposed($td_start,$td_end){
    
            
            //画出最初矩形
            let left = this.getFarLeft($td_start,$td_end);//得到最靠左单元格的left属性值
            let    top =  this.getFarTop($td_start,$td_end);
            let    right =  this.getFarRight($td_start,$td_end);
            let    bottom = this.getFarBottom($td_start,$td_end);
            
            
            let $trs = this.$ele_current.find("tr"),
                $tr_top = $trs.eq(top),
                $tr_bottom = $trs.eq(bottom-1);
            while(true){
    
                let left_temp = left,
                    top_temp = top,
                    right_temp = right,
                    bottom_temp = bottom;
    
    
                for(let i=top;i<bottom;i++){
                    let $tr = $trs.eq(i);
                    let pos = this.get_tdPosLeft($tr,left);
    
                    if(left_temp > pos){
                        left_temp = pos;
                    }
                }
    
                for(let i=top;i<bottom;i++){//推算右边界
                    let $tr = $trs.eq(i);
                    let pos = this.get_tdPosRight($tr,right);
    
                    if(right_temp < pos){
                        right_temp = pos;
                    }
                }
                
                if(this.hasRowCollposed_top($tr_top,left_temp,right_temp)){//推算上边界
                    top_temp--;
                }
                if(this.hasRowCollposed_bottom($tr_bottom,left_temp,right_temp)){//推算下边界
                    bottom_temp++;
                }
    
                if(left_temp == left && right_temp == right && top_temp == top && bottom_temp == bottom){
                    break;
                }
                else{
              //有一个边界值改变则重新推算 left
    = left_temp; right = right_temp; top = top_temp; bottom = bottom_temp; $tr_top = $trs.eq(top); $tr_bottom = $trs.eq(bottom-1); } } return {left,right,top,bottom}; }

      得到四个边界值就可以得到其内的单元格,以及rowspan属性值大小和colspan属性值大小,最后按照开头说的,选取最终划定矩型左上角的单元格进行合并即可,同行的删除,不同行的隐藏。

      测试的时候重现上述所说的状态,保证所写代码逻辑都走一遍。

     
  • 相关阅读:
    freak out用法
    kinda用法
    比较级与最高级
    issue用法
    invite用法
    yet用法
    follow用法
    get用法
    turn up&turn off&turn on用法
    关于document.lastModified属性
  • 原文地址:https://www.cnblogs.com/resolvent/p/7751616.html
Copyright © 2020-2023  润新知