5

给定随机数量的纵向和横向图像,我正在尝试编写 PHP 代码以输出两列等高:

  • 通过将值“1”分配给横向图像并将“2”分配给肖像,我的想法是将总图像($totalphotos)除以二,然后检查图像值($photoval)是否不超过该数字因为它遍历循环。
  • 例如,如果您总共有 6 张图像,并且第一列已经有 2 张风景图像,并且它遇到的第三张图像是肖像,它将重新排列数组 (array_splice) 并将肖像图像向下移动,然后继续下一个图片。
  • 如果输出已经创建了两个等高的列(比如第一列中的 3 个横向,第二列中的 1 个横向 + 1 个纵向,则丢弃最后一个图像)

我不确定我的方法在循环期间尝试输出 html 是否正确,或者是否更有意义分析数组并首先重新排序图像,然后创建第二个循环来输出 html。

我的代码有点脏,我的“array_splice”方法甚至可能不完全正确。如果你看到更好的方法,请随意废弃我的代码,给我看更好的东西!任何帮助是极大的赞赏!!

<?php

     $photoval = 0;
     $totalcolumns = 0;
     $totalphotos = count($photos);

     for ($counter = 0; $counter < $totalphotos; $counter++) :

            $photo = $photos[$counter];

            if ($photoval == 0) echo "        <div class=\"column\">\n";

            if ($photo['orientation'] == "portrait" && $photoval >= $totalphotos/2) {
            if ($counter == $totalphotos)
                    echo "        </div>\n";                
                    array_splice($photos, $counter, 1, array($photo));

                    continue;
            }
?>
     <div class="<? echo $photo['orientation'] ?> thumbnail">
          <a href="<?php echo $photo['link'] ?>">
               <img src="<?php if ($photo['orientation'] == "landscape") echo $photo['src']; else echo $photo['src_medium'];?>" alt="<? echo htmlentities($photo['caption'], ENT_QUOTES) ?>">
          </a>
     </div>
<?php
            if ($photoval >= $totalphotos/2 || $counter == $totalphotos) {
                echo "        </div>\n";
                $photoval = 0;
                $totalcolumns++;
                if ($totalcolumns == 2)
                    break;
            }

     endfor;
?>
4

3 回答 3

2

这是我的解决方案:

<?php

/*
**  Simulated photo array for testing purposes, to be substituted with the real photo array
*/
$photos = array(
  array('orientation' => 'portrait' , 'link' => '#', 'src' => '', 'src_medium' => '', 'caption' => ''),
  array('orientation' => 'portrait' , 'link' => '#', 'src' => '', 'src_medium' => '', 'caption' => ''),
  array('orientation' => 'portrait' , 'link' => '#', 'src' => '', 'src_medium' => '', 'caption' => ''),
  array('orientation' => 'landscape', 'link' => '#', 'src' => '', 'src_medium' => '', 'caption' => ''),
  array('orientation' => 'landscape', 'link' => '#', 'src' => '', 'src_medium' => '', 'caption' => ''),
  array('orientation' => 'landscape', 'link' => '#', 'src' => '', 'src_medium' => '', 'caption' => ''),
  array('orientation' => 'landscape', 'link' => '#', 'src' => '', 'src_medium' => '', 'caption' => ''),
  array('orientation' => 'landscape', 'link' => '#', 'src' => '', 'src_medium' => '', 'caption' => ''),
  array('orientation' => 'landscape', 'link' => '#', 'src' => '', 'src_medium' => '', 'caption' => '')
);

$album = array(
  'portrait'  => array(),
  'landscape' => array()
);

foreach ($photos as $photo) {
  extract($photo);
  $album[$orientation][] = array(
    'orientation' => $orientation,
    'link'        => $link,
    'src'         => ($orientation == 'landscape') ? $src : $src_medium,
    'caption'     => htmlentities($caption, ENT_QUOTES)
  );
}

$columns = array(
  array(),
  array()
);

while (count($album['portrait']) >= 2) {
  if (count($album['landscape']) >= 2) {
    $columns[0][] = array_shift($album['portrait']);
    $columns[1][] = array_shift($album['landscape']);
    $columns[1][] = array_shift($album['landscape']);
  } else {
    $columns[0][] = array_shift($album['portrait']);
    $columns[1][] = array_shift($album['portrait']);
  }
}

while (count($album['landscape']) >= 2) {
  $columns[0][] = array_shift($album['landscape']);
  $columns[1][] = array_shift($album['landscape']);
}

?>
<!DOCTYPE html>
<html>
<head>
  <style>
  .column { width: auto; float: left; border: dashed 1px #666; padding: 10px }
  .column div:not(:last-child) { margin-bottom: 10px }
  .portrait { width: 200px; height: 200px; background-color: red; }
  .landscape { width: 200px; height: 95px; background-color: green; }
  </style>
</head>
<body>
  <?php foreach ($columns as $photos): ?>
  <div class="column">
    <?php foreach ($photos as $photo): ?>
    <div class="<?= $photo['orientation'] ?>">
      <!-- uncomment the following line when using the real photo array -->
      <!-- a href="<?= $photo['link'] ?>"><img src="<?= $photo['src'] ?>"> alt="<?= $photo['caption'] ?>"></a -->
    </div>
    <?php endforeach ?>
  </div>
  <?php endforeach ?>
</body>
</html>

* 更新 *

另一种解决方案:

<?php

/*
**  Create a random photo array
*/
$photos = array();
$totalphotos = rand(10, 30);
for ($i = 0; $i < $totalphotos; $i++) {
    $o = (rand(0, 1) == 1) ? 'portrait' : 'landscape';
    $photos[] = array('orientation' => $o, 'link' => '#', 'src' => '', 'src_medium' => '', 'caption' => '');
}

//----------------------------------
//--- Here starts the real stuff ---
//----------------------------------

/*
**  The "last" array contains the index of the last added
**  portrait and landscape image in each column of the
**  "album" array
*/
$last = array(
    'portrait' => array(0, 0), 
    'landscape' => array(0, 0)
);

/*
**  Add each photo to the lowest height column
*/
$album = array();
$len  = array(0, 0);
for ($i = 0; $i < $totalphotos; $i++) {
    $o = $photos[$i]['orientation'];
    $c = ($len[0] < $len[1]) ? 0 : 1;
    $len[$c] += ($o == 'portrait') ? 2 : 1;
    $album[$c][] = $i;
    $last[$o][$c] = count($album[$c]) - 1;
}

/*
**  If the columns heights are different,
**  remove the last portrait or landscape image
**  from the highest column
*/
$c = ($len[0] > $len[1]) ? 0 : 1;
$diff = abs($len[0] - $len[1]);
//echo "<pre>diff={$diff}</pre>\n";
if ($diff == 1) {
    unset($album[$c][$last['landscape'][$c]]);
} else if ($diff == 2) {
    unset($album[$c][$last['portrait'][$c]]);
}

?>
<!DOCTYPE html>
<html>
    <head>
        <style>
        .column { border: dashed 1px #666; width: 50px; padding: 0 10px 10px 10px; overflow: auto; float: left; }        
        .column div { margin: 10px 5px 0 0; }
        .portrait { width: 50px; height: 100px; background-color: red; }
        .landscape { width: 50px; height: 45px; background-color: green; }
        </style>
    </head>
    <body>
        <?php for ($c = 0; $c < 2; $c++) { ?>
        <div class="column">
            <?php foreach ($album[$c] as $p): ?>
            <div class="<?= $photos[$p]['orientation'] ?> thumbnail">
                <!--a href="<?= $photos[$p]['link'] ?>"><img src="<?= $photos[$p]['src'] ?>" alt="<?= $photos[$p]['caption'] ?>"></a-->
            </div>
            <?php endforeach ?>
        </div>
        <?php } ?>
    </body>
</html>
于 2012-10-22T08:24:22.673 回答
1

分成小块

解决任何问题的最简单方法是单独查看每个位,请参见下面的代码:

/**
 * Turn the array of photos into 2 equal height columns
 *
 * @param array photos - array of photos
 * @return string
 */
function printPhotos($photos) {
    $cells = buildCells($photos);
    return renderColumns($cells);
}

/**
 * Take the input array, and build an indexed array
 *
 * Use variable references to construct portrait and landscape arrays,
 * and maintain an ordered list such that the original order (after
 * accounting for the cell logic) is maintained.
 * If at the end there is one portrait image on its own - delete it.
 *
 * @param array photos - array of photos
 * @return array
 */
function buildCells($photos) {
    $return = array(
        'ordered' => array(),
        'landscape' => array(),
        'portrait' => array()
    );
    $i = 0;

    foreach($photos as $photo) {
        unset($cell);
        $orientation = $photo['orientation'];

        if ($orientation === 'portrait') {
            if (empty($return['portrait'][$i])) {
                $cell = array();
                $return['portrait'][$i] =& $cell;
                $return['ordered'][] =& $cell;
            } else {
                $cell =& $return['portrait'][$i];
            }
            $cell[] = $photo;

            if (count($cell) === 2) {
                $i++;
            }
        } else {
            $cell = array($photo);
            $return['landscape'][] =& $cell;
            $return['ordered'][] =& $cell;
        }

    }

    if (count($return['portrait'][$i]) === 1) {
        $return['portrait'][$i] = null;
        $return['portrait'] = array_filter($return['portrait']);
        $return['ordered'] = array_filter($return['ordered']);
    }

    return $return;
}

/**
 * Convert the output of buildCells into html
 *
 * @param array cells - indexed array of cells
 * @return string column html
 */
function renderColumns($cells) {
    $orderedCells = renderCells($cells);

    $cellsPerColumn = (int)(count($orderedCells) / 2);
    $columns = array_slice(array_chunk($orderedCells, $cellsPerColumn), 0, 2);

    $return = '';

    foreach($columns as $cellsInColumn) {
        $return .= "<div class=\"column\">\n";
        $return .= implode('', $cellsInColumn);
        $return .= "</div>\n";
    }

    return $return;
}

/**
 * Process portrait and landscape photo-cells
 *
 * Converts the array representation of cells into html, and returns
 * The cells in presentation order
 *
 * @param array cells - indexed array of cells
 * @return array
 */
function renderCells($cells) {
    foreach(array('landscape', 'portrait') as $orientation) {
        foreach($cells[$orientation] as &$cell) {
            $cell = renderCell($cell, $orientation);
        }
    }

    return $cells['ordered'];
}

/**
 * For each photo in the cell - turn it into html
 *
 * @param array cell - array of photo(s)
 * @param string orientation
 * @return string
 */
function renderCell(&$cell, $orientation) {
    $return = "\t<div class=\"cell\">\n";

    foreach($cell as $photo) {
        $return .= renderPhoto($photo, $orientation);
    }

    $return .= "\t</div>\n";

    return $return;
}

/**
 * Convert the photo into a html string
 *
 * @param array photo
 * @param string orientation
 * @return string
 */
function renderPhoto($photo, $orientation) {
    if ($orientation === 'landscape') {
        $src = $photo['src'];
    } else {
        $src = $photo['src_medium'];
    }
    $caption = htmlentities($photo['caption'], ENT_QUOTES);

    $return = "\t\t<div class=\"$orientation thumbnail\">\n";
    $return .= "\t\t\t<a href=\"{$photo['link']}\"><img src=\"$src\" alt=\"$caption\"></a>\n";
    $return .= "\t\t</div>\n";

    return $return;
}

通过创建只做一件事的函数 - 它可以更容易地验证代码是否符合您的期望。该问题中有许多要求,如果将其编写为单个代码块,则难以验证是否满足这些要求。

主要功能是buildCells

例子

鉴于此示例数据:

$data = array(
    array('src' => 'x', 'src_medium' => 'y', 'orientation' => 'landscape', 'link' => 'z', 'caption' => 'one'),
    array('src' => 'x', 'src_medium' => 'y', 'orientation' => 'portrait', 'link' => 'z', 'caption' => 'two'),
    array('src' => 'x', 'src_medium' => 'y', 'orientation' => 'portrait', 'link' => 'z', 'caption' => 'three'),
    array('src' => 'x', 'src_medium' => 'y', 'orientation' => 'portrait', 'link' => 'z', 'caption' => 'four'),
    array('src' => 'x', 'src_medium' => 'y', 'orientation' => 'landscape', 'link' => 'z', 'caption' => 'five'),
    array('src' => 'x', 'src_medium' => 'y', 'orientation' => 'portrait', 'link' => 'z', 'caption' => 'six'),
    array('src' => 'x', 'src_medium' => 'y', 'orientation' => 'portrait', 'link' => 'z', 'caption' => 'seven')
);

echo printPhotos($data);

问题中包含的代码输出是:

<div class="column">
    <div class="cell">
        <div class="landscape thumbnail">
            <a href="z"><img src="x" alt="one"></a>
        </div>
    </div>
    <div class="cell">
        <div class="portrait thumbnail">
            <a href="z"><img src="y" alt="two"></a>
        </div>
        <div class="portrait thumbnail">
            <a href="z"><img src="y" alt="three"></a>
        </div>
    </div>
</div>
<div class="column">
    <div class="cell">
        <div class="portrait thumbnail">
            <a href="z"><img src="y" alt="four"></a>
        </div>
        <div class="portrait thumbnail">
            <a href="z"><img src="y" alt="six"></a>
        </div>
    </div>
    <div class="cell">
        <div class="landscape thumbnail">
            <a href="z"><img src="x" alt="five"></a>
        </div>
    </div>
</div>

一些笔记/分析,跟随。

2个等高柱

该方法renderColumns采用照片数据的嵌套数组,并首先将其转换为 html 字符串的平面数组。此方法假定每个 html 字符串的尺寸相同(1 个横向图像,或并排的 2 个纵向图像)。如果有奇数个 html 片段 - 它会删除最后一个。

没有孤独的肖像图像

该方法buildCells检查最后一个肖像图像是否是它自己的,如果是 - 将其删除。如果这不完全是您想要的 - 只需删除 return 语句之前删除孤独的肖像图像的行。

“单元格”的额外标记

您可能会发现为结果设置样式更容易——用一些一致的 html 包装你的 2 张图像——因此我为单元格 div 添加了一些标记:div.column > div.cell > div.thumbnail > img。如果这不是我们想要的 - 再次,很容易删除。

请注意,除非div.thumbnail问题中的标记更多,否则没有必要。

或者用js做

有两个 js 解决方案,每个都出自同一个作者,它们用 js 做的事情类似于你用 php 做的事情:masonryisotope。使用 js 可以更轻松地解决各种(不仅仅是两种)大小的图像,以及导致最终 html 大小与预期不同的任何其他渲染怪癖。

于 2012-10-28T08:42:11.093 回答
1

这是一个很好的问题......好吧,我有一个似乎尽可能高效的解决方案。第一个图像对象在初始堆栈上创建,然后移动到左侧堆栈,只要它有位置,然后移动到右侧堆栈。如果将肖像(2)移动到左侧堆栈并且只有风景(1)的位置,则将风景移动到垃圾堆,并将肖像移动到左侧。右侧的逻辑相同。

只需保存此代码并使用它...(它是自我发布的)

<style type="text/css">
    * { margin: 0; padding: 0; }
    ul { list-style-type: none; width: 100px; }
    b { color: white; }
    .landscape { width: 100px; height: 50px; background-color: #f00; }
    .portrait { width: 50px; height: 100px; background-color: #00f; }
</style>

<form action="<?php echo $_SERVER['REQUEST_URI']; ?>" method="get">
    number of images to simulate: <input type="text" name="nimgs" value="<?php if(isset($_REQUEST['nimgs'])) echo intval($_REQUEST['nimgs']); else echo 10; ?>" /><br/>
    <input type="submit">
</form><br/>
<hr/>

<?php

    class ImageStack
    {
        public $images, $height;
        function __construct()
        {
            $this->images = array();
            $this->height = 0;
        }
        function push($image)
        {
            if( $image === false ) return; # catch failed calls to pop()
            $this->height += $image['height'];
            array_push( $this->images, $image );
        }
        function shift()
        {
            $image = array_shift( $this->images );
            $this->height -= $image['height'];
            return $image;
        }
        function total()
        {
            return $this->height;
        }
        function has($size)
        {
            foreach( $this->images as $i )
            {
                if( $i['height'] == $size ) return true;
            }
            return false;
        }
        function move( $size, $to, $max )
        {
            foreach( $this->images as $key => $value )
            {
                if( $value['height'] == $size )
                {
                    if( $to->total() + $size <= $max )
                    {
                        $this->height -= $size;
                        $to->push( $value );
                        unset( $this->images[$key] );
                    }
                    return;
                }
            }
        }
        function output( $msg )
        {
            echo '<ul style="float: left; margin-left: 10px; background-color: #000;"><b>'.$msg.'&nbsp;</b>';
            foreach( $this->images as $image )
            {
                echo "<li class='".($image['height'] == 1 ? 'landscape' : 'portrait')."'>$image[src]</li>";
            }
            echo '</ul>';
        }
    }

    ### create the random images ###
    $nimgs = intval($_REQUEST['nimgs']);

    $images = new ImageStack;
    for($i=0; $i<$nimgs; $i++)
    {
        $images->push(  array( 'height' => 1+rand()%2, 'src' => "img $i" )  );
    }

    ### write the first column ###
    $images->output('All: '.$images->total());

    ### initialization ###
    $half  = floor($images->total() / 2);
    $left  = new ImageStack;
    $right = new ImageStack;
    $temp  = new ImageStack;

    ### if we got an odd total() of image height, remove a single 1 to temp ###
    if( $images->total() % 2 )
    {
        $images->move(1, $temp, 3*$half); # sad but true: this moves the first one to temp, better would be the last
    }

    ### while we got images on the initial stack ###
    while( $images->total() )
    {
        $next = $images->shift();
        if( $left->total() + $next['height'] <= $half )             # enough space @ left
        {
            $left->push( $next );
        }
        else
        {
            if( $left->total() < $half && $left->has(1) )           # enough space @ left if we move a 1 to temp
            {
                $left->move( 1, $right, $half );
                $left->push( $next );
            }
            else
            {
                if( $right->total() + $next['height'] <= $half )    # enough space @ right
                {
                    $right->push( $next );
                }
                else
                {
                    if( $right->total() < $half && $right->has(1) ) # enough space @ right if we move a 1 to temp
                    {
                        $right->move(1, $temp, 3*$half);
                        $right->push( $next );
                    }
                    else                                            # nowhere enough space left, except @ temp
                    {
                        $temp->push( $next );
                    }
                }
            }
        }
    }

    $left->output('Left: '.$left->total());
    $right->output('Right: '.$right->total());
    $temp->output('Ignore: '.$temp->total());   
?>
于 2012-10-27T19:23:16.363 回答