symbol.js 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. */
  19. var zrUtil = require("zrender/lib/core/util");
  20. var graphic = require("./graphic");
  21. var BoundingRect = require("zrender/lib/core/BoundingRect");
  22. var _text = require("zrender/lib/contain/text");
  23. var calculateTextPosition = _text.calculateTextPosition;
  24. /*
  25. * Licensed to the Apache Software Foundation (ASF) under one
  26. * or more contributor license agreements. See the NOTICE file
  27. * distributed with this work for additional information
  28. * regarding copyright ownership. The ASF licenses this file
  29. * to you under the Apache License, Version 2.0 (the
  30. * "License"); you may not use this file except in compliance
  31. * with the License. You may obtain a copy of the License at
  32. *
  33. * http://www.apache.org/licenses/LICENSE-2.0
  34. *
  35. * Unless required by applicable law or agreed to in writing,
  36. * software distributed under the License is distributed on an
  37. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  38. * KIND, either express or implied. See the License for the
  39. * specific language governing permissions and limitations
  40. * under the License.
  41. */
  42. // Symbol factory
  43. /**
  44. * Triangle shape
  45. * @inner
  46. */
  47. var Triangle = graphic.extendShape({
  48. type: 'triangle',
  49. shape: {
  50. cx: 0,
  51. cy: 0,
  52. width: 0,
  53. height: 0
  54. },
  55. buildPath: function (path, shape) {
  56. var cx = shape.cx;
  57. var cy = shape.cy;
  58. var width = shape.width / 2;
  59. var height = shape.height / 2;
  60. path.moveTo(cx, cy - height);
  61. path.lineTo(cx + width, cy + height);
  62. path.lineTo(cx - width, cy + height);
  63. path.closePath();
  64. }
  65. });
  66. /**
  67. * Diamond shape
  68. * @inner
  69. */
  70. var Diamond = graphic.extendShape({
  71. type: 'diamond',
  72. shape: {
  73. cx: 0,
  74. cy: 0,
  75. width: 0,
  76. height: 0
  77. },
  78. buildPath: function (path, shape) {
  79. var cx = shape.cx;
  80. var cy = shape.cy;
  81. var width = shape.width / 2;
  82. var height = shape.height / 2;
  83. path.moveTo(cx, cy - height);
  84. path.lineTo(cx + width, cy);
  85. path.lineTo(cx, cy + height);
  86. path.lineTo(cx - width, cy);
  87. path.closePath();
  88. }
  89. });
  90. /**
  91. * Pin shape
  92. * @inner
  93. */
  94. var Pin = graphic.extendShape({
  95. type: 'pin',
  96. shape: {
  97. // x, y on the cusp
  98. x: 0,
  99. y: 0,
  100. width: 0,
  101. height: 0
  102. },
  103. buildPath: function (path, shape) {
  104. var x = shape.x;
  105. var y = shape.y;
  106. var w = shape.width / 5 * 3; // Height must be larger than width
  107. var h = Math.max(w, shape.height);
  108. var r = w / 2; // Dist on y with tangent point and circle center
  109. var dy = r * r / (h - r);
  110. var cy = y - h + r + dy;
  111. var angle = Math.asin(dy / r); // Dist on x with tangent point and circle center
  112. var dx = Math.cos(angle) * r;
  113. var tanX = Math.sin(angle);
  114. var tanY = Math.cos(angle);
  115. var cpLen = r * 0.6;
  116. var cpLen2 = r * 0.7;
  117. path.moveTo(x - dx, cy + dy);
  118. path.arc(x, cy, r, Math.PI - angle, Math.PI * 2 + angle);
  119. path.bezierCurveTo(x + dx - tanX * cpLen, cy + dy + tanY * cpLen, x, y - cpLen2, x, y);
  120. path.bezierCurveTo(x, y - cpLen2, x - dx + tanX * cpLen, cy + dy + tanY * cpLen, x - dx, cy + dy);
  121. path.closePath();
  122. }
  123. });
  124. /**
  125. * Arrow shape
  126. * @inner
  127. */
  128. var Arrow = graphic.extendShape({
  129. type: 'arrow',
  130. shape: {
  131. x: 0,
  132. y: 0,
  133. width: 0,
  134. height: 0
  135. },
  136. buildPath: function (ctx, shape) {
  137. var height = shape.height;
  138. var width = shape.width;
  139. var x = shape.x;
  140. var y = shape.y;
  141. var dx = width / 3 * 2;
  142. ctx.moveTo(x, y);
  143. ctx.lineTo(x + dx, y + height);
  144. ctx.lineTo(x, y + height / 4 * 3);
  145. ctx.lineTo(x - dx, y + height);
  146. ctx.lineTo(x, y);
  147. ctx.closePath();
  148. }
  149. });
  150. /**
  151. * Map of path contructors
  152. * @type {Object.<string, module:zrender/graphic/Path>}
  153. */
  154. var symbolCtors = {
  155. line: graphic.Line,
  156. rect: graphic.Rect,
  157. roundRect: graphic.Rect,
  158. square: graphic.Rect,
  159. circle: graphic.Circle,
  160. diamond: Diamond,
  161. pin: Pin,
  162. arrow: Arrow,
  163. triangle: Triangle
  164. };
  165. var symbolShapeMakers = {
  166. line: function (x, y, w, h, shape) {
  167. // FIXME
  168. shape.x1 = x;
  169. shape.y1 = y + h / 2;
  170. shape.x2 = x + w;
  171. shape.y2 = y + h / 2;
  172. },
  173. rect: function (x, y, w, h, shape) {
  174. shape.x = x;
  175. shape.y = y;
  176. shape.width = w;
  177. shape.height = h;
  178. },
  179. roundRect: function (x, y, w, h, shape) {
  180. shape.x = x;
  181. shape.y = y;
  182. shape.width = w;
  183. shape.height = h;
  184. shape.r = Math.min(w, h) / 4;
  185. },
  186. square: function (x, y, w, h, shape) {
  187. var size = Math.min(w, h);
  188. shape.x = x;
  189. shape.y = y;
  190. shape.width = size;
  191. shape.height = size;
  192. },
  193. circle: function (x, y, w, h, shape) {
  194. // Put circle in the center of square
  195. shape.cx = x + w / 2;
  196. shape.cy = y + h / 2;
  197. shape.r = Math.min(w, h) / 2;
  198. },
  199. diamond: function (x, y, w, h, shape) {
  200. shape.cx = x + w / 2;
  201. shape.cy = y + h / 2;
  202. shape.width = w;
  203. shape.height = h;
  204. },
  205. pin: function (x, y, w, h, shape) {
  206. shape.x = x + w / 2;
  207. shape.y = y + h / 2;
  208. shape.width = w;
  209. shape.height = h;
  210. },
  211. arrow: function (x, y, w, h, shape) {
  212. shape.x = x + w / 2;
  213. shape.y = y + h / 2;
  214. shape.width = w;
  215. shape.height = h;
  216. },
  217. triangle: function (x, y, w, h, shape) {
  218. shape.cx = x + w / 2;
  219. shape.cy = y + h / 2;
  220. shape.width = w;
  221. shape.height = h;
  222. }
  223. };
  224. var symbolBuildProxies = {};
  225. zrUtil.each(symbolCtors, function (Ctor, name) {
  226. symbolBuildProxies[name] = new Ctor();
  227. });
  228. var SymbolClz = graphic.extendShape({
  229. type: 'symbol',
  230. shape: {
  231. symbolType: '',
  232. x: 0,
  233. y: 0,
  234. width: 0,
  235. height: 0
  236. },
  237. calculateTextPosition: function (out, style, rect) {
  238. var res = calculateTextPosition(out, style, rect);
  239. var shape = this.shape;
  240. if (shape && shape.symbolType === 'pin' && style.textPosition === 'inside') {
  241. res.y = rect.y + rect.height * 0.4;
  242. }
  243. return res;
  244. },
  245. buildPath: function (ctx, shape, inBundle) {
  246. var symbolType = shape.symbolType;
  247. if (symbolType !== 'none') {
  248. var proxySymbol = symbolBuildProxies[symbolType];
  249. if (!proxySymbol) {
  250. // Default rect
  251. symbolType = 'rect';
  252. proxySymbol = symbolBuildProxies[symbolType];
  253. }
  254. symbolShapeMakers[symbolType](shape.x, shape.y, shape.width, shape.height, proxySymbol.shape);
  255. proxySymbol.buildPath(ctx, proxySymbol.shape, inBundle);
  256. }
  257. }
  258. }); // Provide setColor helper method to avoid determine if set the fill or stroke outside
  259. function symbolPathSetColor(color, innerColor) {
  260. if (this.type !== 'image') {
  261. var symbolStyle = this.style;
  262. var symbolShape = this.shape;
  263. if (symbolShape && symbolShape.symbolType === 'line') {
  264. symbolStyle.stroke = color;
  265. } else if (this.__isEmptyBrush) {
  266. symbolStyle.stroke = color;
  267. symbolStyle.fill = innerColor || '#fff';
  268. } else {
  269. // FIXME 判断图形默认是填充还是描边,使用 onlyStroke ?
  270. symbolStyle.fill && (symbolStyle.fill = color);
  271. symbolStyle.stroke && (symbolStyle.stroke = color);
  272. }
  273. this.dirty(false);
  274. }
  275. }
  276. /**
  277. * Create a symbol element with given symbol configuration: shape, x, y, width, height, color
  278. * @param {string} symbolType
  279. * @param {number} x
  280. * @param {number} y
  281. * @param {number} w
  282. * @param {number} h
  283. * @param {string} color
  284. * @param {boolean} [keepAspect=false] whether to keep the ratio of w/h,
  285. * for path and image only.
  286. */
  287. function createSymbol(symbolType, x, y, w, h, color, keepAspect) {
  288. // TODO Support image object, DynamicImage.
  289. var isEmpty = symbolType.indexOf('empty') === 0;
  290. if (isEmpty) {
  291. symbolType = symbolType.substr(5, 1).toLowerCase() + symbolType.substr(6);
  292. }
  293. var symbolPath;
  294. if (symbolType.indexOf('image://') === 0) {
  295. symbolPath = graphic.makeImage(symbolType.slice(8), new BoundingRect(x, y, w, h), keepAspect ? 'center' : 'cover');
  296. } else if (symbolType.indexOf('path://') === 0) {
  297. symbolPath = graphic.makePath(symbolType.slice(7), {}, new BoundingRect(x, y, w, h), keepAspect ? 'center' : 'cover');
  298. } else {
  299. symbolPath = new SymbolClz({
  300. shape: {
  301. symbolType: symbolType,
  302. x: x,
  303. y: y,
  304. width: w,
  305. height: h
  306. }
  307. });
  308. }
  309. symbolPath.__isEmptyBrush = isEmpty;
  310. symbolPath.setColor = symbolPathSetColor;
  311. symbolPath.setColor(color);
  312. return symbolPath;
  313. }
  314. exports.createSymbol = createSymbol;