You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

rectangle.tsx 3.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. import { v4 as uuid } from 'uuid'
  2. import * as vec from 'utils/vec'
  3. import { RectangleShape, ShapeType } from 'types'
  4. import { registerShapeUtils } from './index'
  5. import { boundsCollidePolygon, boundsContainPolygon } from 'utils/bounds'
  6. import {
  7. getBoundsFromPoints,
  8. getRotatedCorners,
  9. translateBounds,
  10. } from 'utils/utils'
  11. import { defaultStyle, getShapeStyle } from 'lib/shape-styles'
  12. const rectangle = registerShapeUtils<RectangleShape>({
  13. boundsCache: new WeakMap([]),
  14. create(props) {
  15. return {
  16. id: uuid(),
  17. type: ShapeType.Rectangle,
  18. isGenerated: false,
  19. name: 'Rectangle',
  20. parentId: 'page0',
  21. childIndex: 0,
  22. point: [0, 0],
  23. size: [1, 1],
  24. radius: 2,
  25. rotation: 0,
  26. isAspectRatioLocked: false,
  27. isLocked: false,
  28. isHidden: false,
  29. style: defaultStyle,
  30. ...props,
  31. }
  32. },
  33. render({ id, size, radius, style }) {
  34. const styles = getShapeStyle(style)
  35. return (
  36. <g id={id}>
  37. <rect
  38. id={id}
  39. rx={radius}
  40. ry={radius}
  41. width={Math.max(0, size[0] - Number(styles.strokeWidth) / 2)}
  42. height={Math.max(0, size[1] - Number(styles.strokeWidth) / 2)}
  43. />
  44. </g>
  45. )
  46. },
  47. applyStyles(shape, style) {
  48. Object.assign(shape.style, style)
  49. return this
  50. },
  51. getBounds(shape) {
  52. if (!this.boundsCache.has(shape)) {
  53. const [width, height] = shape.size
  54. const bounds = {
  55. minX: 0,
  56. maxX: width,
  57. minY: 0,
  58. maxY: height,
  59. width,
  60. height,
  61. }
  62. this.boundsCache.set(shape, bounds)
  63. }
  64. return translateBounds(this.boundsCache.get(shape), shape.point)
  65. },
  66. getRotatedBounds(shape) {
  67. return getBoundsFromPoints(
  68. getRotatedCorners(this.getBounds(shape), shape.rotation)
  69. )
  70. },
  71. getCenter(shape) {
  72. const bounds = this.getRotatedBounds(shape)
  73. return [bounds.minX + bounds.width / 2, bounds.minY + bounds.height / 2]
  74. },
  75. hitTest(shape) {
  76. return true
  77. },
  78. hitTestBounds(shape, brushBounds) {
  79. const rotatedCorners = getRotatedCorners(
  80. this.getBounds(shape),
  81. shape.rotation
  82. )
  83. return (
  84. boundsContainPolygon(brushBounds, rotatedCorners) ||
  85. boundsCollidePolygon(brushBounds, rotatedCorners)
  86. )
  87. },
  88. rotateTo(shape, rotation) {
  89. shape.rotation = rotation
  90. return this
  91. },
  92. translateTo(shape, point) {
  93. shape.point = vec.toPrecision(point)
  94. return this
  95. },
  96. transform(shape, bounds, { initialShape, transformOrigin, scaleX, scaleY }) {
  97. if (shape.rotation === 0 && !shape.isAspectRatioLocked) {
  98. shape.size = [bounds.width, bounds.height]
  99. shape.point = [bounds.minX, bounds.minY]
  100. } else {
  101. shape.size = vec.mul(
  102. initialShape.size,
  103. Math.min(Math.abs(scaleX), Math.abs(scaleY))
  104. )
  105. shape.point = [
  106. bounds.minX +
  107. (bounds.width - shape.size[0]) *
  108. (scaleX < 0 ? 1 - transformOrigin[0] : transformOrigin[0]),
  109. bounds.minY +
  110. (bounds.height - shape.size[1]) *
  111. (scaleY < 0 ? 1 - transformOrigin[1] : transformOrigin[1]),
  112. ]
  113. shape.rotation =
  114. (scaleX < 0 && scaleY >= 0) || (scaleY < 0 && scaleX >= 0)
  115. ? -initialShape.rotation
  116. : initialShape.rotation
  117. }
  118. return this
  119. },
  120. transformSingle(shape, bounds) {
  121. shape.size = [bounds.width, bounds.height]
  122. shape.point = [bounds.minX, bounds.minY]
  123. return this
  124. },
  125. setProperty(shape, prop, value) {
  126. shape[prop] = value
  127. return this
  128. },
  129. canTransform: true,
  130. canChangeAspectRatio: true,
  131. canStyleFill: true,
  132. })
  133. export default rectangle