7

What I want

I have very big images in my assets, which slows down the site by a lot for slower networks. (you can read more about the topic on this lighthouse linked page)

  • I would like to compress them at build time (ng build --prod).
  • For local development, it is irrelevant (ng serve).
  • Optimally I would like to generate multiple versions for different screen sizes (example.jpg → should become: example_x265.jpg, example_x128.jpg, ...)

What I have tried

The most promising guide I have found for that is this one here, which describes how to use the imagemin package in combination with the ngx-build-plus package.

Unfortunately, after following the tutorial, I get the following error:

[error] TypeError: Cannot assign to read only property 'main.977fe6373cfd108d.js' of object '#<Object>'
    at ImageminPlugin._callee2$ (/.../node_modules/imagemin-webpack-plugin/dist/index.js:264:48)
    at tryCatch (/.../node_modules/babel-runtime/node_modules/regenerator-runtime/runtime.js:62:40)
     // ...

Is there any way to compress asset images on build?

Angular Version: 13.1.0

Note: I do not want to know how to upload images to third party storage solutions.
I specifically want to create a compressed version of my static assets on build time.

MauriceNino
  • 6,214
  • 1
  • 23
  • 60
  • As a note: I am talking about a limited amount of static, unchangeable asset images. There is no reason to bring in third party dependencies (GC/AWS) to host 3 images. Before I would do that, I would rather compress them by hand. How to do that is not the question, though. – MauriceNino Dec 20 '21 at 14:36
  • A png image is already compressed, how much do you hope to gain by recompressing it? – mmomtchev Jan 10 '22 at 13:18
  • @mmomtchev I hope to gain similar results to what lighthouse tells me is possible with compression. That means 1) image compression (which is still possible) 2) generating multiple resolutions for different viewports. The question is also about JPEGs and any other image format in general, not only about PNGs (which are also further compressible according to google). – MauriceNino Jan 10 '22 at 13:22
  • Take a look at https://dev.to/angular/automatic-adaptive-images-in-angular-applications-1ebf – Amirhossein Mehrvarzi Jan 12 '22 at 13:28

3 Answers3

8

You can use a gulpfile with either gulp-responsive or gulp-sharp-responsive. I personally use the latter, because it has support for the AVIF format.

To integrate it nicely with your Angular project, you can follow these steps:

  1. Install the dependencies: npm i --save-dev gulp gulp-sharp-responsive
  2. Create a gulpfile.js in your project root with the following content
const { src, dest } = require("gulp");
const sharpResponsive = require("gulp-sharp-responsive");

const compress = () =>
  src("images/*.{png,jpg}")
    .pipe(
      sharpResponsive({
        formats: [
          // jpeg
          { width: 256, format: "jpeg", rename: { suffix: "-256" } },
          { width: 512, format: "jpeg", rename: { suffix: "-512" } },
          { width: 1024, format: "jpeg", rename: { suffix: "-1024" } },
          // webp
          { width: 256, format: "webp", rename: { suffix: "-256" } },
          { width: 512, format: "webp", rename: { suffix: "-512" } },
          { width: 1024, format: "webp", rename: { suffix: "-1024" } },
          // avif
          { width: 256, format: "avif", rename: { suffix: "-256" } },
          { width: 512, format: "avif", rename: { suffix: "-512" } },
          { width: 1024, format: "avif", rename: { suffix: "-1024" } },
        ],
      })
    )
    .pipe(dest("src/assets/compressed"));

module.exports = {
  compress,
};
  1. Create a folder in your project root, where your uncompressed image files are located (In this example it is called images)
  2. Add a preinstall script to your package.js, so that your gulpfile is called on every build
"scripts": {
  "prebuild": "gulp compress",
  // ...
},

If you call npm run build now, it will compress your images and move them in the specified assets folder, before actually running ng build.

Now you can use the image files with a picture/source combination like in the following snippet. Keep in mind that the order of the source tags is important.

<!-- {{image}} is the image name -->
<picture *ngIf="image">
  <!-- avif -->
  <source
    srcset="assets/compressed/{{image}}-256.avif"
    media="(max-width: 512px)"
    type="image/avif"
  />
  <source
    srcset="assets/compressed/{{image}}-512.avif"
    media="(max-width: 1024px)"
    type="image/avif"
  />
  <source
    srcset="assets/compressed/{{image}}-1024.avif"
    media="(max-width: 2048px)"
    type="image/avif"
  />
  <!-- webp -->
  <source
    srcset="assets/compressed/{{image}}-256.webp"
    media="(max-width: 512px)"
    type="image/webp"
  />
  <source
    srcset="assets/compressed/{{image}}-512.webp"
    media="(max-width: 1024px)"
    type="image/webp"
  />
  <source
    srcset="assets/compressed/{{image}}-1024.webp"
    media="(max-width: 2048px)"
    type="image/webp"
  />
  <!-- jpeg -->
  <source
    srcset="assets/compressed/{{image}}-256.jpg"
    media="(max-width: 512px)"
    type="image/jpeg"
  />
  <source
    srcset="assets/compressed/{{image}}-512.jpg"
    media="(max-width: 1024px)"
    type="image/jpeg"
  />
  <source
    srcset="assets/compressed/{{image}}-1024.jpg"
    media="(max-width: 2048px)"
    type="image/jpeg"
  />
  <!-- original -->
  <img src="assets/compressed/{{ image }}-1024.jpg" />
</picture>

Lyadine
  • 346
  • 1
  • 5
0

If you have very big image assets it should be hosted separately in a service like amazons3 or Google Cloud Storage.

Apart from making your load time and builds faster it will also help to load image depending on screen resolution. You can either automate this using lamda/cloud functions or manually compress images to different sizes and serve them based on device resolution.

Dev Gourav
  • 136
  • 8
  • That would make sense if it were dynamic images (like user uploads), but I doubt there is any good reason to bring in services like Google Cloud or AWS for a handful of static asset images that are never to be changed. That I can manually compress them to different sizes is obvious and no answer to my question. – MauriceNino Dec 20 '21 at 14:33
-2

I would never do that! because its against the convetions You should try Firebase storage, they give you 1 GB for free, and its easy to implement.

Mohammed
  • 171
  • 2
  • 11