├── .gitignore ├── package.json ├── README.md └── responsive-images.php /.gitignore: -------------------------------------------------------------------------------- 1 | *.sublime-workspace 2 | *.sublime-project 3 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kirby-responsive-images", 3 | "description": "Bring responsive images to Kirby CMS", 4 | "author": "Jan Beck ", 5 | "version": "1.1.0", 6 | "type": "kirby-plugin", 7 | "license": "MIT" 8 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kirby Responsive Images 2 | 3 | A simple drop-in implementation of [responsive images](https://responsiveimages.org/) for Kirby CMS. Automatically generates thumbnails for low-resolution devices. 4 | 5 | **Note:** This repository is no longer maintained. As of [3.1.0](https://github.com/getkirby/kirby/releases/tag/3.1.0) Kirby supports native Srcset generation. 6 | 7 | ## Installation 8 | 9 | [Download the latest release](https://github.com/jancbeck/kirby-responsive-images/releases/) and unpack to your kirby `/site/plugins` directory. 10 | 11 | You can also rename the `responsive-images.php` file to `image.php` and move it to the `site/tags` directory if you prefer to keep all your custom tags there. 12 | 13 | ## Usage 14 | 15 | There nothing you need to change in order to have responsive image support. Just include images as you would do normally: 16 | 17 | `(image:workflow@3x.jpg link:workflow@3x.jpg width:1244)` 18 | 19 | Make sure your `/thumbs` directory is present and writable and ImageMagick is available. 20 | 21 | Although the plugin works without configuration, these options are available and can be added to your `/site/config/config.php`: 22 | 23 | It is also possible to link the image to one of the generated sizes using the keyword set via the `responsiveimages.sources` config. The following example will link to the smallest possible image size: 24 | 25 | `(image:workflow@3x.jpg link:small width:1244)` 26 | 27 | ### Sizes 28 | 29 | To control the sizes attribute you can use [media queries](https://ericportis.com/posts/2014/srcset-sizes/). 30 | 31 | ``` 32 | '10em', 36 | 'mq_value' => '60em', 37 | 'mq_name' => 'min-width' 38 | ), 39 | array( 40 | 'size_value' => '$img_width', 41 | 'mq_value' => '30em', 42 | 'mq_name' => 'min-width' 43 | ), 44 | 'alignleft' => array( 45 | 'size_value' => 'calc(50vw - 30px)' 46 | ), 47 | )); 48 | ``` 49 | 50 | 1. `$img_width` is a placeholder that will be replaced by the images actual pixel width. 51 | 2. The array key will be matched against the `classes` attribute of the image. In the example above `'size_value' => 'calc(50vw - 30px)'` will only be added if the image has the `alignleft` attribute. 52 | 53 | ### Sources 54 | 55 | By default, the plugin will generate thumbnails that are 480, 768 and 1200 pixels wide. You can overwrite these settings like these: 56 | 57 | ``` 58 | array('width' => 444), 61 | 'medium' => array('width' => 666), 62 | 'large' => array('width' => 999, 'grayscale' => true) // good for debugging 63 | )); 64 | ``` 65 | 66 | 1. The key names may be used to link the image or for default (see *Usage* and *Fallbacks* section). 67 | 2. Each array item takes the same arguments as Kirbys [thumb()](http://getkirby.com/docs/cheatsheet/helpers/thumb) function (`quality`, `blur`, `upscale` etc..). 68 | 69 | ### Fallbacks 70 | 71 | Browsers that support responive images will ignore the `src` attribute of the image and pick what ever is best for their device from the `srcsset` attribute. 72 | However, you might want to control what older browsers that don't support responsive images load. For that use case there is an option to set the default size based on the key name of the size (explained in the previous section). 73 | 74 | ``` 75 | set('tag', 'image', array( 5 | 'attr' => array( 6 | 'width', 7 | 'height', 8 | 'alt', 9 | 'text', 10 | 'title', 11 | 'class', 12 | 'imgclass', 13 | 'linkclass', 14 | 'caption', 15 | 'link', 16 | 'target', 17 | 'popup', 18 | 'rel', 19 | 'srcset', 20 | 'sizes' 21 | ), 22 | 'html' => function($tag) { 23 | 24 | $url = $tag->attr('image'); 25 | $alt = $tag->attr('alt'); 26 | $title = $tag->attr('title'); 27 | $link = $tag->attr('link'); 28 | $caption = $tag->attr('caption'); 29 | $srcset = $tag->attr('srcset'); 30 | $sizes = $tag->attr('sizes'); 31 | $file = $tag->file($url); 32 | 33 | // use the file url if available and otherwise the given url 34 | $url = $file ? $file->url() : url($url); 35 | 36 | // alt is just an alternative for text 37 | if($text = $tag->attr('text')) $alt = $text; 38 | 39 | // try to get the title from the image object and use it as alt text 40 | if($file) { 41 | 42 | if(empty($alt) and $file->alt() != '') { 43 | $alt = $file->alt(); 44 | } 45 | 46 | if(empty($title) and $file->title() != '') { 47 | $title = $file->title(); 48 | } 49 | 50 | } 51 | 52 | if(empty($alt)) $alt = pathinfo($url, PATHINFO_FILENAME); 53 | 54 | $sources = kirby_get_sources_array(); 55 | 56 | // link builder 57 | $_link = function($image) use($tag, $url, $link, $file, $sources) { 58 | 59 | if(empty($link)) return $image; 60 | 61 | // build the href for the link 62 | if($link == 'self') { 63 | $href = $url; 64 | } else if($file and $link == $file->filename()) { 65 | $href = $file->url(); 66 | } else if(isset($sources[$link])) { 67 | $href = thumb($file, $sources[$link])->url(); 68 | } else if($tag->file($link)) { 69 | $href = $tag->file($link)->url(); 70 | } else { 71 | $href = $link; 72 | } 73 | 74 | return html::a(url($href), $image, array( 75 | 'rel' => $tag->attr('rel'), 76 | 'class' => $tag->attr('linkclass'), 77 | 'title' => $tag->attr('title'), 78 | 'target' => $tag->target() 79 | )); 80 | 81 | }; 82 | 83 | // srcset builder 84 | if($file && empty($srcset)) { 85 | $srcset = kirby_get_srcset($file); 86 | } 87 | 88 | // sizes builder 89 | if($file && empty($sizes)) { 90 | $classes = ( ! empty( $tag->attr('imgclass'))) ? explode( ' ', $tag->attr('imgclass')) : ''; 91 | $sizes = kirby_get_sizes($file, $tag->attr('width'), $classes); 92 | } 93 | // allows src attribute to be overwritten 94 | $defaultsource = kirby()->option('responsiveimages.defaultsource'); 95 | if ( isset($sources[$defaultsource])) { 96 | $url = thumb($file, $sources[$defaultsource])->url(); 97 | } 98 | 99 | // image builder 100 | $_image = function($class) use($tag, $url, $alt, $title, $srcset, $sizes) { 101 | return html::img($url, array( 102 | 'width' => $tag->attr('width'), 103 | 'height' => $tag->attr('height'), 104 | 'class' => $class, 105 | 'title' => $title, 106 | 'alt' => $alt, 107 | 'srcset' => $srcset, 108 | 'sizes' => $sizes 109 | )); 110 | }; 111 | 112 | if(kirby()->option('kirbytext.image.figure') or !empty($caption)) { 113 | $image = $_link($_image($tag->attr('imgclass'))); 114 | $figure = new Brick('figure'); 115 | $figure->addClass($tag->attr('class')); 116 | $figure->append($image); 117 | if(!empty($caption)) { 118 | $figure->append('
' . html($caption) . '
'); 119 | } 120 | return $figure; 121 | } else { 122 | $class = trim($tag->attr('class') . ' ' . $tag->attr('imgclass')); 123 | return $_link($_image($class)); 124 | } 125 | 126 | } 127 | )); 128 | 129 | /** 130 | * Returns the srcset attribute value for a given Kirby file 131 | * Generates thumbnails on the fly 132 | * 133 | * @param File $file 134 | * @uses kirby_get_sources_array 135 | * @uses thumb 136 | * 137 | * @return string 138 | */ 139 | function kirby_get_srcset( $file ) { 140 | $srcset = $file->url() .' '. $file->width() .'w'; 141 | $sources_arr = kirby_get_sources_array( $file ); 142 | 143 | foreach ($sources_arr as $source) { 144 | $thumb = thumb($file, $source); 145 | $srcset .= ', '. $thumb->url() .' '. $thumb->width() .'w'; 146 | } 147 | return $srcset; 148 | } 149 | 150 | /** 151 | * Returns the image sources for a given Kirby file 152 | * 153 | * @return array 154 | */ 155 | function kirby_get_sources_array() { 156 | $sources_arr = kirby()->option('responsiveimages.sources'); 157 | 158 | // set some arbitrary defaults 159 | if (empty($sources_arr)) { 160 | $sources_arr = array( 161 | 'small' => array('width' => 480), 162 | 'medium' => array('width' => 768), 163 | 'large' => array('width' => 1200), 164 | ); 165 | } 166 | return $sources_arr; 167 | } 168 | 169 | /** 170 | * Returns the sizes attribute value for a given Kirby file 171 | * 172 | * @param File $file 173 | * @param int $width Optional. Use when you want to force image to a certain width (retina/high-PPi usecase) 174 | * @uses kirby_get_sizes_array() 175 | * 176 | * @return string 177 | */ 178 | function kirby_get_sizes( $file, $width = null, $imgclass = array() ) { 179 | 180 | $sizes = ''; 181 | $sizes_arr = kirby_get_sizes_array( $file, $width ); 182 | 183 | foreach ( $sizes_arr as $key => $size ) { 184 | 185 | // skip if the size should only be applied to a given class 186 | if (is_string($key) && ! empty($imgclass) && ! in_array($key, $imgclass)) { 187 | continue; 188 | } 189 | 190 | // Use 100vw as the size value unless something else is specified. 191 | $size_value = ( $size['size_value'] ) ? $size['size_value'] : '100vw'; 192 | // If a media length is specified, build the media query. 193 | if ( ! empty( $size['mq_value'] ) ) { 194 | $media_length = $size['mq_value']; 195 | // Use max-width as the media condition unless min-width is specified. 196 | $media_condition = ( ! empty( $size['mq_name'] ) ) ? $size['mq_name'] : 'max-width'; 197 | // If a media_length was set, create the media query. 198 | $media_query = '(' . $media_condition . ": " . $media_length . ') '; 199 | } else { 200 | // If not meda length was set, $media_query is blank. 201 | $media_query = ''; 202 | } 203 | // Add to the source size list string. 204 | $sizes .= $media_query . $size_value . ', '; 205 | } 206 | // Remove the trailing comma and space from the end of the string. 207 | $sizes = substr( $sizes, 0, -2 ); 208 | 209 | return $sizes; 210 | } 211 | 212 | /** 213 | * Returns the sizes for a given Kirby file 214 | * 215 | * Uses 'responsiveimages.sizes' option to let site owners overwrite the defaults 216 | * 217 | * @param File $file 218 | * @param int $width Optional. Use when you want to force image to a certain width (retina/high-PPi usecase) 219 | * 220 | * @return array 221 | */ 222 | function kirby_get_sizes_array( $file, $width = null ) { 223 | 224 | // let users overwrite the sizes via config 225 | $sizes_arr = kirby()->option('responsiveimages.sizes'); 226 | 227 | // let users overwrite the native image size via attribute 228 | $img_width = ( empty($width) ? $file->width() : $width ) . 'px'; 229 | 230 | // default to the image width 231 | if (empty($sizes_arr)) { 232 | $sizes_arr = array( 233 | array( 234 | 'size_value' => '100vw', 235 | 'mq_value' => $img_width, 236 | 'mq_name' => 'max-width' 237 | ), 238 | array( 239 | 'size_value' => $img_width 240 | ), 241 | ); 242 | } else { 243 | $sizes_arr = array_map(function($value) use ($img_width) { 244 | // allow config rules relative to native image size 245 | return str_replace( '$img_width', $img_width, $value ); 246 | }, $sizes_arr); 247 | } 248 | 249 | return $sizes_arr; 250 | } --------------------------------------------------------------------------------