feat: Use blurhash as preview

Signed-off-by: Louis Chemineau <louis@chmn.me>
This commit is contained in:
Louis Chemineau 2024-03-07 11:51:06 +01:00
parent 3b43678533
commit 12f6e25a7d
No known key found for this signature in database
4 changed files with 45 additions and 32 deletions

6
package-lock.json generated
View File

@ -25,6 +25,7 @@
"@nextcloud/sharing": "^0.1.0",
"@nextcloud/upload": "^1.0.4",
"@nextcloud/vue": "^8.8.1",
"blurhash": "^2.0.5",
"camelcase": "^8.0.0",
"debounce": "^1.2.1",
"he": "^1.2.0",
@ -6935,6 +6936,11 @@
"dev": true,
"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/bn.js": {
"version": "5.2.1",
"license": "MIT"

View File

@ -54,6 +54,7 @@
"@nextcloud/sharing": "^0.1.0",
"@nextcloud/upload": "^1.0.4",
"@nextcloud/vue": "^8.8.1",
"blurhash": "^2.0.5",
"camelcase": "^8.0.0",
"debounce": "^1.2.1",
"he": "^1.2.0",

View File

@ -41,6 +41,8 @@
<!-- Preload large preview for near visible files -->
<!-- Preload small preview for further away files -->
<template v-if="initialized">
<canvas v-if="hasBlurhash && !loadedSmall && !loadedLarge" ref="canvas" class="file__blurhash" />
<img v-if="!loadedLarge && (loadedSmall || (distance < 5 && !errorSmall))"
ref="imgSmall"
:key="`${file.basename}-small`"
@ -81,11 +83,12 @@
<script>
import VideoIcon from 'vue-material-design-icons/Video.vue'
import PlayCircleIcon from 'vue-material-design-icons/PlayCircle.vue'
import FavoriteIcon from './FavoriteIcon.vue'
import { decode } from 'blurhash'
import { generateUrl } from '@nextcloud/router'
import { NcCheckboxRadioSwitch } from '@nextcloud/vue'
import FavoriteIcon from './FavoriteIcon.vue'
import { isCachedPreview } from '../services/PreviewService.js'
export default {
@ -155,6 +158,9 @@ export default {
isVisible() {
return this.distance === 0
},
hasBlurhash() {
return this.file.metadataBlurhash !== undefined
},
},
async mounted() {
@ -164,6 +170,10 @@ export default {
])
this.initialized = true
await this.$nextTick() // Wait for next tick to have the canvas in the DOM
this.drawBlurhash()
},
beforeDestroy() {
@ -211,6 +221,21 @@ export default {
return generateUrl(`/apps/photos/api/v1/preview/${this.file.fileid}?etag=${this.decodedEtag}&x=${size}&y=${size}`)
}
},
drawBlurhash() {
if (!this.hasBlurhash || !this.$refs.canvas) {
return
}
const width = this.$refs.canvas.width
const height = this.$refs.canvas.height
const pixels = decode(this.file.metadataBlurhash, width, height)
const ctx = this.$refs.canvas.getContext('2d')
const imageData = ctx.createImageData(width, height)
imageData.data.set(pixels)
ctx.putImageData(imageData, 0, 0)
},
},
}
@ -242,6 +267,7 @@ export default {
outline-offset: -4px;
pointer-events: none;
}
.selection-checkbox {
opacity: 1;
}
@ -254,8 +280,17 @@ export default {
outline: none; // Override global focus state.
display: flex; // Fill parent size
&__blurhash {
position: absolute;
top: 0;
height: 100%;
width: 100%;
object-fit: cover;
}
&__images {
display: contents;
width: 100%;
height: 100%;
.icon-overlay {
position: absolute;
@ -278,36 +313,6 @@ export default {
position: absolute;
color: transparent; /// Hide alt='' text when loading.
}
.loading-overlay {
position: absolute;
height: 100%;
width: 100%;
display: flex;
align-content: center;
align-items: center;
justify-content: center;
svg {
width: 70%;
height: 70%;
}
}
}
&__hidden-description {
position: absolute;
left: -10000px;
top: -10000px;
width: 1px;
height: 1px;
overflow: hidden;
&.show {
position: initial;
width: fit-content;
height: fit-content;
}
}
}

View File

@ -30,6 +30,7 @@ const props = `
<nc:metadata-photos-size />
<nc:metadata-photos-original_date_time />
<nc:metadata-files-live-photo />
<nc:metadata-blurhash/>
<nc:has-preview />
<nc:realpath />
<nc:hidden />