Generate Responsive Images With JavaScript and PHP

Coding Responsive Images With PHP

How does one make images responsive? There are a number of techniques available: each with its own strengths and weaknesses. This method is unique because it ensures that the image served does not need to be scaled in the viewer’s browser. The secret, we will see, lies in PHP’s image editing capabilities.

Capturing the Client’s Screen Resolution

PHP is a server-side scripting language and is unable to detect the client’s device size. Fortunately, JavaScript can.[1] Here is what you would need to grab and store the client’s screen resolution:

var width = screen.width;
var height = screen.height;

The next step is to add the client’s screen resolution to the URL and refresh the page.[2] We can use $_SERVER['REQUEST_URI'] (PHP) to capture the domain name. The following code uses PHP’s echo command to combine the PHP code and the JavaScript code.

echo '
if (width > 0 && height > 0) { // Check if width and height have been properly set
var str = "'.$_SERVER['REQUEST_URI'].'"; // Get domain name using PHP and store in JavaScript variable
var n = str.indexOf("?"); // Determine position of question mark in the domain name
/*-- Place Screen Resolution Into Domain Name... --*/
if(n==-1){ window.location.href = "'.$_SERVER['REQUEST_URI'].'?width=" + width + "&height=" + height; } // ...after a "?" if none were found
else{ window.location.href = "'.$_SERVER['REQUEST_URI'].'&width=" + width + "&height=" + height; } // ...after a "&" if a "?" was found
}
';

Let us put that all together and place it inside an if statement so that it is only called when the screen resolution is not found in the domain name. Placing this code at the beginning of the document gives the fastest results.

<?php
if(!isset($_GET['width'])){ // Skip these steps is the screen resolution is already in the domain name
echo '
<script type="text/javascript">
var width = screen.width;
var height = screen.height;
if (width > 0 && height > 0) {
var str = "'.$_SERVER['REQUEST_URI'].'";
var n = str.indexOf("?");
if(n==-1){ window.location.href = "'.$_SERVER['REQUEST_URI'].'?width=" + width + "&height=" + height; }
else{ window.location.href = "'.$_SERVER['REQUEST_URI'].'&width=" + width + "&height=" + height; }
}
</script>
';
}
?>

Using The Client’s Screen Resolution To Size Images

We are now able to use the client’s screen resolution in a PHP function to resize images. We will start by capturing the screen resolution and storing it in PHP variables.

$screen_width=$_GET['width'];
$screen_height=$_GET['height'];

In this first example, we are going to scale and crop an image to fill the client’s screen. The following code will specify which image we want to use. It will also define its dimensions. (Do not bother using the switch statement if you know the image file extension)

$src ='path/to/original/image.extension';
$type = strtolower(substr(strrchr($src,"."),1)); // Define image type by file name extension
if($type == 'jpeg'){ $type = 'jpg'; }
switch($type){ // Create a duplicate of the original image
case 'bmp': $img = imagecreatefromwbmp($src); break;
case 'gif': $img = imagecreatefromgif($src); break;
case 'jpg': $img = imagecreatefromjpeg($src); break;
case 'png': $img = imagecreatefrompng($src); break;
}
$image_width = imagesx($img); // Image width
$image_height = imagesy($img); // Image height

Things get a bit tricky here. We need to define what portion of the image will be kept when we make our crop. This requires a comparison of the image’s aspect ratio with that of the client’s screen…and some division.

$original_aspect = $image_width / $image_height;
$screen_aspect = $screen_width / $screen_height;
if ( $original_aspect >= $screen_aspect ){
// If image is wider than screen (in aspect ratio sense)
$new_height = $screen_height;
$new_width = $image_width / ($image_height / $screen_height);
}else{
// If the screen is wider than the image
$new_width = $screen_width;
$new_height = $image_height / ($image_width / $screen_width);
}

The next step is to create a black image that is the same size as the client’s screen. Then we take the first image, crop it, and resize it to fit in the blank image we just created.

$img2 = imagecreatetruecolor( $screen_width, $screen_height );
// Resize and crop
imagecopyresampled(
$img2, // Second Image
$img, // Original Image
0, // x-coordinate in Second Image to begin pasting copied image
0, // y-coordinate in Second Image to begin pasting copied image
0, // x-coordinate in Original Image to begin copying
0, // y-coordinate in Original Image to begin copying
$new_width, $new_height,
$image_width, $image_height
);
/*-- Note: coordinates begin in top left corner --*/

Our new image has been created but has not yet been saved to the server. This would be the time to apply any image filters, should you wish to.

// Apply Image Filters (Optional)
imagefilter($img2,IMG_FILTER_GRAYSCALE);
imagefilter($img2,IMG_FILTER_CONTRAST,10);
// Save New Image Onto The Server
imagejpeg($img2,'path/to/location/for/new/image.jpg');
// Free up any memory associated with images we created
imagedestroy($img);
imagedestroy($img2);

Here is the code to crop and resize an image to the dimensions of the client’s screen resolution. I have added an if statement to determine in an identical image has already been created and prevents needless processing time.

<?php
// Check if client's screen resolution has been added to domain name
if($_GET['width'] > 0){
// Check if client's screen resolution has been added to domain name
if($_GET['width'] > 0){
// Make sure that an identical image has not already been created
if(!file_exists('path/to/new/image_'.$_GET['width'].'x'.$_GET['height'].'.jpg')){
$src ='path/to/original/image.extension';
$type = strtolower(substr(strrchr($src,"."),1));
if($type == 'jpeg'){ $type = 'jpg'; }
switch($type){
case 'bmp': $img = imagecreatefromwbmp($src); break;
case 'gif': $img = imagecreatefromgif($src); break;
case 'jpg': $img = imagecreatefromjpeg($src); break;
case 'png': $img = imagecreatefrompng($src); break;
}
$screen_width=$_GET['width'];
$screen_height=$_GET['height'];
$image_width = imagesx($img);
$image_height = imagesy($img);
$original_aspect = $image_width / $image_height;
$screen_aspect = $screen_width / $screen_height;
if ( $original_aspect >= $screen_aspect ){
$new_height = $screen_height;
$new_width = $image_width / ($image_height / $screen_height);
}else{
$new_width = $screen_width;
$new_height = $image_height / ($image_width / $screen_width);
}
$img2 = imagecreatetruecolor( $screen_width, $screen_height );
imagecopyresampled($img2,$img,0,0,0,0,$new_width,$new_height,$image_width,$image_height);
imagejpeg($img2,'path/to/location/for/new/image_'.$screen_width.'x'.$screen_height.'.jpg');
imagedestroy($img);
imagedestroy($img2);
}
// Create a div that is the size of the browser viewport with the image sized for the device
echo '<div style="background:url(path/to/new/image_'.$_GET['width'].'x'.$_GET['height'].'.jpg); background-size:cover;" width="100%" height="100vh"></div>';
}
?>

Using Base64 Encoding

The example that I just showed you saves an image to the server. This works fine for a background image since one only has to store derivatives of a single image. We are going to want to try something different for site-wide implementation. One option is to convert the images into chunks of code and insert them directly into the HTML file.[3]

ob_start();  // Start buffering output image
imagejpeg($img2); // No destination folder has been specified
$jpeg = ob_get_clean(); // Gets the output image and stop buffering
$uri = "data:image/jpeg;base64," . base64_encode($jpeg); // Store encoded image in a new variable

Here is an example of that in practice.

function ImageResize($image,$alt,$title){
if(0 < $_GET['width']){
$img = imagecreatefromjpeg('includes/img/'.$image);
$image_width = imagesx($img);
$image_height = imagesy($img);
// Define output image width
$column_width = '702'; // max-width of column
if($_GET['width'] < $column_width){
$new_width = $_GET['width'];
}else{
$new_width = $column_width;
}
// Define output image height
$new_height = ($image_height / $image_width) * $new_width;
// Create output image
$img2 = imagecreatetruecolor($new_width,$new_height);
imagecopyresampled($img2,$img,0,0,0,0,$new_width,$new_height,$image_width,$image_height);
// Buffer and encode image
ob_start();
imagejpeg($img2,NULL);
$jpeg = ob_get_clean();
$uri = "data:image/jpeg;base64," . base64_encode($jpeg);
// Free up memory
imagedestroy($img);
imagedestroy($img2);
// Display Image
echo '<img src="'.$uri.'" title="'.$title.'" alt="'.$alt.'" />';
}
}
echo '<div id="column" style="width:100%; max-width:702px;";>';
ImageResize(
'path/to/original/image.jpg',
'value for the alt attribute',
'value for the title attribute'
);
echo '<div>';

Footnotes

  1. The client needs to be running JavaScript for the images to be created. Perhaps CSS media queries could be used as a fallback. It might make sense to use PHP’s $_SERVER['HTTP_USER_AGENT'] to approximate the device’s dimensions.
  2. Attaching the dimensions to the URL is an easy way to pass information from JavaScript to PHP – but it is unsightly. Try passing the information through PHP’s POST method or setting some cookies for the scripts to retrieve…and let me know how it turns out.
  3. Creating a new image takes time. Saving the image to the server allows us to skip this step on subsequent loads. Using base64 encoding does not offer this advantage but it does save the end-user money by serving smaller files (unless they have unlimited data plans). A third option is to save the file to the server but only keep it there for a set amount of time.

Posted

by

in

Last Updated