Source: mesh/NineSlicePlane.js

mesh/NineSlicePlane.js

  1. import Plane from './Plane';
  2. import CanvasTinter from '../core/sprites/canvas/CanvasTinter';
  3. const DEFAULT_BORDER_SIZE = 10;
  4. /**
  5. * The NineSlicePlane allows you to stretch a texture using 9-slice scaling. The corners will remain unscaled (useful
  6. * for buttons with rounded corners for example) and the other areas will be scaled horizontally and or vertically
  7. *
  8. *```js
  9. * let Plane9 = new PIXI.NineSlicePlane(PIXI.Texture.fromImage('BoxWithRoundedCorners.png'), 15, 15, 15, 15);
  10. * ```
  11. * <pre>
  12. * A B
  13. * +---+----------------------+---+
  14. * C | 1 | 2 | 3 |
  15. * +---+----------------------+---+
  16. * | | | |
  17. * | 4 | 5 | 6 |
  18. * | | | |
  19. * +---+----------------------+---+
  20. * D | 7 | 8 | 9 |
  21. * +---+----------------------+---+
  22. * When changing this objects width and/or height:
  23. * areas 1 3 7 and 9 will remain unscaled.
  24. * areas 2 and 8 will be stretched horizontally
  25. * areas 4 and 6 will be stretched vertically
  26. * area 5 will be stretched both horizontally and vertically
  27. * </pre>
  28. *
  29. * @class
  30. * @extends PIXI.mesh.Plane
  31. * @memberof PIXI.mesh
  32. *
  33. */
  34. export default class NineSlicePlane extends Plane
  35. {
  36. /**
  37. * @param {PIXI.Texture} texture - The texture to use on the NineSlicePlane.
  38. * @param {int} [leftWidth=10] size of the left vertical bar (A)
  39. * @param {int} [topHeight=10] size of the top horizontal bar (C)
  40. * @param {int} [rightWidth=10] size of the right vertical bar (B)
  41. * @param {int} [bottomHeight=10] size of the bottom horizontal bar (D)
  42. */
  43. constructor(texture, leftWidth, topHeight, rightWidth, bottomHeight)
  44. {
  45. super(texture, 4, 4);
  46. this._origWidth = texture.orig.width;
  47. this._origHeight = texture.orig.height;
  48. /**
  49. * The width of the NineSlicePlane, setting this will actually modify the vertices and UV's of this plane
  50. *
  51. * @member {number}
  52. * @memberof PIXI.NineSlicePlane#
  53. * @override
  54. */
  55. this._width = this._origWidth;
  56. /**
  57. * The height of the NineSlicePlane, setting this will actually modify the vertices and UV's of this plane
  58. *
  59. * @member {number}
  60. * @memberof PIXI.NineSlicePlane#
  61. * @override
  62. */
  63. this._height = this._origHeight;
  64. /**
  65. * The width of the left column (a)
  66. *
  67. * @member {number}
  68. * @memberof PIXI.NineSlicePlane#
  69. * @override
  70. */
  71. this._leftWidth = typeof leftWidth !== 'undefined' ? leftWidth : DEFAULT_BORDER_SIZE;
  72. /**
  73. * The width of the right column (b)
  74. *
  75. * @member {number}
  76. * @memberof PIXI.NineSlicePlane#
  77. * @override
  78. */
  79. this._rightWidth = typeof rightWidth !== 'undefined' ? rightWidth : DEFAULT_BORDER_SIZE;
  80. /**
  81. * The height of the top row (c)
  82. *
  83. * @member {number}
  84. * @memberof PIXI.NineSlicePlane#
  85. * @override
  86. */
  87. this._topHeight = typeof topHeight !== 'undefined' ? topHeight : DEFAULT_BORDER_SIZE;
  88. /**
  89. * The height of the bottom row (d)
  90. *
  91. * @member {number}
  92. * @memberof PIXI.NineSlicePlane#
  93. * @override
  94. */
  95. this._bottomHeight = typeof bottomHeight !== 'undefined' ? bottomHeight : DEFAULT_BORDER_SIZE;
  96. /**
  97. * Cached tint value so we can tell when the tint is changed.
  98. *
  99. * @member {number}
  100. * @protected
  101. */
  102. this._cachedTint = 0xFFFFFF;
  103. /**
  104. * Cached tinted texture.
  105. *
  106. * @member {HTMLCanvasElement}
  107. * @protected
  108. */
  109. this._tintedTexture = null;
  110. /**
  111. * Temporary storage for canvas source coords
  112. *
  113. * @member {number[]}
  114. * @private
  115. */
  116. this._canvasUvs = null;
  117. this.refresh(true);
  118. }
  119. /**
  120. * Updates the horizontal vertices.
  121. *
  122. */
  123. updateHorizontalVertices()
  124. {
  125. const vertices = this.vertices;
  126. const h = this._topHeight + this._bottomHeight;
  127. const scale = this._height > h ? 1.0 : this._height / h;
  128. vertices[9] = vertices[11] = vertices[13] = vertices[15] = this._topHeight * scale;
  129. vertices[17] = vertices[19] = vertices[21] = vertices[23] = this._height - (this._bottomHeight * scale);
  130. vertices[25] = vertices[27] = vertices[29] = vertices[31] = this._height;
  131. }
  132. /**
  133. * Updates the vertical vertices.
  134. *
  135. */
  136. updateVerticalVertices()
  137. {
  138. const vertices = this.vertices;
  139. const w = this._leftWidth + this._rightWidth;
  140. const scale = this._width > w ? 1.0 : this._width / w;
  141. vertices[2] = vertices[10] = vertices[18] = vertices[26] = this._leftWidth * scale;
  142. vertices[4] = vertices[12] = vertices[20] = vertices[28] = this._width - (this._rightWidth * scale);
  143. vertices[6] = vertices[14] = vertices[22] = vertices[30] = this._width;
  144. }
  145. /**
  146. * Renders the object using the Canvas renderer
  147. *
  148. * @private
  149. * @param {PIXI.CanvasRenderer} renderer - The canvas renderer to render with.
  150. */
  151. _renderCanvas(renderer)
  152. {
  153. const context = renderer.context;
  154. const transform = this.worldTransform;
  155. const res = renderer.resolution;
  156. const isTinted = this.tint !== 0xFFFFFF;
  157. const texture = this._texture;
  158. // Work out tinting
  159. if (isTinted)
  160. {
  161. if (this._cachedTint !== this.tint)
  162. {
  163. // Tint has changed, need to update the tinted texture and use that instead
  164. this._cachedTint = this.tint;
  165. this._tintedTexture = CanvasTinter.getTintedTexture(this, this.tint);
  166. }
  167. }
  168. const textureSource = !isTinted ? texture.baseTexture.source : this._tintedTexture;
  169. if (!this._canvasUvs)
  170. {
  171. this._canvasUvs = [0, 0, 0, 0, 0, 0, 0, 0];
  172. }
  173. const vertices = this.vertices;
  174. const uvs = this._canvasUvs;
  175. const u0 = isTinted ? 0 : texture.frame.x;
  176. const v0 = isTinted ? 0 : texture.frame.y;
  177. const u1 = u0 + texture.frame.width;
  178. const v1 = v0 + texture.frame.height;
  179. uvs[0] = u0;
  180. uvs[1] = u0 + this._leftWidth;
  181. uvs[2] = u1 - this._rightWidth;
  182. uvs[3] = u1;
  183. uvs[4] = v0;
  184. uvs[5] = v0 + this._topHeight;
  185. uvs[6] = v1 - this._bottomHeight;
  186. uvs[7] = v1;
  187. for (let i = 0; i < 8; i++)
  188. {
  189. uvs[i] *= texture.baseTexture.resolution;
  190. }
  191. context.globalAlpha = this.worldAlpha;
  192. renderer.setBlendMode(this.blendMode);
  193. if (renderer.roundPixels)
  194. {
  195. context.setTransform(
  196. transform.a * res,
  197. transform.b * res,
  198. transform.c * res,
  199. transform.d * res,
  200. (transform.tx * res) | 0,
  201. (transform.ty * res) | 0
  202. );
  203. }
  204. else
  205. {
  206. context.setTransform(
  207. transform.a * res,
  208. transform.b * res,
  209. transform.c * res,
  210. transform.d * res,
  211. transform.tx * res,
  212. transform.ty * res
  213. );
  214. }
  215. for (let row = 0; row < 3; row++)
  216. {
  217. for (let col = 0; col < 3; col++)
  218. {
  219. const ind = (col * 2) + (row * 8);
  220. const sw = Math.max(1, uvs[col + 1] - uvs[col]);
  221. const sh = Math.max(1, uvs[row + 5] - uvs[row + 4]);
  222. const dw = Math.max(1, vertices[ind + 10] - vertices[ind]);
  223. const dh = Math.max(1, vertices[ind + 11] - vertices[ind + 1]);
  224. context.drawImage(textureSource, uvs[col], uvs[row + 4], sw, sh,
  225. vertices[ind], vertices[ind + 1], dw, dh);
  226. }
  227. }
  228. }
  229. /**
  230. * The width of the NineSlicePlane, setting this will actually modify the vertices and UV's of this plane
  231. *
  232. * @member {number}
  233. */
  234. get width()
  235. {
  236. return this._width;
  237. }
  238. set width(value) // eslint-disable-line require-jsdoc
  239. {
  240. this._width = value;
  241. this._refresh();
  242. }
  243. /**
  244. * The height of the NineSlicePlane, setting this will actually modify the vertices and UV's of this plane
  245. *
  246. * @member {number}
  247. */
  248. get height()
  249. {
  250. return this._height;
  251. }
  252. set height(value) // eslint-disable-line require-jsdoc
  253. {
  254. this._height = value;
  255. this._refresh();
  256. }
  257. /**
  258. * The width of the left column
  259. *
  260. * @member {number}
  261. */
  262. get leftWidth()
  263. {
  264. return this._leftWidth;
  265. }
  266. set leftWidth(value) // eslint-disable-line require-jsdoc
  267. {
  268. this._leftWidth = value;
  269. this._refresh();
  270. }
  271. /**
  272. * The width of the right column
  273. *
  274. * @member {number}
  275. */
  276. get rightWidth()
  277. {
  278. return this._rightWidth;
  279. }
  280. set rightWidth(value) // eslint-disable-line require-jsdoc
  281. {
  282. this._rightWidth = value;
  283. this._refresh();
  284. }
  285. /**
  286. * The height of the top row
  287. *
  288. * @member {number}
  289. */
  290. get topHeight()
  291. {
  292. return this._topHeight;
  293. }
  294. set topHeight(value) // eslint-disable-line require-jsdoc
  295. {
  296. this._topHeight = value;
  297. this._refresh();
  298. }
  299. /**
  300. * The height of the bottom row
  301. *
  302. * @member {number}
  303. */
  304. get bottomHeight()
  305. {
  306. return this._bottomHeight;
  307. }
  308. set bottomHeight(value) // eslint-disable-line require-jsdoc
  309. {
  310. this._bottomHeight = value;
  311. this._refresh();
  312. }
  313. /**
  314. * Refreshes NineSlicePlane coords. All of them.
  315. */
  316. _refresh()
  317. {
  318. super._refresh();
  319. const uvs = this.uvs;
  320. const texture = this._texture;
  321. this._origWidth = texture.orig.width;
  322. this._origHeight = texture.orig.height;
  323. const _uvw = 1.0 / this._origWidth;
  324. const _uvh = 1.0 / this._origHeight;
  325. uvs[0] = uvs[8] = uvs[16] = uvs[24] = 0;
  326. uvs[1] = uvs[3] = uvs[5] = uvs[7] = 0;
  327. uvs[6] = uvs[14] = uvs[22] = uvs[30] = 1;
  328. uvs[25] = uvs[27] = uvs[29] = uvs[31] = 1;
  329. uvs[2] = uvs[10] = uvs[18] = uvs[26] = _uvw * this._leftWidth;
  330. uvs[4] = uvs[12] = uvs[20] = uvs[28] = 1 - (_uvw * this._rightWidth);
  331. uvs[9] = uvs[11] = uvs[13] = uvs[15] = _uvh * this._topHeight;
  332. uvs[17] = uvs[19] = uvs[21] = uvs[23] = 1 - (_uvh * this._bottomHeight);
  333. this.updateHorizontalVertices();
  334. this.updateVerticalVertices();
  335. this.dirty++;
  336. this.multiplyUvs();
  337. }
  338. }