JSFiddle - React, Tailwind, and code Playground
by Andrei Gheorghiu
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.min.js"></script>
<div class="dropzone">
<div id="draggable" draggable="true" ondragstart="event.dataTransfer.setData('text/plain',null)"></div>
</div>
<div class="dropzone"></div>
<div class="dropzone"></div>
<div class="dropzone"></div>
<div id="app">
<table>
<thead><tr>
<th>Count</th>
<th>Min</th>
<th>Max</th>
<th>Avg</th>
</tr></thead>
<tbody><tr>
<td v-text="c.length" />
<td v-text="min" />
<td v-text="max" />
<td v-text="avg" />
</tr></tbody>
</table>
<p>
<em>* Values in miliseconds</em>
</p>
</div>
<div class="visual-test">
<h4>
<code>250ms</code>
</h4>
<h4>
<span>No delay</span>
<span>Delayed</span>
</h4>
<div id="instant"></div>
<div id="delayed"></div>
</div>
CSS
#draggable {
width: 200px;
height: 20px;
text-align: center;
background: white;
}
.dropzone {
width: 200px;
height: 20px;
background: blueviolet;
margin-bottom: 10px;
padding: 10px;
}
body {
background-color: #eee;
}
table {
background-color: white;
width: 220px;
padding: 10px;
}
td, th {
font-family: monospace;
text-align: right;
width: 25%;
}
.visual-test {
display: flex;
justify-content: space-around;
flex-wrap: wrap;
position: fixed;
right: 0;
top: 0;
width: 250px;
}
h4 {
flex: 100%;
margin: 1rem 0;
display: flex;
justify-content: space-around;
}
#delayed, #instant {
width: 40px;
height: 40px;
background-color: #212121;
border-radius: 20px;
}
#delayed.active, #instant.active {
transform: translateY(100px);
}
#delayed.active {
transition-delay: 250ms;
}
JavaScript
var dragged;
const delayed = document.querySelector('#delayed');
const instant = document.querySelector('#instant');
var t_0 = performance.now();
var t_1;
var t_2;
var collector = [];
document.addEventListener("dragstart", function(event) {
dragged = event.target;
t_0 = performance.now();
t_1 = undefined;
t_2 = undefined;
}, false);
document.addEventListener("dragend", function(event) {
t_2 = performance.now() - t_0;
if (t_2 && t_1) {
delayed.classList.add('active');
instant.classList.add('active');
collector.push(t_2 - t_1);
}
}, false);
document.addEventListener("dragover", function(event) {
event.preventDefault();
}, false);
document.addEventListener("drop", function(event) {
t_1 = performance.now() - t_0;
event.preventDefault();
if (event.target.className == "dropzone") {
dragged.parentNode.removeChild(dragged);
event.target.appendChild(dragged);
}
}, false);
new Vue({
el: '#app',
data: () => ({
c: []
}),
computed: {
min() {
return this.c.length ? this.log(Math.min(...this.c)) : undefined
},
max() {
return this.c.length ? this.log(Math.max(...this.c)) : undefined
},
avg() {
return this.c.length ?
this.log(this.c.reduce((a, b) => a + b, 0) / this.c.length) :
undefined
}
},
mounted() {
document.addEventListener('drop', () => {
setTimeout(() => {
this.c = window.collector;
});
setTimeout(() => {
delayed.classList.remove('active');
instant.classList.remove('active');
}, '1e3')
})
},
methods: {
log(n) {
return Math.round(n * 1e3)/1e3
}
}
})