From 1b04a3f90df2d1f0d3c8a6258b59613840072227 Mon Sep 17 00:00:00 2001 From: Calin Crisan Date: Mon, 30 Nov 2015 20:48:32 +0200 Subject: [PATCH] initial work on better camera frame look and feel --- motioneye/static/css/main.css | 270 ++++++++++++++------ motioneye/static/css/ui.css | 13 +- motioneye/static/img/camera-top-buttons.svg | 174 +++++++++++++ motioneye/static/img/logout.svg | 56 ---- motioneye/static/img/settings.svg | 108 -------- motioneye/static/img/top-bar-buttons.svg | 225 ++++++++-------- motioneye/static/js/main.js | 103 ++++++-- motioneye/templates/main.html | 4 +- 8 files changed, 558 insertions(+), 395 deletions(-) create mode 100644 motioneye/static/img/camera-top-buttons.svg delete mode 100644 motioneye/static/img/logout.svg delete mode 100644 motioneye/static/img/settings.svg diff --git a/motioneye/static/css/main.css b/motioneye/static/css/main.css index 140c889..937fc46 100644 --- a/motioneye/static/css/main.css +++ b/motioneye/static/css/main.css @@ -106,13 +106,12 @@ div.copyright-note { } div.page-container { - transition: all 0.2s linear; - padding: 55px 5px 3em 2%; + transition: all 0.1s linear; + padding: 50px 0.2em 50px 0px; } div.page-container.stretched { margin-left: 40%; - padding-left: 5px; } @@ -121,15 +120,23 @@ div.page-container.stretched { div.button.settings-button { margin: 1px; vertical-align: middle; - background-image: url(../img/settings.svg); + background-image: url(../img/top-bar-buttons.svg); + background-size: cover; width: 48px; height: 48px; + transition: transform 0.1s ease; +} + +div.settings-top-bar.open div.button.settings-button { + transform: rotate(90deg); } div.button.logout-button { margin: 1px; vertical-align: middle; - background-image: url(../img/logout.svg); + background-image: url(../img/top-bar-buttons.svg); + background-size: cover; + background-position: -100% 0px; width: 48px; height: 48px; } @@ -150,10 +157,11 @@ div.button.rem-camera-button { display: none; margin: 1px; vertical-align: middle; - background-image: url(../img/settings.svg); + background-image: url(../img/top-bar-buttons.svg); + background-size: cover; + background-position: -200% 0px; width: 48px; height: 48px; - background-position: -48px 0px; } div.settings-top-bar.open div.button.rem-camera-button { @@ -228,7 +236,7 @@ div.settings { div.settings.open { width: 40%; - min-width: 320px; + min-width: 360px; } body:not(.admin) div.settings { @@ -266,7 +274,7 @@ div.settings-top-bar { div.settings-top-bar.open { background-color: #414141; - min-width: 320px; + min-width: 360px; } body:not(.admin) div.settings-top-bar { @@ -321,9 +329,9 @@ div.settings-item-separator { #cameraSelect { display: none; - padding: 4px 1.5em 4px 4px; + padding: 2px 1.5em 2px 2px; vertical-align: middle; - font-size: 1.1em; + font-size: 1.2em; width: auto; max-width: 35%; } @@ -803,34 +811,48 @@ div.camera-list { text-align: center; } -div.camera-frame, -div.camera-frame-place-holder { +div.camera-frame { + width: 100%; position: relative; - width: 32%; + box-sizing: border-box; text-align: left; background-color: #313131; display: inline-block; - padding: 0px 3px 3px 3px; - border-radius: 3px; - transition: all 0.2s, opacity 0s; - margin: 2px; + padding: 0.2em; + border-radius: 0.1em; + border: 0.2em solid #212121; + border-right-width: 0px; + border-bottom-width: 0px; + transition: all 0.1s, opacity 0s; opacity: 0; vertical-align: top; + cursor: pointer; } -div.camera-frame:only-child, -div.camera-frame-place-holder:only-child { - width: 48%; -} - -div.camera-frame-place-holder { - visibility: hidden; +div.camera-frame:HOVER { + background-color: #414141; } div.camera-frame.motion-detected { background-color: #712727; } +div.page-container.one-column div.camera-frame { + width: 100%; +} + +div.page-container.two-columns div.camera-frame { + width: 50%; +} + +div.page-container.three-columns div.camera-frame { + width: 33%; +} + +div.page-container.four-columns div.camera-frame { + width: 25%; +} + div.modal-container div.camera-frame { width: auto; padding: 0px; @@ -838,62 +860,167 @@ div.modal-container div.camera-frame { background-color: #414141; } -div.camera-frame:HOVER { - background-color: #414141; +div.camera-overlay { + position: absolute; + top: 0.2em; + right: 0.2em; + left: 0.2em; + bottom: 0.2em; + opacity: 0; + transition: opacity 0.2s ease; } -div.camera-frame.motion-detected:HOVER { - background-color: #8B3636; +div.camera-overlay.visible { + opacity: 1; } -div.camera-top-bar { - padding: 3px 0px; - font-size: 20px; - height: 25px; +div.camera-overlay-top, +div.camera-overlay-bottom { + position: absolute; + background-color: rgba(49, 49, 49, 0.5); + left: 0px; + width: 100%; } -div.modal-container div.camera-top-bar { - display: none; +div.camera-frame:HOVER div.camera-overlay-top, +div.camera-frame:HOVER div.camera-overlay-bottom { + background-color: rgba(65, 65, 65, 0.5); } -span.camera-name { - float: left; - line-height: 25px; +div.camera-frame.motion-detected div.camera-overlay-top, +div.camera-frame.motion-detected div.camera-overlay-bottom { + background-color: rgba(113, 39, 39, 0.5); } -div.camera-buttons { - float: right; +div.camera-overlay-top { + top: 0px; + height: 2.5em; + line-height: 2.5em; + white-space: nowrap; } -div.camera-button { +div.camera-name { display: inline-block; - width: 24px; - height: 24px; - background-image: url(../img/top-bar-buttons.svg); + font-size: 1.5em; + white-space: nowrap; + width: 50%; + overflow: hidden; + text-overflow: ellipsis; + vertical-align: top; +} + +span.camera-name { + padding-left: 0.4em; +} + +div.camera-top-buttons { + display: inline-block; + width: 50%; + vertical-align: top; + text-align: right; +} + +div.camera-top-button { + background-image: url(../img/camera-top-buttons.svg); background-size: cover; - margin-left: 3px; - cursor: pointer; - transition: all 0.1s linear; + width: 2em; + height: 2em; + vertical-align: top; + margin-top: 0.25em; +} + +div.camera-top-button:last-child { + margin-right: 0.25em; +} + +div.camera-top-button.full-screen { + background-position: -100% 0px; } -div.camera-button.close { +div.camera-top-button.configure { background-position: 0px 0px; } -div.camera-button.full-screen { +div.camera-top-button.media-pictures { background-position: -200% 0px; } -div.camera-button.configure { +div.camera-top-button.media-movies { + background-position: -300% 0px; +} + +div.camera-overlay-bottom { + bottom: 0px; + white-space: nowrap; + height: 4.5em; +} + +div.camera-info { + display: inline-block; + white-space: nowrap; + width: 33%; + overflow: hidden; + text-overflow: ellipsis; + vertical-align: top; +} + +span.camera-info-name { + display: inline-block; + overflow: hidden; + text-overflow: ellipsis; + width: 50%; + text-align: right; +} + +span.camera-info-value { + display: inline-block; + overflow: hidden; + text-overflow: ellipsis; + width: 48%; + text-align: left; + padding-left: 0.5em; + font-weight: bold; +} + +div.camera-action-buttons { + display: inline-block; + white-space: nowrap; + width: 66%; + vertical-align: top; + text-align: right; +} + +div.camera-action-button { + background-image: url(../img/camera-top-buttons.svg); + background-size: cover; + width: 4em; + height: 4em; + vertical-align: top; + margin: 0.25em; +} + +/* div.camera-action-button:last-child { */ +/* margin-right: 0.25em; */ +/* } */ + +div.camera-action-button.lock { background-position: -100% 0px; } -div.camera-button.media-pictures { - background-position: -300% 0px; +div.camera-action-button.unlock { + background-position: 0px 0px; +} + +div.camera-action-button.light-on { + background-position: -200% 0px; +} + +div.camera-action-button.light-off { + background-position: -200% 0px; } -div.camera-button.media-movies { - background-position: -400% 0px; +div.camera-action-button.alarm { + background-position: -300% 0px; } div.camera-container { @@ -966,12 +1093,12 @@ img.camera-progress { /* smaller screens */ body { - font-size: 17px; + font-size: 16px; } } @media all and (max-width: 1000px) { - /* small screens (mobile devices) */ + /* small screens (mobile devices, including tablets) */ div.page-container.stretched { margin-left: 0px; @@ -1008,38 +1135,23 @@ img.camera-progress { } @media all and (max-width: 400px) { - /* very small screens */ + /* very small screens (older phones) */ body { - font-size: 13px; + font-size: 16px; } - div.camera-button { - background-size: cover; - width: 24px; - height: 24px; + div.settings.open { + min-width: 100%; } -} -@media all and (max-width: 1900px) { - div.camera-frame, - div.camera-frame-place-holder { - width: 48%; + div.settings-top-bar.open { + min-width: 100%; } } @media all and (max-width: 1200px) { - div.page-container { - padding-left: 1%; - } - - div.camera-frame, - div.camera-frame-place-holder { - width: 98%; - } - - div.camera-frame:only-child, - div.camera-frame-place-holder:only-child { - width: 98%; + div.camera-frame { + width: 100% !important; } } diff --git a/motioneye/static/css/ui.css b/motioneye/static/css/ui.css index d8c6a42..64d56ed 100644 --- a/motioneye/static/css/ui.css +++ b/motioneye/static/css/ui.css @@ -372,10 +372,11 @@ div.modal-close-button { position: absolute; top: 0.2em; right: 0.3em; - width: 1.1em; - height: 1.1em; - background-image: url(../img/top-bar-buttons.svg); + width: 1.5em; + height: 1.5em; + background-image: url(../img/camera-top-buttons.svg); background-size: cover; + background-position: -400% 0px; cursor: pointer; } @@ -465,10 +466,4 @@ span.popup-message.error { min-height: 2em; line-height: 2em; } - - div.modal-close-button { - background-size: cover; - width: 24px; - height: 24px; - } } diff --git a/motioneye/static/img/camera-top-buttons.svg b/motioneye/static/img/camera-top-buttons.svg new file mode 100644 index 0000000..db32747 --- /dev/null +++ b/motioneye/static/img/camera-top-buttons.svg @@ -0,0 +1,174 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/motioneye/static/img/logout.svg b/motioneye/static/img/logout.svg deleted file mode 100644 index cda383d..0000000 --- a/motioneye/static/img/logout.svg +++ /dev/null @@ -1,56 +0,0 @@ - - - -image/svg+xml \ No newline at end of file diff --git a/motioneye/static/img/settings.svg b/motioneye/static/img/settings.svg deleted file mode 100644 index c11c28e..0000000 --- a/motioneye/static/img/settings.svg +++ /dev/null @@ -1,108 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - diff --git a/motioneye/static/img/top-bar-buttons.svg b/motioneye/static/img/top-bar-buttons.svg index a10c006..2cc1d00 100644 --- a/motioneye/static/img/top-bar-buttons.svg +++ b/motioneye/static/img/top-bar-buttons.svg @@ -9,59 +9,61 @@ xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="80" - height="16" - id="svg2" + width="144" + height="48" + viewBox="0 0 144 48.000001" + id="svg4231" version="1.1" - inkscape:version="0.48.4 r9939" - sodipodi:docname="top-bar-buttons.svg" - inkscape:export-filename="/media/data/projects/motioneye/static/img/top-bar-buttons.png" - inkscape:export-xdpi="90" - inkscape:export-ydpi="90"> + inkscape:version="0.91 r13725" + sodipodi:docname="top-bar-buttons.svg"> + id="defs4233" /> + inkscape:snap-text-baseline="true"> + + + id="metadata4236"> image/svg+xml - + @@ -69,116 +71,105 @@ inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1" - transform="translate(0,-1036.3622)"> - + transform="translate(0,-1004.3622)"> + - - + sodipodi:nodetypes="ccccc" + inkscape:export-filename="/media/data/projects/reshaped/static/img/tool-icons/tool-participants.png" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90" /> + + + + + sodipodi:nodetypes="ccccc" /> + id="path4223" + d="m 115.99999,1017.3622 8,0" + style="fill:none;fill-rule:evenodd;stroke:#3498db;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> - - - - - + sodipodi:nodetypes="cc" /> + sodipodi:nodetypes="cc" /> + + diff --git a/motioneye/static/js/main.js b/motioneye/static/js/main.js index ced0289..97391c8 100644 --- a/motioneye/static/js/main.js +++ b/motioneye/static/js/main.js @@ -53,6 +53,10 @@ Object.keys = Object.keys || (function () { }; })(); +Object.values = function (obj) { + return Object.keys(obj).map(function (k) {return obj[k];}); +}; + Object.update = function (dest, source) { for (var key in source) { if (!source.hasOwnProperty(key)) { @@ -848,6 +852,32 @@ function getCameraProgress(cameraId) { return getCameraFrame(cameraId).find('div.camera-progress'); } +function setLayoutColumns(columns) { + var cssClasses = { + 1: 'one-column', + 2: 'two-columns', + 3: 'three-columns', + 4: 'four-columns' + }; + + getPageContainer().removeClass(Object.values(cssClasses).join(' ')); + getPageContainer().addClass(cssClasses[columns]); +} + +function showCameraOverlay() { + getCameraFrames().find('div.camera-overlay').css('display', ''); + setTimeout(function () { + getCameraFrames().find('div.camera-overlay').addClass('visible'); + }, 10); +} + +function hideCameraOverlay() { + getCameraFrames().find('div.camera-overlay').removeClass('visible'); + setTimeout(function () { + getCameraFrames().find('div.camera-overlay').css('display', 'none'); + }, 300); +} + /* settings */ @@ -2542,7 +2572,7 @@ function pushCameraConfig(reboot) { } /* also update the config stored in the camera frame div */ - var cameraFrame = $('div.camera-frame#camera' + cameraId); + var cameraFrame = getCameraFrame(cameraId); if (cameraFrame.length) { Object.update(cameraFrame[0].config, cameraConfig); } @@ -2592,7 +2622,7 @@ function getCameraIdsByInstance() { * the local instance has both the host and the port set to empty string */ var cameraIdsByInstance = {}; - $('div.camera-frame').each(function () { + getCameraFrames().each(function () { var instance; if (this.config.proto == 'netcam' || this.config.proto == 'v4l2') { instance = ''; @@ -3637,38 +3667,53 @@ function runMediaDialog(cameraId, mediaType) { /* camera frames */ function addCameraFrameUi(cameraConfig) { - if (cameraConfig == null) { - var cameraFrameDivPlaceHolder = $('
'); - getPageContainer().append(cameraFrameDivPlaceHolder); - - return; - } - var cameraId = cameraConfig.id; var cameraFrameDiv = $( '
' + - '
' + - '' + - '
' + - '
' + - '
' + - '
' + - '
' + - '
' + - '
' + '
' + '
' + '' + '
' + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + '
'); - + var nameSpan = cameraFrameDiv.find('span.camera-name'); - var configureButton = cameraFrameDiv.find('div.camera-button.configure'); - var picturesButton = cameraFrameDiv.find('div.camera-button.media-pictures'); - var moviesButton = cameraFrameDiv.find('div.camera-button.media-movies'); - var fullScreenButton = cameraFrameDiv.find('div.camera-button.full-screen'); + + var configureButton = cameraFrameDiv.find('div.camera-top-button.configure'); + var picturesButton = cameraFrameDiv.find('div.camera-top-button.media-pictures'); + var moviesButton = cameraFrameDiv.find('div.camera-top-button.media-movies'); + var fullScreenButton = cameraFrameDiv.find('div.camera-top-button.full-screen'); + + var cameraInfoDiv = cameraFrameDiv.find('div.camera-info'); + + var lockButton = cameraFrameDiv.find('div.camera-action-button.lock'); + var unlockButton = cameraFrameDiv.find('div.camera-action-button.unlock'); + var lightOnButton = cameraFrameDiv.find('div.camera-action-button.light-on'); + var lightOffButton = cameraFrameDiv.find('div.camera-action-button.light-off'); + var alarmButton = cameraFrameDiv.find('div.camera-action-button.alarm'); + var cameraPlaceholder = cameraFrameDiv.find('div.camera-placeholder'); var cameraProgress = cameraFrameDiv.find('div.camera-progress'); var cameraImg = cameraFrameDiv.find('img.camera'); @@ -3720,7 +3765,7 @@ function addCameraFrameUi(cameraConfig) { /* fade in */ cameraFrameDiv.animate({'opacity': 1}, 100); - /* add the button handlers */ + /* add the top button handlers */ configureButton.click(function () { doConfigureCamera(cameraId); }); @@ -3744,6 +3789,16 @@ function addCameraFrameUi(cameraConfig) { }; }(cameraId)); + /* add the top button handlers */ + //TODO + + // TODO move these to the refresh function + cameraInfoDiv.append('frame rate'); + cameraInfoDiv.append('25 fps
'); + cameraInfoDiv.append('temperature'); + cameraInfoDiv.append('38 deg
'); + cameraInfoDiv.css('padding-top', '1em'); + /* error and load handlers */ cameraImg[0].onerror = function () { this.error = true; diff --git a/motioneye/templates/main.html b/motioneye/templates/main.html index bbc9458..3232bf9 100644 --- a/motioneye/templates/main.html +++ b/motioneye/templates/main.html @@ -77,8 +77,8 @@
-
-
+
Apply
-- 2.39.5