<h1>Draggable with "droppable" logic</h1>
<p>Drag the boxes around. We'll run <code>hitTest()</code> logic inside an <code>onDrag</code>, highlighting "droppable" elements but only when at least 50% of their surface area overlaps (you can change the <code>overlapThreshold</code> variable to whatever you want). Then, in an <code>onDragEnd</code>, we'll do the same and make any intersecting "droppable" elements flash. Read the comments in the code to find out how to test for the mouse/touch overlapping an element instead.</p>
<div id="container">
  <div id="box1" class="box gradient-green">box1</div>
  <div id="box2" class="box gradient-blue">box2</div>
  <div id="box3" class="box gradient-red">box3</div>
</div>
body {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  flex-direction: column;
}
h1 {
  text-align: center;
}
body {
  padding: 1rem;
}
#container {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-around;
  gap: 1rem;
  margin-top: 2rem;
}
.box {
  width: 200px;
  height: 80px;
  text-align: center;
  line-height: 80px;
  font-size: 20px;
  color: var(--color-just-black);
  border-radius: 10px;
}

.highlight {
  outline: 4px dashed var(--color-surface-white);
  width: 196px;
  height: 76px;
  line-height: 76px;
}

p {
  width: 80vw;
}
//see https://www.greensock.com/draggable/ for more details.

var droppables = $(".box");

//the overlapThreshold can be a percentage ("50%", for example, would only trigger when 50% or more of the surface area of either element overlaps) or a number of pixels (20 would only trigger when 20 pixels or more overlap), or 0 will trigger when any part of the two elements overlap.
var overlapThreshold = "50%"; 

//we'll call onDrop() when a Draggable is dropped on top of one of the "droppables", and it'll make it "flash" (blink opacity). Obviously you can do whatever you want in this function.
function onDrop(dragged, dropped) {
  gsap.fromTo(dropped, {opacity:1}, {duration: 0.1, opacity:0, repeat:3, yoyo:true});
}

Draggable.create(droppables, {
  bounds:window,
  onDrag: function(e) {
    var i = droppables.length;
		 while (--i > -1) {
       if (this.hitTest(droppables[i], overlapThreshold)) {
         $(droppables[i]).addClass("highlight");
       } else {
         $(droppables[i]).removeClass("highlight");
       }
       
       /* ALTERNATE TEST: you can use the static Draggable.hitTest() method for even more flexibility, like passing in a mouse event to see if the mouse is overlapping with the element...
       if (Draggable.hitTest(droppables[i], e) && droppables[i] !== this.target) {
         $(droppables[i]).addClass("highlight");
       } else {
         $(droppables[i]).removeClass("highlight");
       }
       */
    }
  },
  onDragEnd:function(e) {
		var i = droppables.length;
		while (--i > -1) {
			if (this.hitTest(droppables[i], overlapThreshold)) {
				onDrop(this.target, droppables[i]);
			}
		}
	}
});
Run Pen

External CSS

  1. https://codepen.io/GreenSock/pen/xxmzBrw/fcaef74061bb7a76e5263dfc076c363e.css

External JavaScript

  1. //cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js
  2. https://cdn.jsdelivr.net/npm/gsap@3.0.1/dist/gsap.min.js
  3. https://cdn.jsdelivr.net/npm/gsap@3.0.1/dist/Draggable.min.js