Skip to content

Commit

Permalink
optimized, memoized, img custom element
Browse files Browse the repository at this point in the history
  • Loading branch information
laurentpayot committed Apr 10, 2023
1 parent 77ba327 commit 42581d3
Show file tree
Hide file tree
Showing 8 changed files with 69 additions and 63 deletions.
40 changes: 17 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

Super lightweight SVG identicon generator. No dependencies.

![minified + brotlied size](https://badgen.net/badgesize/brotli/laurentpayot/minidenticons/main/minidenticons.min.js)
![minified + zipped size](https://badgen.net/badgesize/gzip/laurentpayot/minidenticons/main/minidenticons.min.js)
<sup>(using the custom element)</sup>

![minified + brotlied size](https://badgen.net/badgesize/brotli/laurentpayot/minidenticons/main/no-custom-element.min.js)
![minified + zipped size](https://badgen.net/badgesize/gzip/laurentpayot/minidenticons/main/no-custom-element.min.js)
<sup>(custom element tree-shaken)</sup>
<sup>(using the `identicon()` function only)</sup>

![minified + brotlied size](https://badgen.net/badgesize/brotli/laurentpayot/minidenticons/main/minidenticons.min.js)
![minified + zipped size](https://badgen.net/badgesize/gzip/laurentpayot/minidenticons/main/minidenticons.min.js)
<sup>(using the memoized custom element)</sup>

[![dependencies](https://badgen.net/bundlephobia/dependency-count/minidenticons)](https://bundlephobia.com/package/minidenticons)
[![types](https://badgen.net/npm/types/minidenticons)](https://github.com/laurentpayot/minidenticons/blob/main/index.d.ts)
Expand All @@ -29,20 +29,20 @@ Play with it [here](https://laurentpayot.github.io/minidenticons/).

Minidenticons uses [ES modules](https://jakearchibald.com/2017/es-modules-in-browsers/), now [widely supported](https://caniuse.com/es6-module) in browsers.

### Using the `identicon-svg` custom element
### Using the `identicon-img` custom element

Import the `identicon-svg` custom element from the `minidenticons.min.js` file. This file can be located in a CDN (example below) or copied in any directory of your website (for better performance and to be GDPR compliant, since you don’t have to connect to a third party server).
Import the `identicon-img` custom element from the `minidenticons.min.js` file. This file can be located in a CDN (example below) or copied in any directory of your website (for better performance and to be GDPR compliant, since you don’t have to connect to a third party server).

```html
<script type="module">
import { identiconSvg } from 'https://cdn.jsdelivr.net/npm/minidenticons@2.0.2/minidenticons.min.js'
import { identiconImg } from 'https://cdn.jsdelivr.net/npm/minidenticons@2.0.2/minidenticons.min.js'
</script>
```

Then simply use `identicon-svg` tags with a `username` attribute :joy:
Then simply use `identicon-img` tags with a `username` attribute :joy:

```html
<identicon-svg username="alienHead66">
<identicon-img username="alienHead66">
```

For instance with the `alienHead66` username you will get the following identicon (without the border):
Expand All @@ -59,7 +59,7 @@ For instance with the `alienHead66` username you will get the following identico
By default the color saturation and lightness are set to 50%. But you can change these values with the `saturation` and/or `lightness` attributes, for instance:

```html
<identicon-svg username="alienHead66" saturation="95" lightness="60">
<identicon-img username="alienHead66" saturation="95" lightness="60">
```

Play with [the demo](https://laurentpayot.github.io/minidenticons/) to find a combination of saturation and lightness that matches your website theme colors: light, dark, pastel or whatever :sunglasses:
Expand All @@ -70,7 +70,7 @@ Play with [the demo](https://laurentpayot.github.io/minidenticons/) to find a co

### Using the `identicon` function

Alternatively, instead of `identiconSvg`, you can also import the [`identicon`](#usage) function described in the NodeJS section below and use it to generate SVG strings in your browser.
Alternatively, instead of `identiconImg`, you can also import the [`identicon`](#usage) function described in the NodeJS section below and use it to generate SVG strings in your browser.


## On NodeJS
Expand All @@ -89,7 +89,7 @@ npm install minidenticons
import { identicon } from 'minidenticons'
```

The `identicon-svg` custom element should be tree-shaken from your bundle, for an even smaller size of minidenticons :grin:
The `identicon-img` custom element should be tree-shaken from your bundle, for an even smaller size of minidenticons :grin:

### Usage

Expand All @@ -110,20 +110,14 @@ import { identicon } from 'minidenticons'
import { useMemo } from 'react'

const IdenticonImg = ({ username, saturation, lightness, ...props }) => {
const svgText = useMemo(
() => identicon(username, saturation, lightness),
const svgURI = useMemo(
() => 'data:image/svg+xml;utf8,' + encodeURIComponent(identicon(username, saturation, lightness)),
[username, saturation, lightness]
)
return (
<img
src={`data:image/svg+xml;utf8,${encodeURIComponent(svgText)}`}
alt={username}
{...props}
/>
)
return (<img src={svgURI} {...props} />)
}
```
You can then use your react component like so (all props except `username` are optional):
You can then use your react component with `img` attributes along with minidenticons ones (all props except `username` are optional):

```html
<IdenticonImg username="alienHead66" width="150" saturation="90" lightness="50" />
Expand Down
11 changes: 5 additions & 6 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,15 @@
#username-input {
margin-top: 100px;
}
identicon-svg {
identicon-img {
width: 40px;
height: 40px;
border-radius: 50%;
background-color: #EEF;
display: inline-block;
margin: 5px;
}
main + identicon-svg {
main + identicon-img {
width: 80px;
height: 80px;
display: block;
Expand All @@ -50,7 +50,7 @@
<body>

<script type="module">
import { identiconSvg } from "./minidenticons.min.js"
import { identiconImg } from "./minidenticons.js"

const main = document.querySelector('main')
const usernameInput = main.querySelector('#username-input')
Expand All @@ -63,9 +63,8 @@
saturationLabel.textContent = `Saturation ${saturationInput.value}%`
lightnessLabel.textContent = `Lightness ${lightnessInput.value}%`
main.insertAdjacentHTML("afterend",
`<identicon-svg username="${usernameInput.value}"
title='"${usernameInput.value.replaceAll("'", "&apos;")}",
${saturationInput.value}, ${lightnessInput.value}'
`<identicon-img username="${usernameInput.value}"
title='"${usernameInput.value.replaceAll("'", "&apos;")}", ${saturationInput.value}, ${lightnessInput.value}'
saturation="${saturationInput.value}" lightness="${lightnessInput.value}"
>`
)
Expand Down
40 changes: 27 additions & 13 deletions minidenticons.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ function simpleHash(str) {
/**
* @type {import('.').identicon}
*/
export function identicon(username, saturation=DEFAULT_SATURATION, lightness=DEFAULT_LIGHTNESS) {
export function identicon(username="", saturation=DEFAULT_SATURATION, lightness=DEFAULT_LIGHTNESS) {
const hash = simpleHash(username)
// dividing hash by FNV_PRIME to get last XOR result for better color randomness (will be an integer except for empty string hash)
const hue = ((hash / FNV_PRIME) % COLORS_NB) * (360 / COLORS_NB)
Expand All @@ -40,18 +40,32 @@ export function identicon(username, saturation=DEFAULT_SATURATION, lightness=DEF
/**
* @type {void}
*/
export const identiconSvg =
/*@__PURE__*/globalThis.customElements?.define('identicon-svg',
class extends HTMLElement {
connectedCallback() { this.identiconSvg() }
attributeChangedCallback() { this.identiconSvg() }
static get observedAttributes() { return ['username', 'saturation', 'lightness'] }
identiconSvg() {
this.innerHTML = identicon(
this.getAttribute('username') || "",
this.getAttribute('saturation') || DEFAULT_SATURATION,
this.getAttribute('lightness') || DEFAULT_LIGHTNESS
)
export const identiconImg =
// declared as a pure function to be tree-shaken by the bundler
/*@__PURE__*/globalThis.customElements?.define('identicon-img',
class IdenticonImg extends HTMLElement {
static observedAttributes = ['username', 'saturation', 'lightness']
static memo = {}
isConnected = false
connectedCallback() {
this.identiconImg()
this.isConnected = true
}
// attributeChangedCallback() is called for every observed attribute before connectedCallback()
attributeChangedCallback() { if (this.isConnected) this.identiconImg() }
identiconImg() {
const img = document.createElement('img')
this.getAttributeNames().forEach(key => {
if (!IdenticonImg.observedAttributes.includes(key))
img[key] = this.getAttribute(key)
})
const args = IdenticonImg.observedAttributes
.map(key => this.getAttribute(key) || undefined)
const memoKey = args.join(',')
img.src = IdenticonImg.memo[memoKey] ??=
// @ts-ignore
'data:image/svg+xml;utf8,' + encodeURIComponent(identicon(...args))
this.appendChild(img)
}
}
)
2 changes: 1 addition & 1 deletion minidenticons.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion minidenticons.min.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion no-custom-element.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
},
"scripts": {
"build-js": "terser minidenticons.js --compress module=true --mangle module=true --source-map --output minidenticons.min.js",
"no-custom-element": "cat minidenticons.min.js | sed 's/export const identiconSvg=.*$//' > no-custom-element.min.js",
"no-custom-element": "cat minidenticons.min.js | sed 's/export const identiconImg=.*$//' > no-custom-element.min.js",
"size": "echo \"\n\n\nMinified gzip size:\t`gzip -9cn minidenticons.min.js | wc -c` bytes\nMinified brotli size:\t`brotli -Zcn minidenticons.min.js | wc -c` bytes\n\"",
"size-nce": "echo \"\nWithout custom element:\n\nMinified gzip size:\t`gzip -9cn no-custom-element.min.js | wc -c` bytes\nMinified brotli size:\t`brotli -Zcn no-custom-element.min.js | wc -c` bytes\n\"",
"build": "pnpm build-js && printf '_%.0s' {1..40} && pnpm no-custom-element && pnpm --silent size && pnpm --silent size-nce",
Expand Down
33 changes: 16 additions & 17 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 42581d3

Please sign in to comment.