312 lines
10 KiB
HTML
312 lines
10 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en" dir="ltr">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>MT Request: draw over the image</title>
|
|
<style media="screen">
|
|
#sample, svg{
|
|
position:absolute;
|
|
top: 0;
|
|
left: 0;
|
|
bottom: 0;
|
|
right:0;
|
|
width:100%;
|
|
height:100%;
|
|
font-family: sans-serif;
|
|
}
|
|
|
|
path {
|
|
fill: none;
|
|
stroke: red;
|
|
stroke-width: 2px;
|
|
}
|
|
body.submitted path{
|
|
stroke:darkgray;
|
|
}
|
|
|
|
body.submitted .buttons{
|
|
display:none;
|
|
}
|
|
|
|
/*#wrapper {
|
|
height: calc({DRAW_HEIGHT}/{HEIGHT} * 100%);
|
|
width: calc({DRAW_WIDTH}/{WIDTH} * 100%);
|
|
position: absolute;
|
|
left: calc(({WIDTH} - {DRAW_WIDTH})/2/{WIDTH} * 100%);
|
|
top: calc(({HEIGHT} - {DRAW_HEIGHT})/2/{HEIGHT} * 100%);
|
|
background:none;
|
|
cursor: url(cursor.png) 6 6, auto;
|
|
}*/
|
|
#wrapper {
|
|
position:absolute;
|
|
top:0;
|
|
right:0;
|
|
bottom:0;
|
|
left:0;
|
|
background:none;
|
|
cursor: url(cursor.png) 6 6, auto;
|
|
}
|
|
.gray{
|
|
position:absolute;
|
|
background:rgba(255,255,255,0.7);
|
|
}
|
|
#gray_top{
|
|
left:0;
|
|
right:0;
|
|
top:0;
|
|
height:calc({TOP_PADDING}/{HEIGHT} * 100%);
|
|
}
|
|
#gray_bottom{
|
|
left:0;
|
|
right:0;
|
|
bottom:0;
|
|
height:calc(({HEIGHT} - {DRAW_HEIGHT} - {TOP_PADDING})/{HEIGHT} * 100%);
|
|
}
|
|
#gray_left{
|
|
left:0;
|
|
top:calc({TOP_PADDING}/{HEIGHT} * 100%);
|
|
height: calc({DRAW_HEIGHT}/{HEIGHT} * 100%);
|
|
width: calc({LEFT_PADDING}/{WIDTH} * 100%);
|
|
}
|
|
#gray_right{
|
|
right:0;
|
|
top:calc({TOP_PADDING}/{HEIGHT} * 100%);
|
|
height: calc({DRAW_HEIGHT}/{HEIGHT} * 100%);
|
|
width: calc(({WIDTH} - {DRAW_WIDTH} - {LEFT_PADDING})/{WIDTH} * 100%);
|
|
}
|
|
html, body{
|
|
height: 100%;
|
|
width: 100%;
|
|
margin:0;
|
|
background:gray;
|
|
}
|
|
#interface{
|
|
background:white;
|
|
height: 0;
|
|
overflow: hidden;
|
|
padding-top: calc({HEIGHT}/{WIDTH} * 100%);
|
|
background: white;
|
|
position: relative;
|
|
margin: 0 auto;
|
|
background-size: 100% 100%;
|
|
}
|
|
#innerface{
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
@media (min-aspect-ratio: {WIDTH}/{HEIGHT}) {
|
|
#interface {
|
|
height: 100vh;
|
|
width: calc({WIDTH}/{HEIGHT} * 100vh);
|
|
padding-top:0;
|
|
}
|
|
}
|
|
#info{
|
|
position: absolute;
|
|
bottom: 5px;
|
|
width: 600px;
|
|
left: calc(50% - 250px);
|
|
z-index: 999;
|
|
}
|
|
.buttons{
|
|
text-align: center;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id='interface' style="background-image:url('{IMAGE_URL}')">
|
|
<div id='innerface'>
|
|
<div id='wrapper'>
|
|
<!-- <img src="{IMAGE_URL}" id='sample'>-->
|
|
<svg id="canvas" viewBox="0 0 {WIDTH}0 {HEIGHT}0" width="{WIDTH}mm" height="{HEIGHT}mm" preserveAspectRatio="none">
|
|
<path d="" id="stroke" />
|
|
</svg>
|
|
</div>
|
|
<div id='info'>
|
|
<ul>
|
|
<li>Drag the mouse to trace the <strong>clearest lines</strong> drawing above</li>
|
|
<li>Follow the lines as precise as possible</li>
|
|
<li>Press submit when you're done.</li>
|
|
<li>You'll receive a submission token, to fill in at Mechanical Turk</li>
|
|
</ul>
|
|
<div class='buttons'>
|
|
<button id='submit'>Submit</button>
|
|
<!-- <button id='reset'>Reset</button>-->
|
|
</div>
|
|
<div id='message'></div>
|
|
</div>
|
|
<div class='gray' id='gray_top'></div>
|
|
<div class='gray' id='gray_bottom'></div>
|
|
<div class='gray' id='gray_left'></div>
|
|
<div class='gray' id='gray_right'></div>
|
|
</div>
|
|
</div>
|
|
<script type="text/javascript">
|
|
let url = window.location.origin.replace('http', 'ws') +'/ws?' + window.location.search.substring(1);
|
|
let svgEl = document.getElementById("canvas");
|
|
let strokeEl = document.getElementById('stroke');
|
|
let submitEl = document.getElementById('submit');
|
|
let resetEl = document.getElementById('reset');
|
|
let messageEl = document.getElementById('message');
|
|
let innerFaceEl = document.getElementById('innerface'); // wrapper within the interface
|
|
|
|
let svgWidth = {WIDTH};
|
|
let svgHeight = {HEIGHT};
|
|
let drawWidth = {DRAW_WIDTH};
|
|
let drawHeight = {DRAW_HEIGHT};
|
|
|
|
let xPadding = {LEFT_PADDING} / svgWidth;
|
|
let yPadding = {TOP_PADDING} / svgHeight;
|
|
let drawWidthFactor = drawWidth / svgWidth;
|
|
let drawHeightFactor = drawHeight / svgHeight;
|
|
|
|
let strokes = [];
|
|
let isDrawing = false;
|
|
let hasMouseDown = false;
|
|
let currentPoint = null;
|
|
|
|
let getCoordinates = function(e) {
|
|
// convert event coordinates into relative positions on x & y axis
|
|
let box = innerFaceEl.getBoundingClientRect();
|
|
let x = (e.x - box['left']) / box['width'];
|
|
let y = (e.y - box['top']) / box['height'];
|
|
return {'x': x, 'y': y};
|
|
}
|
|
|
|
let isInsideBounds = function(pos) {
|
|
return !(pos['x'] < 0 || pos['y'] < 0 || pos['x'] > 1 || pos['y'] > 1);
|
|
}
|
|
let isInsideDrawingBounds = function(pos) {
|
|
if(pos['x'] > xPadding && pos['x'] < (xPadding+drawWidthFactor) && pos['y'] > yPadding && pos['y'] < yPadding+drawHeightFactor) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
let draw = function(e) {
|
|
let pos = getCoordinates(e);
|
|
|
|
if(!isInsideBounds(pos)) {
|
|
// outside of bounds
|
|
return;
|
|
}
|
|
|
|
if(isDrawing && !isInsideDrawingBounds(pos)){
|
|
stopDrawing(pos);
|
|
}
|
|
if(!isDrawing && hasMouseDown && isInsideDrawingBounds(pos)){
|
|
isDrawing = true;
|
|
}
|
|
|
|
if(isDrawing) {
|
|
strokes.push([pos['x'], pos['y'], 0]);
|
|
let d = strokes2D(strokes);
|
|
strokeEl.setAttribute('d', d);
|
|
}
|
|
|
|
console.log([pos['x'], pos['y']], isDrawing);
|
|
socket.send(JSON.stringify({
|
|
'action': 'move',
|
|
'direction': [pos['x'], pos['y']],
|
|
'mouse': isDrawing,
|
|
}));
|
|
};
|
|
|
|
let stopDrawing = function(pos) {
|
|
if(!isDrawing) {
|
|
return;
|
|
}
|
|
isDrawing = false;
|
|
//document.body.removeEventListener('mousemove', draw);
|
|
|
|
|
|
if(strokes.length > 0){
|
|
// mark point as last of stroke
|
|
strokes[strokes.length - 1][2] = 1;
|
|
}
|
|
socket.send(JSON.stringify({
|
|
'action': 'up',
|
|
'direction': [pos['x'], pos['y']]
|
|
}));
|
|
}
|
|
|
|
let penup = function(e) {
|
|
if(!hasMouseDown) {
|
|
return;
|
|
}
|
|
hasMouseDown = false;
|
|
|
|
let pos = getCoordinates(e);
|
|
stopDrawing(pos);
|
|
};
|
|
let strokes2D = function(strokes) {
|
|
// strokes to a d attribute for a path
|
|
let d = "";
|
|
let last_stroke = undefined;
|
|
let cmd = "";
|
|
for (let stroke of strokes) {
|
|
if(!last_stroke) {
|
|
d += `M${stroke[0]*svgWidth*10},${stroke[1]*svgHeight*10} `;
|
|
cmd = 'M';
|
|
} else {
|
|
if (last_stroke[2] == 1) {
|
|
d += " m";
|
|
cmd = 'm';
|
|
} else if (cmd != 'l') {
|
|
d+=' l ';
|
|
cmd = 'l';
|
|
}
|
|
let rel_stroke = [stroke[0] - last_stroke[0], stroke[1] - last_stroke[1]];
|
|
d += `${rel_stroke[0]*svgWidth*10},${rel_stroke[1]*svgHeight*10} `;
|
|
}
|
|
last_stroke = stroke;
|
|
|
|
}
|
|
return d;
|
|
}
|
|
|
|
let startDrawing = function(e){
|
|
hasMouseDown = true;
|
|
};
|
|
|
|
/*let reset = function() {
|
|
strokes = [];
|
|
strokeEl.setAttribute('d', "");
|
|
socket.send(JSON.stringify({
|
|
'action': 'reset',
|
|
}));
|
|
}*/
|
|
|
|
|
|
let socket = new WebSocket(url);
|
|
document.body.addEventListener('mousemove', draw);
|
|
document.body.addEventListener('mouseup', penup);
|
|
document.body.addEventListener('mousedown', startDrawing);
|
|
|
|
socket.addEventListener('message', function(e){
|
|
let msg = JSON.parse(e.data);
|
|
console.log('receive', msg);
|
|
if(msg['action'] == 'submitted') {
|
|
document.body.classList.add('submitted');
|
|
messageEl.innerHTML = msg['msg'];
|
|
svgEl.removeEventListener('mousedown', startDrawing);
|
|
}
|
|
});
|
|
|
|
//resetEl.addEventListener('click', reset);
|
|
submitEl.addEventListener('click', function(e){
|
|
if(!strokes.length){
|
|
alert('please draw before submitting');
|
|
return;
|
|
}
|
|
|
|
socket.send(JSON.stringify({
|
|
'action': 'submit'
|
|
}));
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|