--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src_js/iconolab-bundle/src/components/editor/ShapeFree.vue Wed Feb 15 16:42:06 2017 +0100
@@ -0,0 +1,192 @@
+<template>
+ <g>
+ <path ref="path" v-bind:d="path" stroke="#000000" fill="#bdc3c7" style="stroke-width: 10; stroke-dasharray: 20, 20; opacity: 0.6;"></path>
+ <circle
+ v-for="(point, key) in points"
+ :key="key"
+ v-bind:data-key="key"
+ ref="handlers"
+ v-bind:cx="point.x"
+ v-bind:cy="point.y"
+ v-bind:r="handlerRadius"
+ stroke="#000000" stroke-width="10"
+ style="opacity: 0.9;"
+ v-bind:class="{ handler: true, 'handler--first': key === 0 && !closed }"></circle>
+ </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',
+ ],
+ data() {
+ return {
+ path: '',
+ closed: false,
+ points: [],
+ handlerRadius: 30
+ }
+ },
+ mounted() {
+
+ },
+ watch: {
+ closed: function(closed) {
+ if (closed) {
+ this.path += ' Z';
+ setTimeout(() => this.addTooltip(), 50);
+ }
+ },
+ // 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;
+ }
+ },
+ 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) {
+
+ 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();
+ });
+ },
+
+ toSVGPath: function() {
+ return this.$parent.normalizePath(this.path) + ';FREE'
+ },
+
+ addResizeHandler: function(handler) {
+
+ var circle = new Snap(handler);
+
+ var isDragged = false;
+
+ circle.click(function(e) {
+ var key = parseInt(this.attr('data-key'), 10);
+ if (key === 0 && self.points.length > 2) {
+ self.closed = true;
+ }
+ });
+
+ // Remove point on double click
+ circle.dblclick((e) => {
+ var circle = new Snap(e.target);
+ var key = parseInt(circle.attr('data-key'), 10);
+ this.points.splice(key, 1);
+ });
+
+ var self = this;
+
+ 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;
+}
+/*.handler:hover {
+ cursor: -webkit-grab;
+ cursor: -moz-grab;
+ cursor: grab;
+}*/
+.handler--first {
+ fill: yellow;
+}
+</style>