1. 程式人生 > >前端合並單元格算法-遁地龍卷風

前端合並單元格算法-遁地龍卷風

行合並 arr this div 範圍 return 12px play nbsp

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屬性值大小,最後按照開頭說的,選取最終劃定矩型左上角的單元格進行合並即可,同行的刪除,不同行的隱藏。

  測試的時候重現上述所說的狀態,保證所寫代碼邏輯都走一遍。

 

前端合並單元格算法-遁地龍卷風