Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JointJs: Scale custom shape html along with element (using paperScroller.zoom)

Tags:

Using something like paperScroller.zoom(0.2, { max: 5 }); only causes svg elements to be zoomed, whereas in my custom shape, I've used html as well, which doesn't scale in tandem.

Since there's no model.change event firing on scaling, the updateBox method in ElementView doesn't get called and so the html elements don't sync their dimensions and positioning accordingly. Is there a way to work around this?

like image 744
Sameet Avatar asked Apr 22 '16 08:04

Sameet


2 Answers

Extending on Marc_Alx's answer.

Calling paper.translate() and paper.scale() fires 'translate' and 'scale' events on the paper.

Custom Elements can listen to these events on their custom ElementView.

For example, if you scale on mousewheel event:

  paper.on('blank:mousewheel', (event, x, y, delta) => {
    const scale = paper.scale();
    paper.scale(scale.sx + (delta * 0.01), scale.sy + (delta * 0.01),);
  });

Override render methods of your custom ElementView to listen to 'scale' event on the paper.

  render(...args) {
    joint.dia.ElementView.prototype.render.apply(this, args);

    this.listenTo(this.paper, 'scale', this.updateBox);
    this.listenTo(this.paper, 'translate', this.updateBox);

    this.paper.$el.prepend(this.$box);

    this.updateBox();

    return this;
},

And the custom ElementView's updateBox should retrieve the scale value from the paper.

  updateBox() {
    if (!this.paper) return;
    const bbox = this.getBBox({ useModelGeometry: true });
    const scale = joint.V(this.paper.viewport).scale();

    // custom html updates
    this.$box.find('label').text(this.model.get('label'));
    this.$box.find('p').text(this.model.get('response'));
    // end of custom html updates

    this.$box.css({
      transform: `scale(${scale.sx},${scale.sy})`,
      transformOrigin: '0 0',
      width: bbox.width / scale.sx,
      height: bbox.height / scale.sy,
      left: bbox.x,
      top: bbox.y,
    });
  }
like image 200
Raunak Mukhia Avatar answered Sep 28 '22 01:09

Raunak Mukhia


I've found a workaround for those who are not using rappid.js (paperScroller is only available for rappid).

Assuming you have an element similar to this one : http://jointjs.com/tutorial/html-elements

My answer is inspired from Murasame answer's from How to scale jonitjs graphs? and assume that you update scale of your paper on mousewheel (+0.1 -0.1).

  • First inside your custom ElementView store a numeric property (initialized to 1) that will store the scale, let's name it scale (accessed via this.scale).

  • Next in the overrided method render (of your cutom ElementView) listen to mousewheel events of the paper : this.paper.$el.on('mousewheel',…) in the handler update the scaleproperty (+=0.1 or -=0.1), then call updateBox

  • Finally in the updateBox method at this line :

    this.$box.css({ width: bbox.width, height: bbox.height, left: bbox.x, top: bbox.y, transform: 'rotate(' + (this.model.get('angle') || 0) + 'deg)' });

    Multiply width, height, x, y by this.scale to get :

    this.$box.css({ width: bbox.width*this.scale, height: bbox.height*this.scale, left: bbox.x*this.scale, top: bbox.y*this.scale, transform: 'rotate(' + (this.model.get('angle') || 0) + 'deg)' });

I think it's a good start for implementing this behavior, you should also size the content of your element.

like image 36
Marc_Alx Avatar answered Sep 28 '22 01:09

Marc_Alx