diff --git a/packages/frontend/src/main/components/common/PreviewImage.vue b/packages/frontend/src/main/components/common/PreviewImage.vue index 8ef3f83ba..389f92c90 100644 --- a/packages/frontend/src/main/components/common/PreviewImage.vue +++ b/packages/frontend/src/main/components/common/PreviewImage.vue @@ -3,22 +3,35 @@ :style="`position: relative; height: ${height}px;`" :class="`${$vuetify.theme.dark ? 'grey darken-4' : 'grey lighten-4'}`" @mouseenter="hovered = true" - @touchmove="parseTouch" - @mouseleave=" - hovered = false - imageIndex = 0 - " + @mouseleave="hovered = false" @mousemove="setIndex" + @touchmove=" + (e) => + setIndex({ + target: e.target, + clientX: e.touches[0].clientX, + clientY: e.touches[0].clientY + }) + " > - -
+ +
+
+ +
+ @@ -36,7 +49,7 @@ export default { }, height: { type: Number, - default: 180 + default: 200 }, rotate: { type: Boolean, @@ -45,68 +58,97 @@ export default { }, data() { return { + isMounted: false, loading: false, hovered: false, hasStartedLoadingImages: false, - currentPreviewImg: '', - previewImages: [], + previewImage: null, + fullPreviewImage: null, imageIndex: 0, legacyMode: false, - angles: [ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, - 22, 23, 0 - ] + controller: new AbortController() } }, computed: { - revImg() { - return this.legacyMode ? this.previewImages : [...this.previewImages].reverse() + background() { + return ` + position: absolute; + top:0; + height: 100%; + width: 100%; + background-size: cover; + background-image:url(${this.previewImage}); + background-position: center center; + ` }, - bgStyle() { - let bgStyle = ` + background360() { + let background = ` + position: absolute; + opacity: ${!this.legacyMode && this.hovered && this.fullPreviewImage ? 1 : 0}; + transition: opacity 0.5s; + top:0; height: 100%; width: 100%; background-size: cover; background-image:` + if (this.fullPreviewImage && (this.hovered || true)) { + background += `url(${this.fullPreviewImage});` + } else { + background += `url(${this.previewImage});` + } + if (!this.isMounted) return background + const scaleFactor = this.$refs.image_360.getBoundingClientRect().height / 400 + const actualWidth = scaleFactor * 700 + const widthDiff = + (this.$refs.image_360.getBoundingClientRect().width - actualWidth) * 0.5 - for (let i = 0; i < this.revImg.length; i++) { - bgStyle += `url(${this.revImg[i]})${i !== this.revImg.length - 1 ? ',' : ';'}` + if (this.fullPreviewImage && (this.hovered || true)) { + background += `background-position: ${-( + actualWidth * (2 * this.imageIndex + 1) - + widthDiff + )}px 0;` + } else { + background += `background-position: center center;` } - bgStyle += ` - background-position:` - for (let i = 0; i < this.revImg.length; i++) { - bgStyle += `${i === this.imageIndex ? 'center' : '10000px'}${ - i !== this.revImg.length - 1 ? ',' : ';' - }` - } - return bgStyle + return background } }, watch: { hovered(val) { - if (!this.rotate) return - if (!val || this.hasStartedLoadingImages) return - this.getOtherAngles() - this.hasStartedLoadingImages = true + if (val && !this.fullPreviewImage) { + setTimeout(async () => { + if (!this.hovered) return + if (this.legacyMode) return + if (this.fullPreviewImage) return + this.loading = true + try { + this.fullPreviewImage = await this.getPreviewImage('all') + } catch (err) { + if (err.toString() === 'Failed getting preview') { + this.legacyMode = true + } + // else: we've aborted the request due to mouse moving out + } + this.loading = false + }, 500) + } + + if (!val && this.loading) { + this.controller.abort() + this.controller = new AbortController() + this.loading = false + } + if (!val) { + this.imageIndex = 0 + } } }, async mounted() { - this.previewImages.push(await this.getPreviewImage()) + this.previewImage = await this.getPreviewImage() + this.isMounted = true }, methods: { - parseTouch(e) { - if (this.loading || !this.rotate) { - this.imageIndex = 0 - return - } - this.hovered = true - this.setIndex({ - target: e.target, - clientX: e.touches[0].clientX, - clientY: e.touches[0].clientY - }) - }, setIndex(e) { if (this.loading || !this.rotate) { this.imageIndex = 0 @@ -114,13 +156,14 @@ export default { } const rect = e.target.getBoundingClientRect() const x = e.clientX - rect.left - const step = rect.width / this.previewImages.length + const step = rect.width / 24 let index = Math.round(x / step) - if (index >= this.previewImages.length) index = this.previewImages.length - 1 + if (index >= 24) index = 24 - 1 this.imageIndex = index }, async getPreviewImage(angle = 0) { const res = await fetch(this.url + `/${angle}`, { + signal: this.controller.signal, headers: localStorage.getItem('AuthToken') ? { Authorization: `Bearer ${localStorage.getItem('AuthToken')}` } : {} @@ -131,44 +174,6 @@ export default { } const blob = await res.blob() return URL.createObjectURL(blob) - }, - async getOtherAngles() { - // Note: previously, previews were generated for only 5 angles (-30deg, -15, 0, 15, 30deg), corresponding to - // labelled angles (-2, -1, 0, 1, 2). We have now switched to generating full 360deg previews in increments - // of 15 deg, going clockwise straight. Eg., 0 = 0deg, 1 = 15deg, 2 = 30 deg, etc. - this.loading = true - - try { - const img = await this.getPreviewImage(this.angles[this.angles.length - 1]) // note: this throws if requesting an incorrect angle. - this.$set(this.previewImages, this.angles.length - 1, img) - const promises = [] - - for (let i = 1; i < this.angles.length; i++) { - promises.push(this.getPreviewImage(this.angles[i])) - } - - const otherImgs = await Promise.all(promises) - for (let i = 0; i < otherImgs.length; i++) { - this.$set(this.previewImages, i + 1, otherImgs[i]) - } - } catch (e) { - // legacy track - this.legacyMode = true - const otherImgs = await Promise.all([ - this.getPreviewImage(-1), - this.getPreviewImage(-2), - this.getPreviewImage(1), - this.getPreviewImage(2) - ]) - this.previewImages.unshift(otherImgs[0]) - this.previewImages.unshift(otherImgs[1]) - this.previewImages.push(otherImgs[2]) - this.previewImages.push(otherImgs[3]) - // TODO: Weird. getPreviewImage throws, but the try block keeps going until the for loop, - // resulting in an incorrect array; we need to filter out nulls/undefineds here... - this.previewImages = this.previewImages.filter((i) => !!i) - } - this.loading = false } } } @@ -178,25 +183,4 @@ export default { transition: all 0.3s; filter: grayscale(100%); } - -.preview-img { - width: 100%; - opacity: 0.8; - object-fit: cover; - transition: all 0.2s ease; -} - -.preview-img:hover { - opacity: 1; -} - -.stream-link a { - /* color: inherit; */ - text-decoration: none; - font-weight: 500; -} - -.stream-link a:hover { - text-decoration: underline; -}