DEV Community

Anton Reshetov
Anton Reshetov

Posted on

Compress image in browser? Easy!

First of all I want to thank people who are interested in the project MySigMail - UI Email Signature Generator.

In repo came an interesting PR where it was implemented a way to compress the image in the browser, without using the server.

I would like to share the code:

function compressImage (base64) {
  const canvas = document.createElement('canvas')
  const img = document.createElement('img')

  return new Promise((resolve, reject) => {
    img.onload = function () {
      let width = img.width
      let height = img.height
      const maxHeight = 200
      const maxWidth = 200

      if (width > height) {
        if (width > maxWidth) {
          height = Math.round((height *= maxWidth / width))
          width = maxWidth
        }
      } else {
        if (height > maxHeight) {
          width = Math.round((width *= maxHeight / height))
          height = maxHeight
        }
      }
      canvas.width = width
      canvas.height = height

      const ctx = canvas.getContext('2d')
      ctx.drawImage(img, 0, 0, width, height)

      resolve(canvas.toDataURL('image/jpeg', 0.7))
    }
    img.onerror = function (err) {
      reject(err)
    }
    img.src = base64
  })
}
Enter fullscreen mode Exit fullscreen mode

Thanks @mykeels

MySigMail always happy to have contributors :)

Now I am working on version 2 in which there will be a WYSIWYG email editor.

Therefore, I would be happy if there are willing to help me in the development of templates for signatures in version 1.

https://github.com/antonreshetov/mysigmail

Top comments (11)

Collapse
 
qm3ster profile image
Mihail Malo

Just a note, the width > height check only works correctly when maxHeight === maxWidth:

{
      let width = 200
      let height = 200
      const maxHeight = 200
      const maxWidth = 10

      if (width > height) {
        if (width > maxWidth) {
          height = Math.round((height *= maxWidth / width))
          width = maxWidth
        }
      } else {
        if (height > maxHeight) {
          width = Math.round((width *= maxHeight / height))
          height = maxHeight
        }
      }
console.log(width,height)
}
{
      let width = 201
      let height = 200
      const maxHeight = 10
      const maxWidth = 200

      if (width > height) {
        if (width > maxWidth) {
          height = Math.round((height *= maxWidth / width))
          width = maxWidth
        }
      } else {
        if (height > maxHeight) {
          width = Math.round((width *= maxHeight / height))
          height = maxHeight
        }
      }
console.log(width,height)
}
Collapse
 
lexlohr profile image
Alex Lohr

It's more resize than compress. If you wanted compression, try something like squoosh.app - transpiling image codecs and running them in the front end.

Collapse
 
antonreshetov profile image
Anton Reshetov

Why only resize? This is resize and compression, though not some advanced as squoosh. But the goals of the application is enough

canvas.toDataURL('image/jpeg', 0.7) 0.7 - is compression rank

developer.mozilla.org/en-US/docs/W...

Collapse
 
lexlohr profile image
Alex Lohr

Actually, it is a percentage of signal quality for the quantizer to aim for. While the result is compression, what you set there is the amount of signal loss you deem acceptable.

Thread Thread
 
antonreshetov profile image
Anton Reshetov

I can not confirm your words, but I trust the documentation

A Number between 0 and 1 indicating the image quality to use for image formats that use lossy compression such as image/jpeg and image/webp.
If this argument is anything else, the default value for image quality is used. The default value is 0.92. Other arguments are ignored.

Thread Thread
 
lexlohr profile image
Alex Lohr

That is exactly what I was referring to: you set the quality of the image in a lossy codec, not the compression factor (though both might coincide).

Thread Thread
 
antonreshetov profile image
Anton Reshetov

Got it )

Collapse
 
rhymes profile image
rhymes

I wonder if this would be the perfect thing to delegate to WebAssembly, what do you think? It might become slow with big images (also it's going to happen in the calling thread, right?)

Collapse
 
antonreshetov profile image
Anton Reshetov

Sadly, but i'am dummy in WebAssembly 😬 So I can't say anything about that.

Collapse
 
rhymes profile image
rhymes

Well, it's not like I'm an expert either. But we can learn things 😉

In the meantime you can test what happens to the function if you give it a truly massive image

Collapse
 
qm3ster profile image
Mihail Malo

Definitely