Итак, вот и наступил день, когда клиент перезвонил и сказал - я хочу себе не только короткий список фамилий и имен политиков на своем новостном портале, но и также в виде сетки с фотографиями. После того, как я ознакомился с макетом, там идет 3 горизонтальные колонки в виде слитных картинок по направляющим, я решил сразу приступить к коду, но без подложки, то есть без заднего фона, чтобы увидеть, где и как могут происходить огрешности
Код вызывается из любого места в программе на PHP и оформлен в виде классов, хотя для очевидной инициализации GridSystem у нас в конструкторе идет кусочками отреверсированный по ключам array
get_header();
$args = array(
‘posts_per_page’ => 25,
'order’ => 'DESC’,
'post_type’ => 'whois’,
);
query_posts($args);
$images = array();
$grid = array();
$coords = array();
$picture_sizes = array();
while(have_posts()) {
the_post();
if(!isset($coords[4]) || $coords[4] >= 3) {
$size_koeff = mt_rand(1,2);
$coords[$size_koeff] = $coords[$size_koeff] + 1;
}
elseif(!isset($coords[2]) || $coords[2] >= 5 && $coords[2] <= 10) {
$size_koeff = mt_rand(1,2) * 2;
$coords[$size_koeff] = $coords[$size_koeff] + 1;
} else {
$size_koeff = 1;
}
//$size_koeff = get_post_meta(get_the_ID(), 'size_koeff’);
if(!get_post_thumbnail_id(get_the_id()))
continue;
$picture = wp_get_attachment_url(get_post_thumbnail_id(get_the_ID()));
$images[$size_koeff . ’_bf’ . mb_substr(md5(rand(0, 100)), 0, 4)]
= $picture;
if(!isset($picture_sizes[$size_koeff]))
$picture_sizes[$size_koeff] = 1;
else
$picture_sizes[$size_koeff] = $picture_sizes[$size_koeff] +1;
}
wp_reset_postdata();
arsort($picture_sizes);
ksort($pictures, SORT_STRING);
$gridCS = new GridView($picture_sizes, $images);
echo $gridCS->getGridSystem();
$js = <<< EOL
Теперь же посмотрим как работает сам класс GridView, в нем нету аггрегации других классов, о боги, это для того, чтобы реально не было накопления лишенго мусора, за то есть сам аргумент в некоторых функциях по типу Point
Сейчас приведу пример класса поинт
class Point {
public $x, $y;
public $picture_url;
public $size;
}
Геттеры и сеттеры не делал, так как не сильно переживаю, что может что-то выйти из подсистемы моей. Потом в принципе, нужно определиться с тем какой же именно размер нам нужен для построения сетки изображений, поэтому в некоторой функции ставим определение размера
public function getGridSystem() {
//self::$pictures = &$pictures;
//self::$picture_sizes = &$picture_sizes;
$this->getCoords();
foreach(self::$pictures as $key => $value) {
$koef = mb_substr($key, 0, 1);
switch($koef) {
case '4’:
$cell = $this->getCellForGrid(array_pop(self::$pictures), 4);
break;
case '2’:
$cell = $this->getCellForGrid(array_pop(self::$pictures), 2);
break;
case '1’:
$cell = $this->getCellForGrid(array_pop(self::$pictures), 1);
break;
default:
$cell = $this->getCellForGrid(array_pop(self::$pictures), 1);
}
if(!$cell) die('Something went wrong’);
}
return $this->getDefaultView();
}
Ах да, Вы можете спросить что такое self::$pictures - это ссылка на массив, что пришла в конструктор класса. Вот сам конструктор
public function __construct($picture_sizes = array(), $pictures = array()) {
self::$grid = array();
self::$coords = array();
self::$grid_height = 0;
self::$picture_sizes = &$picture_sizes;
self::$pictures = &$pictures;
}
Потом мы должны же сначала рандомно подставить в ячейку изображение и откалибрировать ее, для этого я использую 2 функции: getCellForGrid и getNewPointSIze
protected function getCellForGrid($picture_url, $size = 1) {
//if($size == 4) {
$ready = 0;
while(!$ready) {
$ready = 0;
$point = $this->getNewPointSize(null, $size);
if(!empty(self::$coords)) {
foreach(self::$coords as $value) {
if($value->y == $point->y && $value->x == $point->x || ($point->x >= $value->x && $point->x <= ($value->x + $value->size) && ($point->y >= $value->y && $point->y <= $value->y + $value->size)))
$point = $this->getNewPointSize($point, $size);
}
}
$point->picture_url = $picture_url;
$point->size = $size;
array_push(self::$coords, $point);
$ready = 1;
}
//}
return true;
Хотел еще добавить в поинт класс новый параметр opacity, для того, чтобы для разных размеров была возможность в CSS указать прозрачность, но клиент, а этот клиент когда я ему начал опубликовывать код - стал спрашивать, может, ли он бесплатно распространять мой плагин, на что я ему ответил, O, Jeremy, do your business. If you want this code to be opensource - nevermind!
И вот кусок кода для шифта изображения, если код случайно выставил изображение с оверлепом.
protected function freeCellFromCoords(Point $obj, $size = 1, $recursionLvl = 0) {
if(++$recursionLvl >= 4)
return $obj;
$imageStub = array();
for($k = $obj->x; $k < $obj->x + $size; $k++)
array_push($imageStub, $k);
$ready = 0;
$rowsStart = $obj->y;
$colStart = $obj->x;
for($i = $rowsStart; $i < $rowsStart + $size; $i++) {
for($j = $colStart; $j < $colStart + $size; $j++) {
if(array_slice(self::$coords[$i], $j, $size) != $imageStub) {
$obj = $this->shiftLocalImage($obj, $recursionLvl);
$this->freeCellFromCoords($obj, $size, $recursionLvl);
break(1);
$imageStub = array();
for($k = $obj->x; $k < $obj->x + $size; $k++)
array_push($imageStub, $k);
}
}
}
return $obj;
}
protected function shiftLocalImage(Point $obj, $recursionLvl = 1) {
switch($recursionLvl) {
case 1:
$obj->x = $obj->x + $obj->size;
return $obj;
case 2:
$obj->x = $obj->x - $obj->size;
$obj->y = $obj->y + $obj->size;
return $obj;
case 3:
$obj->x = $obj->x + $obj->size;
return $obj;
}
return $obj;
}
Это собственно две функции с учетом рекурсии, я пробовал разные алгоритмы, но остановился на самом простом - не более 5-ти вложенных рекурсий, со сдвигом изображения то вправо, то назд из правого положения в исходное, и вниз.
обычный массив - 2-мерный, с строками {1,2,3,4,5,6,7,8….}, как только у нас что-то не совпадает сигнал - на рекурсию, и еще этот массив используется-инициализируется в функции getCoords. Сейчас приведу этот код:
protected function getCoords() {
$matrix = array();
//if(self::$grid_height == 0) {
$nearByHeight = count(array_keys(self::$picture_sizes, 2));
self::$grid_height = 3;//$nearByHeight * 4 + 1;
//}
for($i = 0; $i < self::$grid_height; $i++) {
$row = array();
for($j = 0; $j < IMAGES_IN_ROW; $j++) {
array_push($row, $j);
}
$matrix[$i] = $row;
}
self::$coords = $matrix;
}
Итак, после тестирования этого кода, он оказался немного глючным, я не имею возможности понять почему. Если кто поможет довести его до ума - буду очень признателен.
Для тех, кто заинтересован в построении сетки на JS, смотрите, пожалуйста, код masonry.js, там все простой и элегантный подход.