Created
May 26, 2022 12:36
-
-
Save mindon/a224958eb774a74a64f2887fe56e7ab2 to your computer and use it in GitHub Desktop.
Let's say, what if your designer providing a svg file with large embedded images... how to optimize the svg file size?
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| (function(svgdoc, scale) { | |
| // optimize large embed images in a svg | |
| // usage: <html><body><svg>your own real svg with too large embeded images...</svg><script src="optimize-embed-images-size-in-svg.js">< /script></body></html> | |
| // author: mindon@live.com | |
| function optimize(img, cb) { | |
| const src = img ? img.getAttribute("xlink:href") : undefined; | |
| let c = document.createElement("canvas"); | |
| const done = () => { | |
| cb && cb(); | |
| src && img.setAttribute('xlink:href', c.toDataURL('image/png')); | |
| c = null; | |
| return fin(); | |
| }; | |
| if (!src) { | |
| return done(); | |
| } | |
| const i = new Image(); | |
| i.onload = () => { | |
| const x = Math.min(i.width, i.height); | |
| const r = x <= scale ? 1 : scale / x; | |
| const w = i.width * r, | |
| h = i.height * r; | |
| c.width = w; | |
| c.height = h; | |
| const ctx = c.getContext('2d'); | |
| ctx.globalAlpha = 1; | |
| if (!img.getAttribute('mask')) { | |
| ctx.drawImage(i, 0, 0, i.width, i.height, 0, 0, c.width, c.height); | |
| return done(); | |
| } | |
| const maskid = img.getAttribute('mask').match(/url\((#[^)]+)\)/)[1]; | |
| img.removeAttribute('mask'); | |
| const t = svgdoc.querySelector(maskid).querySelector('image'), | |
| tmask = t.getAttribute('mask'), | |
| tsrc = t.getAttribute('xlink:href'); | |
| const update = () => { | |
| if (!tsrc) { | |
| return done(); | |
| } | |
| t.parentElement.parentElement.removeChild(t.parentElement); | |
| const j = new Image(); | |
| j.onload = () => { | |
| ctx.drawImage(j, 0, 0, j.width, j.height, 0, 0, w, h); | |
| const alpha = ctx.getImageData(0, 0, w, h).data; | |
| ctx.drawImage(i, 0, 0, i.width, i.height, 0, 0, w, h); | |
| const contents = ctx.getImageData(0, 0, w, h), | |
| data = contents.data; | |
| for (let i = 3, len = data.length; i < len; i = i + 4) { | |
| data[i] = (alpha[i-1] + alpha[i-2] + alpha[i - 3])/3; | |
| } | |
| ctx.clearRect(0, 0, w, h); | |
| ctx.putImageData(contents, 0, 0); | |
| done(); | |
| }; | |
| j.src = tsrc; | |
| }; | |
| if (tmask) { | |
| return optimize(t, update); | |
| } | |
| update(); | |
| }; | |
| i.src = src; | |
| } | |
| function clean() { | |
| // remove tspan x | |
| [].slice.call(svgdoc.querySelectorAll('tspan')).forEach(t => { | |
| t.removeAttribute('x'); | |
| }); | |
| // remove used reference id | |
| [].slice.call(svgdoc.querySelectorAll('defs > *[id]')).forEach(t => { | |
| const id = t.getAttribute('id'); | |
| if (!svgdoc.querySelector(`*[mask="url(#${id})"`) && | |
| !svgdoc.querySelector(`*[clip-path="url(#${id})"`)) { | |
| t.parentElement.removeChild(t); | |
| } | |
| }); | |
| } | |
| const list = [].slice.call(svgdoc.querySelectorAll('image')); | |
| let n = list.length; | |
| const fin = () => { | |
| n--; | |
| if (n == 0) { | |
| clean(); | |
| // download as svg | |
| const a = document.createElement('a'), | |
| now = new Date(); | |
| a.download = `optimized_${ [`0${now.getMonth() + 1}`.slice(-2), `0${now.getDate()}`.slice(-2), `0${now.getHours()}`.slice(-2), , `0${now.getMinutes()}`.slice(-2)].join('') }.svg`; | |
| a.href = `data:image/svg+xml;base64,${ btoa(unescape(encodeURIComponent(svgdoc.outerHTML.replace(/\s+\n/g, '\n'))))}`; | |
| a.click(); | |
| } | |
| }; | |
| list.forEach(img => optimize(img)); | |
| })(document.querySelector('svg'), 320); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment