├── .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 |
--------------------------------------------------------------------------------