image.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. import { Control } from "./control";
  2. import { Nullable, Tools, Observable } from "babylonjs";
  3. import { Measure } from "../measure";
  4. /**
  5. * Class used to create 2D images
  6. */
  7. export class Image extends Control {
  8. private _domImage: HTMLImageElement;
  9. private _imageWidth: number;
  10. private _imageHeight: number;
  11. private _loaded = false;
  12. private _stretch = Image.STRETCH_FILL;
  13. private _source: Nullable<string>;
  14. private _autoScale = false;
  15. private _sourceLeft = 0;
  16. private _sourceTop = 0;
  17. private _sourceWidth = 0;
  18. private _sourceHeight = 0;
  19. private _cellWidth: number = 0;
  20. private _cellHeight: number = 0;
  21. private _cellId: number = -1;
  22. /**
  23. * Observable notified when the content is loaded
  24. */
  25. public onImageLoadedObservable = new Observable<Image>();
  26. /**
  27. * Gets a boolean indicating that the content is loaded
  28. */
  29. public get isLoaded(): boolean {
  30. return this._loaded;
  31. }
  32. /**
  33. * Gets or sets the left coordinate in the source image
  34. */
  35. public get sourceLeft(): number {
  36. return this._sourceLeft;
  37. }
  38. public set sourceLeft(value: number) {
  39. if (this._sourceLeft === value) {
  40. return;
  41. }
  42. this._sourceLeft = value;
  43. this._markAsDirty();
  44. }
  45. /**
  46. * Gets or sets the top coordinate in the source image
  47. */
  48. public get sourceTop(): number {
  49. return this._sourceTop;
  50. }
  51. public set sourceTop(value: number) {
  52. if (this._sourceTop === value) {
  53. return;
  54. }
  55. this._sourceTop = value;
  56. this._markAsDirty();
  57. }
  58. /**
  59. * Gets or sets the width to capture in the source image
  60. */
  61. public get sourceWidth(): number {
  62. return this._sourceWidth;
  63. }
  64. public set sourceWidth(value: number) {
  65. if (this._sourceWidth === value) {
  66. return;
  67. }
  68. this._sourceWidth = value;
  69. this._markAsDirty();
  70. }
  71. /**
  72. * Gets or sets the height to capture in the source image
  73. */
  74. public get sourceHeight(): number {
  75. return this._sourceHeight;
  76. }
  77. public set sourceHeight(value: number) {
  78. if (this._sourceHeight === value) {
  79. return;
  80. }
  81. this._sourceHeight = value;
  82. this._markAsDirty();
  83. }
  84. /**
  85. * Gets or sets a boolean indicating if the image can force its container to adapt its size
  86. * @see http://doc.babylonjs.com/how_to/gui#image
  87. */
  88. public get autoScale(): boolean {
  89. return this._autoScale;
  90. }
  91. public set autoScale(value: boolean) {
  92. if (this._autoScale === value) {
  93. return;
  94. }
  95. this._autoScale = value;
  96. if (value && this._loaded) {
  97. this.synchronizeSizeWithContent();
  98. }
  99. }
  100. /** Gets or sets the streching mode used by the image */
  101. public get stretch(): number {
  102. return this._stretch;
  103. }
  104. public set stretch(value: number) {
  105. if (this._stretch === value) {
  106. return;
  107. }
  108. this._stretch = value;
  109. this._markAsDirty();
  110. }
  111. /**
  112. * Gets or sets the internal DOM image used to render the control
  113. */
  114. public set domImage(value: HTMLImageElement) {
  115. this._domImage = value;
  116. this._loaded = false;
  117. if (this._domImage.width) {
  118. this._onImageLoaded();
  119. } else {
  120. this._domImage.onload = () => {
  121. this._onImageLoaded();
  122. };
  123. }
  124. }
  125. public get domImage(): HTMLImageElement {
  126. return this._domImage;
  127. }
  128. private _onImageLoaded(): void {
  129. this._imageWidth = this._domImage.width;
  130. this._imageHeight = this._domImage.height;
  131. this._loaded = true;
  132. if (this._autoScale) {
  133. this.synchronizeSizeWithContent();
  134. }
  135. this.onImageLoadedObservable.notifyObservers(this);
  136. this._markAsDirty();
  137. }
  138. /**
  139. * Gets or sets image source url
  140. */
  141. public set source(value: Nullable<string>) {
  142. if (this._source === value) {
  143. return;
  144. }
  145. this._loaded = false;
  146. this._source = value;
  147. this._domImage = document.createElement("img");
  148. this._domImage.onload = () => {
  149. this._onImageLoaded();
  150. };
  151. if (value) {
  152. Tools.SetCorsBehavior(value, this._domImage);
  153. this._domImage.src = value;
  154. }
  155. }
  156. /**
  157. * Gets or sets the cell width to use when animation sheet is enabled
  158. * @see http://doc.babylonjs.com/how_to/gui#image
  159. */
  160. get cellWidth(): number {
  161. return this._cellWidth;
  162. }
  163. set cellWidth(value: number) {
  164. if (this._cellWidth === value) {
  165. return;
  166. }
  167. this._cellWidth = value;
  168. this._markAsDirty();
  169. }
  170. /**
  171. * Gets or sets the cell height to use when animation sheet is enabled
  172. * @see http://doc.babylonjs.com/how_to/gui#image
  173. */
  174. get cellHeight(): number {
  175. return this._cellHeight;
  176. }
  177. set cellHeight(value: number) {
  178. if (this._cellHeight === value) {
  179. return;
  180. }
  181. this._cellHeight = value;
  182. this._markAsDirty();
  183. }
  184. /**
  185. * Gets or sets the cell id to use (this will turn on the animation sheet mode)
  186. * @see http://doc.babylonjs.com/how_to/gui#image
  187. */
  188. get cellId(): number {
  189. return this._cellId;
  190. }
  191. set cellId(value: number) {
  192. if (this._cellId === value) {
  193. return;
  194. }
  195. this._cellId = value;
  196. this._markAsDirty();
  197. }
  198. /**
  199. * Creates a new Image
  200. * @param name defines the control name
  201. * @param url defines the image url
  202. */
  203. constructor(public name?: string, url: Nullable<string> = null) {
  204. super(name);
  205. this.source = url;
  206. }
  207. protected _getTypeName(): string {
  208. return "Image";
  209. }
  210. /** Force the control to synchronize with its content */
  211. public synchronizeSizeWithContent() {
  212. if (!this._loaded) {
  213. return;
  214. }
  215. this.width = this._domImage.width + "px";
  216. this.height = this._domImage.height + "px";
  217. }
  218. public _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
  219. context.save();
  220. if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
  221. context.shadowColor = this.shadowColor;
  222. context.shadowBlur = this.shadowBlur;
  223. context.shadowOffsetX = this.shadowOffsetX;
  224. context.shadowOffsetY = this.shadowOffsetY;
  225. }
  226. let x, y, width, height;
  227. if (this.cellId == -1) {
  228. x = this._sourceLeft;
  229. y = this._sourceTop;
  230. width = this._sourceWidth ? this._sourceWidth : this._imageWidth;
  231. height = this._sourceHeight ? this._sourceHeight : this._imageHeight;
  232. }
  233. else {
  234. let rowCount = this._domImage.naturalWidth / this.cellWidth;
  235. let column = (this.cellId / rowCount) >> 0;
  236. let row = this.cellId % rowCount;
  237. x = this.cellWidth * row;
  238. y = this.cellHeight * column;
  239. width = this.cellWidth;
  240. height = this.cellHeight;
  241. }
  242. this._applyStates(context);
  243. if (this._processMeasures(parentMeasure, context)) {
  244. if (this._loaded) {
  245. switch (this._stretch) {
  246. case Image.STRETCH_NONE:
  247. context.drawImage(this._domImage, x, y, width, height,
  248. this._currentMeasure.left, this._currentMeasure.top, this._currentMeasure.width, this._currentMeasure.height);
  249. break;
  250. case Image.STRETCH_FILL:
  251. context.drawImage(this._domImage, x, y, width, height,
  252. this._currentMeasure.left, this._currentMeasure.top, this._currentMeasure.width, this._currentMeasure.height);
  253. break;
  254. case Image.STRETCH_UNIFORM:
  255. var hRatio = this._currentMeasure.width / width;
  256. var vRatio = this._currentMeasure.height / height;
  257. var ratio = Math.min(hRatio, vRatio);
  258. var centerX = (this._currentMeasure.width - width * ratio) / 2;
  259. var centerY = (this._currentMeasure.height - height * ratio) / 2;
  260. context.drawImage(this._domImage, x, y, width, height,
  261. this._currentMeasure.left + centerX, this._currentMeasure.top + centerY, width * ratio, height * ratio);
  262. break;
  263. case Image.STRETCH_EXTEND:
  264. context.drawImage(this._domImage, x, y, width, height,
  265. this._currentMeasure.left, this._currentMeasure.top, this._currentMeasure.width, this._currentMeasure.height);
  266. if (this._autoScale) {
  267. this.synchronizeSizeWithContent();
  268. }
  269. if (this._root && this._root.parent) { // Will update root size if root is not the top root
  270. this._root.width = this.width;
  271. this._root.height = this.height;
  272. }
  273. break;
  274. }
  275. }
  276. }
  277. context.restore();
  278. }
  279. public dispose() {
  280. super.dispose();
  281. this.onImageLoadedObservable.clear();
  282. }
  283. // Static
  284. /** STRETCH_NONE */
  285. public static readonly STRETCH_NONE = 0;
  286. /** STRETCH_FILL */
  287. public static readonly STRETCH_FILL = 1;
  288. /** STRETCH_UNIFORM */
  289. public static readonly STRETCH_UNIFORM = 2;
  290. /** STRETCH_EXTEND */
  291. public static readonly STRETCH_EXTEND = 3;
  292. }