markerMixin.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. 'use strict';
  2. import H from './../../parts/Globals.js';
  3. import './../../parts/Chart.js';
  4. import './../../parts/Utilities.js';
  5. import './../../parts/SvgRenderer.js';
  6. /**
  7. * Options for configuring markers for annotations.
  8. *
  9. * An example of the arrow marker:
  10. * <pre>
  11. * {
  12. * arrow: {
  13. * id: 'arrow',
  14. * tagName: 'marker',
  15. * refY: 5,
  16. * refX: 5,
  17. * markerWidth: 10,
  18. * markerHeight: 10,
  19. * children: [{
  20. * tagName: 'path',
  21. * attrs: {
  22. * d: 'M 0 0 L 10 5 L 0 10 Z',
  23. * strokeWidth: 0
  24. * }
  25. * }]
  26. * }
  27. * }
  28. * </pre>
  29. * @type {Object}
  30. * @sample highcharts/annotations/custom-markers/
  31. * Define a custom marker for annotations
  32. * @sample highcharts/css/annotations-markers/
  33. * Define markers in a styled mode
  34. * @since 6.0.0
  35. * @apioption defs
  36. */
  37. var defaultMarkers = {
  38. arrow: {
  39. tagName: 'marker',
  40. render: false,
  41. id: 'arrow',
  42. refY: 5,
  43. refX: 9,
  44. markerWidth: 10,
  45. markerHeight: 10,
  46. children: [{
  47. tagName: 'path',
  48. d: 'M 0 0 L 10 5 L 0 10 Z', // triangle (used as an arrow)
  49. strokeWidth: 0
  50. }]
  51. },
  52. 'reverse-arrow': {
  53. tagName: 'marker',
  54. render: false,
  55. id: 'reverse-arrow',
  56. refY: 5,
  57. refX: 1,
  58. markerWidth: 10,
  59. markerHeight: 10,
  60. children: [{
  61. tagName: 'path',
  62. // reverse triangle (used as an arrow)
  63. d: 'M 0 5 L 10 0 L 10 10 Z',
  64. strokeWidth: 0
  65. }]
  66. }
  67. };
  68. H.SVGRenderer.prototype.addMarker = function (id, markerOptions) {
  69. var options = { id: id };
  70. var attrs = {
  71. stroke: markerOptions.color || 'none',
  72. fill: markerOptions.color || 'rgba(0, 0, 0, 0.75)'
  73. };
  74. options.children = markerOptions.children.map(function (child) {
  75. return H.merge(attrs, child);
  76. });
  77. var marker = this.definition(H.merge(true, {
  78. markerWidth: 20,
  79. markerHeight: 20,
  80. refX: 0,
  81. refY: 0,
  82. orient: 'auto'
  83. }, markerOptions, options));
  84. marker.id = id;
  85. return marker;
  86. };
  87. var createMarkerSetter = function (markerType) {
  88. return function (value) {
  89. this.attr(markerType, 'url(#' + value + ')');
  90. };
  91. };
  92. /**
  93. * @mixin
  94. */
  95. var markerMixin = {
  96. markerEndSetter: createMarkerSetter('marker-end'),
  97. markerStartSetter: createMarkerSetter('marker-start'),
  98. /*
  99. * Set markers.
  100. *
  101. * @param {Controllable} item
  102. */
  103. setItemMarkers: function (item) {
  104. var itemOptions = item.options,
  105. chart = item.chart,
  106. defs = chart.options.defs,
  107. fill = itemOptions.fill,
  108. color = H.defined(fill) && fill !== 'none' ?
  109. fill :
  110. itemOptions.stroke,
  111. setMarker = function (markerType) {
  112. var markerId = itemOptions[markerType],
  113. def,
  114. predefinedMarker,
  115. key,
  116. marker;
  117. if (markerId) {
  118. for (key in defs) {
  119. def = defs[key];
  120. if (
  121. markerId === def.id && def.tagName === 'marker'
  122. ) {
  123. predefinedMarker = def;
  124. break;
  125. }
  126. }
  127. if (predefinedMarker) {
  128. marker = item[markerType] = chart.renderer
  129. .addMarker(
  130. (itemOptions.id || H.uniqueKey()) + '-' +
  131. predefinedMarker.id,
  132. H.merge(predefinedMarker, { color: color })
  133. );
  134. item.attr(markerType, marker.attr('id'));
  135. }
  136. }
  137. };
  138. ['markerStart', 'markerEnd'].forEach(setMarker);
  139. }
  140. };
  141. // In a styled mode definition is implemented
  142. H.SVGRenderer.prototype.definition = function (def) {
  143. var ren = this;
  144. function recurse(config, parent) {
  145. var ret;
  146. H.splat(config).forEach(function (item) {
  147. var node = ren.createElement(item.tagName),
  148. attr = {};
  149. // Set attributes
  150. H.objectEach(item, function (val, key) {
  151. if (
  152. key !== 'tagName' &&
  153. key !== 'children' &&
  154. key !== 'textContent'
  155. ) {
  156. attr[key] = val;
  157. }
  158. });
  159. node.attr(attr);
  160. // Add to the tree
  161. node.add(parent || ren.defs);
  162. // Add text content
  163. if (item.textContent) {
  164. node.element.appendChild(
  165. H.doc.createTextNode(item.textContent)
  166. );
  167. }
  168. // Recurse
  169. recurse(item.children || [], node);
  170. ret = node;
  171. });
  172. // Return last node added (on top level it's the only one)
  173. return ret;
  174. }
  175. return recurse(def);
  176. };
  177. H.addEvent(H.Chart, 'afterGetContainer', function () {
  178. this.options.defs = H.merge(defaultMarkers, this.options.defs || {});
  179. H.objectEach(this.options.defs, function (def) {
  180. if (def.tagName === 'marker' && def.render !== false) {
  181. this.renderer.addMarker(def.id, def);
  182. }
  183. }, this);
  184. });
  185. export default markerMixin;