How to resize svg shape based on text content

2019-07-13 10:55发布

问题:

I would like to have svg shape scale based on text content of text area or text-input. As the text content increases, the size of the underlying svg element should increase as well

This is what I have so far:

    var graph = new joint.dia.Graph;
                var paper = new joint.dia.Paper({
                    el: $('#myholder'),
                    width: 1330,
                    height: 660,
                    model: graph,
                    gridSize: 1,
                    defaultLink: new joint.dia.Link({
                        attrs: {'.marker-target': {d: 'M 10 0 L 0 5 L 10 10 z'}}
                    }),
                    validateConnection: function (cellViewS, magnetS, cellViewT, magnetT, end, linkView) {
                        // Prevent linking from input ports.
                        if (magnetS && magnetS.getAttribute('type') === 'input')
                            return false;
                        // Prevent linking from output ports to input ports within one element.
                        if (cellViewS === cellViewT)
                            return false;
                        // Prevent loop linking
                        return (magnetS !== magnetT);
                        // Prevent linking to input ports.
                        return magnetT && magnetT.getAttribute('type') === 'input';
                    },
                    // Enable marking available cells & magnets
                    markAvailable: true,
                    //Enable link snapping within 75px lookup radius
//                    snapLinks: {radius: 75},
                    interactive: function (cellView, methodName)
                    {
                        if (cellView.model.get('isInteractive') === false)
                            return false;
//                        return true;
                    }
                });

                joint.shapes.devs.CircleModel = joint.shapes.devs.Model.extend({
                    markup: '<g class="rotatable"><g class="scalable"><circle class="body"/></g><text class="label"/><g class="inPorts"/><g class="outPorts"/></g>',
//                    portMarkup: '<g class="port port<%=1%>"><rect class="port-body"/><text class="port-label"/></g>',
                    defaults: joint.util.deepSupplement({
                        type: 'devs.CircleModel',
                        attrs: {
                            '.body': {r: 50, cx: 50, stroke: '', fill: 'white'},
                            '.label': {text: '', 'ref-y': 0.5, 'y-alignment': 'middle'},
                            '.port-body': {r: 3, width: 10, height: 10, x: -5, stroke: 'gray', fill: 'lightgray', magnet: 'active'}
                        }

                    }, joint.shapes.devs.Model.prototype.defaults)
                });
                joint.shapes.devs.CircleModelView = joint.shapes.devs.ModelView;

                var rect = new joint.shapes.basic.Rect({
                    isInteractive: false,
                    position: {x: 10, y: 50},
                    size: {width: 51, height: 41},
                    attrs: {rect: {fill: '#D6F2FC', stroke: '#7E7E7E'}, '.': {magnet: false}}
                });

                // Create a custom element.
// ------------------------
                joint.shapes.html = {};
                joint.shapes.html.Element = joint.shapes.basic.Rect.extend({
                    defaults: joint.util.deepSupplement({
                        type: 'html.Element',
                        attrs: {
                            rect: {stroke: 'none', 'fill-opacity': 0}
                        }
                    }, joint.shapes.basic.Rect.prototype.defaults)
                });

// Create a custom view for that element that displays an HTML div above it.
// -------------------------------------------------------------------------

                joint.shapes.html.ElementView = joint.dia.ElementView.extend({
                    template: [
                        '<div class="html-element">',
                         '<button class="delete">x</button>',
                        '<span></span>', '<br/>',
//                        '<input type="text" value="" />',
                        '<textarea id="txt" type="text" rows="10" value="Start writing"></textarea>',
                        '</div>'
                    ].join(''),
                    initialize: function () {
                        _.bindAll(this, 'updateBox');
                        joint.dia.ElementView.prototype.initialize.apply(this, arguments);

                        this.$box = $(_.template(this.template)());
                        // Prevent paper from handling pointerdown.
                        this.$box.find('input,select').on('mousedown click', function (evt) {
                            evt.stopPropagation();
                        });

                        this.$ruler = $('<span>', {style: 'visibility: hidden; white-space: pre'});
                        $(document.body).append(this.$ruler);

                        // This is an example of reacting on the input change and storing the input data in the cell model.
                        this.$box.find('textarea').on('input', _.bind(function (evt) {

                            var val = $(evt.target).val();
                            this.model.set('textarea', val);
                            this.$ruler.html(val);
                            var width = this.$ruler[0].offsetWidth;
                            var height = this.$ruler[0].offsetHeight;
                            var area = width * height;
                            height = area / 150;
                            width = 150;
                            if ((area > 9000)) 
                            {
                                this.model.set('size', {width: width + 50, height: height + 80});
                                this.$box.find('textarea').css({width: width, height: height + 30});
//                                this.$box.find('.color-edit').css({width: width + 50, height: height + 80});
                                this.$box.find('.in').css({top: height + 75});
                            }
                        }, this));

                        this.$box.find('textarea').on('click', _.bind(function () {
                            this.$box.find('.delete').css({opacity: 1});
                            this.$box.find('textarea').css({opacity: 1});
                        }, this));

                        this.$box.find('textarea').on('blur', _.bind(function () {
                            this.$box.find('.delete').css({opacity: 0});
                            this.$box.find('textarea').css({opacity: 0});
                        }, this));

                        this.$box.find('.delete').on('click', _.bind(this.model.remove, this.model));
                        // Update the box position whenever the underlying model changes.
                        this.model.on('change', this.updateBox, this);
                        // Remove the box when the model gets removed from the graph.
                        this.model.on('remove', this.removeBox, this);

                        this.updateBox();
                        this.listenTo(this.model, 'process:ports', this.update);
                        joint.dia.ElementView.prototype.initialize.apply(this, arguments);
                    },
                    render: function () {
                        joint.dia.ElementView.prototype.render.apply(this, arguments);
                        this.paper.$el.prepend(this.$box);
                        this.updateBox();
                        return this;
                    },
                    updateBox: function ()
                    {
                        // Set the position and dimension of the box so that it covers the JointJS element.
                        var bbox = this.model.getBBox();
                        // Example of updating the HTML with a data stored in the cell model.
                        this.$box.find('label').text(this.model.get('label'));
                        this.$box.find('span').text(this.model.get('select'));
                        this.$box.css({width: bbox.width + 6, height: bbox.height, left: bbox.x, top: bbox.y, transform: 'rotate(' + (this.model.get('angle') || 0) + 'deg)'});
                    },
                    removeBox: function (evt) {
                        this.$ruler.remove();
                        this.$box.remove();
                    }
                });

                paper.on('cell:pointerdblclick', function (cellView, evt, x, y)
                {
                    var clone = cellView.model.clone();
                    if (rect.id === cellView.model.id)
                    {
                        clone = new joint.shapes.html.Element({
                            position: {x: 100, y: 60},
                            size: {width: 81, height: 69},
                            inPorts: [''],
                            outPorts: [''],
                            attrs: {
                                '.': {magnet: true},
                                '.label': {text: '', 'ref-x': .4, 'ref-y': .2},
                                '.inPorts circle': {type: 'input'},
                                '.outPorts circle': {type: 'output'},
                                '.port-body': {r: 3}
                            }
                        });
//                        clone.resize(2*81,2*39)
                        graph.addCell(clone);
                    }
                });

//                // First, unembed the cell that has just been grabbed by the user.
                paper.on('cell:pointerdown', function (cellView, evt, x, y) {

                    var cell = cellView.model;
                    if (!cell.get('embeds') || cell.get('embeds').length === 0) {
                        // Show the dragged element above all the other cells (except when the
                        // element is a parent).
                        cell.toFront();
                        _.invoke(graph.getConnectedLinks(cell), 'toFront');
                    }

                    if (cell.get('parent')) {
                        graph.getCell(cell.get('parent')).unembed(cell);
                    }
                });
                // When the dragged cell is dropped over another cell, let it become a child of the
                //element below.
                paper.on('cell:pointerup', function (cellView, evt, x, y) {

                    if (cellView.model.isLink())
                        return;

                    var cell = cellView.model;
                    var cellViewsBelow = paper.findViewsFromPoint(cell.getBBox().center());
                    if (cellViewsBelow.length) {
                        // Note that the findViewsFromPoint() returns the view for the `cell` itself.
                        var cellViewBelow = _.find(cellViewsBelow, function (c) {
                            return c.model.id !== cell.id;
                        });
                        // Prevent recursive embedding.
                        if (cellViewBelow && cellViewBelow.model.get('parent') !== cell.id) {
                            cellViewBelow.model.embed(cell);
                        }
                    }
                });
                graph.addCells([rect]);

Could not find a solution elsewhere. Any help would be appreciated. thanks

回答1:

  1. You have to make the HTML Input resize based on the text inside.

Auto-scaling input[type=text] to width of value?

  1. The ElementView has to listen to the HTML Input changes (input event) and update the size of the model based on the width and height of the HTML Input.

Example:

function onTextInput(evt) {    
     var $input = $(evt.target);
     // 1. auto-scaling the input based on the text inside.
     $input.attr('size', Math.max($input.val().length, 10));    
     // 2. resizing the model to the size of the input + padding.
     model.resize($input.outerWidth() + 5, $input.outerHeight() + 40);
}

$('input').on('input', onTextInput);

JS Fiddle: http://jsfiddle.net/kumilingus/Lrffgvqn/

Similar with HTML TextArea, where the only difference will be the way how you auto-scale it based on the text inside.



标签: jointjs