How to create iPhone interface with long press in JavaScript.

Shaking icons and long presses have become something we are very familiar with on our phone screens. On iPhones in particular, moving icons usually mean they’re dragged and editable – while long pressing has become the norm to get extra options.

In this tutorial, we will consider recreating these effects in JavaScript and CSS. In this tutorial, we will cover:

  • New features of CSS– such as background blur, and animated flex boxes.
  • Long press– How to create long press effect with javascript.
  • drag – How to create a simple drag and drop system in JavaScript.

Demo

As always, let’s start with the demo. This is exactly what we are planning to do today.

  • If you click and hold an icon for a second., A popup will flash.
  • If you click and hold for a total of two seconds., The icons will start moving – just like the iPhone. It will also work on mobile.

Step 1: HTML

In this tutorial, I will not focus too much on HTML as it is too preliminary – suffice it to say that the HTML for this demo contains:

  • Icon Containers – A container div with all the information about the individual icons.
  • A Core – A cover div that is at the top of the entire demo, which will fade if the user long presses to display the submenu.
  • Some Notifications – Below are some notifications that give some instructions on what to do.
  • A submenu – a submenu that moves around when the user clicks on different icons.

Step 2: CSS

Most CSS is quite basic, however, I will mention a few important and interesting things that I have used that are very specific to HTML UI design. So our goal is to be able to drag icons, but there’s a lot that can affect it. For one thing, we have a cover that pops up to blur the background. Then there are the annoying images that most browsers allow users to drag.

To solve most of these problems, we can use a property and value in CSS. pointer-events: none;. This means that the user cannot interact with our HTML elements. So on the matte cover, we remove the pointer events when it doesn’t fade, and add them when the background fades. This means that the user usually clicks through this HTML element, unless we want to blur the background, when it becomes active:

#iphone .cover {    
    position: absolute;
    top: -1rem;
    backdrop-filter: blur(4px);
    left: -1rem;
    width: calc(100% + 2rem);
    opacity: 0;
    z-index: 99999;
    height: calc(100% + 2rem);
    transition: all 0.2s ease-out;
    background: rgba(0,0,0,0);
    pointer-events: none; /* No pointer events! */
}
[data-dropdown="true"] #iphone .cover {
    pointer-events: all; /* All pointer events */
    opacity: 1; 
}
#iphone .icon-container img {
    width: calc(100% + 2px);
    pointer-events: none; /* No pointer events */
}

Backdrop filter

This brings us to another CSS property that can be quite useful.backdrop-filter. With support in every major browser except Firefox, backdrop filters allow us to add effects to the elements behind the HMTL tags if the tags are slightly transparent. In us .cover Here’s how we define CSS:

backdrop-filter: blur(4px);

Remove the dynamic icon

For this whole demo, I want the removal of the icon to be smooth. Icons are arranged according to a flex box. To do this I created a custom animation that effectively scales the icon’s scales to zero (so that they appear to zoom in) – and then minimizes their width together. Is. The effect is that the icons begin to close on each other. As soon as one is removed, a smooth animation is created.

This animation is applied to a small piece of Javascript when the user clicks the Remove button.

@keyframes scaleBack {
    0% {
        transform: scale(1);
        width: 5rem;
    }
    40% {
        opacity: 1;
    }
    50% {
        width: 5rem;
        opacity: 0;
        transform: scale(0.0001);
    }
    100% {
        width: 0rem;
        transform: scale(0.0001);
    }
}

Positive to use transformers

  • Lets you use up, down, left and right positioning for other things.
  • Uses 3D acceleration, so it animates extremely fast!

Step 3: JavaScript

JavaScript isn’t too complicated here – and in fact, a lot of the complexity comes from trying to drag icons. There are basically some events we want to track:

  • When a user clicks on an icon – how long we want to measure, so that we can show the effect of dropdown or move.
  • When the user clicks the button – we want to remove the timers for long press, and rearrange another movement tracker.
  • When the user clicks the remove button – we want to remove this icon.
  • When the user clicks on anything other than the button – we want to remove the effects.

So, as you may have guessed, we use pointer events. I always try to use pointer events if possible, as they are compatible with both mobile and desktop. That way, you usually only need to write one script for all the devices. Pointer events replace event handlers such as mouse-down with pointer-down.

Tracking State

To track a user, we have two modes – move, and drop down. We use the properties of the data in the HTML tag to track it – if the icons are moving. data-shaking="true" On the body tag, and if a dropdown appears data-dropdown="true" Shown on the body tag. We do this so that we can change the CSS of the elements based on the state.

After that we have a long press. To manage long presses, all we have to do is:

  • Create a pointer down variable– This variable is set to true when the user clicks on it, and when it releases it is immediately set to false.
  • Create a timeout– After one second and two seconds we check if the pointer down is correct. If so, we either cause the icons to drop down or move.
  • Set reset function.– We rearrange all the variables if the user skips the long press, so we can reuse them if they try again.

In code, it looks like this:

// For selection of the icons and removal icons
let icons = document.querySelectorAll('.icon-container');
let removals = document.querySelectorAll('.remove-icon');


// These all store data on the mouse position at different times in the code
let pointerdown = false;
let offset = 0;
let mouseXInit = 0;
let mouseYInit = 0;
let mouseX = 0;
let mouseY = 0;
let positionX = 0;
let positionY = 0;
let currentTop = 0;
let currentLeft = 0;

// This is for holding our timers
let timers = { first: undefined, second: undefined, third: undefined }

let helpers = {
    reset: function(extended) {
        // This is our reset - it sets everything back to zero, whenever we need to
        // All variables and settings are reset
        mouseX = 0;
        mouseY = 0;
        mouseXInit = 0;
        mouseYInit = 0;
        currentTop = 0;
        currentLeft = 0;
        offset = 0;
        if(typeof timers.first !== "undefined") {
            clearTimeout(timers.first);
        }
        if(typeof timers.second !== "undefined") {
            clearTimeout(timers.second);
        }
        if(typeof timers.third !== "undefined") {
            clearTimeout(timers.third);
        }
        if(typeof extended == "undefined") {
            document.querySelector('.sub-menu').classList.remove('show-sub-menu');
            document.body.setAttribute('data-shaking', false);
            document.body.setAttribute('data-dropdown', false);
            pointerdown = false;
            icons.forEach(function(item) {
                item.setAttribute('data-selected', false);
                item.style.top = 0;
                item.style.left = 0;
            })
        }
    },
    checkPoint: function(x, y, limit) {
        // This checks if the users mouse has moved more than a certain limit. If it has, then they may be dragging..
        // So we don't cause the long press animation
        if(x < limit && x > limit * -1 && y < limit && y > limit * -1) {
            return true;
        } else {
            return false;
        }
    }
}

// For every icon 
icons.forEach(function(item) {
    // Add a pointerdown event
    item.addEventListener('pointerdown', function(e) {
        // Get the click location and set pointerdown to true
        pointerdown = true;
        mouseXInit = e.pageX;
        mouseYInit = e.pageY;
        
        // Get the left and top position of the item, if any
        currentTop = parseFloat(item.style.top) || 0;
        currentLeft = parseFloat(item.style.left) || 0;
        // Set a timer to wait for a hold click
        timers.first = setTimeout(function() {
            // Only do this if pointerdown is true, and if the user hasn't moved more than 10px while clicking down
            if(pointerdown === true && document.body.getAttribute('data-shaking') !== "true" && helpers.checkPoint(mouseX, mouseY, 10)) {
                
                // Icon is now selected, and the dropdown should appear
                item.setAttribute('data-selected', true);
                document.body.setAttribute('data-dropdown', true);
                
                // Find out where exactly the icon is (x, y) coordinates
                let left = item.getBoundingClientRect().left - document.querySelector('#iphone').getBoundingClientRect().left;
                let bottom = item.getBoundingClientRect().bottom - document.querySelector('#iphone').getBoundingClientRect().top;
                // Show the sub menu and move it to where the icon is
                document.querySelector('.sub-menu').classList.add('show-sub-menu');
                document.querySelector('.sub-menu').style.left = `${left}px`;
                document.querySelector('.sub-menu').style.top = `${bottom - 16}px`;

            }
        }, 1000);
        // If the user is still clicking after 2 seconds
        timers.second = setTimeout(function() {
            // Check they are clicking
            if(pointerdown === true && helpers.checkPoint(mouseX, mouseY, 10)) {
                // Now all icons should shake
                document.body.setAttribute('data-shaking', true);
                item.setAttribute('data-dragging', true);
                // Hide the sub menu
                document.querySelector('.sub-menu').classList.remove('show-sub-menu');
                document.body.setAttribute('data-dropdown', false);
                // Give each animation for shaking a delay, to give the appearance of randomness
                timers.third = setTimeout(function() {
                    icons.forEach(function(i) {
                        i.style.animationDelay = `${offset}s`;
                        offset += 0.1;
                    })
                }, 300);
            }
        }, 2000);
        // If the icons are shaking, then the user may be trying to drag this particular icon. Set that icon
        // to have a data-dragging of true. We can use this later
        if(document.body.getAttribute('data-shaking') === "true") {
            item.setAttribute('data-dragging', true);
        }
    });
    // if the user lifts their mouse, then reset everything
    item.addEventListener('pointerup', function() {
        helpers.reset(false);
    });
})

Remove from

When a user clicks on an icon, we need to activate the removal of that icon. To do this, we first add an animation that causes the icon to zoom in and decrease in width. 0. We talked about this in the previous CSS section. Then after the animation was over, we set another timeout to remove the icon completely from HTML.

removals.forEach(function(item) {
    item.addEventListener('click', function(e) {
        // If the removal icon is clicked, then get the parent HTML element - i.e. the icon itself
        let icon = item.parentNode;
        // Animate the icon to disappear
        icon.style.animation = 'scaleBack 0.4s linear 1 forwards';
        // Remove the dropdown, if it is around 
        document.body.setAttribute('data-dropdown', false);
        // And finally, delete the icon completely using the remove() function.
        setTimeout(() => {
            icon.remove();
        }, 400);
    })  
});

Ability to drag

Next, let’s implement a basic form of dragging.The conceptual drag can be divided into a few pieces:

  • First, when the user clicks on the page, we find out where he clicked, using e.clientX, e.clientY. It gives us their click points.
  • As they move forward by clicking on their pointer, we know the difference between their new position and their original click position. This difference is the total amount of the transfer.
  • Then we add that amount to it. top And left CSS values ​​for this icon. It gives us a drag effect. Finally, if the user stops dragging, we reset all the values ​​so that the icon returns to its original position.

Since we need to track certain things, we have a lot of variables. We can track the initial mouse position with mouseXInit, mouseYInit. Then, the difference is stored positionX, positionY, Activates after shaking. We also have a separate store. mouseX, mouseY Before the stir begins. If the user moves too much while clicking down, we do not activate the long press effect, so we can check it with. mouseX, mouseY.

In the code, we end with:

document.body.addEventListener('mousemove', function(e) {
    // If the user is clicking down
    if(pointerdown === true) {
        // Track how much they're moving. If it's too much, we'll cancel the long press timeout
        mouseY = mouseXInit - e.pageY;
        mouseX = mouseXInit - e.pageX;
        if(document.body.getAttribute('data-shaking') == "true") {
            // If they are moving around after shaking starts, then they are dragging
            positionX = mouseXInit - e.pageX;
            positionY = mouseYInit - e.pageY;
            // Set the element to have a data-dragging attribute of true
            let el = document.querySelector('[data-dragging="true"]');
            if(el !== null) {
                // Move the element around
                el.style.top = `${positionY * -1 + currentTop}px`;
                el.style.left = `${positionX * -1 + currentLeft}px`;
            }
        }
    }
})

// When the user lifts their pointer up, then reset all the variables
document.body.addEventListener('pointerup', function(e) {
    if(!e.target.matches('.remove-icon')) {
        helpers.reset(false);
    }
    // And end all icon dragging by setting data-dragging to false on all icons.
    icons.forEach(function(item) {
        item.setAttribute('data-dragging', false);
    });
});

Wrap it up for this tutorial about it. I hope you enjoyed it, and maybe got some new CSS skills. As always, here are some helpful links:

.

Write a Comment

Your email address will not be published. Required fields are marked *