Path.js 14 KB

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