The frontend requests image processing from a dedicated Node API which fetches the request and manipulates the image before returning it to the frontend to hot replace the existing placeholder image. The returned images are cached in the user's browser to prevent unnecessary requests and create a super fast experience.
The tool requires two separate components to function:
- Javascript library (client-side)
- Node API (server-side)
- Set up a simple Node server (on a Digital Ocean droplet or similar) and pull the
server
folder within this repo. - Run
npm run prod
to start the server withpm2
.
- Include the relevant bundle file from the
bundle
folder underclient
. There are bundle files available for ESM and CJS as well as plain JS. - For each of the image(s) you want to process, set the
src
attribute to a placeholder image (examplenoise.png
provided). - Add a
data-imgaide-src
data attribute to the image(s), with the real source URL. This must be a full, absolute URL. - Add desired processing command parameters to the URL within
data-imgaide-src
. These apply the Sharp image processing functions. - Add any caching data attributes you want to apply to the individual image.
- Load the page!
By default the images requested by ImageAide are cached in the user's browser using the Cache API for 30 days. You can control this behaviour by applying the following data-atributes:
data-imgaide-nocache
- Disables the cache functionality to ensure that the image is always requested fresh.
- Doesn't take any parameters.
data-imgaide-cacheexpiry
- Sets the expiry time for the cache item.
- Takes a single parameter (Integer passed as a String).
- The expiry parameter can refer to either days or milliseconds. Any string longer than 5 characters will be taken as milliseconds, otherwise days.
- Defaults to 30 (days)
Image processing (via Sharp)
Image processing is handled by applying parameters to the URL provided to the the data-imgaide-src
data attribute.
You can apply as many of these as required, and they should be chained as normal e.g. ?greyscale=true&flip&blur=10
Takes one parameter ( Boolean ).
Usage:
https://yoursite.com?greyscale=true
...this is equivalent to using Sharp directly like:
sharp(input).greyscale(true)
Takes one parameter ( RGB values | String )
The RGB strong must be formatted as in the example below.
Usage:
https://yoursite.com?tint='rgb(100, 50, 90)'
...this is equivalent to using Sharp directly like:
sharp(input).tint('rgb(100, 50, 90)')
Takes one parameter ( sigma value | Number/String )
This is a value between 0.3 and 1000 representing the sigma of the Gaussian mask and will be converted to a Float.
Usage:
https://yoursite.com?blur=10'
...this is equivalent to using Sharp directly like:
sharp(input).blur(10)
Takes one parameter ( threshold | Number/String )
This is a value representing the allowed difference from the top-left pixel when cropping 'boring' pixels based on the value of the top-left pixel. Defaults to 10.
Usage:
https://yoursite.com?trim=30'
...this is equivalent to using Sharp directly like:
sharp(input).trim(30)
Takes one parameter ( Boolean ).
This is simply a boolean yes/no value and is not required. Defaults to true.
Usage:
https://yoursite.com?flip
...this is equivalent to using Sharp directly like:
sharp(input).flip(true)
Takes one parameter ( Boolean ).
This is simply a boolean yes/no value and is not required. Defaults to true.
Usage:
https://yoursite.com?flop
...this is equivalent to using Sharp directly like:
sharp(input).flop(true)
This option comes in two parts: rotate
& rotateBackground
.
rotate takes one parameter ( Number/String ). This is the angle of rotation.
rotateBackground takes one parameter ( RGB values | String ). This is an RGB string value which will fill the segments left by the rotation if the rotation isn't a multiple of 90deg. Defaults to #0000000.
Usage:
https://yoursite.com?rotate=120&rotateBackground='rgb(100, 50, 90)'
...this is equivalent to using Sharp directly like:
sharp(input).rotate(120, {
'background': 'rgb(100, 50, 90)'
})
This consists of a group of related functions relating to resizing an image. They are best described separately as they are implemented via separate parameters in ImageAide
These are two separate parameters and can be used together or separately.
Both width and height take one parameter ( Number/String ). This is a size in pixels.
Usage:
https://yoursite.com?width=100&height=100
sharp(input).resize({
width: 100,
height: 100
})
This consists of two parameters which can be used together or separately as per width/height: scaleWidth
& scaleHeight
Both scaleWidth and scaleHeight take one parameter ( Number/String ). This is a percentage size expressed as a decimal fraction and must be within the bounds 0.1 -> 100.
The scaling is based on the inherent size values of the image itself.
If present these two parameters will override width and height respectively, but can be used interchangeably e.g. width alongside scaleHeight.
Usage:
https://yoursite.com?scaleWidth=0.5
sharp(input).resize(Math.round(width * 0.5))
When width/height or scaleWidth/scaleHeight are used, this parameter can be set to control the method by which the image should fit the newly defined size.
Fit takes one parameter ( String ). This is the fit method and must be one of the below
- cover (default) | Preserving aspect ratio, ensure the image covers both provided dimensions by cropping/clipping to fit.
- contain | Preserving aspect ratio, contain within both provided dimensions using "letterboxing" where necessary.
- fill | Ignore the aspect ratio of the input and stretch to both provided dimensions.
- inside | Preserving aspect ratio, resize the image to be as large as possible while ensuring its dimensions are less than or equal to both those specified.
- outside | Preserving aspect ratio, resize the image to be as small as possible while ensuring its dimensions are greater than or equal to both those specified.
Usage:
https://yoursite.com?fit=contain
sharp(input).resize({
fit: 'contain'
})
The position parameter is used to control the positioning of the image when a fit
method is applied.
Position takes one parameter ( String ). This must be one of the below options:
When using a fit
of cover or contain, the default position is centre.
[ top, right top, right, right bottom, bottom, left bottom, left, left top ]
Usage:
https://yoursite.com?position='right bottom'
sharp(input).resize({
position: 'right bottom'
})
In place of position the Gravity parameter is also available.
Gravity takes one parameter ( String ). This must be one of the below options:
When using a fit
of cover or contain, the default position is centre.
[ north, northeast, east, southeast, south, southwest, west, northwest, center or centre ]
Usage:
https://yoursite.com?gravity=southwest
sharp(input).resize({
gravity: 'southwest'
})
As well as the specific positioning options provided by position
and gravity
, it is also possible to provided a strategy
value which aims to calculate the position to best show off the content of the image.
Quoting Sharp's docs: "The experimental strategy-based approach resizes so one dimension is at its target length then repeatedly ranks edge regions, discarding the edge with the lowest score based on the selected strategy."
Strategy is only available when using a fit
of cover
.
Strategy takes one parameter ( String ). This must be one of the below options:
entropy
: focus on the region with the highest Shannon entropy.attention
: focus on the region with the highest luminance frequency, colour saturation and presence of skin tones.
Usage:
https://yoursite.com?strategy=entropy
sharp(input).resize({
strategy: 'entropy'
})
Untested work in progress
It is possible to specify the desired interpolation kernel for use with the strategy
method.
Kernel takes one parameter ( String ). This must be one of the below options.
nearest
: Use nearest neighbour interpolation .cubic
: Use a Catmull-Rom spline .mitchell
: Use a Mitchell-Netravali spline .lanczos2
: Use a Lanczos kernel with a=2.lanczos3
: Use a Lanczos kernel with a=3 (the default).
Usage:
https://yoursite.com?kernel=mitchell
sharp(input).resize({
kernel: 'mitchell'
})
When using a fit
of contain
, the image may be smaller than the original size, leaving empty space which can be filled with the background parameter.
Background takes one parameter ( RGB values | String ). This must be an RGB string value. Defaults to black.
Usage:
https://yoursite.com?background='rgb(100, 50, 90)'
sharp(input).resize({
background: 'rgb(100, 50, 90)'
})
Takes one parameter ( Boolean ). Defaults to false
.
Setting withoutEnlargement
to true
will prevent Sharp enlarging the image if the width and height are already less than the user specified dimensions.
Usage:
https://yoursite.com?withoutEnlargement=true
sharp(input).resize({
withoutEnlargement: true
})
Takes one parameter ( Boolean ). Defaults to true
.
Setting fastShrinkOnLoad
to true
will allow Sharp to take greater advantage of the JPEG and WebP shrink-on-load feature. but this can also lead to a slight moiré pattern on some images.
Usage:
https://yoursite.com?fastShrinkOnLoad=true
sharp(input).resize({
fastShrinkOnLoad: true
})
- Add more image processing options
- Improve documentation, particularly of the Sharp options.
- Style up and improve example page
- More server-side validation of requests
- Test Rollup scripts before package publishing
- Test suite