picturefill
One of the key challenges with responsive web design, and a subject of much discussion in recent years, is how to deal with images. Setting a max-width on image elements enables designers to allow their size to adapt to the page dimensions, but in itself that approach can lead to far bigger images being downloaded than are required.
响应式网页设计的主要挑战之一是如何处理图像,这是近年来很多讨论的主题。 在图像元素上设置最大宽度可以使设计人员允许其大小适应页面尺寸,但这种方法本身可能导致下载的图像比所需的大得多。
One solution to this is “source sets” – referencing separate image files at varying sizes (and by extension, file sizes) to be requested and displayed at different resolutions. Indeed, the srcset attribute is being implemented by Webkit. You can use a similar approach straight away and in a cross-browser compatible manner by using Javascript; one such method is the Picturefill plugin.
一种解决方案是“源集”-引用要请求并以不同分辨率显示的大小不同(扩展名是文件大小)的单独图像文件。 实际上,srcset属性由Webkit实现 。 您可以使用Javascript,以跨浏览器兼容的方式立即使用类似的方法; 一种这样的方法是Picturefill插件 。
In essence, Picturefill allows you to specify different src
attributes for an image, each image file corresponding to a different media query. Thus a large image will be fetched if – and only if – the screen size requires it, and likewise a mobile-optimised version of an image will be fetched and displayed as appropriate.
本质上,Picturefill允许您为图像指定不同的src
属性,每个图像文件对应于不同的媒体查询。 因此,当且仅当屏幕尺寸需要时,才可以获取大图像,同样,将获取并显示针对移动设备优化的图像版本,并进行适当显示。
This approach requires some more effort, however – the images themselves need to be created at the appropriate sizes. That's the focus of this article.
但是,这种方法需要付出更多的努力–图像本身需要以适当的尺寸创建。 这就是本文的重点。
我们将创造什么 (What we'll Create)
I'm going to demonstrate a simple application for generating responsive images, or image derivatives – i.e. different versions / sizes – of images, on demand.
我将演示一个简单的应用程序,用于根据需要生成响应图像或图像派生图像,即图像的不同版本/大小。
In essence, we're going to “hijack” requests for a specific version of an image when the requested file doesn't exist. When it doesn't we'll create it, and save it to the file system for subsequent requests.
本质上,当请求的文件不存在时,我们将“劫持”对特定版本图像的请求。 如果没有,我们将创建它,并将其保存到文件系统中以供后续请求。
入门 (Getting Started)
I'm basing this example on SlimBootstrap, which is a skeleton application which uses the Slim Framework. Alternatively, you can clone or download the complete code for this tutorial from Github.
我基于SlimBootstrap这个例子, SlimBootstrap是一个使用Slim Framework的骨架应用程序。 或者,您可以从Github克隆或下载本教程的完整代码。
Once you've cloned or downloaded the skeleton application, there are a few more steps before beginning coding.
克隆或下载框架应用程序后,在开始编码之前,还有几个步骤。
In this example I'm using ImageMagick, so you'll need to ensure that it's installed along with the Imagick PHP extension – refer to the installation instructions for details according to your operating system. If you prefer, you could always rewrite the example using GD or a library such as Imagine.
在此示例中,我使用的是ImageMagick ,因此您需要确保将其与Imagick PHP扩展名一起安装 -请参阅安装说明,以根据您的操作系统获得详细信息。 如果愿意,可以始终使用GD或Imagine之类的库重写示例。
Then you'll need to download the Picturefill library. I've placed the two relevant files – matchmedia.js and picturefill.js – in public/js/lib.
然后,您需要下载Picturefill库 。 我已经将两个相关文件– matchmedia.js和picturefill.js –放置在public / js / lib中。
Create the configuration file config/config.php – there's a skeleton example in the same folder – and add the following:
创建配置文件config / config.php –在同一文件夹中有一个框架示例–并添加以下内容:
'images.dir' => $basedir . 'public/img/', 'logs.dir' => $basedir . 'logs/'
'images.dir' => $basedir . 'public/img/', 'logs.dir' => $basedir . 'logs/'
Finally, ensure that the images directory is both writeable by the web server, and it can create sub-directories – chmod 775
should do the trick. It is recommended you do the same for the logs directory – so that if you do run into any errors, they get conveniently printed there.
最后,确保图像目录都可由Web服务器写入,并且可以创建子目录chmod 775
应该可以解决问题。 建议您对logs目录执行相同的操作-这样,如果您遇到任何错误,都可以在此方便地打印出来。
使用Picturefill (Using Picturefill)
Picturefill takes a “source set” referring to different versions of an image, and selects which one to download and display using media queries. There is a simple demo on Github.
Picturefill具有一个“源集”,该“源集”指的是图像的不同版本,并使用媒体查询选择要下载和显示的版本。 Github上有一个简单的演示 。
To use it, you need to create a <span>
element with the following structure:
要使用它,您需要创建具有以下结构的<span>
元素:
<span data-picture data-alt="Image description goes here">
<span data-src="img/small.jpg"></span>
<span data-src="img/medium.jpg" data-media="(min-width: 400px)"></span>
<span data-src="img/large.jpg" data-media="(min-width: 800px)"></span>
<span data-src="img/extralarge.jpg" data-media="(min-width: 1000px)"></span>
<!-- Fallback content for non-JS browsers. Same img src as the initial, unqualified source element. -->
<noscript>
<img src="img/small.jpg" alt="Image description goes here">
</noscript>
</span>
The data-src
attribute contains the URL to each image derivative, and the data-media attribute accepts any valid media query, for example min- or max- width, or min-resolution.
data-src
属性包含每个图像派生类的URL,而data-media属性接受任何有效的媒体查询,例如min-或max-width或min-resolution。
图像路径和路由 (Image Paths and Routing)
Here's an example of the sort of path we're going to provide:
这是我们将提供的这种路径的示例:
/img/large/uploads/blog/photo.jpg
/img/large/uploads/blog/photo.jpg
Breaking that down;
分解;
img
is the images directory
img
是图像目录
large
is the derivative; in the following example this could also be small
, medium
or extralarge
large
是导数; 在以下示例中,它也可以是small
, medium
或extralarge
uploads/path
is a sub-directory – two-deep, in this example – for organising the images
uploads/path
是一个子目录,在此示例中为两个目录,用于组织图像
test.jpg
is the filename
test.jpg
是文件名
If the file referenced by this path exists then the browser will just fetch it, bypassing any routes we have set up. But it's that route that we'll need to define, in order to intercept requests for derivatives that don't yet exist.
如果此路径引用的文件存在,则浏览器将绕过我们设置的所有路由来获取该文件。 但这就是我们需要定义的路由,以便拦截对尚不存在的派生请求。
We can match all such requests using the following pattern:
我们可以使用以下模式匹配所有此类请求:
$app->get(
'/img/:parts+',
function ($parts) use ($app, $c) {
The path above results in the $parts variable being populated as follows:
上面的路径导致$ parts变量填充如下:
array(4) {
[0]=>
string(5) "large"
[1]=>
string(7) "uploads"
[2]=>
string(4) "blog"
[3]=>
string(9) "photo.jpg"
}
Extracting the relevant parts is easy, by whittling away at this array:
通过简化此数组,可以轻松提取相关部分:
// Grab the filename
$filename = array_pop($parts);
// Grab the derivative
$derivative = array_shift($parts);
// Get the path
$path = implode("/", $parts);
Because we've taken the derivative and filename out of the array, it doesn't matter how many levels deep the file is, as we just implode the remaining array to create the path.
因为我们已经从数组中取出了派生类和文件名,所以文件的深度没有多大关系,因为我们将内建其余数组以创建路径。
The next step is to check that the destination directory exists – and if not, create it.
下一步是检查目标目录是否存在–如果不存在,请创建它。
// assemble the destination path
$destination_path = $c['config']['images.dir'] . $derivative . '/' . $path;
// Create the directory, if required
if (!file_exists($destination_path)) {
mkdir($destination_path, 0777, true);
}
The third argument to mkdir is important; it's going to be nexcessary to recursively create directories.Configuring the Derivatives
mkdir的第三个参数很重要; 递归创建目录将是必需的。
Let's create a flexible configuration which for each derivative, defines the name (which will form part of the URL), the appropriate size, and the corresponding media query.
让我们创建一个灵活的配置,为每个派生定义名称(将构成URL的一部分),适当的大小以及相应的媒体查询。
Here's an example in JSON:
这是JSON中的示例:
{
"jpeg_compression": 80,
"sizes": {
"small" : {
"width" : 180
},
"medium" : {
"width" : 375,
"query" : "(min-width: 400px)"
},
"large" : {
"width" : 480,
"query" : "(min-width: 800px)"
},
"extralarge" : {
"width" : 768,
"query" : "(min-width: 1000px)"
}
}
}
In this example I've only defined widths, but the resize function I'm going to use allows you to define either the width or the height – or both. It also provides cropping functions, but for simplicity I'll just use scaleImage – see the documentation for details.
在此示例中,我仅定义了宽度,但是我将要使用的调整大小功能允许您定义宽度或高度–或两者都定义。 它还提供了裁剪功能,但为简单起见,我仅使用scaleImage –有关详细信息, 请参见文档 。
下一步 (Next Steps)
The next step is to locate the file to process, load the configuration and then resize the image.
下一步是找到要处理的文件,加载配置,然后调整图像大小。
// Now get the source path
$source_path = $c['config']['images.dir'] . $path;
// grab the config
$config = json_decode(file_get_contents('../config/images.json'), true);
// get the specs from the config
$specs = $config['sizes'][$derivative];
// Create a new Imagick object
$image = new Imagick();
// Ping the image
$image->pingImage($source_path . '/' . $filename);
// Read the image
$image->readImage($source_path . '/' . $filename);
// Resize, by width & height OR width OR height, depending what's configured
$image->scaleImage(
(isset($specs['width'])) ? $specs['width'] : 0,
(isset($specs['height'])) ? $specs['height'] : 0
);
Note that the scaleImage
function can accept a width, height, or both.
请注意, scaleImage
函数可以接受宽度,高度或两者。
Now that we've resized the image appropriately, we need to do two things.
现在我们已经适当调整了图像的大小,我们需要做两件事。
First, we need to save the image to the appropriate location, which means that any subsequent request will just grab the image itself – bypassing our resizing function.
首先,我们需要将图像保存到适当的位置,这意味着任何后续请求都只会获取图像本身,从而绕过我们的调整大小功能。
Second, we still need to honour the original request which means outputting the image, setting the appropriate headers along the way.
其次,我们仍然需要满足原始请求,这意味着输出图像,并在此过程中设置适当的标题。
// save the file, for future requests
$image->writeImage($destination_path . '/' . $filename);
// set the headers; first, getting the Mime type
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime_type = finfo_file($finfo, $destination_path . '/' . $filename);
$app->response->headers->set('Content-Type', $mime_type);
// Get the file extension, so we know how to output the file
$path_info = pathinfo($filename);
$ext = $path_info['extension'];
// output the image
echo $image;
// Free up the image resources
$image->destroy();
That's all there is to it! I've cut a few corners for clarity, as there are a few things you'd need to think about in production:
这里的所有都是它的! 为了清楚起见,我做了一些努力,因为您在生产中需要考虑一些事项:
- sanity checking the configuration 完整性检查配置
- ensuring that the files are indeed images 确保文件确实是图像
- better error handling, generating an error image in certain circumstances 更好的错误处理,在某些情况下会生成错误图像
- track any changes in the image derivative configuration, and regenerate the images as appropriate 跟踪图像衍生配置中的任何更改,并适当地重新生成图像
输出影像 (Outputting Images)
I've deliberately included the media queries in the server-side configuration; because Picturefill requires quite a lot of HTML to operate, generating it is probably a good candidate for a view helper. That's outside the scope of this article, but let me know in the comments or submit a pull request at Github if you come up with something suitable.
我故意将媒体查询包括在服务器端配置中。 因为Picturefill需要大量HTML才能运行,所以生成它可能是视图帮助程序的不错选择。 这超出了本文的范围,但是如果您提出合适的建议,请在评论中让我知道,或者在Github上提交拉取请求。
摘要 (Summary)
In this article I've described a possible solution to the issue of adaptive images by combining server-side, on-demand creation of image derivatives designed to be used with the Picturefill Javascript library. In time, a third-party Javascript library may well become redundant as standards such as srcset
evolve, but the need for server-side solutions is likely to remain.
在本文中,我描述了通过结合服务器端按需创建的图像派生类(可能与Picturefill Javascript库一起使用)来解决自适应图像问题的可能解决方案。 随着时间的推移,随着诸如srcset
标准的发展,第三方Javascript库很可能变得多余,但是服务器端解决方案的需求可能仍然存在。
Of course there's no need to just use this code for using Picturefill or similar – it could also be used in, say, a content management system to generate different versions of an image for different displays. Can you think of anything else?
当然,无需使用此代码来使用Picturefill或类似的东西–例如,它也可以在内容管理系统中使用,以为不同的显示器生成不同版本的图像。 你还能想到什么吗?
翻译自: https://www.sitepoint.com/responsive-images-using-picturefill-php/
picturefill