Header Image
Thumbnail Image

Interactive drag animation effect using HTML, CSS, and JavaScript!

By Cristian Quiñones, Published on January 20th 2025 | 7 mins, 1337 words

In this lesson, we wil create a drag animated website using basic front end technologies such as HTML, CSS, and JavaScript.


You’ll learn how to:


• Create basic styling and UI elements.

• create animated styling  and interactivity with CSS and JavaScript.
• create mouse events for drag functionality.




Step 1: Set Up the Front End Structure



let's start with a basic HTML structure. Here's the complete code for the index.html:



<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="style.css">
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">

    <title>Document</title>
</head>
<body>
  <div class="container">
    <div class="help">Drag me around!</div>
    <div class="plane"></div>

    <div class="app">
       <div class="view">
          <div class="title">TEST APP</div>
          <div class="item">Update</div><br>
          <div class="item">Send</div><br>
          <div class="item">Rename</div><br>
          <div class="item close">Exit</div>
       </div>
    </div>
 </div>
 <div class="info"></div>
 <script src="main.js"></script>
</body>
</html>



Step 2: Add Styling to the HTML structure

We need to define basic stylings and animations. Below is the complete style.css:


html, body {
  padding: 0px;
  margin: 0px;
  background:#131417;
  font-family: 'Karla', sans-serif;
  color: #FFF;
  height: 100%;
}

body { 
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  overflow: hidden;
}

.info {
  position: fixed;
  top: 20px;
  left: 20px;
}

.container {
  border-radius: 100%;
  width: 400px;
  height: 400px;
  overflow:hidden;
  user-select: none;
  margin: auto;
  position: relative;
  border: 0px solid #000;
  box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.2), inset 0px 0px 10px rgba(0, 0, 0, 0.5);
  background: #b40ad2;
  display:flex;
  animation:flyin 2s;
}
.container .help {
  position: absolute;
  top: -40px;
  left: 0px;
  width: 100%;
  text-align: center;
}
.container .plane {
  position: absolute;
  top: 0px;
  left: 0px;
  user-select: none;
}
.container .plane .row {
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  margin-top: -13.33333px;
}
.container .plane .row:nth-child(even) {
  margin-left: 50px;
}
.container .plane .row .icon {
  user-select: none;
  cursor: move;
  flex: 1;
  min-width: 100px;
  max-width: 100px;
  height: 100px;
  position: relative;
  box-sizing: border-box;
}
.container .plane .row .icon:active {
  cursor: move;
}
.container .plane .row .icon .draw {
  box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.5);
  user-select: none;
  font-size: 14px;
  background: black;
  width: 100%;
  height: 100%;
  position: absolute;
  top: 50%;
  left: 50%;
  border-radius: 100%;
  transform: translateX(-50%) translateY(-50%) scale(0.5, 0.5);
  text-align: center;
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
}
.container .plane .row .icon .draw:after{
   content: '';
   position:absolute;
   width:100%;
   height:100%;
   border-radius:100%;
   top:50%;
   left:50%;
   transform:translateX(-50%) translateY(-50%);
   border:2px solid #000;
   transition:all 0.5s;
}
.container .plane .row .icon.open{
   z-index:1000;
}
.container .plane .row .icon.open .draw:after{
   transition:all 1s;
   width:500%;
   height:500%;
   background:rgba(0, 0, 0, 1);
}
.container .plane .row .icon .draw i {
  font-size: 36px;
  margin: auto;
  display: block;
}

.container .app{
   pointer-events:none;
   z-index:100000;
   opacity:0;
   color:#FFF;
   top:0px;
   left:0px;
   width:75%;
   margin:auto;
   font-size:28px;
   overflow:hidden;
   text-align:center;
   transition:all 0.25s;
}

.container .app .title{
   font-size:46px;
   font-weight:600;
   transition: all 0.25s;
   transition-delay:0.75s;
}
.container .app .item{
   margin-top:10px;
   font-weight:600;
   position:relative;
   width:auto;
   overflow:hidden;
   cursor:pointer;
   display:inline-block;
   left:100%;
   opacity:0;
   transition: 0s;
}
.container .app .item:nth-of-type(0){
   transition-delay: 0.25s;
}
.container.open .app .item{
   transition:all 0.25s;
   left:0px;
   opacity:1;
}
.container.open .app .item:nth-of-type(1){
   transition-delay: 0.5s;
}
.container.open .app .item:nth-of-type(2){
   transition-delay: 0.75s;
}
.container.open .app .item:nth-of-type(3){
   transition-delay: 1s;
}
.container.open .app .item:nth-of-type(4){
   transition-delay: 1.25s;
}
.container.open .app .item:nth-of-type(5){
   transition-delay: 1.5s;
}

.container .app .item:after{
   content:"";
   position:absolute;
   bottom:0px;
   left:-100%;
   height:4px;
   width:100%;
   opacity:0;
   background:#FFF;
   transition:all 0.25s;
}
.container .app .item:hover:after{
   left:0px;
   opacity:1;
}
.container .app .close{
   color:#F22;
}
.container.open .app{
   transition: all 0.25s;
   transition-delay:0.75s;
   opacity:1 !important;
   z-index:10000;
   transform:translateX(0px) translateY(0px);
   pointer-events:initial;
}

@keyframes flyin{
   from{ transform: translateY(200%) rotateZ(180deg); }
}




Step 3: Make our Application Interactive with Javascript


 Here’s the complete code for main.js: 



var gridSize = 10 
var iconList = [
   'bluetooth', 'brightness_high', 'directions_transit', 'settings',
   'event', 'headset', 'help', 'insert_chart', 'library_music'
]

var html = setupHTML()
listenEvents()

//Send it
setInterval(() => {
   updatePosition()
   updateIconSizes()
}, 1000/60)

function setupHTML(){
   var html = {
      container: document.querySelector('.container'),
      plane: document.querySelector('.plane'),
      appClose: document.querySelector('.app .close')
   }

   for(var i = 0; i < gridSize; i++){
      var icons = ''
      for(var o = 0; o < gridSize; o++){
         icons += `
            <div class="icon">
               <div class="draw">
                  <i class="material-icons">${utilChoose(iconList)}</i>
               </div>
            </div>`
      }
      html.plane.innerHTML += `<div class="row">${icons}</div>`
   }
   html.icons = document.querySelectorAll('.icon')
   return html
}

function listenEvents(){
   var that = this
   for(var i = 0; i < html.icons.length; i++){
      html.icons[i].addEventListener('mouseup', function(e){
         that.eventIconClick(this, e)
      })
   }
   html.container.addEventListener('mousedown', function(e){ eventMouseDown(pos(e)) })
   html.container.addEventListener('mouseup', function(e){ eventMouseUp(pos(e)) })
   html.container.addEventListener('mouseleave', function(e){ eventMouseUp(pos(e)) })
   html.container.addEventListener('mousemove', function(e){ eventMouseMove(pos(e)) })
   html.appClose.addEventListener('click', function(e){ eventCloseApp() })
}


function eventMouseUp(pos){
   if(mouse.state == 'up') return
   mouse.state = 'up'
}

function eventMouseDown(pos){
   mouse.state = 'down'
   mouse.pos.offset = utilTransfer(pos)
   mouse.pos.move = utilTransfer(pos)
}

function eventMouseMove(pos){
   if(mouse.state === 'up') return
   mouse.state = "move"

   mouse.pos.move = utilTransfer(pos)
}

function eventIconClick(ele, event){
   if(mouse.state === 'move') return
   ele.classList.add('open')
   html.container.classList.add('open')

   var box = ele.getBoundingClientRect()
   var contBox = html.container.getBoundingClientRect()
   mouse.pos.click = {
      x: mouse.pos.current.x - (box.left - contBox.left) + contBox.width/2 - box.width/2,
      y: mouse.pos.current.y - (box.top - contBox.top) + contBox.height/2 - box.height/2
   }
}

function eventCloseApp(pos){
   html.container.classList.remove('open')
   for(var i = 0; i < html.icons.length; i++){
      html.icons[i].classList.remove('open')
   }
}
//-------------------------------------------------------------------------------//
//-------------------------------| Positioning |---------------------------------//
//-------------------------------------------------------------------------------//
var mouse = {
   state: 'up',
   pos: {
      offset: {x: 0, y: 0},
      move: {x: 0, y: 0},
      current: {x: 0, y: 0},
      click: {x: 0, y: 0},
      old: {x: 0, y: 0}
   }
}

function updatePosition(){
   // This is going to get bumpy
   if(mouse.state == "move"){
      mouse.pos.current.x += mouse.pos.move.x - mouse.pos.offset.x
      mouse.pos.current.y += mouse.pos.move.y - mouse.pos.offset.y
      mouse.pos.offset = utilTransfer(mouse.pos.move)
      mouse.pos.click = utilTransfer(mouse.pos.current)
   }
   if(mouse.state == "up"){
      mouse.pos.current.x -= (mouse.pos.current.x - mouse.pos.click.x)/10
      mouse.pos.current.y -= (mouse.pos.current.y - mouse.pos.click.y)/10
   }

   var transform = `translateX(${mouse.pos.current.x}px) translateY(${mouse.pos.current.y}px)`
   html.plane.style.transform = transform
}

function updateIconSizes(){
   for(var i = 0; i < html.icons.length; i++){
      // position
      var contBox = html.container.getBoundingClientRect()
      var iconBox = html.icons[i].getBoundingClientRect()

      var iconCenter = {
         x: iconBox.left+iconBox.width/2,
         y: iconBox.top+iconBox.height/2
      }

      var contCenter = {
         x: contBox.left + contBox.width/2,
         y: contBox.top + contBox.height/2
      }

      var center = {
         x: (contCenter.x - iconCenter.x),
         y: (contCenter.y - iconCenter.y)
      }
      var distance = Math.min(Math.floor(utilDistance({x:0, y:0}, center)), contBox.width/2)
      var percent = Math.min((1 - distance/(contBox.width/2))*1.5, 1)

      var iconDraw = html.icons[i].getElementsByClassName('draw')[0]

      iconDraw.style.transform = `translateX(-50%) translateY(-50%) scale(${percent}, ${percent})`
      iconDraw.style.opacity = percent
   }
}

function pos(e){
   var box = html.container.getBoundingClientRect()
   return {
      x: e.clientX - box.left,
      y: e.clientY - box.top
   }
}


function utilChoose(arr){
   return arr[Math.floor(Math.random()*arr.length)]
}

function utilTransfer(obj){
   return JSON.parse(JSON.stringify(obj))
}

function utilDistance(pos1, pos2){
   return Math.sqrt(Math.pow((pos2.x - pos1.x),2) + Math.pow((pos2.y - pos1.y),2))
}




Step 4: Testing the application

Open the index.html file in your browser:

Interactivedrag.png 58.6 KB




Project Recap:

In this project we demonstrate how to build a creative and interactive web application using fundamental web technologies. Feel free to expand this tutorial.





What’s Next?

If you enjoyed this tutorial, check out our other posts on animations and UI design.

Happy coding! 🎉






Discussion (0)

Log in to comment!

No comments yet!