src_js/iconolab-bundle/src/components/editor/ShapeFree.vue
author Alexandre Segura <mex.zktk@gmail.com>
Wed, 22 Feb 2017 11:04:16 +0100
changeset 335 86dbf2cdeeeb
parent 323 55c024fc7c60
child 342 ebec1d59dc74
permissions -rw-r--r--
Introduce ZoomThumbnail, improve zoom behavior.

<template>
    <g>
        <path ref="path" v-bind:d="path" stroke="#000000" fill="#bdc3c7"
            v-bind:stroke-width="handlerRadius / 2"
            v-bind:stroke-dasharray="(handlerRadius / 3) + ',' + (handlerRadius / 3)" style="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"
            v-bind:stroke-width="handlerRadius / 2"
            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';
                }
            },
            // 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, 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'
            },

            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.closed = true;
                        this.$nextTick(() => {
                            // FIXME Race condition with destroy
                            setTimeout(() => this.addTooltip(), 250);
                        });
                    }
                });

                // 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 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>