Advertisement
  1. Code
  2. PHP

Упрощение изменения размера изображения через PHP

Scroll to top
Read Time: 11 min

() translation by (you can also view the original English article)

Всегда хотелось универсального, простого способа изменения размера изображений в PHP? Именно для этого и предназначены классы PHP - функции многократного использования, назначенные выполнять грязную работу за кулисами. Мы собираемся узнать, как создать собственный класс, хорошо построенный и пластичный. Изменение размера должно быть простым. Насколько простым? В три шага!


Введение

Чтобы дать вам быстрый обзор того, чего мы пытаемся достичь с помощью нашего класса, он должен быть:

  • Прост в использовании
  • Независимый от формата. То есть, открывает, изменяет размер и сохраняет несколько различных форматов изображений.
  • Интеллектуальный размер - отсутствие искажения изображения!

Примечание. Это не учебное пособие о том, как создавать классы и объекты, и, если это вы сами умеете, нет необходимости следовать этому руководству.

Нам есть, чем заняться - Давайте начнём.


Шаг 1 Подготовка

Начнём с простого. В рабочей папке создайте два файла: один называется index.php, другой resize-class.php


Шаг 2 Вызов объекта

Чтобы дать вам представление о том, чего мы пытаемся достичь, начнём с кодирования вызовов, которые мы будем использовать для изменения размеров изображений. Откройте файл index.php и добавьте следующий код.

Как вы можете видеть, в наших действиях есть логика. Мы открываем файл изображения, устанавливаем размеры, до которых мы хотим изменить изображение и тип изменения размера.
Затем мы сохраняем изображение, выбираем нужный формат и качество изображения. Сохраните из закройте файл index.php.

1
2
  	// *** Include the class

3
		include("resize-class.php");
4
5
		// *** 1) Initialize / load image

6
		$resizeObj = new resize('sample.jpg');
7
8
		// *** 2) Resize image (options: exact, portrait, landscape, auto, crop)

9
		$resizeObj -> resizeImage(150, 100, 'crop');
10
11
		// *** 3) Save image

12
		$resizeObj -> saveImage('sample-resized.gif', 100);

Из вышеприведённого кода видно, что мы открываем файл jpg, но сохраняем gif. Помните, это насчёт пластичности.


Шаг 3 Скелет класса

Объектно-ориентированное программирование (ООП) делает эту возможность легкодоступной. Думайте о классе как о шаблоне; вы можете инкапсулировать данные - ещё один термин, который на самом деле означает скрытие данных. Мы можем снова и снова использовать этот класс без необходимости переписывать какой-либо код изменения размера - вам нужно только вызвать соответствующие методы, как мы делали на втором шаге. Когда шаблон создан, мы создаём экземпляры этого шаблона, называемые объектами.

«Функция конструирования, известная как конструктор, - это специальный метод класса, который вызывается классом при создании нового объекта».

Давайте начнём создавать наш класс resize. Откройте файл resize-class.php. Ниже приведена базовая структура скелета классов, которую я назвал 'resize'. Обратите внимание на строку с именем переменной класса; здесь мы начнём добавлять наши важные переменные класса позже.

Функция конструирования, известная как конструктор, является специальным методом класса (термин «метод» аналогичен функции, однако при разговоре о классах и объектах часто используется термин «метод»), который вызывается классом при создании нового объекта. Это делает возможным для нас некоторую инициализацию, что мы и сделаем в следующем шаге.

1
2
		Class resize
3
		{
4
			// *** Class variables

5
6
			public function __construct()
7
			{
8
9
			}
10
		}

Обратите внимание: это двойное подчеркивание для метода construct.


Шаг 4 Конструктор

Мы собираемся изменить метод конструктора. Во-первых, мы передадим имя файла (и путь) нашего изображения, которое нужно изменить. Назовём эту переменную $ fileName.

Нам нужно открыть файл, переданный с помощью PHP (более конкретно, PHP GD Library), чтобы PHP мог читать изображение. Мы делаем это с помощью настраиваемого метода 'openImage'. Я расскажу, как этот метод
работает, но пока нам нужно сохранить результат как переменную класса. Переменная класса - это просто переменная, но она специфична для этого класса. Помните комментарий переменной класса, о котором я упоминал ранее? Добавьте 'image' как приватную переменную, набрав 'private $ image;'. Установив переменную как 'Private', вы устанавливаете область видимости этой переменной, поэтому доступ к ней может получить только класс. С этого момента мы можем сделать вызов нашему открытому изображению, известному как ресурс, который мы будем делать позже, когда изменим размер.

Пока мы на нём, давайте сохраним высоту и ширину изображения. У меня такое чувство, что они пригодятся позже.

Теперь у вас должно получиться следующее.

1
2
		Class resize
3
		{
4
			// *** Class variables

5
			private $image;
6
			private $width;
7
			private $height;
8
9
			function __construct($fileName)
10
			{
11
			    // *** Open up the file

12
			    $this->image = $this->openImage($fileName);
13
14
			    // *** Get width and height

15
			    $this->width  = imagesx($this->image);
16
			    $this->height = imagesy($this->image);
17
			}
18
		}

Методы imagesx и imagesy встроены в функции, которые являются частью библиотеки GD. Они, соответственно, получают ширину и высоту вашего изображения.


Шаг 5 Открытие изображения

На предыдущем этапе мы вызвали пользовательский метод openImage. На этом этапе мы создадим этот метод. Мы хотим, чтобы сценарий думал за нас, поэтому в зависимости от типа передаваемого файла сценарий должен определить, какую функцию GD Library вызывает для открытия изображения. Это легко достигается сравнением расширения файлов с аргументом switch.

Мы переходим в наш файл, чтобы изменить размер и вернуть этот файловый ресурс.

1
2
		private function openImage($file)
3
		{
4
		    // *** Get extension

5
		    $extension = strtolower(strrchr($file, '.'));
6
7
		    switch($extension)
8
		    {
9
		        case '.jpg':
10
		        case '.jpeg':
11
		            $img = @imagecreatefromjpeg($file);
12
		            break;
13
		        case '.gif':
14
		            $img = @imagecreatefromgif($file);
15
		            break;
16
		        case '.png':
17
		            $img = @imagecreatefrompng($file);
18
		            break;
19
		        default:
20
		            $img = false;
21
		            break;
22
		    }
23
		    return $img;
24
		}

Шаг 6 Как изменить размер

Здесь случилась любовь. Этот шаг является всего лишь объяснением того, что мы собираемся делать - поэтому здесь нет домашнего задания. На следующем шаге мы создадим public метод, который вызовем, чтобы выполнить изменение размера; поэтому имеет смысл задать ширину и высоту, а также информацию о том, как мы хотим изменить размер изображения. Давайте поговорим об этом минутку. Должны быть сценарии, в которых вы хотели бы изменить размер изображения до нужного. Отлично, давайте включим это. Но будут моменты, когда вам нужно изменить размеры сотен изображений, и каждое изображение имеет другое соотношение сторон - подумайте о портретных изображениях. Изменение их размеров до точного приведёт к серьёзным искажениям. Если мы посмотрим на варианты, чтобы предотвратить искажения, мы можем:

  1. Изменить размер как можно ближе к новым размерам изображения, сохраняя пропорции.
  2. Изменить размер как можно ближе к новым размерам изображения и обрезать остаток.

Оба варианта жизнеспособны, в зависимости от ваших потребностей.

Ага. Мы попытаемся обработать все вышеперечисленное. В итоге мы собираемся привести опции к:

  1. Изменить размер точно по ширине/высоте. (точно)
  2. Изменить размер по ширине - будет установлена точная ширина, высота будет меняться в соответствии с форматным соотношением. (пейзаж)
  3. Изменить размер по высоте - например, Resize by Width, но высота будет установлена и ширина будет изменена динамически. (портрет)
  4. Автоматическое определение опций 2 и 3. Если вы просматриваете папку с фотографиями разного размера, позвольте сценарию определить, как с этим справиться. (авто)
  5. Изменить размер, а затем обрезать. Мой любимый. Точный размер, без искажений. (обрезка)

Шаг 7 Изменение размера. Давай сделаем это!

Метод resize состоит из двух частей. Первая заключается в том, чтобы получить оптимальную ширину и высоту для нашего нового изображения, создав некоторые специальные методы и, конечно, передав параметр «изменить размер», как описано выше. Ширина и высота возвращаются как массив и устанавливаются в соответствующие переменные. Не стесняйтесь 'pass as reference', но я не большой поклонник этого.

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

Мы также сохраняем вывод метода imagecreatetruecolor (новое истинное цветное изображение) в качестве переменной класса. Добавить 'private $imageResized;' с другими переменными класса.

Изменение размера выполняется модулем PHP, известным как Библиотека GD. Многие из методов, которые мы используем, предоставляются этой библиотекой.

1
2
		// *** Add to class variables

3
		private $imageResized;
1
2
		public function resizeImage($newWidth, $newHeight, $option="auto")
3
		{
4
5
			// *** Get optimal width and height - based on $option

6
			$optionArray = $this->getDimensions($newWidth, $newHeight, strtolower($option));
7
8
			$optimalWidth  = $optionArray['optimalWidth'];
9
			$optimalHeight = $optionArray['optimalHeight'];
10
11
			// *** Resample - create image canvas of x, y size

12
			$this->imageResized = imagecreatetruecolor($optimalWidth, $optimalHeight);
13
			imagecopyresampled($this->imageResized, $this->image, 0, 0, 0, 0, $optimalWidth, $optimalHeight, $this->width, $this->height);
14
15
			// *** if option is 'crop', then crop too

16
			if ($option == 'crop') {
17
				$this->crop($optimalWidth, $optimalHeight, $newWidth, $newHeight);
18
			}
19
		}

Шаг 8 Дерево решений

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

1
2
		private function getDimensions($newWidth, $newHeight, $option)
3
		{
4
5
		   switch ($option)
6
		    {
7
		        case 'exact':
8
		            $optimalWidth = $newWidth;
9
		            $optimalHeight= $newHeight;
10
		            break;
11
		        case 'portrait':
12
		            $optimalWidth = $this->getSizeByFixedHeight($newHeight);
13
		            $optimalHeight= $newHeight;
14
		            break;
15
		        case 'landscape':
16
		            $optimalWidth = $newWidth;
17
		            $optimalHeight= $this->getSizeByFixedWidth($newWidth);
18
		            break;
19
		        case 'auto':
20
		            $optionArray = $this->getSizeByAuto($newWidth, $newHeight);
21
					$optimalWidth = $optionArray['optimalWidth'];
22
					$optimalHeight = $optionArray['optimalHeight'];
23
		            break;
24
				case 'crop':
25
		            $optionArray = $this->getOptimalCrop($newWidth, $newHeight);
26
					$optimalWidth = $optionArray['optimalWidth'];
27
					$optimalHeight = $optionArray['optimalHeight'];
28
		            break;
29
		    }
30
			return array('optimalWidth' => $optimalWidth, 'optimalHeight' => $optimalHeight);
31
		}

Шаг 9 Оптимальные размеры

Мы уже обсуждали, что делают эти четыре метода. Они просто математики, которые рассчитывают нам лучшую форму.

1
2
		private function getSizeByFixedHeight($newHeight)
3
		{
4
		    $ratio = $this->width / $this->height;
5
		    $newWidth = $newHeight * $ratio;
6
		    return $newWidth;
7
		}
8
9
		private function getSizeByFixedWidth($newWidth)
10
		{
11
		    $ratio = $this->height / $this->width;
12
		    $newHeight = $newWidth * $ratio;
13
		    return $newHeight;
14
		}
15
16
		private function getSizeByAuto($newWidth, $newHeight)
17
		{
18
		    if ($this->height < $this->width)
19
		    // *** Image to be resized is wider (landscape)

20
		    {
21
		        $optimalWidth = $newWidth;
22
		        $optimalHeight= $this->getSizeByFixedWidth($newWidth);
23
		    }
24
		    elseif ($this->height > $this->width)
25
		    // *** Image to be resized is taller (portrait)

26
		    {
27
		        $optimalWidth = $this->getSizeByFixedHeight($newHeight);
28
		        $optimalHeight= $newHeight;
29
		    }
30
			else
31
		    // *** Image to be resizerd is a square

32
		    {
33
				if ($newHeight < $newWidth) {
34
					$optimalWidth = $newWidth;
35
					$optimalHeight= $this->getSizeByFixedWidth($newWidth);
36
				} else if ($newHeight > $newWidth) {
37
					$optimalWidth = $this->getSizeByFixedHeight($newHeight);
38
				    $optimalHeight= $newHeight;
39
				} else {
40
					// *** Sqaure being resized to a square

41
					$optimalWidth = $newWidth;
42
					$optimalHeight= $newHeight;
43
				}
44
		    }
45
46
			return array('optimalWidth' => $optimalWidth, 'optimalHeight' => $optimalHeight);
47
		}
48
49
		private function getOptimalCrop($newWidth, $newHeight)
50
		{
51
52
			$heightRatio = $this->height / $newHeight;
53
			$widthRatio  = $this->width /  $newWidth;
54
55
			if ($heightRatio < $widthRatio) {
56
				$optimalRatio = $heightRatio;
57
			} else {
58
				$optimalRatio = $widthRatio;
59
			}
60
61
			$optimalHeight = $this->height / $optimalRatio;
62
			$optimalWidth  = $this->width  / $optimalRatio;
63
64
			return array('optimalWidth' => $optimalWidth, 'optimalHeight' => $optimalHeight);
65
		}

Шаг 10 Обрезка

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

1
2
		private function crop($optimalWidth, $optimalHeight, $newWidth, $newHeight)
3
		{
4
			// *** Find center - this will be used for the crop

5
			$cropStartX = ( $optimalWidth / 2) - ( $newWidth /2 );
6
			$cropStartY = ( $optimalHeight/ 2) - ( $newHeight/2 );
7
8
			$crop = $this->imageResized;
9
			//imagedestroy($this->imageResized);

10
11
			// *** Now crop from center to exact requested size

12
			$this->imageResized = imagecreatetruecolor($newWidth , $newHeight);
13
			imagecopyresampled($this->imageResized, $crop , 0, 0, $cropStartX, $cropStartY, $newWidth, $newHeight , $newWidth, $newHeight);
14
		}

Шаг 11 Сохранение изображения

Мы добрались; почти готово. Пришло время сохранить изображение. Мы проходим путь и качество изображения, которое нам нужно, в пределах от 0 до 100, 100 - самое лучшее, и вызываем соответствующий метод. Пара слов о качестве изображения: JPG использует шкалу от 0 до 100, из которых 100 являются лучшим. Изображения GIF не имеют настройки качества изображения. PNG делают, но они используют шкалу 0-9, 0 является лучшей. Это нехорошо, поскольку мы можем не помнить об этом каждый раз, когда захотим сохранить изображение. Применим немного магии, чтобы стандартизировать всё.

1
2
		public function saveImage($savePath, $imageQuality="100")
3
		{
4
			// *** Get extension

5
        	$extension = strrchr($savePath, '.');
6
        	$extension = strtolower($extension);
7
8
			switch($extension)
9
			{
10
				case '.jpg':
11
				case '.jpeg':
12
					if (imagetypes() & IMG_JPG) {
13
						imagejpeg($this->imageResized, $savePath, $imageQuality);
14
					}
15
		            break;
16
17
				case '.gif':
18
					if (imagetypes() & IMG_GIF) {
19
						imagegif($this->imageResized, $savePath);
20
					}
21
					break;
22
23
				case '.png':
24
					// *** Scale quality from 0-100 to 0-9

25
					$scaleQuality = round(($imageQuality/100) * 9);
26
27
					// *** Invert quality setting as 0 is best, not 9

28
					$invertScaleQuality = 9 - $scaleQuality;
29
30
					if (imagetypes() & IMG_PNG) {
31
						imagepng($this->imageResized, $savePath, $invertScaleQuality);
32
					}
33
					break;
34
35
				// ... etc

36
37
				default:
38
					// *** No extension - No save.

39
					break;
40
			}
41
42
			imagedestroy($this->imageResized);
43
		}

Теперь самое подходящее время уничтожить ресурс изображения, чтобы освободить часть памяти. Если вы будете пользоваться этим в производстве, может быть хорошей идеей захватить и вернуть результат сохранённого изображения.


Заключение

Ну вот и всё, ребята. Благодарю за то, что вы следовали уроку. Надеюсь, вы сочтёте это полезным. Буду признателен за ваши отзывы в комментариях.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.