123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378 |
- var Displayable = require("./Displayable");
- var zrUtil = require("../core/util");
- var PathProxy = require("../core/PathProxy");
- var pathContain = require("../contain/path");
- var Pattern = require("./Pattern");
- var getCanvasPattern = Pattern.prototype.getCanvasPattern;
- var abs = Math.abs;
- var pathProxyForDraw = new PathProxy(true);
- /**
- * @alias module:zrender/graphic/Path
- * @extends module:zrender/graphic/Displayable
- * @constructor
- * @param {Object} opts
- */
- function Path(opts) {
- Displayable.call(this, opts);
- /**
- * @type {module:zrender/core/PathProxy}
- * @readOnly
- */
- this.path = null;
- }
- Path.prototype = {
- constructor: Path,
- type: 'path',
- __dirtyPath: true,
- strokeContainThreshold: 5,
- // This item default to be false. But in map series in echarts,
- // in order to improve performance, it should be set to true,
- // so the shorty segment won't draw.
- segmentIgnoreThreshold: 0,
- /**
- * See `module:zrender/src/graphic/helper/subPixelOptimize`.
- * @type {boolean}
- */
- subPixelOptimize: false,
- brush: function (ctx, prevEl) {
- var style = this.style;
- var path = this.path || pathProxyForDraw;
- var hasStroke = style.hasStroke();
- var hasFill = style.hasFill();
- var fill = style.fill;
- var stroke = style.stroke;
- var hasFillGradient = hasFill && !!fill.colorStops;
- var hasStrokeGradient = hasStroke && !!stroke.colorStops;
- var hasFillPattern = hasFill && !!fill.image;
- var hasStrokePattern = hasStroke && !!stroke.image;
- style.bind(ctx, this, prevEl);
- this.setTransform(ctx);
- if (this.__dirty) {
- var rect; // Update gradient because bounding rect may changed
- if (hasFillGradient) {
- rect = rect || this.getBoundingRect();
- this._fillGradient = style.getGradient(ctx, fill, rect);
- }
- if (hasStrokeGradient) {
- rect = rect || this.getBoundingRect();
- this._strokeGradient = style.getGradient(ctx, stroke, rect);
- }
- } // Use the gradient or pattern
- if (hasFillGradient) {
- // PENDING If may have affect the state
- ctx.fillStyle = this._fillGradient;
- } else if (hasFillPattern) {
- ctx.fillStyle = getCanvasPattern.call(fill, ctx);
- }
- if (hasStrokeGradient) {
- ctx.strokeStyle = this._strokeGradient;
- } else if (hasStrokePattern) {
- ctx.strokeStyle = getCanvasPattern.call(stroke, ctx);
- }
- var lineDash = style.lineDash;
- var lineDashOffset = style.lineDashOffset;
- var ctxLineDash = !!ctx.setLineDash; // Update path sx, sy
- var scale = this.getGlobalScale();
- path.setScale(scale[0], scale[1], this.segmentIgnoreThreshold); // Proxy context
- // Rebuild path in following 2 cases
- // 1. Path is dirty
- // 2. Path needs javascript implemented lineDash stroking.
- // In this case, lineDash information will not be saved in PathProxy
- if (this.__dirtyPath || lineDash && !ctxLineDash && hasStroke) {
- path.beginPath(ctx); // Setting line dash before build path
- if (lineDash && !ctxLineDash) {
- path.setLineDash(lineDash);
- path.setLineDashOffset(lineDashOffset);
- }
- this.buildPath(path, this.shape, false); // Clear path dirty flag
- if (this.path) {
- this.__dirtyPath = false;
- }
- } else {
- // Replay path building
- ctx.beginPath();
- this.path.rebuildPath(ctx);
- }
- if (hasFill) {
- if (style.fillOpacity != null) {
- var originalGlobalAlpha = ctx.globalAlpha;
- ctx.globalAlpha = style.fillOpacity * style.opacity;
- path.fill(ctx);
- ctx.globalAlpha = originalGlobalAlpha;
- } else {
- path.fill(ctx);
- }
- }
- if (lineDash && ctxLineDash) {
- ctx.setLineDash(lineDash);
- ctx.lineDashOffset = lineDashOffset;
- }
- if (hasStroke) {
- if (style.strokeOpacity != null) {
- var originalGlobalAlpha = ctx.globalAlpha;
- ctx.globalAlpha = style.strokeOpacity * style.opacity;
- path.stroke(ctx);
- ctx.globalAlpha = originalGlobalAlpha;
- } else {
- path.stroke(ctx);
- }
- }
- if (lineDash && ctxLineDash) {
- // PENDING
- // Remove lineDash
- ctx.setLineDash([]);
- } // Draw rect text
- if (style.text != null) {
- // Only restore transform when needs draw text.
- this.restoreTransform(ctx);
- this.drawRectText(ctx, this.getBoundingRect());
- }
- },
- // When bundling path, some shape may decide if use moveTo to begin a new subpath or closePath
- // Like in circle
- buildPath: function (ctx, shapeCfg, inBundle) {},
- createPathProxy: function () {
- this.path = new PathProxy();
- },
- getBoundingRect: function () {
- var rect = this._rect;
- var style = this.style;
- var needsUpdateRect = !rect;
- if (needsUpdateRect) {
- var path = this.path;
- if (!path) {
- // Create path on demand.
- path = this.path = new PathProxy();
- }
- if (this.__dirtyPath) {
- path.beginPath();
- this.buildPath(path, this.shape, false);
- }
- rect = path.getBoundingRect();
- }
- this._rect = rect;
- if (style.hasStroke()) {
- // Needs update rect with stroke lineWidth when
- // 1. Element changes scale or lineWidth
- // 2. Shape is changed
- var rectWithStroke = this._rectWithStroke || (this._rectWithStroke = rect.clone());
- if (this.__dirty || needsUpdateRect) {
- rectWithStroke.copy(rect); // FIXME Must after updateTransform
- var w = style.lineWidth; // PENDING, Min line width is needed when line is horizontal or vertical
- var lineScale = style.strokeNoScale ? this.getLineScale() : 1; // Only add extra hover lineWidth when there are no fill
- if (!style.hasFill()) {
- w = Math.max(w, this.strokeContainThreshold || 4);
- } // Consider line width
- // Line scale can't be 0;
- if (lineScale > 1e-10) {
- rectWithStroke.width += w / lineScale;
- rectWithStroke.height += w / lineScale;
- rectWithStroke.x -= w / lineScale / 2;
- rectWithStroke.y -= w / lineScale / 2;
- }
- } // Return rect with stroke
- return rectWithStroke;
- }
- return rect;
- },
- contain: function (x, y) {
- var localPos = this.transformCoordToLocal(x, y);
- var rect = this.getBoundingRect();
- var style = this.style;
- x = localPos[0];
- y = localPos[1];
- if (rect.contain(x, y)) {
- var pathData = this.path.data;
- if (style.hasStroke()) {
- var lineWidth = style.lineWidth;
- var lineScale = style.strokeNoScale ? this.getLineScale() : 1; // Line scale can't be 0;
- if (lineScale > 1e-10) {
- // Only add extra hover lineWidth when there are no fill
- if (!style.hasFill()) {
- lineWidth = Math.max(lineWidth, this.strokeContainThreshold);
- }
- if (pathContain.containStroke(pathData, lineWidth / lineScale, x, y)) {
- return true;
- }
- }
- }
- if (style.hasFill()) {
- return pathContain.contain(pathData, x, y);
- }
- }
- return false;
- },
- /**
- * @param {boolean} dirtyPath
- */
- dirty: function (dirtyPath) {
- if (dirtyPath == null) {
- dirtyPath = true;
- } // Only mark dirty, not mark clean
- if (dirtyPath) {
- this.__dirtyPath = dirtyPath;
- this._rect = null;
- }
- this.__dirty = this.__dirtyText = true;
- this.__zr && this.__zr.refresh(); // Used as a clipping path
- if (this.__clipTarget) {
- this.__clipTarget.dirty();
- }
- },
- /**
- * Alias for animate('shape')
- * @param {boolean} loop
- */
- animateShape: function (loop) {
- return this.animate('shape', loop);
- },
- // Overwrite attrKV
- attrKV: function (key, value) {
- // FIXME
- if (key === 'shape') {
- this.setShape(value);
- this.__dirtyPath = true;
- this._rect = null;
- } else {
- Displayable.prototype.attrKV.call(this, key, value);
- }
- },
- /**
- * @param {Object|string} key
- * @param {*} value
- */
- setShape: function (key, value) {
- var shape = this.shape; // Path from string may not have shape
- if (shape) {
- if (zrUtil.isObject(key)) {
- for (var name in key) {
- if (key.hasOwnProperty(name)) {
- shape[name] = key[name];
- }
- }
- } else {
- shape[key] = value;
- }
- this.dirty(true);
- }
- return this;
- },
- getLineScale: function () {
- var m = this.transform; // Get the line scale.
- // Determinant of `m` means how much the area is enlarged by the
- // transformation. So its square root can be used as a scale factor
- // for width.
- return m && abs(m[0] - 1) > 1e-10 && abs(m[3] - 1) > 1e-10 ? Math.sqrt(abs(m[0] * m[3] - m[2] * m[1])) : 1;
- }
- };
- /**
- * 扩展一个 Path element, 比如星形,圆等。
- * Extend a path element
- * @param {Object} props
- * @param {string} props.type Path type
- * @param {Function} props.init Initialize
- * @param {Function} props.buildPath Overwrite buildPath method
- * @param {Object} [props.style] Extended default style config
- * @param {Object} [props.shape] Extended default shape config
- */
- Path.extend = function (defaults) {
- var Sub = function (opts) {
- Path.call(this, opts);
- if (defaults.style) {
- // Extend default style
- this.style.extendFrom(defaults.style, false);
- } // Extend default shape
- var defaultShape = defaults.shape;
- if (defaultShape) {
- this.shape = this.shape || {};
- var thisShape = this.shape;
- for (var name in defaultShape) {
- if (!thisShape.hasOwnProperty(name) && defaultShape.hasOwnProperty(name)) {
- thisShape[name] = defaultShape[name];
- }
- }
- }
- defaults.init && defaults.init.call(this, opts);
- };
- zrUtil.inherits(Sub, Path); // FIXME 不能 extend position, rotation 等引用对象
- for (var name in defaults) {
- // Extending prototype values and methods
- if (name !== 'style' && name !== 'shape') {
- Sub.prototype[name] = defaults[name];
- }
- }
- return Sub;
- };
- zrUtil.inherits(Path, Displayable);
- var _default = Path;
- module.exports = _default;
|