Skip to content

Commit

Permalink
Add responsive images
Browse files Browse the repository at this point in the history
  • Loading branch information
tbela99 committed Mar 1, 2018
1 parent 63b976b commit a38c244
Show file tree
Hide file tree
Showing 5 changed files with 411 additions and 97 deletions.
29 changes: 18 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,21 @@ This performs many things:
* Push resources (require http 2 protocol). you can configure which resources will be pushed
* Efficiently cache resources using http caching headers. This requires apache mod_rewite. I have not tested on other web servers
* Range requests are supported for cached resources
* Insert scripts and css that have 'data-position="head"' attribute in head instead of the body
* force script and css to be ignored by the optimizer by setting 'data-ignore="true"' attribute
* connect to domains faster: automatically detect domains and add < link rel=preconnect >

1. Insert scripts and css that have 'data-position="head"' attribute in head instead of the body
# Images

* deliver images in webp format when the browser signals it supports it
* generate responsive images automatically

## Responsive images

* automatically add srcset and sizes for images. Only necessary images are generated. Images smaller that the breakpoint are ignored.
* resize and crop images using a one of these methods (face detection, entropy, center or default).
* configure breakpoints used to create smaller images
* scrset images url is automatically rewritten when http cache is enabled

# Javascript Improvements

Expand Down Expand Up @@ -85,19 +98,12 @@ Add routes to customize fetch event networking startegy by using either a static
1. resize images for mobile / tablet and leverage < img srcset > (even in css?)
1. serve webp whenever the browser supports it
1. resize images for mobile / tablet in css
1. IMAGES: Implement progressive images loading with intersectionObserver [here](https://jmperezperez.com/medium-image-progressive-loading-placeholder/) and async decoding see [here](https://medium.com/dailyjs/image-loading-with-image-decode-b03652e7d2d2)
1. prerender + [Page Visibility API](http://www.w3.org/TR/page-visibility/): how should prender links be chosen?
1. IMAGES: read this [here](https://kinsta.com/blog/optimize-images-for-web/)
1. Service worker cache expiration api (using localforage or a lightweight indexDb library)
1. IMAGES: Implement progressive images loading with intersectionObserver [here](https://jmperezperez.com/medium-image-progressive-loading-placeholder/)
1. IMAGES: Implement images delivery optimization see [here](https://www.smashingmagazine.com/2017/04/content-delivery-network-optimize-images/) and [here](https://developers.google.com/web/updates/2015/09/automating-resource-selection-with-client-hints)
1. IMAGES: Implement support for <pictures> element see [here]
1. Background Sync see [here](https://developers.google.com/web/updates/2015/12/background-sync)
1. Messaging API (broadcasting messages to and from all/single clients)
1. Remove < Link rel=preload > http header and use < link > HTML tag instead. see [here](https://jakearchibald.com/2017/h2-push-tougher-than-i-thought/)
1. IMAGES: read this [here](https://kinsta.com/blog/optimize-images-for-web/)
1. IMAGES: Implement progressive images loading with intersectionObserver [here](https://jmperezperez.com/medium-image-progressive-loading-placeholder/)
1. IMAGES: Implement images delivery optimization see [here](https://www.smashingmagazine.com/2017/04/content-delivery-network-optimize-images/) and [here](https://developers.google.com/web/updates/2015/09/automating-resource-selection-with-client-hints)
1. IMAGES: Implement support for <pictures> element see [here](https://www.smashingmagazine.com/2013/10/automate-your-responsive-images-with-mobify-js/)
1. CORS for PWA:https://filipbech.github.io/2017/02/service-worker-and-caching-from-other-origins | https://developers.google.com/web/updates/2016/09/foreign-fetch | https://stackoverflow.com/questions/35626269/how-to-use-service-worker-to-cache-cross-domain-resources-if-the-response-is-404
1. CSS: deduplicate, merge properties, rewrite rules, etc
1. Disk quota management see [here](https://developer.chrome.com/apps/offline_storage) and [here](https://developer.mozilla.org/fr/docs/Web/API/API_IndexedDB/Browser_storage_limits_and_eviction_criteria) and [here](https://gist.github.com/ebidel/188a513b1cd5e77d4d1453a4b6d060b0)
Expand All @@ -106,18 +112,19 @@ Add routes to customize fetch event networking startegy by using either a static

## Low priority list

1. Fetch remote resources periodically (css and javascript). right now they are updated only once.
1. Fetch remote resources periodically (configurable) (css and javascript). right now they are updated only once.
1. Manage the service worker settings from the front end (unregister, delete cache, etc ...)?
1. Manage user push notification subscription from the Joomla backend (link user to his Id, etc ...)?
1. Provide push notification endpoints (get user Id, notification clicked, notification closed, etc ...)
1. Mobile apps deep link?
1. PWA: Deep links in pwa app or website. see [here](http://blog.teamtreehouse.com/registering-protocol-handlers-web-applications) and [here](https://developer.mozilla.org/en-US/docs/Web-based_protocol_handlers)
1. Integrate https://www.xarg.org/project/php-facedetect/ and https://onthe.io/learn/en/category/analytic/How-to-detect-face-in-image-with-PHP for better image optimization ?

# Change History

## V2.2

1. remove '+' '=' and ',' from the hash generation alphabet
1. Add responsive image support
1. Disabling service worker will actually uninstall it
1. Server Timing Header see [here](https://w3c.github.io/server-timing/#examples)
1. automatic preconnect < link > added, web fonts preload moved closer to < head > for faster font load
Expand Down
8 changes: 8 additions & 0 deletions gzip.xml
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,15 @@
<option value="1">JYes</option>
<option value="0">JNo</option>
</field>
<field name="imagesresizestrategy" label="PLG_GZIP_FIELD_IMAGE_CROP_STRATEGY_LABEL"
description="PLG_GZIP_FIELD_IMAGE_CROP_STRATEGY_DESCRIPTION" type="list" default="CROP_CENTER">
<option value="CROP_DEFAULT">JOPTION_IMAGE_RESIZE_CROP_DEFAULT</option>
<option value="CROP_CENTER">JOPTION_IMAGE_RESIZE_CROP_CENTER</option>
<option value="CROP_ENTROPY">JOPTION_IMAGE_RESIZE_CROP_ENTROPY</option>
<option value="CROP_FACE">JOPTION_IMAGE_RESIZE_CROP_FACE</option>
</field>
<field name="sizes" label="PLG_GZIP_FIELD_IMAGE_SIZES_LABEL" multiple="multiple" description="PLG_GZIP_FIELD_IMAGE_SIZES_DESCRIPTION" type="list" default="">
<option value="1024">1024px</option>
<option value="768">768px</option>
<option value="320">320px</option>
</field>
Expand Down
107 changes: 96 additions & 11 deletions helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,7 @@ public static function parseURLs($body, array $options = []) {
$replace[$hash] = $matches[0];

return $hash;

}, $body);

$body = preg_replace_callback('#(<script(\s[^>]*)?>)(.*?)</script>#s', function ($matches) use(&$replace) {
Expand Down Expand Up @@ -402,10 +403,23 @@ public static function parseURLs($body, array $options = []) {

$attr = strtolower($matches[2]);

if (isset($options['parse_url_attr'][$attr])) {
if ($attr == 'srcset') {

$return = [];

foreach (explode(',', $matches[6]) as $chunk) {

$parts = explode(' ', $chunk);

$name = trim($parts[0]);

$return[] = (static::isFile($name) ? static::url($name) : $name).' '.$parts[1];
}

return ' '.$attr.'="'.implode(',', $return);
}

// case 'src':
// case 'href':
if (isset($options['parse_url_attr'][$attr])) {

$file = static::getName($matches[6]);

Expand All @@ -423,8 +437,6 @@ public static function parseURLs($body, array $options = []) {

$name = preg_replace('~[#?].*$~', '', $file);

// if (is_file($name)) {

$ext = strtolower(pathinfo($name, PATHINFO_EXTENSION));

$push_data = empty($types) ? false : static::canPush($name, $ext);
Expand Down Expand Up @@ -1218,7 +1230,7 @@ public static function getName($name) {
return preg_replace(static::$regReduce, '', $name);
}

public static function shorten($id, $alphabet = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-@+=,') {
public static function shorten($id, $alphabet = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-@') {

$base = strlen($alphabet);
$short = '';
Expand Down Expand Up @@ -1918,8 +1930,82 @@ public static function parseImages($body, array $options = []) {
$file = $newFile;
}
}
}


// responsive images?
if (!empty($options['imageresize']) && !empty($options['sizes']) && empty($attributes['srcset'])) {

// build mq based on actual image size
$maxwidth = $sizes[0];
$mq = array_filter ($options['sizes'], function ($size) use($maxwidth) {

return $size < $maxwidth;
});

// we need to resize the images
if (!empty($mq)) {

$image = null;
$resource = null;
$method = empty($options['imagesresizestrategy']) ? 'CROP_CENTER' : $options['imagesresizestrategy'];
$const = constant('\Image\Image::'.$method);
$hash = sha1($file);
$short_name = strtolower(str_replace('CROP_', '', $method));
$crop = $path.$hash.'-'. $short_name.'-'.basename($file);

$images = array_map(function ($size) use($file, $hash, $short_name, $path) {

return $path.$hash.'-'.$short_name.'-'.$size.'-'.basename($file);

}, $mq);

$srcset = [];

foreach ($images as $k => $img) {

if (!\is_file($img)) {

if (\is_null($image)) {

if (!\is_file($crop)) {

$image = new \Image\Image($file);

// resize image to use less memory
if ($maxwidth > 1200) {

$image->setSize(1200);
}

$image->resizeAndCrop($mq[$k], null, $method)->save($crop);
}

else {

$image = new \Image\Image($crop);
}
}

$image->setSize($mq[$k])->save($img);
}

$srcset[] = $img.' '.$mq[$k].'w';
}

$srcset[] = $file.' '.$sizes[0].'w';
$maxwidth = end($mq) + 1;

$mq = array_map(function ($size) {

return '(max-width: '.$size.'px)';
});

$mq[] = '(min-width: '.$maxwidth.'px)';

$attributes['srcset'] = implode(',', $srcset);
$attributes['sizes'] = implode(',', $mq);
}
}
}

if (!isset($attributes['alt'])) {

Expand All @@ -1930,13 +2016,12 @@ public static function parseImages($body, array $options = []) {

return $key .= '="'.$value.'"';

}, $attributes, array_keys($attributes))).
'>';
},
$attributes, array_keys($attributes))).'>';
}

return $matches[0];
}, $body);
// }

return $body;
}
Expand Down
8 changes: 8 additions & 0 deletions language/en-GB/en-GB.plg_system_gzip.ini
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,14 @@ PLG_GZIP_FIELD_IMAGE_RESIZE_DESCRIPTION=""
PLG_GZIP_FIELD_IMAGE_SIZES_LABEL="Responsive Image Sizes"
PLG_GZIP_FIELD_IMAGE_SIZES_DESCRIPTION=""

PLG_GZIP_FIELD_IMAGE_CROP_STRATEGY_LABEL="Crop method"
PLG_GZIP_FIELD_IMAGE_CROP_STRATEGY_DESCRIPTION=""

JOPTION_IMAGE_RESIZE_CROP_DEFAULT="Default"
JOPTION_IMAGE_RESIZE_CROP_CENTER="Center"
JOPTION_IMAGE_RESIZE_CROP_ENTROPY="Entropy"
JOPTION_IMAGE_RESIZE_CROP_FACE="Face detection"

PLG_GZIP_FIELD_PWA_ENABLED_LABEL="Enable PWA"
PLG_GZIP_FIELD_PWA_ENABLED_DESCRIPTION=""

Expand Down
Loading

0 comments on commit a38c244

Please sign in to comment.