src_js/iconolab-bundle/src/components/editor/ShapeRect.vue
author Alexandre Segura <mex.zktk@gmail.com>
Mon, 13 Mar 2017 18:48:35 +0100
changeset 418 a04c55054afe
parent 408 159042869b47
child 425 7bc5febc9933
permissions -rw-r--r--
Introduce display of all annotations at the same time. - Add stats to serialized annotation. - Introduce AnnotationList component. - Pass array of annotations to component. - Handle click on annotation & drawing.

<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 }"
        v-bind:style="{ 'outline-width': (handlerSize / 5) + 'px' }"></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',
            'original-path',
        ],
        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);

            if (this.originalPath) {
                this.fromSVGPath(this.originalPath, false);
                g.click(() => this.$emit('click'))
            }
        },
        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: #fff;
    outline-color: #000;
    outline-style: solid;
}
.shape:hover,
.shape.active {
    cursor: pointer;
    fill: #333;
    opacity: 0.5;
}
.shape--draggable:hover {
    cursor: move;
}
.handler-top-left:hover {
    cursor: nw-resize;
}
.handler-bottom-right:hover {
    cursor: se-resize;
}
</style>