JointJS - Ensure Legibility of Element Label

by Roman Bruckner

HTML

<html>
<head>
    <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/jointjs/3.5.5/joint.css" />
</head>
<body>
      <!-- content -->
      <div id="paper"></div>         

      <!-- dependencies -->
        <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.4.0/backbone.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/jointjs/3.5.5/joint.js"></script>
    
    </body>
</html>

CSS

.joint-paper {
  display: inline-block;
  border: 1px solid gray;
}

JavaScript

class Shape extends joint.dia.Element {

  defaults() {
    return {
      ...super.defaults,
      type: 'Shape',
      size: {
        width: 120,
        height: 40
      },
      angle: 0,
      attrs: {
        body: {
          fill: '#ffffff',
          stroke: '#333333',
          strokeWidth: 2,
          width: 'calc(w)',
          height: 'calc(h)',
        },
        labelWrapper: {
          transform: 'translate(calc(.5*w), calc(h+20))'
        },
        label: {
          text: 'A',
          fontSize: 14,
          fontFamily: 'sans-serif',
          fill: '#333333',
          textVerticalAnchor: 'middle',
          textAnchor: 'middle',
        },
      }
    };
  }

  preinitialize() {
    this.markup = [{
      tagName: 'rect',
      selector: 'body'

    }, {
      tagName: 'g',
      selector: 'labelWrapper',
      children: [{
        tagName: 'text',
        selector: 'label'
      }]
    }];
  }

  initialize(...args) {
    super.initialize(...args);
    this.on('change', ({
      changed
    }, opt) => {
      if ('angle' in changed) {
        this.updateBindings(opt);
      }
    });
    this.updateBindings();
  }

  updateBindings(opt) {
    const size = this.size();
    const angle = -this.angle();
    this.attr({
      label: {
        transform: `rotate(${angle})`
      }
    }, opt);
  }
}


joint.shapes.Shape = Shape;

const namespace = {
  ...joint.shapes,
  Shape,
};

const graph = new joint.dia.Graph({}, {
  cellNamespace: namespace
});

const paper = new joint.dia.Paper({
  el: document.getElementById('paper'),
  model: graph,
  async: true,
  sorting: joint.dia.Paper.sorting.APPROX,
  cellViewNamespace: namespace
});

[0, 45, 60, 90, 120].forEach((angle, index) => {
  const shape = new Shape();
  shape.position(10 + index * 150, 100).rotate(angle).addTo(graph);
});

paper.fitToContent({
  useModelGeometry: true,
  padding: 20
});