Изменение размера повернутого элемента с помощью javascript (interactive.js)

Я потратил много дней, пытаясь сделать изменяемый размер элемента, который вращается с помощью interactive.js.

Это код, который у меня есть в данный момент, я попытаюсь объяснить концепцию.

У нас есть элемент селектора по двум причинам, потому что контейнер можно масштабировать с помощью css-преобразования (например, масштабирования), и нам нужно, чтобы селектор был снаружи, и потому что у нас есть многоселемент, а селектор растет, если у меня есть два прямоугольника, но в этом случае это не главная проблема, и мы вычислили масштабированную пропорцию без проблем и прочего.

Когда селектор изменяет размер, он принимает прямоугольник и делает то же самое с шириной, высотой, слева, сверху и вращением.

Javascript:

// TAP - CLICK EVENT (just for positioning the selector)
interact('#rectangle').on('tap', event => {
  console.log('Tap Box!');
  event.stopPropagation();
  const $rectangleCloned = $('#rectangle').clone();
  const previousTransform = $rectangleCloned.css('transform');
  $rectangleCloned.css('transform', 'none');
  $rectangleCloned.css('opacity', '0');
  $rectangleCloned.css('display', 'block');


  $('#container').append($rectangleCloned);
  const values = $rectangleCloned[0].getBoundingClientRect();
  // This is just a trick for fast implementation:
  $('#selector').css('top', values.y);
  $('#selector').css('left', values.x);
  $('#selector').css('width', values.width);
  $('#selector').css('height', values.height);
  $('#selector').css('transform', previousTransform);

  $rectangleCloned.remove();
  return values;
});


interact('.pointer9').draggable({
  max: 1,
  onmove: event => {
    const angleDeg =
      Math.atan2(
        centerRotate.posY - event.pageY,
        centerRotate.posX - event.pageX
      ) *
      180 /
      Math.PI;

    console.log(this.rotate);
    const prevAngle = this.rotate - angleInitial;
    const angle = parseInt(angleDeg) + prevAngle;
    this.$rectangle.css({
      transform: 'rotate(' + angle + 'deg)'
    });
    this.$selector.css({
      transform: 'rotate(' + angle + 'deg)'
    });
  },
  onstart: event => {
    const data = event.interactable.getRect(event.target.parentNode);
    this.centerRotate = {
      posX: data.left + data.width / 2,
      posY: data.top + data.height / 2
    };
    this.angleInitial =
      Math.atan2(
        centerRotate.posY - event.pageY,
        centerRotate.posX - event.pageX
      ) *
      180 /
      Math.PI;
    this.$rectangle = $('#rectangle');
    this.$selector = $('#selector');
    this.rotate = $rectangle.attr('angle') || 0;
  },
  onend: event => {
    const $box = $('#selector');
    const matrix = $box.css('transform');
    const values = matrix
      .split('(')[1]
      .split(')')[0]
      .split(',');

    var a = values[0];
    var b = values[1];
    var angle = Math.round(Math.atan2(b, a) * (180 / Math.PI));
    $rectangle.attr('angle', angle);

  }
});


interact('#selector')
  .resizable({
    // resize from all edges and corners
    edges: {
      left: true,
      right: true,
      bottom: true,
      top: true
    },

    // keep the edges inside the parent
    restrictEdges: {
      outer: 'parent',
      endOnly: true,
    },

    // minimum size
    restrictSize: {
      min: {
        width: 100,
        height: 50
      },
    },

    inertia: true,
  })
  .on('resizemove', function(event) {
    var target = event.target,
      x = parseFloat($(target).offset().left) || 0,
      y = parseFloat($(target).offset().top) || 0;

    // update the element style
    target.style.width = event.rect.width + 'px';
    target.style.height = event.rect.height + 'px';

    // translate when resizing from top or left edges
    x += event.deltaRect.left;
    y += event.deltaRect.top;

    target.style.left = x + 'px';
    target.style.top = y + 'px';

    $('#rectangle')[0].style.left = target.style.left;
    $('#rectangle')[0].style.top = target.style.top;

    $('#rectangle')[0].style.width = target.style.width;
    $('#rectangle')[0].style.height = target.style.height;

    target.setAttribute('data-x', x);
    target.setAttribute('data-y', y);

  });

CSS:

#container {
  width: 500px;
  height: 400px;
  top: 0;
  left: 0;
  position: absolute;
  background-color: #CCC;
}

#rectangle {
  top: 50px;
  left: 50px;
  width: 120px;
  height: 60px;
  background-color: red;
  position: absolute;
}

#selector {
  display: inline-block;
  position: absolute;
  pointer-events: none;
  z-index: 9999;
  top: -1000px;
  /*Not showing at start*/
}

#selector .pointers {
  display: inline-block;
  position: absolute;
  z-index: 2;
  width: 10px;
  height: 10px;
  pointer-events: all;
}

#selector .pointers .point {
  width: 10px;
  height: 10px;
  background-color: #fff;
  border: 2px solid rgba(0, 0, 0, 0.9);
  border-radius: 50%;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}

#selector .pointers.pointer1 {
  top: -5px;
  left: -5px;
}

#selector .pointers.pointer2 {
  bottom: -5px;
  left: -5px;
}

#selector .pointers.pointer3 {
  top: -5px;
  right: -5px;
}

#selector .pointers.pointer4 {
  bottom: -5px;
  right: -5px;
}

#selector .pointers.pointer-north {
  top: -5px;
  left: calc(50% - 5px);
}

#selector .pointers.pointer-south {
  bottom: -5px;
  left: calc(50% - 5px);
}

#selector .pointers.pointer-east {
  right: -5px;
  top: calc(50% - 5px);
}

#selector .pointers.pointer-west {
  left: -5px;
  top: calc(50% - 5px);
}

#selector .pointer-rotate {
  border: 2px solid rgba(0, 0, 0, 0.9);
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
  border-radius: 50%;
  cursor: rotate;
}

#selector .pointer9 {
  bottom: -70px;
  left: calc(50% - 11px);
  display: inline-block;
  width: 20px;
  height: 20px;
  background-color: #fff;
  pointer-events: all;
  position: absolute;
}

#selector .rotate-line {
  border-left: 1px dashed #5f5f5f;
  height: 40px;
  position: absolute;
  top: -40px;
  left: calc(50% - 1px);
  width: 1px;
}

HTML:

<div id="container">
  <div id="rectangle">
  </div>
  <div id="selector">
    <div class="pointers pointer1">
      <div class="point"></div>
    </div>
    <div class="pointers pointer2">
      <div class="point">
      </div>
    </div>
    <div class="pointers pointer3">
      <div class="point">
      </div>
    </div>
    <div class="pointers pointer4">
      <div class="point">
      </div>
    </div>
    <div class="pointers pointer-north">
      <div class="point">
      </div>
    </div>
    <div class="pointers pointer-east">
      <div class="point">

      </div>
    </div>
    <div class="pointers pointer-south">
      <div class="point">
      </div>
    </div>
    <div class="pointers pointer-west">
      <div class="point">
      </div>
    </div>
    <span class="topline lines-resize" />
    <span class="rightline lines-resize" />
    <span class="botline lines-resize" />
    <span class="leftline lines-resize" />
    <div class="pointer-rotate pointer9" />
    <div class="rotate-line" />
  </div>
</div>

Скрипт для тестирования:

https://jsfiddle.net/ub70028c/46/

Я читал о других людях, пытающихся сделать то же самое без результатов...

Спасибо!

Ответы

Ответ 1

Я проверил ваш код и аналогичную библиотеку для изменения resizable и rotatable и я выясню вашу проблему.

Во-первых, проверка аналогичной библиотеки:

Посмотрите эту скрипту, которую я создал jquery.freetrans.js.

Если вы проверите на <div class="shape">, вы можете увидеть

transform: matrix(1, 0, 0, 1, 0, 0);

Если вы повернете его, transform изменится, как показано ниже:

transform: matrix(0.997373, -0.0724379, 0.0724379, 0.997373, 0, 0);

В аналогичном случае ваш код использует transform которое сначала не transform и после поворота оно выглядит следующим образом:

transform: rotate(-2.49576deg);

Если вы можете использовать matrix вместо rotate в transform, ваш код будет работать правильно. Если вы не можете его изменить, вы можете использовать подобную библиотеку, например jquery.freetrans.js которые работают правильно, jquery.freetrans.js и jquery.freetrans.js размер.

Ответ 2

https://github.com/taye/interact.js/issues/569

https://github.com/taye/interact.js/issues/499

https://github.com/taye/interact.js/issues/394

Боюсь, вы выбрали библиотеку, автор которой четко заявил о своем намерении

Там нет встроенного способа. Как я упоминал в #137 я не очень заинтересован в обработке масштабированных или вращающихся элементов

Поэтому вопрос, который вы должны задать себе:

Должен ли я найти обходное решение для работы этой библиотеки или, возможно, выбрать другую библиотеку?

Обновление-1: 28-апр-2018

Если вы хотите сделать это в холсте вместо обычных элементов, то я нашел fabric.js хорошим вариантом

FabricJS

Ответ 3

мы очень близки к завершению работы после пяти дней... нам нужно оптимизировать все математические вычисления... но да, это то, что я искал:

enter image description here

Извините, но у нас нет готового кода... Я опубликую все с комментариями для других людей.

Комментарии: Для математика эта задача не очень сложна, потому что все углы прямоугольные (90º). Я попытаюсь сделать PR для Interact.js, даже для других библиотек, чтобы реализовать эту функцию по умолчанию. Надеюсь, эта работа поможет другим разработчикам;)