Upd: English version of the article: Inscribe an image into container using PHP server-side code.


Задача возникла, когда делал клиенту сайт с электронным магазином. Картинки товаров на страницах должны выводиться единообразно; сам же клиент имеет картинки всех возможных размеров и пропорций. Чтобы освободить его от редактирования изображений, было решено вписывать их в контейнер - блок <div> фиксированного размера, обрамленный рамкой. Вытянутые по горизонтали имиджи вписываться будут по ширине, вытянутые по вертикали - соответственно, по высоте. Сами изображения должны быть кликабельны.

В принципе, задача довольно типичная и имеет существующие варианты решений, например, только через CSS-стили. Однако, когда в руках такое мощное оружие как серверный код, грех им не воспользоваться, когда ты сам не дизайнер, а программер. Поэтому я написал функцию, охватывающую все возможные соотношения сторон контейнера и вписываемого изображения. Ее и предлагаю на суд общественности.

Назовем нашу функцию out_adjusted_link_image. Ее определение будет иметь такой вид:

function out_adjusted_link_image($img_file, $img_alt, $img_title,
                                 $a_href, $div_wd, $div_ht,
                                 $div_more_style = '', $a_more = '')

Параметры:

Имя
Тип
Описание
$img_file
string
путь и имя файла изображения
$img_alt, $img_title
string
атрибуты alt и title тега имиджа
$a_href
string
URL, открываемый по клику на картинке
$div_wd, $div_ht
int
ширина и высота контейнера
$div_more_style
string
дополнительные атрибуты CSS-стиля контейнера (опционально)
$a_more
string
дополнительные атрибуты тега анкора (опционально)

Возвращаемое функцией значение - строка, содержащая HTML-скрипт нашей конструкции контейнер - линк - имидж.

Строим тело функции. Пошагово:

1. Определяем константы, задающие оба измерения, по которым будем вписывать:

define('ALIGN_BY_WD', 0);
define('ALIGN_BY_HT', 1);

2. Выясняем размеры имиджа. Для этого воспользуемся функцией getimagesize(). В качестве параметра эта функция требует имя файла образа, включая путь к нему. Возвращает она массив из 7 элементов, из которых нас интересуют первые 2 (т. е. с индексами 0 и 1), содержащие соответственно ширину и высоту имиджа.

$size = getimagesize($img_file);
$img_wd = $size[0];
$img_ht = $size[1];

3. Определяем, по какому измерению вписывать. Если контейнер более "сплющен" по вертикали, чем имидж, то после подгонки по размеру имидж впишется по высоте, в обратном случае - по ширине. Данная логика реализуется следующим фрагментом кода PHP:

if ($div_wd / $div_ht > $img_wd / $img_ht)
  $align = ALIGN_BY_HT;
else
  $align = ALIGN_BY_WD;

Частный случай равенства пропорций обоих прямоугольников, когда выравнивать можно по любому измерению, обрабатывается в блоке else.

4. Вписываем нашу картинку в контейнер. Для этого вычисляем ширину и высоту вписанного имиджа - $new_img_wd и $new_img_ht. Для последующего центрирования определяем также переменные $rel_edge и $abs_edge - края для относительного и абсолютного позиционирования по соответствующим измерениям, и $new_img_dim - размер, который будем использовать для установки отступа. Присваиваем им значения в соответствии с направлениями выравнивания.

if ($align == ALIGN_BY_HT) {
  $new_img_ht = $div_ht;
  $new_img_wd = $new_img_ht * $img_wd / $img_ht;
  $rel_edge = 'left';
  $abs_edge = 'top';
  $new_img_dim = $new_img_wd;
}
else {
  $new_img_wd = $div_wd;
  $new_img_ht = $new_img_wd * $img_ht / $img_wd;
  $rel_edge = 'top';
  $abs_edge = 'left';
  $new_img_dim = $new_img_ht;
}

5. Центрируем имидж. Вписанный по ширине - по вертикали, вписанный по высоте - по горизонтали. Для этого используем технику выравнивания, описанную в предыдущей статье. Помещаем искомую строку стиля в переменную $img_style:

$img_style = 'position:absolute; ' . $rel_edge . ':50%; width:' .
             $new_img_wd . 'px; height:' . $new_img_ht . 'px; margin-' .
             $rel_edge . ':-' . ($new_img_dim / 2) . 'px; ' .
             $abs_edge . ':0';

Теперь можно выстроить всю конструкцию контейнер - анкор - имидж, строку с которой и будет возвращать наша функция:

return '<div style="position:relative; width:' . $div_wd .
       'px; height:' . $div_ht . 'px; border:1px solid silver;' .
       $div_more_style . '"><a href="' . $a_href . '" '. $a_more .
       '><img src="' . $img_file . '" alt="' . $img_alt . '" title="' .
       $img_title . '" style="' . $img_style . '" /></a></div>';

Вот как она (конструкция то бишь) выглядит в браузере:

Monkey

Достигли ли мы желаемого результата? Не совсем. Попробуйте навести курсор на наш блок и убедитесь, что поля между имиджем и границами контейнера не попадают в ссылку. Чтобы ссылка охватывала весь блок, сделаем следующее:

  1. Наш вписанный и отцентрированный имидж расположим перед анкором ссылки.
  2. Вместо него внутрь анкора поместим прозрачный PNG-имидж, размеры которому зададим равными размерам контейнера.

Таким образом, наша картинка перекроется прозрачным имиджем и останется видимой. Зона же действия линка растянется до границ контейнера.

Модифицированный фрагмент кода, возвращающий значение функции, примет следующий вид:

return '<div style="position:relative; width:' . $div_wd .
       'px; height:' . $div_ht . 'px; border:1px solid silver;' .
       $div_more_style . '"><img src="' . $img_file . '" style="' .
       $img_style . '" /><a href="' . $a_href . '" '. $a_more .
       '><img src="images/Transparent.png" alt="' .
       $img_alt . '" title="' . $img_title . '" width="' .
       $div_wd . '" height="' . $div_ht .
       '" style="position:absolute; top:0px; left:0px;" /></a></div>';

Наведите курсор теперь и почувствуйте разницу:

Monkey

Собрав воедино все вышеприведенные части кода, получим функцию в окончательном виде:

function out_adjusted_link_image($img_file, $img_alt, $img_title,
                                 $a_href, $div_wd, $div_ht,
                                 $div_more_style = '', $a_more = '') {
  define('ALIGN_BY_WD', 0);
  define('ALIGN_BY_HT', 1);
 
  $size = getimagesize($img_file);
  $img_wd = $size[0];
  $img_ht = $size[1];
 
  if ($div_wd / $div_ht > $img_wd / $img_ht)
    $align = ALIGN_BY_HT;
  else
    $align = ALIGN_BY_WD;
 
  if ($align == ALIGN_BY_HT) {
    $new_img_ht = $div_ht;
    $new_img_wd = $new_img_ht * $img_wd / $img_ht;
    $rel_edge = 'left';
    $abs_edge = 'top';
    $new_img_dim = $new_img_wd;
  }
  else {
    $new_img_wd = $div_wd;
    $new_img_ht = $new_img_wd * $img_ht / $img_wd;
    $rel_edge = 'top';
    $abs_edge = 'left';
    $new_img_dim = $new_img_ht;
  }
 
  $img_style = 'position:absolute; ' . $rel_edge . ':50%; width:' .
               $new_img_wd . 'px; height:' . $new_img_ht . 'px; margin-' .
               $rel_edge . ':-' . ($new_img_dim / 2) . 'px; ' .
               $abs_edge . ':0';
 
  return '<div style="position:relative; width:' . $div_wd .
         'px; height:' . $div_ht . 'px; border:1px solid silver;' .
         $div_more_style . '"><img src="' . $img_file . '" style="' .
         $img_style . '" /><a href="' . $a_href . '" '. $a_more .
         '><img src="images/Transparent.png" alt="' .
         $img_alt . '" title="' . $img_title . '" width="' .
         $div_wd . '" height="' . $div_ht .
         '" style="position:absolute; top:0px; left:0px;" /></a></div>';
}

Пример вызова функции. Именно он рендерит в вашем браузере эту милую вписанную в кликабельный квадрат мартышку:

echo out_adjusted_link_image('images/monkey.jpg', 'Monkey', 'Monkey',
                    '#anchor', 200, 200,
                    'left:50%; margin-left:-100px', 'target="_blank"');

Здесь еще и сам контейнер отцентририрован по горизонтали внутри своего собственного родительского блока все по той же методике.

В одной из последующих статей я планирую описать, как я встроил приведенный здесь код в содержимое материала сайта на Joomla.

Комментарии  

Николай
0 # Николай 05.03.2014 05:00
Доброго времени суток (у Вас, наверное, поздний вечер в данный момент)! Не могли бы помочь ссылкой на информацию по следующему вопросу? Сайт на Joomla 3. Необходимо дать ссылку (в моем случае, на всплывающее окно) на кусочек рисунка бэкграунда одной из позиций.
Я пытался добавить модуль с HTML в данное место, прописав в пользовательский текст прозрачную картинку и присвоив модулю стиль: position:absolute; margin-left; margin-top;
Но выше другой модуль с меню с аккордеоном и при раскрытии аккордеона моя прозрачная картинка-ссылка смещается.
Ответить | Ответить с цитатой | Цитировать
Николай
0 # Николай 05.03.2014 05:12
Забыл добавить, что сайтостроением занимаюсь менее полугода и программированием не владею.
Ответить | Ответить с цитатой | Цитировать
Ogri
0 # Ogri 05.03.2014 14:12
Здравствуйте, Николай. Попробовал смоделировать ваш случай: прямо в эту статью перед мартышками вставил аккордеон (на локалке, понятно). Картинка-ссылка никуда не съехала. Предположу поэтому, что у вас где-то в реализации ошибка. А может, и в другом месте страницы что-то влияет на нормальный поток документа. Пришлите код страницы, попробую разобраться. Можете приаттачить на мыло, .
Ответить | Ответить с цитатой | Цитировать

Добавить комментарий