Introduce shadow effect on shapes.
<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="handlerSize / 5"
class="shape"
v-bind:class="{ 'shape--draggable': !readonly }"></rect>
<rect
ref="topLeft"
v-show="showResizeHandlers"
v-bind:x="(handlerSize / 2) * -1" v-bind:y="(handlerSize / 2) * -1"
v-bind:width="handlerSize" v-bind:height="handlerSize"
fill="#ffffff"
stroke="#000000" v-bind:stroke-width="handlerSize / 5" class="handler-rect handler-top-left"></rect>
<rect
ref="bottomRight"
v-show="showResizeHandlers"
v-bind:x="width - (handlerSize / 2)" v-bind:y="height - (handlerSize / 2)"
v-bind:width="handlerSize" v-bind:height="handlerSize"
fill="#ffffff"
stroke="#000000" v-bind:stroke-width="handlerSize / 5" class="handler-rect 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',
],
data() {
return {
transform: 'translate(0, 0)',
isResizing: false,
x: 0, y: 0,
width: 0, height: 0,
handlerSize: 60
}
},
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);
},
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: #000;
opacity: 0.6;
}
.shape--draggable:hover {
cursor: move;
}
.handler-top-left:hover {
cursor: nw-resize;
}
.handler-bottom-right:hover {
cursor: se-resize;
}
</style>