In my gulpfile.js, I want to be able to calculate widths based on the approximate percentage of the screen that they will occupy for an image sizing/compression task using the gulp plugin "gulp-sharp-responsive".
For example, one of my sizes for a full-screen image is 1200. For images that are 1/4 that width, I want it to output a 300px image? I wanted to avoid having to manually calculate the new width and to be able to set the divisor as a command-line option, so this is the solution I came up with the below approach following this tutorial, https://www.sitepoint.com/pass-parameters-gulp-tasks/.
At the top of gulpfile.js, I added the following code:
// fetch command line arguments
const arg = (argList => {
let arg = {}, a, opt, thisOpt, curOpt;
for (a = 0; a < argList.length; a++) {
thisOpt = argList[a].trim();
opt = thisOpt.replace(/^\-+/, '');
if (opt === thisOpt) {
// argument value
if (curOpt) arg[curOpt] = opt;
curOpt = null;
}
else {
// argument name
curOpt = opt;
arg[curOpt] = true;
}
}
return arg;
})(process.argv);
I assigned div as arg.d and provided a fallback if arg is null to 1 (i.e., div = arg.d || 1). Note, since I am mainly going to show featured images at full screen at widths 576px and below (mobile screens), I am not dividing the xs size by a divisor. Also since the gulp-sharp-responsive is not able to process non-integer widths, I had to round the quotient with the round function. My question is, how would I make my code less redundant--for example, so I am not repeating math.round() for each const variable. If you have any suggestions to make my code more succinct please let me know, I am just a beginner. Thanks!
function sharpImg() {
const div = arg.d || 1, xs = (Math.round(576 / div)), sm = (Math.round(769 / div)), md = (Math.round(992 / div)), lg = (Math.round(1200 / div)), xl = (Math.round(1400 / div)), xxl = (Math.round(2048 / div));
return src(['_images/original/process/**/*.{jpeg,jpg,png,tiff,webp}', '!_images/original/raw/**'])
.pipe($.rename(function (path) {
path.dirname += "/" + path.basename;
}))
.pipe($.sharpResponsive({
formats: [
// jpeg
{ width: xs, format: "jpeg", rename: { suffix: "-xs" }, jpegOptions: { quality: 50, progressive: true } },
{ width: sm, format: "jpeg", rename: { suffix: "-sm" }, jpegOptions: { quality: 50, progressive: true } },
{ width: md, format: "jpeg", rename: { suffix: "-md" }, jpegOptions: { quality: 50, progressive: true } },
{ width: lg, format: "jpeg", rename: { suffix: "-lg" }, jpegOptions: { quality: 50, progressive: true } },
{ width: xl, format: "jpeg", rename: { suffix: "-xl" }, jpegOptions: { quality: 50, progressive: true } },
{ width: xxl, format: "jpeg", rename: { suffix: "-xxl" }, jpegOptions: { quality: 50, progressive: true } },
// webp
{ width: xs, format: "webp", rename: { suffix: "-xs" }, webpOptions: { quality: 50 } },
{ width: sm, format: "webp", rename: { suffix: "-sm" }, webpOptions: { quality: 50 } },
{ width: md, format: "webp", rename: { suffix: "-md" }, webpOptions: { quality: 50 } },
{ width: lg, format: "webp", rename: { suffix: "-lg" }, webpOptions: { quality: 50 } },
{ width: xl, format: "webp", rename: { suffix: "-xl" }, webpOptions: { quality: 50 } },
{ width: xxl, format: "webp", rename: { suffix: "-xxl" }, webpOptions: { quality: 50 } },
// avif
{ width: xs, format: "avif", rename: { suffix: "-xs" }, avifOptions: { quality: 50 } },
{ width: sm, format: "avif", rename: { suffix: "-sm" }, avifOptions: { quality: 50 } },
{ width: md, format: "avif", rename: { suffix: "-md" }, avifOptions: { quality: 50 } },
{ width: lg, format: "avif", rename: { suffix: "-lg" }, avifOptions: { quality: 50 } },
{ width: xl, format: "avif", rename: { suffix: "-xl" }, avifOptions: { quality: 50 } },
{ width: xxl, format: "avif", rename: { suffix: "-xxl" }, avifOptions: { quality: 50 } },
]
}))
.pipe(dest('_images/processed'))
}
export task:
exports.sharpImg = sharpImg;
The result: Running "gulp sharpImg" results in the default const widths defined, whereas running "gulp sharpImg --d 4" results in images 1/4 their default width.
You could create a breakpoints object, a function to do the repetitive math.floor of the division, then with some modern JS majicks, I think your code can be a lot shorter, less repetitive, yet just as readable, and, importantly, easy to change the breakpoints for example
function sharpImg() {
const BREAKPOINTS = {
xs: 576,
sm: 769,
md: 992,
lg: 1200,
xl: 1400,
xxl: 2048,
};
const onDiv = div => Object.entries(BREAKPOINTS).map(([bp, value]) => [Math.round(value / div), `-${bp}`]);
// creates an array of [[1, "-xs"], [2, "-sm"], ... ] (obviously the values are 576/div etc)
const div = arg.d || 1, bps = onDiv(div);
const jpegOptions = { quality: 50, progressive: true };
const webpOptions = { quality: 50 };
const avifOptions = { quality: 50 };
return src(['_images/original/process/**/*.{jpeg,jpg,png,tiff,webp}', '!_images/original/raw/**'])
.pipe($.rename(function (path) {
path.dirname += "/" + path.basename;
}))
.pipe($.sharpResponsive({
formats: [
// jpeg
...bps.map(([width, suffix]) => ({ width, format: "jpeg", rename: { suffix }, jpegOptions })),
// webp
...bps.map(([width, suffix]) => ({ width, format: "webp", rename: { suffix }, webpOptions })),
// avif
...bps.map(([width, suffix]) => ({ width, format: "avif", rename: { suffix }, avifOptions })),
]
}))
.pipe(dest('_images/processed'))
}
A snippet that outputs format
to check if it's right
function sharpImg() {
const BREAKPOINTS = {
xs: 576,
sm: 769,
md: 992,
lg: 1200,
xl: 1400,
xxl: 2048,
};
const onDiv = div => Object.entries(BREAKPOINTS).map(([bp, value]) => [Math.round(value / div), `-${bp}`]);
// creates an array of [[1, "-xs"], [2, "-sm"], ... ] (obviously the values are 576/div etc)
const div = 1, bps = onDiv(div);
const jpegOptions = { quality: 50, progressive: true };
const webpOptions = { quality: 50 };
const avifOptions = { quality: 50 };
const formats = [
// jpeg
...bps.map(([width, suffix]) => ({ width, format: "jpeg", rename: { suffix }, jpegOptions })),
// webp
...bps.map(([width, suffix]) => ({ width, format: "webp", rename: { suffix }, webpOptions })),
// avif
...bps.map(([width, suffix]) => ({ width, format: "avif", rename: { suffix }, avifOptions })),
];
return formats;
}
console.log(JSON.stringify(sharpImg(), null, 4));