Drag and Drop Dots

In this example below you will see how to do a Drag and Drop Dots with some HTML / CSS and Javascript

Generate randomly sized, spaced, and colored dots Drag and drop dots around the page Detect collisions between the dots

Thumbnail
This awesome code was written by tessat, you can see more from this user in the personal repository.
You can find the original code on Codepen.io
Copyright tessat ©

Technologies

  • HTML
  • CSS
  • JavaScript
<!DOCTYPE html>
<html lang="en" >

<head>
  <meta charset="UTF-8">
  <title>Drag and Drop Dots</title>
  
  
  
      <link rel="stylesheet" href="css/style.css">

  
</head>

<body>

  <div class="dots-container"></div>
<a href="#" class="redraw">Again?</a>
  <script src='http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js'></script>

  

    <script  src="js/index.js"></script>




</body>

</html>

/*Downloaded from https://www.codeseek.co/tessat/drag-and-drop-dots-aAEDd */
a, a:hover, a:visited, a:active {
  text-decoration: none;
  color: #333;
}

a.redraw {
  display: block;
  position: fixed;
  width: 100%;
  bottom: 0px;
  left: 0px;
  text-align: center;
  color: #333;
  text-decoration: none;
  font-size: 30px;
  display: none;
}

.dot {
  top: -100px;
  position: absolute;
  overflow: hidden;
  cursor: pointer;
  cursor: hand;
  -webkit-border-radius: 200px;
  -moz-border-radius: 200px;
  -ms-border-radius: 200px;
  -o-border-radius: 200px;
  border-radius: 200px;
  -webkit-box-shadow:  0px 0px 2px 0px rgba(0, 0, 0, 0.05);
  -moz-box-shadow:  0px 0px 2px 0px rgba(0, 0, 0, 0.05);
  box-shadow:  0px 0px 2px 0px rgba(0, 0, 0, 0.05);
}
.dot:after {
  content: "";
  display: block;
  -webkit-border-radius: 200px;
  -moz-border-radius: 200px;
  -ms-border-radius: 200px;
  -o-border-radius: 200px;
  border-radius: 200px;
}
.dot.up {
  cursor: move;
  z-index: 100;
}
.dot.merging {
  -webkit-transition: all 0.5s linear 0s;
  -moz-transition: all 0.5s linear 0s;
  -o-transition: all 0.5s linear 0s;
  transition: all 0.5s linear 0s;
}
.dot.merging.od {
  -webkit-transition: all 0.2s linear 0s;
  -moz-transition: all 0.2s linear 0s;
  -o-transition: all 0.2s linear 0s;
  transition: all 0.2s linear 0s;
  z-index: 100;
}
.dot.grow {
  z-index: 100;
  -webkit-border-radius: 1000px;
  -moz-border-radius: 1000px;
  -ms-border-radius: 1000px;
  -o-border-radius: 1000px;
  border-radius: 1000px;
}
.dot .overlap {
  position: absolute;
  -webkit-border-radius: 200px;
  -moz-border-radius: 200px;
  -ms-border-radius: 200px;
  -o-border-radius: 200px;
  border-radius: 200px;
  -webkit-transition: background-color 1.5s linear 0s;
  -moz-transition: background-color 1.5s linear 0s;
  -o-transition: background-color 1.5s linear 0s;
  transition: background-color 1.5s linear 0s;
}

.msg {
  position: fixed;
  z-index: 200;
  text-shadow: 0px 0px 2px rgba(100, 100, 100, 0.5);
}


/*Downloaded from https://www.codeseek.co/tessat/drag-and-drop-dots-aAEDd */
function getRandomInt(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

function arrayUnion(array1, array2) {
	var newArray = [];
	$.merge($.merge(newArray, array1), array2);
	$.unique(newArray);
	return newArray;
}

$('body').ready(function($) {
  
  window.dots = new BasicDots($);
 
 
 $('a.redraw').on('click', function(e) {
   e.preventDefault();
   
   $('a.redraw').fadeOut(100);
   $('.dots-container').html("");
   window.dots = new BasicDots($);
   
   return false;
 });

});

var BasicDots = function($, generateDotData, onCompleted){
  var _this = this;
  
  // ***************
  // Variables
  // ***************

  var dotData = [];
  var inters  = [];
  var startPoint;
  

  // Called on instantiation
  function _init(){
    dotData = generateDotData();
    initializeDots();
    
    $(window).on('resize orientationchange', function() {
  		layoutDots();
  	});

  	$('.dots-container').on('mousemove', function(e) {
  		e.preventDefault();
  	});
  	
  	$('.dots-container').on('completed', function() {
  	  onCompleted();
  	});
  }

	
  // Application methods
  
  var generateDotData = generateDotData || function() {
    var dotData = [];
    var count = parseInt(($(window).height() + $(window).width())/75);

  	// Create and draw dots
  	for (var i=0; i<count; i++) {
  		var dot = {};

  		dot.iD = "dot"+i;

  		dot.diameter = getRandomInt(20, 100);

  		dot.rgb = {
  			r: getRandomInt(100, 255),
  			g: getRandomInt(100, 255),
  			b: getRandomInt(100, 255)
  		};
  		
  		dotData.push(dot);
  	}
  	return dotData;
  };
  
  
  var onCompleted = onCompleted || function() {
    $('a.redraw').fadeIn();
  };
  
  
  var initializeDots = function() {
  	// Draw dots
  	for (var i=0; i<dotData.length; i++) {
  		var dot = dotData[i];
  		drawDot(dot);
  	}

  	// Layout dots
  	layoutDots();

  	$('.dot').on('mousedown', function(e) {
  		startDrag($(this), {x: e.clientX, y: e.clientY});
  	});
  };
  
  
  var drawDot = function(dotData) {
  	$('.dots-container').prepend('<div id="'+dotData.iD+'" class="dot"></div>');

  	var $dot = $('#'+dotData.iD+'.dot');

  	$dot.css({
  		height: dotData.diameter,
  		width: dotData.diameter,
  		background: 'rgb('+dotData.rgb.r+','+dotData.rgb.g+','+dotData.rgb.b+')',
  	});
  };
  
  
  var layoutDots = function() {
  	var h = $(window).height();
  	var w = $(window).width();

  	$('.dot').each(function() {
  		$dot = $(this);

  		do {
  			var top 	= getRandomInt(10, h-110);
  			var left 	= getRandomInt(10, w-110);

  			$dot.css({
  				top: top,
  				left: left,
  			});

  			var collided 	= findCollisions($dot);
  			var isCollided = (collided.length > 0);
  		} while (isCollided);

  	});
  };
  
  
  var startDrag = function($dot, startPoint) {
  	$dot.addClass('up');

  	var collided 		= [];
  	$('.dots-container').on('mousemove', function(e) {
  		e.preventDefault();

  		// Move with drag
  		var dX = e.clientX - startPoint.x;
  		var dY = e.clientY - startPoint.y;

  		$dot.css({
  			left: parseFloat($dot.css('left').replace('px', '')) + dX + 'px',
  			top: parseFloat($dot.css('top').replace('px', '')) + dY + 'px',
  		});

  		startPoint = {
  			x: e.clientX,
  			y: e.clientY
  		};


  		// Manage collisions

  		var oldCollided = collided;
  		collided 		= findCollisions($dot);
  		var union 	= arrayUnion(oldCollided, collided);
  		for (var i=0; i<union.length; i++) {
  			// Collide 
  			if ((oldCollided.indexOf(union[i]) == -1) && (collided.indexOf(union[i]) > -1)) {
  				collide($dot, $('#'+union[i]));
  			// Move collision
  			} else if ((oldCollided.indexOf(union[i]) > -1) && (collided.indexOf(union[i]) > -1)) {
  				moveCollision($dot, $('#'+union[i]));
  			// Uncollide	
  			} else if ((oldCollided.indexOf(union[i]) > -1) && (collided.indexOf(union[i]) == -1)) {
  				uncollide($dot, $('#'+union[i]));
  			}	
  		}
  	});

  	$('.dots-container').on('mouseup', function() {
  		stopDrag($dot, collided);
  	});
  };
  
  
  var stopDrag = function($dot, collided) {
  	$dot.removeClass('up');
  	$('.dots-container').off('mousemove mouseup');

  	// Collision resolution
  	for (var i=0;i<collided.length;i++) {
  		resolveColision($dot, $('#'+collided[i]));
  	}
  };
  
  
  var findCollisions = function($dot) {
  	$dot.addClass('checking');

  	var midpoint = {
  		x: $dot.position().left + ($dot.width()/2),
  		y: $dot.position().top + ($dot.height()/2)
  	};

  	var collided = [];
  	$('.dot:not(.checking)').each(function() {
  		var minD = ($dot.width()/2) + ($(this).width()/2);
  		var cMidpoint = {
  			x: $(this).position().left + ($(this).width()/2),
  			y: $(this).position().top + ($(this).height()/2)
  		};
  		var x = (midpoint.x - cMidpoint.x);
  		var y = (midpoint.y - cMidpoint.y);
  		var d = Math.sqrt((x*x)+(y*y));
  		if (d < minD) {
  			collided.push($(this).attr('id'));
  		}
  	});

  	$dot.removeClass('checking');

  	return collided;
  };
  
  
  var collide = function($originalDot, $collideDot) {
  	var $overlap = $originalDot.find('.overlap[data-collided-with="'+$collideDot.attr('id')+'"]');
  	if ($overlap.length == 0) {
  		$originalDot.prepend('<div class="overlap" data-collided-with="'+$collideDot.attr('id')+'"></div>');
  		$overlap = $originalDot.find('.overlap[data-collided-with="'+$collideDot.attr('id')+'"]');

  		// Calculate background color 
  		var rgb1 = $originalDot.css('background-color').match(/(?:rgb\()(.*?)(?:\))/)[1].split(", ");
  		var rgb2 = $collideDot.css('background-color').match(/(?:rgb\()(.*?)(?:\))/)[1].split(", ");

  		var r = parseInt((parseInt(rgb1[0]) + parseInt(rgb2[0]))/2);
  		var g = parseInt((parseInt(rgb1[1]) + parseInt(rgb2[1]))/2);
  		var b = parseInt((parseInt(rgb1[2]) + parseInt(rgb2[2]))/2);

  		$overlap.css({
  			backgroundColor: 'rgb('+r+','+g+','+b+')',
  			height: $collideDot.css('height'),
  			width: $collideDot.css('width'),
  			boxShadow: '0px 0px 2px rgb('+r+','+g+','+b+')',
  		});

  		oscillateColor($overlap, 'rgb('+rgb1[0]+', '+rgb1[1]+', '+rgb1[2]+')', 'rgb('+rgb2[0]+', '+rgb2[1]+', '+rgb2[2]+')');
  	}
  	moveCollision($originalDot, $collideDot);
  };
  
  
  var moveCollision = function($originalDot, $collideDot) {
  	var $overlap = $originalDot.find('.overlap[data-collided-with="'+$collideDot.attr('id')+'"]');
  	if ($overlap.length == 0) {
  		collide($originalDot, $collideDot);
  		$overlap = $originalDot.find('.overlap[data-collided-with="'+$collideDot.attr('id')+'"]');
  	}

  	var top 	= parseFloat($collideDot.css('top').replace('px', '')) - parseFloat($originalDot.css('top').replace('px', ''));
  	var left 	= parseFloat($collideDot.css('left').replace('px', '')) - parseFloat($originalDot.css('left').replace('px', ''));

  	$overlap.css({
  		top: top + 'px',
  		left: left + 'px'
  	});
  };


  var uncollide = function($originalDot, $collideDot) {
  	$overlap = $originalDot.find('.overlap[data-collided-with="'+$collideDot.attr('id')+'"]');
  	if ($overlap.length > 0) {
  		$overlap.remove();
  	}
  };
  
  
  var resolveColision = function($originalDot, $collideDot) {
  	$originalDot.find('.overlap').remove();
  	for (var i=0;i<inters.length;i++) {
  		clearInterval(inters[i]);
  	}
  	// Calculate background color 
  	var rgb1 = $originalDot.css('background-color').match(/(?:rgb\()(.*?)(?:\))/)[1].split(", ");
  	var rgb2 = $collideDot.css('background-color').match(/(?:rgb\()(.*?)(?:\))/)[1].split(", ");

  	var r = parseInt((parseInt(rgb1[0]) + parseInt(rgb2[0]))/2);
  	var g = parseInt((parseInt(rgb1[1]) + parseInt(rgb2[1]))/2);
  	var b = parseInt((parseInt(rgb1[2]) + parseInt(rgb2[2]))/2);

  	// Calculate size
  	var diameter = (parseFloat($collideDot.css('width').replace('px', '')) + parseFloat($originalDot.css('width').replace('px', '')))/2;

  	// Calculate position
  	var top		= parseFloat($originalDot.css('top').replace('px', ''));
  	var left	= parseFloat($originalDot.css('left').replace('px', ''));

  	$originalDot.addClass('merging');
  	$originalDot.addClass('od');
  	$originalDot.css({
  		backgroundColor: 'rgb('+r+','+g+','+b+')',
  		height: diameter,
  		width: diameter,
  		top: top,
  		left: left
  	});
  	$collideDot.addClass('merging');
  	$collideDot.css({
  		backgroundColor: 'rgb('+r+','+g+','+b+')',
  		height: diameter,
  		width: diameter,
  		top: top,
  		left: left
  	});

  	setTimeout(function() {
  		$originalDot.removeClass('merging');
  		$originalDot.removeClass('od');
  		$collideDot.remove();

  		checkSingleDot();
  	},500);
  };
  
  
  var checkSingleDot = function() {
  	if ($('.dot').length == 1) {
  		$('.dots-container').trigger('completed');
  	}
  };
  
  
  var oscillateColor = function($overlap, rgb1, rgb2) {
  	$overlap.css('background-color', rgb2);
  	setTimeout(function() {
  		$overlap.css('background-color', rgb1);
  	},1000);
  	var inter = setInterval(function() {
  		$overlap.css('background', rgb2);
  		setTimeout(function() {
  			$overlap.css('background-color', rgb1);
  		},1000);
  	},2000);
  	inters.push(inter);
  };


  /* ************************************************************************** */
  /* At the end of App instantiation, call the init function of the App object. */
  /* ************************************************************************** */
  _init.call(this);
};

Comments