diff options
author | Louis Chemineau <louis@chmn.me> | 2024-08-29 14:27:59 +0200 |
---|---|---|
committer | Ferdinand Thiessen <opensource@fthiessen.de> | 2024-08-29 23:08:11 +0200 |
commit | 56e4859201d80d3ea444bc88647d96ad71b59f88 (patch) | |
tree | b307b795be9ad14a471a90fd6794a264ef181faa | |
parent | 19dd32962de7a73ecc934f8e7a5340d09fd24317 (diff) | |
download | nextcloud-server-56e4859201d80d3ea444bc88647d96ad71b59f88.tar.gz nextcloud-server-56e4859201d80d3ea444bc88647d96ad71b59f88.zip |
feat: Use the blurhash in Files
Signed-off-by: Louis Chemineau <louis@chmn.me>
-rw-r--r-- | apps/files/src/components/FileEntry/FileEntryPreview.vue | 64 | ||||
-rw-r--r-- | apps/files/src/components/FilesListVirtual.vue | 15 | ||||
-rw-r--r-- | apps/files/src/init.ts | 1 | ||||
-rw-r--r-- | package-lock.json | 6 | ||||
-rw-r--r-- | package.json | 1 |
5 files changed, 76 insertions, 11 deletions
diff --git a/apps/files/src/components/FileEntry/FileEntryPreview.vue b/apps/files/src/components/FileEntry/FileEntryPreview.vue index 2e43338cf05..6fe7a13a968 100644 --- a/apps/files/src/components/FileEntry/FileEntryPreview.vue +++ b/apps/files/src/components/FileEntry/FileEntryPreview.vue @@ -14,16 +14,22 @@ </template> </template> - <!-- Decorative image, should not be aria documented --> - <img v-else-if="previewUrl && backgroundFailed !== true" - ref="previewImg" - alt="" - class="files-list__row-icon-preview" - :class="{'files-list__row-icon-preview--loaded': backgroundFailed === false}" - loading="lazy" - :src="previewUrl" - @error="onBackgroundError" - @load="backgroundFailed = false"> + <!-- Decorative images, should not be aria documented --> + <span v-else-if="previewUrl" class="files-list__row-icon-preview-container"> + <canvas v-if="hasBlurhash && (backgroundFailed === true || !backgroundLoaded)" + ref="canvas" + class="files-list__row-icon-blurhash" + aria-hidden="true" /> + <img v-if="backgroundFailed !== true" + ref="previewImg" + alt="" + class="files-list__row-icon-preview" + :class="{'files-list__row-icon-preview--loaded': backgroundFailed === false}" + loading="lazy" + :src="previewUrl" + @error="onBackgroundError" + @load="onBackgroundLoad"> + </span> <FileIcon v-else v-once /> @@ -58,6 +64,7 @@ import LinkIcon from 'vue-material-design-icons/Link.vue' import NetworkIcon from 'vue-material-design-icons/Network.vue' import TagIcon from 'vue-material-design-icons/Tag.vue' import PlayCircleIcon from 'vue-material-design-icons/PlayCircle.vue' +import { decode } from 'blurhash' import CollectivesIcon from './CollectivesIcon.vue' import FavoriteIcon from './FavoriteIcon.vue' @@ -107,6 +114,7 @@ export default Vue.extend({ data() { return { backgroundFailed: undefined as boolean | undefined, + backgroundLoaded: false, } }, @@ -206,6 +214,16 @@ export default Vue.extend({ return null }, + + hasBlurhash() { + return this.source.attributes['metadata-blurhash'] !== undefined + }, + }, + + mounted() { + if (this.hasBlurhash && this.$refs.canvas) { + this.drawBlurhash() + } }, methods: { @@ -213,17 +231,43 @@ export default Vue.extend({ reset() { // Reset background state to cancel any ongoing requests this.backgroundFailed = undefined + this.backgroundLoaded = false if (this.$refs.previewImg) { this.$refs.previewImg.src = '' } }, + onBackgroundLoad() { + this.backgroundFailed = false + this.backgroundLoaded = true + }, + onBackgroundError(event) { // Do not fail if we just reset the background if (event.target?.src === '') { return } this.backgroundFailed = true + this.backgroundLoaded = false + }, + + drawBlurhash() { + const canvas = this.$refs.canvas as HTMLCanvasElement + + const width = canvas.width + const height = canvas.height + + const pixels = decode(this.source.attributes['metadata-blurhash'], width, height) + + const ctx = canvas.getContext('2d') + if (ctx === null) { + logger.error('Cannot create context for blurhash canvas') + return + } + + const imageData = ctx.createImageData(width, height) + imageData.data.set(pixels) + ctx.putImageData(imageData, 0, 0) }, t, diff --git a/apps/files/src/components/FilesListVirtual.vue b/apps/files/src/components/FilesListVirtual.vue index 14c846bc727..a791abdb3eb 100644 --- a/apps/files/src/components/FilesListVirtual.vue +++ b/apps/files/src/components/FilesListVirtual.vue @@ -556,11 +556,24 @@ export default defineComponent({ } } - &-preview { + &-preview-container { + position: relative; // Needed for the blurshash to be positioned correctly overflow: hidden; width: var(--icon-preview-size); height: var(--icon-preview-size); border-radius: var(--border-radius); + } + + &-blurhash { + position: absolute; + top: 0; + left: 0; + height: 100%; + width: 100%; + object-fit: cover; + } + + &-preview { // Center and contain the preview object-fit: contain; object-position: center; diff --git a/apps/files/src/init.ts b/apps/files/src/init.ts index db4aec7fa06..0f4ef70b1b2 100644 --- a/apps/files/src/init.ts +++ b/apps/files/src/init.ts @@ -66,5 +66,6 @@ registerPreviewServiceWorker() registerDavProperty('nc:hidden', { nc: 'http://nextcloud.org/ns' }) registerDavProperty('nc:is-mount-root', { nc: 'http://nextcloud.org/ns' }) +registerDavProperty('nc:metadata-blurhash', { nc: 'http://nextcloud.org/ns' }) initLivePhotos() diff --git a/package-lock.json b/package-lock.json index 4764645cffa..429a2ae2ce8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,6 +38,7 @@ "@vueuse/integrations": "^11.0.1", "backbone": "^1.4.1", "blueimp-md5": "^2.19.0", + "blurhash": "^2.0.5", "browserslist-useragent-regexp": "^4.1.1", "camelcase": "^8.0.0", "cancelable-promise": "^4.3.1", @@ -8082,6 +8083,11 @@ "integrity": "sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==", "license": "MIT" }, + "node_modules/blurhash": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/blurhash/-/blurhash-2.0.5.tgz", + "integrity": "sha512-cRygWd7kGBQO3VEhPiTgq4Wc43ctsM+o46urrmPOiuAe+07fzlSB9OJVdpgDL0jPqXUVQ9ht7aq7kxOeJHRK+w==" + }, "node_modules/bmp-js": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz", diff --git a/package.json b/package.json index e9b86b6546a..66b8ce07dc7 100644 --- a/package.json +++ b/package.json @@ -68,6 +68,7 @@ "@vueuse/integrations": "^11.0.1", "backbone": "^1.4.1", "blueimp-md5": "^2.19.0", + "blurhash": "^2.0.5", "browserslist-useragent-regexp": "^4.1.1", "camelcase": "^8.0.0", "cancelable-promise": "^4.3.1", |