zoukankan      html  css  js  c++  java
  • 前端合并单元格算法-遁地龙卷风

    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属性值大小,最后按照开头说的,选取最终划定矩型左上角的单元格进行合并即可,同行的删除,不同行的隐藏。

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

     
  • 相关阅读:
    codeforces 616B Dinner with Emma
    codeforces 616A Comparing Two Long Integers
    codeforces 615C Running Track
    codeforces 612C Replace To Make Regular Bracket Sequence
    codeforces 612B HDD is Outdated Technology
    重写父类中的成员属性
    子类继承父类
    访问修饰符
    方法的参数
    实例化类
  • 原文地址:https://www.cnblogs.com/resolvent/p/7751616.html
Copyright © 2011-2022 走看看