Placeholder for webcam image to avoid moving controls on load

An aspect ratio of 16:9 is assumed, with other ratios showing black
letterbox borders as required.

During loading a text "Webcam loading..." is displayed in the webcam
space. On loading error, the (broken) image is hidden and an error
text is displayed instead.

Solves #478
This commit is contained in:
Gina Häußge 2017-05-08 17:04:03 +02:00
parent f32d7c434d
commit c844217f82
4 changed files with 130 additions and 30 deletions

File diff suppressed because one or more lines are too long

View file

@ -37,6 +37,8 @@ $(function() {
self.additionalControls = []; self.additionalControls = [];
self.webcamDisableTimeout = undefined; self.webcamDisableTimeout = undefined;
self.webcamLoaded = ko.observable(false);
self.webcamError = ko.observable(false);
self.keycontrolActive = ko.observable(false); self.keycontrolActive = ko.observable(false);
self.keycontrolHelpActive = ko.observable(false); self.keycontrolHelpActive = ko.observable(false);
@ -343,42 +345,24 @@ $(function() {
self.requestData(); self.requestData();
}; };
self.updateRotatorWidth = function() {
var webcamImage = $("#webcam_image");
if (self.settings.webcam_rotate90()) {
if (webcamImage.width() > 0) {
$("#webcam_rotator").css("height", webcamImage.width());
} else {
webcamImage.off("load.rotator");
webcamImage.on("load.rotator", function() {
$("#webcam_rotator").css("height", webcamImage.width());
webcamImage.off("load.rotator");
});
}
} else {
$("#webcam_rotator").css("height", "");
}
};
self.onSettingsBeforeSave = self.updateRotatorWidth;
self._isSafari = function() { self._isSafari = function() {
var is_chrome = navigator.userAgent.indexOf('Chrome') > -1; var is_chrome = navigator.userAgent.indexOf('Chrome') > -1;
var is_safari = navigator.userAgent.indexOf("Safari") > -1; var is_safari = navigator.userAgent.indexOf("Safari") > -1;
return is_safari && !is_chrome; return is_safari && !is_chrome;
} };
self._disableWebcam = function() { self._disableWebcam = function() {
// only disable webcam stream if tab is out of focus for more than 5s, otherwise we might cause // only disable webcam stream if tab is out of focus for more than 5s, otherwise we might cause
// more load by the constant connection creation than by the actual webcam stream // more load by the constant connection creation than by the actual webcam stream
// safari bug doesn't release the mjpeg stream, so we just disable this for safari. // safari bug doesn't release the mjpeg stream, so we just disable this for safari.
if (self._isSafari()) { if (self._isSafari()) {
return; return;
} }
self.webcamDisableTimeout = setTimeout(function () { self.webcamDisableTimeout = setTimeout(function () {
$("#webcam_image").attr("src", ""); $("#webcam_image").attr("src", "");
self.webcamLoaded(false);
}, 5000); }, 5000);
}; };
@ -392,12 +376,12 @@ $(function() {
} }
var webcamImage = $("#webcam_image"); var webcamImage = $("#webcam_image");
var currentSrc = webcamImage.attr("src"); var currentSrc = webcamImage.attr("src");
// safari bug doesn't release the mjpeg stream, so we just set it up the once // safari bug doesn't release the mjpeg stream, so we just set it up the once
if (self._isSafari() && currentSrc != undefined) { if (self._isSafari() && currentSrc != undefined) {
return; return;
} }
var newSrc = self.settings.webcam_streamUrl(); var newSrc = self.settings.webcam_streamUrl();
if (currentSrc != newSrc) { if (currentSrc != newSrc) {
if (newSrc.lastIndexOf("?") > -1) { if (newSrc.lastIndexOf("?") > -1) {
@ -407,11 +391,24 @@ $(function() {
} }
newSrc += new Date().getTime(); newSrc += new Date().getTime();
self.updateRotatorWidth(); self.webcamLoaded(false);
self.webcamError(false);
webcamImage.attr("src", newSrc); webcamImage.attr("src", newSrc);
} }
}; };
self.onWebcamLoaded = function() {
log.debug("Webcam stream loaded");
self.webcamLoaded(true);
self.webcamError(false);
};
self.onWebcamErrored = function() {
log.debug("Webcam stream failed to load/disabled");
self.webcamLoaded(false);
self.webcamError(true);
};
self.onTabChange = function (current, previous) { self.onTabChange = function (current, previous) {
if (current == "#control") { if (current == "#control") {
self._enableWebcam(); self._enableWebcam();

View file

@ -491,7 +491,7 @@ ul.dropdown-menu li a {
position: relative; position: relative;
outline: none; outline: none;
//min-height: 440px; background-color: black;
.keycontrol_overlay { .keycontrol_overlay {
position: absolute; position: absolute;
@ -526,6 +526,84 @@ ul.dropdown-menu li a {
float: left; float: left;
} }
} }
.nowebcam {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
.text {
color: white;
text-align: center;
position: relative;
margin: auto;
width: 80%;
top: 50%;
transform: translateY(-50%);
display: block;
&.webcam_loading {
animation: pulsate 3s ease-out;
animation-iteration-count: infinite;
}
}
}
.webcam_rotated {
position: relative;
width: 100%;
padding-bottom: 100%;
.webcam_fixed_ratio {
position: absolute;
transform: rotate(-90deg);
top: 0;
bottom: 0;
.webcam_fixed_ratio_inner {
width: 100%;
height: 100%;
}
}
}
.webcam_unrotated {
.webcam_fixed_ratio {
width: 100%;
padding-bottom: 100%;
&.ratio43 {
padding-bottom: 75%;
}
&.ratio169 {
padding-bottom: 56.25%;
}
&.ratio1610 {
padding-bottom: 62.5%;
}
position: relative;
.webcam_fixed_ratio_inner {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
}
}
img {
width: 100%;
height: 100%;
object-fit: contain;
}
} }
/** State sidebar panel */ /** State sidebar panel */
@ -757,7 +835,7 @@ ul.dropdown-menu li a {
#terminal-sendpanel { #terminal-sendpanel {
text-align: right; text-align: right;
} }
#terminal-output span { #terminal-output span {
display: block; display: block;
} }
@ -871,6 +949,18 @@ textarea.block {
width: 100%; width: 100%;
} }
@keyframes pulsate {
0% {
opacity: 0.5;
}
50% {
opacity: 1.0;
}
100% {
opacity: 0.5;
}
}
#drop_overlay { #drop_overlay {
position: fixed; position: fixed;
top: 0; top: 0;

View file

@ -1,7 +1,20 @@
{% if webcamStream %} {% if webcamStream %}
<div id="webcam_container" tabindex="0" data-bind="event: { keydown: onKeyDown, mouseover: onMouseOver, mouseout: onMouseOut, focus: onFocus }"> <div id="webcam_container" tabindex="0" data-bind="event: { keydown: onKeyDown, mouseover: onMouseOver, mouseout: onMouseOut, focus: onFocus }">
<div id="webcam_rotator" data-bind="css: { rotate90: settings.webcam_rotate90() }"> <div class="nowebcam" data-bind="visible: !webcamLoaded()">
<img id="webcam_image" data-bind="css: { flipH: settings.webcam_flipH(), flipV: settings.webcam_flipV() }"> <div class="text webcam_loading" data-bind="visible: !webcamLoaded() && !webcamError()">
<p><strong>{{ _('Webcam stream loading...') }}</strong></p>
</div>
<div class="text webcam_error" data-bind="visible: !webcamLoaded() && webcamError()">
<p><strong>{{ _('Webcam stream not loaded') }}</strong></p>
<p><small>{{ _('It might not be correctly configured. You can change the URL of the stream under "Settings" > "Webcam & Timelapse" > "Stream URL". If you don\'t have a webcam just set the URL to an empty value.') }}</small></p>
</div>
</div>
<div id="webcam_rotator" data-bind="css: { webcam_rotated: settings.webcam_rotate90(), webcam_unrotated: !settings.webcam_rotate90() }">
<div class="webcam_fixed_ratio ratio169">
<div class="webcam_fixed_ratio_inner">
<img id="webcam_image" data-bind="css: { flipH: settings.webcam_flipH(), flipV: settings.webcam_flipV() }, event: { load: onWebcamLoaded, error: onWebcamErrored }, visible: !webcamError()">
</div>
</div>
</div> </div>
<div class="keycontrol_overlay" data-bind="visible: showKeycontrols"> <div class="keycontrol_overlay" data-bind="visible: showKeycontrols">
<div class="keycontrol_overlay_heading">{{ _("Keyboard controls active") }} <a href="#" data-bind="click: toggleKeycontrolHelp"><i data-bind="css: { 'icon-chevron-down': !keycontrolHelpActive(), 'icon-chevron-up': keycontrolHelpActive() }"></i></a></div> <div class="keycontrol_overlay_heading">{{ _("Keyboard controls active") }} <a href="#" data-bind="click: toggleKeycontrolHelp"><i data-bind="css: { 'icon-chevron-down': !keycontrolHelpActive(), 'icon-chevron-up': keycontrolHelpActive() }"></i></a></div>