| import fixShadow from './helper/fixShadow'; |
| import { ContextCachedBy } from './constant'; |
| var STYLE_COMMON_PROPS = [['shadowBlur', 0], ['shadowOffsetX', 0], ['shadowOffsetY', 0], ['shadowColor', '#000'], ['lineCap', 'butt'], ['lineJoin', 'miter'], ['miterLimit', 10]]; // var SHADOW_PROPS = STYLE_COMMON_PROPS.slice(0, 4); |
| // var LINE_PROPS = STYLE_COMMON_PROPS.slice(4); |
| |
| var Style = function (opts) { |
| this.extendFrom(opts, false); |
| }; |
| |
| function createLinearGradient(ctx, obj, rect) { |
| var x = obj.x == null ? 0 : obj.x; |
| var x2 = obj.x2 == null ? 1 : obj.x2; |
| var y = obj.y == null ? 0 : obj.y; |
| var y2 = obj.y2 == null ? 0 : obj.y2; |
| |
| if (!obj.global) { |
| x = x * rect.width + rect.x; |
| x2 = x2 * rect.width + rect.x; |
| y = y * rect.height + rect.y; |
| y2 = y2 * rect.height + rect.y; |
| } // Fix NaN when rect is Infinity |
| |
| |
| x = isNaN(x) ? 0 : x; |
| x2 = isNaN(x2) ? 1 : x2; |
| y = isNaN(y) ? 0 : y; |
| y2 = isNaN(y2) ? 0 : y2; |
| var canvasGradient = ctx.createLinearGradient(x, y, x2, y2); |
| return canvasGradient; |
| } |
| |
| function createRadialGradient(ctx, obj, rect) { |
| var width = rect.width; |
| var height = rect.height; |
| var min = Math.min(width, height); |
| var x = obj.x == null ? 0.5 : obj.x; |
| var y = obj.y == null ? 0.5 : obj.y; |
| var r = obj.r == null ? 0.5 : obj.r; |
| |
| if (!obj.global) { |
| x = x * width + rect.x; |
| y = y * height + rect.y; |
| r = r * min; |
| } |
| |
| var canvasGradient = ctx.createRadialGradient(x, y, 0, x, y, r); |
| return canvasGradient; |
| } |
| |
| Style.prototype = { |
| constructor: Style, |
| |
| /** |
| * @type {string} |
| */ |
| fill: '#000', |
| |
| /** |
| * @type {string} |
| */ |
| stroke: null, |
| |
| /** |
| * @type {number} |
| */ |
| opacity: 1, |
| |
| /** |
| * @type {number} |
| */ |
| fillOpacity: null, |
| |
| /** |
| * @type {number} |
| */ |
| strokeOpacity: null, |
| |
| /** |
| * `true` is not supported. |
| * `false`/`null`/`undefined` are the same. |
| * `false` is used to remove lineDash in some |
| * case that `null`/`undefined` can not be set. |
| * (e.g., emphasis.lineStyle in echarts) |
| * @type {Array.<number>|boolean} |
| */ |
| lineDash: null, |
| |
| /** |
| * @type {number} |
| */ |
| lineDashOffset: 0, |
| |
| /** |
| * @type {number} |
| */ |
| shadowBlur: 0, |
| |
| /** |
| * @type {number} |
| */ |
| shadowOffsetX: 0, |
| |
| /** |
| * @type {number} |
| */ |
| shadowOffsetY: 0, |
| |
| /** |
| * @type {number} |
| */ |
| lineWidth: 1, |
| |
| /** |
| * If stroke ignore scale |
| * @type {Boolean} |
| */ |
| strokeNoScale: false, |
| // Bounding rect text configuration |
| // Not affected by element transform |
| |
| /** |
| * @type {string} |
| */ |
| text: null, |
| |
| /** |
| * If `fontSize` or `fontFamily` exists, `font` will be reset by |
| * `fontSize`, `fontStyle`, `fontWeight`, `fontFamily`. |
| * So do not visit it directly in upper application (like echarts), |
| * but use `contain/text#makeFont` instead. |
| * @type {string} |
| */ |
| font: null, |
| |
| /** |
| * The same as font. Use font please. |
| * @deprecated |
| * @type {string} |
| */ |
| textFont: null, |
| |
| /** |
| * It helps merging respectively, rather than parsing an entire font string. |
| * @type {string} |
| */ |
| fontStyle: null, |
| |
| /** |
| * It helps merging respectively, rather than parsing an entire font string. |
| * @type {string} |
| */ |
| fontWeight: null, |
| |
| /** |
| * It helps merging respectively, rather than parsing an entire font string. |
| * Should be 12 but not '12px'. |
| * @type {number} |
| */ |
| fontSize: null, |
| |
| /** |
| * It helps merging respectively, rather than parsing an entire font string. |
| * @type {string} |
| */ |
| fontFamily: null, |
| |
| /** |
| * Reserved for special functinality, like 'hr'. |
| * @type {string} |
| */ |
| textTag: null, |
| |
| /** |
| * @type {string} |
| */ |
| textFill: '#000', |
| |
| /** |
| * @type {string} |
| */ |
| textStroke: null, |
| |
| /** |
| * @type {number} |
| */ |
| textWidth: null, |
| |
| /** |
| * Only for textBackground. |
| * @type {number} |
| */ |
| textHeight: null, |
| |
| /** |
| * textStroke may be set as some color as a default |
| * value in upper applicaion, where the default value |
| * of textStrokeWidth should be 0 to make sure that |
| * user can choose to do not use text stroke. |
| * @type {number} |
| */ |
| textStrokeWidth: 0, |
| |
| /** |
| * @type {number} |
| */ |
| textLineHeight: null, |
| |
| /** |
| * 'inside', 'left', 'right', 'top', 'bottom' |
| * [x, y] |
| * Based on x, y of rect. |
| * @type {string|Array.<number>} |
| * @default 'inside' |
| */ |
| textPosition: 'inside', |
| |
| /** |
| * If not specified, use the boundingRect of a `displayable`. |
| * @type {Object} |
| */ |
| textRect: null, |
| |
| /** |
| * [x, y] |
| * @type {Array.<number>} |
| */ |
| textOffset: null, |
| |
| /** |
| * @type {string} |
| */ |
| textAlign: null, |
| |
| /** |
| * @type {string} |
| */ |
| textVerticalAlign: null, |
| |
| /** |
| * @type {number} |
| */ |
| textDistance: 5, |
| |
| /** |
| * @type {string} |
| */ |
| textShadowColor: 'transparent', |
| |
| /** |
| * @type {number} |
| */ |
| textShadowBlur: 0, |
| |
| /** |
| * @type {number} |
| */ |
| textShadowOffsetX: 0, |
| |
| /** |
| * @type {number} |
| */ |
| textShadowOffsetY: 0, |
| |
| /** |
| * @type {string} |
| */ |
| textBoxShadowColor: 'transparent', |
| |
| /** |
| * @type {number} |
| */ |
| textBoxShadowBlur: 0, |
| |
| /** |
| * @type {number} |
| */ |
| textBoxShadowOffsetX: 0, |
| |
| /** |
| * @type {number} |
| */ |
| textBoxShadowOffsetY: 0, |
| |
| /** |
| * Whether transform text. |
| * Only useful in Path and Image element |
| * @type {boolean} |
| */ |
| transformText: false, |
| |
| /** |
| * Text rotate around position of Path or Image |
| * Only useful in Path and Image element and transformText is false. |
| */ |
| textRotation: 0, |
| |
| /** |
| * Text origin of text rotation, like [10, 40]. |
| * Based on x, y of rect. |
| * Useful in label rotation of circular symbol. |
| * By default, this origin is textPosition. |
| * Can be 'center'. |
| * @type {string|Array.<number>} |
| */ |
| textOrigin: null, |
| |
| /** |
| * @type {string} |
| */ |
| textBackgroundColor: null, |
| |
| /** |
| * @type {string} |
| */ |
| textBorderColor: null, |
| |
| /** |
| * @type {number} |
| */ |
| textBorderWidth: 0, |
| |
| /** |
| * @type {number} |
| */ |
| textBorderRadius: 0, |
| |
| /** |
| * Can be `2` or `[2, 4]` or `[2, 3, 4, 5]` |
| * @type {number|Array.<number>} |
| */ |
| textPadding: null, |
| |
| /** |
| * Text styles for rich text. |
| * @type {Object} |
| */ |
| rich: null, |
| |
| /** |
| * {outerWidth, outerHeight, ellipsis, placeholder} |
| * @type {Object} |
| */ |
| truncate: null, |
| |
| /** |
| * https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation |
| * @type {string} |
| */ |
| blend: null, |
| |
| /** |
| * @param {CanvasRenderingContext2D} ctx |
| */ |
| bind: function (ctx, el, prevEl) { |
| var style = this; |
| var prevStyle = prevEl && prevEl.style; // If no prevStyle, it means first draw. |
| // Only apply cache if the last time cachced by this function. |
| |
| var notCheckCache = !prevStyle || ctx.__attrCachedBy !== ContextCachedBy.STYLE_BIND; |
| ctx.__attrCachedBy = ContextCachedBy.STYLE_BIND; |
| |
| for (var i = 0; i < STYLE_COMMON_PROPS.length; i++) { |
| var prop = STYLE_COMMON_PROPS[i]; |
| var styleName = prop[0]; |
| |
| if (notCheckCache || style[styleName] !== prevStyle[styleName]) { |
| // FIXME Invalid property value will cause style leak from previous element. |
| ctx[styleName] = fixShadow(ctx, styleName, style[styleName] || prop[1]); |
| } |
| } |
| |
| if (notCheckCache || style.fill !== prevStyle.fill) { |
| ctx.fillStyle = style.fill; |
| } |
| |
| if (notCheckCache || style.stroke !== prevStyle.stroke) { |
| ctx.strokeStyle = style.stroke; |
| } |
| |
| if (notCheckCache || style.opacity !== prevStyle.opacity) { |
| ctx.globalAlpha = style.opacity == null ? 1 : style.opacity; |
| } |
| |
| if (notCheckCache || style.blend !== prevStyle.blend) { |
| ctx.globalCompositeOperation = style.blend || 'source-over'; |
| } |
| |
| if (this.hasStroke()) { |
| var lineWidth = style.lineWidth; |
| ctx.lineWidth = lineWidth / (this.strokeNoScale && el && el.getLineScale ? el.getLineScale() : 1); |
| } |
| }, |
| hasFill: function () { |
| var fill = this.fill; |
| return fill != null && fill !== 'none'; |
| }, |
| hasStroke: function () { |
| var stroke = this.stroke; |
| return stroke != null && stroke !== 'none' && this.lineWidth > 0; |
| }, |
| |
| /** |
| * Extend from other style |
| * @param {zrender/graphic/Style} otherStyle |
| * @param {boolean} overwrite true: overwrirte any way. |
| * false: overwrite only when !target.hasOwnProperty |
| * others: overwrite when property is not null/undefined. |
| */ |
| extendFrom: function (otherStyle, overwrite) { |
| if (otherStyle) { |
| for (var name in otherStyle) { |
| if (otherStyle.hasOwnProperty(name) && (overwrite === true || (overwrite === false ? !this.hasOwnProperty(name) : otherStyle[name] != null))) { |
| this[name] = otherStyle[name]; |
| } |
| } |
| } |
| }, |
| |
| /** |
| * Batch setting style with a given object |
| * @param {Object|string} obj |
| * @param {*} [obj] |
| */ |
| set: function (obj, value) { |
| if (typeof obj === 'string') { |
| this[obj] = value; |
| } else { |
| this.extendFrom(obj, true); |
| } |
| }, |
| |
| /** |
| * Clone |
| * @return {zrender/graphic/Style} [description] |
| */ |
| clone: function () { |
| var newStyle = new this.constructor(); |
| newStyle.extendFrom(this, true); |
| return newStyle; |
| }, |
| getGradient: function (ctx, obj, rect) { |
| var method = obj.type === 'radial' ? createRadialGradient : createLinearGradient; |
| var canvasGradient = method(ctx, obj, rect); |
| var colorStops = obj.colorStops; |
| |
| for (var i = 0; i < colorStops.length; i++) { |
| canvasGradient.addColorStop(colorStops[i].offset, colorStops[i].color); |
| } |
| |
| return canvasGradient; |
| } |
| }; |
| var styleProto = Style.prototype; |
| |
| for (var i = 0; i < STYLE_COMMON_PROPS.length; i++) { |
| var prop = STYLE_COMMON_PROPS[i]; |
| |
| if (!(prop[0] in styleProto)) { |
| styleProto[prop[0]] = prop[1]; |
| } |
| } // Provide for others |
| |
| |
| Style.getGradient = styleProto.getGradient; |
| export default Style; |