Source: RotateComponent.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 {core} from 'metal';
  15. import Component from 'metal-component';
  16. import Soy from 'metal-soy';
  17. import componentTemplates from './RotateComponent.soy';
  18. import './RotateControls.soy';
  19. /**
  20. * Creates a Rotate component.
  21. */
  22. class RotateComponent extends Component {
  23. /**
  24. * @inheritDoc
  25. */
  26. attached() {
  27. this.cache_ = {};
  28. this.rotationAngle_ = 0;
  29. }
  30. /**
  31. * @inheritDoc
  32. */
  33. detached() {
  34. this.cache_ = {};
  35. }
  36. /**
  37. * Rotates the image to the current selected rotation angle.
  38. *
  39. * @param {ImageData} imageData The image data representation of the image.
  40. * @return {Promise} A promise that resolves when processing is
  41. * complete.
  42. */
  43. preview(imageData) {
  44. return this.process(imageData);
  45. }
  46. /**
  47. * Rotates the image to the current selected rotation angle.
  48. *
  49. * @param {ImageData} imageData The image data representation of the image.
  50. * @return {Promise} A promise that resolves when processing is
  51. * complete.
  52. */
  53. process(imageData) {
  54. let promise = this.cache_[this.rotationAngle_];
  55. if (!promise) {
  56. promise = this.rotate_(imageData, this.rotationAngle_);
  57. this.cache_[this.rotationAngle_] = promise;
  58. }
  59. return promise;
  60. }
  61. /**
  62. * Rotates the passed image data to the current rotation angle.
  63. *
  64. * @param {ImageData} imageData The image data to rotate.
  65. * @param {number} rotationAngle The normalized rotation angle (in degrees)
  66. * in the range [0-360).
  67. * @protected
  68. * @return {Promise} A promise that resolves when the image is
  69. * rotated.
  70. */
  71. rotate_(imageData, rotationAngle) {
  72. const cancellablePromise = new Promise((resolve) => {
  73. const imageWidth = imageData.width;
  74. const imageHeight = imageData.height;
  75. const swapDimensions = (rotationAngle / 90) % 2;
  76. const imageCanvas = document.createElement('canvas');
  77. imageCanvas.width = imageWidth;
  78. imageCanvas.height = imageHeight;
  79. imageCanvas.getContext('2d').putImageData(imageData, 0, 0);
  80. const offscreenCanvas = document.createElement('canvas');
  81. offscreenCanvas.width = swapDimensions ? imageHeight : imageWidth;
  82. offscreenCanvas.height = swapDimensions ? imageWidth : imageHeight;
  83. const offscreenContext = offscreenCanvas.getContext('2d');
  84. offscreenContext.save();
  85. offscreenContext.translate(
  86. offscreenCanvas.width / 2,
  87. offscreenCanvas.height / 2
  88. );
  89. offscreenContext.rotate((rotationAngle * Math.PI) / 180);
  90. offscreenContext.drawImage(
  91. imageCanvas,
  92. -imageCanvas.width / 2,
  93. -imageCanvas.height / 2
  94. );
  95. offscreenContext.restore();
  96. resolve(
  97. offscreenContext.getImageData(
  98. 0,
  99. 0,
  100. offscreenCanvas.width,
  101. offscreenCanvas.height
  102. )
  103. );
  104. });
  105. return cancellablePromise;
  106. }
  107. /**
  108. * Rotates the image 90º counter-clockwise.
  109. */
  110. rotateLeft() {
  111. this.rotationAngle_ = (this.rotationAngle_ - 90) % 360;
  112. this.requestImageEditorPreview();
  113. }
  114. /**
  115. * Rotates the image 90º clockwise.
  116. */
  117. rotateRight() {
  118. this.rotationAngle_ = (this.rotationAngle_ + 90) % 360;
  119. this.requestImageEditorPreview();
  120. }
  121. }
  122. /**
  123. * State definition.
  124. *
  125. * @static
  126. * @type {!Object}
  127. */
  128. RotateComponent.STATE = {
  129. /**
  130. * Path of this module.
  131. *
  132. * @type {Function}
  133. */
  134. modulePath: {
  135. validator: core.isString,
  136. },
  137. /**
  138. * Injected method that notifies the editor that this component wants to
  139. * generate a preview version of the image.
  140. *
  141. * @type {Function}
  142. */
  143. requestImageEditorPreview: {
  144. validator: core.isFunction,
  145. },
  146. };
  147. Soy.register(RotateComponent, componentTemplates);
  148. export default RotateComponent;