HTML
SCSS
.circle { $size: 100px; position: absolute; background: #555; display: inline-block; width: $size; height: $size; border-radius: 100%; cursor: pointer; transition: 200ms ease-in-out; transition-property: transform, background, opacity, box-shadow; transform: scale(.2, .2); box-shadow: inset -2px -2px 50px rgba(0, 0, 0, .5); opacity: .9; &.active { display: inline-block; transform: scale(1, 1); } &.dragging { box-shadow: inset -2px -2px 50px rgba(0, 0, 0, .8); opacity: 1; } &[data-hitting] { background: #f33; } &[data-hitting="0"] { background: #999; } // http://www.perbang.dk/rgbgradient/ &[data-hitting="1"] { background: #AA9393; } &[data-hitting="2"] { background: #BB8989; } &[data-hitting="3"] { background: #CC7A7A; } &[data-hitting="4"] { background: #DD6767; } &[data-hitting="5"] { background: #EE4F4F; } }
Script
let $circleCollector = $(), circleIterator = 0, zIndexIterator = 0 const createCircle = _ => { console.log('creating new circle') let $circle = $('<div />', { class: 'circle', 'data-hitting': '0' }).data('hitting', $()) $circleCollector = $circleCollector.add($circle) $circle.on('mousedown', dragStart) $(document.body).append($circle) setTimeout(_ => $circle.addClass('active')) return $circle }, circleHitTest = (offset1, offset2, diameter) => { return Math.sqrt( Math.pow(offset1.left - offset2.left, 2) + Math.pow(offset1.top - offset2.top, 2) ) < diameter }, setHitting = (status, $circle1, $circle2) => { let callback = status ? 'add' : 'not', hitting1 = $circle1.data('hitting')[callback]($circle2), hitting2 = $circle2.data('hitting')[callback]($circle1) if (status) console.log('hitting') $circle1.data('hitting', hitting1) $circle2.data('hitting', hitting2) $circle1.attr('data-hitting', hitting1.length) $circle2.attr('data-hitting', hitting2.length) }, dragStart = function(event, forceOffset) { console.log('drag start') let $self = $(event.target), offset = forceOffset || { x: event.originalEvent.offsetX, y: event.originalEvent.offsetY, } $self.css('z-index', ++zIndexIterator).addClass('dragging') $(window).on('mousemove.dragging', event => { let pos = { x: event.originalEvent.pageX, y: event.originalEvent.pageY, }, selfId = $self.data('id') $self.css({ left: (pos.x - offset.x) + 'px', top: (pos.y - offset.y) + 'px', }) // collision detection $circleCollector.not($self).each((i, other) => { const $other = $(other), otherId = $other.data('id'), selfOffset = $self.offset(), otherOffset = $other.offset(), diameter = $self.outerWidth() setHitting( circleHitTest(selfOffset, otherOffset, diameter), $self, $other) }) }).on('mouseup.dragging', event => { console.log('drag end') $self.removeClass('dragging') $(window).off('.dragging') }) }, $add = $('<div />', { class: 'circle' }) .appendTo(document.body) .on('mousedown', event => { createCircle().trigger('mousedown', { x: event.target.clientWidth / 2, y: event.target.clientHeight / 2, }) })