盒模型

类似于 DOM 元素,spritejs 的元素也有自己的“盒模型”,具体来说,是指一个sprite元素有内容、padding、border,它们依照标准的规则占据一定的可视区域,另外由于元素还有transform,因此还有一个特殊的boundingRect。

我们用一张图来表示sprite元素的盒模型:

box-model

属性sprite.contentSizeinnerSize表示是元素内容的宽高,给元素设置attr('size', size)改变的也是这个值。而在它外面是padding(如果有的话),再外面是border(如果有的话),它和padding、innerSize共同构成了元素的offsetSize以及originalRect。如果元素被旋转、缩放,那么它实际在canvas内占据的矩形则是boundingRect。我们看一下例子:

盒模型 - 图2

padding: 20

border: 5

rotate: 0

size: 100


contentSize: 100,100

clientSize: 140,140

offsetSize: 150,150

originalRect: -75,-75,150,150

boundingRect: -75,-75,150,150

originalRenderRect: 310,225,150,150

renderRect: 310,225,150,150

  1. const scene = new Scene('#box-rect', {viewport: ['auto', 'auto'], resolution: [770, 600]});
  2. const layer = scene.layer('fglayer');
  3. const s1 = new Path();
  4. s1.attr({
  5. path: {
  6. d: 'M0,0L0,1L1,1L1,0z',
  7. transform: {scale: 100},
  8. },
  9. anchor: [0.5, 0.5],
  10. fillColor: '#f77',
  11. pos: [385, 300],
  12. padding: [20, 20, 20, 20],
  13. bgcolor: 'rgba(0, 0, 0, 0.3)',
  14. border: [5, 'black'],
  15. borderRadius: 10,
  16. });
  17. layer.append(s1);
  18. const contentSize = document.getElementById('contentSize'),
  19. clientSize = document.getElementById('clientSize'),
  20. offsetSize = document.getElementById('offsetSize'),
  21. originalRect = document.getElementById('originalRect'),
  22. boundingRect = document.getElementById('boundingRect'),
  23. originalRenderRect = document.getElementById('originalRenderRect'),
  24. renderRect = document.getElementById('renderRect'),
  25. paddingCtl = document.getElementById('paddingCtl'),
  26. borderCtl = document.getElementById('borderCtl'),
  27. rotateCtl = document.getElementById('rotateCtl'),
  28. sizeCtl = document.getElementById('sizeCtl'),
  29. paddingValue = document.getElementById('paddingValue'),
  30. rotateValue = document.getElementById('rotateValue'),
  31. sizeValue = document.getElementById('sizeValue'),
  32. borderValue = document.getElementById('borderValue');
  33. function box(rect) {
  34. const [x, y, w, h] = rect;
  35. return [...[x, y].map(Math.floor), ...[w, h].map(Math.ceil)];
  36. }
  37. function updateState() {
  38. contentSize.innerHTML = s1.contentSize;
  39. clientSize.innerHTML = s1.clientSize;
  40. offsetSize.innerHTML = s1.offsetSize;
  41. originalRect.innerHTML = box(s1.originalRect);
  42. boundingRect.innerHTML = box(s1.boundingRect);
  43. originalRenderRect.innerHTML = box(s1.originalRenderRect);
  44. renderRect.innerHTML = box(s1.renderRect);
  45. }
  46. updateState();
  47. paddingCtl.addEventListener('change', (evt) => {
  48. const value = evt.target.value;
  49. s1.attr('padding', value);
  50. paddingValue.innerHTML = value;
  51. updateState();
  52. });
  53. borderCtl.addEventListener('change', (evt) => {
  54. const value = evt.target.value;
  55. s1.attr('border', [value]);
  56. borderValue.innerHTML = value;
  57. updateState();
  58. });
  59. rotateCtl.addEventListener('change', (evt) => {
  60. const value = evt.target.value;
  61. s1.attr('rotate', [value]);
  62. rotateValue.innerHTML = value;
  63. updateState();
  64. });
  65. sizeCtl.addEventListener('change', (evt) => {
  66. const value = evt.target.value;
  67. const path = s1.attr('path');
  68. path.transform.scale = value;
  69. s1.attr({path});
  70. sizeValue.innerHTML = value;
  71. updateState();
  72. });

注意一个细节,originalRect和boundingRect的坐标原点是sprite元素的anchor points,因为我们把anchor设为了[0.5, 0.5]所以我们看到的坐标起始点是负值。另外我们还可以直接拿到renderRect和originRect,对应元素transform之前和之后在画布上的实际坐标。