ui_migration bugfix(component) Added setStreamId prop, img v-show and one pixel fallback to drop jpeg stream connection

This commit is contained in:
Antonio Anaya 2026-06-02 06:38:11 -06:00
parent 702d18552d
commit 60f79a41d8
4 changed files with 61 additions and 14 deletions

View file

@ -5,7 +5,7 @@
<paneControl /> <paneControl />
</div> </div>
<div class="view-component uk-width-expand"> <div class="view-component uk-width-expand">
<streamDisplay /> <streamDisplay :stream-id="setStreamId" />
</div> </div>
</div> </div>
</template> </template>
@ -21,6 +21,12 @@ export default {
paneControl, paneControl,
streamDisplay, streamDisplay,
}, },
data() {
return {
setStreamId: "control",
};
},
}; };
</script> </script>

View file

@ -17,7 +17,7 @@
class="image-fit" class="image-fit"
:src="lastStitchedImage" :src="lastStitchedImage"
/> />
<streamDisplay v-else /> <streamDisplay v-else :stream-id="setStreamId" />
<template #controls> <template #controls>
<slideScanControls /> <slideScanControls />
<label class="uk-form-label" for="form-stacked-text">Sample ID</label> <label class="uk-form-label" for="form-stacked-text">Sample ID</label>
@ -81,6 +81,7 @@ export default {
lastStitchedImage: null, lastStitchedImage: null,
scanComplete: false, scanComplete: false,
scan_name: "", scan_name: "",
setStreamId: "slideScan",
}; };
}, },

View file

@ -5,10 +5,9 @@
class="stream-display uk-width-1-1 uk-height-1-1 scrollTarget" class="stream-display uk-width-1-1 uk-height-1-1 scrollTarget"
> >
<img <img
v-if="isVisible" v-show="isVisible && streamEnabled"
ref="click-frame" ref="click-frame"
class="uk-align-center uk-margin-remove-bottom" class="uk-align-center uk-margin-remove-bottom"
:hidden="!streamEnabled"
:src="streamImgUri" :src="streamImgUri"
alt="Stream" alt="Stream"
@dblclick="clickMonitor" @dblclick="clickMonitor"
@ -36,10 +35,19 @@ import { useIntersectionObserver } from "@vueuse/core";
import { useSettingsStore } from "@/stores/settings.js"; import { useSettingsStore } from "@/stores/settings.js";
import { mapState } from "pinia"; import { mapState } from "pinia";
const ONE_PIXEL_FALLBACK = "data:image/PNG;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=";
// Export main app // Export main app
export default { export default {
name: "StreamDisplay", name: "StreamDisplay",
props: {
streamId: {
type: [String],
required: true,
},
},
setup() { setup() {
const store = useSettingsStore(); const store = useSettingsStore();
return { return {
@ -66,7 +74,13 @@ export default {
return !(this.displaySize[0] == 0) && !(this.displaySize[1] == 0); return !(this.displaySize[0] == 0) && !(this.displaySize[1] == 0);
}, },
streamImgUri: function () { streamImgUri: function () {
return `${this.baseUri}/camera/mjpeg_stream`; // Only request the real stream if it's enabled AND currently visible on screen
if (this.isVisible && this.streamEnabled) {
return `${this.baseUri}/camera/mjpeg_stream`;
}
// Force the browser to kill the connection by loading a 1 pixel image
return ONE_PIXEL_FALLBACK;
}, },
}, },
@ -106,9 +120,14 @@ export default {
}, },
beforeUnmount() { beforeUnmount() {
// Kill the timeout // Kill the resize timeout
clearTimeout(this.resizeTimeoutId); clearTimeout(this.resizeTimeoutId);
// Kill the flash animation timeout (Fixes Beth's MR comment)
if (this.flashTimeoutId) {
clearTimeout(this.flashTimeoutId);
}
// Kill the Intersection Observer // Kill the Intersection Observer
if (this.stopIntersectionObserver) { if (this.stopIntersectionObserver) {
this.stopIntersectionObserver(); this.stopIntersectionObserver();
@ -120,7 +139,7 @@ export default {
// Disconnect the size Observer // Disconnect the size Observer
if (this.sizeObserver) { if (this.sizeObserver) {
this.sizeObserver.disconnect(); this.sizeObserver.disconnect();
this.store.removeStream(this.$.uid); this.store.removeStream(this.streamId);
} }
}, },
@ -128,11 +147,26 @@ export default {
flashStream: function () { flashStream: function () {
// Run an animation that flashes the stream (for capture feedback) // Run an animation that flashes the stream (for capture feedback)
let element = this.$refs.streamDisplay; let element = this.$refs.streamDisplay;
// Defensive check in case the ref isn't available
if (!element) return;
element.classList.remove("uk-animation-fade"); element.classList.remove("uk-animation-fade");
element.offsetHeight; /* trigger reflow */ /* trigger reflow */
element.offsetHeight;
element.classList.add("uk-animation-fade"); element.classList.add("uk-animation-fade");
setTimeout(function () {
element.classList.remove("uk-animation-fade"); // Clear the timer if flashStream is called again before the 800ms is up
if (this.flashTimeoutId) {
clearTimeout(this.flashTimeoutId);
}
// Track the timeout ID
this.flashTimeoutId = setTimeout(() => {
// Extra defensive check just in case
if (element && element.classList) {
element.classList.remove("uk-animation-fade");
}
}, 800); }, 800);
}, },
@ -174,12 +208,12 @@ export default {
// Handle closed stream // Handle closed stream
if (this.displaySize[0] == 0 && this.displaySize[1] == 0) { if (this.displaySize[0] == 0 && this.displaySize[1] == 0) {
// If this stream was previously active // If this stream was previously active
if (this.store.activeStreams[this.$.uid] == true) { if (this.store.activeStreams[this.streamId] == true) {
this.store.removeStream(this.$.uid); this.store.removeStream(this.streamId);
} }
// If resized to anything other than zero // If resized to anything other than zero
} else { } else {
this.store.addStream(this.$.uid); this.store.addStream(this.streamId);
} }
}, },

View file

@ -2,7 +2,7 @@
<!-- Grid managing tab content --> <!-- Grid managing tab content -->
<div uk-grid class="uk-height-1-1 uk-margin-remove uk-padding-remove"> <div uk-grid class="uk-height-1-1 uk-margin-remove uk-padding-remove">
<div class="view-component uk-width-expand"> <div class="view-component uk-width-expand">
<streamDisplay /> <streamDisplay :stream-id="setStreamId" />
</div> </div>
</div> </div>
</template> </template>
@ -16,5 +16,11 @@ export default {
components: { components: {
streamDisplay, streamDisplay,
}, },
data() {
return {
setStreamId: "view",
};
},
}; };
</script> </script>