Search code examples
gulpgraphicsmagickresponsive-images

Efficient responsive images with gulp


I am trying to efficiently generate responsive images for use on a website, but have some trouble with handling existing files and paths in gulp. The images reside in hierarchically structured folders, where pages is the root with several subfolders recursing downwards - most of which have images.

My current task looks like this:

gulp.task('responsive', function() {
    gulp.src('pages/**/*.{jpg,png}')
    .pipe($.plumber({
        errorHandler: function (err) {
            console.log(err);
            this.emit('end');
        }
    }))
    .pipe(responsive({
        '**/*.*': [{
            width: 2240,
            suffix: '-2240',
            quality: 70
        }, {
            width: 1920,
            suffix: '-1920',
            quality: 70
        }, {
            width: 1600,
            suffix: '-1600',
            quality: 70
        }, {
            width: 1280,
            suffix: '-1280',
            quality: 70
        }, {
            width: 1280,
            height: 300,
            crop: true,
            gravity: 'Center',
            suffix: '-thumbwide',
            quality: 70
        }, {
            width: 960,
            suffix: '-960',
            quality: 70
        }, {
            width: 640,
            suffix: '-640',
            quality: 70
        }, {
            width: 480,
            suffix: '-480',
            quality: 70
        }, {
            width: 320,
            suffix: '-320',
            quality: 70
        }]
    }))
    .pipe(gulp.dest(function(file) {
        return file.base;
    }));
});

There are two main issues with this:

  1. Images are piped into the same folder as their source, when they would preferably be in an images-folder next to the source.
  2. If the task is ran again, it generates new files based on both the sources and the previously generated responsive images.

The task uses gulp-responsive-images, which leverages GraphicsMagick for the resizing. I tried using gulp-changed (set as .pipe($.cached('pages'))) to resolve issue 2, but it seemed to have no effect.

How can I resolve issue 1 and 2, based on my current setup?


Solution

  • A solution to these two issues were the following:

    1. Ignore the need for an images-folder. Rather, keep a separate Source-folder with neatly organized original images alongside text-files.
    2. Use a temporary folder to ensure that the entire output-folder is rebuilt properly, with responsive images alongside text-files.

    I wrote a Gist for it:

    'use strict';
    var gulp = require('gulp');
    var gutil = require('gulp-util');
    var del = require('del');
    var debug = require('gulp-debug');
    var $ = require('gulp-load-plugins')();
    
    gulp.task('clean:output', function() {
        gutil.log('Deleting /output/**/*', gutil.colors.magenta('123'));
        return del([
            'output/**/*'
        ]);
    });
    gulp.task('build:source', ['clean:output'], function() {
        gutil.log('Build from /Source', gutil.colors.magenta('123'));
        return gulp.src(['Source/**/*'])
        .pipe(debug({title: 'Source:'}))
        .pipe(gulp.dest('output'));
    });
    
    gulp.task('build:responsive', ['build:source'], function() {
        return gulp.src(['output/**/*.{gif,jpg,jpeg,png}'])
        .pipe($.cached('responsive'))
        .pipe($.responsive({
            '**/*': [{
                width: 2240,
                height: 320,
                crop: 'center',
                rename: { suffix: '-thumbwide' }
            }, {
                width: 2240,
                rename: { suffix: '-2240' }
            }, {
                width: 1920,
                rename: { suffix: '-1920' }
            }, {
                width: 1600,
                rename: { suffix: '-1600' }
            }, {
                width: 1280,
                rename: { suffix: '-1280' }
            }, {
                width: 960,
                rename: { suffix: '-960' }
            }, {
                width: 640,
                rename: { suffix: '-640' }
            }, {
                width: 480,
                rename: { suffix: '-480' }
            }, {
                width: 320,
                rename: { suffix: '-320' }
            }, {
                width: 160,
                rename: { suffix: '-160' }
            }],
        }, {
            quality: 70,
            progressive: true,
            withMetadata: false,
            skipOnEnlargement: true,
            errorOnUnusedConfig: false,
            errorOnUnusedImage: false,
            errorOnEnlargement: false
        }))
        .pipe(gulp.dest('responsive'))
    });
    gulp.task('build:images', ['build:responsive'], function() {
        gutil.log('Copying responsive images from /responsive to /output', gutil.colors.magenta('123'));
        return gulp.src('responsive/**/*.{gif,jpg,jpeg,png}')
        .pipe(debug({title: 'Images:'}))
        .pipe($.imagemin({
            progressive: true,
            interlaced: true
        }))
        .pipe(gulp.dest('output'))
    });
    gulp.task('clean:responsive', ['build:images'], function() {
        gutil.log('Deleting /responsive/**/*', gutil.colors.magenta('123'));
        return del([
            'responsive/**/*'
        ]);
    });
    
    gulp.task('default', ['clean:output', 'build:source', 'build:responsive', 'build:images', 'clean:responsive']);
    

    Using the following in packages.json:

      "devDependencies": {
        "del": "^2.2.0",
        "gulp": "^3.9.1",
        "gulp-cached": "^1.1.0",
        "gulp-debug": "^2.1.2",
        "gulp-gitignore": "^0.1.0",
        "gulp-imagemin": "^2.4.0",
        "gulp-load-plugins": "^1.2.2",
        "gulp-responsive": "^2.2.0"
      }
    

    It takes files from Source, moves them to output, builds responsive images from the contents of output and stores them in responsive, then applies imagemin and moves them back into output. Finally, responsive is cleaned.