Comparing version 4.0.4 to 4.0.5
@@ -20,3 +20,4 @@ /** | ||
svgSupported: false, | ||
touchEventsSupported: true | ||
touchEventsSupported: true, | ||
domSupported: false | ||
}; | ||
@@ -30,3 +31,4 @@ } else if (typeof document === 'undefined' && typeof self !== 'undefined') { | ||
worker: true, | ||
canvasSupported: true | ||
canvasSupported: true, | ||
domSupported: false | ||
}; | ||
@@ -42,3 +44,4 @@ } else if (typeof navigator === 'undefined') { | ||
canvasSupported: true, | ||
svgSupported: true | ||
svgSupported: true, | ||
domSupported: false | ||
}; | ||
@@ -143,4 +146,5 @@ } else { | ||
// standard. So we exclude that. (IE 10 is hardly used on touch device) | ||
&& (browser.edge || browser.ie && browser.version >= 11) // passiveSupported: detectPassiveSupport() | ||
&& (browser.edge || browser.ie && browser.version >= 11), | ||
// passiveSupported: detectPassiveSupport() | ||
domSupported: typeof document !== 'undefined' | ||
}; | ||
@@ -147,0 +151,0 @@ } // See https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection |
@@ -216,3 +216,3 @@ var curve = require("./curve"); | ||
this._xi = mathCos(endAngle) * r + cx; | ||
this._yi = mathSin(endAngle) * r + cx; | ||
this._yi = mathSin(endAngle) * r + cy; | ||
return this; | ||
@@ -219,0 +219,0 @@ }, |
@@ -634,3 +634,6 @@ /** | ||
function HashMap(obj) { | ||
var isArr = isArray(obj); | ||
var isArr = isArray(obj); // Key should not be set on this, otherwise | ||
// methods get/set/... may be overrided. | ||
this.data = {}; | ||
var thisMap = this; | ||
@@ -642,5 +645,4 @@ obj instanceof HashMap ? obj.each(visit) : obj && each(obj, visit); | ||
} | ||
} // Add prefix to avoid conflict with Object.prototype. | ||
} | ||
HashMap.prototype = { | ||
@@ -652,3 +654,3 @@ constructor: HashMap, | ||
get: function (key) { | ||
return this.hasOwnProperty(key) ? this[key] : null; | ||
return this.data.hasOwnProperty(key) ? this.data[key] : null; | ||
}, | ||
@@ -658,3 +660,3 @@ set: function (key, value) { | ||
// used in this case: `var someVal = map.set('a', genVal());` | ||
return this[key] = value; | ||
return this.data[key] = value; | ||
}, | ||
@@ -666,4 +668,4 @@ // Although util.each can be performed on this hashMap directly, user | ||
for (var key in this) { | ||
this.hasOwnProperty(key) && cb(this[key], key); | ||
for (var key in this.data) { | ||
this.data.hasOwnProperty(key) && cb(this.data[key], key); | ||
} | ||
@@ -673,3 +675,3 @@ }, | ||
removeKey: function (key) { | ||
delete this[key]; | ||
delete this.data[key]; | ||
} | ||
@@ -676,0 +678,0 @@ }; |
@@ -21,2 +21,7 @@ var zrUtil = require("./core/util"); | ||
var _parseSVG = require("./tool/parseSVG"); | ||
var parseSVG = _parseSVG.parseSVG; | ||
exports.parseSVG = _parseSVG.parseSVG; | ||
var _Group = require("./container/Group"); | ||
@@ -23,0 +28,0 @@ |
@@ -140,4 +140,8 @@ var zrUtil = require("../core/util"); | ||
incremental: false, | ||
// inplace is used with incremental | ||
inplace: false, | ||
/** | ||
* Scale ratio for global scale. | ||
* @type {boolean} | ||
*/ | ||
globalScaleRatio: 1, | ||
beforeBrush: function (ctx) {}, | ||
@@ -197,3 +201,3 @@ afterBrush: function (ctx) {}, | ||
dirty: function () { | ||
this.__dirty = true; | ||
this.__dirty = this.__dirtyText = true; | ||
this._rect = null; | ||
@@ -200,0 +204,0 @@ this.__zr && this.__zr.refresh(); |
@@ -53,3 +53,3 @@ var LRU = require("../../core/LRU"); | ||
!image && (image = new Image()); | ||
image.onload = imageOnLoad; | ||
image.onload = image.onerror = imageOnLoad; | ||
globalImageCache.put(newImageOrSrc, image.__cachedImgObj = { | ||
@@ -71,3 +71,3 @@ image: image, | ||
var cachedImgObj = this.__cachedImgObj; | ||
this.onload = this.__cachedImgObj = null; | ||
this.onload = this.onerror = this.__cachedImgObj = null; | ||
@@ -74,0 +74,0 @@ for (var i = 0; i < cachedImgObj.pending.length; i++) { |
@@ -9,2 +9,3 @@ var _util = require("../../core/util"); | ||
var isObject = _util.isObject; | ||
var isFunction = _util.isFunction; | ||
@@ -29,3 +30,6 @@ var textContain = require("../../contain/text"); | ||
middle: 1 | ||
}; | ||
}; // Different from `STYLE_COMMON_PROPS` of `graphic/Style`, | ||
// the default value of shadowColor is `'transparent'`. | ||
var SHADOW_STYLE_COMMON_PROPS = [['textShadowBlur', 'shadowBlur', 0], ['textShadowOffsetX', 'shadowOffsetX', 0], ['textShadowOffsetY', 'shadowOffsetY', 0], ['textShadowColor', 'shadowColor', 'transparent']]; | ||
/** | ||
@@ -65,16 +69,39 @@ * @param {module:zrender/graphic/Style} style | ||
* If set false, rect text is not used. | ||
* @param {Element} [prevEl] For ctx prop cache. | ||
*/ | ||
function renderText(hostEl, ctx, text, style, rect) { | ||
style.rich ? renderRichText(hostEl, ctx, text, style, rect) : renderPlainText(hostEl, ctx, text, style, rect); | ||
} | ||
function renderText(hostEl, ctx, text, style, rect, prevEl) { | ||
style.rich ? renderRichText(hostEl, ctx, text, style, rect) : renderPlainText(hostEl, ctx, text, style, rect, prevEl); | ||
} // Avoid setting to ctx according to prevEl if possible for | ||
// performance in scenarios of large amount text. | ||
function renderPlainText(hostEl, ctx, text, style, rect) { | ||
var font = setCtx(ctx, 'font', style.font || textContain.DEFAULT_FONT); | ||
function renderPlainText(hostEl, ctx, text, style, rect, prevEl) { | ||
'use strict'; | ||
var prevStyle = prevEl && prevEl.style; // Some cache only available on textEl. | ||
var isPrevTextEl = prevStyle && prevEl.type === 'text'; | ||
var styleFont = style.font || textContain.DEFAULT_FONT; | ||
if (!isPrevTextEl || styleFont !== (prevStyle.font || textContain.DEFAULT_FONT)) { | ||
ctx.font = styleFont; | ||
} // Use the final font from context-2d, because the final | ||
// font might not be the style.font when it is illegal. | ||
// But get `ctx.font` might be time consuming. | ||
var computedFont = hostEl.__computedFont; | ||
if (hostEl.__styleFont !== styleFont) { | ||
hostEl.__styleFont = styleFont; | ||
computedFont = hostEl.__computedFont = ctx.font; | ||
} | ||
var textPadding = style.textPadding; | ||
var contentBlock = hostEl.__textCotentBlock; | ||
if (!contentBlock || hostEl.__dirty) { | ||
contentBlock = hostEl.__textCotentBlock = textContain.parsePlainText(text, font, textPadding, style.truncate); | ||
if (!contentBlock || hostEl.__dirtyText) { | ||
contentBlock = hostEl.__textCotentBlock = textContain.parsePlainText(text, computedFont, textPadding, style.truncate); | ||
} | ||
@@ -88,3 +115,3 @@ | ||
var baseY = boxPos.baseY; | ||
var textAlign = boxPos.textAlign; | ||
var textAlign = boxPos.textAlign || 'left'; | ||
var textVerticalAlign = boxPos.textVerticalAlign; // Origin of textRotation should be the base point of text drawing. | ||
@@ -100,3 +127,3 @@ | ||
// Consider performance, do not call getTextWidth util necessary. | ||
var textWidth = textContain.getWidth(text, font); | ||
var textWidth = textContain.getWidth(text, computedFont); | ||
var outerWidth = textWidth; | ||
@@ -111,16 +138,29 @@ textPadding && (outerWidth += textPadding[1] + textPadding[3]); | ||
} | ||
} | ||
} // Always set textAlign and textBase line, because it is difficute to calculate | ||
// textAlign from prevEl, and we dont sure whether textAlign will be reset if | ||
// font set happened. | ||
setCtx(ctx, 'textAlign', textAlign || 'left'); // Force baseline to be "middle". Otherwise, if using "top", the | ||
ctx.textAlign = textAlign; // Force baseline to be "middle". Otherwise, if using "top", the | ||
// text will offset downward a little bit in font "Microsoft YaHei". | ||
setCtx(ctx, 'textBaseline', 'middle'); // Always set shadowBlur and shadowOffset to avoid leak from displayable. | ||
ctx.textBaseline = 'middle'; // Always set shadowBlur and shadowOffset to avoid leak from displayable. | ||
setCtx(ctx, 'shadowBlur', style.textShadowBlur || 0); | ||
setCtx(ctx, 'shadowColor', style.textShadowColor || 'transparent'); | ||
setCtx(ctx, 'shadowOffsetX', style.textShadowOffsetX || 0); | ||
setCtx(ctx, 'shadowOffsetY', style.textShadowOffsetY || 0); // `textBaseline` is set as 'middle'. | ||
for (var i = 0; i < SHADOW_STYLE_COMMON_PROPS.length; i++) { | ||
var propItem = SHADOW_STYLE_COMMON_PROPS[i]; | ||
var styleProp = propItem[0]; | ||
var ctxProp = propItem[1]; | ||
var val = style[styleProp]; | ||
if (!isPrevTextEl || val !== prevStyle[styleProp]) { | ||
ctx[ctxProp] = fixShadow(ctx, ctxProp, val || propItem[2]); | ||
} | ||
} // `textBaseline` is set as 'middle'. | ||
textY += lineHeight / 2; | ||
var textStrokeWidth = style.textStrokeWidth; | ||
var textStrokeWidthPrev = isPrevTextEl ? prevStyle.textStrokeWidth : null; | ||
var strokeWidthChanged = !isPrevTextEl || textStrokeWidth !== textStrokeWidthPrev; | ||
var strokeChanged = !isPrevTextEl || strokeWidthChanged || style.textStroke !== prevStyle.textStroke; | ||
var textStroke = getStroke(style.textStroke, textStrokeWidth); | ||
@@ -130,15 +170,29 @@ var textFill = getFill(style.textFill); | ||
if (textStroke) { | ||
setCtx(ctx, 'lineWidth', textStrokeWidth); | ||
setCtx(ctx, 'strokeStyle', textStroke); | ||
if (strokeWidthChanged) { | ||
ctx.lineWidth = textStrokeWidth; | ||
} | ||
if (strokeChanged) { | ||
ctx.strokeStyle = textStroke; | ||
} | ||
} | ||
if (textFill) { | ||
setCtx(ctx, 'fillStyle', textFill); | ||
} | ||
if (!isPrevTextEl || style.textFill !== prevStyle.textFill || prevStyle.textBackgroundColor) { | ||
ctx.fillStyle = textFill; | ||
} | ||
} // Optimize simply, in most cases only one line exists. | ||
for (var i = 0; i < textLines.length; i++) { | ||
if (textLines.length === 1) { | ||
// Fill after stroke so the outline will not cover the main part. | ||
textStroke && ctx.strokeText(textLines[i], textX, textY); | ||
textFill && ctx.fillText(textLines[i], textX, textY); | ||
textY += lineHeight; | ||
textStroke && ctx.strokeText(textLines[0], textX, textY); | ||
textFill && ctx.fillText(textLines[0], textX, textY); | ||
} else { | ||
for (var i = 0; i < textLines.length; i++) { | ||
// Fill after stroke so the outline will not cover the main part. | ||
textStroke && ctx.strokeText(textLines[i], textX, textY); | ||
textFill && ctx.fillText(textLines[i], textX, textY); | ||
textY += lineHeight; | ||
} | ||
} | ||
@@ -150,3 +204,3 @@ } | ||
if (!contentBlock || hostEl.__dirty) { | ||
if (!contentBlock || hostEl.__dirtyText) { | ||
contentBlock = hostEl.__textCotentBlock = textContain.parseRichText(text, style); | ||
@@ -245,3 +299,4 @@ } | ||
function placeToken(hostEl, ctx, token, style, lineHeight, lineTop, x, textAlign) { | ||
var tokenStyle = style.rich[token.styleName] || {}; // 'ctx.textBaseline' is always set as 'middle', for sake of | ||
var tokenStyle = style.rich[token.styleName] || {}; | ||
tokenStyle.text = token.text; // 'ctx.textBaseline' is always set as 'middle', for sake of | ||
// the bias of "Microsoft YaHei". | ||
@@ -293,3 +348,3 @@ | ||
return style.textBackgroundColor || style.textBorderWidth && style.textBorderColor; | ||
} // style: {textBackgroundColor, textBorderWidth, textBorderColor, textBorderRadius} | ||
} // style: {textBackgroundColor, textBorderWidth, textBorderColor, textBorderRadius, text} | ||
// shape: {x, y, width, height} | ||
@@ -329,2 +384,13 @@ | ||
setCtx(ctx, 'fillStyle', textBackgroundColor); | ||
if (style.fillOpacity != null) { | ||
var originalGlobalAlpha = ctx.globalAlpha; | ||
ctx.globalAlpha = style.fillOpacity * style.opacity; | ||
ctx.fill(); | ||
ctx.globalAlpha = originalGlobalAlpha; | ||
} else { | ||
ctx.fill(); | ||
} | ||
} else if (isFunction(textBackgroundColor)) { | ||
setCtx(ctx, 'fillStyle', textBackgroundColor(style)); | ||
ctx.fill(); | ||
@@ -343,3 +409,11 @@ } else if (isObject(textBackgroundColor)) { | ||
setCtx(ctx, 'strokeStyle', textBorderColor); | ||
ctx.stroke(); | ||
if (style.strokeOpacity != null) { | ||
var originalGlobalAlpha = ctx.globalAlpha; | ||
ctx.globalAlpha = style.strokeOpacity * style.opacity; | ||
ctx.stroke(); | ||
ctx.globalAlpha = originalGlobalAlpha; | ||
} else { | ||
ctx.stroke(); | ||
} | ||
} | ||
@@ -346,0 +420,0 @@ } |
@@ -33,2 +33,5 @@ var textHelper = require("../helper/text"); | ||
} // FIXME | ||
// Do not provide prevEl to `textHelper.renderText` for ctx prop cache, | ||
// but use `ctx.save()` and `ctx.restore()`. Because the cache for rect | ||
// text propably break the cache for its host elements. | ||
@@ -35,0 +38,0 @@ |
@@ -108,3 +108,12 @@ var Displayable = require("./Displayable"); | ||
hasFill && path.fill(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); | ||
} | ||
} | ||
@@ -116,3 +125,12 @@ if (lineDash && ctxLineDash) { | ||
hasStroke && path.stroke(ctx); | ||
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); | ||
} | ||
} | ||
@@ -242,3 +260,3 @@ if (lineDash && ctxLineDash) { | ||
this.__dirty = true; | ||
this.__dirty = this.__dirtyText = true; | ||
this.__zr && this.__zr.refresh(); // Used as a clipping path | ||
@@ -245,0 +263,0 @@ |
@@ -6,5 +6,4 @@ var fixShadow = require("./helper/fixShadow"); | ||
var Style = function (opts, host) { | ||
var Style = function (opts) { | ||
this.extendFrom(opts, false); | ||
this.host = host; | ||
}; | ||
@@ -56,7 +55,2 @@ | ||
/** | ||
* @type {module:zrender/graphic/Displayable} | ||
*/ | ||
host: null, | ||
/** | ||
* @type {string} | ||
@@ -77,2 +71,12 @@ */ | ||
/** | ||
* @type {number} | ||
*/ | ||
fillOpacity: null, | ||
/** | ||
* @type {number} | ||
*/ | ||
strokeOpacity: null, | ||
/** | ||
* @type {Array.<number>} | ||
@@ -79,0 +83,0 @@ */ |
@@ -31,6 +31,7 @@ var Displayable = require("./Displayable"); | ||
text != null && (text += ''); // Always bind style | ||
text != null && (text += ''); // Do not apply style.bind in Text node. Because the real bind job | ||
// is in textHelper.renderText, and performance of text render should | ||
// be considered. | ||
// style.bind(ctx, this, prevEl); | ||
style.bind(ctx, this, prevEl); | ||
if (!textHelper.needDrawText(text, style)) { | ||
@@ -41,3 +42,3 @@ return; | ||
this.setTransform(ctx); | ||
textHelper.renderText(this, ctx, text, style); | ||
textHelper.renderText(this, ctx, text, style, null, prevEl); | ||
this.restoreTransform(ctx); | ||
@@ -44,0 +45,0 @@ }, |
@@ -9,2 +9,4 @@ var util = require("./core/util"); | ||
var eventTool = require("./core/event"); | ||
var SILENT = 'silent'; | ||
@@ -29,6 +31,11 @@ | ||
zrByTouch: event.zrByTouch, | ||
which: event.which | ||
which: event.which, | ||
stop: stopEvent | ||
}; | ||
} | ||
function stopEvent(event) { | ||
eventTool.stop(this.event); | ||
} | ||
function EmptyProxy() {} | ||
@@ -35,0 +42,0 @@ |
@@ -136,120 +136,141 @@ var Animator = require("../animation/Animator"); | ||
animateTo: function (target, time, delay, easing, callback, forceAnimate) { | ||
// animateTo(target, time, easing, callback); | ||
if (isString(delay)) { | ||
animateTo(this, target, time, delay, easing, callback, forceAnimate); | ||
}, | ||
/** | ||
* Animate from the target state to current state. | ||
* The params and the return value are the same as `this.animateTo`. | ||
*/ | ||
animateFrom: function (target, time, delay, easing, callback, forceAnimate) { | ||
animateTo(this, target, time, delay, easing, callback, forceAnimate, true); | ||
} | ||
}; | ||
function animateTo(animatable, target, time, delay, easing, callback, forceAnimate, reverse) { | ||
// animateTo(target, time, easing, callback); | ||
if (isString(delay)) { | ||
callback = easing; | ||
easing = delay; | ||
delay = 0; | ||
} // animateTo(target, time, delay, callback); | ||
else if (isFunction(easing)) { | ||
callback = easing; | ||
easing = delay; | ||
easing = 'linear'; | ||
delay = 0; | ||
} // animateTo(target, time, delay, callback); | ||
else if (isFunction(easing)) { | ||
callback = easing; | ||
easing = 'linear'; | ||
} // animateTo(target, time, callback); | ||
else if (isFunction(delay)) { | ||
callback = delay; | ||
delay = 0; | ||
} // animateTo(target, time, callback); | ||
else if (isFunction(delay)) { | ||
callback = delay; | ||
delay = 0; | ||
} // animateTo(target, callback) | ||
else if (isFunction(time)) { | ||
callback = time; | ||
} // animateTo(target, callback) | ||
else if (isFunction(time)) { | ||
callback = time; | ||
time = 500; | ||
} // animateTo(target) | ||
else if (!time) { | ||
time = 500; | ||
} // animateTo(target) | ||
else if (!time) { | ||
time = 500; | ||
} // Stop all previous animations | ||
} // Stop all previous animations | ||
this.stopAnimation(); | ||
animatable.stopAnimation(); | ||
animateToShallow(animatable, '', animatable, target, time, delay, reverse); // Animators may be removed immediately after start | ||
// if there is nothing to animate | ||
this._animateToShallow('', this, target, time, delay); // Animators may be removed immediately after start | ||
// if there is nothing to animate | ||
var animators = animatable.animators.slice(); | ||
var count = animators.length; | ||
function done() { | ||
count--; | ||
var animators = this.animators.slice(); | ||
var count = animators.length; | ||
if (!count) { | ||
callback && callback(); | ||
} | ||
} // No animators. This should be checked before animators[i].start(), | ||
// because 'done' may be executed immediately if no need to animate. | ||
function done() { | ||
count--; | ||
if (!count) { | ||
callback && callback(); | ||
} | ||
} // No animators. This should be checked before animators[i].start(), | ||
// because 'done' may be executed immediately if no need to animate. | ||
if (!count) { | ||
callback && callback(); | ||
} // Start after all animators created | ||
// Incase any animator is done immediately when all animation properties are not changed | ||
if (!count) { | ||
callback && callback(); | ||
} // Start after all animators created | ||
// Incase any animator is done immediately when all animation properties are not changed | ||
for (var i = 0; i < animators.length; i++) { | ||
animators[i].done(done).start(easing, forceAnimate); | ||
} | ||
} | ||
/** | ||
* @param {string} path='' | ||
* @param {Object} source=animatable | ||
* @param {Object} target | ||
* @param {number} [time=500] | ||
* @param {number} [delay=0] | ||
* @param {boolean} [reverse] If `true`, animate | ||
* from the `target` to current state. | ||
* | ||
* @example | ||
* // Animate position | ||
* el._animateToShallow({ | ||
* position: [10, 10] | ||
* }) | ||
* | ||
* // Animate shape, style and position in 100ms, delayed 100ms | ||
* el._animateToShallow({ | ||
* shape: { | ||
* width: 500 | ||
* }, | ||
* style: { | ||
* fill: 'red' | ||
* } | ||
* position: [10, 10] | ||
* }, 100, 100) | ||
*/ | ||
for (var i = 0; i < animators.length; i++) { | ||
animators[i].done(done).start(easing, forceAnimate); | ||
function animateToShallow(animatable, path, source, target, time, delay, reverse) { | ||
var objShallow = {}; | ||
var propertyCount = 0; | ||
for (var name in target) { | ||
if (!target.hasOwnProperty(name)) { | ||
continue; | ||
} | ||
}, | ||
/** | ||
* @private | ||
* @param {string} path='' | ||
* @param {Object} source=this | ||
* @param {Object} target | ||
* @param {number} [time=500] | ||
* @param {number} [delay=0] | ||
* | ||
* @example | ||
* // Animate position | ||
* el._animateToShallow({ | ||
* position: [10, 10] | ||
* }) | ||
* | ||
* // Animate shape, style and position in 100ms, delayed 100ms | ||
* el._animateToShallow({ | ||
* shape: { | ||
* width: 500 | ||
* }, | ||
* style: { | ||
* fill: 'red' | ||
* } | ||
* position: [10, 10] | ||
* }, 100, 100) | ||
*/ | ||
_animateToShallow: function (path, source, target, time, delay) { | ||
var objShallow = {}; | ||
var propertyCount = 0; | ||
for (var name in target) { | ||
if (!target.hasOwnProperty(name)) { | ||
continue; | ||
} | ||
if (source[name] != null) { | ||
if (isObject(target[name]) && !isArrayLike(target[name])) { | ||
this._animateToShallow(path ? path + '.' + name : name, source[name], target[name], time, delay); | ||
if (source[name] != null) { | ||
if (isObject(target[name]) && !isArrayLike(target[name])) { | ||
animateToShallow(animatable, path ? path + '.' + name : name, source[name], target[name], time, delay, reverse); | ||
} else { | ||
if (reverse) { | ||
objShallow[name] = source[name]; | ||
setAttrByPath(animatable, path, name, target[name]); | ||
} else { | ||
objShallow[name] = target[name]; | ||
propertyCount++; | ||
} | ||
} else if (target[name] != null) { | ||
// Attr directly if not has property | ||
// FIXME, if some property not needed for element ? | ||
if (!path) { | ||
this.attr(name, target[name]); | ||
} else { | ||
// Shape or style | ||
var props = {}; | ||
props[path] = {}; | ||
props[path][name] = target[name]; | ||
this.attr(props); | ||
} | ||
propertyCount++; | ||
} | ||
} else if (target[name] != null && !reverse) { | ||
setAttrByPath(animatable, path, name, target[name]); | ||
} | ||
} | ||
if (propertyCount > 0) { | ||
this.animate(path, false).when(time == null ? 500 : time, objShallow).delay(delay || 0); | ||
} | ||
if (propertyCount > 0) { | ||
animatable.animate(path, false).when(time == null ? 500 : time, objShallow).delay(delay || 0); | ||
} | ||
} | ||
return this; | ||
function setAttrByPath(el, path, name, value) { | ||
// Attr directly if not has property | ||
// FIXME, if some property not needed for element ? | ||
if (!path) { | ||
el.attr(name, value); | ||
} else { | ||
// Only support set shape or style | ||
var props = {}; | ||
props[path] = {}; | ||
props[path][name] = value; | ||
el.attr(props); | ||
} | ||
}; | ||
} | ||
var _default = Animatable; | ||
module.exports = _default; |
/** | ||
* 事件扩展 | ||
* Event Mixin | ||
* @module zrender/mixin/Eventful | ||
@@ -9,9 +9,23 @@ * @author Kener (@Kener-林峰, kener.linfeng@gmail.com) | ||
/** | ||
* 事件分发器 | ||
* Event dispatcher. | ||
* | ||
* @alias module:zrender/mixin/Eventful | ||
* @constructor | ||
* @param {Object} [eventProcessor] The object eventProcessor is the scope when | ||
* `eventProcessor.xxx` called. | ||
* @param {Function} [eventProcessor.normalizeQuery] | ||
* param: {string|Object} Raw query. | ||
* return: {string|Object} Normalized query. | ||
* @param {Function} [eventProcessor.filter] Event will be dispatched only | ||
* if it returns `true`. | ||
* param: {string} eventType | ||
* param: {string|Object} query | ||
* return: {boolean} | ||
* @param {Function} [eventProcessor.afterTrigger] Call after all handlers called. | ||
* param: {string} eventType | ||
*/ | ||
var Eventful = function () { | ||
var Eventful = function (eventProcessor) { | ||
this._$handlers = {}; | ||
this._$eventProcessor = eventProcessor; | ||
}; | ||
@@ -23,11 +37,18 @@ | ||
/** | ||
* 单次触发绑定,trigger后销毁 | ||
* The handler can only be triggered once, then removed. | ||
* | ||
* @param {string} event 事件名 | ||
* @param {Function} handler 响应函数 | ||
* @param {string} event The event name. | ||
* @param {string|Object} [query] Condition used on event filter. | ||
* @param {Function} handler The event handler. | ||
* @param {Object} context | ||
*/ | ||
one: function (event, handler, context) { | ||
one: function (event, query, handler, context) { | ||
var _h = this._$handlers; | ||
if (typeof query === 'function') { | ||
context = handler; | ||
handler = query; | ||
query = null; | ||
} | ||
if (!handler || !event) { | ||
@@ -37,2 +58,4 @@ return this; | ||
query = normalizeQuery(this, query); | ||
if (!_h[event]) { | ||
@@ -51,2 +74,3 @@ _h[event] = []; | ||
one: true, | ||
query: query, | ||
ctx: context || this | ||
@@ -59,10 +83,18 @@ }); | ||
/** | ||
* 绑定事件 | ||
* @param {string} event 事件名 | ||
* @param {Function} handler 事件处理函数 | ||
* Bind a handler. | ||
* | ||
* @param {string} event The event name. | ||
* @param {string|Object} [query] Condition used on event filter. | ||
* @param {Function} handler The event handler. | ||
* @param {Object} [context] | ||
*/ | ||
on: function (event, handler, context) { | ||
on: function (event, query, handler, context) { | ||
var _h = this._$handlers; | ||
if (typeof query === 'function') { | ||
context = handler; | ||
handler = query; | ||
query = null; | ||
} | ||
if (!handler || !event) { | ||
@@ -72,2 +104,4 @@ return this; | ||
query = normalizeQuery(this, query); | ||
if (!_h[event]) { | ||
@@ -86,2 +120,3 @@ _h[event] = []; | ||
one: false, | ||
query: query, | ||
ctx: context || this | ||
@@ -94,3 +129,4 @@ }); | ||
/** | ||
* 是否绑定了事件 | ||
* Whether any handler has bound. | ||
* | ||
* @param {string} event | ||
@@ -105,5 +141,6 @@ * @return {boolean} | ||
/** | ||
* 解绑事件 | ||
* @param {string} event 事件名 | ||
* @param {Function} [handler] 事件处理函数 | ||
* Unbind a event. | ||
* | ||
* @param {string} event The event name. | ||
* @param {Function} [handler] The event handler. | ||
*/ | ||
@@ -123,3 +160,3 @@ off: function (event, handler) { | ||
for (var i = 0, l = _h[event].length; i < l; i++) { | ||
if (_h[event][i]['h'] != handler) { | ||
if (_h[event][i].h !== handler) { | ||
newList.push(_h[event][i]); | ||
@@ -143,8 +180,11 @@ } | ||
/** | ||
* 事件分发 | ||
* Dispatch a event. | ||
* | ||
* @param {string} type 事件类型 | ||
* @param {string} type The event name. | ||
*/ | ||
trigger: function (type) { | ||
if (this._$handlers[type]) { | ||
var _h = this._$handlers[type]; | ||
var eventProcessor = this._$eventProcessor; | ||
if (_h) { | ||
var args = arguments; | ||
@@ -157,21 +197,24 @@ var argLen = args.length; | ||
var _h = this._$handlers[type]; | ||
var len = _h.length; | ||
for (var i = 0; i < len;) { | ||
// Optimize advise from backbone | ||
var hItem = _h[i]; | ||
if (eventProcessor && eventProcessor.filter && hItem.query != null && !eventProcessor.filter(type, hItem.query)) { | ||
i++; | ||
continue; | ||
} // Optimize advise from backbone | ||
switch (argLen) { | ||
case 1: | ||
_h[i]['h'].call(_h[i]['ctx']); | ||
hItem.h.call(hItem.ctx); | ||
break; | ||
case 2: | ||
_h[i]['h'].call(_h[i]['ctx'], args[1]); | ||
hItem.h.call(hItem.ctx, args[1]); | ||
break; | ||
case 3: | ||
_h[i]['h'].call(_h[i]['ctx'], args[1], args[2]); | ||
hItem.h.call(hItem.ctx, args[1], args[2]); | ||
break; | ||
@@ -181,8 +224,7 @@ | ||
// have more than 2 given arguments | ||
_h[i]['h'].apply(_h[i]['ctx'], args); | ||
hItem.h.apply(hItem.ctx, args); | ||
break; | ||
} | ||
if (_h[i]['one']) { | ||
if (hItem.one) { | ||
_h.splice(i, 1); | ||
@@ -197,2 +239,3 @@ | ||
eventProcessor && eventProcessor.afterTrigger && eventProcessor.afterTrigger(type); | ||
return this; | ||
@@ -202,7 +245,11 @@ }, | ||
/** | ||
* 带有context的事件分发, 最后一个参数是事件回调的context | ||
* @param {string} type 事件类型 | ||
* Dispatch a event with context, which is specified at the last parameter. | ||
* | ||
* @param {string} type The event name. | ||
*/ | ||
triggerWithContext: function (type) { | ||
if (this._$handlers[type]) { | ||
var _h = this._$handlers[type]; | ||
var eventProcessor = this._$eventProcessor; | ||
if (_h) { | ||
var args = arguments; | ||
@@ -216,21 +263,24 @@ var argLen = args.length; | ||
var ctx = args[args.length - 1]; | ||
var _h = this._$handlers[type]; | ||
var len = _h.length; | ||
for (var i = 0; i < len;) { | ||
// Optimize advise from backbone | ||
var hItem = _h[i]; | ||
if (eventProcessor && eventProcessor.filter && hItem.query != null && !eventProcessor.filter(type, hItem.query)) { | ||
i++; | ||
continue; | ||
} // Optimize advise from backbone | ||
switch (argLen) { | ||
case 1: | ||
_h[i]['h'].call(ctx); | ||
hItem.h.call(ctx); | ||
break; | ||
case 2: | ||
_h[i]['h'].call(ctx, args[1]); | ||
hItem.h.call(ctx, args[1]); | ||
break; | ||
case 3: | ||
_h[i]['h'].call(ctx, args[1], args[2]); | ||
hItem.h.call(ctx, args[1], args[2]); | ||
break; | ||
@@ -240,8 +290,7 @@ | ||
// have more than 2 given arguments | ||
_h[i]['h'].apply(ctx, args); | ||
hItem.h.apply(ctx, args); | ||
break; | ||
} | ||
if (_h[i]['one']) { | ||
if (hItem.one) { | ||
_h.splice(i, 1); | ||
@@ -256,6 +305,19 @@ | ||
eventProcessor && eventProcessor.afterTrigger && eventProcessor.afterTrigger(type); | ||
return this; | ||
} | ||
}; // 对象可以通过 onxxxx 绑定事件 | ||
}; | ||
function normalizeQuery(host, query) { | ||
var eventProcessor = host._$eventProcessor; | ||
if (query != null && eventProcessor && eventProcessor.normalizeQuery) { | ||
query = eventProcessor.normalizeQuery(query); | ||
} | ||
return query; | ||
} // ---------------------- | ||
// The events in zrender | ||
// ---------------------- | ||
/** | ||
@@ -345,3 +407,4 @@ * @event module:zrender/mixin/Eventful#onclick | ||
var _default = Eventful; | ||
module.exports = _default; |
@@ -72,2 +72,4 @@ var matrix = require("../core/matrix"); | ||
var scaleTmp = []; | ||
transformableProto.updateTransform = function () { | ||
@@ -103,2 +105,16 @@ var parent = this.parent; | ||
this.transform = m; | ||
var globalScaleRatio = this.globalScaleRatio; | ||
if (globalScaleRatio != null && globalScaleRatio !== 1) { | ||
this.getGlobalScale(scaleTmp); | ||
var relX = scaleTmp[0] < 0 ? -1 : 1; | ||
var relY = scaleTmp[1] < 0 ? -1 : 1; | ||
var sx = ((scaleTmp[0] - relX) * globalScaleRatio + relX) / scaleTmp[0] || 0; | ||
var sy = ((scaleTmp[1] - relY) * globalScaleRatio + relY) / scaleTmp[1] || 0; | ||
m[0] *= sx; | ||
m[1] *= sx; | ||
m[2] *= sy; | ||
m[3] *= sy; | ||
} | ||
this.invTransform = this.invTransform || matrix.create(); | ||
@@ -134,20 +150,10 @@ matrix.invert(this.invTransform, m); | ||
var tmpTransform = []; | ||
/** | ||
* 分解`transform`矩阵到`position`, `rotation`, `scale` | ||
*/ | ||
var originTransform = matrix.create(); | ||
transformableProto.decomposeTransform = function () { | ||
if (!this.transform) { | ||
transformableProto.setLocalTransform = function (m) { | ||
if (!m) { | ||
// TODO return or set identity? | ||
return; | ||
} | ||
var parent = this.parent; | ||
var m = this.transform; | ||
if (parent && parent.transform) { | ||
// Get local transform and decompose them to position, scale, rotation | ||
matrix.mul(tmpTransform, parent.invTransform, m); | ||
m = tmpTransform; | ||
} | ||
var sx = m[0] * m[0] + m[1] * m[1]; | ||
@@ -181,2 +187,34 @@ var sy = m[2] * m[2] + m[3] * m[3]; | ||
/** | ||
* 分解`transform`矩阵到`position`, `rotation`, `scale` | ||
*/ | ||
transformableProto.decomposeTransform = function () { | ||
if (!this.transform) { | ||
return; | ||
} | ||
var parent = this.parent; | ||
var m = this.transform; | ||
if (parent && parent.transform) { | ||
// Get local transform and decompose them to position, scale, rotation | ||
matrix.mul(tmpTransform, parent.invTransform, m); | ||
m = tmpTransform; | ||
} | ||
var origin = this.origin; | ||
if (origin && (origin[0] || origin[1])) { | ||
originTransform[4] = origin[0]; | ||
originTransform[5] = origin[1]; | ||
matrix.mul(tmpTransform, m, originTransform); | ||
tmpTransform[4] -= origin[0]; | ||
tmpTransform[5] -= origin[1]; | ||
m = tmpTransform; | ||
} | ||
this.setLocalTransform(m); | ||
}; | ||
/** | ||
* Get global scale | ||
@@ -187,21 +225,24 @@ * @return {Array.<number>} | ||
transformableProto.getGlobalScale = function () { | ||
transformableProto.getGlobalScale = function (out) { | ||
var m = this.transform; | ||
out = out || []; | ||
if (!m) { | ||
return [1, 1]; | ||
out[0] = 1; | ||
out[1] = 1; | ||
return out; | ||
} | ||
var sx = Math.sqrt(m[0] * m[0] + m[1] * m[1]); | ||
var sy = Math.sqrt(m[2] * m[2] + m[3] * m[3]); | ||
out[0] = Math.sqrt(m[0] * m[0] + m[1] * m[1]); | ||
out[1] = Math.sqrt(m[2] * m[2] + m[3] * m[3]); | ||
if (m[0] < 0) { | ||
sx = -sx; | ||
out[0] = -out[0]; | ||
} | ||
if (m[3] < 0) { | ||
sy = -sy; | ||
out[1] = -out[1]; | ||
} | ||
return [sx, sy]; | ||
return out; | ||
}; | ||
@@ -208,0 +249,0 @@ /** |
@@ -274,9 +274,14 @@ var _config = require("./config"); | ||
style: el.style, | ||
shape: el.shape | ||
shape: el.shape, | ||
z: el.z, | ||
z2: el.z2, | ||
silent: el.silent | ||
}); | ||
elMirror.__from = el; | ||
el.__hoverMir = elMirror; | ||
elMirror.setStyle(hoverStyle); | ||
hoverStyle && elMirror.setStyle(hoverStyle); | ||
this._hoverElements.push(elMirror); | ||
return elMirror; | ||
}, | ||
@@ -434,3 +439,3 @@ removeHover: function (el) { | ||
el.__dirty = false; | ||
el.__dirty = el.__dirtyText = false; | ||
@@ -437,0 +442,0 @@ if (useTimer) { |
@@ -70,3 +70,3 @@ var _core = require("./core"); | ||
function bindStyle(svgEl, style, isText) { | ||
function bindStyle(svgEl, style, isText, el) { | ||
if (pathHasFill(style, isText)) { | ||
@@ -96,3 +96,3 @@ var fill = isText ? style.textFill : style.fill; | ||
attr(svgEl, 'fill', fill); | ||
attr(svgEl, 'fill-opacity', style.opacity); | ||
attr(svgEl, 'fill-opacity', style.fillOpacity != null ? style.fillOpacity * style.opacity : style.opacity); | ||
} else { | ||
@@ -107,7 +107,7 @@ attr(svgEl, 'fill', NONE); | ||
var strokeWidth = isText ? style.textStrokeWidth : style.lineWidth; | ||
var strokeScale = !isText && style.strokeNoScale ? style.host.getLineScale() : 1; | ||
var strokeScale = !isText && style.strokeNoScale ? el.getLineScale() : 1; | ||
attr(svgEl, 'stroke-width', strokeWidth / strokeScale); // stroke then fill for text; fill then stroke for others | ||
attr(svgEl, 'paint-order', isText ? 'stroke' : 'fill'); | ||
attr(svgEl, 'stroke-opacity', style.opacity); | ||
attr(svgEl, 'stroke-opacity', style.strokeOpacity != null ? style.strokeOpacity : style.opacity); | ||
var lineDash = style.lineDash; | ||
@@ -271,3 +271,3 @@ | ||
bindStyle(svgEl, style); | ||
bindStyle(svgEl, style, false, el); | ||
setTransform(svgEl, el.transform); | ||
@@ -389,3 +389,3 @@ | ||
attr(textSvgEl, 'y', y); | ||
bindStyle(textSvgEl, style, true); | ||
bindStyle(textSvgEl, style, true, el); | ||
@@ -404,2 +404,3 @@ if (el instanceof Text || el.style.transformText) { | ||
rect.y = pos[1]; | ||
el.transform = matrix.identity(matrix.create()); | ||
} // Text rotation, but no element transform | ||
@@ -421,3 +422,5 @@ | ||
matrix.rotate(transform, el.transform, rotate); | ||
matrix.rotate(transform, transform, rotate); | ||
var pos = [el.transform[4], el.transform[5]]; | ||
matrix.translate(transform, transform, pos); | ||
setTransform(textSvgEl, transform); | ||
@@ -443,3 +446,3 @@ } | ||
if (verticalAlign === 'baseline') { | ||
if (verticalAlign === 'after-edge') { | ||
dy = -textRect.height + lineHeight; | ||
@@ -504,3 +507,3 @@ textPadding && (dy -= textPadding[2]); | ||
} else if (verticalAlign === 'bottom') { | ||
return 'baseline'; | ||
return 'after-edge'; | ||
} else { | ||
@@ -507,0 +510,0 @@ return 'hanging'; |
@@ -8,3 +8,6 @@ var Path = require("../graphic/Path"); | ||
// command chars | ||
var cc = ['m', 'M', 'l', 'L', 'v', 'V', 'h', 'H', 'z', 'Z', 'c', 'C', 'q', 'Q', 't', 'T', 's', 'S', 'a', 'A']; | ||
// var cc = [ | ||
// 'm', 'M', 'l', 'L', 'v', 'V', 'h', 'H', 'z', 'Z', | ||
// 'c', 'C', 'q', 'Q', 't', 'T', 's', 'S', 'a', 'A' | ||
// ]; | ||
var mathSqrt = Math.sqrt; | ||
@@ -67,44 +70,65 @@ var mathSin = Math.sin; | ||
var commandReg = /([mlvhzcqtsa])([^mlvhzcqtsa]*)/ig; // Consider case: | ||
// (1) delimiter can be comma or space, where continuous commas | ||
// or spaces should be seen as one comma. | ||
// (2) value can be like: | ||
// '2e-4', 'l.5.9' (ignore 0), 'M-10-10', 'l-2.43e-1,34.9983', | ||
// 'l-.5E1,54', '121-23-44-11' (no delimiter) | ||
var numberReg = /-?([0-9]*\.)?[0-9]+([eE]-?[0-9]+)?/g; // var valueSplitReg = /[\s,]+/; | ||
function createPathProxyFromString(data) { | ||
if (!data) { | ||
return []; | ||
} // command string | ||
return new PathProxy(); | ||
} // var data = data.replace(/-/g, ' -') | ||
// .replace(/ /g, ' ') | ||
// .replace(/ /g, ',') | ||
// .replace(/,,/g, ','); | ||
// var n; | ||
// create pipes so that we can split the data | ||
// for (n = 0; n < cc.length; n++) { | ||
// cs = cs.replace(new RegExp(cc[n], 'g'), '|' + cc[n]); | ||
// } | ||
// data = data.replace(/-/g, ',-'); | ||
// create array | ||
// var arr = cs.split('|'); | ||
// init context point | ||
var cs = data.replace(/-/g, ' -').replace(/ /g, ' ').replace(/ /g, ',').replace(/,,/g, ','); | ||
var n; // create pipes so that we can split the data | ||
for (n = 0; n < cc.length; n++) { | ||
cs = cs.replace(new RegExp(cc[n], 'g'), '|' + cc[n]); | ||
} // create array | ||
var arr = cs.split('|'); // init context point | ||
var cpx = 0; | ||
var cpy = 0; | ||
var subpathX = cpx; | ||
var subpathY = cpy; | ||
var prevCmd; | ||
var path = new PathProxy(); | ||
var CMD = PathProxy.CMD; | ||
var prevCmd; | ||
var CMD = PathProxy.CMD; // commandReg.lastIndex = 0; | ||
// var cmdResult; | ||
// while ((cmdResult = commandReg.exec(data)) != null) { | ||
// var cmdStr = cmdResult[1]; | ||
// var cmdContent = cmdResult[2]; | ||
for (n = 1; n < arr.length; n++) { | ||
var str = arr[n]; | ||
var c = str.charAt(0); | ||
var off = 0; | ||
var p = str.slice(1).replace(/e,-/g, 'e-').split(','); | ||
var cmd; | ||
var cmdList = data.match(commandReg); | ||
if (p.length > 0 && p[0] === '') { | ||
p.shift(); | ||
} | ||
for (var l = 0; l < cmdList.length; l++) { | ||
var cmdText = cmdList[l]; | ||
var cmdStr = cmdText.charAt(0); | ||
var cmd; // String#split is faster a little bit than String#replace or RegExp#exec. | ||
// var p = cmdContent.split(valueSplitReg); | ||
// var pLen = 0; | ||
// for (var i = 0; i < p.length; i++) { | ||
// // '' and other invalid str => NaN | ||
// var val = parseFloat(p[i]); | ||
// !isNaN(val) && (p[pLen++] = val); | ||
// } | ||
for (var i = 0; i < p.length; i++) { | ||
var p = cmdText.match(numberReg) || []; | ||
var pLen = p.length; | ||
for (var i = 0; i < pLen; i++) { | ||
p[i] = parseFloat(p[i]); | ||
} | ||
while (off < p.length && !isNaN(p[off])) { | ||
if (isNaN(p[0])) { | ||
break; | ||
} | ||
var off = 0; | ||
while (off < pLen) { | ||
var ctlPtx; | ||
@@ -120,3 +144,3 @@ var ctlPty; | ||
switch (c) { | ||
switch (cmdStr) { | ||
case 'l': | ||
@@ -141,3 +165,5 @@ cpx += p[off++]; | ||
path.addData(cmd, cpx, cpy); | ||
c = 'l'; | ||
subpathX = cpx; | ||
subpathY = cpy; | ||
cmdStr = 'l'; | ||
break; | ||
@@ -150,3 +176,5 @@ | ||
path.addData(cmd, cpx, cpy); | ||
c = 'L'; | ||
subpathX = cpx; | ||
subpathY = cpy; | ||
cmdStr = 'L'; | ||
break; | ||
@@ -310,5 +338,8 @@ | ||
if (c === 'z' || c === 'Z') { | ||
if (cmdStr === 'z' || cmdStr === 'Z') { | ||
cmd = CMD.Z; | ||
path.addData(cmd); | ||
path.addData(cmd); // z may be in the middle of the path. | ||
cpx = subpathX; | ||
cpy = subpathY; | ||
} | ||
@@ -315,0 +346,0 @@ |
@@ -36,3 +36,3 @@ var guid = require("./core/guid"); | ||
var version = '4.0.4'; | ||
var version = '4.0.5'; | ||
/** | ||
@@ -283,4 +283,5 @@ * Initializing a zrender instance | ||
if (this.painter.addHover) { | ||
this.painter.addHover(el, style); | ||
var elMirror = this.painter.addHover(el, style); | ||
this.refreshHover(); | ||
return elMirror; | ||
} | ||
@@ -287,0 +288,0 @@ }, |
{ | ||
"name": "zrender", | ||
"version": "4.0.4", | ||
"version": "4.0.5", | ||
"description": "A lightweight canvas library.", | ||
@@ -9,8 +9,2 @@ "keywords": [ | ||
], | ||
"contributors": [ | ||
{ | ||
"name": "erik", | ||
"email": "errorrik@gmail.com" | ||
} | ||
], | ||
"repository": { | ||
@@ -17,0 +11,0 @@ "type": "git", |
@@ -20,3 +20,4 @@ /** | ||
svgSupported: false, | ||
touchEventsSupported: true | ||
touchEventsSupported: true, | ||
domSupported: false | ||
} | ||
@@ -31,3 +32,4 @@ } | ||
worker: true, | ||
canvasSupported: true | ||
canvasSupported: true, | ||
domSupported: false | ||
}; | ||
@@ -44,3 +46,4 @@ } | ||
canvasSupported: true, | ||
svgSupported: true | ||
svgSupported: true, | ||
domSupported: false | ||
}; | ||
@@ -154,4 +157,5 @@ } | ||
// standard. So we exclude that. (IE 10 is hardly used on touch device) | ||
&& (browser.edge || (browser.ie && browser.version >= 11)) | ||
&& (browser.edge || (browser.ie && browser.version >= 11)), | ||
// passiveSupported: detectPassiveSupport() | ||
domSupported: typeof document !== 'undefined' | ||
}; | ||
@@ -158,0 +162,0 @@ } |
@@ -236,3 +236,3 @@ /** | ||
this._xi = mathCos(endAngle) * r + cx; | ||
this._yi = mathSin(endAngle) * r + cx; | ||
this._yi = mathSin(endAngle) * r + cy; | ||
return this; | ||
@@ -239,0 +239,0 @@ }, |
@@ -613,2 +613,5 @@ /** | ||
var isArr = isArray(obj); | ||
// Key should not be set on this, otherwise | ||
// methods get/set/... may be overrided. | ||
this.data = {}; | ||
var thisMap = this; | ||
@@ -625,4 +628,2 @@ | ||
// Add prefix to avoid conflict with Object.prototype. | ||
HashMap.prototype = { | ||
@@ -634,3 +635,3 @@ constructor: HashMap, | ||
get: function (key) { | ||
return this.hasOwnProperty(key) ? this[key] : null; | ||
return this.data.hasOwnProperty(key) ? this.data[key] : null; | ||
}, | ||
@@ -640,3 +641,3 @@ set: function (key, value) { | ||
// used in this case: `var someVal = map.set('a', genVal());` | ||
return (this[key] = value); | ||
return (this.data[key] = value); | ||
}, | ||
@@ -647,4 +648,4 @@ // Although util.each can be performed on this hashMap directly, user | ||
context !== void 0 && (cb = bind(cb, context)); | ||
for (var key in this) { | ||
this.hasOwnProperty(key) && cb(this[key], key); | ||
for (var key in this.data) { | ||
this.data.hasOwnProperty(key) && cb(this.data[key], key); | ||
} | ||
@@ -654,3 +655,3 @@ }, | ||
removeKey: function (key) { | ||
delete this[key]; | ||
delete this.data[key]; | ||
} | ||
@@ -657,0 +658,0 @@ }; |
@@ -10,2 +10,3 @@ /** | ||
import * as pathTool from './tool/path'; | ||
import {parseSVG} from './tool/parseSVG'; | ||
@@ -47,1 +48,3 @@ | ||
export {zrUtil as util}; | ||
export {parseSVG}; |
@@ -148,4 +148,7 @@ /** | ||
incremental: false, | ||
// inplace is used with incremental | ||
inplace: false, | ||
/** | ||
* Scale ratio for global scale. | ||
* @type {boolean} | ||
*/ | ||
globalScaleRatio: 1, | ||
@@ -207,3 +210,3 @@ beforeBrush: function (ctx) {}, | ||
dirty: function () { | ||
this.__dirty = true; | ||
this.__dirty = this.__dirtyText = true; | ||
@@ -210,0 +213,0 @@ this._rect = null; |
@@ -54,3 +54,3 @@ | ||
!image && (image = new Image()); | ||
image.onload = imageOnLoad; | ||
image.onload = image.onerror = imageOnLoad; | ||
@@ -78,3 +78,3 @@ globalImageCache.put( | ||
var cachedImgObj = this.__cachedImgObj; | ||
this.onload = this.__cachedImgObj = null; | ||
this.onload = this.onerror = this.__cachedImgObj = null; | ||
@@ -81,0 +81,0 @@ for (var i = 0; i < cachedImgObj.pending.length; i++) { |
@@ -8,3 +8,4 @@ | ||
isString, | ||
isObject | ||
isObject, | ||
isFunction | ||
} from '../../core/util'; | ||
@@ -19,2 +20,10 @@ import * as textContain from '../../contain/text'; | ||
var VALID_TEXT_VERTICAL_ALIGN = {top: 1, bottom: 1, middle: 1}; | ||
// Different from `STYLE_COMMON_PROPS` of `graphic/Style`, | ||
// the default value of shadowColor is `'transparent'`. | ||
var SHADOW_STYLE_COMMON_PROPS = [ | ||
['textShadowBlur', 'shadowBlur', 0], | ||
['textShadowOffsetX', 'shadowOffsetX', 0], | ||
['textShadowOffsetY', 'shadowOffsetY', 0], | ||
['textShadowColor', 'shadowColor', 'transparent'] | ||
]; | ||
@@ -62,18 +71,38 @@ /** | ||
* If set false, rect text is not used. | ||
* @param {Element} [prevEl] For ctx prop cache. | ||
*/ | ||
export function renderText(hostEl, ctx, text, style, rect) { | ||
export function renderText(hostEl, ctx, text, style, rect, prevEl) { | ||
style.rich | ||
? renderRichText(hostEl, ctx, text, style, rect) | ||
: renderPlainText(hostEl, ctx, text, style, rect); | ||
: renderPlainText(hostEl, ctx, text, style, rect, prevEl); | ||
} | ||
function renderPlainText(hostEl, ctx, text, style, rect) { | ||
var font = setCtx(ctx, 'font', style.font || textContain.DEFAULT_FONT); | ||
// Avoid setting to ctx according to prevEl if possible for | ||
// performance in scenarios of large amount text. | ||
function renderPlainText(hostEl, ctx, text, style, rect, prevEl) { | ||
'use strict'; | ||
var prevStyle = prevEl && prevEl.style; | ||
// Some cache only available on textEl. | ||
var isPrevTextEl = prevStyle && prevEl.type === 'text'; | ||
var styleFont = style.font || textContain.DEFAULT_FONT; | ||
if (!isPrevTextEl || styleFont !== (prevStyle.font || textContain.DEFAULT_FONT)) { | ||
ctx.font = styleFont; | ||
} | ||
// Use the final font from context-2d, because the final | ||
// font might not be the style.font when it is illegal. | ||
// But get `ctx.font` might be time consuming. | ||
var computedFont = hostEl.__computedFont; | ||
if (hostEl.__styleFont !== styleFont) { | ||
hostEl.__styleFont = styleFont; | ||
computedFont = hostEl.__computedFont = ctx.font; | ||
} | ||
var textPadding = style.textPadding; | ||
var contentBlock = hostEl.__textCotentBlock; | ||
if (!contentBlock || hostEl.__dirty) { | ||
if (!contentBlock || hostEl.__dirtyText) { | ||
contentBlock = hostEl.__textCotentBlock = textContain.parsePlainText( | ||
text, font, textPadding, style.truncate | ||
text, computedFont, textPadding, style.truncate | ||
); | ||
@@ -90,3 +119,3 @@ } | ||
var baseY = boxPos.baseY; | ||
var textAlign = boxPos.textAlign; | ||
var textAlign = boxPos.textAlign || 'left'; | ||
var textVerticalAlign = boxPos.textVerticalAlign; | ||
@@ -104,3 +133,3 @@ | ||
// Consider performance, do not call getTextWidth util necessary. | ||
var textWidth = textContain.getWidth(text, font); | ||
var textWidth = textContain.getWidth(text, computedFont); | ||
var outerWidth = textWidth; | ||
@@ -118,12 +147,20 @@ textPadding && (outerWidth += textPadding[1] + textPadding[3]); | ||
setCtx(ctx, 'textAlign', textAlign || 'left'); | ||
// Always set textAlign and textBase line, because it is difficute to calculate | ||
// textAlign from prevEl, and we dont sure whether textAlign will be reset if | ||
// font set happened. | ||
ctx.textAlign = textAlign; | ||
// Force baseline to be "middle". Otherwise, if using "top", the | ||
// text will offset downward a little bit in font "Microsoft YaHei". | ||
setCtx(ctx, 'textBaseline', 'middle'); | ||
ctx.textBaseline = 'middle'; | ||
// Always set shadowBlur and shadowOffset to avoid leak from displayable. | ||
setCtx(ctx, 'shadowBlur', style.textShadowBlur || 0); | ||
setCtx(ctx, 'shadowColor', style.textShadowColor || 'transparent'); | ||
setCtx(ctx, 'shadowOffsetX', style.textShadowOffsetX || 0); | ||
setCtx(ctx, 'shadowOffsetY', style.textShadowOffsetY || 0); | ||
for (var i = 0; i < SHADOW_STYLE_COMMON_PROPS.length; i++) { | ||
var propItem = SHADOW_STYLE_COMMON_PROPS[i]; | ||
var styleProp = propItem[0]; | ||
var ctxProp = propItem[1]; | ||
var val = style[styleProp]; | ||
if (!isPrevTextEl || val !== prevStyle[styleProp]) { | ||
ctx[ctxProp] = fixShadow(ctx, ctxProp, val || propItem[2]); | ||
} | ||
} | ||
@@ -134,2 +171,5 @@ // `textBaseline` is set as 'middle'. | ||
var textStrokeWidth = style.textStrokeWidth; | ||
var textStrokeWidthPrev = isPrevTextEl ? prevStyle.textStrokeWidth : null; | ||
var strokeWidthChanged = !isPrevTextEl || textStrokeWidth !== textStrokeWidthPrev; | ||
var strokeChanged = !isPrevTextEl || strokeWidthChanged || style.textStroke !== prevStyle.textStroke; | ||
var textStroke = getStroke(style.textStroke, textStrokeWidth); | ||
@@ -139,15 +179,29 @@ var textFill = getFill(style.textFill); | ||
if (textStroke) { | ||
setCtx(ctx, 'lineWidth', textStrokeWidth); | ||
setCtx(ctx, 'strokeStyle', textStroke); | ||
if (strokeWidthChanged) { | ||
ctx.lineWidth = textStrokeWidth; | ||
} | ||
if (strokeChanged) { | ||
ctx.strokeStyle = textStroke; | ||
} | ||
} | ||
if (textFill) { | ||
setCtx(ctx, 'fillStyle', textFill); | ||
if (!isPrevTextEl || style.textFill !== prevStyle.textFill || prevStyle.textBackgroundColor) { | ||
ctx.fillStyle = textFill; | ||
} | ||
} | ||
for (var i = 0; i < textLines.length; i++) { | ||
// Optimize simply, in most cases only one line exists. | ||
if (textLines.length === 1) { | ||
// Fill after stroke so the outline will not cover the main part. | ||
textStroke && ctx.strokeText(textLines[i], textX, textY); | ||
textFill && ctx.fillText(textLines[i], textX, textY); | ||
textY += lineHeight; | ||
textStroke && ctx.strokeText(textLines[0], textX, textY); | ||
textFill && ctx.fillText(textLines[0], textX, textY); | ||
} | ||
else { | ||
for (var i = 0; i < textLines.length; i++) { | ||
// Fill after stroke so the outline will not cover the main part. | ||
textStroke && ctx.strokeText(textLines[i], textX, textY); | ||
textFill && ctx.fillText(textLines[i], textX, textY); | ||
textY += lineHeight; | ||
} | ||
} | ||
} | ||
@@ -158,3 +212,3 @@ | ||
if (!contentBlock || hostEl.__dirty) { | ||
if (!contentBlock || hostEl.__dirtyText) { | ||
contentBlock = hostEl.__textCotentBlock = textContain.parseRichText(text, style); | ||
@@ -264,2 +318,3 @@ } | ||
var tokenStyle = style.rich[token.styleName] || {}; | ||
tokenStyle.text = token.text; | ||
@@ -330,3 +385,3 @@ // 'ctx.textBaseline' is always set as 'middle', for sake of | ||
// style: {textBackgroundColor, textBorderWidth, textBorderColor, textBorderRadius} | ||
// style: {textBackgroundColor, textBorderWidth, textBorderColor, textBorderRadius, text} | ||
// shape: {x, y, width, height} | ||
@@ -360,2 +415,15 @@ function drawBackground(hostEl, ctx, style, x, y, width, height) { | ||
setCtx(ctx, 'fillStyle', textBackgroundColor); | ||
if (style.fillOpacity != null) { | ||
var originalGlobalAlpha = ctx.globalAlpha; | ||
ctx.globalAlpha = style.fillOpacity * style.opacity; | ||
ctx.fill(); | ||
ctx.globalAlpha = originalGlobalAlpha; | ||
} | ||
else { | ||
ctx.fill(); | ||
} | ||
} | ||
else if (isFunction(textBackgroundColor)) { | ||
setCtx(ctx, 'fillStyle', textBackgroundColor(style)); | ||
ctx.fill(); | ||
@@ -377,3 +445,12 @@ } | ||
setCtx(ctx, 'strokeStyle', textBorderColor); | ||
ctx.stroke(); | ||
if (style.strokeOpacity != null) { | ||
var originalGlobalAlpha = ctx.globalAlpha; | ||
ctx.globalAlpha = style.strokeOpacity * style.opacity; | ||
ctx.stroke(); | ||
ctx.globalAlpha = originalGlobalAlpha; | ||
} | ||
else { | ||
ctx.stroke(); | ||
} | ||
} | ||
@@ -380,0 +457,0 @@ } |
@@ -40,2 +40,5 @@ /** | ||
// FIXME | ||
// Do not provide prevEl to `textHelper.renderText` for ctx prop cache, | ||
// but use `ctx.save()` and `ctx.restore()`. Because the cache for rect | ||
// text propably break the cache for its host elements. | ||
ctx.save(); | ||
@@ -42,0 +45,0 @@ |
@@ -118,3 +118,13 @@ import Displayable from './Displayable'; | ||
hasFill && path.fill(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); | ||
} | ||
} | ||
@@ -126,3 +136,13 @@ if (lineDash && ctxLineDash) { | ||
hasStroke && path.stroke(ctx); | ||
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); | ||
} | ||
} | ||
@@ -138,3 +158,3 @@ if (lineDash && ctxLineDash) { | ||
// Only restore transform when needs draw text. | ||
this.restoreTransform(ctx); | ||
this.restoreTransform(ctx); | ||
this.drawRectText(ctx, this.getBoundingRect()); | ||
@@ -248,3 +268,3 @@ } | ||
this.__dirty = true; | ||
this.__dirty = this.__dirtyText = true; | ||
@@ -251,0 +271,0 @@ this.__zr && this.__zr.refresh(); |
@@ -19,3 +19,3 @@ /** | ||
buildPath : function (ctx, shape, inBundle) { | ||
buildPath: function (ctx, shape, inBundle) { | ||
// Better stroking in ShapeBundle | ||
@@ -22,0 +22,0 @@ // Always do it may have performence issue ( fill may be 2x more cost) |
@@ -12,5 +12,4 @@ | ||
var Style = function (opts, host) { | ||
var Style = function (opts) { | ||
this.extendFrom(opts, false); | ||
this.host = host; | ||
}; | ||
@@ -67,7 +66,2 @@ | ||
/** | ||
* @type {module:zrender/graphic/Displayable} | ||
*/ | ||
host: null, | ||
/** | ||
* @type {string} | ||
@@ -88,2 +82,12 @@ */ | ||
/** | ||
* @type {number} | ||
*/ | ||
fillOpacity: null, | ||
/** | ||
* @type {number} | ||
*/ | ||
strokeOpacity: null, | ||
/** | ||
* @type {Array.<number>} | ||
@@ -90,0 +94,0 @@ */ |
@@ -36,4 +36,6 @@ import Displayable from './Displayable'; | ||
// Always bind style | ||
style.bind(ctx, this, prevEl); | ||
// Do not apply style.bind in Text node. Because the real bind job | ||
// is in textHelper.renderText, and performance of text render should | ||
// be considered. | ||
// style.bind(ctx, this, prevEl); | ||
@@ -46,3 +48,3 @@ if (!textHelper.needDrawText(text, style)) { | ||
textHelper.renderText(this, ctx, text, style); | ||
textHelper.renderText(this, ctx, text, style, null, prevEl); | ||
@@ -49,0 +51,0 @@ this.restoreTransform(ctx); |
@@ -5,2 +5,3 @@ import * as util from './core/util'; | ||
import Eventful from './mixin/Eventful'; | ||
import * as eventTool from './core/event'; | ||
@@ -26,6 +27,11 @@ var SILENT = 'silent'; | ||
zrByTouch: event.zrByTouch, | ||
which: event.which | ||
which: event.which, | ||
stop: stopEvent | ||
}; | ||
} | ||
function stopEvent(event) { | ||
eventTool.stop(this.event); | ||
} | ||
function EmptyProxy () {} | ||
@@ -32,0 +38,0 @@ EmptyProxy.prototype.dispose = function () {}; |
@@ -140,132 +140,156 @@ import Animator from '../animation/Animator'; | ||
*/ | ||
// TODO Return animation key | ||
// TODO Return animation key | ||
animateTo: function (target, time, delay, easing, callback, forceAnimate) { | ||
// animateTo(target, time, easing, callback); | ||
if (isString(delay)) { | ||
callback = easing; | ||
easing = delay; | ||
delay = 0; | ||
} | ||
// animateTo(target, time, delay, callback); | ||
else if (isFunction(easing)) { | ||
callback = easing; | ||
easing = 'linear'; | ||
delay = 0; | ||
} | ||
// animateTo(target, time, callback); | ||
else if (isFunction(delay)) { | ||
callback = delay; | ||
delay = 0; | ||
} | ||
// animateTo(target, callback) | ||
else if (isFunction(time)) { | ||
callback = time; | ||
time = 500; | ||
} | ||
// animateTo(target) | ||
else if (!time) { | ||
time = 500; | ||
} | ||
// Stop all previous animations | ||
this.stopAnimation(); | ||
this._animateToShallow('', this, target, time, delay); | ||
animateTo(this, target, time, delay, easing, callback, forceAnimate); | ||
}, | ||
// Animators may be removed immediately after start | ||
// if there is nothing to animate | ||
var animators = this.animators.slice(); | ||
var count = animators.length; | ||
function done() { | ||
count--; | ||
if (!count) { | ||
callback && callback(); | ||
} | ||
} | ||
/** | ||
* Animate from the target state to current state. | ||
* The params and the return value are the same as `this.animateTo`. | ||
*/ | ||
animateFrom: function (target, time, delay, easing, callback, forceAnimate) { | ||
animateTo(this, target, time, delay, easing, callback, forceAnimate, true); | ||
} | ||
}; | ||
// No animators. This should be checked before animators[i].start(), | ||
// because 'done' may be executed immediately if no need to animate. | ||
function animateTo(animatable, target, time, delay, easing, callback, forceAnimate, reverse) { | ||
// animateTo(target, time, easing, callback); | ||
if (isString(delay)) { | ||
callback = easing; | ||
easing = delay; | ||
delay = 0; | ||
} | ||
// animateTo(target, time, delay, callback); | ||
else if (isFunction(easing)) { | ||
callback = easing; | ||
easing = 'linear'; | ||
delay = 0; | ||
} | ||
// animateTo(target, time, callback); | ||
else if (isFunction(delay)) { | ||
callback = delay; | ||
delay = 0; | ||
} | ||
// animateTo(target, callback) | ||
else if (isFunction(time)) { | ||
callback = time; | ||
time = 500; | ||
} | ||
// animateTo(target) | ||
else if (!time) { | ||
time = 500; | ||
} | ||
// Stop all previous animations | ||
animatable.stopAnimation(); | ||
animateToShallow(animatable, '', animatable, target, time, delay, reverse); | ||
// Animators may be removed immediately after start | ||
// if there is nothing to animate | ||
var animators = animatable.animators.slice(); | ||
var count = animators.length; | ||
function done() { | ||
count--; | ||
if (!count) { | ||
callback && callback(); | ||
} | ||
// Start after all animators created | ||
// Incase any animator is done immediately when all animation properties are not changed | ||
for (var i = 0; i < animators.length; i++) { | ||
animators[i] | ||
.done(done) | ||
.start(easing, forceAnimate); | ||
} | ||
// No animators. This should be checked before animators[i].start(), | ||
// because 'done' may be executed immediately if no need to animate. | ||
if (!count) { | ||
callback && callback(); | ||
} | ||
// Start after all animators created | ||
// Incase any animator is done immediately when all animation properties are not changed | ||
for (var i = 0; i < animators.length; i++) { | ||
animators[i] | ||
.done(done) | ||
.start(easing, forceAnimate); | ||
} | ||
} | ||
/** | ||
* @param {string} path='' | ||
* @param {Object} source=animatable | ||
* @param {Object} target | ||
* @param {number} [time=500] | ||
* @param {number} [delay=0] | ||
* @param {boolean} [reverse] If `true`, animate | ||
* from the `target` to current state. | ||
* | ||
* @example | ||
* // Animate position | ||
* el._animateToShallow({ | ||
* position: [10, 10] | ||
* }) | ||
* | ||
* // Animate shape, style and position in 100ms, delayed 100ms | ||
* el._animateToShallow({ | ||
* shape: { | ||
* width: 500 | ||
* }, | ||
* style: { | ||
* fill: 'red' | ||
* } | ||
* position: [10, 10] | ||
* }, 100, 100) | ||
*/ | ||
function animateToShallow(animatable, path, source, target, time, delay, reverse) { | ||
var objShallow = {}; | ||
var propertyCount = 0; | ||
for (var name in target) { | ||
if (!target.hasOwnProperty(name)) { | ||
continue; | ||
} | ||
}, | ||
/** | ||
* @private | ||
* @param {string} path='' | ||
* @param {Object} source=this | ||
* @param {Object} target | ||
* @param {number} [time=500] | ||
* @param {number} [delay=0] | ||
* | ||
* @example | ||
* // Animate position | ||
* el._animateToShallow({ | ||
* position: [10, 10] | ||
* }) | ||
* | ||
* // Animate shape, style and position in 100ms, delayed 100ms | ||
* el._animateToShallow({ | ||
* shape: { | ||
* width: 500 | ||
* }, | ||
* style: { | ||
* fill: 'red' | ||
* } | ||
* position: [10, 10] | ||
* }, 100, 100) | ||
*/ | ||
_animateToShallow: function (path, source, target, time, delay) { | ||
var objShallow = {}; | ||
var propertyCount = 0; | ||
for (var name in target) { | ||
if (!target.hasOwnProperty(name)) { | ||
continue; | ||
if (source[name] != null) { | ||
if (isObject(target[name]) && !isArrayLike(target[name])) { | ||
animateToShallow( | ||
animatable, | ||
path ? path + '.' + name : name, | ||
source[name], | ||
target[name], | ||
time, | ||
delay, | ||
reverse | ||
); | ||
} | ||
if (source[name] != null) { | ||
if (isObject(target[name]) && !isArrayLike(target[name])) { | ||
this._animateToShallow( | ||
path ? path + '.' + name : name, | ||
source[name], | ||
target[name], | ||
time, | ||
delay | ||
); | ||
else { | ||
if (reverse) { | ||
objShallow[name] = source[name]; | ||
setAttrByPath(animatable, path, name, target[name]); | ||
} | ||
else { | ||
objShallow[name] = target[name]; | ||
propertyCount++; | ||
} | ||
propertyCount++; | ||
} | ||
else if (target[name] != null) { | ||
// Attr directly if not has property | ||
// FIXME, if some property not needed for element ? | ||
if (!path) { | ||
this.attr(name, target[name]); | ||
} | ||
else { // Shape or style | ||
var props = {}; | ||
props[path] = {}; | ||
props[path][name] = target[name]; | ||
this.attr(props); | ||
} | ||
} | ||
} | ||
if (propertyCount > 0) { | ||
this.animate(path, false) | ||
.when(time == null ? 500 : time, objShallow) | ||
.delay(delay || 0); | ||
else if (target[name] != null && !reverse) { | ||
setAttrByPath(animatable, path, name, target[name]); | ||
} | ||
} | ||
return this; | ||
if (propertyCount > 0) { | ||
animatable.animate(path, false) | ||
.when(time == null ? 500 : time, objShallow) | ||
.delay(delay || 0); | ||
} | ||
}; | ||
} | ||
function setAttrByPath(el, path, name, value) { | ||
// Attr directly if not has property | ||
// FIXME, if some property not needed for element ? | ||
if (!path) { | ||
el.attr(name, value); | ||
} | ||
else { | ||
// Only support set shape or style | ||
var props = {}; | ||
props[path] = {}; | ||
props[path][name] = value; | ||
el.attr(props); | ||
} | ||
} | ||
export default Animatable; |
/** | ||
* 事件扩展 | ||
* Event Mixin | ||
* @module zrender/mixin/Eventful | ||
@@ -11,8 +11,22 @@ * @author Kener (@Kener-林峰, kener.linfeng@gmail.com) | ||
/** | ||
* 事件分发器 | ||
* Event dispatcher. | ||
* | ||
* @alias module:zrender/mixin/Eventful | ||
* @constructor | ||
* @param {Object} [eventProcessor] The object eventProcessor is the scope when | ||
* `eventProcessor.xxx` called. | ||
* @param {Function} [eventProcessor.normalizeQuery] | ||
* param: {string|Object} Raw query. | ||
* return: {string|Object} Normalized query. | ||
* @param {Function} [eventProcessor.filter] Event will be dispatched only | ||
* if it returns `true`. | ||
* param: {string} eventType | ||
* param: {string|Object} query | ||
* return: {boolean} | ||
* @param {Function} [eventProcessor.afterTrigger] Call after all handlers called. | ||
* param: {string} eventType | ||
*/ | ||
var Eventful = function () { | ||
var Eventful = function (eventProcessor) { | ||
this._$handlers = {}; | ||
this._$eventProcessor = eventProcessor; | ||
}; | ||
@@ -25,11 +39,18 @@ | ||
/** | ||
* 单次触发绑定,trigger后销毁 | ||
* The handler can only be triggered once, then removed. | ||
* | ||
* @param {string} event 事件名 | ||
* @param {Function} handler 响应函数 | ||
* @param {string} event The event name. | ||
* @param {string|Object} [query] Condition used on event filter. | ||
* @param {Function} handler The event handler. | ||
* @param {Object} context | ||
*/ | ||
one: function (event, handler, context) { | ||
one: function (event, query, handler, context) { | ||
var _h = this._$handlers; | ||
if (typeof query === 'function') { | ||
context = handler; | ||
handler = query; | ||
query = null; | ||
} | ||
if (!handler || !event) { | ||
@@ -39,2 +60,4 @@ return this; | ||
query = normalizeQuery(this, query); | ||
if (!_h[event]) { | ||
@@ -53,2 +76,3 @@ _h[event] = []; | ||
one: true, | ||
query: query, | ||
ctx: context || this | ||
@@ -61,10 +85,18 @@ }); | ||
/** | ||
* 绑定事件 | ||
* @param {string} event 事件名 | ||
* @param {Function} handler 事件处理函数 | ||
* Bind a handler. | ||
* | ||
* @param {string} event The event name. | ||
* @param {string|Object} [query] Condition used on event filter. | ||
* @param {Function} handler The event handler. | ||
* @param {Object} [context] | ||
*/ | ||
on: function (event, handler, context) { | ||
on: function (event, query, handler, context) { | ||
var _h = this._$handlers; | ||
if (typeof query === 'function') { | ||
context = handler; | ||
handler = query; | ||
query = null; | ||
} | ||
if (!handler || !event) { | ||
@@ -74,2 +106,4 @@ return this; | ||
query = normalizeQuery(this, query); | ||
if (!_h[event]) { | ||
@@ -88,2 +122,3 @@ _h[event] = []; | ||
one: false, | ||
query: query, | ||
ctx: context || this | ||
@@ -96,3 +131,4 @@ }); | ||
/** | ||
* 是否绑定了事件 | ||
* Whether any handler has bound. | ||
* | ||
* @param {string} event | ||
@@ -107,5 +143,6 @@ * @return {boolean} | ||
/** | ||
* 解绑事件 | ||
* @param {string} event 事件名 | ||
* @param {Function} [handler] 事件处理函数 | ||
* Unbind a event. | ||
* | ||
* @param {string} event The event name. | ||
* @param {Function} [handler] The event handler. | ||
*/ | ||
@@ -124,3 +161,3 @@ off: function (event, handler) { | ||
for (var i = 0, l = _h[event].length; i < l; i++) { | ||
if (_h[event][i]['h'] != handler) { | ||
if (_h[event][i].h !== handler) { | ||
newList.push(_h[event][i]); | ||
@@ -144,8 +181,11 @@ } | ||
/** | ||
* 事件分发 | ||
* Dispatch a event. | ||
* | ||
* @param {string} type 事件类型 | ||
* @param {string} type The event name. | ||
*/ | ||
trigger: function (type) { | ||
if (this._$handlers[type]) { | ||
var _h = this._$handlers[type]; | ||
var eventProcessor = this._$eventProcessor; | ||
if (_h) { | ||
var args = arguments; | ||
@@ -158,23 +198,32 @@ var argLen = args.length; | ||
var _h = this._$handlers[type]; | ||
var len = _h.length; | ||
for (var i = 0; i < len;) { | ||
var hItem = _h[i]; | ||
if (eventProcessor | ||
&& eventProcessor.filter | ||
&& hItem.query != null | ||
&& !eventProcessor.filter(type, hItem.query) | ||
) { | ||
i++; | ||
continue; | ||
} | ||
// Optimize advise from backbone | ||
switch (argLen) { | ||
case 1: | ||
_h[i]['h'].call(_h[i]['ctx']); | ||
hItem.h.call(hItem.ctx); | ||
break; | ||
case 2: | ||
_h[i]['h'].call(_h[i]['ctx'], args[1]); | ||
hItem.h.call(hItem.ctx, args[1]); | ||
break; | ||
case 3: | ||
_h[i]['h'].call(_h[i]['ctx'], args[1], args[2]); | ||
hItem.h.call(hItem.ctx, args[1], args[2]); | ||
break; | ||
default: | ||
// have more than 2 given arguments | ||
_h[i]['h'].apply(_h[i]['ctx'], args); | ||
hItem.h.apply(hItem.ctx, args); | ||
break; | ||
} | ||
if (_h[i]['one']) { | ||
if (hItem.one) { | ||
_h.splice(i, 1); | ||
@@ -189,2 +238,5 @@ len--; | ||
eventProcessor && eventProcessor.afterTrigger | ||
&& eventProcessor.afterTrigger(type); | ||
return this; | ||
@@ -194,7 +246,11 @@ }, | ||
/** | ||
* 带有context的事件分发, 最后一个参数是事件回调的context | ||
* @param {string} type 事件类型 | ||
* Dispatch a event with context, which is specified at the last parameter. | ||
* | ||
* @param {string} type The event name. | ||
*/ | ||
triggerWithContext: function (type) { | ||
if (this._$handlers[type]) { | ||
var _h = this._$handlers[type]; | ||
var eventProcessor = this._$eventProcessor; | ||
if (_h) { | ||
var args = arguments; | ||
@@ -208,23 +264,32 @@ var argLen = args.length; | ||
var _h = this._$handlers[type]; | ||
var len = _h.length; | ||
for (var i = 0; i < len;) { | ||
var hItem = _h[i]; | ||
if (eventProcessor | ||
&& eventProcessor.filter | ||
&& hItem.query != null | ||
&& !eventProcessor.filter(type, hItem.query) | ||
) { | ||
i++; | ||
continue; | ||
} | ||
// Optimize advise from backbone | ||
switch (argLen) { | ||
case 1: | ||
_h[i]['h'].call(ctx); | ||
hItem.h.call(ctx); | ||
break; | ||
case 2: | ||
_h[i]['h'].call(ctx, args[1]); | ||
hItem.h.call(ctx, args[1]); | ||
break; | ||
case 3: | ||
_h[i]['h'].call(ctx, args[1], args[2]); | ||
hItem.h.call(ctx, args[1], args[2]); | ||
break; | ||
default: | ||
// have more than 2 given arguments | ||
_h[i]['h'].apply(ctx, args); | ||
hItem.h.apply(ctx, args); | ||
break; | ||
} | ||
if (_h[i]['one']) { | ||
if (hItem.one) { | ||
_h.splice(i, 1); | ||
@@ -239,2 +304,5 @@ len--; | ||
eventProcessor && eventProcessor.afterTrigger | ||
&& eventProcessor.afterTrigger(type); | ||
return this; | ||
@@ -244,3 +312,14 @@ } | ||
// 对象可以通过 onxxxx 绑定事件 | ||
function normalizeQuery(host, query) { | ||
var eventProcessor = host._$eventProcessor; | ||
if (query != null && eventProcessor && eventProcessor.normalizeQuery) { | ||
query = eventProcessor.normalizeQuery(query); | ||
} | ||
return query; | ||
} | ||
// ---------------------- | ||
// The events in zrender | ||
// ---------------------- | ||
/** | ||
@@ -247,0 +326,0 @@ * @event module:zrender/mixin/Eventful#onclick |
@@ -72,2 +72,3 @@ /** | ||
var scaleTmp = []; | ||
transformableProto.updateTransform = function () { | ||
@@ -105,2 +106,16 @@ var parent = this.parent; | ||
var globalScaleRatio = this.globalScaleRatio; | ||
if (globalScaleRatio != null && globalScaleRatio !== 1) { | ||
this.getGlobalScale(scaleTmp); | ||
var relX = scaleTmp[0] < 0 ? -1 : 1; | ||
var relY = scaleTmp[1] < 0 ? -1 : 1; | ||
var sx = ((scaleTmp[0] - relX) * globalScaleRatio + relX) / scaleTmp[0] || 0; | ||
var sy = ((scaleTmp[1] - relY) * globalScaleRatio + relY) / scaleTmp[1] || 0; | ||
m[0] *= sx; | ||
m[1] *= sx; | ||
m[2] *= sy; | ||
m[3] *= sy; | ||
} | ||
this.invTransform = this.invTransform || matrix.create(); | ||
@@ -135,17 +150,9 @@ matrix.invert(this.invTransform, m); | ||
var tmpTransform = []; | ||
var originTransform = matrix.create(); | ||
/** | ||
* 分解`transform`矩阵到`position`, `rotation`, `scale` | ||
*/ | ||
transformableProto.decomposeTransform = function () { | ||
if (!this.transform) { | ||
transformableProto.setLocalTransform = function (m) { | ||
if (!m) { | ||
// TODO return or set identity? | ||
return; | ||
} | ||
var parent = this.parent; | ||
var m = this.transform; | ||
if (parent && parent.transform) { | ||
// Get local transform and decompose them to position, scale, rotation | ||
matrix.mul(tmpTransform, parent.invTransform, m); | ||
m = tmpTransform; | ||
} | ||
var sx = m[0] * m[0] + m[1] * m[1]; | ||
@@ -167,2 +174,3 @@ var sy = m[2] * m[2] + m[3] * m[3]; | ||
} | ||
position[0] = m[4]; | ||
@@ -174,3 +182,29 @@ position[1] = m[5]; | ||
}; | ||
/** | ||
* 分解`transform`矩阵到`position`, `rotation`, `scale` | ||
*/ | ||
transformableProto.decomposeTransform = function () { | ||
if (!this.transform) { | ||
return; | ||
} | ||
var parent = this.parent; | ||
var m = this.transform; | ||
if (parent && parent.transform) { | ||
// Get local transform and decompose them to position, scale, rotation | ||
matrix.mul(tmpTransform, parent.invTransform, m); | ||
m = tmpTransform; | ||
} | ||
var origin = this.origin; | ||
if (origin && (origin[0] || origin[1])) { | ||
originTransform[4] = origin[0]; | ||
originTransform[5] = origin[1]; | ||
matrix.mul(tmpTransform, m, originTransform); | ||
tmpTransform[4] -= origin[0]; | ||
tmpTransform[5] -= origin[1]; | ||
m = tmpTransform; | ||
} | ||
this.setLocalTransform(m); | ||
}; | ||
/** | ||
@@ -180,16 +214,19 @@ * Get global scale | ||
*/ | ||
transformableProto.getGlobalScale = function () { | ||
transformableProto.getGlobalScale = function (out) { | ||
var m = this.transform; | ||
out = out || []; | ||
if (!m) { | ||
return [1, 1]; | ||
out[0] = 1; | ||
out[1] = 1; | ||
return out; | ||
} | ||
var sx = Math.sqrt(m[0] * m[0] + m[1] * m[1]); | ||
var sy = Math.sqrt(m[2] * m[2] + m[3] * m[3]); | ||
out[0] = Math.sqrt(m[0] * m[0] + m[1] * m[1]); | ||
out[1] = Math.sqrt(m[2] * m[2] + m[3] * m[3]); | ||
if (m[0] < 0) { | ||
sx = -sx; | ||
out[0] = -out[0]; | ||
} | ||
if (m[3] < 0) { | ||
sy = -sy; | ||
out[1] = -out[1]; | ||
} | ||
return [sx, sy]; | ||
return out; | ||
}; | ||
@@ -196,0 +233,0 @@ /** |
@@ -287,8 +287,13 @@ import {devicePixelRatio} from './config'; | ||
style: el.style, | ||
shape: el.shape | ||
shape: el.shape, | ||
z: el.z, | ||
z2: el.z2, | ||
silent: el.silent | ||
}); | ||
elMirror.__from = el; | ||
el.__hoverMir = elMirror; | ||
elMirror.setStyle(hoverStyle); | ||
hoverStyle && elMirror.setStyle(hoverStyle); | ||
this._hoverElements.push(elMirror); | ||
return elMirror; | ||
}, | ||
@@ -359,2 +364,3 @@ | ||
} | ||
hoverLayer.ctx.restore(); | ||
@@ -448,3 +454,3 @@ }, | ||
this._doPaintEl(el, layer, paintAll, scope); | ||
el.__dirty = false; | ||
el.__dirty = el.__dirtyText = false; | ||
@@ -451,0 +457,0 @@ if (useTimer) { |
@@ -64,3 +64,3 @@ // TODO | ||
function bindStyle(svgEl, style, isText) { | ||
function bindStyle(svgEl, style, isText, el) { | ||
if (pathHasFill(style, isText)) { | ||
@@ -90,3 +90,3 @@ var fill = isText ? style.textFill : style.fill; | ||
attr(svgEl, 'fill', fill); | ||
attr(svgEl, 'fill-opacity', style.opacity); | ||
attr(svgEl, 'fill-opacity', style.fillOpacity != null ? style.fillOpacity * style.opacity : style.opacity); | ||
} | ||
@@ -105,3 +105,3 @@ else { | ||
var strokeScale = !isText && style.strokeNoScale | ||
? style.host.getLineScale() | ||
? el.getLineScale() | ||
: 1; | ||
@@ -111,3 +111,3 @@ attr(svgEl, 'stroke-width', strokeWidth / strokeScale); | ||
attr(svgEl, 'paint-order', isText ? 'stroke' : 'fill'); | ||
attr(svgEl, 'stroke-opacity', style.opacity); | ||
attr(svgEl, 'stroke-opacity', style.strokeOpacity != null ? style.strokeOpacity : style.opacity); | ||
var lineDash = style.lineDash; | ||
@@ -275,3 +275,3 @@ if (lineDash) { | ||
bindStyle(svgEl, style); | ||
bindStyle(svgEl, style, false, el); | ||
setTransform(svgEl, el.transform); | ||
@@ -412,3 +412,3 @@ | ||
bindStyle(textSvgEl, style, true); | ||
bindStyle(textSvgEl, style, true, el); | ||
if (el instanceof Text || el.style.transformText) { | ||
@@ -428,2 +428,3 @@ // Transform text with element | ||
rect.y = pos[1]; | ||
el.transform = matrix.identity(matrix.create()); | ||
} | ||
@@ -444,3 +445,6 @@ | ||
// Apply textRotate to element matrix | ||
matrix.rotate(transform, el.transform, rotate); | ||
matrix.rotate(transform, transform, rotate); | ||
var pos = [el.transform[4], el.transform[5]]; | ||
matrix.translate(transform, transform, pos); | ||
setTransform(textSvgEl, transform); | ||
@@ -467,3 +471,3 @@ } | ||
var dy = 0; | ||
if (verticalAlign === 'baseline') { | ||
if (verticalAlign === 'after-edge') { | ||
dy = -textRect.height + lineHeight; | ||
@@ -527,3 +531,3 @@ textPadding && (dy -= textPadding[2]); | ||
else if (verticalAlign === 'bottom') { | ||
return 'baseline'; | ||
return 'after-edge'; | ||
} | ||
@@ -530,0 +534,0 @@ else { |
@@ -6,6 +6,6 @@ import Path from '../graphic/Path'; | ||
// command chars | ||
var cc = [ | ||
'm', 'M', 'l', 'L', 'v', 'V', 'h', 'H', 'z', 'Z', | ||
'c', 'C', 'q', 'Q', 't', 'T', 's', 'S', 'a', 'A' | ||
]; | ||
// var cc = [ | ||
// 'm', 'M', 'l', 'L', 'v', 'V', 'h', 'H', 'z', 'Z', | ||
// 'c', 'C', 'q', 'Q', 't', 'T', 's', 'S', 'a', 'A' | ||
// ]; | ||
@@ -80,24 +80,39 @@ var mathSqrt = Math.sqrt; | ||
var commandReg = /([mlvhzcqtsa])([^mlvhzcqtsa]*)/ig; | ||
// Consider case: | ||
// (1) delimiter can be comma or space, where continuous commas | ||
// or spaces should be seen as one comma. | ||
// (2) value can be like: | ||
// '2e-4', 'l.5.9' (ignore 0), 'M-10-10', 'l-2.43e-1,34.9983', | ||
// 'l-.5E1,54', '121-23-44-11' (no delimiter) | ||
var numberReg = /-?([0-9]*\.)?[0-9]+([eE]-?[0-9]+)?/g; | ||
// var valueSplitReg = /[\s,]+/; | ||
function createPathProxyFromString(data) { | ||
if (!data) { | ||
return []; | ||
return new PathProxy(); | ||
} | ||
// command string | ||
var cs = data.replace(/-/g, ' -') | ||
.replace(/ /g, ' ') | ||
.replace(/ /g, ',') | ||
.replace(/,,/g, ','); | ||
// var data = data.replace(/-/g, ' -') | ||
// .replace(/ /g, ' ') | ||
// .replace(/ /g, ',') | ||
// .replace(/,,/g, ','); | ||
var n; | ||
// var n; | ||
// create pipes so that we can split the data | ||
for (n = 0; n < cc.length; n++) { | ||
cs = cs.replace(new RegExp(cc[n], 'g'), '|' + cc[n]); | ||
} | ||
// for (n = 0; n < cc.length; n++) { | ||
// cs = cs.replace(new RegExp(cc[n], 'g'), '|' + cc[n]); | ||
// } | ||
// data = data.replace(/-/g, ',-'); | ||
// create array | ||
var arr = cs.split('|'); | ||
// var arr = cs.split('|'); | ||
// init context point | ||
var cpx = 0; | ||
var cpy = 0; | ||
var subpathX = cpx; | ||
var subpathY = cpy; | ||
var prevCmd; | ||
@@ -107,21 +122,32 @@ var path = new PathProxy(); | ||
var prevCmd; | ||
for (n = 1; n < arr.length; n++) { | ||
var str = arr[n]; | ||
var c = str.charAt(0); | ||
var off = 0; | ||
var p = str.slice(1).replace(/e,-/g, 'e-').split(','); | ||
// commandReg.lastIndex = 0; | ||
// var cmdResult; | ||
// while ((cmdResult = commandReg.exec(data)) != null) { | ||
// var cmdStr = cmdResult[1]; | ||
// var cmdContent = cmdResult[2]; | ||
var cmdList = data.match(commandReg); | ||
for (var l = 0; l < cmdList.length; l++) { | ||
var cmdText = cmdList[l]; | ||
var cmdStr = cmdText.charAt(0); | ||
var cmd; | ||
if (p.length > 0 && p[0] === '') { | ||
p.shift(); | ||
} | ||
// String#split is faster a little bit than String#replace or RegExp#exec. | ||
// var p = cmdContent.split(valueSplitReg); | ||
// var pLen = 0; | ||
// for (var i = 0; i < p.length; i++) { | ||
// // '' and other invalid str => NaN | ||
// var val = parseFloat(p[i]); | ||
// !isNaN(val) && (p[pLen++] = val); | ||
// } | ||
for (var i = 0; i < p.length; i++) { | ||
var p = cmdText.match(numberReg) || []; | ||
var pLen = p.length; | ||
for (var i = 0; i < pLen; i++) { | ||
p[i] = parseFloat(p[i]); | ||
} | ||
while (off < p.length && !isNaN(p[off])) { | ||
if (isNaN(p[0])) { | ||
break; | ||
} | ||
var off = 0; | ||
while (off < pLen) { | ||
var ctlPtx; | ||
@@ -140,3 +166,3 @@ var ctlPty; | ||
// convert l, H, h, V, and v to L | ||
switch (c) { | ||
switch (cmdStr) { | ||
case 'l': | ||
@@ -159,3 +185,5 @@ cpx += p[off++]; | ||
path.addData(cmd, cpx, cpy); | ||
c = 'l'; | ||
subpathX = cpx; | ||
subpathY = cpy; | ||
cmdStr = 'l'; | ||
break; | ||
@@ -167,3 +195,5 @@ case 'M': | ||
path.addData(cmd, cpx, cpy); | ||
c = 'L'; | ||
subpathX = cpx; | ||
subpathY = cpy; | ||
cmdStr = 'L'; | ||
break; | ||
@@ -318,5 +348,8 @@ case 'h': | ||
if (c === 'z' || c === 'Z') { | ||
if (cmdStr === 'z' || cmdStr === 'Z') { | ||
cmd = CMD.Z; | ||
path.addData(cmd); | ||
// z may be in the middle of the path. | ||
cpx = subpathX; | ||
cpy = subpathY; | ||
} | ||
@@ -323,0 +356,0 @@ |
@@ -31,3 +31,3 @@ /*! | ||
*/ | ||
export var version = '4.0.4'; | ||
export var version = '4.0.5'; | ||
@@ -278,4 +278,5 @@ /** | ||
if (this.painter.addHover) { | ||
this.painter.addHover(el, style); | ||
var elMirror = this.painter.addHover(el, style); | ||
this.refreshHover(); | ||
return elMirror; | ||
} | ||
@@ -282,0 +283,0 @@ }, |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No contributors or author data
MaintenancePackage does not specify a list of contributors or an author in package.json.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
3272491
221
65291
0
2