src_js/iconolab-bundle/src/components/editor/ShapeFree.vue
changeset 320 81945eedc63f
child 323 55c024fc7c60
--- /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 &amp;&amp; !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>