Path.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. import { __extends } from "tslib";
  2. import Displayable, { DEFAULT_COMMON_STYLE, DEFAULT_COMMON_ANIMATION_PROPS } from './Displayable.js';
  3. import PathProxy from '../core/PathProxy.js';
  4. import * as pathContain from '../contain/path.js';
  5. import { defaults, keys, extend, clone, isString, createObject } from '../core/util.js';
  6. import { lum } from '../tool/color.js';
  7. import { DARK_LABEL_COLOR, LIGHT_LABEL_COLOR, DARK_MODE_THRESHOLD, LIGHTER_LABEL_COLOR } from '../config.js';
  8. import { REDRAW_BIT, SHAPE_CHANGED_BIT, STYLE_CHANGED_BIT } from './constants.js';
  9. import { TRANSFORMABLE_PROPS } from '../core/Transformable.js';
  10. export var DEFAULT_PATH_STYLE = defaults({
  11. fill: '#000',
  12. stroke: null,
  13. strokePercent: 1,
  14. fillOpacity: 1,
  15. strokeOpacity: 1,
  16. lineDashOffset: 0,
  17. lineWidth: 1,
  18. lineCap: 'butt',
  19. miterLimit: 10,
  20. strokeNoScale: false,
  21. strokeFirst: false
  22. }, DEFAULT_COMMON_STYLE);
  23. export var DEFAULT_PATH_ANIMATION_PROPS = {
  24. style: defaults({
  25. fill: true,
  26. stroke: true,
  27. strokePercent: true,
  28. fillOpacity: true,
  29. strokeOpacity: true,
  30. lineDashOffset: true,
  31. lineWidth: true,
  32. miterLimit: true
  33. }, DEFAULT_COMMON_ANIMATION_PROPS.style)
  34. };
  35. var pathCopyParams = TRANSFORMABLE_PROPS.concat(['invisible',
  36. 'culling', 'z', 'z2', 'zlevel', 'parent'
  37. ]);
  38. var Path = (function (_super) {
  39. __extends(Path, _super);
  40. function Path(opts) {
  41. return _super.call(this, opts) || this;
  42. }
  43. Path.prototype.update = function () {
  44. var _this = this;
  45. _super.prototype.update.call(this);
  46. var style = this.style;
  47. if (style.decal) {
  48. var decalEl = this._decalEl = this._decalEl || new Path();
  49. if (decalEl.buildPath === Path.prototype.buildPath) {
  50. decalEl.buildPath = function (ctx) {
  51. _this.buildPath(ctx, _this.shape);
  52. };
  53. }
  54. decalEl.silent = true;
  55. var decalElStyle = decalEl.style;
  56. for (var key in style) {
  57. if (decalElStyle[key] !== style[key]) {
  58. decalElStyle[key] = style[key];
  59. }
  60. }
  61. decalElStyle.fill = style.fill ? style.decal : null;
  62. decalElStyle.decal = null;
  63. decalElStyle.shadowColor = null;
  64. style.strokeFirst && (decalElStyle.stroke = null);
  65. for (var i = 0; i < pathCopyParams.length; ++i) {
  66. decalEl[pathCopyParams[i]] = this[pathCopyParams[i]];
  67. }
  68. decalEl.__dirty |= REDRAW_BIT;
  69. }
  70. else if (this._decalEl) {
  71. this._decalEl = null;
  72. }
  73. };
  74. Path.prototype.getDecalElement = function () {
  75. return this._decalEl;
  76. };
  77. Path.prototype._init = function (props) {
  78. var keysArr = keys(props);
  79. this.shape = this.getDefaultShape();
  80. var defaultStyle = this.getDefaultStyle();
  81. if (defaultStyle) {
  82. this.useStyle(defaultStyle);
  83. }
  84. for (var i = 0; i < keysArr.length; i++) {
  85. var key = keysArr[i];
  86. var value = props[key];
  87. if (key === 'style') {
  88. if (!this.style) {
  89. this.useStyle(value);
  90. }
  91. else {
  92. extend(this.style, value);
  93. }
  94. }
  95. else if (key === 'shape') {
  96. extend(this.shape, value);
  97. }
  98. else {
  99. _super.prototype.attrKV.call(this, key, value);
  100. }
  101. }
  102. if (!this.style) {
  103. this.useStyle({});
  104. }
  105. };
  106. Path.prototype.getDefaultStyle = function () {
  107. return null;
  108. };
  109. Path.prototype.getDefaultShape = function () {
  110. return {};
  111. };
  112. Path.prototype.canBeInsideText = function () {
  113. return this.hasFill();
  114. };
  115. Path.prototype.getInsideTextFill = function () {
  116. var pathFill = this.style.fill;
  117. if (pathFill !== 'none') {
  118. if (isString(pathFill)) {
  119. var fillLum = lum(pathFill, 0);
  120. if (fillLum > 0.5) {
  121. return DARK_LABEL_COLOR;
  122. }
  123. else if (fillLum > 0.2) {
  124. return LIGHTER_LABEL_COLOR;
  125. }
  126. return LIGHT_LABEL_COLOR;
  127. }
  128. else if (pathFill) {
  129. return LIGHT_LABEL_COLOR;
  130. }
  131. }
  132. return DARK_LABEL_COLOR;
  133. };
  134. Path.prototype.getInsideTextStroke = function (textFill) {
  135. var pathFill = this.style.fill;
  136. if (isString(pathFill)) {
  137. var zr = this.__zr;
  138. var isDarkMode = !!(zr && zr.isDarkMode());
  139. var isDarkLabel = lum(textFill, 0) < DARK_MODE_THRESHOLD;
  140. if (isDarkMode === isDarkLabel) {
  141. return pathFill;
  142. }
  143. }
  144. };
  145. Path.prototype.buildPath = function (ctx, shapeCfg, inBatch) { };
  146. Path.prototype.pathUpdated = function () {
  147. this.__dirty &= ~SHAPE_CHANGED_BIT;
  148. };
  149. Path.prototype.getUpdatedPathProxy = function (inBatch) {
  150. !this.path && this.createPathProxy();
  151. this.path.beginPath();
  152. this.buildPath(this.path, this.shape, inBatch);
  153. return this.path;
  154. };
  155. Path.prototype.createPathProxy = function () {
  156. this.path = new PathProxy(false);
  157. };
  158. Path.prototype.hasStroke = function () {
  159. var style = this.style;
  160. var stroke = style.stroke;
  161. return !(stroke == null || stroke === 'none' || !(style.lineWidth > 0));
  162. };
  163. Path.prototype.hasFill = function () {
  164. var style = this.style;
  165. var fill = style.fill;
  166. return fill != null && fill !== 'none';
  167. };
  168. Path.prototype.getBoundingRect = function () {
  169. var rect = this._rect;
  170. var style = this.style;
  171. var needsUpdateRect = !rect;
  172. if (needsUpdateRect) {
  173. var firstInvoke = false;
  174. if (!this.path) {
  175. firstInvoke = true;
  176. this.createPathProxy();
  177. }
  178. var path = this.path;
  179. if (firstInvoke || (this.__dirty & SHAPE_CHANGED_BIT)) {
  180. path.beginPath();
  181. this.buildPath(path, this.shape, false);
  182. this.pathUpdated();
  183. }
  184. rect = path.getBoundingRect();
  185. }
  186. this._rect = rect;
  187. if (this.hasStroke() && this.path && this.path.len() > 0) {
  188. var rectStroke = this._rectStroke || (this._rectStroke = rect.clone());
  189. if (this.__dirty || needsUpdateRect) {
  190. rectStroke.copy(rect);
  191. var lineScale = style.strokeNoScale ? this.getLineScale() : 1;
  192. var w = style.lineWidth;
  193. if (!this.hasFill()) {
  194. var strokeContainThreshold = this.strokeContainThreshold;
  195. w = Math.max(w, strokeContainThreshold == null ? 4 : strokeContainThreshold);
  196. }
  197. if (lineScale > 1e-10) {
  198. rectStroke.width += w / lineScale;
  199. rectStroke.height += w / lineScale;
  200. rectStroke.x -= w / lineScale / 2;
  201. rectStroke.y -= w / lineScale / 2;
  202. }
  203. }
  204. return rectStroke;
  205. }
  206. return rect;
  207. };
  208. Path.prototype.contain = function (x, y) {
  209. var localPos = this.transformCoordToLocal(x, y);
  210. var rect = this.getBoundingRect();
  211. var style = this.style;
  212. x = localPos[0];
  213. y = localPos[1];
  214. if (rect.contain(x, y)) {
  215. var pathProxy = this.path;
  216. if (this.hasStroke()) {
  217. var lineWidth = style.lineWidth;
  218. var lineScale = style.strokeNoScale ? this.getLineScale() : 1;
  219. if (lineScale > 1e-10) {
  220. if (!this.hasFill()) {
  221. lineWidth = Math.max(lineWidth, this.strokeContainThreshold);
  222. }
  223. if (pathContain.containStroke(pathProxy, lineWidth / lineScale, x, y)) {
  224. return true;
  225. }
  226. }
  227. }
  228. if (this.hasFill()) {
  229. return pathContain.contain(pathProxy, x, y);
  230. }
  231. }
  232. return false;
  233. };
  234. Path.prototype.dirtyShape = function () {
  235. this.__dirty |= SHAPE_CHANGED_BIT;
  236. if (this._rect) {
  237. this._rect = null;
  238. }
  239. if (this._decalEl) {
  240. this._decalEl.dirtyShape();
  241. }
  242. this.markRedraw();
  243. };
  244. Path.prototype.dirty = function () {
  245. this.dirtyStyle();
  246. this.dirtyShape();
  247. };
  248. Path.prototype.animateShape = function (loop) {
  249. return this.animate('shape', loop);
  250. };
  251. Path.prototype.updateDuringAnimation = function (targetKey) {
  252. if (targetKey === 'style') {
  253. this.dirtyStyle();
  254. }
  255. else if (targetKey === 'shape') {
  256. this.dirtyShape();
  257. }
  258. else {
  259. this.markRedraw();
  260. }
  261. };
  262. Path.prototype.attrKV = function (key, value) {
  263. if (key === 'shape') {
  264. this.setShape(value);
  265. }
  266. else {
  267. _super.prototype.attrKV.call(this, key, value);
  268. }
  269. };
  270. Path.prototype.setShape = function (keyOrObj, value) {
  271. var shape = this.shape;
  272. if (!shape) {
  273. shape = this.shape = {};
  274. }
  275. if (typeof keyOrObj === 'string') {
  276. shape[keyOrObj] = value;
  277. }
  278. else {
  279. extend(shape, keyOrObj);
  280. }
  281. this.dirtyShape();
  282. return this;
  283. };
  284. Path.prototype.shapeChanged = function () {
  285. return !!(this.__dirty & SHAPE_CHANGED_BIT);
  286. };
  287. Path.prototype.createStyle = function (obj) {
  288. return createObject(DEFAULT_PATH_STYLE, obj);
  289. };
  290. Path.prototype._innerSaveToNormal = function (toState) {
  291. _super.prototype._innerSaveToNormal.call(this, toState);
  292. var normalState = this._normalState;
  293. if (toState.shape && !normalState.shape) {
  294. normalState.shape = extend({}, this.shape);
  295. }
  296. };
  297. Path.prototype._applyStateObj = function (stateName, state, normalState, keepCurrentStates, transition, animationCfg) {
  298. _super.prototype._applyStateObj.call(this, stateName, state, normalState, keepCurrentStates, transition, animationCfg);
  299. var needsRestoreToNormal = !(state && keepCurrentStates);
  300. var targetShape;
  301. if (state && state.shape) {
  302. if (transition) {
  303. if (keepCurrentStates) {
  304. targetShape = state.shape;
  305. }
  306. else {
  307. targetShape = extend({}, normalState.shape);
  308. extend(targetShape, state.shape);
  309. }
  310. }
  311. else {
  312. targetShape = extend({}, keepCurrentStates ? this.shape : normalState.shape);
  313. extend(targetShape, state.shape);
  314. }
  315. }
  316. else if (needsRestoreToNormal) {
  317. targetShape = normalState.shape;
  318. }
  319. if (targetShape) {
  320. if (transition) {
  321. this.shape = extend({}, this.shape);
  322. var targetShapePrimaryProps = {};
  323. var shapeKeys = keys(targetShape);
  324. for (var i = 0; i < shapeKeys.length; i++) {
  325. var key = shapeKeys[i];
  326. if (typeof targetShape[key] === 'object') {
  327. this.shape[key] = targetShape[key];
  328. }
  329. else {
  330. targetShapePrimaryProps[key] = targetShape[key];
  331. }
  332. }
  333. this._transitionState(stateName, {
  334. shape: targetShapePrimaryProps
  335. }, animationCfg);
  336. }
  337. else {
  338. this.shape = targetShape;
  339. this.dirtyShape();
  340. }
  341. }
  342. };
  343. Path.prototype._mergeStates = function (states) {
  344. var mergedState = _super.prototype._mergeStates.call(this, states);
  345. var mergedShape;
  346. for (var i = 0; i < states.length; i++) {
  347. var state = states[i];
  348. if (state.shape) {
  349. mergedShape = mergedShape || {};
  350. this._mergeStyle(mergedShape, state.shape);
  351. }
  352. }
  353. if (mergedShape) {
  354. mergedState.shape = mergedShape;
  355. }
  356. return mergedState;
  357. };
  358. Path.prototype.getAnimationStyleProps = function () {
  359. return DEFAULT_PATH_ANIMATION_PROPS;
  360. };
  361. Path.prototype.isZeroArea = function () {
  362. return false;
  363. };
  364. Path.extend = function (defaultProps) {
  365. var Sub = (function (_super) {
  366. __extends(Sub, _super);
  367. function Sub(opts) {
  368. var _this = _super.call(this, opts) || this;
  369. defaultProps.init && defaultProps.init.call(_this, opts);
  370. return _this;
  371. }
  372. Sub.prototype.getDefaultStyle = function () {
  373. return clone(defaultProps.style);
  374. };
  375. Sub.prototype.getDefaultShape = function () {
  376. return clone(defaultProps.shape);
  377. };
  378. return Sub;
  379. }(Path));
  380. for (var key in defaultProps) {
  381. if (typeof defaultProps[key] === 'function') {
  382. Sub.prototype[key] = defaultProps[key];
  383. }
  384. }
  385. return Sub;
  386. };
  387. Path.initDefaultProps = (function () {
  388. var pathProto = Path.prototype;
  389. pathProto.type = 'path';
  390. pathProto.strokeContainThreshold = 5;
  391. pathProto.segmentIgnoreThreshold = 0;
  392. pathProto.subPixelOptimize = false;
  393. pathProto.autoBatch = false;
  394. pathProto.__dirty = REDRAW_BIT | STYLE_CHANGED_BIT | SHAPE_CHANGED_BIT;
  395. })();
  396. return Path;
  397. }(Displayable));
  398. export default Path;