Drag&Drop signature
Proof of concept using native HTML5 drag&drop
HTML
<div ng-controller="AppCtrl">
<div class="controls">
<div>
scale
<select class="scale" ng-model="settings.scale">
<option value="0.6">60%</option>
<option value="0.8">80%</option>
<option value="1.0">100%</option>
<option value="1.2">120%</option>
<option value="1.4">140%</option>
</select>
</div>
<div>
<div class="placeholder" draggable="true">
Signature
</div>
</div>
</div>
<div class="page" ng-style="{'font-size': (settings.scale * 16) + 'px'}">
Hello world!
<div class="placeholder" placeholder-index="{{ $index }}" draggable="true" ng-repeat="placeholder in placeholders" ng-style="{left: (placeholder.x * settings.scale) + 'px', top: (placeholder.y * settings.scale) + 'px'}">
Signature
</div>
</div>
</div>
CSS
@import url('https://fonts.googleapis.com/css?family=Alex+Brush');
.page {
margin-top: 20px;
margin-left: 20px;
width: 32em;
height: 40em;
background: #fff;
-webkit-box-shadow: 1px 1px 2px 0px rgba(0, 0, 0, 0.35);
-moz-box-shadow: 1px 1px 2px 0px rgba(0, 0, 0, 0.35);
box-shadow: 1px 1px 2px 0px rgba(0, 0, 0, 0.35);
padding: 1.5em;
position: relative;
overflow: hidden;
}
.page .placeholder {
position: absolute;
}
.controls {
margin-top: 20px;
float: right;
width: 200px;
}
.controls>div+div {
margin-top: 30px;
}
.controls .scale {
text-align: right;
width: 80px;
}
.placeholder {
width: 4.2em;
height: 1em;
padding: 0.75em;
font-size: 2em;
text-align: center;
font-family: 'Alex Brush', cursive;
background: white;
border: 1px dashed #aaa;
}
.placeholder.hidden {
visibility: hidden;
}
JavaScript
angular.module('app', []);
angular.module('app').controller('AppCtrl', function($scope) {
function resetDragging() {
$scope.draggedPlaceholder = null;
$scope.dragOffset = {
x: 0,
y: 0
};
}
function isDroppedOnPage(target) {
if (target.classList && target.classList.contains('page')) {
return true;
}
return target.parentNode ? isDroppedOnPage(target.parentNode) : false;
}
function getElementOffsetOnPage(target) {
if (target.classList && target.classList.contains('page')) {
return {
x: 0,
y: 0
};
}
if (!target.parentNode) {
return null;
}
var offset = getElementOffsetOnPage(target.parentNode);
offset.x += target.offsetLeft;
offset.y += target.offsetTop;
return offset;
}
$scope.settings = {
scale: '1.0'
};
$scope.placeholders = [];
resetDragging();
document.addEventListener("dragstart", function(event) {
if (!event.target.classList.contains('placeholder')) return;
var element = event.target;
var index = element.getAttribute('placeholder-index');
event.dataTransfer.setData("text", 'dragging');
// Remember which placeholder is dragged + mouse offset
$scope.$apply(function() {
$scope.draggedPlaceholder = index !== null ? $scope.placeholders[index] : {};
$scope.dragOffset.x = event.offsetX;
$scope.dragOffset.y = event.offsetY;
});
if (index === null) {
// Scale placeholder from controls for dragging
element.style.fontSize = (2 * $scope.settings.scale * 16) + 'px';
setTimeout(function() {
element.style.fontSize = null;
}, 0);
} else {
// Hide dragged placeholder on page
setTimeout(function() {
element.classList.add('hidden');
}, 0);
}
}, false);
document.addEventListener("dragover", function(event) {
if (isDroppedOnPage(event.target)) {
event.preventDefault();
}
});
...