cc-gram 1.1.0 → 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2019 - present 汪東陽 EastSun5566
3
+ Copyright (c) 2019 - present Michael Wang
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,22 +1,27 @@
1
- # CCgram
1
+ <img src="./logo.webp" alt="CCgram Logo" width="180">
2
+
3
+ # 🖼 CCgram
2
4
 
3
5
  [![NPM Version](https://img.shields.io/npm/v/cc-gram.svg?style=for-the-badge)](https://www.npmjs.com/package/cc-gram)
4
6
  [![NPM Downloads](https://img.shields.io/npm/dt/cc-gram.svg?style=for-the-badge)](https://www.npmjs.com/package/cc-gram)
7
+ [![JSR Version](https://img.shields.io/jsr/v/%40eastsun5566/cc-gram?style=for-the-badge)](https://jsr.io/@eastsun5566/cc-gram)
5
8
  [![Test Status](https://img.shields.io/github/actions/workflow/status/EastSun5566/cc-gram/test.yml?style=for-the-badge)](https://github.com/EastSun5566/cc-gram/actions/workflows/test.yml)
6
- [![License](https://img.shields.io/github/license/EastSun5566/cc-gram.svg?style=for-the-badge)](https://github.com/EastSun5566/cc-gram/blob/master/LICENSE)
9
+ [![License](https://img.shields.io/github/license/EastSun5566/cc-gram.svg?style=for-the-badge)](https://github.com/EastSun5566/cc-gram/blob/main/LICENSE)
10
+
11
+ [<img src="https://cdn.buymeacoffee.com/buttons/v2/default-blue.png" alt="Buy Me A Coffee" height="40">](https://www.buymeacoffee.com/eastsun5566)
7
12
 
8
- > 🖼 A CSS & Canvas Instagram filters based on CSSgram
13
+ > A CSS & Canvas Instagram filter inspired by CSSgram
9
14
 
10
15
  🔗 <https://eastsun5566.github.io/cc-gram/>
11
16
 
12
17
  ![Demo GIF](./demo.gif)
13
18
 
14
- ## 🤔 The Why
19
+ ## 🤔 Why
15
20
 
16
- > [CSSgram](https://github.com/una/CSSgram) is a great CSS filters library, but sometimes you want to access/download the filter image. Then CCgram comes into the play. It uses pure CSS to preview filters and draw it with Canvas when you need it.
21
+ > [CSSgram](https://github.com/una/CSSgram) is an excellent CSS filter library. However, there are instances where you might need to access or download the image with filter. This is where CCgram comes into play. It enables you to preview filter using pure CSS and draw them with Canvas whenever you need to.
17
22
 
18
- - On-Demand: Uses CSS to preview & draw with Canvas API on demand.
19
- - Non-Blocking: Draw an image on Web Worker with `OffscreenCanvas` & `ImageBitmap`.
23
+ - On-Demand: Utilizes CSS for previewing and draws with the Canvas API as needed
24
+ - Non-Blocking: Images are drawn on a Web Worker using `OffscreenCanvas` & `ImageBitmap`
20
25
 
21
26
  ## ✨ Installation
22
27
 
@@ -142,7 +147,7 @@ filter.removeFilter("my-filter");
142
147
 
143
148
  ---
144
149
 
145
- ### Access Filter image
150
+ ### Access image with filter
146
151
 
147
152
  ```js
148
153
  const image = document.querySelector('img[data-filter="1977"]');
@@ -170,7 +175,21 @@ const blob = await filter.getBlob(image, {
170
175
  - Options
171
176
 
172
177
  - type: `string` - MIME types, defaults to `image/png`,
173
- - quality: `number`- [0 - 1], defaults to `0.92`
178
+ - quality: `number` - [0 - 1], defaults to `0.92`
179
+ - filter: `string` - Override filter name, defaults to reading from data attribute
180
+
181
+ ##### Override filter
182
+
183
+ You can override the filter applied to the image by passing a `filter` option:
184
+
185
+ ```js
186
+ // Image has data-filter="1977"
187
+ const image = document.querySelector('img[data-filter="1977"]');
188
+
189
+ // Apply a different filter when getting the image data
190
+ const dataUrl = await filter.getDataURL(image, { filter: "inkwell" });
191
+ const blob = await filter.getBlob(image, { filter: "valencia" });
192
+ ```
174
193
 
175
194
  ## 🔧 Development
176
195
 
package/dist/core.d.ts CHANGED
@@ -9,6 +9,8 @@ export declare class CCgram {
9
9
  protected readonly _filters: Map<string, FilterSetting>;
10
10
  /** data attribute */
11
11
  protected _dataAttribute: string;
12
+ /** data attribute key in camelCase for dataset access */
13
+ protected _dataAttributeKey: string;
12
14
  /** Initialize CSS filter to all targets */
13
15
  constructor({ dataAttribute, init, }?: Options);
14
16
  /** The filter name list */
package/dist/index.d.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * @license MIT
5
5
  * @copyright (c) 2019 - present
6
- * @author 汪東陽 EastSun5566 <https://github.com/EastSun5566>
6
+ * @author Michael Wang
7
7
  */
8
8
  export { Filter as default } from './core';
9
9
  export * from './filters';
package/dist/index.esm.js CHANGED
@@ -1 +1 @@
1
- function t(t,e,r,n){return new(r||(r=Promise))((function(a,s){function i(t){try{c(n.next(t))}catch(t){s(t)}}function o(t){try{c(n.throw(t))}catch(t){s(t)}}function c(t){var e;t.done?a(t.value):(e=t.value,e instanceof r?e:new r((function(t){t(e)}))).then(i,o)}c((n=n.apply(t,e||[])).next())}))}const e=new Map([["aden",{"hue-rotate":-20,contrast:.9,brightness:1.2,saturate:.85}],["inkwell",{sepia:.3,contrast:1.1,brightness:1.1,grayscale:1}],["reyes",{sepia:.22,contrast:.85,brightness:1.1,saturate:.75}],["gingham",{"hue-rotate":-10,brightness:1.05}],["toaster",{contrast:1.5,brightness:.9}],["walden",{"hue-rotate":-10,brightness:1.1,sepia:.3,saturate:1.6}],["hudson",{brightness:1.2,contrast:.9,saturate:1.1}],["earlybird",{contrast:.9,sepia:.2}],["mayfair",{contrast:1.1,saturate:1.1}],["lofi",{contrast:1.5,saturate:1.1}],["1977",{contrast:1.1,brightness:1.1,saturate:1.3}],["brooklyn",{contrast:.9,brightness:1.1}],["xpro2",{sepia:.3}],["nashville",{contrast:1.2,brightness:1.05,saturate:1.2,sepia:.2}],["lark",{contrast:.9}],["moon",{brightness:1.1,contrast:1.1,grayscale:1}],["clarendon",{contrast:1.2,saturate:1.35}],["willow",{contrast:.95,brightness:.9,grayscale:.5}],["rise",{contrast:.9,brightness:1.05,sepia:.2,saturate:.9}],["slumber",{brightness:1.05,saturate:.66}],["brannan",{contrast:1.4,sepia:.5}],["valencia",{contrast:1.08,brightness:1.08,sepia:.08}],["maven",{contrast:.95,brightness:1.95,saturate:1.5,sepia:.25}],["stinson",{contrast:.75,brightness:1.15,saturate:.85}],["amaro",{"hue-rotate":-10,contrast:.9,brightness:1.1,saturate:1.5}]]),r="undefined"!=typeof OffscreenCanvas;function n(t,e="internal error."){if(!t)throw Error(`[CCgram] ${e}`)}function a(t){n(t&&"IMG"===t.tagName,"The first argument is required and must be an <img> element."),n(t.src,"The <img> element src attribute is empty.")}function s({data:t}){const{canvas:e,image:r,filterStyle:n,options:a}=t,s=e.getContext("2d",{alpha:!1});if(!s)throw new Error("The 2d context canvas is not supported.");return s.filter=n,s.drawImage(r,0,0),e.convertToBlob(a)}const i="filter";class o{constructor({dataAttribute:t="filter",init:r=!0}={}){if(this._filters=e,this._dataAttribute=t,!r)return;if("complete"===document.readyState)return void this.applyFilter();const n=()=>{this.applyFilter(),document.removeEventListener("DOMContentLoaded",n)};document.addEventListener("DOMContentLoaded",n)}get filterNames(){return[...this._filters.keys()]}setFilter(t,e){this._filters.set(t,e)}removeFilter(t){return this._filters.delete(t)}getFilterSetting(t=""){return this._filters.get(t)}getFilterStyle(t=""){return function(t){return t?Object.keys(t).map((e=>`${e}(${t[e]}${"hue-rotate"===e?"deg":"blur"===e?"px":""})`)).join(" "):"none"}(this._filters.get(t))}applyFilter(t=`img[data-${this._dataAttribute}]`){document.querySelectorAll(t).forEach((t=>{const{dataset:e}=t;t.style.setProperty("filter",this.getFilterStyle(e[this._dataAttribute]))}))}getDataURL(e,r={}){return t(this,void 0,void 0,(function*(){a(e);const t=yield this.getBlob(e,r);if(!t)return null;const n=new FileReader;return new Promise((e=>{n.addEventListener("load",(()=>{e(n.result)})),n.readAsDataURL(t)}))}))}getBlob(e,n={}){return t(this,void 0,void 0,(function*(){a(e);const{naturalWidth:t,naturalHeight:i}=e,o=this.getFilterStyle(e.dataset[this._dataAttribute]);if(r){const r=new OffscreenCanvas(t,i),a=yield createImageBitmap(e);return new Promise((t=>{const e=function(t){const e=`\n const work = ${t.toString()};\n\n addEventListener('message', async (...params) => {\n const res = await work(...params);\n postMessage(res);\n });\n `,r=URL.createObjectURL(new Blob([e],{type:"text/javascript"})),n=new Worker(r),{terminate:a}=n;return n.terminate=()=>{URL.revokeObjectURL(r),a.call(n)},n}(s);e.addEventListener("message",(({data:r})=>{t(r),e.terminate()})),e.postMessage({canvas:r,image:a,filterStyle:o,options:n},[r,a])}))}const c=document.createElement("canvas");c.width=t,c.height=i;const l=c.getContext("2d",{alpha:!1});if(!l)throw new Error("The 2d context canvas is not supported.");l.filter=o,l.drawImage(e,0,0);const{type:u,quality:h}=n;return new Promise((t=>c.toBlob((e=>t(e)),u,h)))}))}}o.DEFAULT_DATA_ATTRIBUTE="filter",o.DEFAULT_FILTERS=e;const c=o,l=o;function u(t){return new l(t)}export{c as CCGram,o as CCgram,i as DEFAULT_DATA_ATTRIBUTE,e as DEFAULT_FILTERS,l as Filter,u as createFilter,l as default};
1
+ function t(t,e,r,n){return new(r||(r=Promise))((function(a,s){function i(t){try{c(n.next(t))}catch(t){s(t)}}function o(t){try{c(n.throw(t))}catch(t){s(t)}}function c(t){var e;t.done?a(t.value):(e=t.value,e instanceof r?e:new r((function(t){t(e)}))).then(i,o)}c((n=n.apply(t,e||[])).next())}))}const e=new Map([["aden",{"hue-rotate":-20,contrast:.9,brightness:1.2,saturate:.85}],["inkwell",{sepia:.3,contrast:1.1,brightness:1.1,grayscale:1}],["reyes",{sepia:.22,contrast:.85,brightness:1.1,saturate:.75}],["gingham",{"hue-rotate":-10,brightness:1.05}],["toaster",{contrast:1.5,brightness:.9}],["walden",{"hue-rotate":-10,brightness:1.1,sepia:.3,saturate:1.6}],["hudson",{brightness:1.2,contrast:.9,saturate:1.1}],["earlybird",{contrast:.9,sepia:.2}],["mayfair",{contrast:1.1,saturate:1.1}],["lofi",{contrast:1.5,saturate:1.1}],["1977",{contrast:1.1,brightness:1.1,saturate:1.3}],["brooklyn",{contrast:.9,brightness:1.1}],["xpro2",{sepia:.3}],["nashville",{contrast:1.2,brightness:1.05,saturate:1.2,sepia:.2}],["lark",{contrast:.9}],["moon",{brightness:1.1,contrast:1.1,grayscale:1}],["clarendon",{contrast:1.2,saturate:1.35}],["willow",{contrast:.95,brightness:.9,grayscale:.5}],["rise",{contrast:.9,brightness:1.05,sepia:.2,saturate:.9}],["slumber",{brightness:1.05,saturate:.66}],["brannan",{contrast:1.4,sepia:.5}],["valencia",{contrast:1.08,brightness:1.08,sepia:.08}],["maven",{contrast:.95,brightness:1.95,saturate:1.5,sepia:.25}],["stinson",{contrast:.75,brightness:1.15,saturate:.85}],["amaro",{"hue-rotate":-10,contrast:.9,brightness:1.1,saturate:1.5}]]),r="undefined"!=typeof OffscreenCanvas;function n(t,e="internal error."){if(!t)throw Error(`[CCgram] ${e}`)}function a(t){n(t&&"IMG"===t.tagName,"The first argument is required and must be an <img> element."),n(t.src,"The <img> element src attribute is empty.")}function s({data:t}){const{canvas:e,image:r,filterStyle:n,options:a}=t,s=e.getContext("2d",{alpha:!1});if(!s)throw new Error("The 2d context canvas is not supported.");return s.filter=n,s.drawImage(r,0,0),e.convertToBlob(a)}const i="filter";class o{constructor({dataAttribute:t="filter",init:r=!0}={}){if(this._filters=e,this._dataAttribute=t,this._dataAttributeKey=t.replace(/-./g,(t=>t[1]?t[1].toUpperCase():"")),!r)return;if("complete"===document.readyState)return void this.applyFilter();const n=()=>{this.applyFilter(),document.removeEventListener("DOMContentLoaded",n)};document.addEventListener("DOMContentLoaded",n)}get filterNames(){return[...this._filters.keys()]}setFilter(t,e){this._filters.set(t,e)}removeFilter(t){return this._filters.delete(t)}getFilterSetting(t=""){return this._filters.get(t)}getFilterStyle(t=""){return function(t){return t?Object.keys(t).map((e=>`${e}(${t[e]}${"hue-rotate"===e?"deg":"blur"===e?"px":""})`)).join(" "):"none"}(this._filters.get(t))}applyFilter(t=`img[data-${this._dataAttribute}]`){document.querySelectorAll(t).forEach((t=>{const{dataset:e}=t;t.style.setProperty("filter",this.getFilterStyle(e[this._dataAttributeKey]))}))}getDataURL(e,r={}){return t(this,void 0,void 0,(function*(){a(e);const t=yield this.getBlob(e,r);if(!t)return null;const n=new FileReader;return new Promise((e=>{n.addEventListener("load",(()=>{e(n.result)})),n.readAsDataURL(t)}))}))}getBlob(e,n={}){var i;return t(this,void 0,void 0,(function*(){a(e);const{naturalWidth:t,naturalHeight:o}=e,c=null!==(i=n.filter)&&void 0!==i?i:e.dataset[this._dataAttributeKey],l=this.getFilterStyle(c);if(r){const r=new OffscreenCanvas(t,o),a=yield createImageBitmap(e);return new Promise((t=>{const e=function(t){const e=`\n const work = ${t.toString()};\n\n addEventListener('message', async (...params) => {\n const res = await work(...params);\n postMessage(res);\n });\n `,r=URL.createObjectURL(new Blob([e],{type:"text/javascript"})),n=new Worker(r),{terminate:a}=n;return n.terminate=()=>{URL.revokeObjectURL(r),a.call(n)},n}(s);e.addEventListener("message",(({data:r})=>{t(r),e.terminate()})),e.postMessage({canvas:r,image:a,filterStyle:l,options:n},[r,a])}))}const u=document.createElement("canvas");u.width=t,u.height=o;const d=u.getContext("2d",{alpha:!1});if(!d)throw new Error("The 2d context canvas is not supported.");d.filter=l,d.drawImage(e,0,0);const{type:h,quality:g}=n;return new Promise((t=>u.toBlob((e=>t(e)),h,g)))}))}}o.DEFAULT_DATA_ATTRIBUTE="filter",o.DEFAULT_FILTERS=e;const c=o,l=o;function u(t){return new l(t)}export{c as CCGram,o as CCgram,i as DEFAULT_DATA_ATTRIBUTE,e as DEFAULT_FILTERS,l as Filter,u as createFilter,l as default};
package/dist/index.umd.js CHANGED
@@ -1 +1 @@
1
- !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).CCGram={})}(this,(function(t){"use strict";function e(t,e,r,n){return new(r||(r=Promise))((function(a,s){function i(t){try{c(n.next(t))}catch(t){s(t)}}function o(t){try{c(n.throw(t))}catch(t){s(t)}}function c(t){var e;t.done?a(t.value):(e=t.value,e instanceof r?e:new r((function(t){t(e)}))).then(i,o)}c((n=n.apply(t,e||[])).next())}))}const r=new Map([["aden",{"hue-rotate":-20,contrast:.9,brightness:1.2,saturate:.85}],["inkwell",{sepia:.3,contrast:1.1,brightness:1.1,grayscale:1}],["reyes",{sepia:.22,contrast:.85,brightness:1.1,saturate:.75}],["gingham",{"hue-rotate":-10,brightness:1.05}],["toaster",{contrast:1.5,brightness:.9}],["walden",{"hue-rotate":-10,brightness:1.1,sepia:.3,saturate:1.6}],["hudson",{brightness:1.2,contrast:.9,saturate:1.1}],["earlybird",{contrast:.9,sepia:.2}],["mayfair",{contrast:1.1,saturate:1.1}],["lofi",{contrast:1.5,saturate:1.1}],["1977",{contrast:1.1,brightness:1.1,saturate:1.3}],["brooklyn",{contrast:.9,brightness:1.1}],["xpro2",{sepia:.3}],["nashville",{contrast:1.2,brightness:1.05,saturate:1.2,sepia:.2}],["lark",{contrast:.9}],["moon",{brightness:1.1,contrast:1.1,grayscale:1}],["clarendon",{contrast:1.2,saturate:1.35}],["willow",{contrast:.95,brightness:.9,grayscale:.5}],["rise",{contrast:.9,brightness:1.05,sepia:.2,saturate:.9}],["slumber",{brightness:1.05,saturate:.66}],["brannan",{contrast:1.4,sepia:.5}],["valencia",{contrast:1.08,brightness:1.08,sepia:.08}],["maven",{contrast:.95,brightness:1.95,saturate:1.5,sepia:.25}],["stinson",{contrast:.75,brightness:1.15,saturate:.85}],["amaro",{"hue-rotate":-10,contrast:.9,brightness:1.1,saturate:1.5}]]),n="undefined"!=typeof OffscreenCanvas;function a(t,e="internal error."){if(!t)throw Error(`[CCgram] ${e}`)}function s(t){a(t&&"IMG"===t.tagName,"The first argument is required and must be an <img> element."),a(t.src,"The <img> element src attribute is empty.")}function i({data:t}){const{canvas:e,image:r,filterStyle:n,options:a}=t,s=e.getContext("2d",{alpha:!1});if(!s)throw new Error("The 2d context canvas is not supported.");return s.filter=n,s.drawImage(r,0,0),e.convertToBlob(a)}const o="filter";class c{constructor({dataAttribute:t="filter",init:e=!0}={}){if(this._filters=r,this._dataAttribute=t,!e)return;if("complete"===document.readyState)return void this.applyFilter();const n=()=>{this.applyFilter(),document.removeEventListener("DOMContentLoaded",n)};document.addEventListener("DOMContentLoaded",n)}get filterNames(){return[...this._filters.keys()]}setFilter(t,e){this._filters.set(t,e)}removeFilter(t){return this._filters.delete(t)}getFilterSetting(t=""){return this._filters.get(t)}getFilterStyle(t=""){return function(t){return t?Object.keys(t).map((e=>`${e}(${t[e]}${"hue-rotate"===e?"deg":"blur"===e?"px":""})`)).join(" "):"none"}(this._filters.get(t))}applyFilter(t=`img[data-${this._dataAttribute}]`){document.querySelectorAll(t).forEach((t=>{const{dataset:e}=t;t.style.setProperty("filter",this.getFilterStyle(e[this._dataAttribute]))}))}getDataURL(t,r={}){return e(this,void 0,void 0,(function*(){s(t);const e=yield this.getBlob(t,r);if(!e)return null;const n=new FileReader;return new Promise((t=>{n.addEventListener("load",(()=>{t(n.result)})),n.readAsDataURL(e)}))}))}getBlob(t,r={}){return e(this,void 0,void 0,(function*(){s(t);const{naturalWidth:e,naturalHeight:a}=t,o=this.getFilterStyle(t.dataset[this._dataAttribute]);if(n){const n=new OffscreenCanvas(e,a),s=yield createImageBitmap(t);return new Promise((t=>{const e=function(t){const e=`\n const work = ${t.toString()};\n\n addEventListener('message', async (...params) => {\n const res = await work(...params);\n postMessage(res);\n });\n `,r=URL.createObjectURL(new Blob([e],{type:"text/javascript"})),n=new Worker(r),{terminate:a}=n;return n.terminate=()=>{URL.revokeObjectURL(r),a.call(n)},n}(i);e.addEventListener("message",(({data:r})=>{t(r),e.terminate()})),e.postMessage({canvas:n,image:s,filterStyle:o,options:r},[n,s])}))}const c=document.createElement("canvas");c.width=e,c.height=a;const l=c.getContext("2d",{alpha:!1});if(!l)throw new Error("The 2d context canvas is not supported.");l.filter=o,l.drawImage(t,0,0);const{type:u,quality:d}=r;return new Promise((t=>c.toBlob((e=>t(e)),u,d)))}))}}c.DEFAULT_DATA_ATTRIBUTE=o,c.DEFAULT_FILTERS=r;const l=c,u=c;t.CCGram=l,t.CCgram=c,t.DEFAULT_DATA_ATTRIBUTE=o,t.DEFAULT_FILTERS=r,t.Filter=u,t.createFilter=function(t){return new u(t)},t.default=u,Object.defineProperty(t,"__esModule",{value:!0})}));
1
+ !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).CCGram={})}(this,(function(t){"use strict";function e(t,e,r,n){return new(r||(r=Promise))((function(a,s){function i(t){try{c(n.next(t))}catch(t){s(t)}}function o(t){try{c(n.throw(t))}catch(t){s(t)}}function c(t){var e;t.done?a(t.value):(e=t.value,e instanceof r?e:new r((function(t){t(e)}))).then(i,o)}c((n=n.apply(t,e||[])).next())}))}const r=new Map([["aden",{"hue-rotate":-20,contrast:.9,brightness:1.2,saturate:.85}],["inkwell",{sepia:.3,contrast:1.1,brightness:1.1,grayscale:1}],["reyes",{sepia:.22,contrast:.85,brightness:1.1,saturate:.75}],["gingham",{"hue-rotate":-10,brightness:1.05}],["toaster",{contrast:1.5,brightness:.9}],["walden",{"hue-rotate":-10,brightness:1.1,sepia:.3,saturate:1.6}],["hudson",{brightness:1.2,contrast:.9,saturate:1.1}],["earlybird",{contrast:.9,sepia:.2}],["mayfair",{contrast:1.1,saturate:1.1}],["lofi",{contrast:1.5,saturate:1.1}],["1977",{contrast:1.1,brightness:1.1,saturate:1.3}],["brooklyn",{contrast:.9,brightness:1.1}],["xpro2",{sepia:.3}],["nashville",{contrast:1.2,brightness:1.05,saturate:1.2,sepia:.2}],["lark",{contrast:.9}],["moon",{brightness:1.1,contrast:1.1,grayscale:1}],["clarendon",{contrast:1.2,saturate:1.35}],["willow",{contrast:.95,brightness:.9,grayscale:.5}],["rise",{contrast:.9,brightness:1.05,sepia:.2,saturate:.9}],["slumber",{brightness:1.05,saturate:.66}],["brannan",{contrast:1.4,sepia:.5}],["valencia",{contrast:1.08,brightness:1.08,sepia:.08}],["maven",{contrast:.95,brightness:1.95,saturate:1.5,sepia:.25}],["stinson",{contrast:.75,brightness:1.15,saturate:.85}],["amaro",{"hue-rotate":-10,contrast:.9,brightness:1.1,saturate:1.5}]]),n="undefined"!=typeof OffscreenCanvas;function a(t,e="internal error."){if(!t)throw Error(`[CCgram] ${e}`)}function s(t){a(t&&"IMG"===t.tagName,"The first argument is required and must be an <img> element."),a(t.src,"The <img> element src attribute is empty.")}function i({data:t}){const{canvas:e,image:r,filterStyle:n,options:a}=t,s=e.getContext("2d",{alpha:!1});if(!s)throw new Error("The 2d context canvas is not supported.");return s.filter=n,s.drawImage(r,0,0),e.convertToBlob(a)}const o="filter";class c{constructor({dataAttribute:t="filter",init:e=!0}={}){if(this._filters=r,this._dataAttribute=t,this._dataAttributeKey=t.replace(/-./g,(t=>t[1]?t[1].toUpperCase():"")),!e)return;if("complete"===document.readyState)return void this.applyFilter();const n=()=>{this.applyFilter(),document.removeEventListener("DOMContentLoaded",n)};document.addEventListener("DOMContentLoaded",n)}get filterNames(){return[...this._filters.keys()]}setFilter(t,e){this._filters.set(t,e)}removeFilter(t){return this._filters.delete(t)}getFilterSetting(t=""){return this._filters.get(t)}getFilterStyle(t=""){return function(t){return t?Object.keys(t).map((e=>`${e}(${t[e]}${"hue-rotate"===e?"deg":"blur"===e?"px":""})`)).join(" "):"none"}(this._filters.get(t))}applyFilter(t=`img[data-${this._dataAttribute}]`){document.querySelectorAll(t).forEach((t=>{const{dataset:e}=t;t.style.setProperty("filter",this.getFilterStyle(e[this._dataAttributeKey]))}))}getDataURL(t,r={}){return e(this,void 0,void 0,(function*(){s(t);const e=yield this.getBlob(t,r);if(!e)return null;const n=new FileReader;return new Promise((t=>{n.addEventListener("load",(()=>{t(n.result)})),n.readAsDataURL(e)}))}))}getBlob(t,r={}){var a;return e(this,void 0,void 0,(function*(){s(t);const{naturalWidth:e,naturalHeight:o}=t,c=null!==(a=r.filter)&&void 0!==a?a:t.dataset[this._dataAttributeKey],l=this.getFilterStyle(c);if(n){const n=new OffscreenCanvas(e,o),a=yield createImageBitmap(t);return new Promise((t=>{const e=function(t){const e=`\n const work = ${t.toString()};\n\n addEventListener('message', async (...params) => {\n const res = await work(...params);\n postMessage(res);\n });\n `,r=URL.createObjectURL(new Blob([e],{type:"text/javascript"})),n=new Worker(r),{terminate:a}=n;return n.terminate=()=>{URL.revokeObjectURL(r),a.call(n)},n}(i);e.addEventListener("message",(({data:r})=>{t(r),e.terminate()})),e.postMessage({canvas:n,image:a,filterStyle:l,options:r},[n,a])}))}const u=document.createElement("canvas");u.width=e,u.height=o;const d=u.getContext("2d",{alpha:!1});if(!d)throw new Error("The 2d context canvas is not supported.");d.filter=l,d.drawImage(t,0,0);const{type:h,quality:f}=r;return new Promise((t=>u.toBlob((e=>t(e)),h,f)))}))}}c.DEFAULT_DATA_ATTRIBUTE=o,c.DEFAULT_FILTERS=r;const l=c,u=c;t.CCGram=l,t.CCgram=c,t.DEFAULT_DATA_ATTRIBUTE=o,t.DEFAULT_FILTERS=r,t.Filter=u,t.createFilter=function(t){return new u(t)},t.default=u,Object.defineProperty(t,"__esModule",{value:!0})}));
package/dist/types.d.ts CHANGED
@@ -11,4 +11,6 @@ export interface ParseOptions {
11
11
  type?: string;
12
12
  /** [0 - 1], defaults to `0.92` */
13
13
  quality?: number;
14
+ /** Override filter name, defaults to reading from data attribute */
15
+ filter?: string;
14
16
  }
package/dist/utils.d.ts CHANGED
@@ -2,6 +2,7 @@
2
2
  import { FilterSetting } from './filters';
3
3
  import { ParseOptions } from './types';
4
4
  export declare const hasOffscreenCanvas: boolean;
5
+ export declare function camelize(string: string): string;
5
6
  export declare function assert<TCond = unknown>(condition: TCond, message?: string): asserts condition;
6
7
  export declare function assertIsImage(image: HTMLImageElement): asserts image is HTMLImageElement;
7
8
  export declare function createWorker<TData = unknown, TMessage = unknown>(fn: (messageEvent: MessageEvent<TData>) => TMessage): Worker;
package/package.json CHANGED
@@ -1,8 +1,12 @@
1
1
  {
2
2
  "name": "cc-gram",
3
- "version": "1.1.0",
3
+ "version": "1.2.3",
4
4
  "description": "🖼 A CSS & Canvas Instagram filters based on CSSgram",
5
5
  "type": "module",
6
+ "engines": {
7
+ "node": "^18.0.0 || >=20.0.0"
8
+ },
9
+ "packageManager": "pnpm@9.1.0",
6
10
  "main": "dist/index.umd.js",
7
11
  "module": "dist/index.esm.js",
8
12
  "types": "dist/index.d.ts",
@@ -14,6 +18,16 @@
14
18
  "dist",
15
19
  "src"
16
20
  ],
21
+ "scripts": {
22
+ "build": "rimraf dist && rollup -c rollup.config.ts --configPlugin typescript",
23
+ "type-check": "tsc --noEmit",
24
+ "test": "vitest __tests__",
25
+ "test:coverage": "vitest run __tests__ --coverage",
26
+ "lint": "eslint --fix --ignore-path .gitignore --ext .js,.ts src",
27
+ "prepublishOnly": "pnpm build",
28
+ "release": "sh scripts/release.sh",
29
+ "demo:dev": "pnpm -C demo dev"
30
+ },
17
31
  "keywords": [
18
32
  "image",
19
33
  "filter",
@@ -24,7 +38,7 @@
24
38
  "instagram",
25
39
  "cssgram"
26
40
  ],
27
- "author": "汪東陽 EastSun5566",
41
+ "author": "Michael Wang 汪東陽 <michael19920327@gmail.com> (https://github.com/EastSun5566)",
28
42
  "license": "MIT",
29
43
  "repository": {
30
44
  "type": "git",
@@ -59,14 +73,5 @@
59
73
  "tslib": "^2.4.1",
60
74
  "typescript": "^4.8.4",
61
75
  "vitest": "^0.25.6"
62
- },
63
- "scripts": {
64
- "build": "rimraf dist && rollup -c rollup.config.ts --configPlugin typescript",
65
- "type-check": "tsc --noEmit",
66
- "test": "vitest __tests__",
67
- "test:coverage": "vitest run __tests__ --coverage",
68
- "lint": "eslint --fix --ignore-path .gitignore --ext .js,.ts src",
69
- "release": "sh scripts/release.sh",
70
- "demo:dev": "pnpm -C demo dev"
71
76
  }
72
- }
77
+ }
package/src/core.ts CHANGED
@@ -9,6 +9,7 @@ import {
9
9
  assertIsImage,
10
10
  createWorker,
11
11
  createBlobWorker,
12
+ camelize,
12
13
  } from './utils';
13
14
 
14
15
  import { Options, ParseOptions } from './types';
@@ -27,12 +28,16 @@ export class CCgram {
27
28
  /** data attribute */
28
29
  protected _dataAttribute: string;
29
30
 
31
+ /** data attribute key in camelCase for dataset access */
32
+ protected _dataAttributeKey: string;
33
+
30
34
  /** Initialize CSS filter to all targets */
31
35
  constructor({
32
36
  dataAttribute = DEFAULT_DATA_ATTRIBUTE,
33
37
  init = true,
34
38
  }: Options = {}) {
35
39
  this._dataAttribute = dataAttribute;
40
+ this._dataAttributeKey = camelize(dataAttribute);
36
41
 
37
42
  if (!init) return;
38
43
 
@@ -92,12 +97,13 @@ export class CCgram {
92
97
  * Apply CSS filter to all targets
93
98
  * @param [selectors='img[data-${this._dataAttribute}]'] - selectors
94
99
  */
95
- applyFilter(selectors = `img[data-${this._dataAttribute}]`): void {
100
+ // eslint-disable-next-line @typescript-eslint/no-inferrable-types
101
+ applyFilter(selectors: string = `img[data-${this._dataAttribute}]`): void {
96
102
  document
97
103
  .querySelectorAll<HTMLImageElement>(selectors)
98
104
  .forEach((target): void => {
99
105
  const { dataset } = target;
100
- target.style.setProperty('filter', this.getFilterStyle(dataset[this._dataAttribute]));
106
+ target.style.setProperty('filter', this.getFilterStyle(dataset[this._dataAttributeKey]));
101
107
  });
102
108
  }
103
109
 
@@ -139,7 +145,8 @@ export class CCgram {
139
145
  assertIsImage(image);
140
146
 
141
147
  const { naturalWidth, naturalHeight } = image;
142
- const filterStyle = this.getFilterStyle(image.dataset[this._dataAttribute]);
148
+ const filterName = options.filter ?? image.dataset[this._dataAttributeKey];
149
+ const filterStyle = this.getFilterStyle(filterName);
143
150
 
144
151
  if (hasOffscreenCanvas) {
145
152
  const canvas = new OffscreenCanvas(naturalWidth, naturalHeight);
package/src/index.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * @license MIT
5
5
  * @copyright (c) 2019 - present
6
- * @author 汪東陽 EastSun5566 <https://github.com/EastSun5566>
6
+ * @author Michael Wang
7
7
  */
8
8
 
9
9
  export { Filter as default } from './core';
package/src/types.ts CHANGED
@@ -12,4 +12,6 @@ export interface ParseOptions {
12
12
  type?: string;
13
13
  /** [0 - 1], defaults to `0.92` */
14
14
  quality?: number;
15
+ /** Override filter name, defaults to reading from data attribute */
16
+ filter?: string;
15
17
  }
package/src/utils.ts CHANGED
@@ -3,6 +3,10 @@ import { ParseOptions } from './types';
3
3
 
4
4
  export const hasOffscreenCanvas = typeof OffscreenCanvas !== 'undefined';
5
5
 
6
+ export function camelize(string: string): string {
7
+ return string.replace(/-./g, (c) => (c[1] ? c[1].toUpperCase() : ''));
8
+ }
9
+
6
10
  export function assert<TCond = unknown>(condition: TCond, message = 'internal error.'): asserts condition {
7
11
  if (!condition) throw Error(`[CCgram] ${message}`);
8
12
  }