2

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.

Sam Miller
  • 95
  • 1
  • 6
  • which part of the code are you asking about? i.e. do you have a problem? what is it – Bravo Jun 30 '22 at 05:39
  • The codes works. I am asking if there is a more succinct way to write out the following to avoid repeating the Math.round() for each individual const variable, "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));" For example, this addresses rounding an array, https://stackoverflow.com/questions/57587051/rounded-value-in-array-of-objects? – Sam Miller Jun 30 '22 at 06:56
  • so, in the question, please show the code you want us to look at ... keep the question as is, but put the actual code you are asking about separate as well ... that's what I would recommend – Bravo Jun 30 '22 at 07:01

1 Answers1

2

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));
Bravo
  • 6,022
  • 1
  • 10
  • 15
  • Looks like a beauty. I'll take it for a spin and get back to you. Thanks for your quick reply. – Sam Miller Jun 30 '22 at 08:55
  • @SamMiller - I know it's syntactically correct, but ... not 100% sure on the output : added a snippet to output formats array – Bravo Jun 30 '22 at 09:01
  • Works as expected. Exactly what I was looking for. Next, I'll study your snippet as a masterclass in coding. Thanks a million. – Sam Miller Jun 30 '22 at 23:29