JSFiddle - React, Tailwind, and code Playground
HTML
<div id="stage">
<div id="rect">
<div id="sprite" data-selectable="true"></div>
</div>
<ul id="list">
<li class="list-item" data-selectable="true">Your colon is not really that great</li>
<li class="list-item" data-selectable="true">It's ok but I wouldn't hype it up</li>
<li class="list-item" data-selectable="true">You probably could live without it..</li>
<li class="list-item" data-selectable="true">For about an hour</li>
</ul>
</div>
CSS
* {
box-sizing: border-box;
}
body {
position: relative;
margin: 0px;
color: white;
font-family: 'Roboto', sans-serif;
font-size: 16px;
}
#stage {
position: relative;
cursor: crosshair;
background-color: #dff4fe;
display: flex;
flex-flow: row wrap;
}
.selected {
outline: 2px solid red;
}
#rect {
flex: 1 100%;
}
#sprite {
width: 480px;
height: 360px;
animation: pulse 2s infinite alternate, sprite .833333333s steps(10) infinite;
background: url(https://dl.dropboxusercontent.com/u/1385229/anim-meds.png);
}
#list {
flex: 1 100%;
animation: wiggle 1s infinite alternate;
background-color: white;
color: #46698a;
padding: 1em;
list-style-position: inside;
border-radius: 1em;
box-shadow: 8px 8px rgba(63, 103, 129, .15);
}
#select-box {
position: absolute;
outline: 2px solid red;
z-index: 100;
width: 1px;
height: 1px;
top: 0px;
left: 0px;
transform-origin: top left;
}
@keyframes wiggle {
100% { transform: translateY(10%); }
}
@keyframes pulse {
100% { transform: scale(0.5, 0.5); }
}
@keyframes sprite {
100% { background-position: -4800px; }
}
@media (min-width: 600px) {
#rect, #list {
flex: 1 auto;
}
}
JavaScript
const STAGE_EL = document.getElementById('stage')
function traverseDOMWith ( fn, node ) {
fn (node)
for ( var i = 0, c; c = node.childNodes[i]; i++ ) traverseDOMWith(fn, c)
}
function bcrOverlapBcr ( bcr1, bcr2 ) {
return bcr1.left < bcr2.right
&& bcr1.right > bcr2.left
&& bcr1.top < bcr2.bottom
&& bcr1.bottom > bcr2.top
}
function highlightSelected ( sb, root ) {
const selectBoxBcr = sb.getBoundingClientRect()
traverseDOMWith(n => {
if ( !n.dataset || !n.dataset.selectable ) return
if ( bcrOverlapBcr(selectBoxBcr, n.getBoundingClientRect())) n.classList.add('selected')
else n.classList.remove('selected')
}, root)
}
const selectBox = document.createElement('div')
const mouse = {
down: [0, 0],
current: [0, 0],
mode: 'UP'
}
function render () {
const dx = mouse.current[0] - mouse.down[0]
const dy = mouse.current[1] - mouse.down[1]
selectBox.style.transform = `translate(${ mouse.down[0] }px, ${ mouse.down[1] }px)`
selectBox.style.width = dx + 'px'
selectBox.style.height = dy + 'px'
selectBox.style.display = mouse.mode === 'DOWN' ? 'block' : 'none'
requestAnimationFrame(render)
}
selectBox.setAttribute('id', 'select-box')
document.body.appendChild(selectBox)
STAGE_EL.appendChild(selectBox)
requestAnimationFrame(render)
stage.addEventListener('mousedown', e => {
e.preventDefault()
if ( mouse.mode === 'DOWN' ) return
mouse.mode = 'DOWN'
mouse.down[0] = e.clientX
mouse.down[1] = e.clientY
mouse.current[0] = e.clientX
mouse.current[1] = e.clientY
})
stage.addEventListener('mouseup', e => {
e.preventDefault()
mouse.mode = 'UP'
highlightSelected(selectBox, STAGE_EL)
})
stage.addEventListener('mousemove', e => {
e.preventDefault()
mouse.current[0] = e.clientX
mouse.current[1] = e.clientY
})