Source: frontend-image-editor-capability-crop/src/main/resources/META-INF/resources/CropHandles.es.js

  1. /**
  2. * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
  3. *
  4. * This library is free software; you can redistribute it and/or modify it under
  5. * the terms of the GNU Lesser General Public License as published by the Free
  6. * Software Foundation; either version 2.1 of the License, or (at your option)
  7. * any later version.
  8. *
  9. * This library is distributed in the hope that it will be useful, but WITHOUT
  10. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  11. * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
  12. * details.
  13. */
  14. import {async, core} from 'metal';
  15. import Component from 'metal-component';
  16. import dom from 'metal-dom';
  17. import {Drag} from 'metal-drag-drop';
  18. import Position from 'metal-position';
  19. import Soy from 'metal-soy';
  20. import handlesTemplates from './CropHandles.soy';
  21. /**
  22. * Creates a Crop Handles Component.
  23. */
  24. class CropHandles extends Component {
  25. /**
  26. * @inheritDoc
  27. */
  28. attached() {
  29. this.parentNode_ = this.element.parentNode;
  30. this.resizer = this.element.querySelector('.resize-handle');
  31. this.selectionBorderWidth_ = parseInt(
  32. this.element.style.borderWidth,
  33. 10
  34. );
  35. this.croppedPreview_ = this.element.querySelector(
  36. '.cropped-image-preview'
  37. );
  38. this.croppedPreviewContext_ = this.croppedPreview_.getContext('2d');
  39. async.nextTick(() => {
  40. const canvas = this.getImageEditorCanvas();
  41. this.setSelectionInitialStyle_();
  42. this.initializeDrags_();
  43. dom.removeClasses(this.element, 'hide');
  44. dom.append(canvas.parentElement, this.element);
  45. });
  46. }
  47. /**
  48. * @inheritDoc
  49. */
  50. detached() {
  51. const canvas = this.getImageEditorCanvas();
  52. canvas.style.opacity = 1;
  53. }
  54. /**
  55. * Binds actions for the mover and size handler.
  56. *
  57. * @protected
  58. */
  59. bindDrags_() {
  60. this.resizer.addEventListener('mousedown', event =>
  61. event.stopPropagation()
  62. );
  63. this.bindSelectionDrag_();
  64. this.bindSizeDrag_();
  65. }
  66. /**
  67. * Binds actions for the mover.
  68. *
  69. * @protected
  70. */
  71. bindSelectionDrag_() {
  72. const canvas = this.getImageEditorCanvas();
  73. this.selectionDrag_.on(Drag.Events.DRAG, data => {
  74. const left =
  75. data.relativeX - canvas.offsetLeft + this.selectionBorderWidth_;
  76. const top =
  77. data.relativeY - canvas.offsetTop + this.selectionBorderWidth_;
  78. this.element.style.left = left + 'px';
  79. this.element.style.top = top + 'px';
  80. this.croppedPreviewContext_.drawImage(
  81. canvas,
  82. left,
  83. top,
  84. this.croppedPreview_.width,
  85. this.croppedPreview_.height,
  86. 0,
  87. 0,
  88. this.croppedPreview_.width,
  89. this.croppedPreview_.height
  90. );
  91. });
  92. }
  93. /**
  94. * Binds actions for the size handler.
  95. *
  96. * @protected
  97. */
  98. bindSizeDrag_() {
  99. const canvas = this.getImageEditorCanvas();
  100. this.sizeDrag_.on(Drag.Events.DRAG, data => {
  101. const width = data.relativeX + this.resizer.offsetWidth / 2;
  102. const height = data.relativeY + this.resizer.offsetHeight / 2;
  103. this.element.style.width =
  104. width + this.selectionBorderWidth_ * 2 + 'px';
  105. this.element.style.height =
  106. height + this.selectionBorderWidth_ * 2 + 'px';
  107. this.croppedPreview_.width = width;
  108. this.croppedPreview_.height = height;
  109. this.croppedPreviewContext_.drawImage(
  110. canvas,
  111. this.element.offsetLeft -
  112. canvas.offsetLeft +
  113. this.selectionBorderWidth_,
  114. this.element.offsetTop -
  115. canvas.offsetTop +
  116. this.selectionBorderWidth_,
  117. width,
  118. height,
  119. 0,
  120. 0,
  121. width,
  122. height
  123. );
  124. this.croppedPreview_.style.width = width + 'px';
  125. this.croppedPreview_.style.height = height + 'px';
  126. });
  127. }
  128. /**
  129. * Calculates the constrain region for the selection drag and resize.
  130. *
  131. * @protected
  132. */
  133. getSizeDragConstrain_(region) {
  134. const canvas = this.getImageEditorCanvas();
  135. const constrain = Position.getRegion(canvas);
  136. const selection = Position.getRegion(this.element);
  137. constrain.left =
  138. selection.left +
  139. this.resizer.offsetWidth +
  140. this.selectionBorderWidth_ * 2;
  141. constrain.top =
  142. selection.top +
  143. this.resizer.offsetHeight +
  144. this.selectionBorderWidth_ * 2;
  145. constrain.width = constrain.right - constrain.left;
  146. constrain.height = constrain.bottom - constrain.top;
  147. constrain.right +=
  148. this.resizer.offsetWidth / 2 - this.selectionBorderWidth_;
  149. constrain.bottom +=
  150. this.resizer.offsetHeight / 2 - this.selectionBorderWidth_;
  151. if (region.left < constrain.left) {
  152. region.left = constrain.left;
  153. } else if (region.right > constrain.right) {
  154. region.left -= region.right - constrain.right;
  155. }
  156. if (region.top < constrain.top) {
  157. region.top = constrain.top;
  158. } else if (region.bottom > constrain.bottom) {
  159. region.top -= region.bottom - constrain.bottom;
  160. }
  161. region.right = region.left + region.width;
  162. region.bottom = region.top + region.height;
  163. }
  164. /**
  165. * Initializes the mover and size handler.
  166. *
  167. * @protected
  168. */
  169. initializeDrags_() {
  170. const canvas = this.getImageEditorCanvas();
  171. this.selectionDrag_ = new Drag({
  172. constrain: canvas,
  173. handles: this.element,
  174. sources: this.element
  175. });
  176. this.sizeDrag_ = new Drag({
  177. constrain: this.getSizeDragConstrain_.bind(this),
  178. handles: this.resizer,
  179. sources: this.resizer
  180. });
  181. this.bindDrags_();
  182. }
  183. /**
  184. * Sets the initial style for the selection and preview.
  185. *
  186. * @protected
  187. */
  188. setSelectionInitialStyle_() {
  189. const canvas = this.getImageEditorCanvas();
  190. canvas.style.opacity = 0.5;
  191. this.element.style.width = canvas.offsetWidth + 'px';
  192. this.element.style.height = canvas.offsetHeight + 'px';
  193. this.element.style.left = canvas.offsetLeft + 'px';
  194. this.element.style.top = canvas.offsetTop + 'px';
  195. this.croppedPreview_.width = canvas.offsetWidth;
  196. this.croppedPreview_.height = canvas.offsetHeight;
  197. this.croppedPreviewContext_.drawImage(
  198. canvas,
  199. this.selectionBorderWidth_,
  200. this.selectionBorderWidth_,
  201. canvas.width - this.selectionBorderWidth_ * 2,
  202. canvas.height - this.selectionBorderWidth_ * 2,
  203. 0,
  204. 0,
  205. canvas.width - this.selectionBorderWidth_ * 2,
  206. canvas.height - this.selectionBorderWidth_ * 2
  207. );
  208. this.croppedPreview_.style.width = this.croppedPreview_.width + 'px';
  209. this.croppedPreview_.style.height = this.croppedPreview_.height + 'px';
  210. }
  211. }
  212. /**
  213. * State definition.
  214. *
  215. * @static
  216. * @type {!Object}
  217. */
  218. CropHandles.STATE = {
  219. /**
  220. * Injected helper that retrieves the editor canvas element.
  221. *
  222. * @type {Function}
  223. */
  224. getImageEditorCanvas: {
  225. validator: core.isFunction
  226. }
  227. };
  228. Soy.register(CropHandles, handlesTemplates);
  229. export default CropHandles;