在 PHP 中创建两个等高的随机纵向和横向图像列
Posted
技术标签:
【中文标题】在 PHP 中创建两个等高的随机纵向和横向图像列【英文标题】:Create two equal height columns of random portrait and landscape images in PHP 【发布时间】:2012-10-18 15:35:58 【问题描述】:给定随机数量的纵向和横向图像,我正在尝试编写 PHP 代码以输出两列等高:
通过将值“1”分配给横向图像,将值“2”分配给肖像,我的想法是将总图像 ($totalphotos) 除以 2,然后检查图像值 ($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'];?>" >
</a>
</div>
<?php
if ($photoval >= $totalphotos/2 || $counter == $totalphotos)
echo " </div>\n";
$photoval = 0;
$totalcolumns++;
if ($totalcolumns == 2)
break;
endfor;
?>
【问题讨论】:
您是否将两幅风景视为一幅肖像的高度? 我不明白你想在这里实现什么。请尝试详细说明,也许添加一个示例测试用例。 @FoolishSeth:是的,我将两张风景图像的高度等同于一张肖像。 问题真的应该发到codereview.stackexchange.com @AD7six:抱歉,我没有意识到区别。 【参考方案1】:这是我的解决方案:
<?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'] ?>"> ></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'] ?>" ></a-->
</div>
<?php endforeach ?>
</div>
<?php ?>
</body>
</html>
【讨论】:
感谢您的努力。您的第一个 while 循环“if 语句”将 >=2 图像的每个实例分配到两列中,左侧是肖像,右侧是风景。给定大量随机照片,所有肖像最终都在左侧。当给定偶数张随机照片时,如果一张是风景,一张是肖像,您的代码通常会丢弃最后两张。代码应包括最后一幅肖像,并且只放下一个风景以使高度相等。最后,我希望更多的代码优化不需要五个循环,这在我的共享服务器上很慢。 @Charlie Nibbana 感谢您的反馈。我会努力改进我的解决方案来解决所有问题。 @Charlie Nibbana 正如承诺的那样,我已经用另一种解决方案更新了我的答案。 您的新解决方案运行良好,尽管它仍然有点资源密集。在决定最佳解决方案之前,我将看看其他人的想法。感谢您的帮助! 您应该为每个解决方案发布一个答案,而不是全部合二为一。【参考方案2】:这是一个很好的问题...好吧,我有一个似乎尽可能高效的解决方案。第一个图像对象在初始堆栈上创建,然后移动到左侧堆栈,只要它有位置,然后移动到右侧堆栈。如果将肖像(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.' </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());
?>
【讨论】:
我喜欢你的逻辑,你的解决方案确实有效,但我需要尝试一下并将其集成到最终产品中。明天我会在测试后更新更多的 cmets。 我非常喜欢您的解决方案,但无法将其集成到我的页面中。您能否帮助将您的示例代码转换为一个解决方案,我可以在示例代码中使用相同的变量和 $photos 数组源?【参考方案3】:分成小块
解决任何问题的最简单方法是单独查看每个位,请参见以下代码:
/**
* 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" ></a>
</div>
</div>
<div class="cell">
<div class="portrait thumbnail">
<a href="z"><img src="y" ></a>
</div>
<div class="portrait thumbnail">
<a href="z"><img src="y" ></a>
</div>
</div>
</div>
<div class="column">
<div class="cell">
<div class="portrait thumbnail">
<a href="z"><img src="y" ></a>
</div>
<div class="portrait thumbnail">
<a href="z"><img src="y" ></a>
</div>
</div>
<div class="cell">
<div class="landscape thumbnail">
<a href="z"><img src="x" ></a>
</div>
</div>
</div>
一些笔记/分析,跟随。
2 个等高的列
renderColumns
方法获取照片数据的嵌套数组,并首先将其转换为 html 字符串的平面数组。此方法假定每个 html 字符串的尺寸相同(1 个横向图像,或并排的 2 个纵向图像)。如果有奇数个 html sn-ps - 它会删除最后一个。
没有孤独的肖像图片
buildCells
方法检查最后一张肖像图像是否是它自己的,如果是 - 删除它。如果这不是您想要的 - 只需删除 return 语句之前删除单独的肖像图像的行。
“单元格”的额外标记
您可能会发现为结果设置样式更容易 - 使用一些一致的 html 包装您的 2 张图像 - 为此我为单元格 div 添加了一些标记:div.column > div.cell > div.thumbnail > img
。如果这不是我们想要的 - 再次,很容易删除。
请注意,除非div.thumbnail
中的标记多于问题中的标记,否则没有必要。
或者用js来做
有两个 js 解决方案,每个都出自同一个作者,它们用 js 做的事情与你用 php 做的事情类似:masonry 和 isotope。使用 js 可以更轻松地解决各种(不仅仅是两种)大小的图像,以及导致最终 html 大小与预期不同的任何其他渲染怪癖。
【讨论】:
此解决方案在集成到我的页面时似乎不起作用。它输出两个图像列,但它们并不总是相同的高度。如果第二列中的最后一张图像是肖像,它会丢弃图像,这样你就可以在左边得到 3 个风景,在右边得到 2 个风景,右列不均匀。不知道为什么它不起作用。 还会抛出数组到字符串的转换警告 - 表示错误 =)。已更正以上是关于在 PHP 中创建两个等高的随机纵向和横向图像列的主要内容,如果未能解决你的问题,请参考以下文章
在phonegap中创建带有响应式图像选项的jquery移动应用程序