Create CSS/SVG image sprites.
0
image-sprites.md
270 lines 7.8 kB View raw View rendered
1# Image Sprites 2 3An image sprite is a collection of images put into a single image or file. 4 5Helps reduce the number of HTTP requests, memory and bandwidth usage. 6 7They are often used to significantly improve performance in emoji selectors. 8 9## Raster Sprites 10 11<details> 12<summary><h4>How to create a WebP emojis sprite.</h4></summary> 13 14Download [ImageMagick][imd]. 15 16Create an image sprite using the [Montage][imm] tool: 17 18```bash 19montage png/*.png -background none -geometry +0+0 PNG32:sprites.png 20``` 21 22Convert the image to [WebP][imw] image format: 23 24```bash 25magick sprites.png -quality 100 -define webp:lossless=true -define webp:method=6 sprites.webp 26``` 27 28Suppose we have an image sprite with `72x72` emojis from [Unicode 16.0][u16]. 29 30Use the following JavaScript code to calculate the background size and position: 31 32```js 33const size = 72; // size of each emoji 34const width = 4536; // image sprite width 35const height = 4464; // image sprite height 36 37const horizontal = width / size; // max number of emojis per row (63) 38const vertical = height / size; // max number of emojis per column (62) 39 40// Calculate the background size. 41// 100% is the size of the element that has the background image. 42// We need to multiply by the number of emojis per row and column. 43console.log('Background width:', 100 * horizontal); // 6300% 44console.log('Background height:', 100 * vertical); // 6200% 45// CSS -> background-size: 6300% 6200% 46 47// Calculate the emoji position at column 48, row 56. 48// Column 1 and row 1 are always 0%, the max is 100%. 49const position = calcPosition(48, 56); 50console.log('Background position X:', position.x); // 75.806% 51console.log('Background position Y:', position.y); // 90.164% 52// CSS -> background-position: 75.806% 90.164%; 53 54function calcPosition(col, row, d=3) { 55 const x = 100 / (horizontal - 1) * --col; 56 const y = 100 / (vertical - 1) * --row; 57 return { x: x.toFixed(d), y: y.toFixed(d) }; 58} 59``` 60 61HTML example of how to use the WebP image sprite: 62 63```html 64<!DOCTYPE html> 65 <head> 66 <style> 67 html { color-scheme: dark; } 68 .emoji { 69 /* 70 The <img> element can be any size. 71 Values greater than the emoji size will make the image look blurry. 72 Set 'image-rendering' to 'pixelated' to make the image less blurry. 73 */ 74 width: 72px; 75 aspect-ratio: 1; 76 image-rendering: pixelated; 77 background-size: 6300% 6200%; 78 background-image: url('sprites.webp'); 79 } 80 .emoji28x1 { background-position: 43.548% 0.000%; } 81 .emoji15x45 { background-position: 22.581% 72.131%; } 82 .emoji48x56 { background-position: 75.806% 90.164%; } 83 </style> 84 </head> 85 <body> 86 <!-- Argentina Flag --> 87 <p class="emoji emoji28x1"></p> 88 <!-- Parrot --> 89 <p class="emoji emoji15x45"></p> 90 <!-- Fingerprint (Unicode 16.0) --> 91 <p class="emoji emoji48x56"></p> 92 </body> 93</html> 94``` 95 96</details> 97 98## Vector Sprites (SVG) 99 100<details> 101<summary><h4>How to use the <​use> element to reference SVG images.</h4></summary> 102 103Use the [`<use>`][use] element to reference only a part of an SVG image defined elsewhere. 104 105```html 106<!DOCTYPE html> 107 <body> 108 <svg style="display: none;"> 109 <!-- Define the SVG images using the <symbol> element. --> 110 <!-- Then you can reference them with the <use> element. --> 111 <symbol id="circle" viewBox="0 0 24 24" stroke="currentColor" fill="none"> 112 <circle cx="12" cy="12" r="10"/> 113 </symbol> 114 <symbol id="square" viewBox="0 0 24 24" stroke="currentColor" fill="none"> 115 <rect width="18" height="18" x="3" y="3" rx="2"/> 116 </symbol> 117 </svg> 118 <!-- Circle SVG --> 119 <p> 120 <svg style="width: 72px; aspect-ratio: 1;"> 121 <use href="#circle"/> 122 </svg> 123 </p> 124 <!-- Square SVG --> 125 <p> 126 <svg style="width: 72px; aspect-ratio: 1;"> 127 <use href="#square"/> 128 </svg> 129 </p> 130 </body> 131</html> 132``` 133 134The [svg-sprite][ssp] library can be used to create SVG sprites of several types. 135 136The following example combines `icons/*/*.svg` into a single file for each subdirectory. 137 138```js 139import fs from 'node:fs'; 140import path from 'node:path'; 141import SVGSpriter from 'svg-sprite'; 142 143if (!fs.existsSync('sprites')) 144 fs.mkdirSync('sprites'); 145 146const iter = fs.globSync('icons/*/*.svg'); 147 148const groups = Object.groupBy( 149 Array.from(iter), 150 icon => icon.split(path.sep).at(-2) 151); 152 153for (const [sprite, icons] of Object.entries(groups)) { 154 const spriter = new SVGSpriter({ 155 mode: { 156 symbol: { 157 inline: true 158 } 159 } 160 }); 161 162 for (const icon of icons) { 163 const name = `icon:${sprite}.${path.basename(icon)}`; 164 spriter.add(name, null, fs.readFileSync(icon, 'utf8')); 165 } 166 167 const output = `sprites/${sprite}.svg`; 168 const { result } = await spriter.compileAsync(); 169 170 for (const mode of Object.values(result)) 171 fs.writeFileSync(output, mode.sprite.contents, 'utf8'); 172} 173 174/* 175 INPUT 176 -------------------------------------------------- 177 ./icons/ 178 misc/ 179 circle.svg 180 square.svg 181 emojis/ 182 butterfly.svg 183 184 OUTPUT 185 -------------------------------------------------- 186 ./sprites/ 187 misc.svg 188 emojis.svg 189 190 USAGE 191 -------------------------------------------------- 192 <svg><use href="#icon:misc.circle"/></svg> 193 <svg><use href="#icon:misc.square"/></svg> 194 <svg><use href="#icon:emojis.butterfly"/></svg> 195*/ 196``` 197 198</details> 199 200<details> 201<summary><h4>How to use the <​img> element to display SVG images.</h4></summary> 202 203Use the [`<img>`][img] element with a [fragment identifier][fra] to display only a part of an SVG image. 204 205Using [SVG Fragment Identifiers][sfi] with [`<img>`][img] has a couple of advantages over other methods: 206 207- Only a single HTML element is required. 208- The [`<img>`][img] element behaves similarly to inline text: 209 - When copied, the `alt` attribute text is included in the clipboard. 210 - It can be selected, which makes it ideal for emoji-like icons in text. 211- Natively supports fallback behavior if the image or fragment fails to load. 212 213However, it's worth noting that `<svg>` with [`<use>`][use] remains valuable for: 214 215- Reusable SVG symbols in contexts where you need finer control (e.g., altering fill, stroke). 216- Interactive or animated graphics where direct access to the DOM structure of the SVG is needed. 217 218```html 219<!-- emojis-sprite.svg --> 220 221<svg xmlns="http://www.w3.org/2000/svg"> 222 <defs> 223 <style> 224 g:not(:target) { 225 display: none; 226 } 227 </style> 228 </defs> 229 <!-- Circle SVG --> 230 <svg viewBox="0 0 24 24" stroke="red" fill="none"> 231 <g id="circle"> 232 <circle cx="12" cy="12" r="10"/> 233 </g> 234 </svg> 235 <!-- Square SVG --> 236 <svg viewBox="0 0 24 24" stroke="red" fill="none"> 237 <g id="square"> 238 <rect width="18" height="18" x="3" y="3" rx="2"/> 239 </g> 240 </svg> 241</svg> 242``` 243 244```html 245<html> 246 <body> 247 <img src="/emojis-sprite.svg#circle" alt="circle"/> 248 <img src="/emojis-sprite.svg#square" alt="square"/> 249 </body> 250</html> 251``` 252 253</details> 254 255<!-- REFERENCE LINKS --> 256[imd]: https://imagemagick.org/script/download.php 257[imm]: https://imagemagick.org/script/montage.php 258[imw]: https://imagemagick.org/script/webp.php 259 260[bgi]: https://developer.mozilla.org/docs/Web/CSS/background-image 261[bgs]: https://developer.mozilla.org/docs/Web/CSS/background-size 262[bgp]: https://developer.mozilla.org/docs/Web/CSS/background-position 263[use]: https://developer.mozilla.org/docs/Web/SVG/Element/use 264[img]: https://developer.mozilla.org/docs/Web/SVG/Element/img 265[fra]: https://developer.mozilla.org/en-US/docs/Web/URI/Fragment 266 267[u16]: https://www.unicode.org/versions/Unicode16.0.0 268[sfi]: https://www.w3.org/TR/SVG/linking.html#SVGFragmentIdentifiers 269 270[ssp]: https://github.com/svg-sprite/svg-sprite
Sign up or login to add to the discussion