├── .editorconfig
├── .env
├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── docs
├── Pretendard-Black.subset-CVWqp8MN.woff2
├── Pretendard-Bold.subset-C7miX2zx.woff2
├── Pretendard-ExtraBold.subset-C2-BvFUE.woff2
├── Pretendard-ExtraLight.subset-BOZr4nRl.woff2
├── Pretendard-Light.subset-B94xHKuS.woff2
├── Pretendard-Medium.subset-Y06xfiVw.woff2
├── Pretendard-Regular.subset-ZBqo5aGF.woff2
├── Pretendard-SemiBold.subset-ZPzDHilX.woff2
├── Pretendard-Thin.subset-DWc8mLUC.woff2
├── favicon.ico
├── index-C0EeogAJ.js
├── index-DhP0pGVv.css
└── index.html
├── libs
├── ImageResize.es.js
├── ImageResize.umd.js
└── types.d.ts
├── package.json
├── public
└── favicon.ico
├── src
├── demo
│ ├── fonts.css
│ ├── fonts
│ │ ├── Pretendard-Black.subset.woff2
│ │ ├── Pretendard-Bold.subset.woff2
│ │ ├── Pretendard-ExtraBold.subset.woff2
│ │ ├── Pretendard-ExtraLight.subset.woff2
│ │ ├── Pretendard-Light.subset.woff2
│ │ ├── Pretendard-Medium.subset.woff2
│ │ ├── Pretendard-Regular.subset.woff2
│ │ ├── Pretendard-SemiBold.subset.woff2
│ │ └── Pretendard-Thin.subset.woff2
│ ├── index.css
│ ├── index.html
│ ├── index.js
│ └── libs.js
└── image-resize
│ ├── index.js
│ ├── libs
│ ├── checks.js
│ ├── consts.js
│ ├── files.js
│ ├── filter-sharpen.js
│ ├── numbers.js
│ └── output.js
│ └── worker.js
├── types.d.ts
└── vite.config
├── vite.docs.js
└── vite.libs.js
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/.env:
--------------------------------------------------------------------------------
1 | # dev-server
2 | VITE_HOST="0.0.0.0"
3 | VITE_PORT="3000"
4 | VITE_OPEN_BROWSER="false"
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .idea/
3 |
4 | .DS_Store
5 | .env.local
6 | yarn.lock
7 | package-lock.json
8 | bun.lockb
9 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .idea/
3 | docs/
4 |
5 | .DS_Store
6 | .env.local
7 | yarn.lock
8 | package-lock.json
9 | bun.lockb
10 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 redgoose
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # image-resize
2 |
3 | 개인적으로 마음에 드는 이미지 리사이즈 도구를 못찾아서 이렇게 만들게 되었다.
4 | 이 도구는 웹 브라우저에서 사용하는 ``엘리먼트를 사용하여 이미지 리사이즈 한다.
5 |
6 |
7 | ## Demo
8 |
9 | 데모를 통하여 이미지 url, 이미지 첨부파일을 리사이즈하는 모습을 볼 수 확인할 수 있다.
10 |
11 | > https://kode-team.github.io/image-resize/
12 |
13 |
14 | ## Installation
15 |
16 | `CLI`에서 설치할 프로젝트에서 다음과 같은 명령을 실행한다.
17 |
18 | ```
19 | npm install image-resize
20 | yarn add image-resize
21 | bun add -d image-resize
22 | ```
23 |
24 |
25 | ## Usage
26 |
27 | `import`로 함수를 불러서 비동기 방식으로 사용하면 된다.
28 |
29 | ### Module environment
30 |
31 | ```javascript
32 | import imageResize from 'image-resize'
33 |
34 | let res = await imageResize('image.jpg', {
35 | format: 'png',
36 | width: 640,
37 | })
38 | ```
39 |
40 | ### Vanilla environment
41 |
42 | ```html
43 |
44 |
45 |
51 | ```
52 |
53 |
54 | ## Source
55 |
56 | 사용하기 먼저 어떤 형식의 이미지 데이터를 지원하는지 확인해볼 필요가 있다.
57 | 지원하는 이미지 데이터의 형식은 다음과 같다.
58 |
59 | - `String`: 이미지 url
60 | - `File`: File 형식의 객체
61 | - `Blob`: Blob 타입의 객체
62 | - `HTMLCanvasElement`: canvas 엘리먼트
63 |
64 |
65 | ## Options
66 |
67 | 다음과 같은 옵션을 사용할 수 있다.
68 |
69 | | Name | Type | Default | Description |
70 | |:----------:|:------:|:-------------:|:--------------------------------------------------------|
71 | | width | number | `320` | 조절할 가로사이즈 |
72 | | height | number | `undefined` | 조절할 세로 사이즈. 한쪽값이 있는쪽으로 기준이 되어 조절한다. |
73 | | format | string | `jpg` | 출력할 포맷. `png,jpg,webp` |
74 | | outputType | string | `base64` | 출력방식. `base64,canvas,blob` |
75 | | quality | number | `.75` | jpg 이미지일때의 이미지 퀄리티값 |
76 | | reSample | number | `2` | 리샘플링 횟수. 수치가 높을수록 경계선이 부드러워지지만 처리속도는 느려진다. 최대 4까지 적용된다. |
77 | | sharpen | number | `0.75` | 선명함의 강도 |
78 | | bgColor | string | `transparent` | 캔버스 배경색 (배경이 투명한 이미지를 사용하면 영향을 받을 수 있다.) |
79 |
80 | ### 사이즈 값 조정
81 |
82 | width, height 사이즈 값은 다음과 같은 조건을 따른다.
83 |
84 | - width, height 둘다 있을때: width 값이 기준이 된다.
85 | - width 값이 없을때: height 값이 기준이 된다.
86 | - height 값이 없을때: width 값이 기준이 된다.
87 | - width, height 둘다 없을때: width 값이 기준이 된다.
88 |
89 | 만약 height 값을 기준으로 사용하고 싶다면 `width`값을 `0`이나 `undefined`으로 넣어줘야한다.
90 |
91 | ### 배경색
92 |
93 | 기본값으로 `transparent`로 설정되어있다.
94 | 투명한 배경 이미지에 배경색을 넣고싶다면 `#ffffff`같은 값으로 넣어주면 배경색이 들어가게 된다.
95 |
96 |
97 | ## Output
98 |
99 | 함수를 실행하고 반환해주는 데이터의 형식이다.
100 | 옵션에서 `outputType` 값에 맞는 형식으로 데이터가 리턴된다.
101 |
102 | - `base64`: base64 형식의 문자
103 | - `blob`: Blob 타입의 객체
104 | - `canvas`: canvas 엘리먼트
105 |
106 |
107 | ## Development
108 |
109 | 이 도구를 직접 수정할 수 있다.
110 | `/src`에 있는 소스를 수정하고 다음과 같이 cli 명령을 통하여 빌드할 수 있다.
111 |
112 | ```
113 | // development
114 | npm run dev
115 |
116 | // production
117 | npm run build
118 |
119 | // preview
120 | npm run preview
121 | ```
122 |
--------------------------------------------------------------------------------
/docs/Pretendard-Black.subset-CVWqp8MN.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kode-team/image-resize/44d1a9e79ff966ba5ddf1ac91606d883fc43b3c3/docs/Pretendard-Black.subset-CVWqp8MN.woff2
--------------------------------------------------------------------------------
/docs/Pretendard-Bold.subset-C7miX2zx.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kode-team/image-resize/44d1a9e79ff966ba5ddf1ac91606d883fc43b3c3/docs/Pretendard-Bold.subset-C7miX2zx.woff2
--------------------------------------------------------------------------------
/docs/Pretendard-ExtraBold.subset-C2-BvFUE.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kode-team/image-resize/44d1a9e79ff966ba5ddf1ac91606d883fc43b3c3/docs/Pretendard-ExtraBold.subset-C2-BvFUE.woff2
--------------------------------------------------------------------------------
/docs/Pretendard-ExtraLight.subset-BOZr4nRl.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kode-team/image-resize/44d1a9e79ff966ba5ddf1ac91606d883fc43b3c3/docs/Pretendard-ExtraLight.subset-BOZr4nRl.woff2
--------------------------------------------------------------------------------
/docs/Pretendard-Light.subset-B94xHKuS.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kode-team/image-resize/44d1a9e79ff966ba5ddf1ac91606d883fc43b3c3/docs/Pretendard-Light.subset-B94xHKuS.woff2
--------------------------------------------------------------------------------
/docs/Pretendard-Medium.subset-Y06xfiVw.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kode-team/image-resize/44d1a9e79ff966ba5ddf1ac91606d883fc43b3c3/docs/Pretendard-Medium.subset-Y06xfiVw.woff2
--------------------------------------------------------------------------------
/docs/Pretendard-Regular.subset-ZBqo5aGF.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kode-team/image-resize/44d1a9e79ff966ba5ddf1ac91606d883fc43b3c3/docs/Pretendard-Regular.subset-ZBqo5aGF.woff2
--------------------------------------------------------------------------------
/docs/Pretendard-SemiBold.subset-ZPzDHilX.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kode-team/image-resize/44d1a9e79ff966ba5ddf1ac91606d883fc43b3c3/docs/Pretendard-SemiBold.subset-ZPzDHilX.woff2
--------------------------------------------------------------------------------
/docs/Pretendard-Thin.subset-DWc8mLUC.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kode-team/image-resize/44d1a9e79ff966ba5ddf1ac91606d883fc43b3c3/docs/Pretendard-Thin.subset-DWc8mLUC.woff2
--------------------------------------------------------------------------------
/docs/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kode-team/image-resize/44d1a9e79ff966ba5ddf1ac91606d883fc43b3c3/docs/favicon.ico
--------------------------------------------------------------------------------
/docs/index-C0EeogAJ.js:
--------------------------------------------------------------------------------
1 | (function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const n of document.querySelectorAll('link[rel="modulepreload"]'))l(n);new MutationObserver(n=>{for(const d of n)if(d.type==="childList")for(const i of d.addedNodes)i.tagName==="LINK"&&i.rel==="modulepreload"&&l(i)}).observe(document,{childList:!0,subtree:!0});function t(n){const d={};return n.integrity&&(d.integrity=n.integrity),n.referrerPolicy&&(d.referrerPolicy=n.referrerPolicy),n.crossOrigin==="use-credentials"?d.credentials="include":n.crossOrigin==="anonymous"?d.credentials="omit":d.credentials="same-origin",d}function l(n){if(n.ep)return;n.ep=!0;const d=t(n);fetch(n.href,d)}})();const f="1.4.2",Y="KGZ1bmN0aW9uKCl7InVzZSBzdHJpY3QiO2NvbnN0IG89e1NUQVJUOiJTVEFSVCIsRU5EOiJFTkQiLE5FV19DQU5WQVM6Ik5FV19DQU5WQVMiLEVSUk9SOiJFUlJPUiJ9LFQ9e2NhbnZhczpudWxsLHJlU2FtcGxlOjIsd2lkdGg6MzIwLGhlaWdodDoyNDAsY3g6MCxjeTowLGN3OjAsY2g6MCxkeDowLGR5OjAsZHc6MCxkaDowLGJnQ29sb3I6InRyYW5zcGFyZW50In07YXN5bmMgZnVuY3Rpb24gRShlKXtyZXR1cm4gYXdhaXQoYXdhaXQgZmV0Y2goZSkpLmJsb2IoKX1mdW5jdGlvbiBrKGUpe3JldHVybiBuZXcgUHJvbWlzZSgodCxhKT0+e2NvbnN0IG49bmV3IEZpbGVSZWFkZXI7bi5vbmxvYWQ9cj0+e3ZhciBjO3JldHVybiB0KChjPXIudGFyZ2V0KT09bnVsbD92b2lkIDA6Yy5yZXN1bHQpfSxuLm9uZXJyb3I9YSxuLnJlYWRBc0RhdGFVUkwoZSl9KX1mdW5jdGlvbiBJKGUpe2NvbnN0IHQ9YXRvYihlLnNwbGl0KCIsIilbMV0pLGE9ZS5tYXRjaCgvW146XVx3K1wvW1x3LStcZC5dKyg/PTt8LCkvKVswXSxuPXQubGVuZ3RoLHI9bmV3IFVpbnQ4QXJyYXkobik7Zm9yKGxldCBjPTA7YzxuO2MrKylyW2NdPXQuY2hhckNvZGVBdChjKTtyZXR1cm4gbmV3IEJsb2IoW3IuYnVmZmVyXSx7dHlwZTphfSl9ZnVuY3Rpb24gRChlLHQsYSxuKXtsZXQgcj1lLGM9dDtyZXR1cm4gYSYmbiYmKGE+bj9uPXZvaWQgMDphPXZvaWQgMCksYT8ocj1hLGM9dCooYS9lKSk6biYmKHI9ZSoobi90KSxjPW4pLHt3aWR0aDpOdW1iZXIociksaGVpZ2h0Ok51bWJlcihjKX19ZnVuY3Rpb24gTyhlLHQ9MCxhKXtpZih0PD0wKXJldHVybiBlO2NvbnN0IG49YS5nZXRDb250ZXh0KCIyZCIpO24uZHJhd0ltYWdlKGUsMCwwKTtjb25zdCByPW4uZ2V0SW1hZ2VEYXRhKDAsMCxhLndpZHRoLGEuaGVpZ2h0KSxjPW4uY3JlYXRlSW1hZ2VEYXRhKGEud2lkdGgsYS5oZWlnaHQpLGk9QihyLGMsWzAsLTEsMCwtMSw1LC0xLDAsLTEsMF0pO24ucHV0SW1hZ2VEYXRhKGksMCwwKTtjb25zdCBzPWUuZ2V0Q29udGV4dCgiMmQiKTtyZXR1cm4gcy5nbG9iYWxBbHBoYT10fHwxLHMuZHJhd0ltYWdlKGEsMCwwKSxlfWZ1bmN0aW9uIEIoZSx0LGEpe2NvbnN0IG49ZS5kYXRhLHI9dC5kYXRhLGM9ZS53aWR0aCxpPWUuaGVpZ2h0LHM9TWF0aC5yb3VuZChNYXRoLnNxcnQoYS5sZW5ndGgpKSxsPU1hdGguZmxvb3Iocy8yKSx5PWMsSz1pO2ZvcihsZXQgZD0wO2Q8SztkKyspZm9yKGxldCBoPTA7aDx5O2grKyl7bGV0IHA9MCxBPTAsTj0wLE09MDtmb3IobGV0IGY9MDtmPHM7ZisrKWZvcihsZXQgZz0wO2c8cztnKyspe2NvbnN0IHg9ZCtmLWwsUz1oK2ctbDtpZih4Pj0wJiZ4PGkmJlM+PTAmJlM8Yyl7bGV0IGI9KHgqYytTKSo0LG09YVtmKnMrZ107cCs9bltiXSptLEErPW5bYisxXSptLE4rPW5bYisyXSptLE0rPW5bYiszXSptfX1jb25zdCB3PShkKnkraCkqNDtyW3ddPXAsclt3KzFdPUEsclt3KzJdPU4sclt3KzNdPU19cmV0dXJuIHR9ZnVuY3Rpb24geihlLHQ9ImltYWdlL2pwZWciLGE9Ljc1KXtyZXR1cm4gdD1SKHQpLG5ldyBQcm9taXNlKGFzeW5jKG4scik9Pnt0cnl7Y29uc3QgYz1hd2FpdCBqKGUsdCxhKTtuKGMpfWNhdGNoKGMpe3IoYyl9fSl9ZnVuY3Rpb24gUChlLHQ9ImltYWdlL2pwZWciLGE9Ljc1KXtyZXR1cm4gdD1SKHQpLG5ldyBQcm9taXNlKGFzeW5jKG4scik9Pnt0cnl7Y29uc3QgYz1hd2FpdCBlLmNvbnZlcnRUb0Jsb2Ioe3R5cGU6dCxxdWFsaXR5OmF9KTtuKGMpfWNhdGNoKGMpe3IoYyl9fSl9ZnVuY3Rpb24gUihlKXtsZXQgdDtzd2l0Y2goZSl7Y2FzZSJqcGciOmNhc2UianBlZyI6dD0iaW1hZ2UvanBlZyI7YnJlYWs7Y2FzZSJwbmciOnQ9ImltYWdlL3BuZyI7YnJlYWs7Y2FzZSJ3ZWJwIjp0PSJpbWFnZS93ZWJwIjticmVhaztkZWZhdWx0OnQ9ZTticmVha31yZXR1cm4gdH1hc3luYyBmdW5jdGlvbiBqKGUsdCxhKXtjb25zdCBuPWF3YWl0IGUuY29udmVydFRvQmxvYih7dHlwZTp0LHF1YWxpdHk6YX0pLHI9bmV3IEZpbGVSZWFkZXI7cmV0dXJuIG5ldyBQcm9taXNlKGM9PntyLm9ubG9hZGVuZD0oKT0+YyhyLnJlc3VsdCksci5yZWFkQXNEYXRhVVJMKG4pfSl9bGV0IF89MDtjb25zdCBDPW5ldyBNYXA7b25tZXNzYWdlPWFzeW5jIGZ1bmN0aW9uKGUpe2NvbnN0e2tleTp0LGlkOmEsc3JjOm4sb3B0aW9uczpyfT1lLmRhdGE7dHJ5e2lmKGEhPT12b2lkIDApe2NvbnN0IGM9Qy5nZXQoYSk7YyYmYyhlLmRhdGEpfWVsc2Ugc3dpdGNoKHQpe2Nhc2Ugby5TVEFSVDpsZXQgYz1hd2FpdCBxKG4scik7Yz1hd2FpdCBVKGMsciksYz1hd2FpdCB2KGMsciksYXdhaXQgRyhjLHIpO2JyZWFrO2Nhc2Ugby5ORVdfQ0FOVkFTOmJyZWFrfX1jYXRjaChjKXtwb3N0TWVzc2FnZSh7a2V5Om8uRVJST1IsZXJyb3I6Y30pfX07ZnVuY3Rpb24gRihlLHQpe3JldHVybiBuZXcgUHJvbWlzZShhPT57Y29uc3Qgbj1fKys7Qy5zZXQobixhKSxwb3N0TWVzc2FnZSh7aWQ6bixrZXk6ZSwuLi50fSl9KX1hc3luYyBmdW5jdGlvbiBxKGUsdCl7aWYodHlwZW9mIGU9PSJzdHJpbmciKXtjb25zdCBhPWF3YWl0IEUoZSksbj1hd2FpdCBjcmVhdGVJbWFnZUJpdG1hcChhKSxyPWF3YWl0IHUobi53aWR0aCxuLmhlaWdodCx0LmJnQ29sb3IpO3JldHVybiByLmdldENvbnRleHQoIjJkIikuZHJhd0ltYWdlKG4sMCwwKSxyfWVsc2UgaWYoZSBpbnN0YW5jZW9mIEZpbGV8fGUgaW5zdGFuY2VvZiBCbG9iKXtjb25zdCBhPWF3YWl0IGsoZSksbj1JKGEpLHI9YXdhaXQgY3JlYXRlSW1hZ2VCaXRtYXAobiksYz1hd2FpdCB1KHIud2lkdGgsci5oZWlnaHQsdC5iZ0NvbG9yKTtyZXR1cm4gYy5nZXRDb250ZXh0KCIyZCIpLmRyYXdJbWFnZShyLDAsMCksY319YXN5bmMgZnVuY3Rpb24gdShlPTMyMCx0PTI0MCxhPSJ0cmFuc3BhcmVudCIpe2NvbnN0IHI9KGF3YWl0IEYoby5ORVdfQ0FOVkFTLHt3aWR0aDplLGhlaWdodDp0fSkpLmNhbnZhcyxjPXIuZ2V0Q29udGV4dCgiMmQiKTtyZXR1cm4gYy5maWxsU3R5bGU9YSxjLmZpbGxSZWN0KDAsMCxlLHQpLHJ9YXN5bmMgZnVuY3Rpb24gVShlLHQpe2NvbnN0IGE9RChlLndpZHRoLGUuaGVpZ2h0LHQud2lkdGgsdC5oZWlnaHQpO3JldHVybiBhd2FpdCBWKHtjYW52YXM6ZSxyZVNhbXBsZTp0LnJlU2FtcGxlLHdpZHRoOmEud2lkdGgsaGVpZ2h0OmEuaGVpZ2h0LGN4OjAsY3k6MCxjdzplLndpZHRoLGNoOmUuaGVpZ2h0LGR4OjAsZHk6MCxkdzphLndpZHRoLGRoOmEuaGVpZ2h0LGJnQ29sb3I6dC5iZ0NvbG9yfSl9ZnVuY3Rpb24gVihlKXtlPU9iamVjdC5hc3NpZ24oe30sVCxlKSxlLnJlU2FtcGxlPU1hdGgubWluKDQsZS5yZVNhbXBsZSksZS5yZVNhbXBsZT1NYXRoLm1heCgwLGUucmVTYW1wbGUpO2NvbnN0IHQ9TWF0aC5wb3coMixlLnJlU2FtcGxlKTtyZXR1cm4gbmV3IFByb21pc2UoYXN5bmMoYSxuKT0+e3RyeXtjb25zdCByPWF3YWl0IHUoZS53aWR0aCp0LGUuaGVpZ2h0KnQsZS5iZ0NvbG9yKTtyLmdldENvbnRleHQoIjJkIikuZHJhd0ltYWdlKGUuY2FudmFzLGUuY3gsZS5jeSxlLmN3LGUuY2gsZS5keCp0LGUuZHkqdCxlLmR3KnQsZS5kaCp0KSxlLnJlU2FtcGxlPjA/TChlLGUucmVTYW1wbGUscikudGhlbihhKTphKHIpfWNhdGNoKHIpe24ocil9fSl9ZnVuY3Rpb24gTChlLHQsYSl7cmV0dXJuIG5ldyBQcm9taXNlKG49Pnthc3luYyBmdW5jdGlvbiByKGMsaSl7Y29uc3Qgcz1NYXRoLnBvdygyLGMpLGw9YXdhaXQgdShlLndpZHRoKnMsZS5oZWlnaHQqcyxlLmJnQ29sb3IpO2wuZ2V0Q29udGV4dCgiMmQiKS5kcmF3SW1hZ2UoaSwwLDAsaS53aWR0aCouNSxpLmhlaWdodCouNSksYz4wP3IoYy0xLGwpLnRoZW4oKTpuKGwpfXIodC0xLGEpLnRoZW4oKX0pfWFzeW5jIGZ1bmN0aW9uIHYoZSx0KXtjb25zdCBhPWlzTmFOKHQ9PW51bGw/dm9pZCAwOnQuc2hhcnBlbik/MDp0LnNoYXJwZW47aWYoYTw9MClyZXR1cm4gZTtjb25zdCBuPWF3YWl0IHUoZS53aWR0aCxlLmhlaWdodCx0LmJnQ29sb3IpO3JldHVybiBPKGUsYSxuKX1hc3luYyBmdW5jdGlvbiBHKGUsdCl7bGV0IGE7c3dpdGNoKHQub3V0cHV0VHlwZSl7Y2FzZSJiYXNlNjQiOmE9YXdhaXQgeihlLHQuZm9ybWF0LHQucXVhbGl0eSkscG9zdE1lc3NhZ2Uoe2tleTpvLkVORCxvdXRwdXQ6YX0pO2JyZWFrO2Nhc2UiYmxvYiI6YT1hd2FpdCBQKGUsdC5mb3JtYXQsdC5xdWFsaXR5KSxwb3N0TWVzc2FnZSh7a2V5Om8uRU5ELG91dHB1dDphfSk7YnJlYWs7Y2FzZSJjYW52YXMiOmRlZmF1bHQ6Y29uc3Qgbj1lLnRyYW5zZmVyVG9JbWFnZUJpdG1hcCgpO3Bvc3RNZXNzYWdlKHtrZXk6by5FTkQsb3V0cHV0Om59LFtuXSk7YnJlYWt9fX0pKCk7Cg==",S=e=>Uint8Array.from(atob(e),t=>t.charCodeAt(0)),p=typeof window<"u"&&window.Blob&&new Blob([S(Y)],{type:"text/javascript;charset=utf-8"});function X(e){let t;try{if(t=p&&(window.URL||window.webkitURL).createObjectURL(p),!t)throw"";const l=new Worker(t,{name:e==null?void 0:e.name});return l.addEventListener("error",()=>{(window.URL||window.webkitURL).revokeObjectURL(t)}),l}catch{return new Worker("data:text/javascript;base64,"+Y,{name:e==null?void 0:e.name})}finally{t&&(window.URL||window.webkitURL).revokeObjectURL(t)}}const o={START:"START",END:"END",NEW_CANVAS:"NEW_CANVAS",ERROR:"ERROR"},b={width:320,height:240,format:"jpg",outputType:"base64",quality:.75,reSample:2,sharpen:0,bgColor:"transparent"};function g(e={}){let t={};return Object.keys(b).forEach(l=>{t[l]=e[l]!==void 0?e[l]:b[l]}),e.width===void 0&&e.height===void 0?(t.width=b.width,t.height=void 0):e.width===void 0?(t.width=void 0,t.height=Number(t.height)||0):e.height===void 0?(t.width=Number(t.width)||0,t.height=void 0):(t.width=Number(t.width)||0,t.height=Number(t.height)||0),t.quality=Number(t.quality)||0,t.reSample=Number(t.reSample)||0,t}function K(e=void 0){if(typeof e!="string"){{if(e instanceof File||e instanceof Blob)return;if(e instanceof HTMLCanvasElement)return}throw new Error("Invalid image data.")}}function V(e){return new Promise((t,l)=>{e instanceof HTMLCanvasElement?e.toBlob(n=>t(n)):l(new Error("The provided element is not a Canvas."))})}function R(e,t=void 0){return new Promise(async(l,n)=>{const d=g(t),i=new X;i.onmessage=async r=>{const{id:W,key:y,output:c,error:L}=r.data;switch(y){case o.END:if(i&&i.terminate(),c instanceof ImageBitmap){const a=document.createElement("canvas"),h=a.getContext("2d");a.width=c.width,a.height=c.height,h.fillStyle=d.bgColor,h.fillRect(0,0,c.width,c.height),h.drawImage(c,0,0),l(a)}else l(c);i&&i.terminate();break;case o.ERROR:n(L),i&&i.terminate();break;case o.NEW_CANVAS:const Z=new OffscreenCanvas(r.data.width,r.data.height);i.postMessage({id:W,key:o.NEW_CANVAS,canvas:Z},[Z]);break}},K(e),e instanceof HTMLCanvasElement&&(e=await V(e)),i.postMessage({key:o.START,src:e,options:d})})}function C(e={}){return new Promise((t,l)=>{const n=document.createElement("input");n.type="file",e.accept&&(n.accept=e.accept),e.multiple===!0&&(n.multiple=!0),n.addEventListener("change",d=>{const i=Object.assign([],d.target.files);if(i.length<=0)return t([]);n.value=null,t(e.multiple===!0?i:i[0])}),n.addEventListener("cancel",()=>{t(e.multiple===!0?[]:null)}),n.click()})}function I(e){const t=["Bytes","KB","MB","GB","TB"];if(e===0)return"0 Byte";let l=Math.floor(Math.log(e)/Math.log(1024));return String(Math.round(e/Math.pow(1024,l)))+t[l]}const w=document.getElementById("form"),m=document.getElementById("result"),s=new Proxy({},{get:(e,t)=>e[t],set:(e,t,l)=>{switch(t){case"width":case"height":case"quality":case"reSample":case"sharpen":l=Number(l);break}return e[t]=l,!0}}),u={submit:document.querySelector(".form__submit > button[type=submit]"),fileUpload:document.getElementById("file-upload"),fileUploadInfo:document.getElementById("file-upload-info"),version:document.getElementById("version")};async function N(){const e=await C({accept:"image/*"});e&&(s.upload=e,u.fileUploadInfo.innerText=`${e.name} (${I(e.size)})`)}async function H(e){e.preventDefault(),G(!0);const l=e.target.querySelectorAll("[name]");for(const d of l)s[d.name]=d.value;let n;if(s.upload)n=s.upload;else if(s.url)n=s.url;else return alert("not found source"),!1;try{const d={...s};delete d.url,delete d.upload;const i=await R(n,d);Q(d.outputType,i)}catch(d){k(d)}finally{G(!1)}}function G(e){const t=u.submit;e?(t.setAttribute("disabled",""),t.innerText="처리중.."):(t.removeAttribute("disabled"),t.innerText="이미지 변환")}function Q(e,t){switch(m.innerHTML="",e){case"base64":const l=new Image;l.src=t,m.appendChild(l);break;case"canvas":m.appendChild(t);break;case"blob":default:console.log("RESULT:",t);break}}function k(e){console.error("[ERROR / IMAGE RESIZE]",e.message),alert(`Error resize: ${e.message}`)}w.addEventListener("submit",H);u.fileUpload.addEventListener("click",N);u.version.innerText=f;
2 |
--------------------------------------------------------------------------------
/docs/index-DhP0pGVv.css:
--------------------------------------------------------------------------------
1 | @font-face{font-family:Pretendard;font-weight:900;font-display:swap;src:local("Pretendard Black"),url(./Pretendard-Black.subset-CVWqp8MN.woff2) format("woff2")}@font-face{font-family:Pretendard;font-weight:800;font-display:swap;src:local("Pretendard ExtraBold"),url(./Pretendard-ExtraBold.subset-C2-BvFUE.woff2) format("woff2")}@font-face{font-family:Pretendard;font-weight:700;font-display:swap;src:local("Pretendard Bold"),url(./Pretendard-Bold.subset-C7miX2zx.woff2) format("woff2")}@font-face{font-family:Pretendard;font-weight:600;font-display:swap;src:local("Pretendard SemiBold"),url(./Pretendard-SemiBold.subset-ZPzDHilX.woff2) format("woff2")}@font-face{font-family:Pretendard;font-weight:500;font-display:swap;src:local("Pretendard Medium"),url(./Pretendard-Medium.subset-Y06xfiVw.woff2) format("woff2")}@font-face{font-family:Pretendard;font-weight:400;font-display:swap;src:local("Pretendard Regular"),url(./Pretendard-Regular.subset-ZBqo5aGF.woff2) format("woff2")}@font-face{font-family:Pretendard;font-weight:300;font-display:swap;src:local("Pretendard Light"),url(./Pretendard-Light.subset-B94xHKuS.woff2) format("woff2")}@font-face{font-family:Pretendard;font-weight:200;font-display:swap;src:local("Pretendard ExtraLight"),url(./Pretendard-ExtraLight.subset-BOZr4nRl.woff2) format("woff2")}@font-face{font-family:Pretendard;font-weight:100;font-display:swap;src:local("Pretendard Thin"),url(./Pretendard-Thin.subset-DWc8mLUC.woff2) format("woff2")}:root{--color-base: #111111;--color-weak: #666666;--color-main: hsl(159deg 90% 42%);--color-shape: #f4f4f4;--color-field-line: hsl(0 0% 0% / 10%);--size-radius: 3px;--shadow-box: 0 1px 5px rgba(0, 0, 0, .061), 0 3px 18px rgba(0, 0, 0, .089), 0 12px 80px rgba(0, 0, 0, .15)}html{touch-action:manipulation}body{min-width:640px;margin:0;padding:0 50px 24px;-webkit-text-size-adjust:none;color:var(--color-base);font-size:16px;line-height:1.6}body,button,input,textarea,select{font-family:Pretendard,-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol;-webkit-text-size-adjust:100%;text-size-adjust:none;color:var(--color-base)}main{margin:0 auto;max-width:1000px}.header{padding:42px 0 24px;text-align:center;-webkit-user-select:none;user-select:none}.header>h1{margin:0;font-size:36px;color:var(--color-main);letter-spacing:-.5px;font-weight:900;line-height:1;text-transform:uppercase;-webkit-user-select:none;user-select:none}.header>p{margin:4px 0 0;font-size:13px;line-height:1.15;color:var(--color-weak)}.form{margin:0;padding:24px;background:#fff;box-shadow:var(--shadow-box);border-radius:4px}.form .form__fieldset{margin:0;padding:0;border:none}.form .form__fieldset>legend{font-size:0}.form .form__body{display:grid;margin:0;gap:0 0}.form .form__submit{display:flex;margin:20px 0 0;justify-content:center}.form .form__submit .button{width:160px;height:36px}.box-title{margin:0 0 8px;font-size:18px;font-weight:700;line-height:1.05;letter-spacing:-.5px;-webkit-user-select:none;user-select:none}.field{margin:0;padding:10px 12px;display:grid;grid-template-columns:120px 1fr;align-items:center;border-bottom:1px solid var(--color-field-line)}.field:first-child{border-top:1px solid var(--color-field-line)}.field .field__label{margin:0;line-height:1.15;font-weight:600;font-size:12px;-webkit-user-select:none;user-select:none}.field .field__body{margin:0}.field .field__description{margin:5px 0 0;font-size:11px;color:var(--color-weak);font-weight:400;line-height:1.15}.field-inline{display:grid;align-items:center;grid-template-columns:120px auto;gap:0 8px}.field-inline>span{display:block;font-size:12px;line-height:1}.field-inlines{display:flex;flex-direction:row;flex-wrap:wrap;gap:16px}.field-inlines+.field-inlines{margin-top:8px}.field-inlines .field-inline{grid-template-columns:auto auto;gap:8px}.input-text{display:block;margin:0;padding:0 10px;height:32px;font-size:13px;border-radius:var(--size-radius);border:1px solid #bbb;background:#fff;outline:none;box-sizing:border-box}.input-text.input-text--block{width:100%}.input-select{position:relative;display:inline-block}.input-select>select{display:inline-block;margin:0;padding:0 10px;height:32px;border:1px solid #bbb;font-size:13px;border-radius:var(--size-radius);background:#fff;min-width:100px;outline:none;box-sizing:border-box;-webkit-appearance:none;-moz-appearance:none;appearance:none}.input-select:after{content:"";display:block;position:absolute;right:6px;top:50%;width:16px;aspect-ratio:1/1;translate:0 -50%;pointer-events:none;background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgY2xhc3M9Imx1Y2lkZSBsdWNpZGUtY2hldnJvbi1kb3duIj48cGF0aCBkPSJtNiA5IDYgNiA2LTYiLz48L3N2Zz4=);background-size:100%}.input-file{display:grid;margin:0;padding:0 8px;height:32px;border:1px solid #bbb;background:#fff;font-size:13px;border-radius:var(--size-radius);outline:none;box-sizing:border-box}.input-file--block{width:100%}.input-text:focus-visible,.input-file:focus-visible,.input-select:focus-visible{box-shadow:0 0 0 2px var(--color-main)}.result{margin:48px 0 0;padding:24px 24px 32px;background:#fff;box-shadow:var(--shadow-box);border-radius:4px}.result .box-title{margin:0 0 16px}.result .result__image{margin:0}.result .result__image>*{display:block;margin:0 auto;max-width:100%}.result .result__image:empty:after{display:grid;place-content:center;height:120px;content:"...";background:#0000000a;font-size:13px;color:var(--color-weak)}.button{display:block;margin:0;padding:0;border:none;background:var(--color-main);color:#fff;font-size:15px;font-weight:800;letter-spacing:-.5px;cursor:pointer;outline:none;border-radius:var(--size-radius);transition:box-shadow .2s ease-out;-webkit-user-select:none;user-select:none;box-sizing:border-box}.button:disabled{opacity:.5;cursor:not-allowed}.button:where(:active,:focus-visible){box-shadow:0 0 0 4px #00000026}.file-upload{display:flex;align-items:center;gap:0 12px}.file-upload button{width:100px;height:28px;font-size:13px;font-weight:600}.file-upload p{margin:0;font-size:12px;line-height:1.15;font-weight:700;-webkit-user-select:none;user-select:none}.file-upload p:empty:before{content:"업로드한 파일이 없습니다.";font-weight:500;color:var(--color-weak)}.footer{margin:52px 0 0;text-align:center;-webkit-user-select:none;user-select:none}.footer>.info{margin:0;font-size:13px;line-height:1.05;font-weight:600}.footer>.info em{font-style:normal}.footer>.copyright{margin:4px 0 0;color:var(--color-weak);font-size:10px;line-height:1.05}.github-corner{display:block;position:fixed;right:0;top:0}.github-corner>svg{position:absolute;top:0;border:0;right:0;fill:#222;color:#fff}.github-corner .octo-arm{transform-origin:130px 106px}.github-corner:hover .octo-arm{animation:octocat-wave .56s ease-in-out}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave .56s ease-in-out}}@keyframes octocat-wave{0%,to{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}
2 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Image Resize demo
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
18 |
161 |
165 |
169 |
170 |
171 |
172 |
177 |
178 |
179 |
180 |
--------------------------------------------------------------------------------
/libs/ImageResize.es.js:
--------------------------------------------------------------------------------
1 | const W = "KGZ1bmN0aW9uKCl7InVzZSBzdHJpY3QiO2NvbnN0IG89e1NUQVJUOiJTVEFSVCIsRU5EOiJFTkQiLE5FV19DQU5WQVM6Ik5FV19DQU5WQVMiLEVSUk9SOiJFUlJPUiJ9LFQ9e2NhbnZhczpudWxsLHJlU2FtcGxlOjIsd2lkdGg6MzIwLGhlaWdodDoyNDAsY3g6MCxjeTowLGN3OjAsY2g6MCxkeDowLGR5OjAsZHc6MCxkaDowLGJnQ29sb3I6InRyYW5zcGFyZW50In07YXN5bmMgZnVuY3Rpb24gRShlKXtyZXR1cm4gYXdhaXQoYXdhaXQgZmV0Y2goZSkpLmJsb2IoKX1mdW5jdGlvbiBrKGUpe3JldHVybiBuZXcgUHJvbWlzZSgodCxhKT0+e2NvbnN0IG49bmV3IEZpbGVSZWFkZXI7bi5vbmxvYWQ9cj0+e3ZhciBjO3JldHVybiB0KChjPXIudGFyZ2V0KT09bnVsbD92b2lkIDA6Yy5yZXN1bHQpfSxuLm9uZXJyb3I9YSxuLnJlYWRBc0RhdGFVUkwoZSl9KX1mdW5jdGlvbiBJKGUpe2NvbnN0IHQ9YXRvYihlLnNwbGl0KCIsIilbMV0pLGE9ZS5tYXRjaCgvW146XVx3K1wvW1x3LStcZC5dKyg/PTt8LCkvKVswXSxuPXQubGVuZ3RoLHI9bmV3IFVpbnQ4QXJyYXkobik7Zm9yKGxldCBjPTA7YzxuO2MrKylyW2NdPXQuY2hhckNvZGVBdChjKTtyZXR1cm4gbmV3IEJsb2IoW3IuYnVmZmVyXSx7dHlwZTphfSl9ZnVuY3Rpb24gRChlLHQsYSxuKXtsZXQgcj1lLGM9dDtyZXR1cm4gYSYmbiYmKGE+bj9uPXZvaWQgMDphPXZvaWQgMCksYT8ocj1hLGM9dCooYS9lKSk6biYmKHI9ZSoobi90KSxjPW4pLHt3aWR0aDpOdW1iZXIociksaGVpZ2h0Ok51bWJlcihjKX19ZnVuY3Rpb24gTyhlLHQ9MCxhKXtpZih0PD0wKXJldHVybiBlO2NvbnN0IG49YS5nZXRDb250ZXh0KCIyZCIpO24uZHJhd0ltYWdlKGUsMCwwKTtjb25zdCByPW4uZ2V0SW1hZ2VEYXRhKDAsMCxhLndpZHRoLGEuaGVpZ2h0KSxjPW4uY3JlYXRlSW1hZ2VEYXRhKGEud2lkdGgsYS5oZWlnaHQpLGk9QihyLGMsWzAsLTEsMCwtMSw1LC0xLDAsLTEsMF0pO24ucHV0SW1hZ2VEYXRhKGksMCwwKTtjb25zdCBzPWUuZ2V0Q29udGV4dCgiMmQiKTtyZXR1cm4gcy5nbG9iYWxBbHBoYT10fHwxLHMuZHJhd0ltYWdlKGEsMCwwKSxlfWZ1bmN0aW9uIEIoZSx0LGEpe2NvbnN0IG49ZS5kYXRhLHI9dC5kYXRhLGM9ZS53aWR0aCxpPWUuaGVpZ2h0LHM9TWF0aC5yb3VuZChNYXRoLnNxcnQoYS5sZW5ndGgpKSxsPU1hdGguZmxvb3Iocy8yKSx5PWMsSz1pO2ZvcihsZXQgZD0wO2Q8SztkKyspZm9yKGxldCBoPTA7aDx5O2grKyl7bGV0IHA9MCxBPTAsTj0wLE09MDtmb3IobGV0IGY9MDtmPHM7ZisrKWZvcihsZXQgZz0wO2c8cztnKyspe2NvbnN0IHg9ZCtmLWwsUz1oK2ctbDtpZih4Pj0wJiZ4PGkmJlM+PTAmJlM8Yyl7bGV0IGI9KHgqYytTKSo0LG09YVtmKnMrZ107cCs9bltiXSptLEErPW5bYisxXSptLE4rPW5bYisyXSptLE0rPW5bYiszXSptfX1jb25zdCB3PShkKnkraCkqNDtyW3ddPXAsclt3KzFdPUEsclt3KzJdPU4sclt3KzNdPU19cmV0dXJuIHR9ZnVuY3Rpb24geihlLHQ9ImltYWdlL2pwZWciLGE9Ljc1KXtyZXR1cm4gdD1SKHQpLG5ldyBQcm9taXNlKGFzeW5jKG4scik9Pnt0cnl7Y29uc3QgYz1hd2FpdCBqKGUsdCxhKTtuKGMpfWNhdGNoKGMpe3IoYyl9fSl9ZnVuY3Rpb24gUChlLHQ9ImltYWdlL2pwZWciLGE9Ljc1KXtyZXR1cm4gdD1SKHQpLG5ldyBQcm9taXNlKGFzeW5jKG4scik9Pnt0cnl7Y29uc3QgYz1hd2FpdCBlLmNvbnZlcnRUb0Jsb2Ioe3R5cGU6dCxxdWFsaXR5OmF9KTtuKGMpfWNhdGNoKGMpe3IoYyl9fSl9ZnVuY3Rpb24gUihlKXtsZXQgdDtzd2l0Y2goZSl7Y2FzZSJqcGciOmNhc2UianBlZyI6dD0iaW1hZ2UvanBlZyI7YnJlYWs7Y2FzZSJwbmciOnQ9ImltYWdlL3BuZyI7YnJlYWs7Y2FzZSJ3ZWJwIjp0PSJpbWFnZS93ZWJwIjticmVhaztkZWZhdWx0OnQ9ZTticmVha31yZXR1cm4gdH1hc3luYyBmdW5jdGlvbiBqKGUsdCxhKXtjb25zdCBuPWF3YWl0IGUuY29udmVydFRvQmxvYih7dHlwZTp0LHF1YWxpdHk6YX0pLHI9bmV3IEZpbGVSZWFkZXI7cmV0dXJuIG5ldyBQcm9taXNlKGM9PntyLm9ubG9hZGVuZD0oKT0+YyhyLnJlc3VsdCksci5yZWFkQXNEYXRhVVJMKG4pfSl9bGV0IF89MDtjb25zdCBDPW5ldyBNYXA7b25tZXNzYWdlPWFzeW5jIGZ1bmN0aW9uKGUpe2NvbnN0e2tleTp0LGlkOmEsc3JjOm4sb3B0aW9uczpyfT1lLmRhdGE7dHJ5e2lmKGEhPT12b2lkIDApe2NvbnN0IGM9Qy5nZXQoYSk7YyYmYyhlLmRhdGEpfWVsc2Ugc3dpdGNoKHQpe2Nhc2Ugby5TVEFSVDpsZXQgYz1hd2FpdCBxKG4scik7Yz1hd2FpdCBVKGMsciksYz1hd2FpdCB2KGMsciksYXdhaXQgRyhjLHIpO2JyZWFrO2Nhc2Ugby5ORVdfQ0FOVkFTOmJyZWFrfX1jYXRjaChjKXtwb3N0TWVzc2FnZSh7a2V5Om8uRVJST1IsZXJyb3I6Y30pfX07ZnVuY3Rpb24gRihlLHQpe3JldHVybiBuZXcgUHJvbWlzZShhPT57Y29uc3Qgbj1fKys7Qy5zZXQobixhKSxwb3N0TWVzc2FnZSh7aWQ6bixrZXk6ZSwuLi50fSl9KX1hc3luYyBmdW5jdGlvbiBxKGUsdCl7aWYodHlwZW9mIGU9PSJzdHJpbmciKXtjb25zdCBhPWF3YWl0IEUoZSksbj1hd2FpdCBjcmVhdGVJbWFnZUJpdG1hcChhKSxyPWF3YWl0IHUobi53aWR0aCxuLmhlaWdodCx0LmJnQ29sb3IpO3JldHVybiByLmdldENvbnRleHQoIjJkIikuZHJhd0ltYWdlKG4sMCwwKSxyfWVsc2UgaWYoZSBpbnN0YW5jZW9mIEZpbGV8fGUgaW5zdGFuY2VvZiBCbG9iKXtjb25zdCBhPWF3YWl0IGsoZSksbj1JKGEpLHI9YXdhaXQgY3JlYXRlSW1hZ2VCaXRtYXAobiksYz1hd2FpdCB1KHIud2lkdGgsci5oZWlnaHQsdC5iZ0NvbG9yKTtyZXR1cm4gYy5nZXRDb250ZXh0KCIyZCIpLmRyYXdJbWFnZShyLDAsMCksY319YXN5bmMgZnVuY3Rpb24gdShlPTMyMCx0PTI0MCxhPSJ0cmFuc3BhcmVudCIpe2NvbnN0IHI9KGF3YWl0IEYoby5ORVdfQ0FOVkFTLHt3aWR0aDplLGhlaWdodDp0fSkpLmNhbnZhcyxjPXIuZ2V0Q29udGV4dCgiMmQiKTtyZXR1cm4gYy5maWxsU3R5bGU9YSxjLmZpbGxSZWN0KDAsMCxlLHQpLHJ9YXN5bmMgZnVuY3Rpb24gVShlLHQpe2NvbnN0IGE9RChlLndpZHRoLGUuaGVpZ2h0LHQud2lkdGgsdC5oZWlnaHQpO3JldHVybiBhd2FpdCBWKHtjYW52YXM6ZSxyZVNhbXBsZTp0LnJlU2FtcGxlLHdpZHRoOmEud2lkdGgsaGVpZ2h0OmEuaGVpZ2h0LGN4OjAsY3k6MCxjdzplLndpZHRoLGNoOmUuaGVpZ2h0LGR4OjAsZHk6MCxkdzphLndpZHRoLGRoOmEuaGVpZ2h0LGJnQ29sb3I6dC5iZ0NvbG9yfSl9ZnVuY3Rpb24gVihlKXtlPU9iamVjdC5hc3NpZ24oe30sVCxlKSxlLnJlU2FtcGxlPU1hdGgubWluKDQsZS5yZVNhbXBsZSksZS5yZVNhbXBsZT1NYXRoLm1heCgwLGUucmVTYW1wbGUpO2NvbnN0IHQ9TWF0aC5wb3coMixlLnJlU2FtcGxlKTtyZXR1cm4gbmV3IFByb21pc2UoYXN5bmMoYSxuKT0+e3RyeXtjb25zdCByPWF3YWl0IHUoZS53aWR0aCp0LGUuaGVpZ2h0KnQsZS5iZ0NvbG9yKTtyLmdldENvbnRleHQoIjJkIikuZHJhd0ltYWdlKGUuY2FudmFzLGUuY3gsZS5jeSxlLmN3LGUuY2gsZS5keCp0LGUuZHkqdCxlLmR3KnQsZS5kaCp0KSxlLnJlU2FtcGxlPjA/TChlLGUucmVTYW1wbGUscikudGhlbihhKTphKHIpfWNhdGNoKHIpe24ocil9fSl9ZnVuY3Rpb24gTChlLHQsYSl7cmV0dXJuIG5ldyBQcm9taXNlKG49Pnthc3luYyBmdW5jdGlvbiByKGMsaSl7Y29uc3Qgcz1NYXRoLnBvdygyLGMpLGw9YXdhaXQgdShlLndpZHRoKnMsZS5oZWlnaHQqcyxlLmJnQ29sb3IpO2wuZ2V0Q29udGV4dCgiMmQiKS5kcmF3SW1hZ2UoaSwwLDAsaS53aWR0aCouNSxpLmhlaWdodCouNSksYz4wP3IoYy0xLGwpLnRoZW4oKTpuKGwpfXIodC0xLGEpLnRoZW4oKX0pfWFzeW5jIGZ1bmN0aW9uIHYoZSx0KXtjb25zdCBhPWlzTmFOKHQ9PW51bGw/dm9pZCAwOnQuc2hhcnBlbik/MDp0LnNoYXJwZW47aWYoYTw9MClyZXR1cm4gZTtjb25zdCBuPWF3YWl0IHUoZS53aWR0aCxlLmhlaWdodCx0LmJnQ29sb3IpO3JldHVybiBPKGUsYSxuKX1hc3luYyBmdW5jdGlvbiBHKGUsdCl7bGV0IGE7c3dpdGNoKHQub3V0cHV0VHlwZSl7Y2FzZSJiYXNlNjQiOmE9YXdhaXQgeihlLHQuZm9ybWF0LHQucXVhbGl0eSkscG9zdE1lc3NhZ2Uoe2tleTpvLkVORCxvdXRwdXQ6YX0pO2JyZWFrO2Nhc2UiYmxvYiI6YT1hd2FpdCBQKGUsdC5mb3JtYXQsdC5xdWFsaXR5KSxwb3N0TWVzc2FnZSh7a2V5Om8uRU5ELG91dHB1dDphfSk7YnJlYWs7Y2FzZSJjYW52YXMiOmRlZmF1bHQ6Y29uc3Qgbj1lLnRyYW5zZmVyVG9JbWFnZUJpdG1hcCgpO3Bvc3RNZXNzYWdlKHtrZXk6by5FTkQsb3V0cHV0Om59LFtuXSk7YnJlYWt9fX0pKCk7Cg==", u = (l) => Uint8Array.from(atob(l), (d) => d.charCodeAt(0)), s = typeof window < "u" && window.Blob && new Blob([u(W)], { type: "text/javascript;charset=utf-8" });
2 | function L(l) {
3 | let d;
4 | try {
5 | if (d = s && (window.URL || window.webkitURL).createObjectURL(s), !d)
6 | throw "";
7 | const e = new Worker(d, {
8 | name: l == null ? void 0 : l.name
9 | });
10 | return e.addEventListener("error", () => {
11 | (window.URL || window.webkitURL).revokeObjectURL(d);
12 | }), e;
13 | } catch {
14 | return new Worker(
15 | "data:text/javascript;base64," + W,
16 | {
17 | name: l == null ? void 0 : l.name
18 | }
19 | );
20 | } finally {
21 | d && (window.URL || window.webkitURL).revokeObjectURL(d);
22 | }
23 | }
24 | const Z = {
25 | START: "START",
26 | END: "END",
27 | NEW_CANVAS: "NEW_CANVAS",
28 | ERROR: "ERROR"
29 | }, G = {
30 | width: 320,
31 | height: 240,
32 | format: "jpg",
33 | // png,jpg,webp
34 | outputType: "base64",
35 | // base64,canvas,blob
36 | quality: 0.75,
37 | reSample: 2,
38 | sharpen: 0,
39 | bgColor: "transparent"
40 | };
41 | function X(l = {}) {
42 | let d = {};
43 | return Object.keys(G).forEach((e) => {
44 | d[e] = l[e] !== void 0 ? l[e] : G[e];
45 | }), l.width === void 0 && l.height === void 0 ? (d.width = G.width, d.height = void 0) : l.width === void 0 ? (d.width = void 0, d.height = Number(d.height) || 0) : l.height === void 0 ? (d.width = Number(d.width) || 0, d.height = void 0) : (d.width = Number(d.width) || 0, d.height = Number(d.height) || 0), d.quality = Number(d.quality) || 0, d.reSample = Number(d.reSample) || 0, d;
46 | }
47 | function S(l = void 0) {
48 | if (typeof l != "string") {
49 | {
50 | if (l instanceof File || l instanceof Blob)
51 | return;
52 | if (l instanceof HTMLCanvasElement)
53 | return;
54 | }
55 | throw new Error("Invalid image data.");
56 | }
57 | }
58 | function K(l) {
59 | return new Promise((d, e) => {
60 | l instanceof HTMLCanvasElement ? l.toBlob((b) => d(b)) : e(new Error("The provided element is not a Canvas."));
61 | });
62 | }
63 | function V(l, d = void 0) {
64 | return new Promise(async (e, b) => {
65 | const Y = X(d), n = new L();
66 | n.onmessage = async (i) => {
67 | const { id: o, key: m, output: t, error: p } = i.data;
68 | switch (m) {
69 | case Z.END:
70 | if (n && n.terminate(), t instanceof ImageBitmap) {
71 | const h = document.createElement("canvas"), c = h.getContext("2d");
72 | h.width = t.width, h.height = t.height, c.fillStyle = Y.bgColor, c.fillRect(0, 0, t.width, t.height), c.drawImage(t, 0, 0), e(h);
73 | } else
74 | e(t);
75 | n && n.terminate();
76 | break;
77 | case Z.ERROR:
78 | b(p), n && n.terminate();
79 | break;
80 | case Z.NEW_CANVAS:
81 | const a = new OffscreenCanvas(i.data.width, i.data.height);
82 | n.postMessage({
83 | id: o,
84 | key: Z.NEW_CANVAS,
85 | canvas: a
86 | }, [a]);
87 | break;
88 | }
89 | }, S(l), l instanceof HTMLCanvasElement && (l = await K(l)), n.postMessage({
90 | key: Z.START,
91 | src: l,
92 | options: Y
93 | });
94 | });
95 | }
96 | export {
97 | V as default
98 | };
99 |
--------------------------------------------------------------------------------
/libs/ImageResize.umd.js:
--------------------------------------------------------------------------------
1 | (function(i,Z){typeof exports=="object"&&typeof module<"u"?module.exports=Z():typeof define=="function"&&define.amd?define(Z):(i=typeof globalThis<"u"?globalThis:i||self,i.imageResize=Z())})(this,function(){"use strict";const i="KGZ1bmN0aW9uKCl7InVzZSBzdHJpY3QiO2NvbnN0IG89e1NUQVJUOiJTVEFSVCIsRU5EOiJFTkQiLE5FV19DQU5WQVM6Ik5FV19DQU5WQVMiLEVSUk9SOiJFUlJPUiJ9LFQ9e2NhbnZhczpudWxsLHJlU2FtcGxlOjIsd2lkdGg6MzIwLGhlaWdodDoyNDAsY3g6MCxjeTowLGN3OjAsY2g6MCxkeDowLGR5OjAsZHc6MCxkaDowLGJnQ29sb3I6InRyYW5zcGFyZW50In07YXN5bmMgZnVuY3Rpb24gRShlKXtyZXR1cm4gYXdhaXQoYXdhaXQgZmV0Y2goZSkpLmJsb2IoKX1mdW5jdGlvbiBrKGUpe3JldHVybiBuZXcgUHJvbWlzZSgodCxhKT0+e2NvbnN0IG49bmV3IEZpbGVSZWFkZXI7bi5vbmxvYWQ9cj0+e3ZhciBjO3JldHVybiB0KChjPXIudGFyZ2V0KT09bnVsbD92b2lkIDA6Yy5yZXN1bHQpfSxuLm9uZXJyb3I9YSxuLnJlYWRBc0RhdGFVUkwoZSl9KX1mdW5jdGlvbiBJKGUpe2NvbnN0IHQ9YXRvYihlLnNwbGl0KCIsIilbMV0pLGE9ZS5tYXRjaCgvW146XVx3K1wvW1x3LStcZC5dKyg/PTt8LCkvKVswXSxuPXQubGVuZ3RoLHI9bmV3IFVpbnQ4QXJyYXkobik7Zm9yKGxldCBjPTA7YzxuO2MrKylyW2NdPXQuY2hhckNvZGVBdChjKTtyZXR1cm4gbmV3IEJsb2IoW3IuYnVmZmVyXSx7dHlwZTphfSl9ZnVuY3Rpb24gRChlLHQsYSxuKXtsZXQgcj1lLGM9dDtyZXR1cm4gYSYmbiYmKGE+bj9uPXZvaWQgMDphPXZvaWQgMCksYT8ocj1hLGM9dCooYS9lKSk6biYmKHI9ZSoobi90KSxjPW4pLHt3aWR0aDpOdW1iZXIociksaGVpZ2h0Ok51bWJlcihjKX19ZnVuY3Rpb24gTyhlLHQ9MCxhKXtpZih0PD0wKXJldHVybiBlO2NvbnN0IG49YS5nZXRDb250ZXh0KCIyZCIpO24uZHJhd0ltYWdlKGUsMCwwKTtjb25zdCByPW4uZ2V0SW1hZ2VEYXRhKDAsMCxhLndpZHRoLGEuaGVpZ2h0KSxjPW4uY3JlYXRlSW1hZ2VEYXRhKGEud2lkdGgsYS5oZWlnaHQpLGk9QihyLGMsWzAsLTEsMCwtMSw1LC0xLDAsLTEsMF0pO24ucHV0SW1hZ2VEYXRhKGksMCwwKTtjb25zdCBzPWUuZ2V0Q29udGV4dCgiMmQiKTtyZXR1cm4gcy5nbG9iYWxBbHBoYT10fHwxLHMuZHJhd0ltYWdlKGEsMCwwKSxlfWZ1bmN0aW9uIEIoZSx0LGEpe2NvbnN0IG49ZS5kYXRhLHI9dC5kYXRhLGM9ZS53aWR0aCxpPWUuaGVpZ2h0LHM9TWF0aC5yb3VuZChNYXRoLnNxcnQoYS5sZW5ndGgpKSxsPU1hdGguZmxvb3Iocy8yKSx5PWMsSz1pO2ZvcihsZXQgZD0wO2Q8SztkKyspZm9yKGxldCBoPTA7aDx5O2grKyl7bGV0IHA9MCxBPTAsTj0wLE09MDtmb3IobGV0IGY9MDtmPHM7ZisrKWZvcihsZXQgZz0wO2c8cztnKyspe2NvbnN0IHg9ZCtmLWwsUz1oK2ctbDtpZih4Pj0wJiZ4PGkmJlM+PTAmJlM8Yyl7bGV0IGI9KHgqYytTKSo0LG09YVtmKnMrZ107cCs9bltiXSptLEErPW5bYisxXSptLE4rPW5bYisyXSptLE0rPW5bYiszXSptfX1jb25zdCB3PShkKnkraCkqNDtyW3ddPXAsclt3KzFdPUEsclt3KzJdPU4sclt3KzNdPU19cmV0dXJuIHR9ZnVuY3Rpb24geihlLHQ9ImltYWdlL2pwZWciLGE9Ljc1KXtyZXR1cm4gdD1SKHQpLG5ldyBQcm9taXNlKGFzeW5jKG4scik9Pnt0cnl7Y29uc3QgYz1hd2FpdCBqKGUsdCxhKTtuKGMpfWNhdGNoKGMpe3IoYyl9fSl9ZnVuY3Rpb24gUChlLHQ9ImltYWdlL2pwZWciLGE9Ljc1KXtyZXR1cm4gdD1SKHQpLG5ldyBQcm9taXNlKGFzeW5jKG4scik9Pnt0cnl7Y29uc3QgYz1hd2FpdCBlLmNvbnZlcnRUb0Jsb2Ioe3R5cGU6dCxxdWFsaXR5OmF9KTtuKGMpfWNhdGNoKGMpe3IoYyl9fSl9ZnVuY3Rpb24gUihlKXtsZXQgdDtzd2l0Y2goZSl7Y2FzZSJqcGciOmNhc2UianBlZyI6dD0iaW1hZ2UvanBlZyI7YnJlYWs7Y2FzZSJwbmciOnQ9ImltYWdlL3BuZyI7YnJlYWs7Y2FzZSJ3ZWJwIjp0PSJpbWFnZS93ZWJwIjticmVhaztkZWZhdWx0OnQ9ZTticmVha31yZXR1cm4gdH1hc3luYyBmdW5jdGlvbiBqKGUsdCxhKXtjb25zdCBuPWF3YWl0IGUuY29udmVydFRvQmxvYih7dHlwZTp0LHF1YWxpdHk6YX0pLHI9bmV3IEZpbGVSZWFkZXI7cmV0dXJuIG5ldyBQcm9taXNlKGM9PntyLm9ubG9hZGVuZD0oKT0+YyhyLnJlc3VsdCksci5yZWFkQXNEYXRhVVJMKG4pfSl9bGV0IF89MDtjb25zdCBDPW5ldyBNYXA7b25tZXNzYWdlPWFzeW5jIGZ1bmN0aW9uKGUpe2NvbnN0e2tleTp0LGlkOmEsc3JjOm4sb3B0aW9uczpyfT1lLmRhdGE7dHJ5e2lmKGEhPT12b2lkIDApe2NvbnN0IGM9Qy5nZXQoYSk7YyYmYyhlLmRhdGEpfWVsc2Ugc3dpdGNoKHQpe2Nhc2Ugby5TVEFSVDpsZXQgYz1hd2FpdCBxKG4scik7Yz1hd2FpdCBVKGMsciksYz1hd2FpdCB2KGMsciksYXdhaXQgRyhjLHIpO2JyZWFrO2Nhc2Ugby5ORVdfQ0FOVkFTOmJyZWFrfX1jYXRjaChjKXtwb3N0TWVzc2FnZSh7a2V5Om8uRVJST1IsZXJyb3I6Y30pfX07ZnVuY3Rpb24gRihlLHQpe3JldHVybiBuZXcgUHJvbWlzZShhPT57Y29uc3Qgbj1fKys7Qy5zZXQobixhKSxwb3N0TWVzc2FnZSh7aWQ6bixrZXk6ZSwuLi50fSl9KX1hc3luYyBmdW5jdGlvbiBxKGUsdCl7aWYodHlwZW9mIGU9PSJzdHJpbmciKXtjb25zdCBhPWF3YWl0IEUoZSksbj1hd2FpdCBjcmVhdGVJbWFnZUJpdG1hcChhKSxyPWF3YWl0IHUobi53aWR0aCxuLmhlaWdodCx0LmJnQ29sb3IpO3JldHVybiByLmdldENvbnRleHQoIjJkIikuZHJhd0ltYWdlKG4sMCwwKSxyfWVsc2UgaWYoZSBpbnN0YW5jZW9mIEZpbGV8fGUgaW5zdGFuY2VvZiBCbG9iKXtjb25zdCBhPWF3YWl0IGsoZSksbj1JKGEpLHI9YXdhaXQgY3JlYXRlSW1hZ2VCaXRtYXAobiksYz1hd2FpdCB1KHIud2lkdGgsci5oZWlnaHQsdC5iZ0NvbG9yKTtyZXR1cm4gYy5nZXRDb250ZXh0KCIyZCIpLmRyYXdJbWFnZShyLDAsMCksY319YXN5bmMgZnVuY3Rpb24gdShlPTMyMCx0PTI0MCxhPSJ0cmFuc3BhcmVudCIpe2NvbnN0IHI9KGF3YWl0IEYoby5ORVdfQ0FOVkFTLHt3aWR0aDplLGhlaWdodDp0fSkpLmNhbnZhcyxjPXIuZ2V0Q29udGV4dCgiMmQiKTtyZXR1cm4gYy5maWxsU3R5bGU9YSxjLmZpbGxSZWN0KDAsMCxlLHQpLHJ9YXN5bmMgZnVuY3Rpb24gVShlLHQpe2NvbnN0IGE9RChlLndpZHRoLGUuaGVpZ2h0LHQud2lkdGgsdC5oZWlnaHQpO3JldHVybiBhd2FpdCBWKHtjYW52YXM6ZSxyZVNhbXBsZTp0LnJlU2FtcGxlLHdpZHRoOmEud2lkdGgsaGVpZ2h0OmEuaGVpZ2h0LGN4OjAsY3k6MCxjdzplLndpZHRoLGNoOmUuaGVpZ2h0LGR4OjAsZHk6MCxkdzphLndpZHRoLGRoOmEuaGVpZ2h0LGJnQ29sb3I6dC5iZ0NvbG9yfSl9ZnVuY3Rpb24gVihlKXtlPU9iamVjdC5hc3NpZ24oe30sVCxlKSxlLnJlU2FtcGxlPU1hdGgubWluKDQsZS5yZVNhbXBsZSksZS5yZVNhbXBsZT1NYXRoLm1heCgwLGUucmVTYW1wbGUpO2NvbnN0IHQ9TWF0aC5wb3coMixlLnJlU2FtcGxlKTtyZXR1cm4gbmV3IFByb21pc2UoYXN5bmMoYSxuKT0+e3RyeXtjb25zdCByPWF3YWl0IHUoZS53aWR0aCp0LGUuaGVpZ2h0KnQsZS5iZ0NvbG9yKTtyLmdldENvbnRleHQoIjJkIikuZHJhd0ltYWdlKGUuY2FudmFzLGUuY3gsZS5jeSxlLmN3LGUuY2gsZS5keCp0LGUuZHkqdCxlLmR3KnQsZS5kaCp0KSxlLnJlU2FtcGxlPjA/TChlLGUucmVTYW1wbGUscikudGhlbihhKTphKHIpfWNhdGNoKHIpe24ocil9fSl9ZnVuY3Rpb24gTChlLHQsYSl7cmV0dXJuIG5ldyBQcm9taXNlKG49Pnthc3luYyBmdW5jdGlvbiByKGMsaSl7Y29uc3Qgcz1NYXRoLnBvdygyLGMpLGw9YXdhaXQgdShlLndpZHRoKnMsZS5oZWlnaHQqcyxlLmJnQ29sb3IpO2wuZ2V0Q29udGV4dCgiMmQiKS5kcmF3SW1hZ2UoaSwwLDAsaS53aWR0aCouNSxpLmhlaWdodCouNSksYz4wP3IoYy0xLGwpLnRoZW4oKTpuKGwpfXIodC0xLGEpLnRoZW4oKX0pfWFzeW5jIGZ1bmN0aW9uIHYoZSx0KXtjb25zdCBhPWlzTmFOKHQ9PW51bGw/dm9pZCAwOnQuc2hhcnBlbik/MDp0LnNoYXJwZW47aWYoYTw9MClyZXR1cm4gZTtjb25zdCBuPWF3YWl0IHUoZS53aWR0aCxlLmhlaWdodCx0LmJnQ29sb3IpO3JldHVybiBPKGUsYSxuKX1hc3luYyBmdW5jdGlvbiBHKGUsdCl7bGV0IGE7c3dpdGNoKHQub3V0cHV0VHlwZSl7Y2FzZSJiYXNlNjQiOmE9YXdhaXQgeihlLHQuZm9ybWF0LHQucXVhbGl0eSkscG9zdE1lc3NhZ2Uoe2tleTpvLkVORCxvdXRwdXQ6YX0pO2JyZWFrO2Nhc2UiYmxvYiI6YT1hd2FpdCBQKGUsdC5mb3JtYXQsdC5xdWFsaXR5KSxwb3N0TWVzc2FnZSh7a2V5Om8uRU5ELG91dHB1dDphfSk7YnJlYWs7Y2FzZSJjYW52YXMiOmRlZmF1bHQ6Y29uc3Qgbj1lLnRyYW5zZmVyVG9JbWFnZUJpdG1hcCgpO3Bvc3RNZXNzYWdlKHtrZXk6by5FTkQsb3V0cHV0Om59LFtuXSk7YnJlYWt9fX0pKCk7Cg==",Z=e=>Uint8Array.from(atob(e),d=>d.charCodeAt(0)),a=typeof window<"u"&&window.Blob&&new Blob([Z(i)],{type:"text/javascript;charset=utf-8"});function u(e){let d;try{if(d=a&&(window.URL||window.webkitURL).createObjectURL(a),!d)throw"";const l=new Worker(d,{name:e==null?void 0:e.name});return l.addEventListener("error",()=>{(window.URL||window.webkitURL).revokeObjectURL(d)}),l}catch{return new Worker("data:text/javascript;base64,"+i,{name:e==null?void 0:e.name})}finally{d&&(window.URL||window.webkitURL).revokeObjectURL(d)}}const h={START:"START",END:"END",NEW_CANVAS:"NEW_CANVAS",ERROR:"ERROR"},c={width:320,height:240,format:"jpg",outputType:"base64",quality:.75,reSample:2,sharpen:0,bgColor:"transparent"};function m(e={}){let d={};return Object.keys(c).forEach(l=>{d[l]=e[l]!==void 0?e[l]:c[l]}),e.width===void 0&&e.height===void 0?(d.width=c.width,d.height=void 0):e.width===void 0?(d.width=void 0,d.height=Number(d.height)||0):e.height===void 0?(d.width=Number(d.width)||0,d.height=void 0):(d.width=Number(d.width)||0,d.height=Number(d.height)||0),d.quality=Number(d.quality)||0,d.reSample=Number(d.reSample)||0,d}function p(e=void 0){if(typeof e!="string"){{if(e instanceof File||e instanceof Blob)return;if(e instanceof HTMLCanvasElement)return}throw new Error("Invalid image data.")}}function L(e){return new Promise((d,l)=>{e instanceof HTMLCanvasElement?e.toBlob(s=>d(s)):l(new Error("The provided element is not a Canvas."))})}function X(e,d=void 0){return new Promise(async(l,s)=>{const o=m(d),n=new u;n.onmessage=async G=>{const{id:S,key:K,output:t,error:V}=G.data;switch(K){case h.END:if(n&&n.terminate(),t instanceof ImageBitmap){const b=document.createElement("canvas"),Y=b.getContext("2d");b.width=t.width,b.height=t.height,Y.fillStyle=o.bgColor,Y.fillRect(0,0,t.width,t.height),Y.drawImage(t,0,0),l(b)}else l(t);n&&n.terminate();break;case h.ERROR:s(V),n&&n.terminate();break;case h.NEW_CANVAS:const W=new OffscreenCanvas(G.data.width,G.data.height);n.postMessage({id:S,key:h.NEW_CANVAS,canvas:W},[W]);break}},p(e),e instanceof HTMLCanvasElement&&(e=await L(e)),n.postMessage({key:h.START,src:e,options:o})})}return X});
2 |
--------------------------------------------------------------------------------
/libs/types.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'image-resize' {
2 |
3 | // types
4 | type typeOptions = {
5 | width?: number
6 | height?: number
7 | format?: 'png'|'jpg'|'webp'
8 | outputType?: 'base64'|'canvas'|'blob'
9 | quality?: number
10 | reSample?: number
11 | sharpen?: number
12 | bgColor?: string
13 | }
14 | type typeSource = string|File|Blob|HTMLCanvasElement
15 | type typeOutput = Promise
16 |
17 | // function
18 | export default function imageResize(src: typeSource, options?: typeOptions): typeOutput
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "image-resize",
3 | "version": "1.4.2",
4 | "description": "Resize image for javascript",
5 | "files": [
6 | "libs"
7 | ],
8 | "main": "libs/imageResize.umd.js",
9 | "module": "libs/imageResize.es.js",
10 | "exports": {
11 | ".": {
12 | "import": "./libs/imageResize.es.js",
13 | "require": "./libs/imageResize.umd.js"
14 | }
15 | },
16 | "type": "module",
17 | "types": "libs/types.d.ts",
18 | "scripts": {
19 | "dev": "bun run --bun vite --config vite.config/vite.docs.js",
20 | "prebuild": "rm -rf ./docs && rm -rf ./libs",
21 | "build": "bun run --bun vite build --emptyOutDir --config vite.config/vite.docs.js && bun run --bun vite build --emptyOutDir --config vite.config/vite.libs.js && cp types.d.ts libs",
22 | "preview": "bun run --bun vite preview --config vite.config/vite.docs.js"
23 | },
24 | "repository": {
25 | "type": "git",
26 | "url": "git+https://github.com/kode-team/image-resize.git"
27 | },
28 | "keywords": [
29 | "image",
30 | "resize",
31 | "resize-image",
32 | "canvas",
33 | "javascript",
34 | "js"
35 | ],
36 | "author": {
37 | "name": "redgoose",
38 | "email": "scripter@me.com",
39 | "url": "https://redgoose.me"
40 | },
41 | "license": "MIT",
42 | "bugs": {
43 | "url": "https://github.com/kode-team/image-resize/issues"
44 | },
45 | "homepage": "https://kode-team.github.io/image-resize/",
46 | "browserslist": [
47 | "> 1%",
48 | "last 2 versions"
49 | ],
50 | "dependencies": {},
51 | "devDependencies": {
52 | "vite": "^5.2.12"
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kode-team/image-resize/44d1a9e79ff966ba5ddf1ac91606d883fc43b3c3/public/favicon.ico
--------------------------------------------------------------------------------
/src/demo/fonts.css:
--------------------------------------------------------------------------------
1 | @font-face{
2 | font-family: Pretendard;
3 | font-weight: 900;
4 | font-display: swap;
5 | src: local('Pretendard Black'), url('./fonts/Pretendard-Black.subset.woff2') format('woff2')
6 | }
7 | @font-face{
8 | font-family: Pretendard;
9 | font-weight: 800;
10 | font-display: swap;
11 | src: local('Pretendard ExtraBold'), url('./fonts/Pretendard-ExtraBold.subset.woff2') format('woff2')
12 | }
13 | @font-face{
14 | font-family: Pretendard;
15 | font-weight: 700;
16 | font-display: swap;
17 | src: local('Pretendard Bold'), url('./fonts/Pretendard-Bold.subset.woff2') format('woff2')
18 | }
19 | @font-face{
20 | font-family: Pretendard;
21 | font-weight: 600;
22 | font-display: swap;
23 | src: local('Pretendard SemiBold'), url('./fonts/Pretendard-SemiBold.subset.woff2') format('woff2')
24 | }
25 | @font-face{
26 | font-family: Pretendard;
27 | font-weight: 500;
28 | font-display: swap;
29 | src: local('Pretendard Medium'), url('./fonts/Pretendard-Medium.subset.woff2') format('woff2')
30 | }
31 | @font-face{
32 | font-family: Pretendard;
33 | font-weight: 400;
34 | font-display: swap;
35 | src: local('Pretendard Regular'), url('./fonts/Pretendard-Regular.subset.woff2') format('woff2')
36 | }
37 | @font-face{
38 | font-family: Pretendard;
39 | font-weight: 300;
40 | font-display: swap;
41 | src: local('Pretendard Light'), url('./fonts/Pretendard-Light.subset.woff2') format('woff2')
42 | }
43 | @font-face{
44 | font-family: Pretendard;
45 | font-weight: 200;
46 | font-display: swap;
47 | src: local('Pretendard ExtraLight'), url('./fonts/Pretendard-ExtraLight.subset.woff2') format('woff2')
48 | }
49 | @font-face{
50 | font-family: Pretendard;
51 | font-weight: 100;
52 | font-display: swap;
53 | src: local('Pretendard Thin'), url('./fonts/Pretendard-Thin.subset.woff2') format('woff2')
54 | }
55 |
--------------------------------------------------------------------------------
/src/demo/fonts/Pretendard-Black.subset.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kode-team/image-resize/44d1a9e79ff966ba5ddf1ac91606d883fc43b3c3/src/demo/fonts/Pretendard-Black.subset.woff2
--------------------------------------------------------------------------------
/src/demo/fonts/Pretendard-Bold.subset.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kode-team/image-resize/44d1a9e79ff966ba5ddf1ac91606d883fc43b3c3/src/demo/fonts/Pretendard-Bold.subset.woff2
--------------------------------------------------------------------------------
/src/demo/fonts/Pretendard-ExtraBold.subset.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kode-team/image-resize/44d1a9e79ff966ba5ddf1ac91606d883fc43b3c3/src/demo/fonts/Pretendard-ExtraBold.subset.woff2
--------------------------------------------------------------------------------
/src/demo/fonts/Pretendard-ExtraLight.subset.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kode-team/image-resize/44d1a9e79ff966ba5ddf1ac91606d883fc43b3c3/src/demo/fonts/Pretendard-ExtraLight.subset.woff2
--------------------------------------------------------------------------------
/src/demo/fonts/Pretendard-Light.subset.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kode-team/image-resize/44d1a9e79ff966ba5ddf1ac91606d883fc43b3c3/src/demo/fonts/Pretendard-Light.subset.woff2
--------------------------------------------------------------------------------
/src/demo/fonts/Pretendard-Medium.subset.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kode-team/image-resize/44d1a9e79ff966ba5ddf1ac91606d883fc43b3c3/src/demo/fonts/Pretendard-Medium.subset.woff2
--------------------------------------------------------------------------------
/src/demo/fonts/Pretendard-Regular.subset.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kode-team/image-resize/44d1a9e79ff966ba5ddf1ac91606d883fc43b3c3/src/demo/fonts/Pretendard-Regular.subset.woff2
--------------------------------------------------------------------------------
/src/demo/fonts/Pretendard-SemiBold.subset.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kode-team/image-resize/44d1a9e79ff966ba5ddf1ac91606d883fc43b3c3/src/demo/fonts/Pretendard-SemiBold.subset.woff2
--------------------------------------------------------------------------------
/src/demo/fonts/Pretendard-Thin.subset.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kode-team/image-resize/44d1a9e79ff966ba5ddf1ac91606d883fc43b3c3/src/demo/fonts/Pretendard-Thin.subset.woff2
--------------------------------------------------------------------------------
/src/demo/index.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --color-base: #111111;
3 | --color-weak: #666666;
4 | --color-main: hsl(159deg 90% 42%);
5 | --color-shape: #f4f4f4;
6 | --color-field-line: hsl(0 0% 0% / 10%);
7 | --size-radius: 3px;
8 | --shadow-box:
9 | 0 1px 5px rgba(0, 0, 0, .061),
10 | 0 3px 18px rgba(0, 0, 0, .089),
11 | 0 12px 80px rgba(0, 0, 0, .15);
12 | }
13 |
14 | html {
15 | touch-action: manipulation;
16 | }
17 | body {
18 | min-width: 640px;
19 | margin: 0;
20 | padding: 0 50px 24px;
21 | -webkit-text-size-adjust: none;
22 | color: var(--color-base);
23 | font-size: 16px;
24 | line-height: 1.6;
25 | }
26 | body, button, input, textarea, select {
27 | font-family: 'Pretendard', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
28 | -webkit-text-size-adjust: 100%;
29 | text-size-adjust: none;
30 | color: var(--color-base);
31 | }
32 |
33 | main {
34 | margin: 0 auto;
35 | max-width: 1000px;
36 | }
37 |
38 | /* header */
39 | .header {
40 | padding: 42px 0 24px;
41 | text-align: center;
42 | user-select: none;
43 | > h1 {
44 | margin: 0;
45 | font-size: 36px;
46 | color: var(--color-main);
47 | letter-spacing: -.5px;
48 | font-weight: 900;
49 | line-height: 1;
50 | text-transform: uppercase;
51 | user-select: none;
52 | }
53 | > p {
54 | margin: 4px 0 0;
55 | font-size: 13px;
56 | line-height: 1.15;
57 | color: var(--color-weak);
58 | }
59 | }
60 |
61 | /* form */
62 | .form {
63 | margin: 0;
64 | padding: 24px;
65 | background: hsl(0 0% 100%);
66 | box-shadow: var(--shadow-box);
67 | border-radius: 4px;
68 | .form__fieldset {
69 | margin: 0;
70 | padding: 0;
71 | border: none;
72 | > legend {
73 | font-size: 0;
74 | }
75 | }
76 | .form__body {
77 | display: grid;
78 | margin: 0;
79 | gap: 0 0;
80 | }
81 | .form__submit {
82 | display: flex;
83 | margin: 20px 0 0;
84 | justify-content: center;
85 | .button {
86 | width: 160px;
87 | height: 36px;
88 | }
89 | }
90 | }
91 |
92 | /* box title */
93 | .box-title {
94 | margin: 0 0 8px;
95 | font-size: 18px;
96 | font-weight: 700;
97 | line-height: 1.05;
98 | letter-spacing: -.5px;
99 | user-select: none;
100 | }
101 |
102 | /* field */
103 | .field {
104 | margin: 0;
105 | padding: 10px 12px;
106 | display: grid;
107 | grid-template-columns: 120px 1fr;
108 | align-items: center;
109 | border-bottom: 1px solid var(--color-field-line);
110 | &:first-child {
111 | border-top: 1px solid var(--color-field-line);
112 | }
113 | .field__label {
114 | margin: 0;
115 | line-height: 1.15;
116 | font-weight: 600;
117 | font-size: 12px;
118 | user-select: none;
119 | }
120 | .field__body {
121 | margin: 0;
122 | }
123 | .field__description {
124 | margin: 5px 0 0;
125 | font-size: 11px;
126 | color: var(--color-weak);
127 | font-weight: 400;
128 | line-height: 1.15;
129 | }
130 | }
131 | .field-inline {
132 | display: grid;
133 | align-items: center;
134 | grid-template-columns: 120px auto;
135 | gap: 0 8px;
136 | > span {
137 | display: block;
138 | font-size: 12px;
139 | line-height: 1;
140 | }
141 | }
142 | .field-inlines {
143 | display: flex;
144 | flex-direction: row;
145 | flex-wrap: wrap;
146 | gap: 16px;
147 | & + .field-inlines {
148 | margin-top: 8px;
149 | }
150 | .field-inline {
151 | grid-template-columns: auto auto;
152 | gap: 8px;
153 | }
154 | }
155 |
156 | /* input form */
157 | .input-text {
158 | display: block;
159 | margin: 0;
160 | padding: 0 10px;
161 | height: 32px;
162 | font-size: 13px;
163 | border-radius: var(--size-radius);
164 | border: 1px solid #bbb;
165 | background: #fff;
166 | outline: none;
167 | box-sizing: border-box;
168 | &.input-text--block {
169 | width: 100%;
170 | }
171 | }
172 | .input-select {
173 | position: relative;
174 | display: inline-block;
175 | > select {
176 | display: inline-block;
177 | margin: 0;
178 | padding: 0 10px;
179 | height: 32px;
180 | border: 1px solid #bbb;
181 | font-size: 13px;
182 | border-radius: var(--size-radius);
183 | background: #fff;
184 | min-width: 100px;
185 | outline: none;
186 | box-sizing: border-box;
187 | appearance: none;
188 | }
189 | &:after {
190 | content: '';
191 | display: block;
192 | position: absolute;
193 | right: 6px;
194 | top: 50%;
195 | width: 16px;
196 | aspect-ratio: 1/1;
197 | translate: 0 -50%;
198 | pointer-events: none;
199 | background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgY2xhc3M9Imx1Y2lkZSBsdWNpZGUtY2hldnJvbi1kb3duIj48cGF0aCBkPSJtNiA5IDYgNiA2LTYiLz48L3N2Zz4=');
200 | background-size: 100%;
201 | }
202 | }
203 | .input-file {
204 | display: grid;
205 | margin: 0;
206 | padding: 0 8px;
207 | height: 32px;
208 | border: 1px solid #bbb;
209 | background: #fff;
210 | font-size: 13px;
211 | border-radius: var(--size-radius);
212 | outline: none;
213 | box-sizing: border-box;
214 | }
215 | .input-file--block {
216 | width: 100%;
217 | }
218 | .input-text:focus-visible,
219 | .input-file:focus-visible,
220 | .input-select:focus-visible {
221 | box-shadow: 0 0 0 2px var(--color-main);
222 | }
223 |
224 | /* result */
225 | .result {
226 | margin: 48px 0 0;
227 | padding: 24px 24px 32px;
228 | background: hsl(0 0% 100%);
229 | box-shadow: var(--shadow-box);
230 | border-radius: 4px;
231 | .box-title {
232 | margin: 0 0 16px;
233 | }
234 | .result__image {
235 | margin: 0;
236 | > * {
237 | display: block;
238 | margin: 0 auto;
239 | max-width: 100%;
240 | }
241 | &:empty:after {
242 | display: grid;
243 | place-content: center;
244 | height: 120px;
245 | content: '...';
246 | background: hsla(0 0% 0% / 4%);
247 | font-size: 13px;
248 | color: var(--color-weak);
249 | }
250 | }
251 | }
252 |
253 | /* button */
254 | .button {
255 | display: block;
256 | margin: 0;
257 | padding: 0;
258 | border: none;
259 | background: var(--color-main);
260 | color: #fff;
261 | font-size: 15px;
262 | font-weight: 800;
263 | letter-spacing: -.5px;
264 | cursor: pointer;
265 | outline: none;
266 | border-radius: var(--size-radius);
267 | transition: box-shadow 200ms ease-out;
268 | user-select: none;
269 | box-sizing: border-box;
270 | &:disabled {
271 | opacity: .5;
272 | cursor: not-allowed;
273 | }
274 | &:where(:active, :focus-visible) {
275 | box-shadow: 0 0 0 4px rgb(0 0 0 / 15%);
276 | }
277 | }
278 |
279 | /* file upload */
280 | .file-upload {
281 | display: flex;
282 | align-items: center;
283 | gap: 0 12px;
284 | button {
285 | width: 100px;
286 | height: 28px;
287 | font-size: 13px;
288 | font-weight: 600;
289 | }
290 | p {
291 | margin: 0;
292 | font-size: 12px;
293 | line-height: 1.15;
294 | font-weight: 700;
295 | user-select: none;
296 | &:empty:before {
297 | content: '업로드한 파일이 없습니다.';
298 | font-weight: 500;
299 | color: var(--color-weak);
300 | }
301 | }
302 | }
303 |
304 | /* footer */
305 | .footer {
306 | margin: 52px 0 0;
307 | text-align: center;
308 | user-select: none;
309 | > .info {
310 | margin: 0;
311 | font-size: 13px;
312 | line-height: 1.05;
313 | font-weight: 600;
314 | em {
315 | font-style: normal;
316 | }
317 | }
318 | > .copyright {
319 | margin: 4px 0 0;
320 | color: var(--color-weak);
321 | font-size: 10px;
322 | line-height: 1.05;
323 | }
324 | }
325 |
326 | /* github link */
327 | .github-corner {
328 | display: block;
329 | position: fixed;
330 | right: 0;
331 | top: 0;
332 | > svg {
333 | position: absolute;
334 | top: 0;
335 | border: 0;
336 | right: 0;
337 | fill:#222222;
338 | color:#fff;
339 | }
340 | .octo-arm {
341 | transform-origin: 130px 106px;
342 | }
343 | &:hover .octo-arm {
344 | animation: octocat-wave 560ms ease-in-out;
345 | }
346 | @media (max-width:500px) {
347 | &:hover .octo-arm {
348 | animation: none;
349 | }
350 | .octo-arm {
351 | animation:octocat-wave 560ms ease-in-out;
352 | }
353 | }
354 |
355 | }
356 | @keyframes octocat-wave {
357 | 0%, 100% {
358 | transform: rotate(0);
359 | }
360 | 20%, 60% {
361 | transform: rotate(-25deg);
362 | }
363 | 40%, 80% {
364 | transform: rotate(10deg);
365 | }
366 | }
367 |
--------------------------------------------------------------------------------
/src/demo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Image Resize demo
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
17 |
160 |
164 |
168 |
169 |
170 |
171 |
176 |
177 |
178 |
179 |
--------------------------------------------------------------------------------
/src/demo/index.js:
--------------------------------------------------------------------------------
1 | import { version } from '../../package.json'
2 | import imageResize from '../image-resize/index.js'
3 | import { fileUploader, getByte } from './libs.js'
4 | import './fonts.css'
5 | import './index.css'
6 |
7 | const $form = document.getElementById('form')
8 | const $result = document.getElementById('result')
9 | const values = new Proxy({}, {
10 | get: (obj, name) => (obj[name]),
11 | set: (obj, prop, value) => {
12 | switch (prop)
13 | {
14 | case 'width':
15 | case 'height':
16 | case 'quality':
17 | case 'reSample':
18 | case 'sharpen':
19 | value = Number(value)
20 | break
21 | }
22 | obj[prop] = value
23 | return true
24 | },
25 | })
26 | const $el = {
27 | submit: document.querySelector('.form__submit > button[type=submit]'),
28 | fileUpload: document.getElementById('file-upload'),
29 | fileUploadInfo: document.getElementById('file-upload-info'),
30 | version: document.getElementById('version'),
31 | }
32 |
33 | async function onClickFileUpload()
34 | {
35 | const file = await fileUploader({ accept: 'image/*' })
36 | if (!file) return
37 | values.upload = file
38 | $el.fileUploadInfo.innerText = `${file.name} (${getByte(file.size)})`
39 | }
40 |
41 | async function onSubmitForm(e)
42 | {
43 | e.preventDefault()
44 | processing(true)
45 |
46 | // set values
47 | const $self = e.target
48 | const $fields = $self.querySelectorAll('[name]')
49 | for (const $item of $fields)
50 | {
51 | values[$item.name] = $item.value
52 | }
53 | // set source
54 | let src
55 | if (values.upload)
56 | {
57 | src = values.upload
58 | // src = new Blob([src], { type: src.type }) // set Blob
59 | }
60 | else if (values.url)
61 | {
62 | src = values.url
63 | }
64 | else
65 | {
66 | alert('not found source')
67 | return false
68 | }
69 |
70 | try
71 | {
72 | // set values
73 | const pureValues = { ...values }
74 | delete pureValues.url
75 | delete pureValues.upload
76 | // method: function
77 | const res = await imageResize(src, pureValues)
78 | // to result output image
79 | completeResizeImage(pureValues.outputType, res)
80 | }
81 | catch(e)
82 | {
83 | errorResizeImage(e)
84 | }
85 | finally
86 | {
87 | processing(false)
88 | }
89 | }
90 |
91 | function processing(sw)
92 | {
93 | const $submit = $el.submit
94 | if (sw)
95 | {
96 | // on
97 | $submit.setAttribute('disabled', '')
98 | $submit.innerText = '처리중..'
99 | }
100 | else
101 | {
102 | // off
103 | $submit.removeAttribute('disabled')
104 | $submit.innerText = '이미지 변환'
105 | }
106 | }
107 |
108 | function completeResizeImage(outputType, res)
109 | {
110 | $result.innerHTML = ''
111 | switch(outputType)
112 | {
113 | case 'base64':
114 | const image = new Image()
115 | image.src = res
116 | $result.appendChild(image)
117 | break
118 | case 'canvas':
119 | $result.appendChild(res)
120 | break
121 | case 'blob':
122 | default:
123 | console.log('RESULT:', res)
124 | break
125 | }
126 | }
127 | function errorResizeImage(e)
128 | {
129 | console.error('[ERROR / IMAGE RESIZE]', e.message)
130 | alert(`Error resize: ${e.message}`)
131 | }
132 |
133 | // action
134 | $form.addEventListener('submit', onSubmitForm)
135 | $el.fileUpload.addEventListener('click', onClickFileUpload)
136 | $el.version.innerText = version
137 |
--------------------------------------------------------------------------------
/src/demo/libs.js:
--------------------------------------------------------------------------------
1 | /**
2 | * sleep (delay tool)
3 | * @param {number} delay
4 | * @return {Promise}
5 | */
6 | export const sleep = (delay = 3000) => {
7 | return new Promise(resolve => setTimeout(resolve, delay))
8 | }
9 |
10 | /**
11 | * file uploader
12 | * @param {string} [options.accept]
13 | * @param {boolean} [options.multiple]
14 | * @return {Promise}
15 | */
16 | export function fileUploader(options = {})
17 | {
18 | return new Promise((resolve, reject) => {
19 | const input = document.createElement('input')
20 | input.type = 'file'
21 | if (options.accept) input.accept = options.accept
22 | if (options.multiple === true) input.multiple = true
23 | input.addEventListener('change', e => {
24 | const files = Object.assign([], e.target.files)
25 | if (files.length <= 0) return resolve([])
26 | input.value = null
27 | resolve((options.multiple === true) ? files : files[0])
28 | })
29 | input.addEventListener('cancel', () => {
30 | resolve((options.multiple === true) ? [] : null)
31 | })
32 | input.click()
33 | })
34 | }
35 |
36 | /**
37 | * file loader
38 | * @param {string} url
39 | * @return {Blob}
40 | */
41 | export async function fileLoader(url)
42 | {
43 | const res = await fetch(url)
44 | const blob = await res.blob()
45 | return blob
46 | }
47 |
48 | /**
49 | * blob to canvas
50 | * @param {Blob} blob
51 | * @return {Promise}
52 | */
53 | export function blobToCanvas(blob)
54 | {
55 | return new Promise(resolve => {
56 | let img = new Image()
57 | img.onload = () => {
58 | const canvas = document.createElement('canvas')
59 | canvas.width = img.width
60 | canvas.height = img.height
61 | const ctx = canvas.getContext('2d')
62 | ctx.drawImage(img, 0, 0, img.width, img.height)
63 | resolve(canvas)
64 | }
65 | img.src = URL.createObjectURL(blob)
66 | })
67 | }
68 |
69 | export function getByte(bytes)
70 | {
71 | const sizes = [ 'Bytes', 'KB', 'MB', 'GB', 'TB' ]
72 | if (bytes === 0) return '0 Byte'
73 | let i = Math.floor(Math.log(bytes) / Math.log(1024))
74 | return String(Math.round(bytes / Math.pow(1024, i))) + sizes[i]
75 | }
76 |
--------------------------------------------------------------------------------
/src/image-resize/index.js:
--------------------------------------------------------------------------------
1 | import ImageResizeWorker from './worker.js?worker&inline'
2 | import { WORKER_MESSAGE } from './libs/consts.js'
3 | import { checkOptions, checkSource } from './libs/checks.js'
4 | import { canvasToBlob } from './libs/files.js'
5 |
6 | /**
7 | * resize
8 | * 한번에 리사이즈 실행하는 함수
9 | * @param {string|File|Blob|HTMLCanvasElement} src
10 | * @param {object} options
11 | * @param {number} [options.width]
12 | * @param {number} [options.height]
13 | * @param {'png'|'jpg'|'webp'} [options.format]
14 | * @param {'base64'|'canvas'|'blob'} [options.outputType]
15 | * @param {number} [options.quality]
16 | * @param {number} [options.reSample]
17 | * @param {number} [options.sharpen]
18 | * @param {string} [options.bgColor]
19 | * @return {Promise}
20 | */
21 | function imageResize(src, options = undefined)
22 | {
23 | return new Promise(async (resolve, reject) => {
24 | // check options
25 | const opts = checkOptions(options)
26 |
27 | // set worker
28 | const worker = new ImageResizeWorker()
29 | // const worker = new Worker(new URL('./worker.js', import.meta.url), { type: 'module' })
30 | worker.onmessage = async (e) => {
31 | const { id, key, output, error } = e.data
32 | switch (key)
33 | {
34 | case WORKER_MESSAGE.END:
35 | if (worker) worker.terminate()
36 | if (output instanceof ImageBitmap)
37 | {
38 | const canvas = document.createElement('canvas')
39 | const ctx = canvas.getContext('2d')
40 | canvas.width = output.width
41 | canvas.height = output.height
42 | ctx.fillStyle = opts.bgColor
43 | ctx.fillRect(0, 0, output.width, output.height)
44 | ctx.drawImage(output, 0, 0)
45 | resolve(canvas)
46 | }
47 | else
48 | {
49 | resolve(output)
50 | }
51 | if (worker) worker.terminate()
52 | break
53 | case WORKER_MESSAGE.ERROR:
54 | reject(error)
55 | if (worker) worker.terminate()
56 | break
57 | case WORKER_MESSAGE.NEW_CANVAS:
58 | const canvas = new OffscreenCanvas(e.data.width, e.data.height)
59 | worker.postMessage({
60 | id,
61 | key: WORKER_MESSAGE.NEW_CANVAS,
62 | canvas,
63 | }, [ canvas ])
64 | break
65 | }
66 | }
67 |
68 | // check source
69 | checkSource(src)
70 |
71 | // 캔버스 엘리먼트라면 `Blob`로 변환해줘야한다.
72 | if (src instanceof HTMLCanvasElement)
73 | {
74 | src = await canvasToBlob(src)
75 | }
76 |
77 | // send to worker thread
78 | worker.postMessage({
79 | key: WORKER_MESSAGE.START,
80 | src,
81 | options: opts,
82 | })
83 | })
84 | }
85 |
86 | export default imageResize
87 |
--------------------------------------------------------------------------------
/src/image-resize/libs/checks.js:
--------------------------------------------------------------------------------
1 | import { defaultOptions } from './consts.js'
2 |
3 | /**
4 | * Check options
5 | * @param {object} target
6 | */
7 | export function checkOptions(target={})
8 | {
9 | let result = {}
10 | Object.keys(defaultOptions).forEach((key) => {
11 | result[key] = target[key] !== undefined ? target[key] : defaultOptions[key]
12 | })
13 | if (target.width === undefined && target.height === undefined)
14 | {
15 | result.width = defaultOptions.width
16 | result.height = undefined
17 | }
18 | else if (target.width === undefined)
19 | {
20 | result.width = undefined
21 | result.height = Number(result.height) || 0
22 | }
23 | else if (target.height === undefined)
24 | {
25 | result.width = Number(result.width) || 0
26 | result.height = undefined
27 | }
28 | else
29 | {
30 | result.width = Number(result.width) || 0
31 | result.height = Number(result.height) || 0
32 | }
33 | result.quality = Number(result.quality) || 0
34 | result.reSample = Number(result.reSample) || 0
35 | return result
36 | }
37 |
38 | /**
39 | * check source
40 | * @param {string|File|Blob|HTMLCanvasElement} src
41 | * @throws
42 | */
43 | export function checkSource(src = undefined)
44 | {
45 | if (typeof src === 'string') return
46 | else if (src instanceof File || src instanceof Blob) return
47 | else if (src instanceof HTMLCanvasElement) return
48 | throw new Error('Invalid image data.')
49 | }
50 |
--------------------------------------------------------------------------------
/src/image-resize/libs/consts.js:
--------------------------------------------------------------------------------
1 | // 웹 워커 통신용 메시지
2 | export const WORKER_MESSAGE = {
3 | START: 'START',
4 | END: 'END',
5 | NEW_CANVAS: 'NEW_CANVAS',
6 | ERROR: 'ERROR',
7 | }
8 |
9 | // 이미지 라시아즈 옵션
10 | export const defaultOptions = {
11 | width: 320,
12 | height: 240,
13 | format: 'jpg', // png,jpg,webp
14 | outputType: 'base64', // base64,canvas,blob
15 | quality: .75,
16 | reSample: 2,
17 | sharpen: 0,
18 | bgColor: 'transparent',
19 | }
20 |
21 | // default options for resize canvas
22 | export const defaultResizeCanvasOptions = {
23 | canvas: null,
24 | reSample: 2,
25 | width: 320,
26 | height: 240,
27 | cx: 0,
28 | cy: 0,
29 | cw: 0,
30 | ch: 0,
31 | dx: 0,
32 | dy: 0,
33 | dw: 0,
34 | dh: 0,
35 | bgColor: 'transparent',
36 | }
37 |
--------------------------------------------------------------------------------
/src/image-resize/libs/files.js:
--------------------------------------------------------------------------------
1 | /**
2 | * fetch image data
3 | * url 주소로 이미지를 가져온다.
4 | * @param {string} url
5 | * @return {Promise}
6 | */
7 | export async function fetchImageData(url)
8 | {
9 | let image = await fetch(url)
10 | return await image.blob()
11 | }
12 |
13 | /**
14 | * file reader
15 | * @param {Blob|File} file
16 | * @return {Promise}
17 | */
18 | export function fileReader(file)
19 | {
20 | return new Promise((resolve, reject) => {
21 | const reader = new FileReader()
22 | reader.onload = e => resolve(e.target?.result)
23 | reader.onerror = reject
24 | reader.readAsDataURL(file)
25 | })
26 | }
27 |
28 | /**
29 | * base64 to Blob
30 | * @param {string} base64
31 | * @return {Blob}
32 | */
33 | export function base64ToBlob(base64)
34 | {
35 | const binaryString = atob(base64.split(',')[1])
36 | const mimeType = base64.match(/[^:]\w+\/[\w-+\d.]+(?=;|,)/)[0]
37 | const len = binaryString.length
38 | const bytes = new Uint8Array(len)
39 | for (let i = 0; i < len; i++) {
40 | bytes[i] = binaryString.charCodeAt(i);
41 | }
42 | return new Blob([ bytes.buffer ], { type: mimeType })
43 | }
44 |
45 | /**
46 | * canvas to blob
47 | * @param {HTMLCanvasElement} canvas
48 | * @return {Promise}
49 | */
50 | export function canvasToBlob(canvas)
51 | {
52 | return new Promise((resolve, reject) => {
53 | if (canvas instanceof HTMLCanvasElement)
54 | {
55 | canvas.toBlob(blob => resolve(blob))
56 | }
57 | else
58 | {
59 | reject(new Error('The provided element is not a Canvas.'))
60 | }
61 |
62 | })
63 | }
64 |
65 |
--------------------------------------------------------------------------------
/src/image-resize/libs/filter-sharpen.js:
--------------------------------------------------------------------------------
1 | /**
2 | * filter sharpen
3 | * @param {OffscreenCanvas} canvas
4 | * @param {Number} amount
5 | * @param {OffscreenCanvas} newCanvas
6 | * @return {any}
7 | */
8 | export default function filterSharpen(canvas, amount = 0, newCanvas)
9 | {
10 | if (amount <= 0) return canvas
11 | const newContext = newCanvas.getContext('2d')
12 | newContext.drawImage(canvas, 0, 0)
13 | const sourceImageData = newContext.getImageData(0, 0, newCanvas.width, newCanvas.height)
14 | const blankOutputImageData = newContext.createImageData(newCanvas.width, newCanvas.height)
15 | const filteredData = convolution(sourceImageData, blankOutputImageData, [
16 | 0, -1, 0,
17 | -1, 5, -1,
18 | 0, -1, 0,
19 | ])
20 | newContext.putImageData(filteredData, 0, 0)
21 | // merge sharpen image
22 | const context = canvas.getContext('2d')
23 | context.globalAlpha = amount || 1
24 | context.drawImage(newCanvas, 0, 0)
25 | // return
26 | return canvas
27 | }
28 |
29 | /**
30 | * convolution
31 | * https://img.ly/blog/how-to-apply-filters-in-javascript/
32 | * @param {ImageData} sourceImageData
33 | * @param {ImageData} outputImageData
34 | * @param {Array} kernel
35 | */
36 | function convolution(sourceImageData, outputImageData, kernel)
37 | {
38 | const src = sourceImageData.data
39 | const dst = outputImageData.data
40 | const srcWidth = sourceImageData.width
41 | const srcHeight = sourceImageData.height
42 | const side = Math.round(Math.sqrt(kernel.length))
43 | const halfSide = Math.floor(side / 2)
44 | // padding the output by the convolution kernel
45 | const w = srcWidth
46 | const h = srcHeight
47 | // iterating through the output image pixels
48 | for (let y = 0; y < h; y++)
49 | {
50 | for (let x = 0; x < w; x++)
51 | {
52 | let r = 0, g = 0, b = 0, a = 0
53 | // calculating the weighed sum of the source image pixels that
54 | // fall under the convolution kernel
55 | for (let cy = 0; cy < side; cy++)
56 | {
57 | for (let cx = 0; cx < side; cx++)
58 | {
59 | const scy = y + cy - halfSide
60 | const scx = x + cx - halfSide
61 | if (scy >= 0 && scy < srcHeight && scx >= 0 && scx < srcWidth)
62 | {
63 | let srcOffset = (scy * srcWidth + scx) * 4
64 | let wt = kernel[cy * side + cx]
65 | r += src[srcOffset] * wt
66 | g += src[srcOffset + 1] * wt
67 | b += src[srcOffset + 2] * wt
68 | a += src[srcOffset + 3] * wt
69 | }
70 | }
71 | }
72 | const dstOffset = (y * w + x) * 4
73 | dst[dstOffset] = r
74 | dst[dstOffset + 1] = g
75 | dst[dstOffset + 2] = b
76 | dst[dstOffset + 3] = a
77 | }
78 | }
79 | return outputImageData
80 | }
81 |
--------------------------------------------------------------------------------
/src/image-resize/libs/numbers.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Get image size
3 | * @param {number} width original width
4 | * @param {number} height original height
5 | * @param {number} targetWidth target width
6 | * @param {number} targetHeight target height
7 | * @return {object}
8 | */
9 | export function getImageSize(width, height, targetWidth, targetHeight)
10 | {
11 | let w = width
12 | let h = height
13 | if (targetWidth && targetHeight)
14 | {
15 | if (targetWidth > targetHeight) targetHeight = undefined
16 | else targetWidth = undefined
17 | }
18 | if (targetWidth)
19 | {
20 | w = targetWidth
21 | h = height * (targetWidth / width)
22 | }
23 | else if (targetHeight)
24 | {
25 | w = width * (targetHeight / height)
26 | h = targetHeight
27 | }
28 | return {
29 | width: Number(w),
30 | height: Number(h),
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/image-resize/libs/output.js:
--------------------------------------------------------------------------------
1 | /**
2 | * type - base64
3 | * @param {OffscreenCanvas} canvas
4 | * @param {string} format
5 | * @param {number} quality
6 | * @return {Promise}
7 | */
8 | export function base64(canvas, format='image/jpeg', quality=.75)
9 | {
10 | // set format
11 | format = getFormat(format)
12 | return new Promise(async (resolve, reject) => {
13 | try
14 | {
15 | const uri = await canvasToDataURL(canvas, format, quality)
16 | resolve(uri)
17 | }
18 | catch (e)
19 | {
20 | reject(e)
21 | }
22 | })
23 | }
24 |
25 | /**
26 | * type - blob
27 | * @param {OffscreenCanvas} canvas
28 | * @param {string} format
29 | * @param {number} quality
30 | * @return {Promise}
31 | */
32 | export function blob(canvas, format='image/jpeg', quality=.75)
33 | {
34 | // set format
35 | format = getFormat(format)
36 | return new Promise(async (resolve, reject) => {
37 | try
38 | {
39 | const blob = await canvas.convertToBlob({
40 | type: format,
41 | quality,
42 | })
43 | resolve(blob)
44 | }
45 | catch (e)
46 | {
47 | reject(e)
48 | }
49 | })
50 | }
51 |
52 | /**
53 | * Get format
54 | * @param {string} str
55 | * @return {string}
56 | */
57 | function getFormat(str)
58 | {
59 | let format
60 | switch(str)
61 | {
62 | case 'jpg':
63 | case 'jpeg':
64 | format = 'image/jpeg'
65 | break
66 | case 'png':
67 | format = 'image/png'
68 | break
69 | case 'webp':
70 | format = 'image/webp'
71 | break
72 | default:
73 | format = str
74 | break
75 | }
76 | return format
77 | }
78 |
79 | /**
80 | * OffScreenCanvas 객체를 toDataURL 기능을 사용한다.
81 | * @param {OffscreenCanvas} canvas
82 | * @param {string} format
83 | * @param {number} quality
84 | * @return {any}
85 | */
86 | async function canvasToDataURL(canvas, format, quality)
87 | {
88 | const blob = await canvas.convertToBlob({
89 | type: format,
90 | quality,
91 | })
92 | const reader = new FileReader()
93 | return new Promise(resolve => {
94 | reader.onloadend = () => resolve(reader.result)
95 | reader.readAsDataURL(blob)
96 | })
97 | }
98 |
--------------------------------------------------------------------------------
/src/image-resize/worker.js:
--------------------------------------------------------------------------------
1 | import { WORKER_MESSAGE, defaultResizeCanvasOptions } from './libs/consts.js'
2 | import { fileReader, base64ToBlob, fetchImageData } from './libs/files.js'
3 | import { getImageSize } from './libs/numbers.js'
4 | import filterSharpen from './libs/filter-sharpen.js'
5 | import { base64, blob } from './libs/output.js'
6 |
7 | let messageId = 0
8 | const pendingRequests = new Map()
9 |
10 | // worker events
11 | onmessage = async function(e)
12 | {
13 | const { key, id, src, options } = e.data
14 | try
15 | {
16 | if (id !== undefined)
17 | {
18 | const resolve = pendingRequests.get(id)
19 | if (resolve) resolve(e.data)
20 | }
21 | else
22 | {
23 | switch (key)
24 | {
25 | case WORKER_MESSAGE.START:
26 | // 캔버스로 변환한다.
27 | let canvas = await convertSourceToCanvas(src, options)
28 | canvas = await resize(canvas, options)
29 | canvas = await sharpen(canvas, options)
30 | await output(canvas, options)
31 | break
32 | case WORKER_MESSAGE.NEW_CANVAS:
33 | break
34 | }
35 | }
36 | }
37 | catch (e)
38 | {
39 | postMessage({
40 | key: WORKER_MESSAGE.ERROR,
41 | error: e,
42 | })
43 | }
44 | }
45 |
46 | /**
47 | * 메인 쓰레드에 메시지를 비동기 방식으로 보낸다. 이 함수를 활용하면 await을 이용할 수 있게된다.
48 | * @param {string} key 통신 메시지를 구분하는 값
49 | * @param {any} params
50 | */
51 | function sendMessageToMainThread(key, params)
52 | {
53 | return new Promise(resolve => {
54 | const id = messageId++
55 | pendingRequests.set(id, resolve)
56 | postMessage({ id, key, ...params })
57 | })
58 | }
59 |
60 | /**
61 | * 소스 데이터를 캔버스로 변환한다.
62 | * @return
63 | */
64 | async function convertSourceToCanvas(src, options)
65 | {
66 | if (typeof src === 'string')
67 | {
68 | // image url address
69 | const blob = await fetchImageData(src)
70 | const bitmap = await createImageBitmap(blob)
71 | const canvas = await createCanvas(bitmap.width, bitmap.height, options.bgColor)
72 | const ctx = canvas.getContext('2d')
73 | ctx.drawImage(bitmap, 0, 0)
74 | return canvas
75 | }
76 | else if (src instanceof File || src instanceof Blob)
77 | {
78 | // File,Blob
79 | const base64 = await fileReader(src)
80 | const blob = base64ToBlob(base64)
81 | const bitmap = await createImageBitmap(blob)
82 | const canvas = await createCanvas(bitmap.width, bitmap.height, options.bgColor)
83 | const ctx = canvas.getContext('2d')
84 | ctx.drawImage(bitmap, 0, 0)
85 | return canvas
86 | }
87 | }
88 |
89 | /**
90 | * 메인쓰레드에 요청하여 캔버스 엘리먼트를 만든다.
91 | * @return {OffscreenCanvas}
92 | */
93 | export async function createCanvas(width = 320, height = 240, bgColor = 'transparent')
94 | {
95 | const res = await sendMessageToMainThread(WORKER_MESSAGE.NEW_CANVAS, {
96 | width,
97 | height,
98 | })
99 | const canvas = res.canvas
100 | const ctx = canvas.getContext('2d')
101 | ctx.fillStyle = bgColor
102 | ctx.fillRect(0, 0, width, height)
103 | return canvas
104 | }
105 |
106 | /**
107 | * 캔버스를 리사이즈한다.
108 | * @param {OffscreenCanvas} canvas
109 | * @param {object} options
110 | * @return {OffscreenCanvas}
111 | */
112 | async function resize(canvas, options)
113 | {
114 | const size = getImageSize(canvas.width, canvas.height, options.width, options.height)
115 | const result = await resizeCanvas({
116 | canvas,
117 | reSample: options.reSample,
118 | width: size.width,
119 | height: size.height,
120 | cx: 0,
121 | cy: 0,
122 | cw: canvas.width,
123 | ch: canvas.height,
124 | dx: 0,
125 | dy: 0,
126 | dw: size.width,
127 | dh: size.height,
128 | bgColor: options.bgColor,
129 | })
130 | return result
131 | }
132 |
133 | /**
134 | * resize canvas
135 | * @param {Object} options
136 | * @return {Promise}
137 | */
138 | export function resizeCanvas(options)
139 | {
140 | // assign options
141 | options = Object.assign({}, defaultResizeCanvasOptions, options)
142 | // set resampling count
143 | options.reSample = Math.min(4, options.reSample)
144 | options.reSample = Math.max(0, options.reSample)
145 | const reSamplingCount = Math.pow(2, options.reSample)
146 | return new Promise(async (resolve, reject) => {
147 | try
148 | {
149 | const canvas = await createCanvas(
150 | options.width * reSamplingCount,
151 | options.height * reSamplingCount,
152 | options.bgColor
153 | )
154 | const ctx = canvas.getContext('2d')
155 | ctx.drawImage(
156 | options.canvas,
157 | options.cx,
158 | options.cy,
159 | options.cw,
160 | options.ch,
161 | options.dx * reSamplingCount,
162 | options.dy * reSamplingCount,
163 | options.dw * reSamplingCount,
164 | options.dh * reSamplingCount
165 | )
166 | if (options.reSample > 0)
167 | {
168 | _resizeCanvas(options, options.reSample, canvas).then(resolve)
169 | }
170 | else
171 | {
172 | resolve(canvas)
173 | }
174 | }
175 | catch (e)
176 | {
177 | reject(e)
178 | }
179 | })
180 | }
181 | /**
182 | * resize
183 | * @param {Object} options
184 | * @param {Number} count
185 | * @param {OffscreenCanvas} parentCanvas
186 | * @return {Promise}
187 | */
188 | function _resizeCanvas(options, count, parentCanvas)
189 | {
190 | return new Promise(resolve => {
191 | async function func(count, parentCanvas)
192 | {
193 | const pow = Math.pow(2, count)
194 | const canvasForResize = await createCanvas(
195 | options.width * pow,
196 | options.height * pow,
197 | options.bgColor
198 | )
199 | const ctx = canvasForResize.getContext('2d')
200 | ctx.drawImage(
201 | parentCanvas,
202 | 0,
203 | 0,
204 | parentCanvas.width * 0.5,
205 | parentCanvas.height * 0.5,
206 | )
207 | if (count > 0)
208 | {
209 | func(count - 1, canvasForResize).then()
210 | }
211 | else
212 | {
213 | resolve(canvasForResize)
214 | }
215 | }
216 | func(count - 1, parentCanvas).then()
217 | })
218 | }
219 |
220 | /**
221 | * sharpen
222 | * @param {OffscreenCanvas} canvas
223 | * @param {object} options
224 | */
225 | async function sharpen(canvas, options)
226 | {
227 | const amount = (!isNaN(options?.sharpen)) ? options.sharpen : 0
228 | if (amount <= 0) return canvas
229 | const newCanvas = await createCanvas(canvas.width, canvas.height, options.bgColor)
230 | return filterSharpen(canvas, amount, newCanvas)
231 | }
232 |
233 | /**
234 | * Output data
235 | * @param {OffscreenCanvas} canvas
236 | * @param {object} options
237 | * @return {Promise}
238 | */
239 | async function output(canvas, options)
240 | {
241 | let res
242 | switch (options.outputType)
243 | {
244 | case 'base64':
245 | res = await base64(canvas, options.format, options.quality)
246 | postMessage({
247 | key: WORKER_MESSAGE.END,
248 | output: res,
249 | })
250 | break
251 | case 'blob':
252 | res = await blob(canvas, options.format, options.quality)
253 | postMessage({
254 | key: WORKER_MESSAGE.END,
255 | output: res,
256 | })
257 | break
258 | case 'canvas':
259 | default:
260 | const bitmap = canvas.transferToImageBitmap()
261 | postMessage({
262 | key: WORKER_MESSAGE.END,
263 | output: bitmap,
264 | }, [ bitmap ])
265 | break
266 | }
267 | }
268 |
--------------------------------------------------------------------------------
/types.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'image-resize' {
2 |
3 | // types
4 | type typeOptions = {
5 | width?: number
6 | height?: number
7 | format?: 'png'|'jpg'|'webp'
8 | outputType?: 'base64'|'canvas'|'blob'
9 | quality?: number
10 | reSample?: number
11 | sharpen?: number
12 | bgColor?: string
13 | }
14 | type typeSource = string|File|Blob|HTMLCanvasElement
15 | type typeOutput = Promise
16 |
17 | // function
18 | export default function imageResize(src: typeSource, options?: typeOptions): typeOutput
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/vite.config/vite.docs.js:
--------------------------------------------------------------------------------
1 | import { defineConfig, loadEnv } from 'vite'
2 |
3 | // docs: https://vitejs.dev/config
4 |
5 | const config = defineConfig(({ mode }) => {
6 | const path = process.cwd()
7 | const env = loadEnv(mode, path)
8 | return {
9 | root: './src/demo',
10 | base: './',
11 | publicDir: path + '/public',
12 | server: {
13 | host: env.VITE_HOST,
14 | port: Number(env.VITE_PORT),
15 | open: env.VITE_OPEN_BROWSER === 'true',
16 | },
17 | build: {
18 | outDir: path + '/docs',
19 | assetsDir: '',
20 | },
21 | preview: {
22 | host: env.VITE_HOST,
23 | port: Number(env.VITE_PORT),
24 | },
25 | }
26 | })
27 |
28 | export default config
29 |
--------------------------------------------------------------------------------
/vite.config/vite.libs.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 |
3 | const projectName = 'imageResize'
4 | const config = defineConfig(() => {
5 | return {
6 | publicDir: false,
7 | base: './',
8 | build: {
9 | minify: true,
10 | outDir: 'libs',
11 | assetsDir: '',
12 | lib: {
13 | entry: 'src/image-resize/index.js',
14 | name: projectName,
15 | formats: [ 'es', 'umd' ],
16 | fileName: (format, a, b) => {
17 | return `${projectName}.${format}.js`
18 | },
19 | },
20 | },
21 | }
22 | })
23 |
24 | export default config
25 |
--------------------------------------------------------------------------------