Improve calculation of stroke width depending on image ratio.
<template>
<g ref="g" filter="url(#shadow)" v-bind:transform="transform">
<rect
ref="shape"
x="0" y="0"
v-bind:width="width" v-bind:height="height"
v-bind:stroke-width="strokeWidth"
class="shape"
v-bind:class="{ 'shape--draggable': !readonly }"
v-bind:style="{ 'outline-width': (strokeWidth / 2) + 'px' }"></rect>
<rect
ref="topLeft"
v-show="showResizeHandlers"
v-bind:x="((strokeWidth * 4) / 2) * -1" v-bind:y="((strokeWidth * 4) / 2) * -1"
v-bind:width="strokeWidth * 4" v-bind:height="strokeWidth * 4"
v-bind:stroke-width="strokeWidth" class="handler handler-top-left"></rect>
<rect
ref="bottomRight"
v-show="showResizeHandlers"
v-bind:x="width - ((strokeWidth * 4) / 2)" v-bind:y="height - ((strokeWidth * 4) / 2)"
v-bind:width="strokeWidth * 4" v-bind:height="strokeWidth * 4"
v-bind:stroke-width="strokeWidth" class="handler handler-bottom-right"></rect>
</g>
</template>
<script>
import Snap from 'snapsvg'
import tooltip from './mixins/tooltip'
import save from './mixins/save'
export default {
mixins: [ tooltip, save ],
props: [
'paper',
'original-annotation',
'tooltip',
'readonly',
'original-path',
'stroke-width'
],
data() {
return {
transform: 'translate(0, 0)',
isResizing: false,
x: 0, y: 0,
width: 0, height: 0,
}
},
computed: {
showResizeHandlers: function() {
return !this.readonly && (this.width > 0 && this.height > 0);
}
},
mounted() {
var self = this;
var groupEvents = {
onMove: function(dx, dy) {
if (self.isResizing || self.readonly) { return; }
var snapInvMatrix = this.transform().diffMatrix.invert();
snapInvMatrix.e = snapInvMatrix.f = 0;
var tdx = snapInvMatrix.x(dx, dy);
var tdy = snapInvMatrix.y(dx, dy);
var transformValue = this.data('origTransform') + (this.data('origTransform') ? "T" : "t") + [tdx, tdy];
this.transform(transformValue);
},
onStart: function() {
if (self.readonly) { return; }
this.data('origTransform', this.transform().local);
self.$emit('drag:start');
},
onEnd: () => self.$emit('drag:end')
}
var g = new Snap(this.$refs.g);
g.drag(groupEvents.onMove, groupEvents.onStart, groupEvents.onEnd);
if (this.originalPath) {
this.fromSVGPath(this.originalPath, false);
g.click(() => this.$emit('click'))
}
},
watch: {
x: function (val, oldVal) {
this.transform = 'translate(' + this.x + ', ' + this.y + ')';
},
y: function (val, oldVal) {
this.transform = 'translate(' + this.x + ', ' + this.y + ')';
},
},
methods: {
clear: function() {
var shape = new Snap(this.$refs.shape);
var topLeftHandler = new Snap(this.$refs.topLeft);
var bottomRightHandler = new Snap(this.$refs.bottomRight);
shape.node.removeAttribute('transform');
topLeftHandler.node.removeAttribute('transform');
bottomRightHandler.node.removeAttribute('transform');
this.destroyTooltip();
Object.assign(this, {
transform: 'translate(0, 0)',
x: 0, y: 0,
width: 0, height: 0,
});
},
getTooltipTarget: function() {
return this.$refs.shape;
},
addResizeHandlers: function() {
var self = this;
var shape = new Snap(this.$refs.shape);
var topLeftHandler = new Snap(this.$refs.topLeft);
var bottomRightHandler = new Snap(this.$refs.bottomRight);
var handlerEvents = {
onMove: function(dx, dy) {
var snapInvMatrix = this.transform().diffMatrix.invert();
snapInvMatrix.e = snapInvMatrix.f = 0;
var tdx = snapInvMatrix.x(dx, dy);
var tdy = snapInvMatrix.y(dx, dy);
this.transform( "t" + [ tdx, tdy ] + this.data("origTransform") );
// Update shape
var newWidth = bottomRightHandler.getBBox().x - topLeftHandler.getBBox().x;
var newHeight = bottomRightHandler.getBBox().y - topLeftHandler.getBBox().y;
var attr = {
width: newWidth,
height: newHeight
};
if (this === topLeftHandler) {
attr.transform = shape.data('origTransform') + (shape.data('origTransform') ? "T" : "t") + [tdx, tdy];
}
shape.attr(attr);
},
onStart: function() {
self.isResizing = true;
shape.data("origTransform", shape.transform().local);
this.data('origTransform', this.transform().local);
},
onEnd: function() {
self.isResizing = false;
}
}
topLeftHandler.drag(handlerEvents.onMove, handlerEvents.onStart, handlerEvents.onEnd);
bottomRightHandler.drag(handlerEvents.onMove, handlerEvents.onStart, handlerEvents.onEnd);
},
fromSVGPath: function(pathString, tooltip) {
var bBox = Snap.path.getBBox(pathString);
Object.assign(this, {
x: bBox.x, y: bBox.y,
width: bBox.width, height: bBox.height
});
this.$nextTick(() => {
this.addResizeHandlers();
if (tooltip) {
// FIXME Race condition with destroy
setTimeout(() => this.addTooltip(), 250);
}
});
},
toSVGPath: function() {
var shape = new Snap(this.$refs.shape);
var shapePath;
var bBox = shape.getBBox();
var transform = shape.transform();
if (!transform.global.length) {
shapePath = shape.getBBox().path;
} else {
var shapeX = shape.node.getAttribute('x');
var shapeY = shape.node.getAttribute('y');
var transformMatrix = transform.totalMatrix;
var fakeShape = this.paper.rect(transformMatrix.x(shapeX, shapeY),transformMatrix.y(shapeX, shapeY), bBox.width, bBox.height);
shapePath = fakeShape.getBBox().path;
fakeShape.remove();
}
var path = Snap.path.toAbsolute(shapePath).toString();
return this.$parent.normalizePath(path) + ';RECT';
}
}
}
</script>
<style scoped>
.shape {
fill: transparent;
stroke: #fff;
outline-color: #000;
outline-style: solid;
}
.shape:hover,
.shape.active {
cursor: pointer;
fill: #c5f2ff;
opacity: 0.25;
}
.shape--draggable:hover {
cursor: move;
}
.handler {
fill: #fff;
stroke: #000;
}
.handler-top-left:hover {
cursor: nw-resize;
}
.handler-bottom-right:hover {
cursor: se-resize;
}
</style>