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.

sugar.js 4.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. import { registerMethods } from '../../utils/methods.js'
  2. import Color from '../../types/Color.js'
  3. import Element from '../../elements/Element.js'
  4. import Matrix from '../../types/Matrix.js'
  5. import Point from '../../types/Point.js'
  6. import SVGNumber from '../../types/SVGNumber.js'
  7. // Define list of available attributes for stroke and fill
  8. const sugar = {
  9. stroke: [
  10. 'color',
  11. 'width',
  12. 'opacity',
  13. 'linecap',
  14. 'linejoin',
  15. 'miterlimit',
  16. 'dasharray',
  17. 'dashoffset'
  18. ],
  19. fill: ['color', 'opacity', 'rule'],
  20. prefix: function (t, a) {
  21. return a === 'color' ? t : t + '-' + a
  22. }
  23. }
  24. // Add sugar for fill and stroke
  25. ;['fill', 'stroke'].forEach(function (m) {
  26. const extension = {}
  27. let i
  28. extension[m] = function (o) {
  29. if (typeof o === 'undefined') {
  30. return this.attr(m)
  31. }
  32. if (
  33. typeof o === 'string' ||
  34. o instanceof Color ||
  35. Color.isRgb(o) ||
  36. o instanceof Element
  37. ) {
  38. this.attr(m, o)
  39. } else {
  40. // set all attributes from sugar.fill and sugar.stroke list
  41. for (i = sugar[m].length - 1; i >= 0; i--) {
  42. if (o[sugar[m][i]] != null) {
  43. this.attr(sugar.prefix(m, sugar[m][i]), o[sugar[m][i]])
  44. }
  45. }
  46. }
  47. return this
  48. }
  49. registerMethods(['Element', 'Runner'], extension)
  50. })
  51. registerMethods(['Element', 'Runner'], {
  52. // Let the user set the matrix directly
  53. matrix: function (mat, b, c, d, e, f) {
  54. // Act as a getter
  55. if (mat == null) {
  56. return new Matrix(this)
  57. }
  58. // Act as a setter, the user can pass a matrix or a set of numbers
  59. return this.attr('transform', new Matrix(mat, b, c, d, e, f))
  60. },
  61. // Map rotation to transform
  62. rotate: function (angle, cx, cy) {
  63. return this.transform({ rotate: angle, ox: cx, oy: cy }, true)
  64. },
  65. // Map skew to transform
  66. skew: function (x, y, cx, cy) {
  67. return arguments.length === 1 || arguments.length === 3
  68. ? this.transform({ skew: x, ox: y, oy: cx }, true)
  69. : this.transform({ skew: [x, y], ox: cx, oy: cy }, true)
  70. },
  71. shear: function (lam, cx, cy) {
  72. return this.transform({ shear: lam, ox: cx, oy: cy }, true)
  73. },
  74. // Map scale to transform
  75. scale: function (x, y, cx, cy) {
  76. return arguments.length === 1 || arguments.length === 3
  77. ? this.transform({ scale: x, ox: y, oy: cx }, true)
  78. : this.transform({ scale: [x, y], ox: cx, oy: cy }, true)
  79. },
  80. // Map translate to transform
  81. translate: function (x, y) {
  82. return this.transform({ translate: [x, y] }, true)
  83. },
  84. // Map relative translations to transform
  85. relative: function (x, y) {
  86. return this.transform({ relative: [x, y] }, true)
  87. },
  88. // Map flip to transform
  89. flip: function (direction = 'both', origin = 'center') {
  90. if ('xybothtrue'.indexOf(direction) === -1) {
  91. origin = direction
  92. direction = 'both'
  93. }
  94. return this.transform({ flip: direction, origin: origin }, true)
  95. },
  96. // Opacity
  97. opacity: function (value) {
  98. return this.attr('opacity', value)
  99. }
  100. })
  101. registerMethods('radius', {
  102. // Add x and y radius
  103. radius: function (x, y = x) {
  104. const type = (this._element || this).type
  105. return type === 'radialGradient'
  106. ? this.attr('r', new SVGNumber(x))
  107. : this.rx(x).ry(y)
  108. }
  109. })
  110. registerMethods('Path', {
  111. // Get path length
  112. length: function () {
  113. return this.node.getTotalLength()
  114. },
  115. // Get point at length
  116. pointAt: function (length) {
  117. return new Point(this.node.getPointAtLength(length))
  118. }
  119. })
  120. registerMethods(['Element', 'Runner'], {
  121. // Set font
  122. font: function (a, v) {
  123. if (typeof a === 'object') {
  124. for (v in a) this.font(v, a[v])
  125. return this
  126. }
  127. return a === 'leading'
  128. ? this.leading(v)
  129. : a === 'anchor'
  130. ? this.attr('text-anchor', v)
  131. : a === 'size' ||
  132. a === 'family' ||
  133. a === 'weight' ||
  134. a === 'stretch' ||
  135. a === 'variant' ||
  136. a === 'style'
  137. ? this.attr('font-' + a, v)
  138. : this.attr(a, v)
  139. }
  140. })
  141. // Add events to elements
  142. const methods = [
  143. 'click',
  144. 'dblclick',
  145. 'mousedown',
  146. 'mouseup',
  147. 'mouseover',
  148. 'mouseout',
  149. 'mousemove',
  150. 'mouseenter',
  151. 'mouseleave',
  152. 'touchstart',
  153. 'touchmove',
  154. 'touchleave',
  155. 'touchend',
  156. 'touchcancel',
  157. 'contextmenu',
  158. 'wheel',
  159. 'pointerdown',
  160. 'pointermove',
  161. 'pointerup',
  162. 'pointerleave',
  163. 'pointercancel'
  164. ].reduce(function (last, event) {
  165. // add event to Element
  166. const fn = function (f) {
  167. if (f === null) {
  168. this.off(event)
  169. } else {
  170. this.on(event, f)
  171. }
  172. return this
  173. }
  174. last[event] = fn
  175. return last
  176. }, {})
  177. registerMethods('Element', methods)