web-dev-qa-db-ja.com

グーグルのようなページネーションの背後にあるロジック

グーグルのページネーション動作の背後にある論理は何ですか?

私のページネーターは次のようなものです:

[1]  2   3  ...  184   >
 <   1  [2]  3   4  ...  184   >
 <   1   2  [3]  4   5  ...  184   >
 <   1   2   3  [4]  5   6   ...  184   >
 <   1  ...  3   4  [5]  6    7   ...  184   >
 <   1  ...  4   5  [6]  7    8   ...  184   >
 <   1  ...  5   6  [7]  8    9   ...  184   >
 <   1  ...  6   7  [8]  9    10  ...  184   >

上記の例のライブバージョンは次のとおりです: http://www.dev.thomaskile.me/?page=test-zone&module=Paginator .
これがなぜ起こっているのか私は知っています。現在のページの両側に表示されるページ番号の数を2に設定しました。

私はむしろ次のように数値の範囲が等しくなるようにしたいと思います:

[1]  2   3   4   5   6   7   8   ...   184   >
 <   1  [2]  3   4   5   6   7   ...   184   >
 <   1   2  [3]  4   5   6   7   ...   184   >
 <   1   2   3  [4]  5   6   7   ...   184   >
 <   1  ...  3   4  [5]  6   7   ...   184   >
 <   1  ...  4   5  [6]  7   8   ...   184   >
 <   1  ...  5   6  [7]  8   9   ...   184   >    
 <   1  ...  6   7  [8]  9   10  ...   184   >

それは最初と最後にあり、いくつかの変更を加える必要がありますが、簡単な操作にする方法がわかりません...
私も柔軟にしたいと思います。つまり、必要なページの数を両側で変更し、スクリプトを拡張してすべて計算できるようにしたいと考えています...

これが今のところ私のコードです:

/**
 *  page controller buttons 
 *  @param str $this->querySting      href="URL string"
 *  @param str $this->pageIdentifier  $_GET['this-name']
 *  @param int $this->numPages        Total amount of pages
 *  @param int $this->midRange        Number of pages to show on each side of current page
 */

public function prevPage() 
{
    if ($this->currentPage > 1){ 
        $prevPage = ($this->currentPage - 1); 
        return '<a href="'.$this->queryString.'&'.$this->pageIdentifier.'='.$prevPage.'" class="prev">prev</a>'; 
    }
}
public function nextPage() 
{
    if ($this->currentPage < $this->numPages) { 
        $nextPage = $this->currentPage + 1;
        return '<a href="'.$this->queryString.'&'.$this->pageIdentifier.'='.$nextPage.'" class="next">next</a>';  
    }  
}
public function firstPage() 
{
    if ($this->currentPage > ($this->midRange + 1)) {  //  if number of pages between "currentPage" and "firstPage" exceeds $midRange with 1...
        $firstPage .= '<a href="'.$this->queryString.'&'.$this->pageIdentifier.'=1" class="first">1</a>';  //  ...show "first page"-link
        if ($this->currentPage > ($this->midRange + 2)) {   //  if number of pages between $currentPage and "first page" exceeds $midRange with more than 1
            $firstPage .= '&hellip;';  //  add "..." between "1st page"-link and first page in $range
        }
    }
    return $firstPage;
}
public function lastPage() 
{
    if ($this->currentPage < ($this->numPages - $this->midRange)) {  //  if number of pages between "currentPage" and "last page" is equal to $midRange
        if (($this->currentPage < ($this->numPages - $this->midRange) - 1)) {  //  if number of pages between $currentPage and "last page" exceeds $range with more than two
            $lastPage .= '&hellip;';  //  add "..." between "last page"-link and last page in $range
        } 
        $lastPage .= '<a href="'.$this->queryString.'&'.$this->pageIdentifier.'='.$this->numPages.'" class="last">'.$this->numPages.'</a>';   //  show "last page"-link
    }
    return $lastPage;
}

#  Range of pages between (prev first ...) and (... last next)
public function listPages() 
{
    for ($i = ($this->currentPage - $this->midRange); $i < (($this->currentPage + $this->midRange) + 1); $i++){
       if (($i > 0) && ($i <= $this->numPages))  //  if page number are within page range
       {
          if ($i == $this->currentPage) { $listPages .= '<a class="current">'.$i.'</a>'; }  //  if we're on current page
          else { $listPages .= '<a href="'.$this->queryString.'&'.$this->pageIdentifier.'='.$i.'">'.$i.'</a>'; }  //  if not current page
        }
    }
    return $listPages; 
}
33
ThomasK

これは私がページネーションのために行うことです。

$startPage = $currentPage - 4;
$endPage = $currentPage + 4;

if ($startPage <= 0) {
    $endPage -= ($startPage - 1);
    $startPage = 1;
}

if ($endPage > $totalPage)
    $endPage = $totalPage;

if ($startPage > 1) echo " First ... ";
for($i=$startPage; $i<=$endPage; $i++) echo " {$i} ";
if ($endPage < $totalPage) echo " ... Last ";

私のコードは自明だと思いますが、わかりやすい英語で説明しようと思います。最初に、ページネーションを生成する前に、2つのことを知っておく必要があります:$ totalPageおよび$ currentPage

ステップ1:現在のページがミッドレンジにあると想定しています。 $ startPageと$ endPageは、ページネーションが生成しようとするページの範囲を格納します。

ステップ2$ startPageが負の場合、$ endPageを埋め合わせる必要があります。

ステップ$ endPage超過$ totalPageの場合、$ endPageが最後のページです。

ステップ4:HTMLへのページ分割の生成。 (ページネーションの見た目はあなた次第です。ページネーションを表すためにプレーンテキストを使用します)

if ($startPage > 1) echo " First ... ";
for($i=$startPage; $i<=$endPage; $i++) echo " {$i} ";
if ($endPage < $totalPage) echo " ... Last ";

以前のロジックの問題を修正

$startPage = ($curPage < 5)? 1 : $curPage - 4;
$endPage = 8 + $startPage;
$endPage = ($totalPage < $endPage) ? $totalPage : $endPage;
$diff = $startPage - $endPage + 8;
$startPage -= ($startPage - $diff > 0) ? $diff : 0;

if ($startPage > 1) echo " First ... ";
for($i=$startPage; $i<=$endPage; $i++) echo " {$i} ";
if ($endPage < $totalPage) echo " ... Last ";
40
invisal

この会話は私にとって素晴らしいスタートでした!しかし、私は元の質問の意図に近いページ編集者を求めていました。
1)合計ページ数、現在のページ、および表示する現在のページの両側のページ数を変更する変数を含む関数に含めることができます。
2)元の投稿と同様に、一定の幅を維持します:

 <  [1]   2    3    4    5    6   7    ...   99   >
 <   1   [2]   3    4    5    6   7    ...   99   >
 <   1    2   [3]   4    5    6   7    ...   99   >
 <   1    2    3   [4]   5    6   7    ...   99   >
 <   1    2    3    4   [5]   6   7    ...   99   >
 <   1   ...   4    5   [6]   7   8    ...   99   >
 <   1   ...   5    6   [7]   8   9    ...   99   >
 <   1   ...   92   93  [94]  95  96   ...   99   >
 <   1   ...   93   94  [95]  96  97   98    99   >
 <   1   ...   93   94   95  [96] 97   98    99   >
 <   1   ...   93   94   95   96 [97]  98    99   >
 <   1   ...   93   94   95   96  97  [98]   99   >
 <   1   ...   93   94   95   96  97   98   [99]  >

3)1 ... 3の場合は、「...」ではなく「2」を表示し続けます。
4)最後に同じこと。

これが私がしたことです。私は別の言語(coffeescript)でコーディングしていますが、とにかくそれは良いSudoコードとして機能するはずです。

get_pages_array = (total_page, each_side, curr_page) ->
    if total_page <= (2*each_side)+5
        # in this case, too few pages, so display them all
        start_page = 1
        end_page = total_page
    else if curr_page<=each_side+3
        # in this case, curr_page is too close to the beginning
        start_page = 1
        end_page = (2*each_side)+3
    else if curr_page >= total_page - (each_side+2)
        # in this case, curr_page is too close to the end
        start_page = total_page - (2*each_side) - 2
        end_page = total_page
    else
        # regular case
        start_page = curr_page - each_side
        end_page = curr_page + each_side
    return_me = []
    if start_page> 1
        return_me.Push "1"
    if start_page>2
        return_me.Push "..."
    for x in [start_page..end_page]
        return_me.Push x
    if end_page<total_page-1
        return_me.Push "..."
    if end_page<total_page
        return_me.Push total_page
    return return_me

私はeach_side = 2にこのコードを使用しているので、それが確実に機能します。

編集:@Vextilによるロジックの修正

5
Autumn Leonard

これは、Pythonプログラムで、これを正しく行う方法を示しています。

def main():
    num_pages = 13
    page = 12

    window = 5
    start = page - window
    end = page + window - 1
    if start <= 0:
        end = end - start + 1
        start = 1
    if end > num_pages:
        end = num_pages
        start = max(end - (window * 2) + 1, 1)

    for no in range(start, end + 1):
        print "{}*".format(no) if page == no else no

if __name__ == '__main__':
    main()
1
John Wheeler

これは純粋に素晴らしいです!私は、このページ編集者が私が説明した方法で機能するようにしたと思います。
ご覧ください。こちらからお試しください http://dev.thomaskile.me/?page=test-zone&module=Paginator お知らせください...

多くの論理的な数学を勉強した後、私はついにこの結論に達しました:
これを異なるレベルで非常に異なるように動作させるには、各レベルのロジックを個別に処理するためにいくつかのifelsef- sが必要です。説明しようと思いますが、良い方法で行うのは難しいと思います...

これらは私が話しているレベルです:

  • CurrentPage == firstPageの場合:
    2ページ目から開始するcurrentPageの後に表示するページ数を計算します。
    この計算は、多くてもページボックスの数に基づいて行う必要がありました。 (midRange値はここで重要な要素です)

    [1] 2   3    4    5    6    7    8   ...   184   >
    
  • elseif currentPageがfirstPageとmidRange値の間にある最大値を超えています。
    範囲内のページを1つ減らして、prevPageが追加された後にページネーション全体を右に移動しないようにします。 currentPageの前後に表示するページを計算して、ページ全体の量を同じにします。

    <   1  [2]   3    4    5    6    7   ...   184   >
    <   1   2   [3]   4    5    6    7   ...   184   >
    <   1   2    3   [4]   5    6    7   ...   184   >
    
  • elseif midRange値が両側で最大になります。私たちはどこかの真ん中にいるという意味です。
    midRangeページ+現在のページ+ midRangeページ。かなりまっすぐ進むと思います...

    <   1  ...   3    4   [5]   6    7   ...   184   >
                          ...
                          ...
                          ...
    <   1  ...  178  179 [180] 181  182  ...   184   >
    
  • elseif currentPageがmidRange値とlastPageの間にある
    最初とほとんど同じです。違いは、ページを開始する静的ページ番号を計算してから、現在のページの前後に表示するページを計算することでした...
    (ところで、これは今週末、私の頭痛の種でした)

    <   1  ...  178  179  180 [181] 182  183   184   >
    <   1  ...  178  179  180  181 [182] 183   184   >
    <   1  ...  178  179  180  181  182 [183]  184   >
    
  • elseif currentPage == numPages(致命的なページの数)。 firstPageオペレーションとほぼ同じです...全体を埋めるために必要なページ数を計算し、どこから開始するかを計算します...

私が今する必要があるのは、コード自体をより良くすることです...

    <   1  ...  178  179  180  181  182  183  [184]  >

私の場合の「問題」は、ページ編集者全体がmidRange値に基づいてすべてを計算し、それ以外は何も計算しないことでした。私の将来のプロジェクトでこのページネーションを実行するには、次のことを行うだけです。

    $paginator = new paginator((int));  //  e.g. number of total results from a db request

ほとんどの場合、a hrefが機能していることを確認するために、個人のクエリ文字列を追加する必要があります。

    $paginator->set_queryString('my querystring');

そして、それはほとんどすべてです。私はこのようないくつかのオプション機能を設定しました:

    $paginator->set_resultsPerPage((int));
    $paginator->set_midRange((int));
    $paginator->set_pageIdentifier('querystring-pageNumber-identifier-name-for-get');  //  whatever I needed

最後に、私はこのようなページネーターページコントローラーを表示します:

    $paginator->pageController('full');  //  full, med, min for different styles.

これらのどれでも十分でない場合は、次のように各ボタンを呼び出すだけです。

    $paginator->prevPage();
    $paginator->firstPage();
    $paginator->listPages();
    $paginator->lastPage();
    $paginator->nextPage();
    $paginator->pageJumper();
    $paginator->perPageSelector();
1
ThomasK

私はあなたのページネーションがこの構造を持っていると思います:

number_of_active_page + separate(...)+ page(184)+ next_page(>)

Number_of_active_pageを8に設定できます(prev_page(<)+ pages(...とページ番号を含む)

[1]  2   3   4   5   6   7   8         ...     184       >
[number_of_active_page(set to 8)] +別+ページ+次のページ 
 <   1  ...  3   4  [5]  6   7         ...     184       >
0
Joko Wandiro