我设法创建了一个包含元素,端口和多个输出端口的图表。 所有元素都使用从输入端口到输出端口的链接进行连接。
当我尝试使用我已经拥有的链接(从JSON加载)时,它可以正常工作。 出于某种原因,当我尝试通过按下端口来创建新链接时,它会拖动元素。
当我按下它们时,如何让端口创建新链接?
joint.shapes.Question = joint.shapes.basic.Generic.extend(_.extend({}, joint.shapes.basic.PortsModelInterface, {
markup: '<g class="rotatable">' +
'<g class=""><g class="inPorts" /><rect class="question-wrapper" /><rect class="question-title" /><rect class="options-body" /><g class="outPorts" /></g>' +
'<text class="question" /><g class="options" />' +
'</g>',
portMarkup: '<g class="port port-<%= port.type %> port-<%= port.id %>"><circle class="port-body" /><text class="port-label" /></g>',
optionMarkup: '<g class="option-wrapper"><text class="option"/></g>',
defaults: joint.util.deepSupplement({
type: 'Question',
minWidth: 200,
optionHeight: 25,
inPorts: [{"id": "in", "label": "IN"}],
attrs: {
'.': {
magnet: false
},
'.question-wrapper': {
width: 200,
height: 95,
fill: '#f2f2f2',
rx: 10,
ry: 10,
ref: '.paper'
},
'.question-title': {
height: 30,
fill: '#ffffff',
rx: 5,
ry: 5,
ref: '.question-wrapper',
'ref-x': 5,
'ref-y': 5,
'ref-width': -10
},
'.question': {
fill: '#000000',
'font-size': 14,
'y-alignment': 'middle',
'x-alignment': 'middle',
ref: '.question-title',
'ref-x': .5,
'ref-y': .5,
},
'.options-body': {
height: 50,
fill: '#ffffff',
rx: 5,
ry: 5,
ref: '.question-wrapper',
'ref-x': 5,
'ref-y': 40,
'ref-width': -10
},
'.options': {
ref: '.options-body',
'ref-x': 5,
'ref-y': 5
},
'.option-wrapper': {
ref: '.options'
},
'.option': {
fill: '#000000',
'font-size': 14
},
'.port-in': {
ref: '.question-wrapper',
'ref-y': 0,
'ref-x': .5,
magnet: true,
type: 'in'
},
'.port-in .port-body': {
r: 15,
fill: '#75caeb',
ref: '.port-in'
},
'.port-in .port-label': {
'font-size': 10,
fill: '#ffffff',
'x-alignment': 'middle',
'y-alignment': -10,
ref: '.port-in .port-body'
},
'.port-out': {
ref: '.question-wrapper',
magnet: true,
type: 'out'
},
'.port-out .port-body': {
r: 10,
ref: '.question-wrapper',
'ref-dy': 15,
'ref-x': .5,
fill: '#158cba',
'y-alignment': 'middle'
},
'.port-out .port-label': {
'text': 'R',
'font-size': 10,
'y-alignment': 'middle',
'x-alignment': 'middle',
fill: '#ffffff',
ref: '.port-out .port-body',
'ref-x': .5,
'ref-y': 15,
'pointer-events': 'none'
}
}
}, joint.shapes.basic.Generic.prototype.defaults),
initialize: function () {
this.attr('.question/text', this.get('question'), {silent: true});
this.onChangePosition();
this.onChangeOptions();
joint.shapes.basic.PortsModelInterface.initialize.apply(this, arguments);
},
onChangePosition: function () {
var timer;
var timer_interval = 500;
this.on('change:position', function (cellView, position) {
clearTimeout(timer);
timer = setTimeout(function () {
$.ajax({
url: routes.questionPosition.replace(':question', cellView.id),
data: position,
method: 'post',
headers: {'X-CSRF-TOKEN': $('[name="csrf_token"]').attr('content')}
});
}, timer_interval);
});
},
onChangeOptions: function () {
var options = this.get('options');
var size = this.get('size');
var optionHeight = this.get('optionHeight');
// First clean up the previously set attrs for the old options object.
// We mark every new attribute object with the `dynamic` flag set to `true`.
// This is how we recognize previously set attributes.
var attrs = this.get('attrs');
_.each(attrs, function (attrs, selector) {
if (attrs.dynamic) {
// Remove silently because we're going to update `attrs`
// later in this method anyway.
this.removeAttr(selector, {silent: true});
}
}, this);
// Collect new attrs for the new options.
var offsetY = 0;
var attrsUpdate = {};
_.each(options, function (option) {
var selector = '.option-' + option.id;
attrsUpdate[selector] = {transform: 'translate(0, ' + offsetY + ')', dynamic: true};
attrsUpdate[selector + ' .option'] = {text: option.text, dynamic: true};
offsetY += optionHeight;
}, this);
this.attr(attrsUpdate);
this.autoresize();
},
autoresize: function () {
var options = this.get('options') || [];
var gap = this.get('paddingBottom') || 15;
var height = options.length * this.get('optionHeight');
var wrapperHeight = height + this.attr('.question-title/height') + gap;
var width = joint.util.measureText(this.get('question'), {
fontSize: this.attr('.question/font-size')
}).width + (this.attr('.question-title/rx') * 6);
this.attr('.options-body/height', height);
this.resize(Math.max(this.get('minWidth'), width), wrapperHeight);
this.attr('.question-wrapper/width', width);
this.attr('.question-wrapper/height', wrapperHeight);
},
getPortAttrs: function (port, index, total, selector, type) {
var attrs = {};
var portSelector = selector + ' .port-' + type;
attrs[portSelector + ' .port-label'] = {text: port.label};
attrs[portSelector + ' .port-body'] = {
port: {
id: port.id,
type: type
}
};
if (selector === '.outPorts') {
attrs[portSelector] = {'ref-x': ((total / 2) * 30 * -1) + (index * 30) + 15};
}
return attrs;
}
}));
joint.shapes.QuestionView = joint.dia.ElementView.extend(_.extend({}, joint.shapes.basic.PortsViewInterface, {
initialize: function () {
joint.shapes.basic.PortsViewInterface.initialize.apply(this, arguments);
},
renderMarkup: function () {
joint.dia.ElementView.prototype.renderMarkup.apply(this, arguments);
// A holder for all the options.
this.$options = this.$('.options');
// Create an SVG element representing one option. This element will
// be cloned in order to create more options.
this.elOption = V(this.model.optionMarkup);
this.renderOptions();
},
renderOptions: function () {
this.$options.empty();
_.each(this.model.get('options'), function (option, index) {
var className = 'option-' + option.id;
var elOption = this.elOption.clone().addClass(className);
elOption.attr('option-id', option.id);
this.$options.append(elOption.node);
}, this);
// Apply `attrs` to the newly created SVG elements.
this.update();
}
}));