Hardware back button and choppy animations (android)



  • I don’t know where to start looking/debugging, so I’ll try to explain what’s happening…

    I have 2 pages:

    1. Dashboard (contains progressbar and push button that calls Test page)
    2. Test (contains pop button and some content user has to scroll - ons-list).

    It’s happening only on Android (iOS devices not having hardware back button, on Windows 10 mobile works ok)
    Using v2.4.2 UI, vanilla JS, adding event listners for buttons on init page (if this matters at all).

    Case A:
    User scrolls Test page and quickly press hardware back button, progressbar animations on dashboard page are choppy.

    Case B:
    User scrolls Test page and quickly press pop button, progressbar animations on dashboard page are smooth.

    Case C:
    User NOT scrolling Test page, just press hardware back button, progressbar animations on dashboard page are smooth.

    Obviously, the combination of page scrolling and hardware back button is giving me choppy animations and I can’t figure it out why.
    Can someone give me a pointer where to start looking, because it’s driving me crazy :(

    Thanks



  • @p3cRo Are you using the debugger or doing a build for the device? If doing a build, are you using the debug or production build?



  • @munsterlander said:

    @p3cRo Are you using the debugger or doing a build for the device? If doing a build, are you using the debug or production build?

    I’m doing a build, debug/release makes no difference, same thing. Maybe I should mention that in browser (chrome) works fine, because there is no hardware back button…



  • @p3cRo Ok, then lets post some of your code so I can load it on mine and see what happens. Do you have a quick snippet that causes the issue?



  • OK, I’m posting my index.html and app.js code of this test.
    In happens rearly, but it happens. Please, try some combinations of scroll + hardware back button on Test page and it should happen.

    index.html

    <!DOCTYPE html>
    
    <html>
    
    <head>
    
      <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
      <title>Hello World</title>
      <link rel="stylesheet" href="lib/onsen/css/onsenui.css" />
      <link rel="stylesheet" href="lib/onsen/css/onsen-css-components.css" />
    
    </head>
    
    <body onload="onLoad()">
    
      <ons-splitter>
        <ons-splitter-side id="menu" side="left" width="270px" collapse cancelable animation="overlay"></ons-splitter-side>
        <ons-splitter-content>
          <ons-navigator id="navi"></ons-navigator>
        </ons-splitter-content>
      </ons-splitter>
    
      <template id="dashboard.html">
        <ons-page id="dashboard">
          <ons-toolbar>
            <div class="center">Dashboard</div>
          </ons-toolbar>
          <ons-row>
            <ons-col style="text-align: center;">
              <div style="margin: 5px">
                <p id="myCounter">0</p>
                <ons-progress-bar id="myProgressBar" value="0"></ons-progress-bar>
                <p><ons-button id="btnPush">Push page</ons-button></p>
              </div>
            </ons-col>
          </ons-row>
        </ons-page>
      </template>
    
      <template id="test.html">
        <ons-page id="test">
          <ons-toolbar>
            <div class="center">Test</div>
          </ons-toolbar>
          <ons-row>
            <ons-col style="text-align: center;">
              <div style="margin: 5px">
                <ons-button id="btnPop">Pop page</ons-button>
              </div>
            </ons-col>
          </ons-row>
          <ons-row>
               <ons-list id="myList"></ons-list>
              </ons-row>
        </ons-page>
      </template>
    
      <!-- js -->
      <script src="lib/onsen/js/onsenui.min.js"></script>
      <script type="text/javascript" src="cordova.js"></script>
      <script type="text/javascript" src="app.js"></script>
    
    </body>
    
    </html>
    

    app.js

    var myInterval;
    
    function onLoad() {
      document.addEventListener('deviceready', onDeviceReady, false);
      setTimeout(function() {
        navi.resetToPage('dashboard.html');
      }, 0);
    }
    
    function onDeviceReady() {
      document.addEventListener('resume', onResume, false);
      document.addEventListener('backbutton', onBackButton, false);
    }
    
    function onResume() {
      setTimeout(function() {
        navi.resetToPage('dashboard.html');
      }, 0);
    }
    
    function onBackButton() {}
    
    ons.ready(function() {
      var navi = document.getElementById('navi');
    });
    
    document.addEventListener('init', function(event) {
      var page = event.target;
    
      try {
        if (matches(page, '#dashboard')) {
          document.getElementById('btnPush').addEventListener('click', function() {
            navi.pushPage('test.html');
          });
    
        } else if (matches(page, '#test')) {
          document.getElementById('btnPop').addEventListener('click', function() {
            navi.popPage();
          });
    
        }
    
      } catch (e) {
        console.log('init: ' + e.message);
      } finally {}
    
    }, false);
    
    document.addEventListener('show', function(event) {
      var page = event.target;
    
      try {
        if (matches(page, '#dashboard')) {
          var el_myProgressBar = document.getElementById('myProgressBar');
          var el_myCounter = document.getElementById('myCounter');
    
          myInterval = setInterval(function() {
            if (el_myProgressBar.value >= 100) {
              clearInterval(myInterval);
            } else {
              el_myProgressBar.value += 1;
              el_myCounter.innerHTML = el_myProgressBar.value;
            }
          }, 40);
    
        } else if (matches(page, '#test')) {
          var el_myList = document.getElementById('myList');
    
          for (i = 1; i < 31; i++) {
            el_myList.innerHTML += '<ons-list-item>Item ' + i + '</ons-list-item>';
          }
    
        }
    
      } catch (e) {
        console.log('show: ' + e.message);
      } finally {}
    
    }, false);
    
    document.addEventListener('hide', function(event) {
      var page = event.target;
    
      try {
        if (matches(page, '#dashboard')) {
          clearInterval(myInterval);
          var el_myProgressBar = document.getElementById('myProgressBar');
          el_myProgressBar.value = 0;
          document.getElementById('myCounter').innerHTML = el_myProgressBar.value;
    
        }
    
      } catch (e) {
        console.log('hide: ' + e.message);
      } finally {}
    
    }, false);
    
    function matches(el, selector) {
      return (el.matches || el.matchesSelector || el.msMatchesSelector || el.mozMatchesSelector || el.webkitMatchesSelector || el.oMatchesSelector).call(el, selector);
    }
    

    I don’t know if the code is too big to post it here, but thank you for taking time to look into it.



  • @p3cRo So I went to start working on this and immediately a few things jump out at me that will cause this flickering issue.

    • First, you have a navigator inside of a splitter content - which is basically a navigator. Do you need a navigator inside of a navigator? If so, why?
    • Second, you are constantly resetting the page back to dashboard when you can just make the default the dashboard. Is there a reason why you want to constantly force the user back instead of letting them pickup where they left off?
    • Third, your invocation of event listeners do not need to be loaded in the way you are doing it.
    • Fourth, although you can do your scripts at the end, most scripts need to be in the head. @Fran-Diox can chime in on this as Onsen has recently been updated to change this; however, in my experience, you are better loading your scripts and have them waiting instead of loading elements then loading your scripts. You used to do scripts at the bottom so visual elements would load due to internet connectivity and the HTTP specification suggesting 2 downloads from a domain (very truncated explanation). Since this is a mobile app and everything is already on the device, I prefer to not have the app wait for commands which can cause performance issues.

    Edit: I can redo this to make it how I would do it, if you need the example.



  • @munsterlander said:

    @p3cRo So I went to start working on this and immediately a few things jump out at me that will cause this flickering issue.

    • First, you have a navigator inside of a splitter content - which is basically a navigator. Do you need a navigator inside of a navigator? If so, why?

    I wanted to have transition animations, which splitter does not provide (or does?); http://tutorial.onsen.io/?framework=vanilla&category=community tutorials&module=splitter_anim_navigator

    • Second, you are constantly resetting the page back to dashboard when you can just make the default the dashboard. Is there a reason why you want to constantly force the user back instead of letting them pickup where they left off?

    The app always has to show dashboard page, no matter where user left off. That’s why I’m resetting it on resume. Agree that I could make it default on first run.

    • Third, your invocation of event listeners do not need to be loaded in the way you are doing it.

    Please can you give me an example, so I could do it the right way?

    • Fourth, although you can do your scripts at the end, most scripts need to be in the head. @Fran-Diox can chime in on this as Onsen has recently been updated to change this; however, in my experience, you are better loading your scripts and have them waiting instead of loading elements then loading your scripts. You used to do scripts at the bottom so visual elements would load due to internet connectivity and the HTTP specification suggesting 2 downloads from a domain (very truncated explanation). Since this is a mobile app and everything is already on the device, I prefer to not have the app wait for commands which can cause performance issues.

    I’ve read somewhere that it is a good practice to load scripts on the bottom, so I thought it wouldn’t be a problem.

    Edit: I can redo this to make it how I would do it, if you need the example.

    The example would be great, because I’m new to this and still learning.

    Instead of taking your time with all this, maybe I should have asked a simple question: what’s the difference between calling popPage and just pressing the backbutton? When I call popPage, everything works smoothly. The problem is combination of scrolling & pressing the backbutton.



  • @p3cRo You are right on the first one in that the splitter does not have animations. The reason why I was asking was to understand your workflow. I have found in the past where users are combining elements when they really just need to either rethink the workflow to be more efficient for the user or there is a misunderstanding of how they work.

    So the backbutton just calls popPage until the stack is empty then it calls quit application.

    I can make an example real quick. Give me a few hours as it is dinner time and after I get the kids to bed, then I will work on this.

    Edit: So check this out. I tested it on a Galaxy S6 without issue. There are the animators that you can change:

    https://codepen.io/munsterlander/pen/gRBGGy

    Full code that anyone can just copy and paste to an index.html file and get going with the most current version of Onsen automatically.

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="utf-8" />
        <meta name="apple-mobile-web-app-capable" content="yes" />
        <meta name="mobile-web-app-capable" content="yes" />
        <meta http-equiv="Content-Security-Policy" content="default-src * data:; style-src * 'unsafe-inline'; script-src * 'unsafe-inline' 'unsafe-eval'">
        <link rel="shortcut icon" href="favicon.ico" type="image/x-icon" />
        <link href='https://fonts.googleapis.com/css?family=Roboto:400,300italic,300,400italic,500,700,700italic,500italic' rel='stylesheet' type='text/css'>
        <title>Onsen UI Forum Help by Munsterlander</title>
    
        <link rel="stylesheet" href="https://unpkg.com/onsenui/css/onsenui.css" type="text/css" media="all" />
        <link rel="stylesheet" href="https://unpkg.com/onsenui/css/onsen-css-components.css">
        <script src="https://unpkg.com/onsenui"></script>
        <script src="components/loader.js"></script> 
        
        <script>
        window.fn = {};
    
    window.fn.open = function() {
      var menu = document.getElementById('menu');
      menu.open();
    };
    
    window.fn.load = function(page) {
      var menu = document.getElementById('menu');
      var navi = document.getElementById('navi');
    
      menu.close();
      navi.resetToPage(page, { animation: 'fade' });
    };
    
    window.fn.onResume = function () {
        document.getElementById('navi').resetToPage('dashboard.html');
    }
    
     document.addEventListener('deviceready',function(){
        document.addEventListener('resume', window.fn.onResume, false);
    }); 
    
    document.addEventListener('show',function(e){
        if(e.target.id=='dashboard'){
            
            document.getElementById('btnPush').addEventListener('click',function(){
                document.getElementById('navi').pushPage('test.html');
            });
            var el_myProgressBar = document.getElementById('myProgressBar');
            var el_myCounter = document.getElementById('myCounter');
    
            myInterval = setInterval(function() {
                if (el_myProgressBar.value >= 100) {
                  clearInterval(myInterval);
                } else {
                  el_myProgressBar.value += 1;
                  el_myCounter.innerHTML = el_myProgressBar.value;
                }
            }, 40);
                                                                 
        } else if(e.target.id=='test') {        
        
            document.getElementById('btnPop').addEventListener('click',function(){
                document.getElementById('navi').popPage();
            });
                                                                            
            var el_myList = document.getElementById('myList');
    
            for (i = 1; i < 31; i++) {
                el_myList.innerHTML += '<ons-list-item>Item ' + i + '</ons-list-item>';
            }
        }
    });
        </script>
        
      </head>
      <body>
      
    <ons-splitter>
      <ons-splitter-side id="menu" side="left" width="220px" collapse swipeable>
        <ons-page>
          <ons-list>
            <ons-list-item onclick="fn.load('dashboard.html')" tappable>
              Home
            </ons-list-item>
          </ons-list>
        </ons-page>
      </ons-splitter-side>
      <ons-splitter-content>
        <ons-navigator id="navi" page="dashboard.html"></ons-navigator>
      </ons-splitter-content>
    </ons-splitter>
    
    <template id="dashboard.html">
        <ons-page id="dashboard">
          <ons-toolbar>
                    <div class="left">
            <ons-toolbar-button onclick="fn.open()">
              <ons-icon icon="md-menu"></ons-icon>
            </ons-toolbar-button>
          </div>
            <div class="center">Dashboard</div>
          </ons-toolbar>
          <ons-row>
            <ons-col style="text-align: center;">
              <div style="margin: 5px">
                <p id="myCounter">0</p>
                <ons-progress-bar id="myProgressBar" value="0"></ons-progress-bar>
                <p><ons-button id="btnPush">Push page</ons-button></p>
              </div>
            </ons-col>
          </ons-row>
        </ons-page>
      </template>
    
      <template id="test.html">
        <ons-page id="test">
          <ons-toolbar>
            <div class="center">Test</div>
          </ons-toolbar>
          <ons-row>
            <ons-col style="text-align: center;">
              <div style="margin: 5px">
                <ons-button id="btnPop">Pop page</ons-button>
              </div>
            </ons-col>
          </ons-row>
          <ons-row>
               <ons-list id="myList"></ons-list>
              </ons-row>
        </ons-page>
      </template>
    
    
    </body>
    </html>
    


  • @munsterlander Thank you for your time and effort, I’ll look into it


Log in to reply