├── .prettierignore ├── .gitignore ├── tsdx.config.js ├── test └── blah.test.ts ├── markdown.config.js ├── html.js ├── tsconfig.json ├── index.html ├── .github └── workflows │ └── main.yml ├── LICENSE ├── package.json ├── src └── index.ts └── README.md /.prettierignore: -------------------------------------------------------------------------------- 1 | *.md -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | .DS_Store 3 | node_modules 4 | dist 5 | -------------------------------------------------------------------------------- /tsdx.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | rollup(config, options) { 3 | config.external = id => false 4 | return config 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /test/blah.test.ts: -------------------------------------------------------------------------------- 1 | import { sum } from '../src'; 2 | 3 | describe('blah', () => { 4 | it('works', () => { 5 | expect(sum(1, 1)).toEqual(2); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /markdown.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | transforms: { 3 | SETUP(content, options) { 4 | return [ 5 | '', 6 | '```html', 7 | require('child_process') 8 | .execSync(`node html.js`) 9 | .toString() 10 | .trim(), 11 | '```', 12 | '', 13 | ].join('\n') 14 | }, 15 | }, 16 | } 17 | -------------------------------------------------------------------------------- /html.js: -------------------------------------------------------------------------------- 1 | require('make-promises-safe') 2 | 3 | const { minify } = require('terser') 4 | 5 | async function main() { 6 | const result = await minify( 7 | require('fs').readFileSync( 8 | 'dist/dtinthblurhashimage.umd.production.min.js', 9 | 'utf8' 10 | ), 11 | { format: { semicolons: false } } 12 | ) 13 | const header = [ 14 | require('./package.json').name, 15 | require('./package.json').version, 16 | ].join('@') 17 | console.log(``) 18 | } 19 | 20 | main() 21 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src", "types"], 3 | "compilerOptions": { 4 | "module": "esnext", 5 | "target": "es6", 6 | "lib": ["dom", "esnext"], 7 | "importHelpers": true, 8 | "declaration": true, 9 | "sourceMap": true, 10 | "rootDir": "./src", 11 | "strict": true, 12 | "noUnusedLocals": true, 13 | "noUnusedParameters": true, 14 | "noImplicitReturns": true, 15 | "noFallthroughCasesInSwitch": true, 16 | "moduleResolution": "node", 17 | "jsx": "react", 18 | "esModuleInterop": true 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | blurhash-image demo 7 | 16 | 17 | 18 |

<blurhash-image> demo

19 |
20 | 21 |
22 |

23 |     
26 |     
27 |   
28 | 
29 | 


--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
 1 | name: CI
 2 | on: [push]
 3 | jobs:
 4 |   build:
 5 |     runs-on: ubuntu-latest
 6 | 
 7 |     steps:
 8 |       - name: Begin CI...
 9 |         uses: actions/checkout@v2
10 | 
11 |       - name: Use Node 12
12 |         uses: actions/setup-node@v1
13 |         with:
14 |           node-version: 12.x
15 | 
16 |       - name: Use cached node_modules
17 |         uses: actions/cache@v2
18 |         with:
19 |           path: node_modules
20 |           key: nodeModules-${{ hashFiles('**/yarn.lock') }}
21 |           restore-keys: |
22 |             nodeModules-
23 | 
24 |       - name: Install dependencies
25 |         run: yarn install --frozen-lockfile
26 |         env:
27 |           CI: true
28 | 
29 |       - name: Lint
30 |         run: yarn lint
31 |         env:
32 |           CI: true
33 | 
34 |       - name: Test
35 |         run: yarn test --ci --coverage --maxWorkers=2
36 |         env:
37 |           CI: true
38 | 
39 |       - name: Build
40 |         run: yarn build
41 |         env:
42 |           CI: true
43 | 


--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
 1 | MIT License
 2 | 
 3 | Copyright (c) 2020 dtinth on Cloud9
 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.


--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "version": "0.1.0",
 3 |   "license": "MIT",
 4 |   "main": "dist/index.js",
 5 |   "typings": "dist/index.d.ts",
 6 |   "files": [
 7 |     "dist",
 8 |     "src"
 9 |   ],
10 |   "engines": {
11 |     "node": ">=10"
12 |   },
13 |   "scripts": {
14 |     "start": "tsdx watch",
15 |     "build": "tsdx build --format=umd --name=dtinthBlurhashImage",
16 |     "test": "tsdx test",
17 |     "lint": "tsdx lint",
18 |     "prepare": "yarn build && yarn md-magic"
19 |   },
20 |   "peerDependencies": {},
21 |   "husky": {
22 |     "hooks": {
23 |       "pre-commit": "tsdx lint"
24 |     }
25 |   },
26 |   "prettier": {
27 |     "printWidth": 80,
28 |     "semi": false,
29 |     "singleQuote": true,
30 |     "trailingComma": "es5"
31 |   },
32 |   "name": "@dtinth/blurhash-image",
33 |   "author": "dtinth",
34 |   "module": "dist/dtinthblurhashimage.umd.production.min.js",
35 |   "devDependencies": {
36 |     "husky": "^4.3.0",
37 |     "make-promises-safe": "^5.1.0",
38 |     "markdown-magic": "^1.0.0",
39 |     "terser": "^5.3.2",
40 |     "tsdx": "^0.13.3",
41 |     "tslib": "^2.0.1",
42 |     "typescript": "^4.0.3"
43 |   },
44 |   "dependencies": {
45 |     "blurhash": "^1.1.3"
46 |   },
47 |   "browserslist": "since 2018 and not dead"
48 | }
49 | 


--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
 1 | import { decode } from 'blurhash'
 2 | 
 3 | export class BlurhashToDataUrlConverter {
 4 |   canvas: HTMLCanvasElement | null = null
 5 |   cache: { [blurhash: string]: string } = {}
 6 |   constructor(public size = 16) {}
 7 | 
 8 |   async toDataURL(blurhash: string) {
 9 |     if (this.cache[blurhash]) {
10 |       return this.cache[blurhash]
11 |     }
12 |     if (!this.canvas) {
13 |       this.canvas = document.createElement('canvas')
14 |       this.canvas.width = this.size
15 |       this.canvas.height = this.size
16 |     }
17 |     const ctx = this.canvas.getContext('2d')!
18 |     const pixels = decode(blurhash, this.size, this.size)
19 |     const imageData = ctx.createImageData(this.size, this.size)
20 |     imageData.data.set(pixels)
21 |     ctx.putImageData(imageData, 0, 0)
22 |     const url = this.canvas.toDataURL(blurhash)
23 |     this.cache[blurhash] = url
24 |     return url
25 |   }
26 | }
27 | 
28 | export const defaultConverter = new BlurhashToDataUrlConverter()
29 | 
30 | export class BlurhashImage extends HTMLElement {
31 |   converter = defaultConverter
32 |   constructor() {
33 |     super()
34 |   }
35 |   async connectedCallback() {
36 |     const blurhash: string = this.getAttribute('blurhash')!
37 |     try {
38 |       this.style.backgroundImage = `url("${await this.converter.toDataURL(
39 |         blurhash
40 |       )}")`
41 |     } catch (error) {
42 |       console.warn('Cannot decode blurhash', blurhash, error)
43 |     }
44 |   }
45 | }
46 | 
47 | customElements.define('blurhash-image', BlurhashImage)
48 | 


--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
  1 | # <blurhash-image>
  2 | 
  3 | HTML custom element for displaying image with [BlurHash](https://blurha.sh/).
  4 | 
  5 | At 2.7 KB (minified and gzipped), this library is designed to be inlined into the HTML page where a BlurHash image is displayed.
  6 | 
  7 | 
  8 | - [Usage](#usage)
  9 | 
 10 | 
 11 | ## Usage
 12 | 
 13 | Set up some styling:
 14 | 
 15 | ```html
 16 | 
 28 | ```
 29 | 
 30 | Inline the script into the web page:
 31 | 
 32 | 
 33 | 
 34 | ```html
 35 | 
 93 | ```
 94 | 
 95 | 
 96 | 
 97 | Put in the image:
 98 | 
 99 | ```html
100 | 
101 | ```
102 | 


--------------------------------------------------------------------------------