问题
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:
- You have to make the HTML Input resize based on the text inside.
Auto-scaling input[type=text] to width of value?
- 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.
来源:https://stackoverflow.com/questions/31695146/how-to-resize-svg-shape-based-on-text-content