From 178df476bdfa6384f4b7c0a2058fdbb1e73a4ef1 Mon Sep 17 00:00:00 2001 From: "gregzovic@bazaar.launchpad.net" Date: Thu, 24 Jan 2019 12:49:45 +0300 Subject: [PATCH] Added alert animations and transitions --- openlp/core/display/html/display.html | 77 +++++++++++++ openlp/core/display/html/display.js | 149 +++++++++++++++++++++++++- openlp/core/display/window.py | 2 +- tests/js/test_display.js | 47 ++++++++ 4 files changed, 270 insertions(+), 5 deletions(-) diff --git a/openlp/core/display/html/display.html b/openlp/core/display/html/display.html index 83e0bac90..ccecd7788 100644 --- a/openlp/core/display/html/display.html +++ b/openlp/core/display/html/display.html @@ -24,14 +24,91 @@ visibility: visible; z-index: -1; } + + /* Animation key frames for horizontal scrolling of alert */ + @keyframes alert-scrolling-text { + from { margin-left: 100%; } + to { margin-left: -300% } + } + /* Middle fade-in alert animation */ + @keyframes middle-fade-in { + from { opacity: 0;} + to { opacity: 1;} + } + + /* Middle fade-out alert animation */ + @keyframes middle-fade-out { + from { opacity: 1;} + to { opacity: 0;} + } + + /* Fade in when alert location is in the middle */ + .middle-entrance-animation { + animation-duration: 2s; + animation-timing-function: linear; + animation-name: middle-fade-in; + } + + /* Fade out when alert location is in the middle */ + .middle-exit-animation { + animation-duration: 2s; + animation-timing-function: linear; + animation-name: middle-fade-out; + } + + .horizontal-scroll-animation { + animation-duration: 10s; + animation-iteration-count: 1; + animation-timing-function: linear; + animation-name: alert-scrolling-text; + } + + /* ALERT STYLING */ + #alert-background { + position: absolute; + margin: 0; + padding: 0; + left: 0px; + right: 0px; + z-index: 10; + width: 100%; + height: 25%; + vertical-align: middle; + color: #ffffff; + background-color: #660000; + overflow: hidden; + visibility:hidden; + } + + #alert { + position: relative; + top: 50% + transform: translateY(-50%); + margin-top: 0%; + margin-right: 0%; + margin-left: 100%; + margin-bottom: 0%; + z-index: 11; + overflow: visible; + white-space: nowrap; + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + font-size: 100pt; + color: #ffffff; + visibility: hidden; + } + + + +
+

Testing alerts

diff --git a/openlp/core/display/html/display.js b/openlp/core/display/html/display.js index aafdf7cb3..4bc56fa48 100644 --- a/openlp/core/display/html/display.js +++ b/openlp/core/display/html/display.js @@ -52,6 +52,31 @@ var AudioState = { Stopped: "stopped" }; +/** + * Transition state enumeration + */ + +var TransitionState = { + EntranceTransition: "entranceTransition", + NoTransition: "noTransition", + ExitTransition: "exitTransition" +}; + +/** + * Animation state enumeration + */ +var AnimationState = { + NoAnimation: "noAnimation", + ScrollingAnimation: "scrollingAnimation", + FadeInAnimation: "fadeInAnimation", + FadeOutAnimation: "fadeOutAnimation" +}; + +/** + * + * @param {Location} selector + */ + /** * Return an array of elements based on the selector query * @param {string} selector - The selector to find elements @@ -131,6 +156,10 @@ function _pathToString(path) { return filename; } +/** + * + */ + /** * An audio player with a play list */ @@ -249,6 +278,8 @@ AudioPlayer.prototype.stop = function () { */ var Display = { _slides: {}, + _transitionState: TransitionState.NoTransition, + _animationState: AnimationState.NoAnimation, _revealConfig: { margin: 0.0, minScale: 1.0, @@ -372,14 +403,124 @@ var Display = { * @param {string} text - The alert text * @param {int} location - The location of the text (top, middle or bottom) */ - alert: function (text, location) { - console.debug(" alert text: " + text, ", location: " + location); + alert: function (text, location) { + console.debug(" alert text: " + text + ", location: " + location); + + if(text == ""){ + return null; + } + + var alertBackground = $("#alert-background")[0]; + var alertText = $("#alert")[0]; + + alertText.innerHTML = text; + + /* Bring in the transition background */ + Display._transitionState = Display.alertEntranceTransition(location); + + alertBackground.addEventListener('transitionend', function(e){ + e.stopPropagation(); + console.debug("Transition end event captured"); + if(Display._transitionState == TransitionState.EntranceTransition){ + console.debug("Entrance Transition Condition"); + alertText.style.visibility = "visible"; + alertText.classList.add("horizontal-scroll-animation"); + }else if(Display._transitionState == TransitionState.ExitTransition){ + console.debug("Exit Transition Condition"); + Display._transitionState = TransitionState.NoTransition; + alertBackground.style.visibility = "hidden"; + alertBackground.classList.remove("middle-exit-animation"); + } + }); + + alertBackground.addEventListener('animationend',function(){ + + console.debug("Noticed an animation has ended. The animation state is: ", Display._animationState); + if(Display._animationState == AnimationState.FadeInAnimation){ + console.debug("Entrance Animation Condition"); + alertText.style.visibility = "visible"; + alertText.classList.add("horizontal-scroll-animation"); + alertText.classList.remove("middle-entrance-animation"); + Display._animationState = AnimationState.ScrollingAnimation; + }else if(Display._animationState == AnimationState.FadeOutAnimation){ + console.debug("Exit Animation Condition"); + alertBackground.style.visibility = "hidden"; + alertBackground.classList.remove("middle-exit-animation"); + Display._animationState = AnimationState.NoAnimation; + }else if(alertText.classList.contains("horizontal-scroll-animation")){ + console.debug("Scrolling Animation Ended"); + alertText.classList.remove("horizontal-scroll-animation"); + Display._animationState = AnimationState.NoAnimation; + Display._transitionState = Display.alertExitTransition(location); + } + + }); + /* * The implementation should show an alert. * It should be able to handle receiving a new alert before a previous one is "finished", basically queueing it. */ - return; -}, + //return; + }, + + /** + * Start background entrance transition for display of alert + * @param {string} location - String showing the location of the alert on screen + */ + alertEntranceTransition: function (location){ + console.debug("Alert Entrance Transition Method. The value of animation state is: " + Display._animationState); + var alertBackground = $("#alert-background")[0]; + + switch(location){ + case "0": + console.debug("Top Location Entrance Transition"); + alertBackground.style.top = '0'; + alertBackground.style.transition = "2s linear"; + alertBackground.style.height = "25%"; + break; + case "1": + console.debug("Middle Location Entrance Animation"); + console.debug("The value of animation state is: " + Display._animationState); + alertBackground.style.top = ((window.innerHeight - alertBackground.clientHeight) / 2) + + 'px'; + alertBackground.classList.add("middle-entrance-animation"); + Display._animationState = AnimationState.FadeInAnimation; + break; + case "2": + default: + console.debug("Bottom Location Entrance Transition"); + alertBackground.style.bottom = '0'; + alertBackground.style.transition= "2s linear"; + alertBackground.style.height = "25%"; + break; + } + alertBackground.style.visibility = "visible"; + return TransitionState.EntranceTransition; + + }, + + /** + * Start background exit transition once alert has been displayed + * @param {string} location - Integer showing the location of the alert on screen + */ + alertExitTransition: function(location){ + + console.debug("Alert Exit Transition"); + var alertBackground = $("#alert-background")[0]; + + if(location == "0" || location == "2"){ + console.debug("Exit Transition for top or bottom"); + alertBackground.style.height = "0%"; + alertBackground.style.transition = '2s linear'; + }else if(location == "1"){ + // alertBackground.style.opacity = 0; + console.debug("Fade out Animation"); + alertBackground.classList.add("middle-exit-animation"); + Display._animationState = AnimationState.FadeOutAnimation; + } + + return TransitionState.ExitTransition; + }, /** * Add a slides. If the slide exists but the HTML is different, update the slide. diff --git a/openlp/core/display/window.py b/openlp/core/display/window.py index e8545968d..30284483c 100644 --- a/openlp/core/display/window.py +++ b/openlp/core/display/window.py @@ -401,4 +401,4 @@ class DisplayWindow(QtWidgets.QWidget): """ Set an alert """ - self.run_javascript('Display.alert({text}, {location});'.format(text=text, location=location)) + self.run_javascript('Display.alert("{text}", "{location}");'.format(text=text, location=location)) diff --git a/tests/js/test_display.js b/tests/js/test_display.js index 196ba06bb..bbb7a75da 100644 --- a/tests/js/test_display.js +++ b/tests/js/test_display.js @@ -18,6 +18,14 @@ describe("The enumeration object", function () { it("AudioState should exist", function () { expect(AudioState).toBeDefined(); }); + + it("TransitionState should exist", function(){ + expect(TransitionState).toBeDefined(); + }); + + it("AnimationState should exist", function(){ + expect(AnimationState).toBeDefined(); + }); }); describe("The function", function () { @@ -138,8 +146,47 @@ describe("The Display object", function () { Display.goToSlide("v1"); expect(Reveal.slide).toHaveBeenCalledWith(0); }); + + it("should have an alert() method", function () { + expect(Display.alert).toBeDefined(); + }); + + it("should have a correctly functioning alert() method", function (){ + spyOn(Display,"alert"); + Display.alert("OPEN-LP-3.0 Alert Test", "2"); + expect(Display.alert).toHaveBeenCalledWith("OPEN-LP-3.0 Alert Test", "2"); + }); + }); +describe("Display.alert",function(){ + beforeEach(function(){ + var alertBackground = document.getElementById("alert-background"); + var alertText = document.getElementById("alert"); + }); + + it("should return if called without any text", function(){ + spyOn(Display, "alert"); + Display.alert("", "2"); + expect(Display.alert).toHaveBeenCalled(); + }); + + it("should call alertEntranceTransition", function(){ + spyOn(Display,"alertEntranceTransition"); + Display.alertEntranceTransition("2"); + expect(Display.alertEntranceTransition).toHaveBeenCalledWith("2"); + }); + + it("should call alertExitTransition", function(){ + spyOn(Display,"alertExitTransition"); + Display.alertExitTransition("2"); + expect(Display.alertExitTransition).toHaveBeenCalledWith("2"); + }); +}); + + + + describe("Display.addTextSlide", function () { beforeEach(function() { document.body.innerHTML = "";