Fix bug on zoom with free shape.
<template>
<g>
<defs>
<path v-bind:id="annotationHref" ref="path" v-bind:d="path" class="path"></path>
</defs>
<g ref="g">
<use v-bind:xlink:href="annotationSelector" v-bind:stroke-width="strokeWidth * 2" class="stroke-bg" filter="url(#shadow)" />
<use v-bind:xlink:href="annotationSelector" v-bind:stroke-width="strokeWidth" class="stroke-fg" />
<use v-bind:xlink:href="annotationSelector" class="overlay" v-bind:class="{ active: !originalAnnotation }" />
<circle ref="handlers"
v-for="(point, key) in points"
:key="key"
v-show="!readonly"
v-bind:data-key="key"
v-bind:cx="point.x"
v-bind:cy="point.y"
v-bind:r="strokeWidth * 2"
v-bind:stroke-width="strokeWidth"
v-bind:class="{ handler: true, 'handler--first': key === 0 && !closed }"></circle>
<text ref="text"
v-bind:x="text.x" v-bind:y="text.y"
v-show="!originalAnnotation && !closed && points.length > 3"
font-family="Open Sans"
v-bind:font-size="strokeWidth * 4"
class="text">
Double-cliquez pour fermer la zone
</text>
<g>
</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',
'original-path',
'readonly',
'stroke-width',
],
data() {
return {
path: '',
closed: false,
points: [],
text: { x: 0, y: 0 }
}
},
computed: {
annotationHref: function() {
if (this.originalAnnotation) {
return 'annotation-path-' + this.originalAnnotation.annotation_guid;
} else {
return 'annotation-path-new';
}
},
annotationSelector: function() {
return '#' + this.annotationHref;
},
},
mounted() {
var g = new Snap(this.$refs.g);
if (this.originalPath) {
this.fromSVGPath(this.originalPath, false);
g.click(() => this.$emit('click'));
} else {
g.dblclick(() => this.closePath());
}
},
watch: {
closed: function(closed) {
if (closed) {
this.path += ' Z';
}
},
// Redraw the path when the points have changed
points: function(points) {
var path = "M";
if (points.length <= 1) {
return;
}
path += points[0].x + ',' + points[0].y;
for (var i = 0; i < points.length; i++) {
if (i == 0) continue;
var pointInfos = points[i];
var lPath = "L" + pointInfos.x + "," + pointInfos.y;
path += " " + lPath;
}
if (this.closed) { path += ' Z'; }
this.path = path;
this.$nextTick(() => {
var group = new Snap(this.$refs.g);
var text = new Snap(this.$refs.text);
this.text = {
x: group.getBBox().x + (group.getBBox().width / 2) - (text.getBBox().width / 2),
y: group.getBBox().y + (group.getBBox().height / 2) - (text.getBBox().height / 2)
}
});
}
},
methods: {
addPoint: function(x, y) {
this.points.push({ x: x, y: y });
// Attach events to last point once DOM has been refreshed
// @link https://vuejs.org/v2/guide/reactivity.html
this.$nextTick(() => {
var handler = this.$refs.handlers[this.$refs.handlers.length - 1];
this.addResizeHandler(handler);
});
},
clear: function() {
this.destroyTooltip();
Object.assign(this, {
points: [],
closed: false,
path: ''
});
},
getTooltipTarget: function() {
return this.$refs.path;
},
fromSVGPath: function(pathString, tooltip) {
var segments = Snap.parsePathString(pathString);
var points = [];
// Don't use this.addPoint to avoid
// race condition when registering events
segments.map((segment) => {
if (segment[0] !== 'Z') {
points.push({ x: segment[1], y: segment[2] });
}
});
this.points = points;
this.closed = true;
this.$nextTick(() => {
this.addResizeHandlers();
if (tooltip) {
// FIXME Race condition with destroy
setTimeout(() => this.addTooltip(), 250);
}
});
},
toSVGPath: function() {
return this.$parent.normalizePath(this.path) + ';FREE'
},
closePath: function() {
this.closed = true;
this.$nextTick(() => {
// FIXME Race condition with destroy
setTimeout(() => this.addTooltip(), 250);
});
},
addResizeHandler: function(handler) {
var self = this;
var circle = new Snap(handler);
var isDragged = false;
circle.click((e) => {
var key = parseInt(circle.attr('data-key'), 10);
if (key === 0 && this.points.length > 2) {
this.closePath();
}
});
// Remove point on double click
circle.dblclick((e) => {
e.preventDefault();
e.stopPropagation();
var circle = new Snap(e.target);
var key = parseInt(circle.attr('data-key'), 10);
this.points.splice(key, 1);
});
var dragEvents = {
onMove: function(dx, dy, x, y, e) {
isDragged = true;
var offset = self.$parent.computeOffset(e);
this.attr({ cx: offset.x, cy: offset.y });
var key = parseInt(this.attr('data-key'), 10);
// Must use splice for reactivity to work
// @see https://vuejs.org/v2/guide/list.html#Mutation-Methods
self.points.splice(key, 1, { x: offset.x, y: offset.y });
},
onStart: () => this.hideTooltip(),
onEnd: function(e) {
if (!isDragged) { return; }
isDragged = false;
self.showTooltip();
}
}
circle.drag(dragEvents.onMove, dragEvents.onStart, dragEvents.onEnd);
},
addResizeHandlers: function() {
this.$refs.handlers.forEach((handler) => this.addResizeHandler(handler));
}
}
}
</script>
<style scoped>
.handler {
fill: #fff;
stroke: #000;
}
.path {
stroke: inherit;
stroke-width: inherit;
fill: inherit;
transform: inherit;
}
.text {
}
.stroke-fg {
stroke: #000;
fill: transparent;
}
.overlay {
fill: transparent;
}
.overlay.active,
.overlay:hover {
cursor: pointer;
fill: #c5f2ff;
opacity: 0.25;
stroke-opacity: 1;
}
.stroke-bg {
stroke: #fff;
fill: transparent;
}
.handler--first {
fill: yellow;
}
</style>