Thursday, April 14, 2016

Fade-in Animation for Oracle JET ojDialog

It may just be my eye-mind coordination (or lack thereof), but I have trouble adjusting to abrupt user interface changes. For example, if I click a button and a dialog box instantly appears, my business-related thought processes pause for a moment while I try to mentally digest what just happened and why. It is mentally easier for me if user interface changes occur through fast, but gradual animations. Don't waste my time with a slow animation... but then again, don't waste my time with no animation. I function best when enterprise application developers use animations to prepare my mind for the next step. So here I am developing an enterprise application with Oracle JET and using ojDialog. As my ojDialog appears, I think to myself, "Where is the animation?"

Here is how I added fade-in animation to ojDialog. Starting from the ojDialog cookbook recipe, I added the class animate as a rootAttribute to identify that this dialog should be animated:

<div id="dialogWrapper">
  <div style="display:none" id="modalDialog1" title="Modal Dialog" 
       data-bind="ojComponent:{component: 'ojDialog',
                               initialVisibility: 'hide',
                               rootAttributes: {
                                 class: 'animate'
                               }}">
     <div class="oj-dialog-body">
       The dialog window can be moved, resized and closed with the 'x' icon.
       Arbitrary content can be added to the the oj-dialog-body and
       oj-dialog-footer sections.
     </div>
     <div class="oj-dialog-footer">
        <button id="okButton" data-bind="click: closeDialog,
                                         ojComponent: {component: 'ojButton',
                                                       label: 'OK'}"> 
      </div>
  </div>
  <button id="buttonOpener" data-bind="click: openDialog,  
                                       ojComponent: {component: 'ojButton',
                                                     label: 'Open Modal Dialog'}">

</div>

Next I added some CSS to set the initial opacity for all animated ojDialogs to 0, meaning fully transparent:

.oj-dialog.animate {
  opacity: 0;
  transition: opacity .25s ease-in-out;
}

Now when I invoke the open method, the dialog will appear, but it will be fully transparent which means I won't actually be able to see it (talk about usability and user interface design!). Then I added a new selector and corresponding CSS to represent a dialog in the fully opaque state:

.oj-dialog.animate.fully-opaque {
  opacity: 1;
}

The plan is to toggle the fully-opaque class on the oj-dialog.animate selector when the dialog opens. Let's revisit that dialog HTML definition and add an open event handler:

<div style="display:none" id="modalDialog1" title="Modal Dialog" 
     data-bind="ojComponent:{component: 'ojDialog',
                             initialVisibility: 'hide',
                             open: dialogOpen,
                             rootAttributes: {
                               class: 'animate'
                             }}">

And now the JavaScript to handle the open event:

    self.dialogOpen = function(event, ui) {
      $(event.target)
        .closest(".oj-dialog.animate")
        .addClass("fully-opaque");
    };

Since we added a CSS transform to oj-dialog.animate the ojDialog container will fade in from 0 to 100% opacity. Notice the interesting selector in that JavaScript fragment? The event target is the ojDialog div we defined, but Oracle JET wraps that element in a new root element. It is THAT element we want to animate (hence the use of rootAttributes in the ojDialog definition). There are probably a million different selectors I could have chosen. In fact, $(event.target).parent() was my first idea, but then I thought, "what if the ojDialog structure changes?"

If you followed these steps, you should now have an ojDialog with a fade-in effect. What about fade-out? I don't have a good answer here. I tried both the close and beforeClose events, but those happen too quickly, meaning the dialog display is changed to hidden before the animation. My not-so-good work-around is to perform the animation when the user clicks the OK button in the dialog footer:

self.closeDialog = function(data, event) {

  // attempt fadeOut with jQuery
  $(event.target)
    .closest(".oj-dialog.animate")
    .fadeOut(function() {
      $("#modalDialog1").ojDialog("close");
    });
  };

I put together a jsFiddle named ojDialog Fade-in to demonstrate the animated ojDialog. Once Oracle JET starts accepting pull requests, perhaps animations would be a valuable community enhancement?

No comments:

Post a Comment