cc-gram 0.2.1 → 0.2.2-beta.0

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/README.md CHANGED
@@ -1,15 +1,27 @@
1
1
  # CCgram
2
2
 
3
- [![Build Status](https://travis-ci.org/EastSun5566/cc-gram.svg?branch=master)](https://travis-ci.org/EastSun5566/cc-gram) [![npm](https://img.shields.io/npm/v/cc-gram.svg)](https://www.npmjs.com/package/cc-gram)
3
+ [![NPM Version](https://img.shields.io/npm/v/cc-gram.svg?style=for-the-badge)](https://www.npmjs.com/package/cc-gram)
4
+ [![NPM Downloads](https://img.shields.io/npm/dt/cc-gram.svg?style=for-the-badge)](https://www.npmjs.com/package/cc-gram)
5
+ [![Build Status](https://img.shields.io/github/workflow/status/EastSun5566/cc-gram/Test/master.svg?style=for-the-badge)](https://github.com/EastSun5566/cc-gram/actions?query=workflow%3ATest)
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)
4
7
 
5
8
  > 🖼 A CSS & Canvas Instagram filters based on CSSgram
6
9
 
7
- ## ✨ Install
10
+ 🔗 <https://eastsun5566.github.io/cc-gram/>
11
+
12
+ ![Demo GIF](./demo.gif)
13
+
14
+ ## 🤔 The Why
15
+
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.
17
+
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`.
20
+
21
+ ## ✨ Installation
8
22
 
9
23
  ```sh
10
24
  npm i cc-gram
11
-
12
- # or yarn add cc-gram
13
25
  ```
14
26
 
15
27
  ## 🚀 Usage
@@ -29,9 +41,18 @@ npm i cc-gram
29
41
  > Initialize to apply CSS filter to All targets has `data-filter` attribute
30
42
 
31
43
  ```js
32
- import CCGram from "cc-gram";
44
+ import { createFilter } from "cc-gram";
45
+
46
+ const filter = createFilter();
47
+ ```
48
+
49
+ ```js
50
+ // or you can turn off init apply
51
+ const filter = createFilter({ init: false });
33
52
 
34
- const cg = new CCGram();
53
+ // you can also specify data attribute
54
+ // i.e., <img data-my-attr="1977">
55
+ const filter = createFilter({ dataAttribute: "my-attr" });
35
56
  ```
36
57
 
37
58
  ---
@@ -42,10 +63,10 @@ const cg = new CCGram();
42
63
 
43
64
  ```js
44
65
  // apply to All targets has `data-filter` attribute
45
- cg.applyFilter();
66
+ filter.applyFilter();
46
67
 
47
- // or you can just use selector
48
- cg.applyFilter("#my-image");
68
+ // or you can just use selector for performance
69
+ filter.applyFilter("#my-image");
49
70
  ```
50
71
 
51
72
  ##### All available filter name list
@@ -53,7 +74,7 @@ cg.applyFilter("#my-image");
53
74
  > `filterNames`
54
75
 
55
76
  ```js
56
- const { filterNames } = cg;
77
+ const filterNames = filter.filterNames;
57
78
  ```
58
79
 
59
80
  - Default filter Name list:
@@ -84,7 +105,7 @@ const { filterNames } = cg;
84
105
  - `stinson`
85
106
  - `amaro`
86
107
 
87
- ##### Add / Set filter to filter list
108
+ ##### Add / Set filter to the filter list
88
109
 
89
110
  > `setFilter(name, setting)`
90
111
 
@@ -92,7 +113,7 @@ const { filterNames } = cg;
92
113
  - setting: `object` - The filter setting
93
114
 
94
115
  ```js
95
- cg.setFilter("my-filter", {
116
+ filter.setFilter("my-filter", {
96
117
  saturate: 0.8,
97
118
  contrast: 1.2,
98
119
  });
@@ -109,14 +130,14 @@ cg.setFilter("my-filter", {
109
130
  - `saturate`
110
131
  - `sepia`
111
132
 
112
- ##### Remove filter from filter list
133
+ ##### Remove the filter from the filter list
113
134
 
114
135
  > `removeFilter(name)`
115
136
 
116
137
  - name: `string` - The filter name
117
138
 
118
139
  ```js
119
- cg.removeFilter("my-filter");
140
+ filter.removeFilter("my-filter");
120
141
  ```
121
142
 
122
143
  ---
@@ -124,7 +145,7 @@ cg.removeFilter("my-filter");
124
145
  ### Access Filter image
125
146
 
126
147
  ```js
127
- const target = document.querySelector('img[data-filter="1977"]');
148
+ const image = document.querySelector('img[data-filter="1977"]');
128
149
  ```
129
150
 
130
151
  #### Data URL
@@ -132,7 +153,7 @@ const target = document.querySelector('img[data-filter="1977"]');
132
153
  > `getDataURL(image[, options = {}])`
133
154
 
134
155
  ```js
135
- const dataUrl = await ccGram.getDataURL(target);
156
+ const dataUrl = await filter.getDataURL(image);
136
157
  ```
137
158
 
138
159
  #### Blob
@@ -140,7 +161,7 @@ const dataUrl = await ccGram.getDataURL(target);
140
161
  > `getBlob(image[, options = {}])`
141
162
 
142
163
  ```js
143
- const blob = await cg.getBlob(target, {
164
+ const blob = await filter.getBlob(image, {
144
165
  type: "image/jpeg",
145
166
  quality: 0.8,
146
167
  });
@@ -148,21 +169,21 @@ const blob = await cg.getBlob(target, {
148
169
 
149
170
  - Options
150
171
 
151
- - type: `string` - MIME types, default is `image/png`,
152
- - quality: `number`- [0 - 1], default is `0.92`
172
+ - type: `string` - MIME types, defaults to `image/png`,
173
+ - quality: `number`- [0 - 1], defaults to `0.92`
153
174
 
154
- ## 🔧 Develop
175
+ ## 🔧 Development
155
176
 
156
177
  ```sh
157
- # install dependencies
158
- yarn
178
+ # install deps
179
+ pnpm i
159
180
 
160
181
  # fix style
161
- yarn lint
182
+ pnpm run lint
162
183
 
163
184
  # run test
164
- yarn test
185
+ pnpm test
165
186
 
166
- # build for production
167
- yarn build
187
+ # build for prod
188
+ pnpm run build
168
189
  ```
package/dist/core.d.ts ADDED
@@ -0,0 +1,60 @@
1
+ import { FilterName, FilterSetting } from './filters';
2
+ import { Options, ParseOptions } from './types';
3
+ export declare const DEFAULT_DATA_ATTRIBUTE = "filter";
4
+ /** 🖼 A CSS & Canvas Instagram filters based on CSSgram */
5
+ export declare class CCgram {
6
+ static readonly DEFAULT_DATA_ATTRIBUTE = "filter";
7
+ static readonly DEFAULT_FILTERS: Map<string, FilterSetting>;
8
+ /** filter list */
9
+ protected readonly _filters: Map<string, FilterSetting>;
10
+ /** data attribute */
11
+ protected _dataAttribute: string;
12
+ /** Initialize CSS filter to all targets */
13
+ constructor({ dataAttribute, init, }?: Options);
14
+ /** The filter name list */
15
+ get filterNames(): FilterName[];
16
+ /**
17
+ * Add/Set filter
18
+ * @param name - the Filter name
19
+ * @param setting - the Filter setting
20
+ */
21
+ setFilter(name: FilterName, setting: FilterSetting): void;
22
+ /**
23
+ * Remove filter
24
+ * @param name - the Filter name
25
+ */
26
+ removeFilter(name: FilterName): boolean;
27
+ /**
28
+ * Get setting object of filter
29
+ * @param [name=''] - The filter name
30
+ */
31
+ getFilterSetting(name?: FilterName): FilterSetting | void;
32
+ /**
33
+ * Get the CSS inline style string of filter
34
+ * @param [name=''] - The filter name
35
+ */
36
+ getFilterStyle(name?: FilterName): string;
37
+ /**
38
+ * Apply CSS filter to all targets
39
+ * @param [selectors='img[data-${this._dataAttribute}]'] - selectors
40
+ */
41
+ applyFilter(selectors?: string): void;
42
+ /**
43
+ * Get the data URL of image element
44
+ * @param image - image element
45
+ * @param [options] - options
46
+ */
47
+ getDataURL(image: HTMLImageElement, options?: ParseOptions): Promise<string | null>;
48
+ /**
49
+ * Get the blob of image element
50
+ * @param image - image element
51
+ * @param [options={}] - parse options
52
+ */
53
+ getBlob(image: HTMLImageElement, options?: ParseOptions): Promise<Blob | null>;
54
+ }
55
+ /** old Name, alias for `CCgram` */
56
+ export declare const CCGram: typeof CCgram;
57
+ /** alias for `CCgram` */
58
+ export declare const Filter: typeof CCgram;
59
+ export declare type FilterInstance = InstanceType<typeof CCgram>;
60
+ export declare function createFilter(options: Options): FilterInstance;
@@ -1,10 +1,10 @@
1
1
  /**
2
2
  * Base on CSSgram by Una Kravets
3
- * @see CSSgram <https://github.com/una/CSSgram>
3
+ * @see CSSgram {@link https://github.com/una/CSSgram}
4
4
  */
5
5
  /** The Name of Filter */
6
6
  export declare type FilterName = string;
7
- /** The Setting of Filter */
7
+ /** The Setting object of Filter */
8
8
  export interface FilterSetting {
9
9
  blur?: number;
10
10
  brightness?: number;
@@ -18,4 +18,3 @@ export interface FilterSetting {
18
18
  }
19
19
  /** The default Filter List */
20
20
  export declare const DEFAULT_FILTERS: Map<FilterName, FilterSetting>;
21
- export default DEFAULT_FILTERS;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @package cc-gram <https://github.com/EastSun5566/cc-gram>
3
+ *
4
+ * @license MIT
5
+ * @copyright (c) 2019 - present
6
+ * @author 汪東陽 EastSun5566 <https://github.com/EastSun5566>
7
+ */
8
+ export { Filter as default } from './core';
9
+ export * from './filters';
10
+ export * from './core';
@@ -0,0 +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};
@@ -0,0 +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})}));
@@ -0,0 +1,14 @@
1
+ /** constructor Options */
2
+ export interface Options {
3
+ /** The default data attribute */
4
+ dataAttribute?: string;
5
+ /** is Init CSS filter to all targets */
6
+ init?: boolean;
7
+ }
8
+ /** The parse options for canvas */
9
+ export interface ParseOptions {
10
+ /** MIME types, defaults to `image/png` */
11
+ type?: string;
12
+ /** [0 - 1], defaults to `0.92` */
13
+ quality?: number;
14
+ }
@@ -0,0 +1,20 @@
1
+ /// <reference types="offscreencanvas" />
2
+ import { FilterSetting } from './filters';
3
+ import { ParseOptions } from './types';
4
+ export declare const hasOffscreenCanvas: boolean;
5
+ export declare function assert<TCond = unknown>(condition: TCond, message?: string): asserts condition;
6
+ export declare function assertIsImage(image: HTMLImageElement): asserts image is HTMLImageElement;
7
+ export declare function createWorker<TData = unknown, TMessage = unknown>(fn: (messageEvent: MessageEvent<TData>) => TMessage): Worker;
8
+ /**
9
+ * Parse setting to style string
10
+ * @param setting - The filter setting
11
+ */
12
+ export declare function parseSettingToStyle(setting?: FilterSetting): string;
13
+ interface CreateBlobOptions<TCanvas extends HTMLCanvasElement | OffscreenCanvas = HTMLCanvasElement> {
14
+ canvas: TCanvas;
15
+ image: CanvasImageSource;
16
+ filterStyle: string;
17
+ options: ParseOptions;
18
+ }
19
+ export declare function createBlobWorker({ data, }: MessageEvent<CreateBlobOptions<OffscreenCanvas>>): Promise<Blob | null>;
20
+ export {};
package/package.json CHANGED
@@ -1,25 +1,19 @@
1
1
  {
2
2
  "name": "cc-gram",
3
- "version": "0.2.1",
3
+ "version": "0.2.2-beta.0",
4
4
  "description": "🖼 A CSS & Canvas Instagram filters based on CSSgram",
5
- "main": "dist/index.js",
6
- "types": "dist/types/index.d.ts",
5
+ "type": "module",
6
+ "main": "dist/index.umd.js",
7
+ "module": "dist/index.esm.js",
8
+ "types": "dist/index.d.ts",
9
+ "exports": {
10
+ "import": "./dist/index.esm.js",
11
+ "require": "./dist/index.umd.js"
12
+ },
7
13
  "files": [
8
- "dist"
14
+ "dist",
15
+ "src"
9
16
  ],
10
- "scripts": {
11
- "type-check": "tsc --noEmit",
12
- "build:types": "tsc --emitDeclarationOnly",
13
- "build:js": "webpack -p",
14
- "build:js:dev": "webpack -d",
15
- "build": "yarn build:types && yarn build:js",
16
- "test": "jest __tests__/",
17
- "lint": "eslint --fix --ignore-path .gitignore --ext .js,.ts src/",
18
- "release:patch": "sh bin/release.sh patch",
19
- "release:minor": "sh bin/release.sh minor",
20
- "release:major": "sh bin/release.sh major",
21
- "deploy": "sh bin/deploy.sh"
22
- },
23
17
  "keywords": [
24
18
  "image",
25
19
  "filter",
@@ -37,36 +31,43 @@
37
31
  "url": "https://github.com/EastSun5566/cc-gram"
38
32
  },
39
33
  "homepage": "https://eastsun5566.github.io/cc-gram/",
34
+ "bugs": {
35
+ "url": "https://github.com/EastSun5566/cc-gram/issues"
36
+ },
40
37
  "husky": {
41
38
  "hooks": {
42
- "pre-commit": "yarn lint"
39
+ "pre-commit": "pnpm lint"
43
40
  }
44
41
  },
45
42
  "devDependencies": {
46
- "@babel/core": "^7.4.3",
47
- "@babel/plugin-proposal-class-properties": "^7.4.4",
48
- "@babel/plugin-proposal-object-rest-spread": "^7.4.4",
49
- "@babel/plugin-transform-runtime": "^7.4.4",
50
- "@babel/preset-env": "^7.4.3",
51
- "@babel/preset-typescript": "^7.3.3",
52
- "@types/jest": "^24.0.24",
43
+ "@rollup/plugin-node-resolve": "^15.0.1",
44
+ "@rollup/plugin-terser": "^0.1.0",
45
+ "@rollup/plugin-typescript": "^9.0.2",
46
+ "@types/offscreencanvas": "^2019.7.0",
53
47
  "@typescript-eslint/eslint-plugin": "^2.14.0",
54
48
  "@typescript-eslint/parser": "^2.14.0",
49
+ "@vitest/coverage-c8": "^0.25.6",
55
50
  "babel-eslint": "^10.0.1",
56
- "babel-loader": "^8.0.5",
57
- "canvas": "^2.4.1",
58
51
  "eslint": "^6.8.0",
59
52
  "eslint-config-airbnb-base": "^14.0.0",
60
53
  "eslint-plugin-import": "^2.19.1",
61
- "fork-ts-checker-webpack-plugin": "^3.1.1",
62
54
  "husky": "^3.1.0",
63
- "jest": "^24.7.1",
64
- "ts-jest": "^24.0.2",
65
- "typescript": "^3.7.4",
66
- "webpack": "^4.30.0",
67
- "webpack-cli": "^3.3.0"
55
+ "jsdom": "^20.0.3",
56
+ "rimraf": "^3.0.2",
57
+ "rollup": "^3.2.5",
58
+ "rollup-plugin-filesize": "^9.1.2",
59
+ "tslib": "^2.4.1",
60
+ "typescript": "^4.8.4",
61
+ "vitest": "^0.25.6"
68
62
  },
69
- "dependencies": {
70
- "@babel/runtime": "^7.4.4"
63
+ "scripts": {
64
+ "preinstall": "npx only-allow pnpm",
65
+ "build": "rimraf dist && rollup -c rollup.config.ts --configPlugin typescript",
66
+ "type-check": "tsc --noEmit",
67
+ "test": "vitest __tests__",
68
+ "test:coverage": "vitest run __tests__ --coverage",
69
+ "lint": "eslint --fix --ignore-path .gitignore --ext .js,.ts src",
70
+ "release": "sh scripts/release.sh",
71
+ "demo:dev": "pnpm -C demo dev"
71
72
  }
72
- }
73
+ }
package/src/core.ts ADDED
@@ -0,0 +1,188 @@
1
+ import {
2
+ DEFAULT_FILTERS,
3
+ FilterName,
4
+ FilterSetting,
5
+ } from './filters';
6
+ import {
7
+ parseSettingToStyle,
8
+ hasOffscreenCanvas,
9
+ assertIsImage,
10
+ createWorker,
11
+ createBlobWorker,
12
+ } from './utils';
13
+
14
+ import { Options, ParseOptions } from './types';
15
+
16
+ export const DEFAULT_DATA_ATTRIBUTE = 'filter';
17
+
18
+ /** 🖼 A CSS & Canvas Instagram filters based on CSSgram */
19
+ export class CCgram {
20
+ static readonly DEFAULT_DATA_ATTRIBUTE = DEFAULT_DATA_ATTRIBUTE;
21
+
22
+ static readonly DEFAULT_FILTERS = DEFAULT_FILTERS;
23
+
24
+ /** filter list */
25
+ protected readonly _filters = DEFAULT_FILTERS;
26
+
27
+ /** data attribute */
28
+ protected _dataAttribute: string;
29
+
30
+ /** Initialize CSS filter to all targets */
31
+ constructor({
32
+ dataAttribute = DEFAULT_DATA_ATTRIBUTE,
33
+ init = true,
34
+ }: Options = {}) {
35
+ this._dataAttribute = dataAttribute;
36
+
37
+ if (!init) return;
38
+
39
+ if (document.readyState === 'complete') {
40
+ this.applyFilter();
41
+ return;
42
+ }
43
+
44
+ const handleLoaded = (): void => {
45
+ this.applyFilter();
46
+ document.removeEventListener('DOMContentLoaded', handleLoaded);
47
+ };
48
+ document.addEventListener('DOMContentLoaded', handleLoaded);
49
+ }
50
+
51
+ /** The filter name list */
52
+ get filterNames(): FilterName[] {
53
+ return [...this._filters.keys()];
54
+ }
55
+
56
+ /**
57
+ * Add/Set filter
58
+ * @param name - the Filter name
59
+ * @param setting - the Filter setting
60
+ */
61
+ setFilter(name: FilterName, setting: FilterSetting): void {
62
+ this._filters.set(name, setting);
63
+ }
64
+
65
+ /**
66
+ * Remove filter
67
+ * @param name - the Filter name
68
+ */
69
+ removeFilter(name: FilterName): boolean {
70
+ return this._filters.delete(name);
71
+ }
72
+
73
+ /**
74
+ * Get setting object of filter
75
+ * @param [name=''] - The filter name
76
+ */
77
+ getFilterSetting(name: FilterName = ''): FilterSetting | void {
78
+ return this._filters.get(name);
79
+ }
80
+
81
+ /**
82
+ * Get the CSS inline style string of filter
83
+ * @param [name=''] - The filter name
84
+ */
85
+ getFilterStyle(name: FilterName = ''): string {
86
+ const setting = this._filters.get(name);
87
+
88
+ return parseSettingToStyle(setting);
89
+ }
90
+
91
+ /**
92
+ * Apply CSS filter to all targets
93
+ * @param [selectors='img[data-${this._dataAttribute}]'] - selectors
94
+ */
95
+ applyFilter(selectors = `img[data-${this._dataAttribute}]`): void {
96
+ document
97
+ .querySelectorAll<HTMLImageElement>(selectors)
98
+ .forEach((target): void => {
99
+ const { dataset } = target;
100
+ target.style.setProperty('filter', this.getFilterStyle(dataset[this._dataAttribute]));
101
+ });
102
+ }
103
+
104
+ /**
105
+ * Get the data URL of image element
106
+ * @param image - image element
107
+ * @param [options] - options
108
+ */
109
+ async getDataURL(
110
+ image: HTMLImageElement,
111
+ options: ParseOptions = {},
112
+ ): Promise<string | null> {
113
+ assertIsImage(image);
114
+
115
+ // don't use canvas.toDataURL, use blob to DataURL
116
+ const blob = await this.getBlob(image, options);
117
+ if (!blob) return null;
118
+
119
+ const reader = new FileReader();
120
+
121
+ return new Promise((resolve) => {
122
+ reader.addEventListener('load', () => {
123
+ resolve(reader.result as string);
124
+ });
125
+
126
+ reader.readAsDataURL(blob);
127
+ });
128
+ }
129
+
130
+ /**
131
+ * Get the blob of image element
132
+ * @param image - image element
133
+ * @param [options={}] - parse options
134
+ */
135
+ async getBlob(
136
+ image: HTMLImageElement,
137
+ options: ParseOptions = {},
138
+ ): Promise<Blob | null> {
139
+ assertIsImage(image);
140
+
141
+ const { naturalWidth, naturalHeight } = image;
142
+ const filterStyle = this.getFilterStyle(image.dataset[this._dataAttribute]);
143
+
144
+ if (hasOffscreenCanvas) {
145
+ const canvas = new OffscreenCanvas(naturalWidth, naturalHeight);
146
+ const bmp = await createImageBitmap(image);
147
+
148
+ return new Promise((resolve) => {
149
+ const worker = createWorker(createBlobWorker);
150
+
151
+ worker.addEventListener('message', ({ data }: MessageEvent<Blob>) => {
152
+ resolve(data);
153
+ worker.terminate();
154
+ });
155
+
156
+ worker.postMessage({
157
+ canvas,
158
+ image: bmp,
159
+ filterStyle,
160
+ options,
161
+ }, [canvas, bmp]);
162
+ });
163
+ }
164
+
165
+ const canvas = document.createElement('canvas');
166
+ canvas.width = naturalWidth;
167
+ canvas.height = naturalHeight;
168
+
169
+ const ctx = canvas.getContext('2d', { alpha: false });
170
+ if (!ctx) throw new Error('The 2d context canvas is not supported.');
171
+
172
+ ctx.filter = filterStyle;
173
+ ctx.drawImage(image, 0, 0);
174
+
175
+ const { type, quality } = options;
176
+ return new Promise((resolve) => canvas.toBlob((blob): void => resolve(blob), type, quality));
177
+ }
178
+ }
179
+
180
+ /** old Name, alias for `CCgram` */
181
+ export const CCGram = CCgram;
182
+ /** alias for `CCgram` */
183
+ export const Filter = CCgram;
184
+
185
+ export type FilterInstance = InstanceType<typeof CCgram>;
186
+ export function createFilter(options: Options): FilterInstance {
187
+ return new Filter(options);
188
+ }
package/src/filters.ts ADDED
@@ -0,0 +1,219 @@
1
+ /**
2
+ * Base on CSSgram by Una Kravets
3
+ * @see CSSgram {@link https://github.com/una/CSSgram}
4
+ */
5
+
6
+ /** The Name of Filter */
7
+ export type FilterName = string;
8
+
9
+ /** The Setting object of Filter */
10
+ export interface FilterSetting {
11
+ blur?: number;
12
+ brightness?: number;
13
+ contrast?: number;
14
+ grayscale?: number;
15
+ 'hue-rotate'?: number;
16
+ invert?: number;
17
+ saturate?: number;
18
+ sepia?: number;
19
+ [key: string]: number | undefined;
20
+ }
21
+
22
+ /** The default Filter List */
23
+ export const DEFAULT_FILTERS: Map<FilterName, FilterSetting> = new Map([
24
+ [
25
+ 'aden',
26
+ {
27
+ 'hue-rotate': -20,
28
+ contrast: 0.9,
29
+ brightness: 1.2,
30
+ saturate: 0.85,
31
+ },
32
+ ],
33
+ [
34
+ 'inkwell',
35
+ {
36
+ sepia: 0.3,
37
+ contrast: 1.1,
38
+ brightness: 1.1,
39
+ grayscale: 1,
40
+ },
41
+ ],
42
+ [
43
+ 'reyes',
44
+ {
45
+ sepia: 0.22,
46
+ contrast: 0.85,
47
+ brightness: 1.1,
48
+ saturate: 0.75,
49
+ },
50
+ ],
51
+ [
52
+ 'gingham',
53
+ {
54
+ 'hue-rotate': -10,
55
+ brightness: 1.05,
56
+ },
57
+ ],
58
+ [
59
+ 'toaster',
60
+ {
61
+ contrast: 1.5,
62
+ brightness: 0.9,
63
+ },
64
+ ],
65
+ [
66
+ 'walden',
67
+ {
68
+ 'hue-rotate': -10,
69
+ brightness: 1.1,
70
+ sepia: 0.3,
71
+ saturate: 1.6,
72
+ },
73
+ ],
74
+ [
75
+ 'hudson',
76
+ {
77
+ brightness: 1.2,
78
+ contrast: 0.9,
79
+ saturate: 1.1,
80
+ },
81
+ ],
82
+ [
83
+ 'earlybird',
84
+ {
85
+ contrast: 0.9,
86
+ sepia: 0.2,
87
+ },
88
+ ],
89
+ [
90
+ 'mayfair',
91
+ {
92
+ contrast: 1.1,
93
+ saturate: 1.1,
94
+ },
95
+ ],
96
+ [
97
+ 'lofi',
98
+ {
99
+ contrast: 1.5,
100
+ saturate: 1.1,
101
+ },
102
+ ],
103
+ [
104
+ '1977',
105
+ {
106
+ contrast: 1.1,
107
+ brightness: 1.1,
108
+ saturate: 1.3,
109
+ },
110
+ ],
111
+ [
112
+ 'brooklyn',
113
+ {
114
+ contrast: 0.9,
115
+ brightness: 1.1,
116
+ },
117
+ ],
118
+ [
119
+ 'xpro2',
120
+ {
121
+ sepia: 0.3,
122
+ },
123
+ ],
124
+ [
125
+ 'nashville',
126
+ {
127
+ contrast: 1.2,
128
+ brightness: 1.05,
129
+ saturate: 1.2,
130
+ sepia: 0.2,
131
+ },
132
+ ],
133
+ [
134
+ 'lark',
135
+ {
136
+ contrast: 0.9,
137
+ },
138
+ ],
139
+ [
140
+ 'moon',
141
+ {
142
+ brightness: 1.1,
143
+ contrast: 1.1,
144
+ grayscale: 1,
145
+ },
146
+ ],
147
+ [
148
+ 'clarendon',
149
+ {
150
+ contrast: 1.2,
151
+ saturate: 1.35,
152
+ },
153
+ ],
154
+ [
155
+ 'willow',
156
+ {
157
+ contrast: 0.95,
158
+ brightness: 0.9,
159
+ grayscale: 0.5,
160
+ },
161
+ ],
162
+ [
163
+ 'rise',
164
+ {
165
+ contrast: 0.9,
166
+ brightness: 1.05,
167
+ sepia: 0.2,
168
+ saturate: 0.9,
169
+ },
170
+ ],
171
+ [
172
+ 'slumber',
173
+ {
174
+ brightness: 1.05,
175
+ saturate: 0.66,
176
+ },
177
+ ],
178
+ [
179
+ 'brannan',
180
+ {
181
+ contrast: 1.4,
182
+ sepia: 0.5,
183
+ },
184
+ ],
185
+ [
186
+ 'valencia',
187
+ {
188
+ contrast: 1.08,
189
+ brightness: 1.08,
190
+ sepia: 0.08,
191
+ },
192
+ ],
193
+ [
194
+ 'maven',
195
+ {
196
+ contrast: 0.95,
197
+ brightness: 1.95,
198
+ saturate: 1.5,
199
+ sepia: 0.25,
200
+ },
201
+ ],
202
+ [
203
+ 'stinson',
204
+ {
205
+ contrast: 0.75,
206
+ brightness: 1.15,
207
+ saturate: 0.85,
208
+ },
209
+ ],
210
+ [
211
+ 'amaro',
212
+ {
213
+ 'hue-rotate': -10,
214
+ contrast: 0.9,
215
+ brightness: 1.1,
216
+ saturate: 1.5,
217
+ },
218
+ ],
219
+ ]);
package/src/index.ts ADDED
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @package cc-gram <https://github.com/EastSun5566/cc-gram>
3
+ *
4
+ * @license MIT
5
+ * @copyright (c) 2019 - present
6
+ * @author 汪東陽 EastSun5566 <https://github.com/EastSun5566>
7
+ */
8
+
9
+ export { Filter as default } from './core';
10
+
11
+ export * from './filters';
12
+ export * from './core';
package/src/types.ts ADDED
@@ -0,0 +1,15 @@
1
+ /** constructor Options */
2
+ export interface Options {
3
+ /** The default data attribute */
4
+ dataAttribute?: string;
5
+ /** is Init CSS filter to all targets */
6
+ init?: boolean;
7
+ }
8
+
9
+ /** The parse options for canvas */
10
+ export interface ParseOptions {
11
+ /** MIME types, defaults to `image/png` */
12
+ type?: string;
13
+ /** [0 - 1], defaults to `0.92` */
14
+ quality?: number;
15
+ }
package/src/utils.ts ADDED
@@ -0,0 +1,85 @@
1
+ import { FilterSetting } from './filters';
2
+ import { ParseOptions } from './types';
3
+
4
+ export const hasOffscreenCanvas = typeof OffscreenCanvas !== 'undefined';
5
+
6
+ export function assert<TCond = unknown>(condition: TCond, message = 'internal error.'): asserts condition {
7
+ if (!condition) throw Error(`[CCgram] ${message}`);
8
+ }
9
+
10
+ export function assertIsImage(image: HTMLImageElement): asserts image is HTMLImageElement {
11
+ assert(image && image.tagName === 'IMG', 'The first argument is required and must be an <img> element.');
12
+ assert(image.src, 'The <img> element src attribute is empty.');
13
+ }
14
+
15
+ export function createWorker<
16
+ TData = unknown,
17
+ TMessage = unknown
18
+ >(fn: (messageEvent: MessageEvent<TData>) => TMessage): Worker {
19
+ const code = `
20
+ const work = ${fn.toString()};
21
+
22
+ addEventListener('message', async (...params) => {
23
+ const res = await work(...params);
24
+ postMessage(res);
25
+ });
26
+ `;
27
+
28
+ const url = URL.createObjectURL(new Blob([code], { type: 'text/javascript' }));
29
+ const worker = new Worker(url);
30
+
31
+ const { terminate } = worker;
32
+ worker.terminate = (): void => {
33
+ URL.revokeObjectURL(url);
34
+ terminate.call(worker);
35
+ };
36
+
37
+ return worker;
38
+ }
39
+
40
+ /**
41
+ * Parse setting to style string
42
+ * @param setting - The filter setting
43
+ */
44
+ export function parseSettingToStyle(setting?: FilterSetting): string {
45
+ if (!setting) return 'none';
46
+
47
+ return Object
48
+ .keys(setting)
49
+ .map((key): string => `${key}(${setting[key]}${
50
+ key === 'hue-rotate'
51
+ ? 'deg'
52
+ : key === 'blur'
53
+ ? 'px'
54
+ : ''
55
+ })`)
56
+ .join(' ');
57
+ }
58
+
59
+ interface CreateBlobOptions<
60
+ TCanvas extends HTMLCanvasElement | OffscreenCanvas = HTMLCanvasElement
61
+ > {
62
+ canvas: TCanvas;
63
+ image: CanvasImageSource;
64
+ filterStyle: string;
65
+ options: ParseOptions;
66
+ }
67
+
68
+ export function createBlobWorker({
69
+ data,
70
+ }: MessageEvent<CreateBlobOptions<OffscreenCanvas>>): Promise<Blob | null> {
71
+ const {
72
+ canvas,
73
+ image,
74
+ filterStyle,
75
+ options,
76
+ } = data;
77
+
78
+ const ctx = canvas.getContext('2d', { alpha: false });
79
+ if (!ctx) throw new Error('The 2d context canvas is not supported.');
80
+
81
+ ctx.filter = filterStyle;
82
+ ctx.drawImage(image, 0, 0);
83
+
84
+ return canvas.convertToBlob(options);
85
+ }
package/dist/index.js DELETED
@@ -1 +0,0 @@
1
- !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.CCGram=e():t.CCGram=e()}("undefined"!=typeof self?self:this,(function(){return function(t){var e={};function r(n){if(e[n])return e[n].exports;var o=e[n]={i:n,l:!1,exports:{}};return t[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}return r.m=t,r.c=e,r.d=function(t,e,n){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(t,e){if(1&e&&(t=r(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)r.d(n,o,function(e){return t[e]}.bind(null,o));return n},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="",r(r.s=12)}([function(t,e,r){t.exports=r(11)},function(t,e){t.exports=function(t,e,r){return e in t?Object.defineProperty(t,e,{value:r,enumerable:!0,configurable:!0,writable:!0}):t[e]=r,t}},function(t,e){function r(t,e,r,n,o,i,a){try{var u=t[i](a),c=u.value}catch(t){return void r(t)}u.done?e(c):Promise.resolve(c).then(n,o)}t.exports=function(t){return function(){var e=this,n=arguments;return new Promise((function(o,i){var a=t.apply(e,n);function u(t){r(a,o,i,u,c,"next",t)}function c(t){r(a,o,i,u,c,"throw",t)}u(void 0)}))}}},function(t,e){t.exports=function(t,e){(null==e||e>t.length)&&(e=t.length);for(var r=0,n=new Array(e);r<e;r++)n[r]=t[r];return n}},function(t,e,r){var n=r(7),o=r(8),i=r(9),a=r(10);t.exports=function(t){return n(t)||o(t)||i(t)||a()}},function(t,e){t.exports=function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}},function(t,e){function r(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}t.exports=function(t,e,n){return e&&r(t.prototype,e),n&&r(t,n),t}},function(t,e,r){var n=r(3);t.exports=function(t){if(Array.isArray(t))return n(t)}},function(t,e){t.exports=function(t){if("undefined"!=typeof Symbol&&Symbol.iterator in Object(t))return Array.from(t)}},function(t,e,r){var n=r(3);t.exports=function(t,e){if(t){if("string"==typeof t)return n(t,e);var r=Object.prototype.toString.call(t).slice(8,-1);return"Object"===r&&t.constructor&&(r=t.constructor.name),"Map"===r||"Set"===r?Array.from(t):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?n(t,e):void 0}}},function(t,e){t.exports=function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}},function(t,e,r){var n=function(t){"use strict";var e=Object.prototype,r=e.hasOwnProperty,n="function"==typeof Symbol?Symbol:{},o=n.iterator||"@@iterator",i=n.asyncIterator||"@@asyncIterator",a=n.toStringTag||"@@toStringTag";function u(t,e,r){return Object.defineProperty(t,e,{value:r,enumerable:!0,configurable:!0,writable:!0}),t[e]}try{u({},"")}catch(t){u=function(t,e,r){return t[e]=r}}function c(t,e,r,n){var o=e&&e.prototype instanceof l?e:l,i=Object.create(o.prototype),a=new L(n||[]);return i._invoke=function(t,e,r){var n="suspendedStart";return function(o,i){if("executing"===n)throw new Error("Generator is already running");if("completed"===n){if("throw"===o)throw i;return j()}for(r.method=o,r.arg=i;;){var a=r.delegate;if(a){var u=w(a,r);if(u){if(u===f)continue;return u}}if("next"===r.method)r.sent=r._sent=r.arg;else if("throw"===r.method){if("suspendedStart"===n)throw n="completed",r.arg;r.dispatchException(r.arg)}else"return"===r.method&&r.abrupt("return",r.arg);n="executing";var c=s(t,e,r);if("normal"===c.type){if(n=r.done?"completed":"suspendedYield",c.arg===f)continue;return{value:c.arg,done:r.done}}"throw"===c.type&&(n="completed",r.method="throw",r.arg=c.arg)}}}(t,r,a),i}function s(t,e,r){try{return{type:"normal",arg:t.call(e,r)}}catch(t){return{type:"throw",arg:t}}}t.wrap=c;var f={};function l(){}function h(){}function p(){}var d={};d[o]=function(){return this};var y=Object.getPrototypeOf,v=y&&y(y(E([])));v&&v!==e&&r.call(v,o)&&(d=v);var g=p.prototype=l.prototype=Object.create(d);function m(t){["next","throw","return"].forEach((function(e){u(t,e,(function(t){return this._invoke(e,t)}))}))}function b(t,e){var n;this._invoke=function(o,i){function a(){return new e((function(n,a){!function n(o,i,a,u){var c=s(t[o],t,i);if("throw"!==c.type){var f=c.arg,l=f.value;return l&&"object"==typeof l&&r.call(l,"__await")?e.resolve(l.__await).then((function(t){n("next",t,a,u)}),(function(t){n("throw",t,a,u)})):e.resolve(l).then((function(t){f.value=t,a(f)}),(function(t){return n("throw",t,a,u)}))}u(c.arg)}(o,i,n,a)}))}return n=n?n.then(a,a):a()}}function w(t,e){var r=t.iterator[e.method];if(void 0===r){if(e.delegate=null,"throw"===e.method){if(t.iterator.return&&(e.method="return",e.arg=void 0,w(t,e),"throw"===e.method))return f;e.method="throw",e.arg=new TypeError("The iterator does not provide a 'throw' method")}return f}var n=s(r,t.iterator,e.arg);if("throw"===n.type)return e.method="throw",e.arg=n.arg,e.delegate=null,f;var o=n.arg;return o?o.done?(e[t.resultName]=o.value,e.next=t.nextLoc,"return"!==e.method&&(e.method="next",e.arg=void 0),e.delegate=null,f):o:(e.method="throw",e.arg=new TypeError("iterator result is not an object"),e.delegate=null,f)}function x(t){var e={tryLoc:t[0]};1 in t&&(e.catchLoc=t[1]),2 in t&&(e.finallyLoc=t[2],e.afterLoc=t[3]),this.tryEntries.push(e)}function _(t){var e=t.completion||{};e.type="normal",delete e.arg,t.completion=e}function L(t){this.tryEntries=[{tryLoc:"root"}],t.forEach(x,this),this.reset(!0)}function E(t){if(t){var e=t[o];if(e)return e.call(t);if("function"==typeof t.next)return t;if(!isNaN(t.length)){var n=-1,i=function e(){for(;++n<t.length;)if(r.call(t,n))return e.value=t[n],e.done=!1,e;return e.value=void 0,e.done=!0,e};return i.next=i}}return{next:j}}function j(){return{value:void 0,done:!0}}return h.prototype=g.constructor=p,p.constructor=h,h.displayName=u(p,a,"GeneratorFunction"),t.isGeneratorFunction=function(t){var e="function"==typeof t&&t.constructor;return!!e&&(e===h||"GeneratorFunction"===(e.displayName||e.name))},t.mark=function(t){return Object.setPrototypeOf?Object.setPrototypeOf(t,p):(t.__proto__=p,u(t,a,"GeneratorFunction")),t.prototype=Object.create(g),t},t.awrap=function(t){return{__await:t}},m(b.prototype),b.prototype[i]=function(){return this},t.AsyncIterator=b,t.async=function(e,r,n,o,i){void 0===i&&(i=Promise);var a=new b(c(e,r,n,o),i);return t.isGeneratorFunction(r)?a:a.next().then((function(t){return t.done?t.value:a.next()}))},m(g),u(g,a,"Generator"),g[o]=function(){return this},g.toString=function(){return"[object Generator]"},t.keys=function(t){var e=[];for(var r in t)e.push(r);return e.reverse(),function r(){for(;e.length;){var n=e.pop();if(n in t)return r.value=n,r.done=!1,r}return r.done=!0,r}},t.values=E,L.prototype={constructor:L,reset:function(t){if(this.prev=0,this.next=0,this.sent=this._sent=void 0,this.done=!1,this.delegate=null,this.method="next",this.arg=void 0,this.tryEntries.forEach(_),!t)for(var e in this)"t"===e.charAt(0)&&r.call(this,e)&&!isNaN(+e.slice(1))&&(this[e]=void 0)},stop:function(){this.done=!0;var t=this.tryEntries[0].completion;if("throw"===t.type)throw t.arg;return this.rval},dispatchException:function(t){if(this.done)throw t;var e=this;function n(r,n){return a.type="throw",a.arg=t,e.next=r,n&&(e.method="next",e.arg=void 0),!!n}for(var o=this.tryEntries.length-1;o>=0;--o){var i=this.tryEntries[o],a=i.completion;if("root"===i.tryLoc)return n("end");if(i.tryLoc<=this.prev){var u=r.call(i,"catchLoc"),c=r.call(i,"finallyLoc");if(u&&c){if(this.prev<i.catchLoc)return n(i.catchLoc,!0);if(this.prev<i.finallyLoc)return n(i.finallyLoc)}else if(u){if(this.prev<i.catchLoc)return n(i.catchLoc,!0)}else{if(!c)throw new Error("try statement without catch or finally");if(this.prev<i.finallyLoc)return n(i.finallyLoc)}}}},abrupt:function(t,e){for(var n=this.tryEntries.length-1;n>=0;--n){var o=this.tryEntries[n];if(o.tryLoc<=this.prev&&r.call(o,"finallyLoc")&&this.prev<o.finallyLoc){var i=o;break}}i&&("break"===t||"continue"===t)&&i.tryLoc<=e&&e<=i.finallyLoc&&(i=null);var a=i?i.completion:{};return a.type=t,a.arg=e,i?(this.method="next",this.next=i.finallyLoc,f):this.complete(a)},complete:function(t,e){if("throw"===t.type)throw t.arg;return"break"===t.type||"continue"===t.type?this.next=t.arg:"return"===t.type?(this.rval=this.arg=t.arg,this.method="return",this.next="end"):"normal"===t.type&&e&&(this.next=e),f},finish:function(t){for(var e=this.tryEntries.length-1;e>=0;--e){var r=this.tryEntries[e];if(r.finallyLoc===t)return this.complete(r.completion,r.afterLoc),_(r),f}},catch:function(t){for(var e=this.tryEntries.length-1;e>=0;--e){var r=this.tryEntries[e];if(r.tryLoc===t){var n=r.completion;if("throw"===n.type){var o=n.arg;_(r)}return o}}throw new Error("illegal catch attempt")},delegateYield:function(t,e,r){return this.delegate={iterator:E(t),resultName:e,nextLoc:r},"next"===this.method&&(this.arg=void 0),f}},t}(t.exports);try{regeneratorRuntime=n}catch(t){Function("r","regeneratorRuntime = r")(n)}},function(t,e,r){"use strict";r.r(e),r.d(e,"CCGram",(function(){return g}));var n=r(4),o=r.n(n),i=r(0),a=r.n(i),u=r(2),c=r.n(u),s=r(5),f=r.n(s),l=r(6),h=r.n(l),p=r(1),d=r.n(p),y=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}]]),v=function(t){return t?Object.keys(t).map((function(e){return"".concat(e,"(").concat(t[e]).concat("hue-rotate"===e?"deg":"blur"===e?"px":"",")")})).join(" "):"none"},g=function(){function t(){var e=this,r=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=r.dataAttribute,o=void 0===n?t.DEFAULT_DATA_ATTRIBUTE:n,i=r.init,a=void 0===i||i;if(f()(this,t),d()(this,"_filters",y),d()(this,"_dataAttribute",void 0),this._dataAttribute=o,a)if("complete"!==document.readyState){var u=function t(){e.applyFilter(),document.removeEventListener("DOMContentLoaded",t)};document.addEventListener("DOMContentLoaded",u)}else this.applyFilter()}var e,r;return h()(t,[{key:"setFilter",value:function(t,e){this._filters.set(t,e)}},{key:"removeFilter",value:function(t){return this._filters.delete(t)}},{key:"getFilterSetting",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";return this._filters.get(t)}},{key:"getFilterStyle",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",e=this._filters.get(t);return v(e)}},{key:"applyFilter",value:function(){var t=this,e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"img[data-".concat(this._dataAttribute,"]");document.querySelectorAll(e).forEach((function(e){var r=e.dataset;e.style.filter=t.getFilterStyle(r[t._dataAttribute])}))}},{key:"_getImageCanvas",value:function(t){if(!t||"IMG"!==t.tagName)throw new Error("The first argument is required and must be an <img> element.");if(!t.src)throw new Error("The <img> element src attribute is empty.");return function(t,e){return new Promise((function(r,n){var o=t.naturalWidth,i=t.naturalHeight,a=document.createElement("canvas");a.width=o,a.height=i;var u=a.getContext("2d",{alpha:!1});return u?(u.filter=e,u.drawImage(t,0,0),r(a)):n(new Error("The 2d context canvas is not supported."))}))}(t,this.getFilterStyle(t.dataset[this._dataAttribute]))}},{key:"getDataURL",value:(r=c()(a.a.mark((function t(e){var r,n,o,i,u=arguments;return a.a.wrap((function(t){for(;;)switch(t.prev=t.next){case 0:return r=u.length>1&&void 0!==u[1]?u[1]:{},n=r.type,o=r.quality,t.next=3,this._getImageCanvas(e);case 3:return i=t.sent,t.abrupt("return",i.toDataURL(n,o));case 5:case"end":return t.stop()}}),t,this)}))),function(t){return r.apply(this,arguments)})},{key:"getBlob",value:(e=c()(a.a.mark((function t(e){var r,n,o,i,u=arguments;return a.a.wrap((function(t){for(;;)switch(t.prev=t.next){case 0:return r=u.length>1&&void 0!==u[1]?u[1]:{},n=r.type,o=r.quality,t.next=3,this._getImageCanvas(e);case 3:return i=t.sent,t.abrupt("return",new Promise((function(t){i.toBlob((function(e){return t(e)}),n,o)})));case 5:case"end":return t.stop()}}),t,this)}))),function(t){return e.apply(this,arguments)})},{key:"filterNames",get:function(){return o()(this._filters.keys())}}]),t}();d()(g,"DEFAULT_DATA_ATTRIBUTE","filter");e.default=g}])}));
@@ -1,79 +0,0 @@
1
- /**
2
- * @package cc-gram <https://github.com/EastSun5566/cc-gram>
3
- *
4
- * @license MIT
5
- * @copyright (c) 2019 - present
6
- * @author 汪東陽 EastSun5566 <https://github.com/EastSun5566>
7
- */
8
- import { FilterName, FilterSetting } from './filters';
9
- /** The options for canvas */
10
- interface Options {
11
- /** MIME types, default is `image/png` */
12
- type?: string;
13
- /** [0 - 1], default is `0.92` */
14
- quality?: number;
15
- }
16
- /** Initial Config */
17
- interface Config {
18
- /** The default data attribute */
19
- dataAttribute?: string;
20
- /** is Init CSS filter to all targets */
21
- init?: boolean;
22
- }
23
- /** 🖼 A CSS & Canvas Instagram filters based on CSSgram */
24
- export declare class CCGram {
25
- /** default option */
26
- static readonly DEFAULT_DATA_ATTRIBUTE = "filter";
27
- /** filter list */
28
- protected readonly _filters: Map<string, FilterSetting>;
29
- /** data attribute */
30
- protected _dataAttribute: string;
31
- /** Initialize CSS filter to all targets */
32
- constructor({ dataAttribute, init, }?: Config);
33
- /** The filter name list */
34
- get filterNames(): FilterName[];
35
- /**
36
- * Add/Set filter
37
- * @param {FilterName} name - the Filter name
38
- * @param {FilterSetting} setting - the Filter setting
39
- */
40
- setFilter(name: FilterName, setting: FilterSetting): void;
41
- /**
42
- * Remove filter
43
- * @param {FilterName} name - the Filter name
44
- */
45
- removeFilter(name: FilterName): boolean;
46
- /**
47
- * Get setting of filter
48
- * @param {FilterName} [name=''] - The filter name
49
- */
50
- getFilterSetting(name?: FilterName): FilterSetting | void;
51
- /**
52
- * Get the CSS inline style of filter
53
- * @param {FilterName} [name=''] - The filter name
54
- */
55
- getFilterStyle(name?: FilterName): string;
56
- /**
57
- * Apply CSS filter to all targets
58
- * @param {string} [selectors='img[data-${this._dataAttribute}]'] - selectors
59
- */
60
- applyFilter(selectors?: string): void;
61
- /**
62
- * Create canvas of image element
63
- * @param {HTMLImageElement} image
64
- */
65
- protected _getImageCanvas(image: HTMLImageElement): Promise<HTMLCanvasElement>;
66
- /**
67
- * Get the data URL of image element
68
- * @param {HTMLImageElement} image - image element
69
- * @param {Options} [options={}] - options
70
- */
71
- getDataURL(image: HTMLImageElement, { type, quality }?: Options): Promise<string>;
72
- /**
73
- * Get the blob of image element
74
- * @param {HTMLImageElement} image - image element
75
- * @param {Options} [options={}] - options
76
- */
77
- getBlob(image: HTMLImageElement, { type, quality }?: Options): Promise<Blob | null>;
78
- }
79
- export default CCGram;
@@ -1,12 +0,0 @@
1
- import { FilterSetting } from './filters';
2
- /**
3
- * Parse setting to style
4
- * @param {FilterSetting} setting - The filter setting
5
- */
6
- export declare const parseSettingToStyle: (setting?: FilterSetting | undefined) => string;
7
- /**
8
- * Create filter image canvas
9
- * @param {HTMLImageElement} image - The image
10
- * @param {string} filterStyle - The filter style
11
- */
12
- export declare const createFilterImageCanvas: (image: HTMLImageElement, filterStyle: string) => Promise<HTMLCanvasElement>;