Source: 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. }
  154. else if (region.right > constrain.right) {
  155. region.left -= region.right - constrain.right;
  156. }
  157. if (region.top < constrain.top) {
  158. region.top = constrain.top;
  159. }
  160. else if (region.bottom > constrain.bottom) {
  161. region.top -= region.bottom - constrain.bottom;
  162. }
  163. region.right = region.left + region.width;
  164. region.bottom = region.top + region.height;
  165. }
  166. /**
  167. * Initializes the mover and size handler.
  168. *
  169. * @protected
  170. */
  171. initializeDrags_() {
  172. const canvas = this.getImageEditorCanvas();
  173. this.selectionDrag_ = new Drag({
  174. constrain: canvas,
  175. handles: this.element,
  176. sources: this.element,
  177. });
  178. this.sizeDrag_ = new Drag({
  179. constrain: this.getSizeDragConstrain_.bind(this),
  180. handles: this.resizer,
  181. sources: this.resizer,
  182. });
  183. this.bindDrags_();
  184. }
  185. /**
  186. * Sets the initial style for the selection and preview.
  187. *
  188. * @protected
  189. */
  190. setSelectionInitialStyle_() {
  191. const canvas = this.getImageEditorCanvas();
  192. canvas.style.opacity = 0.5;
  193. this.element.style.width = canvas.offsetWidth + 'px';
  194. this.element.style.height = canvas.offsetHeight + 'px';
  195. this.element.style.left = canvas.offsetLeft + 'px';
  196. this.element.style.top = canvas.offsetTop + 'px';
  197. this.croppedPreview_.width = canvas.offsetWidth;
  198. this.croppedPreview_.height = canvas.offsetHeight;
  199. this.croppedPreviewContext_.drawImage(
  200. canvas,
  201. this.selectionBorderWidth_,
  202. this.selectionBorderWidth_,
  203. canvas.width - this.selectionBorderWidth_ * 2,
  204. canvas.height - this.selectionBorderWidth_ * 2,
  205. 0,
  206. 0,
  207. canvas.width - this.selectionBorderWidth_ * 2,
  208. canvas.height - this.selectionBorderWidth_ * 2
  209. );
  210. this.croppedPreview_.style.width = this.croppedPreview_.width + 'px';
  211. this.croppedPreview_.style.height = this.croppedPreview_.height + 'px';
  212. }
  213. }
  214. /**
  215. * State definition.
  216. *
  217. * @static
  218. * @type {!Object}
  219. */
  220. CropHandles.STATE = {
  221. /**
  222. * Injected helper that retrieves the editor canvas element.
  223. *
  224. * @type {Function}
  225. */
  226. getImageEditorCanvas: {
  227. validator: core.isFunction,
  228. },
  229. };
  230. Soy.register(CropHandles, handlesTemplates);
  231. export default CropHandles;