about summary refs log tree commit diff
path: root/app/javascript/mastodon/components/blurhash.js
diff options
context:
space:
mode:
authorSasha Sorokin <dafri.nochiterov8@gmail.com>2020-07-09 18:01:30 +0700
committerGitHub <noreply@github.com>2020-07-09 13:01:30 +0200
commit61c07c37317f01c1ab4981826704750fe9937fe7 (patch)
treeabecace8339a034b8615c4a11eabf541c178bdb5 /app/javascript/mastodon/components/blurhash.js
parent5b7a93b02cfa4916bd7a99d876b0e6b567e639c3 (diff)
Replace repetitive blurhash code with component (#14267)
This commit replaces all unnecessarily repeated code for decoding and
embedding blurhash canvases with separate component - <Blurhash>.

Under the hood Blurhash component will use effect dependent on its
props. This gives a few benefits: it will only be re-rendered whenever
the hash or width/height/dummy props update, and will not render if
canvas won't get to the final DOM, because then effect won't fire,
which prevents weird bugs like #14257.
Diffstat (limited to 'app/javascript/mastodon/components/blurhash.js')
-rw-r--r--app/javascript/mastodon/components/blurhash.js61
1 files changed, 61 insertions, 0 deletions
diff --git a/app/javascript/mastodon/components/blurhash.js b/app/javascript/mastodon/components/blurhash.js
new file mode 100644
index 000000000..172f8c2f5
--- /dev/null
+++ b/app/javascript/mastodon/components/blurhash.js
@@ -0,0 +1,61 @@
+// @ts-check
+
+import { decode } from 'blurhash';
+import React, { useRef, useEffect } from 'react';
+import PropTypes from 'prop-types';
+
+/**
+ * @typedef BlurhashPropsBase
+ * @property {string} hash Hash to render
+ * @property {number} width
+ * Width of the blurred region in pixels. Defaults to 32
+ * @property {number} [height]
+ * Height of the blurred region in pixels. Defaults to width
+ * @property {boolean} [dummy]
+ * Whether dummy mode is enabled. If enabled, nothing is rendered
+ * and canvas left untouched
+ */
+
+/** @typedef {JSX.IntrinsicElements['canvas'] & BlurhashPropsBase} BlurhashProps */
+
+/**
+ * Component that is used to render blurred of blurhash string
+ *
+ * @param {BlurhashProps} param1 Props of the component
+ * @returns Canvas which will render blurred region element to embed
+ */
+function Blurhash({
+  hash,
+  width = 32,
+  height = width,
+  dummy = false,
+  ...canvasProps
+}) {
+  const canvasRef = /** @type {import('react').MutableRefObject<HTMLCanvasElement>} */ (useRef());
+
+  useEffect(() => {
+    const { current: canvas } = canvasRef;
+    canvas.width = canvas.width; // resets canvas
+
+    if (dummy) return;
+
+    const pixels = decode(hash, width, height);
+    const ctx = canvas.getContext('2d');
+    const imageData = new ImageData(pixels, width, height);
+
+    ctx.putImageData(imageData, 0, 0);
+  }, [dummy, hash, width, height]);
+
+  return (
+    <canvas {...canvasProps} ref={canvasRef} width={width} height={height} />
+  );
+}
+
+Blurhash.propTypes = {
+  hash: PropTypes.string.isRequired,
+  width: PropTypes.number,
+  height: PropTypes.number,
+  dummy: PropTypes.bool,
+};
+
+export default React.memo(Blurhash);