You won’t believe how easy it is! From UI to UX: tweaks to create an immersive user experience Recap This article is the fifth and final part of our series on creating interactive infographics with plain Javascript. Previously we built a feature-rich User Interface (UI) to browse inter-connected information. In this article, we’ll demonstrate why it is so easy to create UIs with a human touch. Objective We’ll highlight ten UI tweaks that’ll transform a casual visit into a superior user experience. Concept At its core, UI design is just two words: and . contextualise communicate If we can the story, we can identify the right problem to solve. contextualise If we can design a UI that , we can guide users towards their goals. communicates , this story is not about design principals or Javascript itself. It is about the needs and challenges of the user. As such, the “correct answer” should vary from project to project. To clarify Let’s see how that comes into play with practical examples. Tweak #1 How to present the information architecture without cluttering the display? Use transition effects to tell a story. Contextualise: Communicate: The “usual” way of highlighting information. Before: <div id="myElement"><a href="myURL">Maps</a></div> Undifferentiated content A CSS tweak will give new dimensions to the information architecture. After: A simple tweak to highlight the information architecture var myIcon = document.getElementById( "myElement" ); addEventListener("mouseover", mouseOver );function mouseOver(event) {myicon.style.transform = "scale3d(1.20, 1.20, 1.20)"; // node;mylink.style.border = "solid 1px black"; // link} Tweak #2 How to reveal information hierarchy quickly? Stagger the entrance of containers progressively. Contextualise: Communicate: A “typical” page load Before: Without staggered entrance A staggered entrance highlights the information hierarchy instantly. After: . The core content, comprising text and media, are wrapped within a larger container to the far-right so that its implied order is noticable. Observe the ultrafast creation of the Youtube embedded element even as its parent container is generated on-the-fly. No worries on any video content slowing down your website. Tier-one Using staggered animation to reveal information architecture . The middle container shows summary information and related topics for users who are interested to find out more. Tier-two . If users are indeed engaged, they can trigger the far-left container to reveal more information (see the demo from part-four of our story). Tier-three Initiate the “start/hidden” position of all containers. rightContainer.style.transform = "translate3d(50vw, 100vh, 0)";middleContainer.style.transform = "translate3d(25vw, 100vh, 0)";leftContainer.style.transform = "translate3d(0vw, 100vh, 0)"; Sequence the entrance towards the “end/show” position. rightContainer.style.transform = "translate3d(50vw, 0vh, 0)";setTimeout(() => {middleContainer.style.transform = "translate3d(25vw, 0vh, 0)";}, 200);setTimeout(() => {leftContainer.style.transform = "translate3d(0vw, 0vh, 0)";}, 400); delays code execution to emulate the staggered animation effect. setTimeout The “start/hidden” position of each container is (from the top). The “end/show” position will be . 100vh 0vh or can produce similar results. keyframes transition-delay Select an to create butter-smooth animations. easing transition Tweak #3 How to maximise the view and navigation space? Autohide unused UI elements during navigation events. Contextualise: Communicate: Visible navigation space is halved. While users can view content on the “sticky” container, it is not needed in the midst of a navigation event itself. Before: Visible navigation space is halved Since users won’t need the sticky container while navigating, we can safely fadeout the container to maximise view and navigation space. After: The sticky container fades out contextually to maximise navigation space (){rightContainer.style.opacity = "0";}; fadeOutSticky // reusing event listener (from part-one discussion)canvas.addEventListener("mousedown", handlerGrab, false); function handlerGrab(event) { if (event.which == 1) { ... **fadeOutSticky**(); }; ... } Tweak #4 How to “not-block” active elements? Autohide blocking elements in response to interaction events. Contextualise: Communicate: Fadeout the so that it won’t “block” the mouse cursor when users are “panning” the canvas (a navigation feature discussed in part-two). minimap The minimap makes way for the mouse cursor contextually canvas.addEventListener("mousemove", handlerMouseMove, false);function handlerMouseMove(event) {if (event.which == 3) {if ( (event.clientX, event.clientY, minimap) != false) {minimap.style.visibility = "hidden";} else {minimap.style.visibility = "visible";}}} boundingbox is a custom function to determine if the mouse cursor is currently hovering over the . **boundingbox** minimap function (ClientX = 0, ClientY = 0, element) {if (element.getBoundingClientRect) {var rect = element.getBoundingClientRect ();y = rect.top;xR = rect.right;yB = rect.bottom;xL = rect.left;boxHeight = yB - y;boxWidth = xR - xL;}if ( ClientX <= (xR) && ClientY <= (yB) ) {return true;} else return false;} boundingbox Tweak #5 How to use the “hidden dimension” to reveal a deeper information architecture? Emulate a see-through effect by manipulating the opacity level of the “ ” element. The content of both the above and layers are visible to help users visualise a more complete picture. Contextualise: Communicate: above underneath You can broaden the navigation view or create a sense of visual depth with just the value. Notice the semi-visible layer underneath the (top-left) and the sticky container (right-hand side)? opacity minimap Good-old CSS opacity creates both information and visual depth Tweak #6 How to show related information only when it’s relevant (without a page reload)? Hide the information by default, but show on mouse focus. Contextualise: Communicate: . In the GCP world, is tightly integrated with other GCP products. While PubSub may be categorised under , it is also a key integration point for many GCP products. How can we feature this contextual relationship without cluttering the view? It’s not so complicated once we do this. A little background on the Google Cloud Platform (GCP) PubSub Big Data PubSub and Cloud Storage is the invisible glue that connects many GCP products The technique is similar to how we would highlight the nodes (discussed in part-one of the story). “Hide” the dotted line. dottedLine.style.borderRight = "1px dashed #FFF";// opacity or visibility works well too When we mouse-over the node, the for the previously hidden dotted lines become visible. Note that other non-essential links become hidden. With just one line of code, you can achieve a funnelling effect on the user’s attention and tell a story without narration. PubSub eventlistener dottedLine.style.borderRight = "1px dashed #ddd"; It’s almost mandatory to apply a fade-in fade-out transition. dottedLine.style.borderRight = “300ms ease”; Tweak #7 How to show the contextual usage of the mouse cursor? Update the mouse cursor icon to signify different usage or behaviour. Contextualise: Communicate: . Users drag the rectangular bracket (top left) within the to update the canvas (main view). The cursor change from a default pointer to a four-way arrow. Interacting with the minimap minimap Minimap navigation: updating mouse cursor from pointer to four-way arrow canvas.style.cursor = “inherit”; // default mouse pointercanvas.style.cursor = “move”; // four-way arrow icon . Users drag the main canvas. The location on the updates concurrently. The cursor change from a “grab”(hand) icon to a “grabbing”(fist) icon. Interacting with the main canvas minimap Canvas navigation: updating mouse cursor from “grab” to “grabbing” canvas.style.cursor = “-webkit-grab”; // hand iconcanvas.style.cursor = “-webkit-grabbing"; // fist icon Tweak #8 How to add contextual usage to the same mouse click? Differentiate a from a mouse click. Contextualise: Communicate: mousedown We want a left-click to do this. The stand-alone left-click: “ and “mouseup” action in a rapid succession mousedown" We also want a left-click to do this. prolonged The grab-and-drag action: a prolonged mousedown with a mousemove to drag things that end with a mouseup As you can see, a left-click is not the same as a left-click. The later drags the canvas. prolonged A mouse button can have dual usage and still feel intuitive. When a left-click is on , and not yet, it may mean either: mousedown mouseup click on something, or drag things. It’s trivial to implement. The trick is knowing what happens the event is detected. Is there an immediate event or a event? The former suggests a “standard” click and the later a custom drag action. after mousedown mouseup mousemove If a is not detected within, say 300ms, then let the user drag the canvas. mouseup In the main loop (from part-one), listen to the event. while mousedown while (item[i]) {itemElementName[i].addEventListener("mousedown", );...} clickTimer Add a custom function named to determine whether it’s a click or a drag. clickTimer var = true;function (event) {...// measure the time interval between mousedown and mouseup...} mouseClick clickTimer Since we’re only interested in knowing if the time interval is more than 300ms ( the same as the interval value itself), we can use a simple hack to do the job. not // measurement hacksetTimeout(function(){ = false;}, 300); mouseClick On , set to true. However, after 300ms, set it to false. mousedown mouseClick Tweak #9 How to transit from “in-browser” to full-screen mode? Provide a quick-toggle option. Contextualise: Communicate: . Offer an option to go interactive. By default, the “in-browser” mode won’t block users from scrolling pass the canvas (also see from part-two). A toggle will enable navigation from within the canvas. Toggle-to-interact scroll Toggle between “in-browser” and “interactive” mode canvas.style.overflow = "hidden"; // default with scroll disabledcanvas.style.overflow = "scroll"; // enable scroll . Offer an option to go fullscreen from “inline” mode to maximise navigation space. Toggle to fullscreen Toggle between full-screen and inline mode buttonFullScreen.addEventListener("click", handlerFullScreen, false); function handlerFullScreen(event) {...canvas.style.height = "100vh";canvas.style.width = "100vw";// transform to new canvas full-width and full height// handle new minimap placement position & adjust the new location position// Calculate relative scroll position after canvas turns full-screenfullScreenSize = true; // global var for other functions} references the HTML . buttonFullScreen <button></button> and toggles the canvas to full screen. Remember to account for changes in the position. canvas.style.height canvas.style.height minimap Finally, use a boolean flag to tell your down-stream functions to calculate based on full-screen dimensions (or not). Tweak #10 (Zoom with SVG) How to let users examine close-up details and still stay high-def? Use the mouse-scroll button to zoom. Use SVG to achieve unlimited scaling in high-definition. Contextualise: Communicate: Zoom incrementally with the mouse-scroll button (before applying easing effect) Use the API to increment or decrement size. zoom canvas.style.zoom = zoomLevel;zoomLevel = zoomLevel + 0.01; //increment the zoom level Tweak #10 (Zoom with easing) How to make UIs respond in a fluid and human-like manner? Incorporate an easing function to create an intuitive experience. Contextualise: Communicate: Animation and easing effects work hand in hand to provide useful visual cues for users. One example is the original iPhone end-of-page “rebound” effect. Let’s try this on our zoom example. Zoom with easing function easeInOutQuad(t, b, c, d) {if ((t /= d / 2) < 1) return c / 2 * t * t + b;return -c / 2 * ((--t) * (t - 2) - 1) + b;} function spreadZoom(direction) {var time = 0;var diff = 1;var minTime = 5;var maxTime = 40; for (var i = 0, len = diff; i <= len; i++) { (function(s) { setTimeout(function() { if (direction == 'zoomin') { ... } else if (direction == 'zoomout') { ... }; }, time); })(i); time = easeInOutQuad(i, minTime, maxTime, diff); } // for loop } The result is a smoother start-stop curve that mimics a more natural response. breaks a physical scroll action into a series of smaller virtual zooms “spread over” a curve. spreadZoom easeInOutQuad creates the analogue effect. The formula is based on the easing equation. easeInOutQuad Robert Penner’s The behaviour can be further configured via , , , and variables. time diff minTime maxTime Concept aside: Easing Why is easing a first-class citizen in UI design? A simple tweak makes the difference between “just ok” and “just right”. Perhaps due to its mathematical nature, as illustrated in the previous example (Tweak #10), easing may be an overlooked and underused feature. However, it can be very straightforward too. One line of CSS usually gets the job done. easeInOutQuad CSS usage #myElement {transition: 1s ease;} Customised usage #myElement {transition: 1s cubic-bezier(0.25,0.1,0.25,1);} Javascript equivalent myElement.style.transition = "all 1s cubic-bezier(0.25,0.1,0.25,1)"; Easing is used extensively in our demo project. For example, the staggered entrance of the sticky containers are eased (Tweak #2). So are the scaling and highlighting effects of the SVG icons and connecting links (Tweak #1). Let’s have some fun with easing. The ballerina chasing the mouse cursor, with easing. Next Steps Some of the most complex UIs are built on top of the simplest ideas. Once we apply those ideas to contextualise the problem, we can create solutions that work better. While we’ve devoted five stories and covered some of the most common Javascript techniques, we are saying only one thing. Anyone can create a compelling design with just simple tools. Anyone can transform a casual visit into a superior user experience. Links to other parts sets the foundation for designing interactive infographics. Part-one adds navigation features to browse content. Part-two adds a dynamic mini-map to enhance navigation. Part-three adds an inline UI to access layered content. Part-four Part-five → You’re are here. Yay! If you enjoyed our story, find out more at . Pageii Studio