123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527 |
- /*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
- var zrUtil = require("zrender/lib/core/util");
- var pathTool = require("zrender/lib/tool/path");
- var colorTool = require("zrender/lib/tool/color");
- var matrix = require("zrender/lib/core/matrix");
- var vector = require("zrender/lib/core/vector");
- var Path = require("zrender/lib/graphic/Path");
- var Transformable = require("zrender/lib/mixin/Transformable");
- var ZImage = require("zrender/lib/graphic/Image");
- exports.Image = ZImage;
- var Group = require("zrender/lib/container/Group");
- exports.Group = Group;
- var Text = require("zrender/lib/graphic/Text");
- exports.Text = Text;
- var Circle = require("zrender/lib/graphic/shape/Circle");
- exports.Circle = Circle;
- var Sector = require("zrender/lib/graphic/shape/Sector");
- exports.Sector = Sector;
- var Ring = require("zrender/lib/graphic/shape/Ring");
- exports.Ring = Ring;
- var Polygon = require("zrender/lib/graphic/shape/Polygon");
- exports.Polygon = Polygon;
- var Polyline = require("zrender/lib/graphic/shape/Polyline");
- exports.Polyline = Polyline;
- var Rect = require("zrender/lib/graphic/shape/Rect");
- exports.Rect = Rect;
- var Line = require("zrender/lib/graphic/shape/Line");
- exports.Line = Line;
- var BezierCurve = require("zrender/lib/graphic/shape/BezierCurve");
- exports.BezierCurve = BezierCurve;
- var Arc = require("zrender/lib/graphic/shape/Arc");
- exports.Arc = Arc;
- var CompoundPath = require("zrender/lib/graphic/CompoundPath");
- exports.CompoundPath = CompoundPath;
- var LinearGradient = require("zrender/lib/graphic/LinearGradient");
- exports.LinearGradient = LinearGradient;
- var RadialGradient = require("zrender/lib/graphic/RadialGradient");
- exports.RadialGradient = RadialGradient;
- var BoundingRect = require("zrender/lib/core/BoundingRect");
- exports.BoundingRect = BoundingRect;
- var IncrementalDisplayable = require("zrender/lib/graphic/IncrementalDisplayable");
- exports.IncrementalDisplayable = IncrementalDisplayable;
- var subPixelOptimizeUtil = require("zrender/lib/graphic/helper/subPixelOptimize");
- /*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
- var mathMax = Math.max;
- var mathMin = Math.min;
- var EMPTY_OBJ = {};
- var Z2_EMPHASIS_LIFT = 1; // key: label model property nane, value: style property name.
- var CACHED_LABEL_STYLE_PROPERTIES = {
- color: 'textFill',
- textBorderColor: 'textStroke',
- textBorderWidth: 'textStrokeWidth'
- };
- var EMPHASIS = 'emphasis';
- var NORMAL = 'normal'; // Reserve 0 as default.
- var _highlightNextDigit = 1;
- var _highlightKeyMap = {};
- var _customShapeMap = {};
- /**
- * Extend shape with parameters
- */
- function extendShape(opts) {
- return Path.extend(opts);
- }
- /**
- * Extend path
- */
- function extendPath(pathData, opts) {
- return pathTool.extendFromString(pathData, opts);
- }
- /**
- * Register a user defined shape.
- * The shape class can be fetched by `getShapeClass`
- * This method will overwrite the registered shapes, including
- * the registered built-in shapes, if using the same `name`.
- * The shape can be used in `custom series` and
- * `graphic component` by declaring `{type: name}`.
- *
- * @param {string} name
- * @param {Object} ShapeClass Can be generated by `extendShape`.
- */
- function registerShape(name, ShapeClass) {
- _customShapeMap[name] = ShapeClass;
- }
- /**
- * Find shape class registered by `registerShape`. Usually used in
- * fetching user defined shape.
- *
- * [Caution]:
- * (1) This method **MUST NOT be used inside echarts !!!**, unless it is prepared
- * to use user registered shapes.
- * Because the built-in shape (see `getBuiltInShape`) will be registered by
- * `registerShape` by default. That enables users to get both built-in
- * shapes as well as the shapes belonging to themsleves. But users can overwrite
- * the built-in shapes by using names like 'circle', 'rect' via calling
- * `registerShape`. So the echarts inner featrues should not fetch shapes from here
- * in case that it is overwritten by users, except that some features, like
- * `custom series`, `graphic component`, do it deliberately.
- *
- * (2) In the features like `custom series`, `graphic component`, the user input
- * `{tpye: 'xxx'}` does not only specify shapes but also specify other graphic
- * elements like `'group'`, `'text'`, `'image'` or event `'path'`. Those names
- * are reserved names, that is, if some user register a shape named `'image'`,
- * the shape will not be used. If we intending to add some more reserved names
- * in feature, that might bring break changes (disable some existing user shape
- * names). But that case probably rearly happen. So we dont make more mechanism
- * to resolve this issue here.
- *
- * @param {string} name
- * @return {Object} The shape class. If not found, return nothing.
- */
- function getShapeClass(name) {
- if (_customShapeMap.hasOwnProperty(name)) {
- return _customShapeMap[name];
- }
- }
- /**
- * Create a path element from path data string
- * @param {string} pathData
- * @param {Object} opts
- * @param {module:zrender/core/BoundingRect} rect
- * @param {string} [layout=cover] 'center' or 'cover'
- */
- function makePath(pathData, opts, rect, layout) {
- var path = pathTool.createFromString(pathData, opts);
- if (rect) {
- if (layout === 'center') {
- rect = centerGraphic(rect, path.getBoundingRect());
- }
- resizePath(path, rect);
- }
- return path;
- }
- /**
- * Create a image element from image url
- * @param {string} imageUrl image url
- * @param {Object} opts options
- * @param {module:zrender/core/BoundingRect} rect constrain rect
- * @param {string} [layout=cover] 'center' or 'cover'
- */
- function makeImage(imageUrl, rect, layout) {
- var path = new ZImage({
- style: {
- image: imageUrl,
- x: rect.x,
- y: rect.y,
- width: rect.width,
- height: rect.height
- },
- onload: function (img) {
- if (layout === 'center') {
- var boundingRect = {
- width: img.width,
- height: img.height
- };
- path.setStyle(centerGraphic(rect, boundingRect));
- }
- }
- });
- return path;
- }
- /**
- * Get position of centered element in bounding box.
- *
- * @param {Object} rect element local bounding box
- * @param {Object} boundingRect constraint bounding box
- * @return {Object} element position containing x, y, width, and height
- */
- function centerGraphic(rect, boundingRect) {
- // Set rect to center, keep width / height ratio.
- var aspect = boundingRect.width / boundingRect.height;
- var width = rect.height * aspect;
- var height;
- if (width <= rect.width) {
- height = rect.height;
- } else {
- width = rect.width;
- height = width / aspect;
- }
- var cx = rect.x + rect.width / 2;
- var cy = rect.y + rect.height / 2;
- return {
- x: cx - width / 2,
- y: cy - height / 2,
- width: width,
- height: height
- };
- }
- var mergePath = pathTool.mergePath;
- /**
- * Resize a path to fit the rect
- * @param {module:zrender/graphic/Path} path
- * @param {Object} rect
- */
- function resizePath(path, rect) {
- if (!path.applyTransform) {
- return;
- }
- var pathRect = path.getBoundingRect();
- var m = pathRect.calculateTransform(rect);
- path.applyTransform(m);
- }
- /**
- * Sub pixel optimize line for canvas
- *
- * @param {Object} param
- * @param {Object} [param.shape]
- * @param {number} [param.shape.x1]
- * @param {number} [param.shape.y1]
- * @param {number} [param.shape.x2]
- * @param {number} [param.shape.y2]
- * @param {Object} [param.style]
- * @param {number} [param.style.lineWidth]
- * @return {Object} Modified param
- */
- function subPixelOptimizeLine(param) {
- subPixelOptimizeUtil.subPixelOptimizeLine(param.shape, param.shape, param.style);
- return param;
- }
- /**
- * Sub pixel optimize rect for canvas
- *
- * @param {Object} param
- * @param {Object} [param.shape]
- * @param {number} [param.shape.x]
- * @param {number} [param.shape.y]
- * @param {number} [param.shape.width]
- * @param {number} [param.shape.height]
- * @param {Object} [param.style]
- * @param {number} [param.style.lineWidth]
- * @return {Object} Modified param
- */
- function subPixelOptimizeRect(param) {
- subPixelOptimizeUtil.subPixelOptimizeRect(param.shape, param.shape, param.style);
- return param;
- }
- /**
- * Sub pixel optimize for canvas
- *
- * @param {number} position Coordinate, such as x, y
- * @param {number} lineWidth Should be nonnegative integer.
- * @param {boolean=} positiveOrNegative Default false (negative).
- * @return {number} Optimized position.
- */
- var subPixelOptimize = subPixelOptimizeUtil.subPixelOptimize;
- function hasFillOrStroke(fillOrStroke) {
- return fillOrStroke != null && fillOrStroke !== 'none';
- } // Most lifted color are duplicated.
- var liftedColorMap = zrUtil.createHashMap();
- var liftedColorCount = 0;
- function liftColor(color) {
- if (typeof color !== 'string') {
- return color;
- }
- var liftedColor = liftedColorMap.get(color);
- if (!liftedColor) {
- liftedColor = colorTool.lift(color, -0.1);
- if (liftedColorCount < 10000) {
- liftedColorMap.set(color, liftedColor);
- liftedColorCount++;
- }
- }
- return liftedColor;
- }
- function cacheElementStl(el) {
- if (!el.__hoverStlDirty) {
- return;
- }
- el.__hoverStlDirty = false;
- var hoverStyle = el.__hoverStl;
- if (!hoverStyle) {
- el.__cachedNormalStl = el.__cachedNormalZ2 = null;
- return;
- }
- var normalStyle = el.__cachedNormalStl = {};
- el.__cachedNormalZ2 = el.z2;
- var elStyle = el.style;
- for (var name in hoverStyle) {
- // See comment in `singleEnterEmphasis`.
- if (hoverStyle[name] != null) {
- normalStyle[name] = elStyle[name];
- }
- } // Always cache fill and stroke to normalStyle for lifting color.
- normalStyle.fill = elStyle.fill;
- normalStyle.stroke = elStyle.stroke;
- }
- function singleEnterEmphasis(el) {
- var hoverStl = el.__hoverStl;
- if (!hoverStl || el.__highlighted) {
- return;
- }
- var zr = el.__zr;
- var useHoverLayer = el.useHoverLayer && zr && zr.painter.type === 'canvas';
- el.__highlighted = useHoverLayer ? 'layer' : 'plain';
- if (el.isGroup || !zr && el.useHoverLayer) {
- return;
- }
- var elTarget = el;
- var targetStyle = el.style;
- if (useHoverLayer) {
- elTarget = zr.addHover(el);
- targetStyle = elTarget.style;
- }
- rollbackDefaultTextStyle(targetStyle);
- if (!useHoverLayer) {
- cacheElementStl(elTarget);
- } // styles can be:
- // {
- // label: {
- // show: false,
- // position: 'outside',
- // fontSize: 18
- // },
- // emphasis: {
- // label: {
- // show: true
- // }
- // }
- // },
- // where properties of `emphasis` may not appear in `normal`. We previously use
- // module:echarts/util/model#defaultEmphasis to merge `normal` to `emphasis`.
- // But consider rich text and setOption in merge mode, it is impossible to cover
- // all properties in merge. So we use merge mode when setting style here.
- // But we choose the merge strategy that only properties that is not `null/undefined`.
- // Because when making a textStyle (espacially rich text), it is not easy to distinguish
- // `hasOwnProperty` and `null/undefined` in code, so we trade them as the same for simplicity.
- // But this strategy brings a trouble that `null/undefined` can not be used to remove
- // style any more in `emphasis`. Users can both set properties directly on normal and
- // emphasis to avoid this issue, or we might support `'none'` for this case if required.
- targetStyle.extendFrom(hoverStl);
- setDefaultHoverFillStroke(targetStyle, hoverStl, 'fill');
- setDefaultHoverFillStroke(targetStyle, hoverStl, 'stroke');
- applyDefaultTextStyle(targetStyle);
- if (!useHoverLayer) {
- el.dirty(false);
- el.z2 += Z2_EMPHASIS_LIFT;
- }
- }
- function setDefaultHoverFillStroke(targetStyle, hoverStyle, prop) {
- if (!hasFillOrStroke(hoverStyle[prop]) && hasFillOrStroke(targetStyle[prop])) {
- targetStyle[prop] = liftColor(targetStyle[prop]);
- }
- }
- function singleEnterNormal(el) {
- var highlighted = el.__highlighted;
- if (!highlighted) {
- return;
- }
- el.__highlighted = false;
- if (el.isGroup) {
- return;
- }
- if (highlighted === 'layer') {
- el.__zr && el.__zr.removeHover(el);
- } else {
- var style = el.style;
- var normalStl = el.__cachedNormalStl;
- if (normalStl) {
- rollbackDefaultTextStyle(style);
- el.setStyle(normalStl);
- applyDefaultTextStyle(style);
- } // `__cachedNormalZ2` will not be reset if calling `setElementHoverStyle`
- // when `el` is on emphasis state. So here by comparing with 1, we try
- // hard to make the bug case rare.
- var normalZ2 = el.__cachedNormalZ2;
- if (normalZ2 != null && el.z2 - normalZ2 === Z2_EMPHASIS_LIFT) {
- el.z2 = normalZ2;
- }
- }
- }
- function traverseUpdate(el, updater, commonParam) {
- // If root is group, also enter updater for `highDownOnUpdate`.
- var fromState = NORMAL;
- var toState = NORMAL;
- var trigger; // See the rule of `highDownOnUpdate` on `graphic.setAsHighDownDispatcher`.
- el.__highlighted && (fromState = EMPHASIS, trigger = true);
- updater(el, commonParam);
- el.__highlighted && (toState = EMPHASIS, trigger = true);
- el.isGroup && el.traverse(function (child) {
- !child.isGroup && updater(child, commonParam);
- });
- trigger && el.__highDownOnUpdate && el.__highDownOnUpdate(fromState, toState);
- }
- /**
- * Set hover style (namely "emphasis style") of element, based on the current
- * style of the given `el`.
- * This method should be called after all of the normal styles have been adopted
- * to the `el`. See the reason on `setHoverStyle`.
- *
- * @param {module:zrender/Element} el Should not be `zrender/container/Group`.
- * @param {Object} [el.hoverStyle] Can be set on el or its descendants,
- * e.g., `el.hoverStyle = ...; graphic.setHoverStyle(el); `.
- * Often used when item group has a label element and it's hoverStyle is different.
- * @param {Object|boolean} [hoverStl] The specified hover style.
- * If set as `false`, disable the hover style.
- * Similarly, The `el.hoverStyle` can alse be set
- * as `false` to disable the hover style.
- * Otherwise, use the default hover style if not provided.
- */
- function setElementHoverStyle(el, hoverStl) {
- // For performance consideration, it might be better to make the "hover style" only the
- // difference properties from the "normal style", but not a entire copy of all styles.
- hoverStl = el.__hoverStl = hoverStl !== false && (el.hoverStyle || hoverStl || {});
- el.__hoverStlDirty = true; // FIXME
- // It is not completely right to save "normal"/"emphasis" flag on elements.
- // It probably should be saved on `data` of series. Consider the cases:
- // (1) A highlighted elements are moved out of the view port and re-enter
- // again by dataZoom.
- // (2) call `setOption` and replace elements totally when they are highlighted.
- if (el.__highlighted) {
- // Consider the case:
- // The styles of a highlighted `el` is being updated. The new "emphasis style"
- // should be adapted to the `el`. Notice here new "normal styles" should have
- // been set outside and the cached "normal style" is out of date.
- el.__cachedNormalStl = null; // Do not clear `__cachedNormalZ2` here, because setting `z2` is not a constraint
- // of this method. In most cases, `z2` is not set and hover style should be able
- // to rollback. Of course, that would bring bug, but only in a rare case, see
- // `doSingleLeaveHover` for details.
- singleEnterNormal(el);
- singleEnterEmphasis(el);
- }
- }
- function onElementMouseOver(e) {
- !shouldSilent(this, e) // "emphasis" event highlight has higher priority than mouse highlight.
- && !this.__highByOuter && traverseUpdate(this, singleEnterEmphasis);
- }
- function onElementMouseOut(e) {
- !shouldSilent(this, e) // "emphasis" event highlight has higher priority than mouse highlight.
- && !this.__highByOuter && traverseUpdate(this, singleEnterNormal);
- }
- function onElementEmphasisEvent(highlightDigit) {
- this.__highByOuter |= 1 << (highlightDigit || 0);
- traverseUpdate(this, singleEnterEmphasis);
- }
- function onElementNormalEvent(highlightDigit) {
- !(this.__highByOuter &= ~(1 << (highlightDigit || 0))) && traverseUpdate(this, singleEnterNormal);
- }
- function shouldSilent(el, e) {
- return el.__highDownSilentOnTouch && e.zrByTouch;
- }
- /**
- * Set hover style (namely "emphasis style") of element,
- * based on the current style of the given `el`.
- *
- * (1)
- * **CONSTRAINTS** for this method:
- * <A> This method MUST be called after all of the normal styles having been adopted
- * to the `el`.
- * <B> The input `hoverStyle` (that is, "emphasis style") MUST be the subset of the
- * "normal style" having been set to the el.
- * <C> `color` MUST be one of the "normal styles" (because color might be lifted as
- * a default hover style).
- *
- * The reason: this method treat the current style of the `el` as the "normal style"
- * and cache them when enter/update the "emphasis style". Consider the case: the `el`
- * is in "emphasis" state and `setOption`/`dispatchAction` trigger the style updating
- * logic, where the el should shift from the original emphasis style to the new
- * "emphasis style" and should be able to "downplay" back to the new "normal style".
- *
- * Indeed, it is error-prone to make a interface has so many constraints, but I have
- * not found a better solution yet to fit the backward compatibility, performance and
- * the current programming style.
- *
- * (2)
- * Call the method for a "root" element once. Do not call it for each descendants.
- * If the descendants elemenets of a group has itself hover style different from the
- * root group, we can simply mount the style on `el.hoverStyle` for them, but should
- * not call this method for them.
- *
- * (3) These input parameters can be set directly on `el`:
- *
- * @param {module:zrender/Element} el
- * @param {Object} [el.hoverStyle] See `graphic.setElementHoverStyle`.
- * @param {boolean} [el.highDownSilentOnTouch=false] See `graphic.setAsHighDownDispatcher`.
- * @param {Function} [el.highDownOnUpdate] See `graphic.setAsHighDownDispatcher`.
- * @param {Object|boolean} [hoverStyle] See `graphic.setElementHoverStyle`.
- */
- function setHoverStyle(el, hoverStyle) {
- setAsHighDownDispatcher(el, true);
- traverseUpdate(el, setElementHoverStyle, hoverStyle);
- }
- /**
- * @param {module:zrender/Element} el
- * @param {Function} [el.highDownOnUpdate] Called when state updated.
- * Since `setHoverStyle` has the constraint that it must be called after
- * all of the normal style updated, `highDownOnUpdate` is not needed to
- * trigger if both `fromState` and `toState` is 'normal', and needed to
- * trigger if both `fromState` and `toState` is 'emphasis', which enables
- * to sync outside style settings to "emphasis" state.
- * @this {string} This dispatcher `el`.
- * @param {string} fromState Can be "normal" or "emphasis".
- * `fromState` might equal to `toState`,
- * for example, when this method is called when `el` is
- * on "emphasis" state.
- * @param {string} toState Can be "normal" or "emphasis".
- *
- * FIXME
- * CAUTION: Do not expose `highDownOnUpdate` outside echarts.
- * Because it is not a complete solution. The update
- * listener should not have been mount in element,
- * and the normal/emphasis state should not have
- * mantained on elements.
- *
- * @param {boolean} [el.highDownSilentOnTouch=false]
- * In touch device, mouseover event will be trigger on touchstart event
- * (see module:zrender/dom/HandlerProxy). By this mechanism, we can
- * conveniently use hoverStyle when tap on touch screen without additional
- * code for compatibility.
- * But if the chart/component has select feature, which usually also use
- * hoverStyle, there might be conflict between 'select-highlight' and
- * 'hover-highlight' especially when roam is enabled (see geo for example).
- * In this case, `highDownSilentOnTouch` should be used to disable
- * hover-highlight on touch device.
- * @param {boolean} [asDispatcher=true] If `false`, do not set as "highDownDispatcher".
- */
- function setAsHighDownDispatcher(el, asDispatcher) {
- var disable = asDispatcher === false; // Make `highDownSilentOnTouch` and `highDownOnUpdate` only work after
- // `setAsHighDownDispatcher` called. Avoid it is modified by user unexpectedly.
- el.__highDownSilentOnTouch = el.highDownSilentOnTouch;
- el.__highDownOnUpdate = el.highDownOnUpdate; // Simple optimize, since this method might be
- // called for each elements of a group in some cases.
- if (!disable || el.__highDownDispatcher) {
- var method = disable ? 'off' : 'on'; // Duplicated function will be auto-ignored, see Eventful.js.
- el[method]('mouseover', onElementMouseOver)[method]('mouseout', onElementMouseOut); // Emphasis, normal can be triggered manually by API or other components like hover link.
- el[method]('emphasis', onElementEmphasisEvent)[method]('normal', onElementNormalEvent); // Also keep previous record.
- el.__highByOuter = el.__highByOuter || 0;
- el.__highDownDispatcher = !disable;
- }
- }
- /**
- * @param {module:zrender/src/Element} el
- * @return {boolean}
- */
- function isHighDownDispatcher(el) {
- return !!(el && el.__highDownDispatcher);
- }
- /**
- * Support hightlight/downplay record on each elements.
- * For the case: hover highlight/downplay (legend, visualMap, ...) and
- * user triggerred hightlight/downplay should not conflict.
- * Only all of the highlightDigit cleared, return to normal.
- * @param {string} highlightKey
- * @return {number} highlightDigit
- */
- function getHighlightDigit(highlightKey) {
- var highlightDigit = _highlightKeyMap[highlightKey];
- if (highlightDigit == null && _highlightNextDigit <= 32) {
- highlightDigit = _highlightKeyMap[highlightKey] = _highlightNextDigit++;
- }
- return highlightDigit;
- }
- /**
- * See more info in `setTextStyleCommon`.
- * @param {Object|module:zrender/graphic/Style} normalStyle
- * @param {Object} emphasisStyle
- * @param {module:echarts/model/Model} normalModel
- * @param {module:echarts/model/Model} emphasisModel
- * @param {Object} opt Check `opt` of `setTextStyleCommon` to find other props.
- * @param {string|Function} [opt.defaultText]
- * @param {module:echarts/model/Model} [opt.labelFetcher] Fetch text by
- * `opt.labelFetcher.getFormattedLabel(opt.labelDataIndex, 'normal'/'emphasis', null, opt.labelDimIndex, opt.labelProp)`
- * @param {number} [opt.labelDataIndex] Fetch text by
- * `opt.textFetcher.getFormattedLabel(opt.labelDataIndex, 'normal'/'emphasis', null, opt.labelDimIndex, opt.labelProp)`
- * @param {number} [opt.labelDimIndex] Fetch text by
- * `opt.textFetcher.getFormattedLabel(opt.labelDataIndex, 'normal'/'emphasis', null, opt.labelDimIndex, opt.labelProp)`
- * @param {string} [opt.labelProp] Fetch text by
- * `opt.textFetcher.getFormattedLabel(opt.labelDataIndex, 'normal'/'emphasis', null, opt.labelDimIndex, opt.labelProp)`
- * @param {Object} [normalSpecified]
- * @param {Object} [emphasisSpecified]
- */
- function setLabelStyle(normalStyle, emphasisStyle, normalModel, emphasisModel, opt, normalSpecified, emphasisSpecified) {
- opt = opt || EMPTY_OBJ;
- var labelFetcher = opt.labelFetcher;
- var labelDataIndex = opt.labelDataIndex;
- var labelDimIndex = opt.labelDimIndex;
- var labelProp = opt.labelProp; // This scenario, `label.normal.show = true; label.emphasis.show = false`,
- // is not supported util someone requests.
- var showNormal = normalModel.getShallow('show');
- var showEmphasis = emphasisModel.getShallow('show'); // Consider performance, only fetch label when necessary.
- // If `normal.show` is `false` and `emphasis.show` is `true` and `emphasis.formatter` is not set,
- // label should be displayed, where text is fetched by `normal.formatter` or `opt.defaultText`.
- var baseText;
- if (showNormal || showEmphasis) {
- if (labelFetcher) {
- baseText = labelFetcher.getFormattedLabel(labelDataIndex, 'normal', null, labelDimIndex, labelProp);
- }
- if (baseText == null) {
- baseText = zrUtil.isFunction(opt.defaultText) ? opt.defaultText(labelDataIndex, opt) : opt.defaultText;
- }
- }
- var normalStyleText = showNormal ? baseText : null;
- var emphasisStyleText = showEmphasis ? zrUtil.retrieve2(labelFetcher ? labelFetcher.getFormattedLabel(labelDataIndex, 'emphasis', null, labelDimIndex, labelProp) : null, baseText) : null; // Optimize: If style.text is null, text will not be drawn.
- if (normalStyleText != null || emphasisStyleText != null) {
- // Always set `textStyle` even if `normalStyle.text` is null, because default
- // values have to be set on `normalStyle`.
- // If we set default values on `emphasisStyle`, consider case:
- // Firstly, `setOption(... label: {normal: {text: null}, emphasis: {show: true}} ...);`
- // Secondly, `setOption(... label: {noraml: {show: true, text: 'abc', color: 'red'} ...);`
- // Then the 'red' will not work on emphasis.
- setTextStyle(normalStyle, normalModel, normalSpecified, opt);
- setTextStyle(emphasisStyle, emphasisModel, emphasisSpecified, opt, true);
- }
- normalStyle.text = normalStyleText;
- emphasisStyle.text = emphasisStyleText;
- }
- /**
- * Modify label style manually.
- * Only works after `setLabelStyle` and `setElementHoverStyle` called.
- *
- * @param {module:zrender/src/Element} el
- * @param {Object} [normalStyleProps] optional
- * @param {Object} [emphasisStyleProps] optional
- */
- function modifyLabelStyle(el, normalStyleProps, emphasisStyleProps) {
- var elStyle = el.style;
- if (normalStyleProps) {
- rollbackDefaultTextStyle(elStyle);
- el.setStyle(normalStyleProps);
- applyDefaultTextStyle(elStyle);
- }
- elStyle = el.__hoverStl;
- if (emphasisStyleProps && elStyle) {
- rollbackDefaultTextStyle(elStyle);
- zrUtil.extend(elStyle, emphasisStyleProps);
- applyDefaultTextStyle(elStyle);
- }
- }
- /**
- * Set basic textStyle properties.
- * See more info in `setTextStyleCommon`.
- * @param {Object|module:zrender/graphic/Style} textStyle
- * @param {module:echarts/model/Model} model
- * @param {Object} [specifiedTextStyle] Can be overrided by settings in model.
- * @param {Object} [opt] See `opt` of `setTextStyleCommon`.
- * @param {boolean} [isEmphasis]
- */
- function setTextStyle(textStyle, textStyleModel, specifiedTextStyle, opt, isEmphasis) {
- setTextStyleCommon(textStyle, textStyleModel, opt, isEmphasis);
- specifiedTextStyle && zrUtil.extend(textStyle, specifiedTextStyle); // textStyle.host && textStyle.host.dirty && textStyle.host.dirty(false);
- return textStyle;
- }
- /**
- * Set text option in the style.
- * See more info in `setTextStyleCommon`.
- * @deprecated
- * @param {Object} textStyle
- * @param {module:echarts/model/Model} labelModel
- * @param {string|boolean} defaultColor Default text color.
- * If set as false, it will be processed as a emphasis style.
- */
- function setText(textStyle, labelModel, defaultColor) {
- var opt = {
- isRectText: true
- };
- var isEmphasis;
- if (defaultColor === false) {
- isEmphasis = true;
- } else {
- // Support setting color as 'auto' to get visual color.
- opt.autoColor = defaultColor;
- }
- setTextStyleCommon(textStyle, labelModel, opt, isEmphasis); // textStyle.host && textStyle.host.dirty && textStyle.host.dirty(false);
- }
- /**
- * The uniform entry of set text style, that is, retrieve style definitions
- * from `model` and set to `textStyle` object.
- *
- * Never in merge mode, but in overwrite mode, that is, all of the text style
- * properties will be set. (Consider the states of normal and emphasis and
- * default value can be adopted, merge would make the logic too complicated
- * to manage.)
- *
- * The `textStyle` object can either be a plain object or an instance of
- * `zrender/src/graphic/Style`, and either be the style of normal or emphasis.
- * After this mothod called, the `textStyle` object can then be used in
- * `el.setStyle(textStyle)` or `el.hoverStyle = textStyle`.
- *
- * Default value will be adopted and `insideRollbackOpt` will be created.
- * See `applyDefaultTextStyle` `rollbackDefaultTextStyle` for more details.
- *
- * opt: {
- * disableBox: boolean, Whether diable drawing box of block (outer most).
- * isRectText: boolean,
- * autoColor: string, specify a color when color is 'auto',
- * for textFill, textStroke, textBackgroundColor, and textBorderColor.
- * If autoColor specified, it is used as default textFill.
- * useInsideStyle:
- * `true`: Use inside style (textFill, textStroke, textStrokeWidth)
- * if `textFill` is not specified.
- * `false`: Do not use inside style.
- * `null/undefined`: use inside style if `isRectText` is true and
- * `textFill` is not specified and textPosition contains `'inside'`.
- * forceRich: boolean
- * }
- */
- function setTextStyleCommon(textStyle, textStyleModel, opt, isEmphasis) {
- // Consider there will be abnormal when merge hover style to normal style if given default value.
- opt = opt || EMPTY_OBJ;
- if (opt.isRectText) {
- var textPosition;
- if (opt.getTextPosition) {
- textPosition = opt.getTextPosition(textStyleModel, isEmphasis);
- } else {
- textPosition = textStyleModel.getShallow('position') || (isEmphasis ? null : 'inside'); // 'outside' is not a valid zr textPostion value, but used
- // in bar series, and magric type should be considered.
- textPosition === 'outside' && (textPosition = 'top');
- }
- textStyle.textPosition = textPosition;
- textStyle.textOffset = textStyleModel.getShallow('offset');
- var labelRotate = textStyleModel.getShallow('rotate');
- labelRotate != null && (labelRotate *= Math.PI / 180);
- textStyle.textRotation = labelRotate;
- textStyle.textDistance = zrUtil.retrieve2(textStyleModel.getShallow('distance'), isEmphasis ? null : 5);
- }
- var ecModel = textStyleModel.ecModel;
- var globalTextStyle = ecModel && ecModel.option.textStyle; // Consider case:
- // {
- // data: [{
- // value: 12,
- // label: {
- // rich: {
- // // no 'a' here but using parent 'a'.
- // }
- // }
- // }],
- // rich: {
- // a: { ... }
- // }
- // }
- var richItemNames = getRichItemNames(textStyleModel);
- var richResult;
- if (richItemNames) {
- richResult = {};
- for (var name in richItemNames) {
- if (richItemNames.hasOwnProperty(name)) {
- // Cascade is supported in rich.
- var richTextStyle = textStyleModel.getModel(['rich', name]); // In rich, never `disableBox`.
- // FIXME: consider `label: {formatter: '{a|xx}', color: 'blue', rich: {a: {}}}`,
- // the default color `'blue'` will not be adopted if no color declared in `rich`.
- // That might confuses users. So probably we should put `textStyleModel` as the
- // root ancestor of the `richTextStyle`. But that would be a break change.
- setTokenTextStyle(richResult[name] = {}, richTextStyle, globalTextStyle, opt, isEmphasis);
- }
- }
- }
- textStyle.rich = richResult;
- setTokenTextStyle(textStyle, textStyleModel, globalTextStyle, opt, isEmphasis, true);
- if (opt.forceRich && !opt.textStyle) {
- opt.textStyle = {};
- }
- return textStyle;
- } // Consider case:
- // {
- // data: [{
- // value: 12,
- // label: {
- // rich: {
- // // no 'a' here but using parent 'a'.
- // }
- // }
- // }],
- // rich: {
- // a: { ... }
- // }
- // }
- function getRichItemNames(textStyleModel) {
- // Use object to remove duplicated names.
- var richItemNameMap;
- while (textStyleModel && textStyleModel !== textStyleModel.ecModel) {
- var rich = (textStyleModel.option || EMPTY_OBJ).rich;
- if (rich) {
- richItemNameMap = richItemNameMap || {};
- for (var name in rich) {
- if (rich.hasOwnProperty(name)) {
- richItemNameMap[name] = 1;
- }
- }
- }
- textStyleModel = textStyleModel.parentModel;
- }
- return richItemNameMap;
- }
- function setTokenTextStyle(textStyle, textStyleModel, globalTextStyle, opt, isEmphasis, isBlock) {
- // In merge mode, default value should not be given.
- globalTextStyle = !isEmphasis && globalTextStyle || EMPTY_OBJ;
- textStyle.textFill = getAutoColor(textStyleModel.getShallow('color'), opt) || globalTextStyle.color;
- textStyle.textStroke = getAutoColor(textStyleModel.getShallow('textBorderColor'), opt) || globalTextStyle.textBorderColor;
- textStyle.textStrokeWidth = zrUtil.retrieve2(textStyleModel.getShallow('textBorderWidth'), globalTextStyle.textBorderWidth);
- if (!isEmphasis) {
- if (isBlock) {
- textStyle.insideRollbackOpt = opt;
- applyDefaultTextStyle(textStyle);
- } // Set default finally.
- if (textStyle.textFill == null) {
- textStyle.textFill = opt.autoColor;
- }
- } // Do not use `getFont` here, because merge should be supported, where
- // part of these properties may be changed in emphasis style, and the
- // others should remain their original value got from normal style.
- textStyle.fontStyle = textStyleModel.getShallow('fontStyle') || globalTextStyle.fontStyle;
- textStyle.fontWeight = textStyleModel.getShallow('fontWeight') || globalTextStyle.fontWeight;
- textStyle.fontSize = textStyleModel.getShallow('fontSize') || globalTextStyle.fontSize;
- textStyle.fontFamily = textStyleModel.getShallow('fontFamily') || globalTextStyle.fontFamily;
- textStyle.textAlign = textStyleModel.getShallow('align');
- textStyle.textVerticalAlign = textStyleModel.getShallow('verticalAlign') || textStyleModel.getShallow('baseline');
- textStyle.textLineHeight = textStyleModel.getShallow('lineHeight');
- textStyle.textWidth = textStyleModel.getShallow('width');
- textStyle.textHeight = textStyleModel.getShallow('height');
- textStyle.textTag = textStyleModel.getShallow('tag');
- if (!isBlock || !opt.disableBox) {
- textStyle.textBackgroundColor = getAutoColor(textStyleModel.getShallow('backgroundColor'), opt);
- textStyle.textPadding = textStyleModel.getShallow('padding');
- textStyle.textBorderColor = getAutoColor(textStyleModel.getShallow('borderColor'), opt);
- textStyle.textBorderWidth = textStyleModel.getShallow('borderWidth');
- textStyle.textBorderRadius = textStyleModel.getShallow('borderRadius');
- textStyle.textBoxShadowColor = textStyleModel.getShallow('shadowColor');
- textStyle.textBoxShadowBlur = textStyleModel.getShallow('shadowBlur');
- textStyle.textBoxShadowOffsetX = textStyleModel.getShallow('shadowOffsetX');
- textStyle.textBoxShadowOffsetY = textStyleModel.getShallow('shadowOffsetY');
- }
- textStyle.textShadowColor = textStyleModel.getShallow('textShadowColor') || globalTextStyle.textShadowColor;
- textStyle.textShadowBlur = textStyleModel.getShallow('textShadowBlur') || globalTextStyle.textShadowBlur;
- textStyle.textShadowOffsetX = textStyleModel.getShallow('textShadowOffsetX') || globalTextStyle.textShadowOffsetX;
- textStyle.textShadowOffsetY = textStyleModel.getShallow('textShadowOffsetY') || globalTextStyle.textShadowOffsetY;
- }
- function getAutoColor(color, opt) {
- return color !== 'auto' ? color : opt && opt.autoColor ? opt.autoColor : null;
- }
- /**
- * Give some default value to the input `textStyle` object, based on the current settings
- * in this `textStyle` object.
- *
- * The Scenario:
- * when text position is `inside` and `textFill` is not specified, we show
- * text border by default for better view. But it should be considered that text position
- * might be changed when hovering or being emphasis, where the `insideRollback` is used to
- * restore the style.
- *
- * Usage (& NOTICE):
- * When a style object (eithor plain object or instance of `zrender/src/graphic/Style`) is
- * about to be modified on its text related properties, `rollbackDefaultTextStyle` should
- * be called before the modification and `applyDefaultTextStyle` should be called after that.
- * (For the case that all of the text related properties is reset, like `setTextStyleCommon`
- * does, `rollbackDefaultTextStyle` is not needed to be called).
- */
- function applyDefaultTextStyle(textStyle) {
- var textPosition = textStyle.textPosition;
- var opt = textStyle.insideRollbackOpt;
- var insideRollback;
- if (opt && textStyle.textFill == null) {
- var autoColor = opt.autoColor;
- var isRectText = opt.isRectText;
- var useInsideStyle = opt.useInsideStyle;
- var useInsideStyleCache = useInsideStyle !== false && (useInsideStyle === true || isRectText && textPosition // textPosition can be [10, 30]
- && typeof textPosition === 'string' && textPosition.indexOf('inside') >= 0);
- var useAutoColorCache = !useInsideStyleCache && autoColor != null; // All of the props declared in `CACHED_LABEL_STYLE_PROPERTIES` are to be cached.
- if (useInsideStyleCache || useAutoColorCache) {
- insideRollback = {
- textFill: textStyle.textFill,
- textStroke: textStyle.textStroke,
- textStrokeWidth: textStyle.textStrokeWidth
- };
- }
- if (useInsideStyleCache) {
- textStyle.textFill = '#fff'; // Consider text with #fff overflow its container.
- if (textStyle.textStroke == null) {
- textStyle.textStroke = autoColor;
- textStyle.textStrokeWidth == null && (textStyle.textStrokeWidth = 2);
- }
- }
- if (useAutoColorCache) {
- textStyle.textFill = autoColor;
- }
- } // Always set `insideRollback`, so that the previous one can be cleared.
- textStyle.insideRollback = insideRollback;
- }
- /**
- * Consider the case: in a scatter,
- * label: {
- * normal: {position: 'inside'},
- * emphasis: {position: 'top'}
- * }
- * In the normal state, the `textFill` will be set as '#fff' for pretty view (see
- * `applyDefaultTextStyle`), but when switching to emphasis state, the `textFill`
- * should be retured to 'autoColor', but not keep '#fff'.
- */
- function rollbackDefaultTextStyle(style) {
- var insideRollback = style.insideRollback;
- if (insideRollback) {
- // Reset all of the props in `CACHED_LABEL_STYLE_PROPERTIES`.
- style.textFill = insideRollback.textFill;
- style.textStroke = insideRollback.textStroke;
- style.textStrokeWidth = insideRollback.textStrokeWidth;
- style.insideRollback = null;
- }
- }
- function getFont(opt, ecModel) {
- var gTextStyleModel = ecModel && ecModel.getModel('textStyle');
- return zrUtil.trim([// FIXME in node-canvas fontWeight is before fontStyle
- opt.fontStyle || gTextStyleModel && gTextStyleModel.getShallow('fontStyle') || '', opt.fontWeight || gTextStyleModel && gTextStyleModel.getShallow('fontWeight') || '', (opt.fontSize || gTextStyleModel && gTextStyleModel.getShallow('fontSize') || 12) + 'px', opt.fontFamily || gTextStyleModel && gTextStyleModel.getShallow('fontFamily') || 'sans-serif'].join(' '));
- }
- function animateOrSetProps(isUpdate, el, props, animatableModel, dataIndex, cb) {
- if (typeof dataIndex === 'function') {
- cb = dataIndex;
- dataIndex = null;
- } // Do not check 'animation' property directly here. Consider this case:
- // animation model is an `itemModel`, whose does not have `isAnimationEnabled`
- // but its parent model (`seriesModel`) does.
- var animationEnabled = animatableModel && animatableModel.isAnimationEnabled();
- if (animationEnabled) {
- var postfix = isUpdate ? 'Update' : '';
- var duration = animatableModel.getShallow('animationDuration' + postfix);
- var animationEasing = animatableModel.getShallow('animationEasing' + postfix);
- var animationDelay = animatableModel.getShallow('animationDelay' + postfix);
- if (typeof animationDelay === 'function') {
- animationDelay = animationDelay(dataIndex, animatableModel.getAnimationDelayParams ? animatableModel.getAnimationDelayParams(el, dataIndex) : null);
- }
- if (typeof duration === 'function') {
- duration = duration(dataIndex);
- }
- duration > 0 ? el.animateTo(props, duration, animationDelay || 0, animationEasing, cb, !!cb) : (el.stopAnimation(), el.attr(props), cb && cb());
- } else {
- el.stopAnimation();
- el.attr(props);
- cb && cb();
- }
- }
- /**
- * Update graphic element properties with or without animation according to the
- * configuration in series.
- *
- * Caution: this method will stop previous animation.
- * So do not use this method to one element twice before
- * animation starts, unless you know what you are doing.
- *
- * @param {module:zrender/Element} el
- * @param {Object} props
- * @param {module:echarts/model/Model} [animatableModel]
- * @param {number} [dataIndex]
- * @param {Function} [cb]
- * @example
- * graphic.updateProps(el, {
- * position: [100, 100]
- * }, seriesModel, dataIndex, function () { console.log('Animation done!'); });
- * // Or
- * graphic.updateProps(el, {
- * position: [100, 100]
- * }, seriesModel, function () { console.log('Animation done!'); });
- */
- function updateProps(el, props, animatableModel, dataIndex, cb) {
- animateOrSetProps(true, el, props, animatableModel, dataIndex, cb);
- }
- /**
- * Init graphic element properties with or without animation according to the
- * configuration in series.
- *
- * Caution: this method will stop previous animation.
- * So do not use this method to one element twice before
- * animation starts, unless you know what you are doing.
- *
- * @param {module:zrender/Element} el
- * @param {Object} props
- * @param {module:echarts/model/Model} [animatableModel]
- * @param {number} [dataIndex]
- * @param {Function} cb
- */
- function initProps(el, props, animatableModel, dataIndex, cb) {
- animateOrSetProps(false, el, props, animatableModel, dataIndex, cb);
- }
- /**
- * Get transform matrix of target (param target),
- * in coordinate of its ancestor (param ancestor)
- *
- * @param {module:zrender/mixin/Transformable} target
- * @param {module:zrender/mixin/Transformable} [ancestor]
- */
- function getTransform(target, ancestor) {
- var mat = matrix.identity([]);
- while (target && target !== ancestor) {
- matrix.mul(mat, target.getLocalTransform(), mat);
- target = target.parent;
- }
- return mat;
- }
- /**
- * Apply transform to an vertex.
- * @param {Array.<number>} target [x, y]
- * @param {Array.<number>|TypedArray.<number>|Object} transform Can be:
- * + Transform matrix: like [1, 0, 0, 1, 0, 0]
- * + {position, rotation, scale}, the same as `zrender/Transformable`.
- * @param {boolean=} invert Whether use invert matrix.
- * @return {Array.<number>} [x, y]
- */
- function applyTransform(target, transform, invert) {
- if (transform && !zrUtil.isArrayLike(transform)) {
- transform = Transformable.getLocalTransform(transform);
- }
- if (invert) {
- transform = matrix.invert([], transform);
- }
- return vector.applyTransform([], target, transform);
- }
- /**
- * @param {string} direction 'left' 'right' 'top' 'bottom'
- * @param {Array.<number>} transform Transform matrix: like [1, 0, 0, 1, 0, 0]
- * @param {boolean=} invert Whether use invert matrix.
- * @return {string} Transformed direction. 'left' 'right' 'top' 'bottom'
- */
- function transformDirection(direction, transform, invert) {
- // Pick a base, ensure that transform result will not be (0, 0).
- var hBase = transform[4] === 0 || transform[5] === 0 || transform[0] === 0 ? 1 : Math.abs(2 * transform[4] / transform[0]);
- var vBase = transform[4] === 0 || transform[5] === 0 || transform[2] === 0 ? 1 : Math.abs(2 * transform[4] / transform[2]);
- var vertex = [direction === 'left' ? -hBase : direction === 'right' ? hBase : 0, direction === 'top' ? -vBase : direction === 'bottom' ? vBase : 0];
- vertex = applyTransform(vertex, transform, invert);
- return Math.abs(vertex[0]) > Math.abs(vertex[1]) ? vertex[0] > 0 ? 'right' : 'left' : vertex[1] > 0 ? 'bottom' : 'top';
- }
- /**
- * Apply group transition animation from g1 to g2.
- * If no animatableModel, no animation.
- */
- function groupTransition(g1, g2, animatableModel, cb) {
- if (!g1 || !g2) {
- return;
- }
- function getElMap(g) {
- var elMap = {};
- g.traverse(function (el) {
- if (!el.isGroup && el.anid) {
- elMap[el.anid] = el;
- }
- });
- return elMap;
- }
- function getAnimatableProps(el) {
- var obj = {
- position: vector.clone(el.position),
- rotation: el.rotation
- };
- if (el.shape) {
- obj.shape = zrUtil.extend({}, el.shape);
- }
- return obj;
- }
- var elMap1 = getElMap(g1);
- g2.traverse(function (el) {
- if (!el.isGroup && el.anid) {
- var oldEl = elMap1[el.anid];
- if (oldEl) {
- var newProp = getAnimatableProps(el);
- el.attr(getAnimatableProps(oldEl));
- updateProps(el, newProp, animatableModel, el.dataIndex);
- } // else {
- // if (el.previousProps) {
- // graphic.updateProps
- // }
- // }
- }
- });
- }
- /**
- * @param {Array.<Array.<number>>} points Like: [[23, 44], [53, 66], ...]
- * @param {Object} rect {x, y, width, height}
- * @return {Array.<Array.<number>>} A new clipped points.
- */
- function clipPointsByRect(points, rect) {
- // FIXME: this way migth be incorrect when grpahic clipped by a corner.
- // and when element have border.
- return zrUtil.map(points, function (point) {
- var x = point[0];
- x = mathMax(x, rect.x);
- x = mathMin(x, rect.x + rect.width);
- var y = point[1];
- y = mathMax(y, rect.y);
- y = mathMin(y, rect.y + rect.height);
- return [x, y];
- });
- }
- /**
- * @param {Object} targetRect {x, y, width, height}
- * @param {Object} rect {x, y, width, height}
- * @return {Object} A new clipped rect. If rect size are negative, return undefined.
- */
- function clipRectByRect(targetRect, rect) {
- var x = mathMax(targetRect.x, rect.x);
- var x2 = mathMin(targetRect.x + targetRect.width, rect.x + rect.width);
- var y = mathMax(targetRect.y, rect.y);
- var y2 = mathMin(targetRect.y + targetRect.height, rect.y + rect.height); // If the total rect is cliped, nothing, including the border,
- // should be painted. So return undefined.
- if (x2 >= x && y2 >= y) {
- return {
- x: x,
- y: y,
- width: x2 - x,
- height: y2 - y
- };
- }
- }
- /**
- * @param {string} iconStr Support 'image://' or 'path://' or direct svg path.
- * @param {Object} [opt] Properties of `module:zrender/Element`, except `style`.
- * @param {Object} [rect] {x, y, width, height}
- * @return {module:zrender/Element} Icon path or image element.
- */
- function createIcon(iconStr, opt, rect) {
- opt = zrUtil.extend({
- rectHover: true
- }, opt);
- var style = opt.style = {
- strokeNoScale: true
- };
- rect = rect || {
- x: -1,
- y: -1,
- width: 2,
- height: 2
- };
- if (iconStr) {
- return iconStr.indexOf('image://') === 0 ? (style.image = iconStr.slice(8), zrUtil.defaults(style, rect), new ZImage(opt)) : makePath(iconStr.replace('path://', ''), opt, rect, 'center');
- }
- }
- /**
- * Return `true` if the given line (line `a`) and the given polygon
- * are intersect.
- * Note that we do not count colinear as intersect here because no
- * requirement for that. We could do that if required in future.
- *
- * @param {number} a1x
- * @param {number} a1y
- * @param {number} a2x
- * @param {number} a2y
- * @param {Array.<Array.<number>>} points Points of the polygon.
- * @return {boolean}
- */
- function linePolygonIntersect(a1x, a1y, a2x, a2y, points) {
- for (var i = 0, p2 = points[points.length - 1]; i < points.length; i++) {
- var p = points[i];
- if (lineLineIntersect(a1x, a1y, a2x, a2y, p[0], p[1], p2[0], p2[1])) {
- return true;
- }
- p2 = p;
- }
- }
- /**
- * Return `true` if the given two lines (line `a` and line `b`)
- * are intersect.
- * Note that we do not count colinear as intersect here because no
- * requirement for that. We could do that if required in future.
- *
- * @param {number} a1x
- * @param {number} a1y
- * @param {number} a2x
- * @param {number} a2y
- * @param {number} b1x
- * @param {number} b1y
- * @param {number} b2x
- * @param {number} b2y
- * @return {boolean}
- */
- function lineLineIntersect(a1x, a1y, a2x, a2y, b1x, b1y, b2x, b2y) {
- // let `vec_m` to be `vec_a2 - vec_a1` and `vec_n` to be `vec_b2 - vec_b1`.
- var mx = a2x - a1x;
- var my = a2y - a1y;
- var nx = b2x - b1x;
- var ny = b2y - b1y; // `vec_m` and `vec_n` are parallel iff
- // exising `k` such that `vec_m = k · vec_n`, equivalent to `vec_m X vec_n = 0`.
- var nmCrossProduct = crossProduct2d(nx, ny, mx, my);
- if (nearZero(nmCrossProduct)) {
- return false;
- } // `vec_m` and `vec_n` are intersect iff
- // existing `p` and `q` in [0, 1] such that `vec_a1 + p * vec_m = vec_b1 + q * vec_n`,
- // such that `q = ((vec_a1 - vec_b1) X vec_m) / (vec_n X vec_m)`
- // and `p = ((vec_a1 - vec_b1) X vec_n) / (vec_n X vec_m)`.
- var b1a1x = a1x - b1x;
- var b1a1y = a1y - b1y;
- var q = crossProduct2d(b1a1x, b1a1y, mx, my) / nmCrossProduct;
- if (q < 0 || q > 1) {
- return false;
- }
- var p = crossProduct2d(b1a1x, b1a1y, nx, ny) / nmCrossProduct;
- if (p < 0 || p > 1) {
- return false;
- }
- return true;
- }
- /**
- * Cross product of 2-dimension vector.
- */
- function crossProduct2d(x1, y1, x2, y2) {
- return x1 * y2 - x2 * y1;
- }
- function nearZero(val) {
- return val <= 1e-6 && val >= -1e-6;
- } // Register built-in shapes. These shapes might be overwirtten
- // by users, although we do not recommend that.
- registerShape('circle', Circle);
- registerShape('sector', Sector);
- registerShape('ring', Ring);
- registerShape('polygon', Polygon);
- registerShape('polyline', Polyline);
- registerShape('rect', Rect);
- registerShape('line', Line);
- registerShape('bezierCurve', BezierCurve);
- registerShape('arc', Arc);
- exports.Z2_EMPHASIS_LIFT = Z2_EMPHASIS_LIFT;
- exports.CACHED_LABEL_STYLE_PROPERTIES = CACHED_LABEL_STYLE_PROPERTIES;
- exports.extendShape = extendShape;
- exports.extendPath = extendPath;
- exports.registerShape = registerShape;
- exports.getShapeClass = getShapeClass;
- exports.makePath = makePath;
- exports.makeImage = makeImage;
- exports.mergePath = mergePath;
- exports.resizePath = resizePath;
- exports.subPixelOptimizeLine = subPixelOptimizeLine;
- exports.subPixelOptimizeRect = subPixelOptimizeRect;
- exports.subPixelOptimize = subPixelOptimize;
- exports.setElementHoverStyle = setElementHoverStyle;
- exports.setHoverStyle = setHoverStyle;
- exports.setAsHighDownDispatcher = setAsHighDownDispatcher;
- exports.isHighDownDispatcher = isHighDownDispatcher;
- exports.getHighlightDigit = getHighlightDigit;
- exports.setLabelStyle = setLabelStyle;
- exports.modifyLabelStyle = modifyLabelStyle;
- exports.setTextStyle = setTextStyle;
- exports.setText = setText;
- exports.getFont = getFont;
- exports.updateProps = updateProps;
- exports.initProps = initProps;
- exports.getTransform = getTransform;
- exports.applyTransform = applyTransform;
- exports.transformDirection = transformDirection;
- exports.groupTransition = groupTransition;
- exports.clipPointsByRect = clipPointsByRect;
- exports.clipRectByRect = clipRectByRect;
- exports.createIcon = createIcon;
- exports.linePolygonIntersect = linePolygonIntersect;
- exports.lineLineIntersect = lineLineIntersect;
|