Browse Source

chore(web): fade between thumbhash and thumbnail (#2856)

Thomas 2 years ago
parent
commit
df74111427
3 changed files with 106 additions and 83 deletions
  1. 82 49
      web/package-lock.json
  2. 2 1
      web/package.json
  3. 22 33
      web/src/lib/components/assets/thumbnail/image-thumbnail.svelte

+ 82 - 49
web/package-lock.json

@@ -10,6 +10,7 @@
 			"dependencies": {
 				"@zoom-image/svelte": "^0.1.0",
 				"axios": "^0.27.2",
+				"buffer": "^6.0.3",
 				"copy-image-clipboard": "^2.1.2",
 				"handlebars": "^4.7.7",
 				"justified-layout": "^4.1.0",
@@ -21,7 +22,7 @@
 				"socket.io-client": "^4.6.1",
 				"svelte-local-storage-store": "^0.5.0",
 				"svelte-material-icons": "^3.0.4",
-				"unlazy": "^0.8.9"
+				"thumbhash": "^0.1.1"
 			},
 			"devDependencies": {
 				"@babel/preset-env": "^7.20.2",
@@ -4135,15 +4136,6 @@
 				"url": "https://opencollective.com/typescript-eslint"
 			}
 		},
-		"node_modules/@unlazy/core": {
-			"version": "0.8.9",
-			"resolved": "https://registry.npmjs.org/@unlazy/core/-/core-0.8.9.tgz",
-			"integrity": "sha512-DQ4WB/cuEWTknU/59uRwpSipvMAJzBDmRyaHDUc1RcXi0Z7/Vcl0EE7BpROxEynqd1EI+2oMWQaDLyXffUdUiA==",
-			"dependencies": {
-				"fast-blurhash": "^1.1.2",
-				"thumbhash": "^0.1.1"
-			}
-		},
 		"node_modules/@zoom-image/core": {
 			"version": "0.18.2",
 			"resolved": "https://registry.npmjs.org/@zoom-image/core/-/core-0.18.2.tgz",
@@ -4595,6 +4587,25 @@
 			"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
 			"dev": true
 		},
+		"node_modules/base64-js": {
+			"version": "1.5.1",
+			"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+			"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+			"funding": [
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/feross"
+				},
+				{
+					"type": "patreon",
+					"url": "https://www.patreon.com/feross"
+				},
+				{
+					"type": "consulting",
+					"url": "https://feross.org/support"
+				}
+			]
+		},
 		"node_modules/binary-extensions": {
 			"version": "2.2.0",
 			"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
@@ -4663,6 +4674,29 @@
 				"node-int64": "^0.4.0"
 			}
 		},
+		"node_modules/buffer": {
+			"version": "6.0.3",
+			"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
+			"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+			"funding": [
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/feross"
+				},
+				{
+					"type": "patreon",
+					"url": "https://www.patreon.com/feross"
+				},
+				{
+					"type": "consulting",
+					"url": "https://feross.org/support"
+				}
+			],
+			"dependencies": {
+				"base64-js": "^1.3.1",
+				"ieee754": "^1.2.1"
+			}
+		},
 		"node_modules/buffer-crc32": {
 			"version": "0.2.13",
 			"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
@@ -5955,11 +5989,6 @@
 				"node": ">= 14"
 			}
 		},
-		"node_modules/fast-blurhash": {
-			"version": "1.1.2",
-			"resolved": "https://registry.npmjs.org/fast-blurhash/-/fast-blurhash-1.1.2.tgz",
-			"integrity": "sha512-lJVOgYSlahqkRhrKumNx/SGB2F/qS0D1z7xjGYjb5EZJRtlzySGMniZjkQ9h9Rv8sPmM/V9orEgRiMwazDNH6A=="
-		},
 		"node_modules/fast-deep-equal": {
 			"version": "3.1.3",
 			"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -6518,6 +6547,25 @@
 				"node": ">=4"
 			}
 		},
+		"node_modules/ieee754": {
+			"version": "1.2.1",
+			"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+			"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+			"funding": [
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/feross"
+				},
+				{
+					"type": "patreon",
+					"url": "https://www.patreon.com/feross"
+				},
+				{
+					"type": "consulting",
+					"url": "https://feross.org/support"
+				}
+			]
+		},
 		"node_modules/ignore": {
 			"version": "5.2.4",
 			"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
@@ -11461,18 +11509,6 @@
 				"node": ">= 4.0.0"
 			}
 		},
-		"node_modules/unlazy": {
-			"version": "0.8.9",
-			"resolved": "https://registry.npmjs.org/unlazy/-/unlazy-0.8.9.tgz",
-			"integrity": "sha512-lRCuXN20N1esqSQqtSVBLAw9GJz0lcBuOBs3UGGw7cFWHQlWJVZZ3OviwOl42f1CnVHjAON1rs2hIdJWgMAUyg==",
-			"dependencies": {
-				"@unlazy/core": "0.8.9"
-			},
-			"peerDependencies": {
-				"fast-blurhash": "^1.1.2",
-				"thumbhash": "^0.1.1"
-			}
-		},
 		"node_modules/update-browserslist-db": {
 			"version": "1.0.10",
 			"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz",
@@ -14771,15 +14807,6 @@
 				"eslint-visitor-keys": "^3.3.0"
 			}
 		},
-		"@unlazy/core": {
-			"version": "0.8.9",
-			"resolved": "https://registry.npmjs.org/@unlazy/core/-/core-0.8.9.tgz",
-			"integrity": "sha512-DQ4WB/cuEWTknU/59uRwpSipvMAJzBDmRyaHDUc1RcXi0Z7/Vcl0EE7BpROxEynqd1EI+2oMWQaDLyXffUdUiA==",
-			"requires": {
-				"fast-blurhash": "^1.1.2",
-				"thumbhash": "^0.1.1"
-			}
-		},
 		"@zoom-image/core": {
 			"version": "0.18.2",
 			"resolved": "https://registry.npmjs.org/@zoom-image/core/-/core-0.18.2.tgz",
@@ -15110,6 +15137,11 @@
 			"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
 			"dev": true
 		},
+		"base64-js": {
+			"version": "1.5.1",
+			"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+			"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
+		},
 		"binary-extensions": {
 			"version": "2.2.0",
 			"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
@@ -15156,6 +15188,15 @@
 				"node-int64": "^0.4.0"
 			}
 		},
+		"buffer": {
+			"version": "6.0.3",
+			"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
+			"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+			"requires": {
+				"base64-js": "^1.3.1",
+				"ieee754": "^1.2.1"
+			}
+		},
 		"buffer-crc32": {
 			"version": "0.2.13",
 			"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
@@ -16094,11 +16135,6 @@
 				"source-map-support": "^0.5.21"
 			}
 		},
-		"fast-blurhash": {
-			"version": "1.1.2",
-			"resolved": "https://registry.npmjs.org/fast-blurhash/-/fast-blurhash-1.1.2.tgz",
-			"integrity": "sha512-lJVOgYSlahqkRhrKumNx/SGB2F/qS0D1z7xjGYjb5EZJRtlzySGMniZjkQ9h9Rv8sPmM/V9orEgRiMwazDNH6A=="
-		},
 		"fast-deep-equal": {
 			"version": "3.1.3",
 			"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -16517,6 +16553,11 @@
 				"harmony-reflect": "^1.4.6"
 			}
 		},
+		"ieee754": {
+			"version": "1.2.1",
+			"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+			"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
+		},
 		"ignore": {
 			"version": "5.2.4",
 			"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
@@ -20074,14 +20115,6 @@
 			"integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
 			"dev": true
 		},
-		"unlazy": {
-			"version": "0.8.9",
-			"resolved": "https://registry.npmjs.org/unlazy/-/unlazy-0.8.9.tgz",
-			"integrity": "sha512-lRCuXN20N1esqSQqtSVBLAw9GJz0lcBuOBs3UGGw7cFWHQlWJVZZ3OviwOl42f1CnVHjAON1rs2hIdJWgMAUyg==",
-			"requires": {
-				"@unlazy/core": "0.8.9"
-			}
-		},
 		"update-browserslist-db": {
 			"version": "1.0.10",
 			"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz",

+ 2 - 1
web/package.json

@@ -60,6 +60,7 @@
 	"dependencies": {
 		"@zoom-image/svelte": "^0.1.0",
 		"axios": "^0.27.2",
+		"buffer": "^6.0.3",
 		"copy-image-clipboard": "^2.1.2",
 		"handlebars": "^4.7.7",
 		"justified-layout": "^4.1.0",
@@ -71,6 +72,6 @@
 		"socket.io-client": "^4.6.1",
 		"svelte-local-storage-store": "^0.5.0",
 		"svelte-material-icons": "^3.0.4",
-		"unlazy": "^0.8.9"
+		"thumbhash": "^0.1.1"
 	}
 }

+ 22 - 33
web/src/lib/components/assets/thumbnail/image-thumbnail.svelte

@@ -1,7 +1,8 @@
 <script lang="ts">
-	import { onMount } from 'svelte';
-	import { lazyLoad } from 'unlazy';
 	import { imageLoad } from '$lib/utils/image-load';
+	import { fade } from 'svelte/transition';
+	import { thumbHashToDataURL } from 'thumbhash';
+	import { Buffer } from 'buffer';
 
 	export let url: string;
 	export let altText: string;
@@ -11,48 +12,36 @@
 	export let curve = false;
 	export let shadow = false;
 	export let circle = false;
-	let loading = true;
 
-	let imageElement: HTMLImageElement;
-
-	onMount(() => {
-		if (thumbhash) {
-			lazyLoad(imageElement, {
-				hash: thumbhash,
-				hashType: 'thumbhash'
-			});
-		}
-	});
+	let complete = false;
 </script>
 
-{#if thumbhash}
-	<img
-		style:width={widthStyle}
-		style:height={heightStyle}
-		data-src={url}
-		alt={altText}
-		class="object-cover"
-		class:rounded-lg={curve}
-		class:shadow-lg={shadow}
-		class:rounded-full={circle}
-		draggable="false"
-		bind:this={imageElement}
-	/>
+<img
+	style:width={widthStyle}
+	style:height={heightStyle}
+	src={url}
+	alt={altText}
+	class="object-cover transition-opacity duration-300"
+	class:rounded-lg={curve}
+	class:shadow-lg={shadow}
+	class:rounded-full={circle}
+	class:opacity-0={!thumbhash && !complete}
+	draggable="false"
+	use:imageLoad
+	on:image-load|once={() => (complete = true)}
+/>
 
-	<!-- not everthing yet has thumbhash support so the old method is kept -->
-{:else}
+{#if thumbhash && !complete}
 	<img
 		style:width={widthStyle}
 		style:height={heightStyle}
-		src={url}
+		src={thumbHashToDataURL(Buffer.from(thumbhash, 'base64'))}
 		alt={altText}
-		class="object-cover transition-opacity duration-300"
+		class="absolute object-cover top-0"
 		class:rounded-lg={curve}
 		class:shadow-lg={shadow}
 		class:rounded-full={circle}
-		class:opacity-0={loading}
 		draggable="false"
-		use:imageLoad
-		on:image-load|once={() => (loading = false)}
+		out:fade={{ duration: 300 }}
 	/>
 {/if}