echarts.simple.js 883 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612861386148615861686178618861986208621862286238624862586268627862886298630863186328633863486358636863786388639864086418642864386448645864686478648864986508651865286538654865586568657865886598660866186628663866486658666866786688669867086718672867386748675867686778678867986808681868286838684868586868687868886898690869186928693869486958696869786988699870087018702870387048705870687078708870987108711871287138714871587168717871887198720872187228723872487258726872787288729873087318732873387348735873687378738873987408741874287438744874587468747874887498750875187528753875487558756875787588759876087618762876387648765876687678768876987708771877287738774877587768777877887798780878187828783878487858786878787888789879087918792879387948795879687978798879988008801880288038804880588068807880888098810881188128813881488158816881788188819882088218822882388248825882688278828882988308831883288338834883588368837883888398840884188428843884488458846884788488849885088518852885388548855885688578858885988608861886288638864886588668867886888698870887188728873887488758876887788788879888088818882888388848885888688878888888988908891889288938894889588968897889888998900890189028903890489058906890789088909891089118912891389148915891689178918891989208921892289238924892589268927892889298930893189328933893489358936893789388939894089418942894389448945894689478948894989508951895289538954895589568957895889598960896189628963896489658966896789688969897089718972897389748975897689778978897989808981898289838984898589868987898889898990899189928993899489958996899789988999900090019002900390049005900690079008900990109011901290139014901590169017901890199020902190229023902490259026902790289029903090319032903390349035903690379038903990409041904290439044904590469047904890499050905190529053905490559056905790589059906090619062906390649065906690679068906990709071907290739074907590769077907890799080908190829083908490859086908790889089909090919092909390949095909690979098909991009101910291039104910591069107910891099110911191129113911491159116911791189119912091219122912391249125912691279128912991309131913291339134913591369137913891399140914191429143914491459146914791489149915091519152915391549155915691579158915991609161916291639164916591669167916891699170917191729173917491759176917791789179918091819182918391849185918691879188918991909191919291939194919591969197919891999200920192029203920492059206920792089209921092119212921392149215921692179218921992209221922292239224922592269227922892299230923192329233923492359236923792389239924092419242924392449245924692479248924992509251925292539254925592569257925892599260926192629263926492659266926792689269927092719272927392749275927692779278927992809281928292839284928592869287928892899290929192929293929492959296929792989299930093019302930393049305930693079308930993109311931293139314931593169317931893199320932193229323932493259326932793289329933093319332933393349335933693379338933993409341934293439344934593469347934893499350935193529353935493559356935793589359936093619362936393649365936693679368936993709371937293739374937593769377937893799380938193829383938493859386938793889389939093919392939393949395939693979398939994009401940294039404940594069407940894099410941194129413941494159416941794189419942094219422942394249425942694279428942994309431943294339434943594369437943894399440944194429443944494459446944794489449945094519452945394549455945694579458945994609461946294639464946594669467946894699470947194729473947494759476947794789479948094819482948394849485948694879488948994909491949294939494949594969497949894999500950195029503950495059506950795089509951095119512951395149515951695179518951995209521952295239524952595269527952895299530953195329533953495359536953795389539954095419542954395449545954695479548954995509551955295539554955595569557955895599560956195629563956495659566956795689569957095719572957395749575957695779578957995809581958295839584958595869587958895899590959195929593959495959596959795989599960096019602960396049605960696079608960996109611961296139614961596169617961896199620962196229623962496259626962796289629963096319632963396349635963696379638963996409641964296439644964596469647964896499650965196529653965496559656965796589659966096619662966396649665966696679668966996709671967296739674967596769677967896799680968196829683968496859686968796889689969096919692969396949695969696979698969997009701970297039704970597069707970897099710971197129713971497159716971797189719972097219722972397249725972697279728972997309731973297339734973597369737973897399740974197429743974497459746974797489749975097519752975397549755975697579758975997609761976297639764976597669767976897699770977197729773977497759776977797789779978097819782978397849785978697879788978997909791979297939794979597969797979897999800980198029803980498059806980798089809981098119812981398149815981698179818981998209821982298239824982598269827982898299830983198329833983498359836983798389839984098419842984398449845984698479848984998509851985298539854985598569857985898599860986198629863986498659866986798689869987098719872987398749875987698779878987998809881988298839884988598869887988898899890989198929893989498959896989798989899990099019902990399049905990699079908990999109911991299139914991599169917991899199920992199229923992499259926992799289929993099319932993399349935993699379938993999409941994299439944994599469947994899499950995199529953995499559956995799589959996099619962996399649965996699679968996999709971997299739974997599769977997899799980998199829983998499859986998799889989999099919992999399949995999699979998999910000100011000210003100041000510006100071000810009100101001110012100131001410015100161001710018100191002010021100221002310024100251002610027100281002910030100311003210033100341003510036100371003810039100401004110042100431004410045100461004710048100491005010051100521005310054100551005610057100581005910060100611006210063100641006510066100671006810069100701007110072100731007410075100761007710078100791008010081100821008310084100851008610087100881008910090100911009210093100941009510096100971009810099101001010110102101031010410105101061010710108101091011010111101121011310114101151011610117101181011910120101211012210123101241012510126101271012810129101301013110132101331013410135101361013710138101391014010141101421014310144101451014610147101481014910150101511015210153101541015510156101571015810159101601016110162101631016410165101661016710168101691017010171101721017310174101751017610177101781017910180101811018210183101841018510186101871018810189101901019110192101931019410195101961019710198101991020010201102021020310204102051020610207102081020910210102111021210213102141021510216102171021810219102201022110222102231022410225102261022710228102291023010231102321023310234102351023610237102381023910240102411024210243102441024510246102471024810249102501025110252102531025410255102561025710258102591026010261102621026310264102651026610267102681026910270102711027210273102741027510276102771027810279102801028110282102831028410285102861028710288102891029010291102921029310294102951029610297102981029910300103011030210303103041030510306103071030810309103101031110312103131031410315103161031710318103191032010321103221032310324103251032610327103281032910330103311033210333103341033510336103371033810339103401034110342103431034410345103461034710348103491035010351103521035310354103551035610357103581035910360103611036210363103641036510366103671036810369103701037110372103731037410375103761037710378103791038010381103821038310384103851038610387103881038910390103911039210393103941039510396103971039810399104001040110402104031040410405104061040710408104091041010411104121041310414104151041610417104181041910420104211042210423104241042510426104271042810429104301043110432104331043410435104361043710438104391044010441104421044310444104451044610447104481044910450104511045210453104541045510456104571045810459104601046110462104631046410465104661046710468104691047010471104721047310474104751047610477104781047910480104811048210483104841048510486104871048810489104901049110492104931049410495104961049710498104991050010501105021050310504105051050610507105081050910510105111051210513105141051510516105171051810519105201052110522105231052410525105261052710528105291053010531105321053310534105351053610537105381053910540105411054210543105441054510546105471054810549105501055110552105531055410555105561055710558105591056010561105621056310564105651056610567105681056910570105711057210573105741057510576105771057810579105801058110582105831058410585105861058710588105891059010591105921059310594105951059610597105981059910600106011060210603106041060510606106071060810609106101061110612106131061410615106161061710618106191062010621106221062310624106251062610627106281062910630106311063210633106341063510636106371063810639106401064110642106431064410645106461064710648106491065010651106521065310654106551065610657106581065910660106611066210663106641066510666106671066810669106701067110672106731067410675106761067710678106791068010681106821068310684106851068610687106881068910690106911069210693106941069510696106971069810699107001070110702107031070410705107061070710708107091071010711107121071310714107151071610717107181071910720107211072210723107241072510726107271072810729107301073110732107331073410735107361073710738107391074010741107421074310744107451074610747107481074910750107511075210753107541075510756107571075810759107601076110762107631076410765107661076710768107691077010771107721077310774107751077610777107781077910780107811078210783107841078510786107871078810789107901079110792107931079410795107961079710798107991080010801108021080310804108051080610807108081080910810108111081210813108141081510816108171081810819108201082110822108231082410825108261082710828108291083010831108321083310834108351083610837108381083910840108411084210843108441084510846108471084810849108501085110852108531085410855108561085710858108591086010861108621086310864108651086610867108681086910870108711087210873108741087510876108771087810879108801088110882108831088410885108861088710888108891089010891108921089310894108951089610897108981089910900109011090210903109041090510906109071090810909109101091110912109131091410915109161091710918109191092010921109221092310924109251092610927109281092910930109311093210933109341093510936109371093810939109401094110942109431094410945109461094710948109491095010951109521095310954109551095610957109581095910960109611096210963109641096510966109671096810969109701097110972109731097410975109761097710978109791098010981109821098310984109851098610987109881098910990109911099210993109941099510996109971099810999110001100111002110031100411005110061100711008110091101011011110121101311014110151101611017110181101911020110211102211023110241102511026110271102811029110301103111032110331103411035110361103711038110391104011041110421104311044110451104611047110481104911050110511105211053110541105511056110571105811059110601106111062110631106411065110661106711068110691107011071110721107311074110751107611077110781107911080110811108211083110841108511086110871108811089110901109111092110931109411095110961109711098110991110011101111021110311104111051110611107111081110911110111111111211113111141111511116111171111811119111201112111122111231112411125111261112711128111291113011131111321113311134111351113611137111381113911140111411114211143111441114511146111471114811149111501115111152111531115411155111561115711158111591116011161111621116311164111651116611167111681116911170111711117211173111741117511176111771117811179111801118111182111831118411185111861118711188111891119011191111921119311194111951119611197111981119911200112011120211203112041120511206112071120811209112101121111212112131121411215112161121711218112191122011221112221122311224112251122611227112281122911230112311123211233112341123511236112371123811239112401124111242112431124411245112461124711248112491125011251112521125311254112551125611257112581125911260112611126211263112641126511266112671126811269112701127111272112731127411275112761127711278112791128011281112821128311284112851128611287112881128911290112911129211293112941129511296112971129811299113001130111302113031130411305113061130711308113091131011311113121131311314113151131611317113181131911320113211132211323113241132511326113271132811329113301133111332113331133411335113361133711338113391134011341113421134311344113451134611347113481134911350113511135211353113541135511356113571135811359113601136111362113631136411365113661136711368113691137011371113721137311374113751137611377113781137911380113811138211383113841138511386113871138811389113901139111392113931139411395113961139711398113991140011401114021140311404114051140611407114081140911410114111141211413114141141511416114171141811419114201142111422114231142411425114261142711428114291143011431114321143311434114351143611437114381143911440114411144211443114441144511446114471144811449114501145111452114531145411455114561145711458114591146011461114621146311464114651146611467114681146911470114711147211473114741147511476114771147811479114801148111482114831148411485114861148711488114891149011491114921149311494114951149611497114981149911500115011150211503115041150511506115071150811509115101151111512115131151411515115161151711518115191152011521115221152311524115251152611527115281152911530115311153211533115341153511536115371153811539115401154111542115431154411545115461154711548115491155011551115521155311554115551155611557115581155911560115611156211563115641156511566115671156811569115701157111572115731157411575115761157711578115791158011581115821158311584115851158611587115881158911590115911159211593115941159511596115971159811599116001160111602116031160411605116061160711608116091161011611116121161311614116151161611617116181161911620116211162211623116241162511626116271162811629116301163111632116331163411635116361163711638116391164011641116421164311644116451164611647116481164911650116511165211653116541165511656116571165811659116601166111662116631166411665116661166711668116691167011671116721167311674116751167611677116781167911680116811168211683116841168511686116871168811689116901169111692116931169411695116961169711698116991170011701117021170311704117051170611707117081170911710117111171211713117141171511716117171171811719117201172111722117231172411725117261172711728117291173011731117321173311734117351173611737117381173911740117411174211743117441174511746117471174811749117501175111752117531175411755117561175711758117591176011761117621176311764117651176611767117681176911770117711177211773117741177511776117771177811779117801178111782117831178411785117861178711788117891179011791117921179311794117951179611797117981179911800118011180211803118041180511806118071180811809118101181111812118131181411815118161181711818118191182011821118221182311824118251182611827118281182911830118311183211833118341183511836118371183811839118401184111842118431184411845118461184711848118491185011851118521185311854118551185611857118581185911860118611186211863118641186511866118671186811869118701187111872118731187411875118761187711878118791188011881118821188311884118851188611887118881188911890118911189211893118941189511896118971189811899119001190111902119031190411905119061190711908119091191011911119121191311914119151191611917119181191911920119211192211923119241192511926119271192811929119301193111932119331193411935119361193711938119391194011941119421194311944119451194611947119481194911950119511195211953119541195511956119571195811959119601196111962119631196411965119661196711968119691197011971119721197311974119751197611977119781197911980119811198211983119841198511986119871198811989119901199111992119931199411995119961199711998119991200012001120021200312004120051200612007120081200912010120111201212013120141201512016120171201812019120201202112022120231202412025120261202712028120291203012031120321203312034120351203612037120381203912040120411204212043120441204512046120471204812049120501205112052120531205412055120561205712058120591206012061120621206312064120651206612067120681206912070120711207212073120741207512076120771207812079120801208112082120831208412085120861208712088120891209012091120921209312094120951209612097120981209912100121011210212103121041210512106121071210812109121101211112112121131211412115121161211712118121191212012121121221212312124121251212612127121281212912130121311213212133121341213512136121371213812139121401214112142121431214412145121461214712148121491215012151121521215312154121551215612157121581215912160121611216212163121641216512166121671216812169121701217112172121731217412175121761217712178121791218012181121821218312184121851218612187121881218912190121911219212193121941219512196121971219812199122001220112202122031220412205122061220712208122091221012211122121221312214122151221612217122181221912220122211222212223122241222512226122271222812229122301223112232122331223412235122361223712238122391224012241122421224312244122451224612247122481224912250122511225212253122541225512256122571225812259122601226112262122631226412265122661226712268122691227012271122721227312274122751227612277122781227912280122811228212283122841228512286122871228812289122901229112292122931229412295122961229712298122991230012301123021230312304123051230612307123081230912310123111231212313123141231512316123171231812319123201232112322123231232412325123261232712328123291233012331123321233312334123351233612337123381233912340123411234212343123441234512346123471234812349123501235112352123531235412355123561235712358123591236012361123621236312364123651236612367123681236912370123711237212373123741237512376123771237812379123801238112382123831238412385123861238712388123891239012391123921239312394123951239612397123981239912400124011240212403124041240512406124071240812409124101241112412124131241412415124161241712418124191242012421124221242312424124251242612427124281242912430124311243212433124341243512436124371243812439124401244112442124431244412445124461244712448124491245012451124521245312454124551245612457124581245912460124611246212463124641246512466124671246812469124701247112472124731247412475124761247712478124791248012481124821248312484124851248612487124881248912490124911249212493124941249512496124971249812499125001250112502125031250412505125061250712508125091251012511125121251312514125151251612517125181251912520125211252212523125241252512526125271252812529125301253112532125331253412535125361253712538125391254012541125421254312544125451254612547125481254912550125511255212553125541255512556125571255812559125601256112562125631256412565125661256712568125691257012571125721257312574125751257612577125781257912580125811258212583125841258512586125871258812589125901259112592125931259412595125961259712598125991260012601126021260312604126051260612607126081260912610126111261212613126141261512616126171261812619126201262112622126231262412625126261262712628126291263012631126321263312634126351263612637126381263912640126411264212643126441264512646126471264812649126501265112652126531265412655126561265712658126591266012661126621266312664126651266612667126681266912670126711267212673126741267512676126771267812679126801268112682126831268412685126861268712688126891269012691126921269312694126951269612697126981269912700127011270212703127041270512706127071270812709127101271112712127131271412715127161271712718127191272012721127221272312724127251272612727127281272912730127311273212733127341273512736127371273812739127401274112742127431274412745127461274712748127491275012751127521275312754127551275612757127581275912760127611276212763127641276512766127671276812769127701277112772127731277412775127761277712778127791278012781127821278312784127851278612787127881278912790127911279212793127941279512796127971279812799128001280112802128031280412805128061280712808128091281012811128121281312814128151281612817128181281912820128211282212823128241282512826128271282812829128301283112832128331283412835128361283712838128391284012841128421284312844128451284612847128481284912850128511285212853128541285512856128571285812859128601286112862128631286412865128661286712868128691287012871128721287312874128751287612877128781287912880128811288212883128841288512886128871288812889128901289112892128931289412895128961289712898128991290012901129021290312904129051290612907129081290912910129111291212913129141291512916129171291812919129201292112922129231292412925129261292712928129291293012931129321293312934129351293612937129381293912940129411294212943129441294512946129471294812949129501295112952129531295412955129561295712958129591296012961129621296312964129651296612967129681296912970129711297212973129741297512976129771297812979129801298112982129831298412985129861298712988129891299012991129921299312994129951299612997129981299913000130011300213003130041300513006130071300813009130101301113012130131301413015130161301713018130191302013021130221302313024130251302613027130281302913030130311303213033130341303513036130371303813039130401304113042130431304413045130461304713048130491305013051130521305313054130551305613057130581305913060130611306213063130641306513066130671306813069130701307113072130731307413075130761307713078130791308013081130821308313084130851308613087130881308913090130911309213093130941309513096130971309813099131001310113102131031310413105131061310713108131091311013111131121311313114131151311613117131181311913120131211312213123131241312513126131271312813129131301313113132131331313413135131361313713138131391314013141131421314313144131451314613147131481314913150131511315213153131541315513156131571315813159131601316113162131631316413165131661316713168131691317013171131721317313174131751317613177131781317913180131811318213183131841318513186131871318813189131901319113192131931319413195131961319713198131991320013201132021320313204132051320613207132081320913210132111321213213132141321513216132171321813219132201322113222132231322413225132261322713228132291323013231132321323313234132351323613237132381323913240132411324213243132441324513246132471324813249132501325113252132531325413255132561325713258132591326013261132621326313264132651326613267132681326913270132711327213273132741327513276132771327813279132801328113282132831328413285132861328713288132891329013291132921329313294132951329613297132981329913300133011330213303133041330513306133071330813309133101331113312133131331413315133161331713318133191332013321133221332313324133251332613327133281332913330133311333213333133341333513336133371333813339133401334113342133431334413345133461334713348133491335013351133521335313354133551335613357133581335913360133611336213363133641336513366133671336813369133701337113372133731337413375133761337713378133791338013381133821338313384133851338613387133881338913390133911339213393133941339513396133971339813399134001340113402134031340413405134061340713408134091341013411134121341313414134151341613417134181341913420134211342213423134241342513426134271342813429134301343113432134331343413435134361343713438134391344013441134421344313444134451344613447134481344913450134511345213453134541345513456134571345813459134601346113462134631346413465134661346713468134691347013471134721347313474134751347613477134781347913480134811348213483134841348513486134871348813489134901349113492134931349413495134961349713498134991350013501135021350313504135051350613507135081350913510135111351213513135141351513516135171351813519135201352113522135231352413525135261352713528135291353013531135321353313534135351353613537135381353913540135411354213543135441354513546135471354813549135501355113552135531355413555135561355713558135591356013561135621356313564135651356613567135681356913570135711357213573135741357513576135771357813579135801358113582135831358413585135861358713588135891359013591135921359313594135951359613597135981359913600136011360213603136041360513606136071360813609136101361113612136131361413615136161361713618136191362013621136221362313624136251362613627136281362913630136311363213633136341363513636136371363813639136401364113642136431364413645136461364713648136491365013651136521365313654136551365613657136581365913660136611366213663136641366513666136671366813669136701367113672136731367413675136761367713678136791368013681136821368313684136851368613687136881368913690136911369213693136941369513696136971369813699137001370113702137031370413705137061370713708137091371013711137121371313714137151371613717137181371913720137211372213723137241372513726137271372813729137301373113732137331373413735137361373713738137391374013741137421374313744137451374613747137481374913750137511375213753137541375513756137571375813759137601376113762137631376413765137661376713768137691377013771137721377313774137751377613777137781377913780137811378213783137841378513786137871378813789137901379113792137931379413795137961379713798137991380013801138021380313804138051380613807138081380913810138111381213813138141381513816138171381813819138201382113822138231382413825138261382713828138291383013831138321383313834138351383613837138381383913840138411384213843138441384513846138471384813849138501385113852138531385413855138561385713858138591386013861138621386313864138651386613867138681386913870138711387213873138741387513876138771387813879138801388113882138831388413885138861388713888138891389013891138921389313894138951389613897138981389913900139011390213903139041390513906139071390813909139101391113912139131391413915139161391713918139191392013921139221392313924139251392613927139281392913930139311393213933139341393513936139371393813939139401394113942139431394413945139461394713948139491395013951139521395313954139551395613957139581395913960139611396213963139641396513966139671396813969139701397113972139731397413975139761397713978139791398013981139821398313984139851398613987139881398913990139911399213993139941399513996139971399813999140001400114002140031400414005140061400714008140091401014011140121401314014140151401614017140181401914020140211402214023140241402514026140271402814029140301403114032140331403414035140361403714038140391404014041140421404314044140451404614047140481404914050140511405214053140541405514056140571405814059140601406114062140631406414065140661406714068140691407014071140721407314074140751407614077140781407914080140811408214083140841408514086140871408814089140901409114092140931409414095140961409714098140991410014101141021410314104141051410614107141081410914110141111411214113141141411514116141171411814119141201412114122141231412414125141261412714128141291413014131141321413314134141351413614137141381413914140141411414214143141441414514146141471414814149141501415114152141531415414155141561415714158141591416014161141621416314164141651416614167141681416914170141711417214173141741417514176141771417814179141801418114182141831418414185141861418714188141891419014191141921419314194141951419614197141981419914200142011420214203142041420514206142071420814209142101421114212142131421414215142161421714218142191422014221142221422314224142251422614227142281422914230142311423214233142341423514236142371423814239142401424114242142431424414245142461424714248142491425014251142521425314254142551425614257142581425914260142611426214263142641426514266142671426814269142701427114272142731427414275142761427714278142791428014281142821428314284142851428614287142881428914290142911429214293142941429514296142971429814299143001430114302143031430414305143061430714308143091431014311143121431314314143151431614317143181431914320143211432214323143241432514326143271432814329143301433114332143331433414335143361433714338143391434014341143421434314344143451434614347143481434914350143511435214353143541435514356143571435814359143601436114362143631436414365143661436714368143691437014371143721437314374143751437614377143781437914380143811438214383143841438514386143871438814389143901439114392143931439414395143961439714398143991440014401144021440314404144051440614407144081440914410144111441214413144141441514416144171441814419144201442114422144231442414425144261442714428144291443014431144321443314434144351443614437144381443914440144411444214443144441444514446144471444814449144501445114452144531445414455144561445714458144591446014461144621446314464144651446614467144681446914470144711447214473144741447514476144771447814479144801448114482144831448414485144861448714488144891449014491144921449314494144951449614497144981449914500145011450214503145041450514506145071450814509145101451114512145131451414515145161451714518145191452014521145221452314524145251452614527145281452914530145311453214533145341453514536145371453814539145401454114542145431454414545145461454714548145491455014551145521455314554145551455614557145581455914560145611456214563145641456514566145671456814569145701457114572145731457414575145761457714578145791458014581145821458314584145851458614587145881458914590145911459214593145941459514596145971459814599146001460114602146031460414605146061460714608146091461014611146121461314614146151461614617146181461914620146211462214623146241462514626146271462814629146301463114632146331463414635146361463714638146391464014641146421464314644146451464614647146481464914650146511465214653146541465514656146571465814659146601466114662146631466414665146661466714668146691467014671146721467314674146751467614677146781467914680146811468214683146841468514686146871468814689146901469114692146931469414695146961469714698146991470014701147021470314704147051470614707147081470914710147111471214713147141471514716147171471814719147201472114722147231472414725147261472714728147291473014731147321473314734147351473614737147381473914740147411474214743147441474514746147471474814749147501475114752147531475414755147561475714758147591476014761147621476314764147651476614767147681476914770147711477214773147741477514776147771477814779147801478114782147831478414785147861478714788147891479014791147921479314794147951479614797147981479914800148011480214803148041480514806148071480814809148101481114812148131481414815148161481714818148191482014821148221482314824148251482614827148281482914830148311483214833148341483514836148371483814839148401484114842148431484414845148461484714848148491485014851148521485314854148551485614857148581485914860148611486214863148641486514866148671486814869148701487114872148731487414875148761487714878148791488014881148821488314884148851488614887148881488914890148911489214893148941489514896148971489814899149001490114902149031490414905149061490714908149091491014911149121491314914149151491614917149181491914920149211492214923149241492514926149271492814929149301493114932149331493414935149361493714938149391494014941149421494314944149451494614947149481494914950149511495214953149541495514956149571495814959149601496114962149631496414965149661496714968149691497014971149721497314974149751497614977149781497914980149811498214983149841498514986149871498814989149901499114992149931499414995149961499714998149991500015001150021500315004150051500615007150081500915010150111501215013150141501515016150171501815019150201502115022150231502415025150261502715028150291503015031150321503315034150351503615037150381503915040150411504215043150441504515046150471504815049150501505115052150531505415055150561505715058150591506015061150621506315064150651506615067150681506915070150711507215073150741507515076150771507815079150801508115082150831508415085150861508715088150891509015091150921509315094150951509615097150981509915100151011510215103151041510515106151071510815109151101511115112151131511415115151161511715118151191512015121151221512315124151251512615127151281512915130151311513215133151341513515136151371513815139151401514115142151431514415145151461514715148151491515015151151521515315154151551515615157151581515915160151611516215163151641516515166151671516815169151701517115172151731517415175151761517715178151791518015181151821518315184151851518615187151881518915190151911519215193151941519515196151971519815199152001520115202152031520415205152061520715208152091521015211152121521315214152151521615217152181521915220152211522215223152241522515226152271522815229152301523115232152331523415235152361523715238152391524015241152421524315244152451524615247152481524915250152511525215253152541525515256152571525815259152601526115262152631526415265152661526715268152691527015271152721527315274152751527615277152781527915280152811528215283152841528515286152871528815289152901529115292152931529415295152961529715298152991530015301153021530315304153051530615307153081530915310153111531215313153141531515316153171531815319153201532115322153231532415325153261532715328153291533015331153321533315334153351533615337153381533915340153411534215343153441534515346153471534815349153501535115352153531535415355153561535715358153591536015361153621536315364153651536615367153681536915370153711537215373153741537515376153771537815379153801538115382153831538415385153861538715388153891539015391153921539315394153951539615397153981539915400154011540215403154041540515406154071540815409154101541115412154131541415415154161541715418154191542015421154221542315424154251542615427154281542915430154311543215433154341543515436154371543815439154401544115442154431544415445154461544715448154491545015451154521545315454154551545615457154581545915460154611546215463154641546515466154671546815469154701547115472154731547415475154761547715478154791548015481154821548315484154851548615487154881548915490154911549215493154941549515496154971549815499155001550115502155031550415505155061550715508155091551015511155121551315514155151551615517155181551915520155211552215523155241552515526155271552815529155301553115532155331553415535155361553715538155391554015541155421554315544155451554615547155481554915550155511555215553155541555515556155571555815559155601556115562155631556415565155661556715568155691557015571155721557315574155751557615577155781557915580155811558215583155841558515586155871558815589155901559115592155931559415595155961559715598155991560015601156021560315604156051560615607156081560915610156111561215613156141561515616156171561815619156201562115622156231562415625156261562715628156291563015631156321563315634156351563615637156381563915640156411564215643156441564515646156471564815649156501565115652156531565415655156561565715658156591566015661156621566315664156651566615667156681566915670156711567215673156741567515676156771567815679156801568115682156831568415685156861568715688156891569015691156921569315694156951569615697156981569915700157011570215703157041570515706157071570815709157101571115712157131571415715157161571715718157191572015721157221572315724157251572615727157281572915730157311573215733157341573515736157371573815739157401574115742157431574415745157461574715748157491575015751157521575315754157551575615757157581575915760157611576215763157641576515766157671576815769157701577115772157731577415775157761577715778157791578015781157821578315784157851578615787157881578915790157911579215793157941579515796157971579815799158001580115802158031580415805158061580715808158091581015811158121581315814158151581615817158181581915820158211582215823158241582515826158271582815829158301583115832158331583415835158361583715838158391584015841158421584315844158451584615847158481584915850158511585215853158541585515856158571585815859158601586115862158631586415865158661586715868158691587015871158721587315874158751587615877158781587915880158811588215883158841588515886158871588815889158901589115892158931589415895158961589715898158991590015901159021590315904159051590615907159081590915910159111591215913159141591515916159171591815919159201592115922159231592415925159261592715928159291593015931159321593315934159351593615937159381593915940159411594215943159441594515946159471594815949159501595115952159531595415955159561595715958159591596015961159621596315964159651596615967159681596915970159711597215973159741597515976159771597815979159801598115982159831598415985159861598715988159891599015991159921599315994159951599615997159981599916000160011600216003160041600516006160071600816009160101601116012160131601416015160161601716018160191602016021160221602316024160251602616027160281602916030160311603216033160341603516036160371603816039160401604116042160431604416045160461604716048160491605016051160521605316054160551605616057160581605916060160611606216063160641606516066160671606816069160701607116072160731607416075160761607716078160791608016081160821608316084160851608616087160881608916090160911609216093160941609516096160971609816099161001610116102161031610416105161061610716108161091611016111161121611316114161151611616117161181611916120161211612216123161241612516126161271612816129161301613116132161331613416135161361613716138161391614016141161421614316144161451614616147161481614916150161511615216153161541615516156161571615816159161601616116162161631616416165161661616716168161691617016171161721617316174161751617616177161781617916180161811618216183161841618516186161871618816189161901619116192161931619416195161961619716198161991620016201162021620316204162051620616207162081620916210162111621216213162141621516216162171621816219162201622116222162231622416225162261622716228162291623016231162321623316234162351623616237162381623916240162411624216243162441624516246162471624816249162501625116252162531625416255162561625716258162591626016261162621626316264162651626616267162681626916270162711627216273162741627516276162771627816279162801628116282162831628416285162861628716288162891629016291162921629316294162951629616297162981629916300163011630216303163041630516306163071630816309163101631116312163131631416315163161631716318163191632016321163221632316324163251632616327163281632916330163311633216333163341633516336163371633816339163401634116342163431634416345163461634716348163491635016351163521635316354163551635616357163581635916360163611636216363163641636516366163671636816369163701637116372163731637416375163761637716378163791638016381163821638316384163851638616387163881638916390163911639216393163941639516396163971639816399164001640116402164031640416405164061640716408164091641016411164121641316414164151641616417164181641916420164211642216423164241642516426164271642816429164301643116432164331643416435164361643716438164391644016441164421644316444164451644616447164481644916450164511645216453164541645516456164571645816459164601646116462164631646416465164661646716468164691647016471164721647316474164751647616477164781647916480164811648216483164841648516486164871648816489164901649116492164931649416495164961649716498164991650016501165021650316504165051650616507165081650916510165111651216513165141651516516165171651816519165201652116522165231652416525165261652716528165291653016531165321653316534165351653616537165381653916540165411654216543165441654516546165471654816549165501655116552165531655416555165561655716558165591656016561165621656316564165651656616567165681656916570165711657216573165741657516576165771657816579165801658116582165831658416585165861658716588165891659016591165921659316594165951659616597165981659916600166011660216603166041660516606166071660816609166101661116612166131661416615166161661716618166191662016621166221662316624166251662616627166281662916630166311663216633166341663516636166371663816639166401664116642166431664416645166461664716648166491665016651166521665316654166551665616657166581665916660166611666216663166641666516666166671666816669166701667116672166731667416675166761667716678166791668016681166821668316684166851668616687166881668916690166911669216693166941669516696166971669816699167001670116702167031670416705167061670716708167091671016711167121671316714167151671616717167181671916720167211672216723167241672516726167271672816729167301673116732167331673416735167361673716738167391674016741167421674316744167451674616747167481674916750167511675216753167541675516756167571675816759167601676116762167631676416765167661676716768167691677016771167721677316774167751677616777167781677916780167811678216783167841678516786167871678816789167901679116792167931679416795167961679716798167991680016801168021680316804168051680616807168081680916810168111681216813168141681516816168171681816819168201682116822168231682416825168261682716828168291683016831168321683316834168351683616837168381683916840168411684216843168441684516846168471684816849168501685116852168531685416855168561685716858168591686016861168621686316864168651686616867168681686916870168711687216873168741687516876168771687816879168801688116882168831688416885168861688716888168891689016891168921689316894168951689616897168981689916900169011690216903169041690516906169071690816909169101691116912169131691416915169161691716918169191692016921169221692316924169251692616927169281692916930169311693216933169341693516936169371693816939169401694116942169431694416945169461694716948169491695016951169521695316954169551695616957169581695916960169611696216963169641696516966169671696816969169701697116972169731697416975169761697716978169791698016981169821698316984169851698616987169881698916990169911699216993169941699516996169971699816999170001700117002170031700417005170061700717008170091701017011170121701317014170151701617017170181701917020170211702217023170241702517026170271702817029170301703117032170331703417035170361703717038170391704017041170421704317044170451704617047170481704917050170511705217053170541705517056170571705817059170601706117062170631706417065170661706717068170691707017071170721707317074170751707617077170781707917080170811708217083170841708517086170871708817089170901709117092170931709417095170961709717098170991710017101171021710317104171051710617107171081710917110171111711217113171141711517116171171711817119171201712117122171231712417125171261712717128171291713017131171321713317134171351713617137171381713917140171411714217143171441714517146171471714817149171501715117152171531715417155171561715717158171591716017161171621716317164171651716617167171681716917170171711717217173171741717517176171771717817179171801718117182171831718417185171861718717188171891719017191171921719317194171951719617197171981719917200172011720217203172041720517206172071720817209172101721117212172131721417215172161721717218172191722017221172221722317224172251722617227172281722917230172311723217233172341723517236172371723817239172401724117242172431724417245172461724717248172491725017251172521725317254172551725617257172581725917260172611726217263172641726517266172671726817269172701727117272172731727417275172761727717278172791728017281172821728317284172851728617287172881728917290172911729217293172941729517296172971729817299173001730117302173031730417305173061730717308173091731017311173121731317314173151731617317173181731917320173211732217323173241732517326173271732817329173301733117332173331733417335173361733717338173391734017341173421734317344173451734617347173481734917350173511735217353173541735517356173571735817359173601736117362173631736417365173661736717368173691737017371173721737317374173751737617377173781737917380173811738217383173841738517386173871738817389173901739117392173931739417395173961739717398173991740017401174021740317404174051740617407174081740917410174111741217413174141741517416174171741817419174201742117422174231742417425174261742717428174291743017431174321743317434174351743617437174381743917440174411744217443174441744517446174471744817449174501745117452174531745417455174561745717458174591746017461174621746317464174651746617467174681746917470174711747217473174741747517476174771747817479174801748117482174831748417485174861748717488174891749017491174921749317494174951749617497174981749917500175011750217503175041750517506175071750817509175101751117512175131751417515175161751717518175191752017521175221752317524175251752617527175281752917530175311753217533175341753517536175371753817539175401754117542175431754417545175461754717548175491755017551175521755317554175551755617557175581755917560175611756217563175641756517566175671756817569175701757117572175731757417575175761757717578175791758017581175821758317584175851758617587175881758917590175911759217593175941759517596175971759817599176001760117602176031760417605176061760717608176091761017611176121761317614176151761617617176181761917620176211762217623176241762517626176271762817629176301763117632176331763417635176361763717638176391764017641176421764317644176451764617647176481764917650176511765217653176541765517656176571765817659176601766117662176631766417665176661766717668176691767017671176721767317674176751767617677176781767917680176811768217683176841768517686176871768817689176901769117692176931769417695176961769717698176991770017701177021770317704177051770617707177081770917710177111771217713177141771517716177171771817719177201772117722177231772417725177261772717728177291773017731177321773317734177351773617737177381773917740177411774217743177441774517746177471774817749177501775117752177531775417755177561775717758177591776017761177621776317764177651776617767177681776917770177711777217773177741777517776177771777817779177801778117782177831778417785177861778717788177891779017791177921779317794177951779617797177981779917800178011780217803178041780517806178071780817809178101781117812178131781417815178161781717818178191782017821178221782317824178251782617827178281782917830178311783217833178341783517836178371783817839178401784117842178431784417845178461784717848178491785017851178521785317854178551785617857178581785917860178611786217863178641786517866178671786817869178701787117872178731787417875178761787717878178791788017881178821788317884178851788617887178881788917890178911789217893178941789517896178971789817899179001790117902179031790417905179061790717908179091791017911179121791317914179151791617917179181791917920179211792217923179241792517926179271792817929179301793117932179331793417935179361793717938179391794017941179421794317944179451794617947179481794917950179511795217953179541795517956179571795817959179601796117962179631796417965179661796717968179691797017971179721797317974179751797617977179781797917980179811798217983179841798517986179871798817989179901799117992179931799417995179961799717998179991800018001180021800318004180051800618007180081800918010180111801218013180141801518016180171801818019180201802118022180231802418025180261802718028180291803018031180321803318034180351803618037180381803918040180411804218043180441804518046180471804818049180501805118052180531805418055180561805718058180591806018061180621806318064180651806618067180681806918070180711807218073180741807518076180771807818079180801808118082180831808418085180861808718088180891809018091180921809318094180951809618097180981809918100181011810218103181041810518106181071810818109181101811118112181131811418115181161811718118181191812018121181221812318124181251812618127181281812918130181311813218133181341813518136181371813818139181401814118142181431814418145181461814718148181491815018151181521815318154181551815618157181581815918160181611816218163181641816518166181671816818169181701817118172181731817418175181761817718178181791818018181181821818318184181851818618187181881818918190181911819218193181941819518196181971819818199182001820118202182031820418205182061820718208182091821018211182121821318214182151821618217182181821918220182211822218223182241822518226182271822818229182301823118232182331823418235182361823718238182391824018241182421824318244182451824618247182481824918250182511825218253182541825518256182571825818259182601826118262182631826418265182661826718268182691827018271182721827318274182751827618277182781827918280182811828218283182841828518286182871828818289182901829118292182931829418295182961829718298182991830018301183021830318304183051830618307183081830918310183111831218313183141831518316183171831818319183201832118322183231832418325183261832718328183291833018331183321833318334183351833618337183381833918340183411834218343183441834518346183471834818349183501835118352183531835418355183561835718358183591836018361183621836318364183651836618367183681836918370183711837218373183741837518376183771837818379183801838118382183831838418385183861838718388183891839018391183921839318394183951839618397183981839918400184011840218403184041840518406184071840818409184101841118412184131841418415184161841718418184191842018421184221842318424184251842618427184281842918430184311843218433184341843518436184371843818439184401844118442184431844418445184461844718448184491845018451184521845318454184551845618457184581845918460184611846218463184641846518466184671846818469184701847118472184731847418475184761847718478184791848018481184821848318484184851848618487184881848918490184911849218493184941849518496184971849818499185001850118502185031850418505185061850718508185091851018511185121851318514185151851618517185181851918520185211852218523185241852518526185271852818529185301853118532185331853418535185361853718538185391854018541185421854318544185451854618547185481854918550185511855218553185541855518556185571855818559185601856118562185631856418565185661856718568185691857018571185721857318574185751857618577185781857918580185811858218583185841858518586185871858818589185901859118592185931859418595185961859718598185991860018601186021860318604186051860618607186081860918610186111861218613186141861518616186171861818619186201862118622186231862418625186261862718628186291863018631186321863318634186351863618637186381863918640186411864218643186441864518646186471864818649186501865118652186531865418655186561865718658186591866018661186621866318664186651866618667186681866918670186711867218673186741867518676186771867818679186801868118682186831868418685186861868718688186891869018691186921869318694186951869618697186981869918700187011870218703187041870518706187071870818709187101871118712187131871418715187161871718718187191872018721187221872318724187251872618727187281872918730187311873218733187341873518736187371873818739187401874118742187431874418745187461874718748187491875018751187521875318754187551875618757187581875918760187611876218763187641876518766187671876818769187701877118772187731877418775187761877718778187791878018781187821878318784187851878618787187881878918790187911879218793187941879518796187971879818799188001880118802188031880418805188061880718808188091881018811188121881318814188151881618817188181881918820188211882218823188241882518826188271882818829188301883118832188331883418835188361883718838188391884018841188421884318844188451884618847188481884918850188511885218853188541885518856188571885818859188601886118862188631886418865188661886718868188691887018871188721887318874188751887618877188781887918880188811888218883188841888518886188871888818889188901889118892188931889418895188961889718898188991890018901189021890318904189051890618907189081890918910189111891218913189141891518916189171891818919189201892118922189231892418925189261892718928189291893018931189321893318934189351893618937189381893918940189411894218943189441894518946189471894818949189501895118952189531895418955189561895718958189591896018961189621896318964189651896618967189681896918970189711897218973189741897518976189771897818979189801898118982189831898418985189861898718988189891899018991189921899318994189951899618997189981899919000190011900219003190041900519006190071900819009190101901119012190131901419015190161901719018190191902019021190221902319024190251902619027190281902919030190311903219033190341903519036190371903819039190401904119042190431904419045190461904719048190491905019051190521905319054190551905619057190581905919060190611906219063190641906519066190671906819069190701907119072190731907419075190761907719078190791908019081190821908319084190851908619087190881908919090190911909219093190941909519096190971909819099191001910119102191031910419105191061910719108191091911019111191121911319114191151911619117191181911919120191211912219123191241912519126191271912819129191301913119132191331913419135191361913719138191391914019141191421914319144191451914619147191481914919150191511915219153191541915519156191571915819159191601916119162191631916419165191661916719168191691917019171191721917319174191751917619177191781917919180191811918219183191841918519186191871918819189191901919119192191931919419195191961919719198191991920019201192021920319204192051920619207192081920919210192111921219213192141921519216192171921819219192201922119222192231922419225192261922719228192291923019231192321923319234192351923619237192381923919240192411924219243192441924519246192471924819249192501925119252192531925419255192561925719258192591926019261192621926319264192651926619267192681926919270192711927219273192741927519276192771927819279192801928119282192831928419285192861928719288192891929019291192921929319294192951929619297192981929919300193011930219303193041930519306193071930819309193101931119312193131931419315193161931719318193191932019321193221932319324193251932619327193281932919330193311933219333193341933519336193371933819339193401934119342193431934419345193461934719348193491935019351193521935319354193551935619357193581935919360193611936219363193641936519366193671936819369193701937119372193731937419375193761937719378193791938019381193821938319384193851938619387193881938919390193911939219393193941939519396193971939819399194001940119402194031940419405194061940719408194091941019411194121941319414194151941619417194181941919420194211942219423194241942519426194271942819429194301943119432194331943419435194361943719438194391944019441194421944319444194451944619447194481944919450194511945219453194541945519456194571945819459194601946119462194631946419465194661946719468194691947019471194721947319474194751947619477194781947919480194811948219483194841948519486194871948819489194901949119492194931949419495194961949719498194991950019501195021950319504195051950619507195081950919510195111951219513195141951519516195171951819519195201952119522195231952419525195261952719528195291953019531195321953319534195351953619537195381953919540195411954219543195441954519546195471954819549195501955119552195531955419555195561955719558195591956019561195621956319564195651956619567195681956919570195711957219573195741957519576195771957819579195801958119582195831958419585195861958719588195891959019591195921959319594195951959619597195981959919600196011960219603196041960519606196071960819609196101961119612196131961419615196161961719618196191962019621196221962319624196251962619627196281962919630196311963219633196341963519636196371963819639196401964119642196431964419645196461964719648196491965019651196521965319654196551965619657196581965919660196611966219663196641966519666196671966819669196701967119672196731967419675196761967719678196791968019681196821968319684196851968619687196881968919690196911969219693196941969519696196971969819699197001970119702197031970419705197061970719708197091971019711197121971319714197151971619717197181971919720197211972219723197241972519726197271972819729197301973119732197331973419735197361973719738197391974019741197421974319744197451974619747197481974919750197511975219753197541975519756197571975819759197601976119762197631976419765197661976719768197691977019771197721977319774197751977619777197781977919780197811978219783197841978519786197871978819789197901979119792197931979419795197961979719798197991980019801198021980319804198051980619807198081980919810198111981219813198141981519816198171981819819198201982119822198231982419825198261982719828198291983019831198321983319834198351983619837198381983919840198411984219843198441984519846198471984819849198501985119852198531985419855198561985719858198591986019861198621986319864198651986619867198681986919870198711987219873198741987519876198771987819879198801988119882198831988419885198861988719888198891989019891198921989319894198951989619897198981989919900199011990219903199041990519906199071990819909199101991119912199131991419915199161991719918199191992019921199221992319924199251992619927199281992919930199311993219933199341993519936199371993819939199401994119942199431994419945199461994719948199491995019951199521995319954199551995619957199581995919960199611996219963199641996519966199671996819969199701997119972199731997419975199761997719978199791998019981199821998319984199851998619987199881998919990199911999219993199941999519996199971999819999200002000120002200032000420005200062000720008200092001020011200122001320014200152001620017200182001920020200212002220023200242002520026200272002820029200302003120032200332003420035200362003720038200392004020041200422004320044200452004620047200482004920050200512005220053200542005520056200572005820059200602006120062200632006420065200662006720068200692007020071200722007320074200752007620077200782007920080200812008220083200842008520086200872008820089200902009120092200932009420095200962009720098200992010020101201022010320104201052010620107201082010920110201112011220113201142011520116201172011820119201202012120122201232012420125201262012720128201292013020131201322013320134201352013620137201382013920140201412014220143201442014520146201472014820149201502015120152201532015420155201562015720158201592016020161201622016320164201652016620167201682016920170201712017220173201742017520176201772017820179201802018120182201832018420185201862018720188201892019020191201922019320194201952019620197201982019920200202012020220203202042020520206202072020820209202102021120212202132021420215202162021720218202192022020221202222022320224202252022620227202282022920230202312023220233202342023520236202372023820239202402024120242202432024420245202462024720248202492025020251202522025320254202552025620257202582025920260202612026220263202642026520266202672026820269202702027120272202732027420275202762027720278202792028020281202822028320284202852028620287202882028920290202912029220293202942029520296202972029820299203002030120302203032030420305203062030720308203092031020311203122031320314203152031620317203182031920320203212032220323203242032520326203272032820329203302033120332203332033420335203362033720338203392034020341203422034320344203452034620347203482034920350203512035220353203542035520356203572035820359203602036120362203632036420365203662036720368203692037020371203722037320374203752037620377203782037920380203812038220383203842038520386203872038820389203902039120392203932039420395203962039720398203992040020401204022040320404204052040620407204082040920410204112041220413204142041520416204172041820419204202042120422204232042420425204262042720428204292043020431204322043320434204352043620437204382043920440204412044220443204442044520446204472044820449204502045120452204532045420455204562045720458204592046020461204622046320464204652046620467204682046920470204712047220473204742047520476204772047820479204802048120482204832048420485204862048720488204892049020491204922049320494204952049620497204982049920500205012050220503205042050520506205072050820509205102051120512205132051420515205162051720518205192052020521205222052320524205252052620527205282052920530205312053220533205342053520536205372053820539205402054120542205432054420545205462054720548205492055020551205522055320554205552055620557205582055920560205612056220563205642056520566205672056820569205702057120572205732057420575205762057720578205792058020581205822058320584205852058620587205882058920590205912059220593205942059520596205972059820599206002060120602206032060420605206062060720608206092061020611206122061320614206152061620617206182061920620206212062220623206242062520626206272062820629206302063120632206332063420635206362063720638206392064020641206422064320644206452064620647206482064920650206512065220653206542065520656206572065820659206602066120662206632066420665206662066720668206692067020671206722067320674206752067620677206782067920680206812068220683206842068520686206872068820689206902069120692206932069420695206962069720698206992070020701207022070320704207052070620707207082070920710207112071220713207142071520716207172071820719207202072120722207232072420725207262072720728207292073020731207322073320734207352073620737207382073920740207412074220743207442074520746207472074820749207502075120752207532075420755207562075720758207592076020761207622076320764207652076620767207682076920770207712077220773207742077520776207772077820779207802078120782207832078420785207862078720788207892079020791207922079320794207952079620797207982079920800208012080220803208042080520806208072080820809208102081120812208132081420815208162081720818208192082020821208222082320824208252082620827208282082920830208312083220833208342083520836208372083820839208402084120842208432084420845208462084720848208492085020851208522085320854208552085620857208582085920860208612086220863208642086520866208672086820869208702087120872208732087420875208762087720878208792088020881208822088320884208852088620887208882088920890208912089220893208942089520896208972089820899209002090120902209032090420905209062090720908209092091020911209122091320914209152091620917209182091920920209212092220923209242092520926209272092820929209302093120932209332093420935209362093720938209392094020941209422094320944209452094620947209482094920950209512095220953209542095520956209572095820959209602096120962209632096420965209662096720968209692097020971209722097320974209752097620977209782097920980209812098220983209842098520986209872098820989209902099120992209932099420995209962099720998209992100021001210022100321004210052100621007210082100921010210112101221013210142101521016210172101821019210202102121022210232102421025210262102721028210292103021031210322103321034210352103621037210382103921040210412104221043210442104521046210472104821049210502105121052210532105421055210562105721058210592106021061210622106321064210652106621067210682106921070210712107221073210742107521076210772107821079210802108121082210832108421085210862108721088210892109021091210922109321094210952109621097210982109921100211012110221103211042110521106211072110821109211102111121112211132111421115211162111721118211192112021121211222112321124211252112621127211282112921130211312113221133211342113521136211372113821139211402114121142211432114421145211462114721148211492115021151211522115321154211552115621157211582115921160211612116221163211642116521166211672116821169211702117121172211732117421175211762117721178211792118021181211822118321184211852118621187211882118921190211912119221193211942119521196211972119821199212002120121202212032120421205212062120721208212092121021211212122121321214212152121621217212182121921220212212122221223212242122521226212272122821229212302123121232212332123421235212362123721238212392124021241212422124321244212452124621247212482124921250212512125221253212542125521256212572125821259212602126121262212632126421265212662126721268212692127021271212722127321274212752127621277212782127921280212812128221283212842128521286212872128821289212902129121292212932129421295212962129721298212992130021301213022130321304213052130621307213082130921310213112131221313213142131521316213172131821319213202132121322213232132421325213262132721328213292133021331213322133321334213352133621337213382133921340213412134221343213442134521346213472134821349213502135121352213532135421355213562135721358213592136021361213622136321364213652136621367213682136921370213712137221373213742137521376213772137821379213802138121382213832138421385213862138721388213892139021391213922139321394213952139621397213982139921400214012140221403214042140521406214072140821409214102141121412214132141421415214162141721418214192142021421214222142321424214252142621427214282142921430214312143221433214342143521436214372143821439214402144121442214432144421445214462144721448214492145021451214522145321454214552145621457214582145921460214612146221463214642146521466214672146821469214702147121472214732147421475214762147721478214792148021481214822148321484214852148621487214882148921490214912149221493214942149521496214972149821499215002150121502215032150421505215062150721508215092151021511215122151321514215152151621517215182151921520215212152221523215242152521526215272152821529215302153121532215332153421535215362153721538215392154021541215422154321544215452154621547215482154921550215512155221553215542155521556215572155821559215602156121562215632156421565215662156721568215692157021571215722157321574215752157621577215782157921580215812158221583215842158521586215872158821589215902159121592215932159421595215962159721598215992160021601216022160321604216052160621607216082160921610216112161221613216142161521616216172161821619216202162121622216232162421625216262162721628216292163021631216322163321634216352163621637216382163921640216412164221643216442164521646216472164821649216502165121652216532165421655216562165721658216592166021661216622166321664216652166621667216682166921670216712167221673216742167521676216772167821679216802168121682216832168421685216862168721688216892169021691216922169321694216952169621697216982169921700217012170221703217042170521706217072170821709217102171121712217132171421715217162171721718217192172021721217222172321724217252172621727217282172921730217312173221733217342173521736217372173821739217402174121742217432174421745217462174721748217492175021751217522175321754217552175621757217582175921760217612176221763217642176521766217672176821769217702177121772217732177421775217762177721778217792178021781217822178321784217852178621787217882178921790217912179221793217942179521796217972179821799218002180121802218032180421805218062180721808218092181021811218122181321814218152181621817218182181921820218212182221823218242182521826218272182821829218302183121832218332183421835218362183721838218392184021841218422184321844218452184621847218482184921850218512185221853218542185521856218572185821859218602186121862218632186421865218662186721868218692187021871218722187321874218752187621877218782187921880218812188221883218842188521886218872188821889218902189121892218932189421895218962189721898218992190021901219022190321904219052190621907219082190921910219112191221913219142191521916219172191821919219202192121922219232192421925219262192721928219292193021931219322193321934219352193621937219382193921940219412194221943219442194521946219472194821949219502195121952219532195421955219562195721958219592196021961219622196321964219652196621967219682196921970219712197221973219742197521976219772197821979219802198121982219832198421985219862198721988219892199021991219922199321994219952199621997219982199922000220012200222003220042200522006220072200822009220102201122012220132201422015220162201722018220192202022021220222202322024220252202622027220282202922030220312203222033220342203522036220372203822039220402204122042220432204422045220462204722048220492205022051220522205322054220552205622057220582205922060220612206222063220642206522066220672206822069220702207122072220732207422075220762207722078220792208022081220822208322084220852208622087220882208922090220912209222093220942209522096220972209822099221002210122102221032210422105221062210722108221092211022111221122211322114221152211622117221182211922120221212212222123221242212522126221272212822129221302213122132221332213422135221362213722138221392214022141221422214322144221452214622147221482214922150221512215222153221542215522156221572215822159221602216122162221632216422165221662216722168221692217022171221722217322174221752217622177221782217922180221812218222183221842218522186221872218822189221902219122192221932219422195221962219722198221992220022201222022220322204222052220622207222082220922210222112221222213222142221522216222172221822219222202222122222222232222422225222262222722228222292223022231222322223322234222352223622237222382223922240222412224222243222442224522246222472224822249222502225122252222532225422255222562225722258222592226022261222622226322264222652226622267222682226922270222712227222273222742227522276222772227822279222802228122282222832228422285222862228722288222892229022291222922229322294222952229622297222982229922300223012230222303223042230522306223072230822309223102231122312223132231422315223162231722318223192232022321223222232322324223252232622327223282232922330223312233222333223342233522336223372233822339223402234122342223432234422345223462234722348223492235022351223522235322354223552235622357223582235922360223612236222363223642236522366223672236822369223702237122372223732237422375223762237722378223792238022381223822238322384223852238622387223882238922390223912239222393223942239522396223972239822399224002240122402224032240422405224062240722408224092241022411224122241322414224152241622417224182241922420224212242222423224242242522426224272242822429224302243122432224332243422435224362243722438224392244022441224422244322444224452244622447224482244922450224512245222453224542245522456224572245822459224602246122462224632246422465224662246722468224692247022471224722247322474224752247622477224782247922480224812248222483224842248522486224872248822489224902249122492224932249422495224962249722498224992250022501225022250322504225052250622507225082250922510225112251222513225142251522516225172251822519225202252122522225232252422525225262252722528225292253022531225322253322534225352253622537225382253922540225412254222543225442254522546225472254822549225502255122552225532255422555225562255722558225592256022561225622256322564225652256622567225682256922570225712257222573225742257522576225772257822579225802258122582225832258422585225862258722588225892259022591225922259322594225952259622597225982259922600226012260222603226042260522606226072260822609226102261122612226132261422615226162261722618226192262022621226222262322624226252262622627226282262922630226312263222633226342263522636226372263822639226402264122642226432264422645226462264722648226492265022651226522265322654226552265622657226582265922660226612266222663226642266522666226672266822669226702267122672226732267422675226762267722678226792268022681226822268322684226852268622687226882268922690226912269222693226942269522696226972269822699227002270122702227032270422705227062270722708227092271022711227122271322714227152271622717227182271922720227212272222723227242272522726227272272822729227302273122732227332273422735227362273722738227392274022741227422274322744227452274622747227482274922750227512275222753227542275522756227572275822759227602276122762227632276422765227662276722768227692277022771227722277322774227752277622777227782277922780227812278222783227842278522786227872278822789227902279122792227932279422795227962279722798227992280022801228022280322804228052280622807228082280922810228112281222813228142281522816228172281822819228202282122822228232282422825228262282722828228292283022831228322283322834228352283622837228382283922840228412284222843228442284522846228472284822849228502285122852228532285422855228562285722858228592286022861228622286322864228652286622867228682286922870228712287222873228742287522876228772287822879228802288122882228832288422885228862288722888228892289022891228922289322894228952289622897228982289922900229012290222903229042290522906229072290822909229102291122912229132291422915229162291722918229192292022921229222292322924229252292622927229282292922930229312293222933229342293522936229372293822939229402294122942229432294422945229462294722948229492295022951229522295322954229552295622957229582295922960229612296222963229642296522966229672296822969229702297122972229732297422975229762297722978229792298022981229822298322984229852298622987229882298922990229912299222993229942299522996229972299822999230002300123002230032300423005230062300723008230092301023011230122301323014230152301623017230182301923020230212302223023230242302523026230272302823029230302303123032230332303423035230362303723038230392304023041230422304323044230452304623047230482304923050230512305223053230542305523056230572305823059230602306123062230632306423065230662306723068230692307023071230722307323074230752307623077230782307923080230812308223083230842308523086230872308823089230902309123092230932309423095230962309723098230992310023101231022310323104231052310623107231082310923110231112311223113231142311523116231172311823119231202312123122231232312423125231262312723128231292313023131231322313323134231352313623137231382313923140231412314223143231442314523146231472314823149231502315123152231532315423155231562315723158231592316023161231622316323164231652316623167231682316923170231712317223173231742317523176231772317823179231802318123182231832318423185231862318723188231892319023191231922319323194231952319623197231982319923200232012320223203232042320523206232072320823209232102321123212232132321423215232162321723218232192322023221232222322323224232252322623227232282322923230232312323223233232342323523236232372323823239232402324123242232432324423245232462324723248232492325023251232522325323254232552325623257232582325923260232612326223263232642326523266232672326823269232702327123272232732327423275232762327723278232792328023281232822328323284232852328623287232882328923290232912329223293232942329523296232972329823299233002330123302233032330423305233062330723308233092331023311233122331323314233152331623317233182331923320233212332223323233242332523326233272332823329233302333123332233332333423335233362333723338233392334023341233422334323344233452334623347233482334923350233512335223353233542335523356233572335823359233602336123362233632336423365233662336723368233692337023371233722337323374233752337623377233782337923380233812338223383233842338523386233872338823389233902339123392233932339423395233962339723398233992340023401234022340323404234052340623407234082340923410234112341223413234142341523416234172341823419234202342123422234232342423425234262342723428234292343023431234322343323434234352343623437234382343923440234412344223443234442344523446234472344823449234502345123452234532345423455234562345723458234592346023461234622346323464234652346623467234682346923470234712347223473234742347523476234772347823479234802348123482234832348423485234862348723488234892349023491234922349323494234952349623497234982349923500235012350223503235042350523506235072350823509235102351123512235132351423515235162351723518235192352023521235222352323524235252352623527235282352923530235312353223533235342353523536235372353823539235402354123542235432354423545235462354723548235492355023551235522355323554235552355623557235582355923560235612356223563235642356523566235672356823569235702357123572235732357423575235762357723578235792358023581235822358323584235852358623587235882358923590235912359223593235942359523596235972359823599236002360123602236032360423605236062360723608236092361023611236122361323614236152361623617236182361923620236212362223623236242362523626236272362823629236302363123632236332363423635236362363723638236392364023641236422364323644236452364623647236482364923650236512365223653236542365523656236572365823659236602366123662236632366423665236662366723668236692367023671236722367323674236752367623677236782367923680236812368223683236842368523686236872368823689236902369123692236932369423695236962369723698236992370023701237022370323704237052370623707237082370923710237112371223713237142371523716237172371823719237202372123722237232372423725237262372723728237292373023731237322373323734237352373623737237382373923740237412374223743237442374523746237472374823749237502375123752237532375423755237562375723758237592376023761237622376323764237652376623767237682376923770237712377223773237742377523776237772377823779237802378123782237832378423785237862378723788237892379023791237922379323794237952379623797237982379923800238012380223803238042380523806238072380823809238102381123812238132381423815238162381723818238192382023821238222382323824238252382623827238282382923830238312383223833238342383523836238372383823839238402384123842238432384423845238462384723848238492385023851238522385323854238552385623857238582385923860238612386223863238642386523866238672386823869238702387123872238732387423875238762387723878238792388023881238822388323884238852388623887238882388923890238912389223893238942389523896238972389823899239002390123902239032390423905239062390723908239092391023911239122391323914239152391623917239182391923920239212392223923239242392523926239272392823929239302393123932239332393423935239362393723938239392394023941239422394323944239452394623947239482394923950239512395223953239542395523956239572395823959239602396123962239632396423965239662396723968239692397023971239722397323974239752397623977239782397923980239812398223983239842398523986239872398823989239902399123992239932399423995239962399723998239992400024001240022400324004240052400624007240082400924010240112401224013240142401524016240172401824019240202402124022240232402424025240262402724028240292403024031240322403324034240352403624037240382403924040240412404224043240442404524046240472404824049240502405124052240532405424055240562405724058240592406024061240622406324064240652406624067240682406924070240712407224073240742407524076240772407824079240802408124082240832408424085240862408724088240892409024091240922409324094240952409624097240982409924100241012410224103241042410524106241072410824109241102411124112241132411424115241162411724118241192412024121241222412324124241252412624127241282412924130241312413224133241342413524136241372413824139241402414124142241432414424145241462414724148241492415024151241522415324154241552415624157241582415924160241612416224163241642416524166241672416824169241702417124172241732417424175241762417724178241792418024181241822418324184241852418624187241882418924190241912419224193241942419524196241972419824199242002420124202242032420424205242062420724208242092421024211242122421324214242152421624217242182421924220242212422224223242242422524226242272422824229242302423124232242332423424235242362423724238242392424024241242422424324244242452424624247242482424924250242512425224253242542425524256242572425824259242602426124262242632426424265242662426724268242692427024271242722427324274242752427624277242782427924280242812428224283242842428524286242872428824289242902429124292242932429424295242962429724298242992430024301243022430324304243052430624307243082430924310243112431224313243142431524316243172431824319243202432124322243232432424325243262432724328243292433024331243322433324334243352433624337243382433924340243412434224343243442434524346243472434824349243502435124352243532435424355243562435724358243592436024361243622436324364243652436624367243682436924370243712437224373243742437524376243772437824379243802438124382243832438424385243862438724388243892439024391243922439324394243952439624397243982439924400244012440224403244042440524406244072440824409244102441124412244132441424415244162441724418244192442024421244222442324424244252442624427244282442924430244312443224433244342443524436244372443824439244402444124442244432444424445244462444724448244492445024451244522445324454244552445624457244582445924460244612446224463244642446524466244672446824469244702447124472244732447424475244762447724478244792448024481244822448324484244852448624487244882448924490244912449224493244942449524496244972449824499245002450124502245032450424505245062450724508245092451024511245122451324514245152451624517245182451924520245212452224523245242452524526245272452824529245302453124532245332453424535245362453724538245392454024541245422454324544245452454624547245482454924550245512455224553245542455524556245572455824559245602456124562245632456424565245662456724568245692457024571245722457324574245752457624577245782457924580245812458224583245842458524586245872458824589245902459124592245932459424595245962459724598245992460024601246022460324604246052460624607246082460924610246112461224613246142461524616246172461824619246202462124622246232462424625246262462724628246292463024631246322463324634246352463624637246382463924640246412464224643246442464524646246472464824649246502465124652246532465424655246562465724658246592466024661246622466324664246652466624667246682466924670246712467224673246742467524676246772467824679246802468124682246832468424685246862468724688246892469024691246922469324694246952469624697246982469924700247012470224703247042470524706247072470824709247102471124712247132471424715247162471724718247192472024721247222472324724247252472624727247282472924730247312473224733247342473524736247372473824739247402474124742247432474424745247462474724748247492475024751247522475324754247552475624757247582475924760247612476224763247642476524766247672476824769247702477124772247732477424775247762477724778247792478024781247822478324784247852478624787247882478924790247912479224793247942479524796247972479824799248002480124802248032480424805248062480724808248092481024811248122481324814248152481624817248182481924820248212482224823248242482524826248272482824829248302483124832248332483424835248362483724838248392484024841248422484324844248452484624847248482484924850248512485224853248542485524856248572485824859248602486124862248632486424865248662486724868248692487024871248722487324874248752487624877248782487924880248812488224883248842488524886248872488824889248902489124892248932489424895248962489724898248992490024901249022490324904249052490624907249082490924910249112491224913249142491524916249172491824919249202492124922249232492424925249262492724928249292493024931249322493324934249352493624937249382493924940249412494224943249442494524946249472494824949249502495124952249532495424955249562495724958249592496024961249622496324964249652496624967249682496924970249712497224973249742497524976249772497824979249802498124982249832498424985249862498724988249892499024991249922499324994249952499624997249982499925000250012500225003250042500525006250072500825009250102501125012250132501425015250162501725018250192502025021250222502325024250252502625027250282502925030250312503225033250342503525036250372503825039250402504125042250432504425045250462504725048250492505025051250522505325054250552505625057250582505925060250612506225063250642506525066250672506825069250702507125072250732507425075250762507725078250792508025081250822508325084250852508625087250882508925090250912509225093250942509525096250972509825099251002510125102251032510425105251062510725108251092511025111251122511325114251152511625117251182511925120251212512225123251242512525126251272512825129251302513125132251332513425135251362513725138251392514025141251422514325144251452514625147251482514925150251512515225153251542515525156251572515825159251602516125162251632516425165251662516725168251692517025171251722517325174251752517625177251782517925180251812518225183251842518525186251872518825189251902519125192251932519425195251962519725198251992520025201252022520325204252052520625207252082520925210252112521225213252142521525216252172521825219252202522125222252232522425225252262522725228252292523025231252322523325234252352523625237252382523925240252412524225243252442524525246252472524825249252502525125252252532525425255252562525725258252592526025261252622526325264252652526625267252682526925270252712527225273252742527525276252772527825279252802528125282252832528425285252862528725288252892529025291252922529325294252952529625297252982529925300253012530225303253042530525306253072530825309253102531125312253132531425315253162531725318253192532025321253222532325324253252532625327253282532925330253312533225333253342533525336253372533825339253402534125342253432534425345253462534725348253492535025351253522535325354253552535625357253582535925360253612536225363253642536525366253672536825369253702537125372253732537425375253762537725378253792538025381253822538325384253852538625387253882538925390253912539225393253942539525396253972539825399254002540125402254032540425405254062540725408254092541025411254122541325414254152541625417254182541925420254212542225423254242542525426254272542825429254302543125432254332543425435254362543725438254392544025441254422544325444254452544625447254482544925450254512545225453254542545525456254572545825459254602546125462254632546425465254662546725468254692547025471254722547325474254752547625477254782547925480254812548225483254842548525486254872548825489254902549125492254932549425495254962549725498254992550025501255022550325504255052550625507255082550925510255112551225513255142551525516255172551825519255202552125522255232552425525255262552725528255292553025531255322553325534255352553625537255382553925540255412554225543255442554525546255472554825549255502555125552255532555425555255562555725558255592556025561255622556325564255652556625567255682556925570255712557225573255742557525576255772557825579255802558125582255832558425585255862558725588255892559025591255922559325594255952559625597255982559925600256012560225603256042560525606256072560825609256102561125612256132561425615256162561725618256192562025621256222562325624256252562625627256282562925630256312563225633256342563525636256372563825639256402564125642256432564425645256462564725648256492565025651256522565325654256552565625657256582565925660256612566225663256642566525666256672566825669256702567125672256732567425675256762567725678256792568025681256822568325684256852568625687256882568925690256912569225693256942569525696256972569825699257002570125702257032570425705257062570725708257092571025711257122571325714257152571625717257182571925720257212572225723257242572525726257272572825729257302573125732257332573425735257362573725738257392574025741257422574325744257452574625747257482574925750257512575225753257542575525756257572575825759257602576125762257632576425765257662576725768257692577025771257722577325774257752577625777257782577925780257812578225783257842578525786257872578825789257902579125792257932579425795257962579725798257992580025801258022580325804258052580625807258082580925810258112581225813258142581525816258172581825819258202582125822258232582425825258262582725828258292583025831258322583325834258352583625837258382583925840258412584225843258442584525846258472584825849258502585125852258532585425855258562585725858258592586025861258622586325864258652586625867258682586925870258712587225873258742587525876258772587825879258802588125882258832588425885258862588725888258892589025891258922589325894258952589625897258982589925900259012590225903259042590525906259072590825909259102591125912259132591425915259162591725918259192592025921259222592325924259252592625927259282592925930259312593225933259342593525936259372593825939259402594125942259432594425945259462594725948259492595025951259522595325954259552595625957259582595925960259612596225963259642596525966259672596825969259702597125972259732597425975259762597725978259792598025981259822598325984259852598625987259882598925990259912599225993259942599525996259972599825999260002600126002260032600426005260062600726008260092601026011260122601326014260152601626017260182601926020260212602226023260242602526026260272602826029260302603126032260332603426035260362603726038260392604026041260422604326044260452604626047260482604926050260512605226053260542605526056260572605826059260602606126062260632606426065260662606726068260692607026071260722607326074260752607626077260782607926080260812608226083260842608526086260872608826089260902609126092260932609426095260962609726098260992610026101261022610326104261052610626107261082610926110261112611226113261142611526116261172611826119261202612126122261232612426125261262612726128261292613026131261322613326134261352613626137261382613926140261412614226143261442614526146261472614826149261502615126152261532615426155261562615726158261592616026161261622616326164261652616626167261682616926170261712617226173261742617526176261772617826179261802618126182261832618426185261862618726188261892619026191261922619326194261952619626197261982619926200262012620226203262042620526206262072620826209262102621126212262132621426215262162621726218262192622026221262222622326224262252622626227262282622926230262312623226233262342623526236262372623826239262402624126242262432624426245262462624726248262492625026251262522625326254262552625626257262582625926260262612626226263262642626526266262672626826269262702627126272262732627426275262762627726278262792628026281262822628326284262852628626287262882628926290262912629226293262942629526296262972629826299263002630126302263032630426305263062630726308263092631026311263122631326314263152631626317263182631926320263212632226323263242632526326263272632826329263302633126332263332633426335263362633726338263392634026341263422634326344263452634626347263482634926350263512635226353263542635526356263572635826359263602636126362263632636426365263662636726368263692637026371263722637326374263752637626377263782637926380263812638226383263842638526386263872638826389263902639126392263932639426395263962639726398263992640026401264022640326404264052640626407264082640926410264112641226413264142641526416264172641826419264202642126422264232642426425264262642726428264292643026431264322643326434264352643626437264382643926440264412644226443264442644526446264472644826449264502645126452264532645426455264562645726458264592646026461264622646326464264652646626467264682646926470264712647226473264742647526476264772647826479264802648126482264832648426485264862648726488264892649026491264922649326494264952649626497264982649926500265012650226503265042650526506265072650826509265102651126512265132651426515265162651726518265192652026521265222652326524265252652626527265282652926530265312653226533265342653526536265372653826539265402654126542265432654426545265462654726548265492655026551265522655326554265552655626557265582655926560265612656226563265642656526566265672656826569265702657126572265732657426575265762657726578265792658026581265822658326584265852658626587265882658926590265912659226593265942659526596265972659826599266002660126602266032660426605266062660726608266092661026611266122661326614266152661626617266182661926620266212662226623266242662526626266272662826629266302663126632266332663426635266362663726638266392664026641266422664326644266452664626647266482664926650266512665226653266542665526656266572665826659266602666126662266632666426665266662666726668266692667026671266722667326674266752667626677266782667926680266812668226683266842668526686266872668826689266902669126692266932669426695266962669726698266992670026701267022670326704267052670626707267082670926710267112671226713267142671526716267172671826719267202672126722267232672426725267262672726728267292673026731267322673326734267352673626737267382673926740267412674226743267442674526746267472674826749267502675126752267532675426755267562675726758267592676026761267622676326764267652676626767267682676926770267712677226773267742677526776267772677826779267802678126782267832678426785267862678726788267892679026791267922679326794267952679626797267982679926800268012680226803268042680526806268072680826809268102681126812268132681426815268162681726818268192682026821268222682326824268252682626827268282682926830268312683226833268342683526836268372683826839268402684126842268432684426845268462684726848268492685026851268522685326854268552685626857268582685926860268612686226863268642686526866268672686826869268702687126872268732687426875268762687726878268792688026881268822688326884268852688626887268882688926890268912689226893268942689526896268972689826899269002690126902269032690426905269062690726908269092691026911269122691326914269152691626917269182691926920269212692226923269242692526926269272692826929269302693126932269332693426935269362693726938269392694026941269422694326944269452694626947269482694926950269512695226953269542695526956269572695826959269602696126962269632696426965269662696726968269692697026971269722697326974269752697626977269782697926980269812698226983269842698526986269872698826989269902699126992269932699426995269962699726998269992700027001270022700327004270052700627007270082700927010270112701227013270142701527016270172701827019270202702127022270232702427025270262702727028270292703027031270322703327034270352703627037270382703927040270412704227043270442704527046270472704827049270502705127052270532705427055270562705727058270592706027061270622706327064270652706627067270682706927070270712707227073270742707527076270772707827079270802708127082270832708427085270862708727088270892709027091270922709327094270952709627097270982709927100271012710227103271042710527106271072710827109271102711127112271132711427115271162711727118271192712027121271222712327124271252712627127271282712927130271312713227133271342713527136271372713827139271402714127142271432714427145271462714727148271492715027151271522715327154271552715627157271582715927160271612716227163271642716527166271672716827169271702717127172271732717427175271762717727178271792718027181271822718327184271852718627187271882718927190271912719227193271942719527196271972719827199272002720127202272032720427205272062720727208272092721027211272122721327214272152721627217272182721927220272212722227223272242722527226272272722827229272302723127232272332723427235272362723727238272392724027241272422724327244272452724627247272482724927250272512725227253272542725527256272572725827259272602726127262272632726427265272662726727268272692727027271272722727327274272752727627277272782727927280272812728227283272842728527286272872728827289272902729127292272932729427295272962729727298272992730027301273022730327304273052730627307273082730927310273112731227313273142731527316273172731827319273202732127322273232732427325273262732727328273292733027331273322733327334273352733627337273382733927340273412734227343273442734527346273472734827349273502735127352273532735427355273562735727358273592736027361273622736327364273652736627367273682736927370273712737227373273742737527376273772737827379273802738127382273832738427385273862738727388273892739027391273922739327394273952739627397273982739927400274012740227403274042740527406274072740827409274102741127412274132741427415274162741727418274192742027421274222742327424274252742627427274282742927430274312743227433274342743527436274372743827439274402744127442274432744427445274462744727448274492745027451274522745327454274552745627457274582745927460274612746227463274642746527466274672746827469274702747127472274732747427475274762747727478274792748027481274822748327484274852748627487274882748927490274912749227493274942749527496274972749827499275002750127502275032750427505275062750727508275092751027511275122751327514275152751627517275182751927520275212752227523275242752527526275272752827529275302753127532275332753427535275362753727538275392754027541275422754327544275452754627547275482754927550275512755227553275542755527556275572755827559275602756127562275632756427565275662756727568275692757027571275722757327574275752757627577275782757927580275812758227583275842758527586275872758827589275902759127592275932759427595275962759727598275992760027601276022760327604276052760627607276082760927610276112761227613276142761527616276172761827619276202762127622276232762427625276262762727628276292763027631276322763327634276352763627637276382763927640276412764227643276442764527646276472764827649276502765127652276532765427655276562765727658276592766027661276622766327664276652766627667276682766927670276712767227673276742767527676276772767827679276802768127682276832768427685276862768727688276892769027691276922769327694276952769627697276982769927700277012770227703277042770527706277072770827709277102771127712277132771427715277162771727718277192772027721277222772327724277252772627727277282772927730277312773227733277342773527736277372773827739277402774127742277432774427745277462774727748277492775027751277522775327754277552775627757277582775927760277612776227763277642776527766277672776827769277702777127772277732777427775277762777727778277792778027781277822778327784277852778627787277882778927790277912779227793277942779527796277972779827799278002780127802278032780427805278062780727808278092781027811278122781327814278152781627817278182781927820278212782227823278242782527826278272782827829278302783127832278332783427835278362783727838278392784027841278422784327844278452784627847278482784927850278512785227853278542785527856278572785827859278602786127862278632786427865278662786727868278692787027871278722787327874278752787627877278782787927880278812788227883278842788527886278872788827889278902789127892278932789427895278962789727898278992790027901279022790327904279052790627907279082790927910279112791227913279142791527916279172791827919279202792127922279232792427925279262792727928279292793027931279322793327934279352793627937279382793927940279412794227943279442794527946279472794827949279502795127952279532795427955279562795727958279592796027961279622796327964279652796627967279682796927970279712797227973279742797527976279772797827979279802798127982279832798427985279862798727988279892799027991279922799327994279952799627997279982799928000280012800228003280042800528006280072800828009280102801128012280132801428015280162801728018280192802028021280222802328024280252802628027280282802928030280312803228033280342803528036280372803828039280402804128042280432804428045280462804728048280492805028051280522805328054280552805628057280582805928060280612806228063280642806528066280672806828069280702807128072280732807428075280762807728078280792808028081280822808328084280852808628087280882808928090280912809228093280942809528096280972809828099281002810128102281032810428105281062810728108281092811028111281122811328114281152811628117281182811928120281212812228123281242812528126281272812828129281302813128132281332813428135281362813728138281392814028141281422814328144281452814628147281482814928150281512815228153281542815528156281572815828159281602816128162281632816428165281662816728168281692817028171281722817328174281752817628177281782817928180281812818228183281842818528186281872818828189281902819128192281932819428195281962819728198281992820028201282022820328204282052820628207282082820928210282112821228213282142821528216282172821828219282202822128222282232822428225282262822728228282292823028231282322823328234282352823628237282382823928240282412824228243282442824528246282472824828249282502825128252282532825428255282562825728258282592826028261282622826328264282652826628267282682826928270282712827228273282742827528276282772827828279282802828128282282832828428285282862828728288282892829028291282922829328294282952829628297282982829928300283012830228303283042830528306283072830828309283102831128312283132831428315283162831728318283192832028321283222832328324283252832628327283282832928330283312833228333283342833528336283372833828339283402834128342283432834428345283462834728348283492835028351283522835328354283552835628357283582835928360283612836228363283642836528366283672836828369283702837128372283732837428375283762837728378283792838028381283822838328384283852838628387283882838928390283912839228393283942839528396283972839828399284002840128402284032840428405284062840728408284092841028411284122841328414284152841628417284182841928420284212842228423284242842528426284272842828429284302843128432284332843428435284362843728438284392844028441284422844328444284452844628447284482844928450284512845228453284542845528456284572845828459284602846128462284632846428465284662846728468284692847028471284722847328474284752847628477284782847928480284812848228483284842848528486284872848828489284902849128492284932849428495284962849728498284992850028501285022850328504285052850628507285082850928510285112851228513285142851528516285172851828519285202852128522285232852428525285262852728528285292853028531285322853328534285352853628537285382853928540285412854228543285442854528546285472854828549285502855128552285532855428555285562855728558285592856028561285622856328564285652856628567285682856928570285712857228573285742857528576285772857828579285802858128582285832858428585285862858728588285892859028591285922859328594285952859628597285982859928600286012860228603286042860528606286072860828609286102861128612286132861428615286162861728618286192862028621286222862328624286252862628627286282862928630286312863228633286342863528636286372863828639286402864128642286432864428645286462864728648286492865028651286522865328654286552865628657286582865928660286612866228663286642866528666286672866828669286702867128672286732867428675286762867728678286792868028681286822868328684286852868628687286882868928690286912869228693286942869528696286972869828699287002870128702287032870428705287062870728708287092871028711287122871328714287152871628717287182871928720287212872228723287242872528726287272872828729287302873128732287332873428735287362873728738287392874028741287422874328744287452874628747287482874928750287512875228753287542875528756287572875828759287602876128762287632876428765287662876728768287692877028771287722877328774287752877628777287782877928780287812878228783287842878528786287872878828789287902879128792287932879428795287962879728798287992880028801288022880328804288052880628807288082880928810288112881228813288142881528816288172881828819288202882128822288232882428825288262882728828288292883028831288322883328834288352883628837288382883928840288412884228843288442884528846288472884828849288502885128852288532885428855288562885728858288592886028861288622886328864288652886628867288682886928870288712887228873288742887528876288772887828879288802888128882288832888428885288862888728888288892889028891288922889328894288952889628897288982889928900289012890228903289042890528906289072890828909289102891128912289132891428915289162891728918289192892028921289222892328924289252892628927289282892928930289312893228933289342893528936289372893828939289402894128942289432894428945289462894728948289492895028951289522895328954289552895628957289582895928960289612896228963289642896528966289672896828969289702897128972289732897428975289762897728978289792898028981289822898328984289852898628987289882898928990289912899228993289942899528996289972899828999290002900129002290032900429005290062900729008290092901029011290122901329014290152901629017290182901929020290212902229023290242902529026290272902829029290302903129032290332903429035290362903729038290392904029041290422904329044290452904629047290482904929050290512905229053290542905529056290572905829059290602906129062290632906429065290662906729068290692907029071290722907329074290752907629077290782907929080290812908229083290842908529086290872908829089290902909129092290932909429095290962909729098290992910029101291022910329104291052910629107291082910929110291112911229113291142911529116291172911829119291202912129122291232912429125291262912729128291292913029131291322913329134291352913629137291382913929140291412914229143291442914529146291472914829149291502915129152291532915429155291562915729158291592916029161291622916329164291652916629167291682916929170291712917229173291742917529176291772917829179291802918129182291832918429185291862918729188291892919029191291922919329194291952919629197291982919929200292012920229203292042920529206292072920829209292102921129212292132921429215292162921729218292192922029221292222922329224292252922629227292282922929230292312923229233292342923529236292372923829239292402924129242292432924429245292462924729248292492925029251292522925329254292552925629257292582925929260292612926229263292642926529266292672926829269292702927129272292732927429275292762927729278292792928029281292822928329284292852928629287292882928929290292912929229293292942929529296292972929829299293002930129302293032930429305293062930729308293092931029311293122931329314293152931629317293182931929320293212932229323293242932529326293272932829329293302933129332293332933429335293362933729338293392934029341293422934329344293452934629347293482934929350293512935229353293542935529356293572935829359293602936129362293632936429365293662936729368293692937029371293722937329374293752937629377293782937929380293812938229383293842938529386293872938829389293902939129392293932939429395293962939729398293992940029401294022940329404294052940629407294082940929410294112941229413294142941529416294172941829419294202942129422294232942429425294262942729428294292943029431294322943329434294352943629437294382943929440294412944229443294442944529446294472944829449294502945129452294532945429455294562945729458294592946029461294622946329464294652946629467294682946929470294712947229473294742947529476294772947829479294802948129482294832948429485294862948729488294892949029491294922949329494294952949629497294982949929500295012950229503295042950529506295072950829509295102951129512295132951429515295162951729518295192952029521295222952329524295252952629527295282952929530295312953229533295342953529536295372953829539295402954129542295432954429545295462954729548295492955029551295522955329554295552955629557295582955929560295612956229563295642956529566295672956829569295702957129572295732957429575295762957729578295792958029581295822958329584295852958629587295882958929590295912959229593295942959529596295972959829599296002960129602296032960429605296062960729608296092961029611296122961329614296152961629617296182961929620296212962229623296242962529626296272962829629296302963129632296332963429635296362963729638296392964029641296422964329644296452964629647296482964929650296512965229653296542965529656296572965829659296602966129662296632966429665296662966729668296692967029671296722967329674296752967629677296782967929680296812968229683296842968529686296872968829689296902969129692296932969429695296962969729698296992970029701297022970329704297052970629707297082970929710297112971229713297142971529716297172971829719297202972129722297232972429725297262972729728297292973029731297322973329734297352973629737297382973929740297412974229743297442974529746297472974829749297502975129752297532975429755297562975729758297592976029761297622976329764297652976629767297682976929770297712977229773297742977529776297772977829779297802978129782297832978429785297862978729788297892979029791297922979329794297952979629797297982979929800298012980229803298042980529806298072980829809298102981129812298132981429815298162981729818298192982029821298222982329824298252982629827298282982929830298312983229833298342983529836298372983829839298402984129842298432984429845298462984729848298492985029851298522985329854298552985629857298582985929860298612986229863298642986529866298672986829869298702987129872298732987429875298762987729878298792988029881298822988329884298852988629887298882988929890298912989229893298942989529896298972989829899299002990129902299032990429905299062990729908299092991029911299122991329914299152991629917299182991929920299212992229923299242992529926299272992829929299302993129932299332993429935299362993729938299392994029941299422994329944299452994629947299482994929950299512995229953299542995529956299572995829959299602996129962299632996429965299662996729968299692997029971299722997329974299752997629977299782997929980299812998229983299842998529986299872998829989299902999129992299932999429995299962999729998299993000030001300023000330004300053000630007300083000930010300113001230013300143001530016300173001830019300203002130022300233002430025300263002730028300293003030031300323003330034300353003630037300383003930040300413004230043300443004530046300473004830049300503005130052300533005430055300563005730058300593006030061300623006330064300653006630067300683006930070300713007230073300743007530076300773007830079300803008130082300833008430085300863008730088300893009030091300923009330094300953009630097300983009930100301013010230103301043010530106301073010830109301103011130112301133011430115301163011730118301193012030121301223012330124301253012630127301283012930130301313013230133301343013530136301373013830139301403014130142301433014430145301463014730148301493015030151301523015330154301553015630157301583015930160301613016230163301643016530166301673016830169301703017130172301733017430175301763017730178301793018030181301823018330184301853018630187301883018930190301913019230193301943019530196301973019830199302003020130202302033020430205302063020730208302093021030211302123021330214302153021630217302183021930220302213022230223302243022530226302273022830229302303023130232302333023430235302363023730238302393024030241302423024330244302453024630247302483024930250302513025230253302543025530256302573025830259302603026130262302633026430265302663026730268302693027030271302723027330274302753027630277302783027930280302813028230283302843028530286302873028830289302903029130292302933029430295302963029730298302993030030301303023030330304303053030630307303083030930310303113031230313303143031530316303173031830319303203032130322303233032430325303263032730328303293033030331303323033330334303353033630337303383033930340303413034230343303443034530346303473034830349303503035130352303533035430355303563035730358303593036030361303623036330364303653036630367303683036930370303713037230373303743037530376303773037830379303803038130382303833038430385303863038730388303893039030391303923039330394303953039630397303983039930400304013040230403304043040530406304073040830409304103041130412304133041430415304163041730418304193042030421304223042330424304253042630427304283042930430304313043230433304343043530436304373043830439304403044130442304433044430445304463044730448304493045030451304523045330454304553045630457304583045930460304613046230463304643046530466304673046830469304703047130472304733047430475304763047730478304793048030481304823048330484304853048630487304883048930490304913049230493304943049530496304973049830499305003050130502305033050430505305063050730508305093051030511305123051330514305153051630517305183051930520305213052230523305243052530526305273052830529305303053130532305333053430535305363053730538305393054030541305423054330544305453054630547305483054930550305513055230553305543055530556305573055830559305603056130562305633056430565305663056730568305693057030571305723057330574305753057630577305783057930580305813058230583305843058530586305873058830589305903059130592305933059430595305963059730598305993060030601306023060330604306053060630607306083060930610306113061230613306143061530616306173061830619306203062130622306233062430625306263062730628306293063030631306323063330634306353063630637306383063930640306413064230643306443064530646306473064830649306503065130652306533065430655306563065730658306593066030661306623066330664306653066630667306683066930670306713067230673306743067530676306773067830679306803068130682306833068430685306863068730688306893069030691306923069330694306953069630697306983069930700307013070230703307043070530706307073070830709307103071130712307133071430715307163071730718307193072030721307223072330724307253072630727307283072930730307313073230733307343073530736307373073830739307403074130742307433074430745307463074730748307493075030751307523075330754307553075630757307583075930760307613076230763307643076530766307673076830769307703077130772307733077430775307763077730778307793078030781307823078330784307853078630787307883078930790307913079230793307943079530796307973079830799308003080130802308033080430805308063080730808308093081030811308123081330814308153081630817308183081930820308213082230823308243082530826308273082830829308303083130832308333083430835308363083730838308393084030841308423084330844308453084630847308483084930850308513085230853308543085530856308573085830859308603086130862308633086430865308663086730868308693087030871308723087330874308753087630877308783087930880308813088230883308843088530886308873088830889308903089130892308933089430895308963089730898308993090030901309023090330904309053090630907309083090930910309113091230913309143091530916309173091830919309203092130922309233092430925309263092730928309293093030931309323093330934309353093630937309383093930940309413094230943309443094530946309473094830949309503095130952309533095430955309563095730958309593096030961309623096330964309653096630967309683096930970309713097230973309743097530976309773097830979309803098130982309833098430985309863098730988309893099030991309923099330994309953099630997309983099931000310013100231003310043100531006310073100831009310103101131012310133101431015310163101731018310193102031021310223102331024310253102631027310283102931030310313103231033310343103531036310373103831039310403104131042310433104431045310463104731048310493105031051310523105331054310553105631057310583105931060310613106231063310643106531066310673106831069310703107131072310733107431075310763107731078310793108031081310823108331084310853108631087310883108931090310913109231093310943109531096310973109831099311003110131102311033110431105311063110731108311093111031111311123111331114311153111631117311183111931120311213112231123311243112531126311273112831129311303113131132311333113431135311363113731138311393114031141311423114331144311453114631147311483114931150311513115231153311543115531156311573115831159311603116131162311633116431165311663116731168311693117031171311723117331174311753117631177311783117931180311813118231183311843118531186311873118831189311903119131192311933119431195311963119731198311993120031201312023120331204312053120631207312083120931210312113121231213312143121531216312173121831219312203122131222312233122431225312263122731228312293123031231312323123331234312353123631237312383123931240312413124231243312443124531246312473124831249312503125131252312533125431255312563125731258312593126031261312623126331264312653126631267312683126931270312713127231273312743127531276312773127831279312803128131282312833128431285312863128731288312893129031291312923129331294312953129631297312983129931300313013130231303313043130531306313073130831309313103131131312313133131431315313163131731318313193132031321313223132331324313253132631327313283132931330313313133231333313343133531336313373133831339313403134131342313433134431345313463134731348313493135031351313523135331354313553135631357313583135931360313613136231363313643136531366313673136831369313703137131372313733137431375313763137731378313793138031381313823138331384313853138631387313883138931390313913139231393313943139531396313973139831399314003140131402314033140431405314063140731408314093141031411314123141331414314153141631417314183141931420314213142231423314243142531426314273142831429314303143131432314333143431435314363143731438314393144031441314423144331444314453144631447314483144931450314513145231453314543145531456314573145831459314603146131462314633146431465314663146731468314693147031471314723147331474314753147631477314783147931480314813148231483314843148531486314873148831489314903149131492314933149431495314963149731498314993150031501315023150331504315053150631507315083150931510315113151231513315143151531516315173151831519315203152131522315233152431525315263152731528315293153031531315323153331534315353153631537315383153931540315413154231543315443154531546315473154831549315503155131552315533155431555315563155731558315593156031561315623156331564315653156631567315683156931570315713157231573315743157531576315773157831579315803158131582315833158431585315863158731588315893159031591315923159331594315953159631597315983159931600316013160231603316043160531606316073160831609316103161131612316133161431615316163161731618316193162031621316223162331624316253162631627316283162931630316313163231633316343163531636316373163831639316403164131642316433164431645316463164731648316493165031651316523165331654316553165631657316583165931660316613166231663316643166531666316673166831669316703167131672316733167431675316763167731678316793168031681316823168331684316853168631687316883168931690316913169231693316943169531696316973169831699317003170131702317033170431705317063170731708317093171031711317123171331714317153171631717317183171931720317213172231723317243172531726317273172831729317303173131732317333173431735317363173731738317393174031741317423174331744317453174631747317483174931750317513175231753317543175531756317573175831759317603176131762317633176431765317663176731768317693177031771317723177331774317753177631777317783177931780317813178231783317843178531786317873178831789317903179131792317933179431795317963179731798317993180031801318023180331804318053180631807318083180931810318113181231813318143181531816318173181831819318203182131822318233182431825318263182731828318293183031831318323183331834318353183631837318383183931840318413184231843318443184531846318473184831849318503185131852318533185431855318563185731858318593186031861318623186331864318653186631867318683186931870318713187231873318743187531876318773187831879318803188131882318833188431885318863188731888318893189031891318923189331894318953189631897318983189931900319013190231903319043190531906319073190831909319103191131912319133191431915319163191731918319193192031921319223192331924319253192631927319283192931930319313193231933319343193531936319373193831939319403194131942319433194431945319463194731948319493195031951319523195331954319553195631957319583195931960319613196231963319643196531966319673196831969319703197131972319733197431975319763197731978319793198031981319823198331984319853198631987319883198931990319913199231993319943199531996319973199831999320003200132002320033200432005320063200732008320093201032011320123201332014320153201632017320183201932020320213202232023320243202532026320273202832029320303203132032320333203432035320363203732038320393204032041320423204332044320453204632047320483204932050320513205232053320543205532056320573205832059320603206132062320633206432065320663206732068320693207032071320723207332074320753207632077320783207932080320813208232083320843208532086320873208832089320903209132092320933209432095320963209732098320993210032101321023210332104321053210632107321083210932110321113211232113321143211532116321173211832119321203212132122321233212432125321263212732128321293213032131321323213332134321353213632137321383213932140321413214232143321443214532146321473214832149321503215132152321533215432155321563215732158321593216032161321623216332164321653216632167321683216932170321713217232173321743217532176321773217832179321803218132182321833218432185321863218732188321893219032191321923219332194321953219632197321983219932200322013220232203322043220532206322073220832209322103221132212322133221432215322163221732218322193222032221322223222332224322253222632227322283222932230322313223232233322343223532236322373223832239322403224132242322433224432245322463224732248322493225032251322523225332254322553225632257322583225932260322613226232263322643226532266322673226832269322703227132272322733227432275322763227732278322793228032281322823228332284322853228632287
  1. (function (global, factory) {
  2. typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
  3. typeof define === 'function' && define.amd ? define(['exports'], factory) :
  4. (factory((global.echarts = {})));
  5. }(this, (function (exports) { 'use strict';
  6. // (1) The code `if (__DEV__) ...` can be removed by build tool.
  7. // (2) If intend to use `__DEV__`, this module should be imported. Use a global
  8. // variable `__DEV__` may cause that miss the declaration (see #6535), or the
  9. // declaration is behind of the using position (for example in `Model.extent`,
  10. // And tools like rollup can not analysis the dependency if not import).
  11. var dev;
  12. // In browser
  13. if (typeof window !== 'undefined') {
  14. dev = window.__DEV__;
  15. }
  16. // In node
  17. else if (typeof global !== 'undefined') {
  18. dev = global.__DEV__;
  19. }
  20. if (typeof dev === 'undefined') {
  21. dev = true;
  22. }
  23. var __DEV__ = dev;
  24. /**
  25. * zrender: 生成唯一id
  26. *
  27. * @author errorrik (errorrik@gmail.com)
  28. */
  29. var idStart = 0x0907;
  30. var guid = function () {
  31. return idStart++;
  32. };
  33. /**
  34. * echarts设备环境识别
  35. *
  36. * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
  37. * @author firede[firede@firede.us]
  38. * @desc thanks zepto.
  39. */
  40. var env = {};
  41. if (typeof navigator === 'undefined') {
  42. // In node
  43. env = {
  44. browser: {},
  45. os: {},
  46. node: true,
  47. // Assume canvas is supported
  48. canvasSupported: true,
  49. svgSupported: true
  50. };
  51. }
  52. else {
  53. env = detect(navigator.userAgent);
  54. }
  55. var env$1 = env;
  56. // Zepto.js
  57. // (c) 2010-2013 Thomas Fuchs
  58. // Zepto.js may be freely distributed under the MIT license.
  59. function detect(ua) {
  60. var os = {};
  61. var browser = {};
  62. // var webkit = ua.match(/Web[kK]it[\/]{0,1}([\d.]+)/);
  63. // var android = ua.match(/(Android);?[\s\/]+([\d.]+)?/);
  64. // var ipad = ua.match(/(iPad).*OS\s([\d_]+)/);
  65. // var ipod = ua.match(/(iPod)(.*OS\s([\d_]+))?/);
  66. // var iphone = !ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/);
  67. // var webos = ua.match(/(webOS|hpwOS)[\s\/]([\d.]+)/);
  68. // var touchpad = webos && ua.match(/TouchPad/);
  69. // var kindle = ua.match(/Kindle\/([\d.]+)/);
  70. // var silk = ua.match(/Silk\/([\d._]+)/);
  71. // var blackberry = ua.match(/(BlackBerry).*Version\/([\d.]+)/);
  72. // var bb10 = ua.match(/(BB10).*Version\/([\d.]+)/);
  73. // var rimtabletos = ua.match(/(RIM\sTablet\sOS)\s([\d.]+)/);
  74. // var playbook = ua.match(/PlayBook/);
  75. // var chrome = ua.match(/Chrome\/([\d.]+)/) || ua.match(/CriOS\/([\d.]+)/);
  76. var firefox = ua.match(/Firefox\/([\d.]+)/);
  77. // var safari = webkit && ua.match(/Mobile\//) && !chrome;
  78. // var webview = ua.match(/(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)/) && !chrome;
  79. var ie = ua.match(/MSIE\s([\d.]+)/)
  80. // IE 11 Trident/7.0; rv:11.0
  81. || ua.match(/Trident\/.+?rv:(([\d.]+))/);
  82. var edge = ua.match(/Edge\/([\d.]+)/); // IE 12 and 12+
  83. var weChat = (/micromessenger/i).test(ua);
  84. // Todo: clean this up with a better OS/browser seperation:
  85. // - discern (more) between multiple browsers on android
  86. // - decide if kindle fire in silk mode is android or not
  87. // - Firefox on Android doesn't specify the Android version
  88. // - possibly devide in os, device and browser hashes
  89. // if (browser.webkit = !!webkit) browser.version = webkit[1];
  90. // if (android) os.android = true, os.version = android[2];
  91. // if (iphone && !ipod) os.ios = os.iphone = true, os.version = iphone[2].replace(/_/g, '.');
  92. // if (ipad) os.ios = os.ipad = true, os.version = ipad[2].replace(/_/g, '.');
  93. // if (ipod) os.ios = os.ipod = true, os.version = ipod[3] ? ipod[3].replace(/_/g, '.') : null;
  94. // if (webos) os.webos = true, os.version = webos[2];
  95. // if (touchpad) os.touchpad = true;
  96. // if (blackberry) os.blackberry = true, os.version = blackberry[2];
  97. // if (bb10) os.bb10 = true, os.version = bb10[2];
  98. // if (rimtabletos) os.rimtabletos = true, os.version = rimtabletos[2];
  99. // if (playbook) browser.playbook = true;
  100. // if (kindle) os.kindle = true, os.version = kindle[1];
  101. // if (silk) browser.silk = true, browser.version = silk[1];
  102. // if (!silk && os.android && ua.match(/Kindle Fire/)) browser.silk = true;
  103. // if (chrome) browser.chrome = true, browser.version = chrome[1];
  104. if (firefox) {
  105. browser.firefox = true;
  106. browser.version = firefox[1];
  107. }
  108. // if (safari && (ua.match(/Safari/) || !!os.ios)) browser.safari = true;
  109. // if (webview) browser.webview = true;
  110. if (ie) {
  111. browser.ie = true;
  112. browser.version = ie[1];
  113. }
  114. if (edge) {
  115. browser.edge = true;
  116. browser.version = edge[1];
  117. }
  118. // It is difficult to detect WeChat in Win Phone precisely, because ua can
  119. // not be set on win phone. So we do not consider Win Phone.
  120. if (weChat) {
  121. browser.weChat = true;
  122. }
  123. // os.tablet = !!(ipad || playbook || (android && !ua.match(/Mobile/)) ||
  124. // (firefox && ua.match(/Tablet/)) || (ie && !ua.match(/Phone/) && ua.match(/Touch/)));
  125. // os.phone = !!(!os.tablet && !os.ipod && (android || iphone || webos ||
  126. // (chrome && ua.match(/Android/)) || (chrome && ua.match(/CriOS\/([\d.]+)/)) ||
  127. // (firefox && ua.match(/Mobile/)) || (ie && ua.match(/Touch/))));
  128. return {
  129. browser: browser,
  130. os: os,
  131. node: false,
  132. // 原生canvas支持,改极端点了
  133. // canvasSupported : !(browser.ie && parseFloat(browser.version) < 9)
  134. canvasSupported: !!document.createElement('canvas').getContext,
  135. svgSupported: typeof SVGRect !== 'undefined',
  136. // @see <http://stackoverflow.com/questions/4817029/whats-the-best-way-to-detect-a-touch-screen-device-using-javascript>
  137. // works on most browsers
  138. // IE10/11 does not support touch event, and MS Edge supports them but not by
  139. // default, so we dont check navigator.maxTouchPoints for them here.
  140. touchEventsSupported: 'ontouchstart' in window && !browser.ie && !browser.edge,
  141. // <http://caniuse.com/#search=pointer%20event>.
  142. pointerEventsSupported: 'onpointerdown' in window
  143. // Firefox supports pointer but not by default, only MS browsers are reliable on pointer
  144. // events currently. So we dont use that on other browsers unless tested sufficiently.
  145. // Although IE 10 supports pointer event, it use old style and is different from the
  146. // standard. So we exclude that. (IE 10 is hardly used on touch device)
  147. && (browser.edge || (browser.ie && browser.version >= 11))
  148. };
  149. }
  150. /**
  151. * @module zrender/core/util
  152. */
  153. // 用于处理merge时无法遍历Date等对象的问题
  154. var BUILTIN_OBJECT = {
  155. '[object Function]': 1,
  156. '[object RegExp]': 1,
  157. '[object Date]': 1,
  158. '[object Error]': 1,
  159. '[object CanvasGradient]': 1,
  160. '[object CanvasPattern]': 1,
  161. // For node-canvas
  162. '[object Image]': 1,
  163. '[object Canvas]': 1
  164. };
  165. var TYPED_ARRAY = {
  166. '[object Int8Array]': 1,
  167. '[object Uint8Array]': 1,
  168. '[object Uint8ClampedArray]': 1,
  169. '[object Int16Array]': 1,
  170. '[object Uint16Array]': 1,
  171. '[object Int32Array]': 1,
  172. '[object Uint32Array]': 1,
  173. '[object Float32Array]': 1,
  174. '[object Float64Array]': 1
  175. };
  176. var objToString = Object.prototype.toString;
  177. var arrayProto = Array.prototype;
  178. var nativeForEach = arrayProto.forEach;
  179. var nativeFilter = arrayProto.filter;
  180. var nativeSlice = arrayProto.slice;
  181. var nativeMap = arrayProto.map;
  182. var nativeReduce = arrayProto.reduce;
  183. // Avoid assign to an exported variable, for transforming to cjs.
  184. var methods = {};
  185. function $override(name, fn) {
  186. methods[name] = fn;
  187. }
  188. /**
  189. * Those data types can be cloned:
  190. * Plain object, Array, TypedArray, number, string, null, undefined.
  191. * Those data types will be assgined using the orginal data:
  192. * BUILTIN_OBJECT
  193. * Instance of user defined class will be cloned to a plain object, without
  194. * properties in prototype.
  195. * Other data types is not supported (not sure what will happen).
  196. *
  197. * Caution: do not support clone Date, for performance consideration.
  198. * (There might be a large number of date in `series.data`).
  199. * So date should not be modified in and out of echarts.
  200. *
  201. * @param {*} source
  202. * @return {*} new
  203. */
  204. function clone(source) {
  205. if (source == null || typeof source != 'object') {
  206. return source;
  207. }
  208. var result = source;
  209. var typeStr = objToString.call(source);
  210. if (typeStr === '[object Array]') {
  211. result = [];
  212. for (var i = 0, len = source.length; i < len; i++) {
  213. result[i] = clone(source[i]);
  214. }
  215. }
  216. else if (TYPED_ARRAY[typeStr]) {
  217. var Ctor = source.constructor;
  218. if (source.constructor.from) {
  219. result = Ctor.from(source);
  220. }
  221. else {
  222. result = new Ctor(source.length);
  223. for (var i = 0, len = source.length; i < len; i++) {
  224. result[i] = clone(source[i]);
  225. }
  226. }
  227. }
  228. else if (!BUILTIN_OBJECT[typeStr] && !isPrimitive(source) && !isDom(source)) {
  229. result = {};
  230. for (var key in source) {
  231. if (source.hasOwnProperty(key)) {
  232. result[key] = clone(source[key]);
  233. }
  234. }
  235. }
  236. return result;
  237. }
  238. /**
  239. * @memberOf module:zrender/core/util
  240. * @param {*} target
  241. * @param {*} source
  242. * @param {boolean} [overwrite=false]
  243. */
  244. function merge(target, source, overwrite) {
  245. // We should escapse that source is string
  246. // and enter for ... in ...
  247. if (!isObject(source) || !isObject(target)) {
  248. return overwrite ? clone(source) : target;
  249. }
  250. for (var key in source) {
  251. if (source.hasOwnProperty(key)) {
  252. var targetProp = target[key];
  253. var sourceProp = source[key];
  254. if (isObject(sourceProp)
  255. && isObject(targetProp)
  256. && !isArray(sourceProp)
  257. && !isArray(targetProp)
  258. && !isDom(sourceProp)
  259. && !isDom(targetProp)
  260. && !isBuiltInObject(sourceProp)
  261. && !isBuiltInObject(targetProp)
  262. && !isPrimitive(sourceProp)
  263. && !isPrimitive(targetProp)
  264. ) {
  265. // 如果需要递归覆盖,就递归调用merge
  266. merge(targetProp, sourceProp, overwrite);
  267. }
  268. else if (overwrite || !(key in target)) {
  269. // 否则只处理overwrite为true,或者在目标对象中没有此属性的情况
  270. // NOTE,在 target[key] 不存在的时候也是直接覆盖
  271. target[key] = clone(source[key], true);
  272. }
  273. }
  274. }
  275. return target;
  276. }
  277. /**
  278. * @param {Array} targetAndSources The first item is target, and the rests are source.
  279. * @param {boolean} [overwrite=false]
  280. * @return {*} target
  281. */
  282. function mergeAll(targetAndSources, overwrite) {
  283. var result = targetAndSources[0];
  284. for (var i = 1, len = targetAndSources.length; i < len; i++) {
  285. result = merge(result, targetAndSources[i], overwrite);
  286. }
  287. return result;
  288. }
  289. /**
  290. * @param {*} target
  291. * @param {*} source
  292. * @memberOf module:zrender/core/util
  293. */
  294. function extend(target, source) {
  295. for (var key in source) {
  296. if (source.hasOwnProperty(key)) {
  297. target[key] = source[key];
  298. }
  299. }
  300. return target;
  301. }
  302. /**
  303. * @param {*} target
  304. * @param {*} source
  305. * @param {boolean} [overlay=false]
  306. * @memberOf module:zrender/core/util
  307. */
  308. function defaults(target, source, overlay) {
  309. for (var key in source) {
  310. if (source.hasOwnProperty(key)
  311. && (overlay ? source[key] != null : target[key] == null)
  312. ) {
  313. target[key] = source[key];
  314. }
  315. }
  316. return target;
  317. }
  318. var createCanvas = function () {
  319. return methods.createCanvas();
  320. };
  321. methods.createCanvas = function () {
  322. return document.createElement('canvas');
  323. };
  324. // FIXME
  325. var _ctx;
  326. function getContext() {
  327. if (!_ctx) {
  328. // Use util.createCanvas instead of createCanvas
  329. // because createCanvas may be overwritten in different environment
  330. _ctx = createCanvas().getContext('2d');
  331. }
  332. return _ctx;
  333. }
  334. /**
  335. * 查询数组中元素的index
  336. * @memberOf module:zrender/core/util
  337. */
  338. function indexOf(array, value) {
  339. if (array) {
  340. if (array.indexOf) {
  341. return array.indexOf(value);
  342. }
  343. for (var i = 0, len = array.length; i < len; i++) {
  344. if (array[i] === value) {
  345. return i;
  346. }
  347. }
  348. }
  349. return -1;
  350. }
  351. /**
  352. * 构造类继承关系
  353. *
  354. * @memberOf module:zrender/core/util
  355. * @param {Function} clazz 源类
  356. * @param {Function} baseClazz 基类
  357. */
  358. function inherits(clazz, baseClazz) {
  359. var clazzPrototype = clazz.prototype;
  360. function F() {}
  361. F.prototype = baseClazz.prototype;
  362. clazz.prototype = new F();
  363. for (var prop in clazzPrototype) {
  364. clazz.prototype[prop] = clazzPrototype[prop];
  365. }
  366. clazz.prototype.constructor = clazz;
  367. clazz.superClass = baseClazz;
  368. }
  369. /**
  370. * @memberOf module:zrender/core/util
  371. * @param {Object|Function} target
  372. * @param {Object|Function} sorce
  373. * @param {boolean} overlay
  374. */
  375. function mixin(target, source, overlay) {
  376. target = 'prototype' in target ? target.prototype : target;
  377. source = 'prototype' in source ? source.prototype : source;
  378. defaults(target, source, overlay);
  379. }
  380. /**
  381. * Consider typed array.
  382. * @param {Array|TypedArray} data
  383. */
  384. function isArrayLike(data) {
  385. if (! data) {
  386. return;
  387. }
  388. if (typeof data == 'string') {
  389. return false;
  390. }
  391. return typeof data.length == 'number';
  392. }
  393. /**
  394. * 数组或对象遍历
  395. * @memberOf module:zrender/core/util
  396. * @param {Object|Array} obj
  397. * @param {Function} cb
  398. * @param {*} [context]
  399. */
  400. function each$1(obj, cb, context) {
  401. if (!(obj && cb)) {
  402. return;
  403. }
  404. if (obj.forEach && obj.forEach === nativeForEach) {
  405. obj.forEach(cb, context);
  406. }
  407. else if (obj.length === +obj.length) {
  408. for (var i = 0, len = obj.length; i < len; i++) {
  409. cb.call(context, obj[i], i, obj);
  410. }
  411. }
  412. else {
  413. for (var key in obj) {
  414. if (obj.hasOwnProperty(key)) {
  415. cb.call(context, obj[key], key, obj);
  416. }
  417. }
  418. }
  419. }
  420. /**
  421. * 数组映射
  422. * @memberOf module:zrender/core/util
  423. * @param {Array} obj
  424. * @param {Function} cb
  425. * @param {*} [context]
  426. * @return {Array}
  427. */
  428. function map(obj, cb, context) {
  429. if (!(obj && cb)) {
  430. return;
  431. }
  432. if (obj.map && obj.map === nativeMap) {
  433. return obj.map(cb, context);
  434. }
  435. else {
  436. var result = [];
  437. for (var i = 0, len = obj.length; i < len; i++) {
  438. result.push(cb.call(context, obj[i], i, obj));
  439. }
  440. return result;
  441. }
  442. }
  443. /**
  444. * @memberOf module:zrender/core/util
  445. * @param {Array} obj
  446. * @param {Function} cb
  447. * @param {Object} [memo]
  448. * @param {*} [context]
  449. * @return {Array}
  450. */
  451. function reduce(obj, cb, memo, context) {
  452. if (!(obj && cb)) {
  453. return;
  454. }
  455. if (obj.reduce && obj.reduce === nativeReduce) {
  456. return obj.reduce(cb, memo, context);
  457. }
  458. else {
  459. for (var i = 0, len = obj.length; i < len; i++) {
  460. memo = cb.call(context, memo, obj[i], i, obj);
  461. }
  462. return memo;
  463. }
  464. }
  465. /**
  466. * 数组过滤
  467. * @memberOf module:zrender/core/util
  468. * @param {Array} obj
  469. * @param {Function} cb
  470. * @param {*} [context]
  471. * @return {Array}
  472. */
  473. function filter(obj, cb, context) {
  474. if (!(obj && cb)) {
  475. return;
  476. }
  477. if (obj.filter && obj.filter === nativeFilter) {
  478. return obj.filter(cb, context);
  479. }
  480. else {
  481. var result = [];
  482. for (var i = 0, len = obj.length; i < len; i++) {
  483. if (cb.call(context, obj[i], i, obj)) {
  484. result.push(obj[i]);
  485. }
  486. }
  487. return result;
  488. }
  489. }
  490. /**
  491. * 数组项查找
  492. * @memberOf module:zrender/core/util
  493. * @param {Array} obj
  494. * @param {Function} cb
  495. * @param {*} [context]
  496. * @return {*}
  497. */
  498. /**
  499. * @memberOf module:zrender/core/util
  500. * @param {Function} func
  501. * @param {*} context
  502. * @return {Function}
  503. */
  504. function bind(func, context) {
  505. var args = nativeSlice.call(arguments, 2);
  506. return function () {
  507. return func.apply(context, args.concat(nativeSlice.call(arguments)));
  508. };
  509. }
  510. /**
  511. * @memberOf module:zrender/core/util
  512. * @param {Function} func
  513. * @return {Function}
  514. */
  515. function curry(func) {
  516. var args = nativeSlice.call(arguments, 1);
  517. return function () {
  518. return func.apply(this, args.concat(nativeSlice.call(arguments)));
  519. };
  520. }
  521. /**
  522. * @memberOf module:zrender/core/util
  523. * @param {*} value
  524. * @return {boolean}
  525. */
  526. function isArray(value) {
  527. return objToString.call(value) === '[object Array]';
  528. }
  529. /**
  530. * @memberOf module:zrender/core/util
  531. * @param {*} value
  532. * @return {boolean}
  533. */
  534. function isFunction(value) {
  535. return typeof value === 'function';
  536. }
  537. /**
  538. * @memberOf module:zrender/core/util
  539. * @param {*} value
  540. * @return {boolean}
  541. */
  542. function isString(value) {
  543. return objToString.call(value) === '[object String]';
  544. }
  545. /**
  546. * @memberOf module:zrender/core/util
  547. * @param {*} value
  548. * @return {boolean}
  549. */
  550. function isObject(value) {
  551. // Avoid a V8 JIT bug in Chrome 19-20.
  552. // See https://code.google.com/p/v8/issues/detail?id=2291 for more details.
  553. var type = typeof value;
  554. return type === 'function' || (!!value && type == 'object');
  555. }
  556. /**
  557. * @memberOf module:zrender/core/util
  558. * @param {*} value
  559. * @return {boolean}
  560. */
  561. function isBuiltInObject(value) {
  562. return !!BUILTIN_OBJECT[objToString.call(value)];
  563. }
  564. /**
  565. * @memberOf module:zrender/core/util
  566. * @param {*} value
  567. * @return {boolean}
  568. */
  569. function isDom(value) {
  570. return typeof value === 'object'
  571. && typeof value.nodeType === 'number'
  572. && typeof value.ownerDocument === 'object';
  573. }
  574. /**
  575. * Whether is exactly NaN. Notice isNaN('a') returns true.
  576. * @param {*} value
  577. * @return {boolean}
  578. */
  579. function eqNaN(value) {
  580. return value !== value;
  581. }
  582. /**
  583. * If value1 is not null, then return value1, otherwise judget rest of values.
  584. * Low performance.
  585. * @memberOf module:zrender/core/util
  586. * @return {*} Final value
  587. */
  588. function retrieve(values) {
  589. for (var i = 0, len = arguments.length; i < len; i++) {
  590. if (arguments[i] != null) {
  591. return arguments[i];
  592. }
  593. }
  594. }
  595. function retrieve2(value0, value1) {
  596. return value0 != null
  597. ? value0
  598. : value1;
  599. }
  600. function retrieve3(value0, value1, value2) {
  601. return value0 != null
  602. ? value0
  603. : value1 != null
  604. ? value1
  605. : value2;
  606. }
  607. /**
  608. * @memberOf module:zrender/core/util
  609. * @param {Array} arr
  610. * @param {number} startIndex
  611. * @param {number} endIndex
  612. * @return {Array}
  613. */
  614. function slice() {
  615. return Function.call.apply(nativeSlice, arguments);
  616. }
  617. /**
  618. * Normalize css liked array configuration
  619. * e.g.
  620. * 3 => [3, 3, 3, 3]
  621. * [4, 2] => [4, 2, 4, 2]
  622. * [4, 3, 2] => [4, 3, 2, 3]
  623. * @param {number|Array.<number>} val
  624. * @return {Array.<number>}
  625. */
  626. function normalizeCssArray(val) {
  627. if (typeof (val) === 'number') {
  628. return [val, val, val, val];
  629. }
  630. var len = val.length;
  631. if (len === 2) {
  632. // vertical | horizontal
  633. return [val[0], val[1], val[0], val[1]];
  634. }
  635. else if (len === 3) {
  636. // top | horizontal | bottom
  637. return [val[0], val[1], val[2], val[1]];
  638. }
  639. return val;
  640. }
  641. /**
  642. * @memberOf module:zrender/core/util
  643. * @param {boolean} condition
  644. * @param {string} message
  645. */
  646. function assert(condition, message) {
  647. if (!condition) {
  648. throw new Error(message);
  649. }
  650. }
  651. var primitiveKey = '__ec_primitive__';
  652. /**
  653. * Set an object as primitive to be ignored traversing children in clone or merge
  654. */
  655. function setAsPrimitive(obj) {
  656. obj[primitiveKey] = true;
  657. }
  658. function isPrimitive(obj) {
  659. return obj[primitiveKey];
  660. }
  661. /**
  662. * @constructor
  663. * @param {Object} obj Only apply `ownProperty`.
  664. */
  665. function HashMap(obj) {
  666. obj && each$1(obj, function (value, key) {
  667. this.set(key, value);
  668. }, this);
  669. }
  670. // Add prefix to avoid conflict with Object.prototype.
  671. var HASH_MAP_PREFIX = '_ec_';
  672. var HASH_MAP_PREFIX_LENGTH = 4;
  673. HashMap.prototype = {
  674. constructor: HashMap,
  675. // Do not provide `has` method to avoid defining what is `has`.
  676. // (We usually treat `null` and `undefined` as the same, different
  677. // from ES6 Map).
  678. get: function (key) {
  679. return this[HASH_MAP_PREFIX + key];
  680. },
  681. set: function (key, value) {
  682. this[HASH_MAP_PREFIX + key] = value;
  683. // Comparing with invocation chaining, `return value` is more commonly
  684. // used in this case: `var someVal = map.set('a', genVal());`
  685. return value;
  686. },
  687. // Although util.each can be performed on this hashMap directly, user
  688. // should not use the exposed keys, who are prefixed.
  689. each: function (cb, context) {
  690. context !== void 0 && (cb = bind(cb, context));
  691. for (var prefixedKey in this) {
  692. this.hasOwnProperty(prefixedKey)
  693. && cb(this[prefixedKey], prefixedKey.slice(HASH_MAP_PREFIX_LENGTH));
  694. }
  695. },
  696. // Do not use this method if performance sensitive.
  697. removeKey: function (key) {
  698. delete this[HASH_MAP_PREFIX + key];
  699. }
  700. };
  701. function createHashMap(obj) {
  702. return new HashMap(obj);
  703. }
  704. function noop() {}
  705. var ArrayCtor = typeof Float32Array === 'undefined'
  706. ? Array
  707. : Float32Array;
  708. /**
  709. * 创建一个向量
  710. * @param {number} [x=0]
  711. * @param {number} [y=0]
  712. * @return {Vector2}
  713. */
  714. function create(x, y) {
  715. var out = new ArrayCtor(2);
  716. if (x == null) {
  717. x = 0;
  718. }
  719. if (y == null) {
  720. y = 0;
  721. }
  722. out[0] = x;
  723. out[1] = y;
  724. return out;
  725. }
  726. /**
  727. * 复制向量数据
  728. * @param {Vector2} out
  729. * @param {Vector2} v
  730. * @return {Vector2}
  731. */
  732. function copy(out, v) {
  733. out[0] = v[0];
  734. out[1] = v[1];
  735. return out;
  736. }
  737. /**
  738. * 克隆一个向量
  739. * @param {Vector2} v
  740. * @return {Vector2}
  741. */
  742. function clone$1(v) {
  743. var out = new ArrayCtor(2);
  744. out[0] = v[0];
  745. out[1] = v[1];
  746. return out;
  747. }
  748. /**
  749. * 设置向量的两个项
  750. * @param {Vector2} out
  751. * @param {number} a
  752. * @param {number} b
  753. * @return {Vector2} 结果
  754. */
  755. /**
  756. * 向量相加
  757. * @param {Vector2} out
  758. * @param {Vector2} v1
  759. * @param {Vector2} v2
  760. */
  761. function add(out, v1, v2) {
  762. out[0] = v1[0] + v2[0];
  763. out[1] = v1[1] + v2[1];
  764. return out;
  765. }
  766. /**
  767. * 向量缩放后相加
  768. * @param {Vector2} out
  769. * @param {Vector2} v1
  770. * @param {Vector2} v2
  771. * @param {number} a
  772. */
  773. function scaleAndAdd(out, v1, v2, a) {
  774. out[0] = v1[0] + v2[0] * a;
  775. out[1] = v1[1] + v2[1] * a;
  776. return out;
  777. }
  778. /**
  779. * 向量相减
  780. * @param {Vector2} out
  781. * @param {Vector2} v1
  782. * @param {Vector2} v2
  783. */
  784. function sub(out, v1, v2) {
  785. out[0] = v1[0] - v2[0];
  786. out[1] = v1[1] - v2[1];
  787. return out;
  788. }
  789. /**
  790. * 向量长度
  791. * @param {Vector2} v
  792. * @return {number}
  793. */
  794. function len(v) {
  795. return Math.sqrt(lenSquare(v));
  796. }
  797. // jshint ignore:line
  798. /**
  799. * 向量长度平方
  800. * @param {Vector2} v
  801. * @return {number}
  802. */
  803. function lenSquare(v) {
  804. return v[0] * v[0] + v[1] * v[1];
  805. }
  806. /**
  807. * 向量乘法
  808. * @param {Vector2} out
  809. * @param {Vector2} v1
  810. * @param {Vector2} v2
  811. */
  812. /**
  813. * 向量除法
  814. * @param {Vector2} out
  815. * @param {Vector2} v1
  816. * @param {Vector2} v2
  817. */
  818. /**
  819. * 向量点乘
  820. * @param {Vector2} v1
  821. * @param {Vector2} v2
  822. * @return {number}
  823. */
  824. /**
  825. * 向量缩放
  826. * @param {Vector2} out
  827. * @param {Vector2} v
  828. * @param {number} s
  829. */
  830. function scale(out, v, s) {
  831. out[0] = v[0] * s;
  832. out[1] = v[1] * s;
  833. return out;
  834. }
  835. /**
  836. * 向量归一化
  837. * @param {Vector2} out
  838. * @param {Vector2} v
  839. */
  840. function normalize(out, v) {
  841. var d = len(v);
  842. if (d === 0) {
  843. out[0] = 0;
  844. out[1] = 0;
  845. }
  846. else {
  847. out[0] = v[0] / d;
  848. out[1] = v[1] / d;
  849. }
  850. return out;
  851. }
  852. /**
  853. * 计算向量间距离
  854. * @param {Vector2} v1
  855. * @param {Vector2} v2
  856. * @return {number}
  857. */
  858. function distance(v1, v2) {
  859. return Math.sqrt(
  860. (v1[0] - v2[0]) * (v1[0] - v2[0])
  861. + (v1[1] - v2[1]) * (v1[1] - v2[1])
  862. );
  863. }
  864. var dist = distance;
  865. /**
  866. * 向量距离平方
  867. * @param {Vector2} v1
  868. * @param {Vector2} v2
  869. * @return {number}
  870. */
  871. function distanceSquare(v1, v2) {
  872. return (v1[0] - v2[0]) * (v1[0] - v2[0])
  873. + (v1[1] - v2[1]) * (v1[1] - v2[1]);
  874. }
  875. var distSquare = distanceSquare;
  876. /**
  877. * 求负向量
  878. * @param {Vector2} out
  879. * @param {Vector2} v
  880. */
  881. /**
  882. * 插值两个点
  883. * @param {Vector2} out
  884. * @param {Vector2} v1
  885. * @param {Vector2} v2
  886. * @param {number} t
  887. */
  888. /**
  889. * 矩阵左乘向量
  890. * @param {Vector2} out
  891. * @param {Vector2} v
  892. * @param {Vector2} m
  893. */
  894. function applyTransform(out, v, m) {
  895. var x = v[0];
  896. var y = v[1];
  897. out[0] = m[0] * x + m[2] * y + m[4];
  898. out[1] = m[1] * x + m[3] * y + m[5];
  899. return out;
  900. }
  901. /**
  902. * 求两个向量最小值
  903. * @param {Vector2} out
  904. * @param {Vector2} v1
  905. * @param {Vector2} v2
  906. */
  907. function min(out, v1, v2) {
  908. out[0] = Math.min(v1[0], v2[0]);
  909. out[1] = Math.min(v1[1], v2[1]);
  910. return out;
  911. }
  912. /**
  913. * 求两个向量最大值
  914. * @param {Vector2} out
  915. * @param {Vector2} v1
  916. * @param {Vector2} v2
  917. */
  918. function max(out, v1, v2) {
  919. out[0] = Math.max(v1[0], v2[0]);
  920. out[1] = Math.max(v1[1], v2[1]);
  921. return out;
  922. }
  923. // TODO Draggable for group
  924. // FIXME Draggable on element which has parent rotation or scale
  925. function Draggable() {
  926. this.on('mousedown', this._dragStart, this);
  927. this.on('mousemove', this._drag, this);
  928. this.on('mouseup', this._dragEnd, this);
  929. this.on('globalout', this._dragEnd, this);
  930. // this._dropTarget = null;
  931. // this._draggingTarget = null;
  932. // this._x = 0;
  933. // this._y = 0;
  934. }
  935. Draggable.prototype = {
  936. constructor: Draggable,
  937. _dragStart: function (e) {
  938. var draggingTarget = e.target;
  939. if (draggingTarget && draggingTarget.draggable) {
  940. this._draggingTarget = draggingTarget;
  941. draggingTarget.dragging = true;
  942. this._x = e.offsetX;
  943. this._y = e.offsetY;
  944. this.dispatchToElement(param(draggingTarget, e), 'dragstart', e.event);
  945. }
  946. },
  947. _drag: function (e) {
  948. var draggingTarget = this._draggingTarget;
  949. if (draggingTarget) {
  950. var x = e.offsetX;
  951. var y = e.offsetY;
  952. var dx = x - this._x;
  953. var dy = y - this._y;
  954. this._x = x;
  955. this._y = y;
  956. draggingTarget.drift(dx, dy, e);
  957. this.dispatchToElement(param(draggingTarget, e), 'drag', e.event);
  958. var dropTarget = this.findHover(x, y, draggingTarget).target;
  959. var lastDropTarget = this._dropTarget;
  960. this._dropTarget = dropTarget;
  961. if (draggingTarget !== dropTarget) {
  962. if (lastDropTarget && dropTarget !== lastDropTarget) {
  963. this.dispatchToElement(param(lastDropTarget, e), 'dragleave', e.event);
  964. }
  965. if (dropTarget && dropTarget !== lastDropTarget) {
  966. this.dispatchToElement(param(dropTarget, e), 'dragenter', e.event);
  967. }
  968. }
  969. }
  970. },
  971. _dragEnd: function (e) {
  972. var draggingTarget = this._draggingTarget;
  973. if (draggingTarget) {
  974. draggingTarget.dragging = false;
  975. }
  976. this.dispatchToElement(param(draggingTarget, e), 'dragend', e.event);
  977. if (this._dropTarget) {
  978. this.dispatchToElement(param(this._dropTarget, e), 'drop', e.event);
  979. }
  980. this._draggingTarget = null;
  981. this._dropTarget = null;
  982. }
  983. };
  984. function param(target, e) {
  985. return {target: target, topTarget: e && e.topTarget};
  986. }
  987. /**
  988. * 事件扩展
  989. * @module zrender/mixin/Eventful
  990. * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
  991. * pissang (https://www.github.com/pissang)
  992. */
  993. var arrySlice = Array.prototype.slice;
  994. /**
  995. * 事件分发器
  996. * @alias module:zrender/mixin/Eventful
  997. * @constructor
  998. */
  999. var Eventful = function () {
  1000. this._$handlers = {};
  1001. };
  1002. Eventful.prototype = {
  1003. constructor: Eventful,
  1004. /**
  1005. * 单次触发绑定,trigger后销毁
  1006. *
  1007. * @param {string} event 事件名
  1008. * @param {Function} handler 响应函数
  1009. * @param {Object} context
  1010. */
  1011. one: function (event, handler, context) {
  1012. var _h = this._$handlers;
  1013. if (!handler || !event) {
  1014. return this;
  1015. }
  1016. if (!_h[event]) {
  1017. _h[event] = [];
  1018. }
  1019. for (var i = 0; i < _h[event].length; i++) {
  1020. if (_h[event][i].h === handler) {
  1021. return this;
  1022. }
  1023. }
  1024. _h[event].push({
  1025. h: handler,
  1026. one: true,
  1027. ctx: context || this
  1028. });
  1029. return this;
  1030. },
  1031. /**
  1032. * 绑定事件
  1033. * @param {string} event 事件名
  1034. * @param {Function} handler 事件处理函数
  1035. * @param {Object} [context]
  1036. */
  1037. on: function (event, handler, context) {
  1038. var _h = this._$handlers;
  1039. if (!handler || !event) {
  1040. return this;
  1041. }
  1042. if (!_h[event]) {
  1043. _h[event] = [];
  1044. }
  1045. for (var i = 0; i < _h[event].length; i++) {
  1046. if (_h[event][i].h === handler) {
  1047. return this;
  1048. }
  1049. }
  1050. _h[event].push({
  1051. h: handler,
  1052. one: false,
  1053. ctx: context || this
  1054. });
  1055. return this;
  1056. },
  1057. /**
  1058. * 是否绑定了事件
  1059. * @param {string} event
  1060. * @return {boolean}
  1061. */
  1062. isSilent: function (event) {
  1063. var _h = this._$handlers;
  1064. return _h[event] && _h[event].length;
  1065. },
  1066. /**
  1067. * 解绑事件
  1068. * @param {string} event 事件名
  1069. * @param {Function} [handler] 事件处理函数
  1070. */
  1071. off: function (event, handler) {
  1072. var _h = this._$handlers;
  1073. if (!event) {
  1074. this._$handlers = {};
  1075. return this;
  1076. }
  1077. if (handler) {
  1078. if (_h[event]) {
  1079. var newList = [];
  1080. for (var i = 0, l = _h[event].length; i < l; i++) {
  1081. if (_h[event][i]['h'] != handler) {
  1082. newList.push(_h[event][i]);
  1083. }
  1084. }
  1085. _h[event] = newList;
  1086. }
  1087. if (_h[event] && _h[event].length === 0) {
  1088. delete _h[event];
  1089. }
  1090. }
  1091. else {
  1092. delete _h[event];
  1093. }
  1094. return this;
  1095. },
  1096. /**
  1097. * 事件分发
  1098. *
  1099. * @param {string} type 事件类型
  1100. */
  1101. trigger: function (type) {
  1102. if (this._$handlers[type]) {
  1103. var args = arguments;
  1104. var argLen = args.length;
  1105. if (argLen > 3) {
  1106. args = arrySlice.call(args, 1);
  1107. }
  1108. var _h = this._$handlers[type];
  1109. var len = _h.length;
  1110. for (var i = 0; i < len;) {
  1111. // Optimize advise from backbone
  1112. switch (argLen) {
  1113. case 1:
  1114. _h[i]['h'].call(_h[i]['ctx']);
  1115. break;
  1116. case 2:
  1117. _h[i]['h'].call(_h[i]['ctx'], args[1]);
  1118. break;
  1119. case 3:
  1120. _h[i]['h'].call(_h[i]['ctx'], args[1], args[2]);
  1121. break;
  1122. default:
  1123. // have more than 2 given arguments
  1124. _h[i]['h'].apply(_h[i]['ctx'], args);
  1125. break;
  1126. }
  1127. if (_h[i]['one']) {
  1128. _h.splice(i, 1);
  1129. len--;
  1130. }
  1131. else {
  1132. i++;
  1133. }
  1134. }
  1135. }
  1136. return this;
  1137. },
  1138. /**
  1139. * 带有context的事件分发, 最后一个参数是事件回调的context
  1140. * @param {string} type 事件类型
  1141. */
  1142. triggerWithContext: function (type) {
  1143. if (this._$handlers[type]) {
  1144. var args = arguments;
  1145. var argLen = args.length;
  1146. if (argLen > 4) {
  1147. args = arrySlice.call(args, 1, args.length - 1);
  1148. }
  1149. var ctx = args[args.length - 1];
  1150. var _h = this._$handlers[type];
  1151. var len = _h.length;
  1152. for (var i = 0; i < len;) {
  1153. // Optimize advise from backbone
  1154. switch (argLen) {
  1155. case 1:
  1156. _h[i]['h'].call(ctx);
  1157. break;
  1158. case 2:
  1159. _h[i]['h'].call(ctx, args[1]);
  1160. break;
  1161. case 3:
  1162. _h[i]['h'].call(ctx, args[1], args[2]);
  1163. break;
  1164. default:
  1165. // have more than 2 given arguments
  1166. _h[i]['h'].apply(ctx, args);
  1167. break;
  1168. }
  1169. if (_h[i]['one']) {
  1170. _h.splice(i, 1);
  1171. len--;
  1172. }
  1173. else {
  1174. i++;
  1175. }
  1176. }
  1177. }
  1178. return this;
  1179. }
  1180. };
  1181. /**
  1182. * Handler
  1183. * @module zrender/Handler
  1184. * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
  1185. * errorrik (errorrik@gmail.com)
  1186. * pissang (shenyi.914@gmail.com)
  1187. */
  1188. var SILENT = 'silent';
  1189. function makeEventPacket(eveType, targetInfo, event) {
  1190. return {
  1191. type: eveType,
  1192. event: event,
  1193. // target can only be an element that is not silent.
  1194. target: targetInfo.target,
  1195. // topTarget can be a silent element.
  1196. topTarget: targetInfo.topTarget,
  1197. cancelBubble: false,
  1198. offsetX: event.zrX,
  1199. offsetY: event.zrY,
  1200. gestureEvent: event.gestureEvent,
  1201. pinchX: event.pinchX,
  1202. pinchY: event.pinchY,
  1203. pinchScale: event.pinchScale,
  1204. wheelDelta: event.zrDelta,
  1205. zrByTouch: event.zrByTouch,
  1206. which: event.which
  1207. };
  1208. }
  1209. function EmptyProxy () {}
  1210. EmptyProxy.prototype.dispose = function () {};
  1211. var handlerNames = [
  1212. 'click', 'dblclick', 'mousewheel', 'mouseout',
  1213. 'mouseup', 'mousedown', 'mousemove', 'contextmenu'
  1214. ];
  1215. /**
  1216. * @alias module:zrender/Handler
  1217. * @constructor
  1218. * @extends module:zrender/mixin/Eventful
  1219. * @param {module:zrender/Storage} storage Storage instance.
  1220. * @param {module:zrender/Painter} painter Painter instance.
  1221. * @param {module:zrender/dom/HandlerProxy} proxy HandlerProxy instance.
  1222. * @param {HTMLElement} painterRoot painter.root (not painter.getViewportRoot()).
  1223. */
  1224. var Handler = function(storage, painter, proxy, painterRoot) {
  1225. Eventful.call(this);
  1226. this.storage = storage;
  1227. this.painter = painter;
  1228. this.painterRoot = painterRoot;
  1229. proxy = proxy || new EmptyProxy();
  1230. /**
  1231. * Proxy of event. can be Dom, WebGLSurface, etc.
  1232. */
  1233. this.proxy = proxy;
  1234. // Attach handler
  1235. proxy.handler = this;
  1236. /**
  1237. * {target, topTarget, x, y}
  1238. * @private
  1239. * @type {Object}
  1240. */
  1241. this._hovered = {};
  1242. /**
  1243. * @private
  1244. * @type {Date}
  1245. */
  1246. this._lastTouchMoment;
  1247. /**
  1248. * @private
  1249. * @type {number}
  1250. */
  1251. this._lastX;
  1252. /**
  1253. * @private
  1254. * @type {number}
  1255. */
  1256. this._lastY;
  1257. Draggable.call(this);
  1258. each$1(handlerNames, function (name) {
  1259. proxy.on && proxy.on(name, this[name], this);
  1260. }, this);
  1261. };
  1262. Handler.prototype = {
  1263. constructor: Handler,
  1264. mousemove: function (event) {
  1265. var x = event.zrX;
  1266. var y = event.zrY;
  1267. var lastHovered = this._hovered;
  1268. var lastHoveredTarget = lastHovered.target;
  1269. // If lastHoveredTarget is removed from zr (detected by '__zr') by some API call
  1270. // (like 'setOption' or 'dispatchAction') in event handlers, we should find
  1271. // lastHovered again here. Otherwise 'mouseout' can not be triggered normally.
  1272. // See #6198.
  1273. if (lastHoveredTarget && !lastHoveredTarget.__zr) {
  1274. lastHovered = this.findHover(lastHovered.x, lastHovered.y);
  1275. lastHoveredTarget = lastHovered.target;
  1276. }
  1277. var hovered = this._hovered = this.findHover(x, y);
  1278. var hoveredTarget = hovered.target;
  1279. var proxy = this.proxy;
  1280. proxy.setCursor && proxy.setCursor(hoveredTarget ? hoveredTarget.cursor : 'default');
  1281. // Mouse out on previous hovered element
  1282. if (lastHoveredTarget && hoveredTarget !== lastHoveredTarget) {
  1283. this.dispatchToElement(lastHovered, 'mouseout', event);
  1284. }
  1285. // Mouse moving on one element
  1286. this.dispatchToElement(hovered, 'mousemove', event);
  1287. // Mouse over on a new element
  1288. if (hoveredTarget && hoveredTarget !== lastHoveredTarget) {
  1289. this.dispatchToElement(hovered, 'mouseover', event);
  1290. }
  1291. },
  1292. mouseout: function (event) {
  1293. this.dispatchToElement(this._hovered, 'mouseout', event);
  1294. // There might be some doms created by upper layer application
  1295. // at the same level of painter.getViewportRoot() (e.g., tooltip
  1296. // dom created by echarts), where 'globalout' event should not
  1297. // be triggered when mouse enters these doms. (But 'mouseout'
  1298. // should be triggered at the original hovered element as usual).
  1299. var element = event.toElement || event.relatedTarget;
  1300. var innerDom;
  1301. do {
  1302. element = element && element.parentNode;
  1303. }
  1304. while (element && element.nodeType != 9 && !(
  1305. innerDom = element === this.painterRoot
  1306. ));
  1307. !innerDom && this.trigger('globalout', {event: event});
  1308. },
  1309. /**
  1310. * Resize
  1311. */
  1312. resize: function (event) {
  1313. this._hovered = {};
  1314. },
  1315. /**
  1316. * Dispatch event
  1317. * @param {string} eventName
  1318. * @param {event=} eventArgs
  1319. */
  1320. dispatch: function (eventName, eventArgs) {
  1321. var handler = this[eventName];
  1322. handler && handler.call(this, eventArgs);
  1323. },
  1324. /**
  1325. * Dispose
  1326. */
  1327. dispose: function () {
  1328. this.proxy.dispose();
  1329. this.storage =
  1330. this.proxy =
  1331. this.painter = null;
  1332. },
  1333. /**
  1334. * 设置默认的cursor style
  1335. * @param {string} [cursorStyle='default'] 例如 crosshair
  1336. */
  1337. setCursorStyle: function (cursorStyle) {
  1338. var proxy = this.proxy;
  1339. proxy.setCursor && proxy.setCursor(cursorStyle);
  1340. },
  1341. /**
  1342. * 事件分发代理
  1343. *
  1344. * @private
  1345. * @param {Object} targetInfo {target, topTarget} 目标图形元素
  1346. * @param {string} eventName 事件名称
  1347. * @param {Object} event 事件对象
  1348. */
  1349. dispatchToElement: function (targetInfo, eventName, event) {
  1350. targetInfo = targetInfo || {};
  1351. var el = targetInfo.target;
  1352. if (el && el.silent) {
  1353. return;
  1354. }
  1355. var eventHandler = 'on' + eventName;
  1356. var eventPacket = makeEventPacket(eventName, targetInfo, event);
  1357. while (el) {
  1358. el[eventHandler]
  1359. && (eventPacket.cancelBubble = el[eventHandler].call(el, eventPacket));
  1360. el.trigger(eventName, eventPacket);
  1361. el = el.parent;
  1362. if (eventPacket.cancelBubble) {
  1363. break;
  1364. }
  1365. }
  1366. if (!eventPacket.cancelBubble) {
  1367. // 冒泡到顶级 zrender 对象
  1368. this.trigger(eventName, eventPacket);
  1369. // 分发事件到用户自定义层
  1370. // 用户有可能在全局 click 事件中 dispose,所以需要判断下 painter 是否存在
  1371. this.painter && this.painter.eachOtherLayer(function (layer) {
  1372. if (typeof(layer[eventHandler]) == 'function') {
  1373. layer[eventHandler].call(layer, eventPacket);
  1374. }
  1375. if (layer.trigger) {
  1376. layer.trigger(eventName, eventPacket);
  1377. }
  1378. });
  1379. }
  1380. },
  1381. /**
  1382. * @private
  1383. * @param {number} x
  1384. * @param {number} y
  1385. * @param {module:zrender/graphic/Displayable} exclude
  1386. * @return {model:zrender/Element}
  1387. * @method
  1388. */
  1389. findHover: function(x, y, exclude) {
  1390. var list = this.storage.getDisplayList();
  1391. var out = {x: x, y: y};
  1392. for (var i = list.length - 1; i >= 0 ; i--) {
  1393. var hoverCheckResult;
  1394. if (list[i] !== exclude
  1395. // getDisplayList may include ignored item in VML mode
  1396. && !list[i].ignore
  1397. && (hoverCheckResult = isHover(list[i], x, y))
  1398. ) {
  1399. !out.topTarget && (out.topTarget = list[i]);
  1400. if (hoverCheckResult !== SILENT) {
  1401. out.target = list[i];
  1402. break;
  1403. }
  1404. }
  1405. }
  1406. return out;
  1407. }
  1408. };
  1409. // Common handlers
  1410. each$1(['click', 'mousedown', 'mouseup', 'mousewheel', 'dblclick', 'contextmenu'], function (name) {
  1411. Handler.prototype[name] = function (event) {
  1412. // Find hover again to avoid click event is dispatched manually. Or click is triggered without mouseover
  1413. var hovered = this.findHover(event.zrX, event.zrY);
  1414. var hoveredTarget = hovered.target;
  1415. if (name === 'mousedown') {
  1416. this._downEl = hoveredTarget;
  1417. this._downPoint = [event.zrX, event.zrY];
  1418. // In case click triggered before mouseup
  1419. this._upEl = hoveredTarget;
  1420. }
  1421. else if (name === 'mosueup') {
  1422. this._upEl = hoveredTarget;
  1423. }
  1424. else if (name === 'click') {
  1425. if (this._downEl !== this._upEl
  1426. // Original click event is triggered on the whole canvas element,
  1427. // including the case that `mousedown` - `mousemove` - `mouseup`,
  1428. // which should be filtered, otherwise it will bring trouble to
  1429. // pan and zoom.
  1430. || !this._downPoint
  1431. // Arbitrary value
  1432. || dist(this._downPoint, [event.zrX, event.zrY]) > 4
  1433. ) {
  1434. return;
  1435. }
  1436. this._downPoint = null;
  1437. }
  1438. this.dispatchToElement(hovered, name, event);
  1439. };
  1440. });
  1441. function isHover(displayable, x, y) {
  1442. if (displayable[displayable.rectHover ? 'rectContain' : 'contain'](x, y)) {
  1443. var el = displayable;
  1444. var isSilent;
  1445. while (el) {
  1446. // If clipped by ancestor.
  1447. // FIXME: If clipPath has neither stroke nor fill,
  1448. // el.clipPath.contain(x, y) will always return false.
  1449. if (el.clipPath && !el.clipPath.contain(x, y)) {
  1450. return false;
  1451. }
  1452. if (el.silent) {
  1453. isSilent = true;
  1454. }
  1455. el = el.parent;
  1456. }
  1457. return isSilent ? SILENT : true;
  1458. }
  1459. return false;
  1460. }
  1461. mixin(Handler, Eventful);
  1462. mixin(Handler, Draggable);
  1463. /**
  1464. * 3x2矩阵操作类
  1465. * @exports zrender/tool/matrix
  1466. */
  1467. var ArrayCtor$1 = typeof Float32Array === 'undefined'
  1468. ? Array
  1469. : Float32Array;
  1470. /**
  1471. * 创建一个单位矩阵
  1472. * @return {Float32Array|Array.<number>}
  1473. */
  1474. function create$1() {
  1475. var out = new ArrayCtor$1(6);
  1476. identity(out);
  1477. return out;
  1478. }
  1479. /**
  1480. * 设置矩阵为单位矩阵
  1481. * @param {Float32Array|Array.<number>} out
  1482. */
  1483. function identity(out) {
  1484. out[0] = 1;
  1485. out[1] = 0;
  1486. out[2] = 0;
  1487. out[3] = 1;
  1488. out[4] = 0;
  1489. out[5] = 0;
  1490. return out;
  1491. }
  1492. /**
  1493. * 复制矩阵
  1494. * @param {Float32Array|Array.<number>} out
  1495. * @param {Float32Array|Array.<number>} m
  1496. */
  1497. function copy$1(out, m) {
  1498. out[0] = m[0];
  1499. out[1] = m[1];
  1500. out[2] = m[2];
  1501. out[3] = m[3];
  1502. out[4] = m[4];
  1503. out[5] = m[5];
  1504. return out;
  1505. }
  1506. /**
  1507. * 矩阵相乘
  1508. * @param {Float32Array|Array.<number>} out
  1509. * @param {Float32Array|Array.<number>} m1
  1510. * @param {Float32Array|Array.<number>} m2
  1511. */
  1512. function mul$1(out, m1, m2) {
  1513. // Consider matrix.mul(m, m2, m);
  1514. // where out is the same as m2.
  1515. // So use temp variable to escape error.
  1516. var out0 = m1[0] * m2[0] + m1[2] * m2[1];
  1517. var out1 = m1[1] * m2[0] + m1[3] * m2[1];
  1518. var out2 = m1[0] * m2[2] + m1[2] * m2[3];
  1519. var out3 = m1[1] * m2[2] + m1[3] * m2[3];
  1520. var out4 = m1[0] * m2[4] + m1[2] * m2[5] + m1[4];
  1521. var out5 = m1[1] * m2[4] + m1[3] * m2[5] + m1[5];
  1522. out[0] = out0;
  1523. out[1] = out1;
  1524. out[2] = out2;
  1525. out[3] = out3;
  1526. out[4] = out4;
  1527. out[5] = out5;
  1528. return out;
  1529. }
  1530. /**
  1531. * 平移变换
  1532. * @param {Float32Array|Array.<number>} out
  1533. * @param {Float32Array|Array.<number>} a
  1534. * @param {Float32Array|Array.<number>} v
  1535. */
  1536. function translate(out, a, v) {
  1537. out[0] = a[0];
  1538. out[1] = a[1];
  1539. out[2] = a[2];
  1540. out[3] = a[3];
  1541. out[4] = a[4] + v[0];
  1542. out[5] = a[5] + v[1];
  1543. return out;
  1544. }
  1545. /**
  1546. * 旋转变换
  1547. * @param {Float32Array|Array.<number>} out
  1548. * @param {Float32Array|Array.<number>} a
  1549. * @param {number} rad
  1550. */
  1551. function rotate(out, a, rad) {
  1552. var aa = a[0];
  1553. var ac = a[2];
  1554. var atx = a[4];
  1555. var ab = a[1];
  1556. var ad = a[3];
  1557. var aty = a[5];
  1558. var st = Math.sin(rad);
  1559. var ct = Math.cos(rad);
  1560. out[0] = aa * ct + ab * st;
  1561. out[1] = -aa * st + ab * ct;
  1562. out[2] = ac * ct + ad * st;
  1563. out[3] = -ac * st + ct * ad;
  1564. out[4] = ct * atx + st * aty;
  1565. out[5] = ct * aty - st * atx;
  1566. return out;
  1567. }
  1568. /**
  1569. * 缩放变换
  1570. * @param {Float32Array|Array.<number>} out
  1571. * @param {Float32Array|Array.<number>} a
  1572. * @param {Float32Array|Array.<number>} v
  1573. */
  1574. function scale$1(out, a, v) {
  1575. var vx = v[0];
  1576. var vy = v[1];
  1577. out[0] = a[0] * vx;
  1578. out[1] = a[1] * vy;
  1579. out[2] = a[2] * vx;
  1580. out[3] = a[3] * vy;
  1581. out[4] = a[4] * vx;
  1582. out[5] = a[5] * vy;
  1583. return out;
  1584. }
  1585. /**
  1586. * 求逆矩阵
  1587. * @param {Float32Array|Array.<number>} out
  1588. * @param {Float32Array|Array.<number>} a
  1589. */
  1590. function invert(out, a) {
  1591. var aa = a[0];
  1592. var ac = a[2];
  1593. var atx = a[4];
  1594. var ab = a[1];
  1595. var ad = a[3];
  1596. var aty = a[5];
  1597. var det = aa * ad - ab * ac;
  1598. if (!det) {
  1599. return null;
  1600. }
  1601. det = 1.0 / det;
  1602. out[0] = ad * det;
  1603. out[1] = -ab * det;
  1604. out[2] = -ac * det;
  1605. out[3] = aa * det;
  1606. out[4] = (ac * aty - ad * atx) * det;
  1607. out[5] = (ab * atx - aa * aty) * det;
  1608. return out;
  1609. }
  1610. /**
  1611. * 提供变换扩展
  1612. * @module zrender/mixin/Transformable
  1613. * @author pissang (https://www.github.com/pissang)
  1614. */
  1615. var mIdentity = identity;
  1616. var EPSILON = 5e-5;
  1617. function isNotAroundZero(val) {
  1618. return val > EPSILON || val < -EPSILON;
  1619. }
  1620. /**
  1621. * @alias module:zrender/mixin/Transformable
  1622. * @constructor
  1623. */
  1624. var Transformable = function (opts) {
  1625. opts = opts || {};
  1626. // If there are no given position, rotation, scale
  1627. if (!opts.position) {
  1628. /**
  1629. * 平移
  1630. * @type {Array.<number>}
  1631. * @default [0, 0]
  1632. */
  1633. this.position = [0, 0];
  1634. }
  1635. if (opts.rotation == null) {
  1636. /**
  1637. * 旋转
  1638. * @type {Array.<number>}
  1639. * @default 0
  1640. */
  1641. this.rotation = 0;
  1642. }
  1643. if (!opts.scale) {
  1644. /**
  1645. * 缩放
  1646. * @type {Array.<number>}
  1647. * @default [1, 1]
  1648. */
  1649. this.scale = [1, 1];
  1650. }
  1651. /**
  1652. * 旋转和缩放的原点
  1653. * @type {Array.<number>}
  1654. * @default null
  1655. */
  1656. this.origin = this.origin || null;
  1657. };
  1658. var transformableProto = Transformable.prototype;
  1659. transformableProto.transform = null;
  1660. /**
  1661. * 判断是否需要有坐标变换
  1662. * 如果有坐标变换, 则从position, rotation, scale以及父节点的transform计算出自身的transform矩阵
  1663. */
  1664. transformableProto.needLocalTransform = function () {
  1665. return isNotAroundZero(this.rotation)
  1666. || isNotAroundZero(this.position[0])
  1667. || isNotAroundZero(this.position[1])
  1668. || isNotAroundZero(this.scale[0] - 1)
  1669. || isNotAroundZero(this.scale[1] - 1);
  1670. };
  1671. transformableProto.updateTransform = function () {
  1672. var parent = this.parent;
  1673. var parentHasTransform = parent && parent.transform;
  1674. var needLocalTransform = this.needLocalTransform();
  1675. var m = this.transform;
  1676. if (!(needLocalTransform || parentHasTransform)) {
  1677. m && mIdentity(m);
  1678. return;
  1679. }
  1680. m = m || create$1();
  1681. if (needLocalTransform) {
  1682. this.getLocalTransform(m);
  1683. }
  1684. else {
  1685. mIdentity(m);
  1686. }
  1687. // 应用父节点变换
  1688. if (parentHasTransform) {
  1689. if (needLocalTransform) {
  1690. mul$1(m, parent.transform, m);
  1691. }
  1692. else {
  1693. copy$1(m, parent.transform);
  1694. }
  1695. }
  1696. // 保存这个变换矩阵
  1697. this.transform = m;
  1698. this.invTransform = this.invTransform || create$1();
  1699. invert(this.invTransform, m);
  1700. };
  1701. transformableProto.getLocalTransform = function (m) {
  1702. return Transformable.getLocalTransform(this, m);
  1703. };
  1704. /**
  1705. * 将自己的transform应用到context上
  1706. * @param {CanvasRenderingContext2D} ctx
  1707. */
  1708. transformableProto.setTransform = function (ctx) {
  1709. var m = this.transform;
  1710. var dpr = ctx.dpr || 1;
  1711. if (m) {
  1712. ctx.setTransform(dpr * m[0], dpr * m[1], dpr * m[2], dpr * m[3], dpr * m[4], dpr * m[5]);
  1713. }
  1714. else {
  1715. ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
  1716. }
  1717. };
  1718. transformableProto.restoreTransform = function (ctx) {
  1719. var dpr = ctx.dpr || 1;
  1720. ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
  1721. };
  1722. var tmpTransform = [];
  1723. /**
  1724. * 分解`transform`矩阵到`position`, `rotation`, `scale`
  1725. */
  1726. transformableProto.decomposeTransform = function () {
  1727. if (!this.transform) {
  1728. return;
  1729. }
  1730. var parent = this.parent;
  1731. var m = this.transform;
  1732. if (parent && parent.transform) {
  1733. // Get local transform and decompose them to position, scale, rotation
  1734. mul$1(tmpTransform, parent.invTransform, m);
  1735. m = tmpTransform;
  1736. }
  1737. var sx = m[0] * m[0] + m[1] * m[1];
  1738. var sy = m[2] * m[2] + m[3] * m[3];
  1739. var position = this.position;
  1740. var scale$$1 = this.scale;
  1741. if (isNotAroundZero(sx - 1)) {
  1742. sx = Math.sqrt(sx);
  1743. }
  1744. if (isNotAroundZero(sy - 1)) {
  1745. sy = Math.sqrt(sy);
  1746. }
  1747. if (m[0] < 0) {
  1748. sx = -sx;
  1749. }
  1750. if (m[3] < 0) {
  1751. sy = -sy;
  1752. }
  1753. position[0] = m[4];
  1754. position[1] = m[5];
  1755. scale$$1[0] = sx;
  1756. scale$$1[1] = sy;
  1757. this.rotation = Math.atan2(-m[1] / sy, m[0] / sx);
  1758. };
  1759. /**
  1760. * Get global scale
  1761. * @return {Array.<number>}
  1762. */
  1763. transformableProto.getGlobalScale = function () {
  1764. var m = this.transform;
  1765. if (!m) {
  1766. return [1, 1];
  1767. }
  1768. var sx = Math.sqrt(m[0] * m[0] + m[1] * m[1]);
  1769. var sy = Math.sqrt(m[2] * m[2] + m[3] * m[3]);
  1770. if (m[0] < 0) {
  1771. sx = -sx;
  1772. }
  1773. if (m[3] < 0) {
  1774. sy = -sy;
  1775. }
  1776. return [sx, sy];
  1777. };
  1778. /**
  1779. * 变换坐标位置到 shape 的局部坐标空间
  1780. * @method
  1781. * @param {number} x
  1782. * @param {number} y
  1783. * @return {Array.<number>}
  1784. */
  1785. transformableProto.transformCoordToLocal = function (x, y) {
  1786. var v2 = [x, y];
  1787. var invTransform = this.invTransform;
  1788. if (invTransform) {
  1789. applyTransform(v2, v2, invTransform);
  1790. }
  1791. return v2;
  1792. };
  1793. /**
  1794. * 变换局部坐标位置到全局坐标空间
  1795. * @method
  1796. * @param {number} x
  1797. * @param {number} y
  1798. * @return {Array.<number>}
  1799. */
  1800. transformableProto.transformCoordToGlobal = function (x, y) {
  1801. var v2 = [x, y];
  1802. var transform = this.transform;
  1803. if (transform) {
  1804. applyTransform(v2, v2, transform);
  1805. }
  1806. return v2;
  1807. };
  1808. /**
  1809. * @static
  1810. * @param {Object} target
  1811. * @param {Array.<number>} target.origin
  1812. * @param {number} target.rotation
  1813. * @param {Array.<number>} target.position
  1814. * @param {Array.<number>} [m]
  1815. */
  1816. Transformable.getLocalTransform = function (target, m) {
  1817. m = m || [];
  1818. mIdentity(m);
  1819. var origin = target.origin;
  1820. var scale$$1 = target.scale || [1, 1];
  1821. var rotation = target.rotation || 0;
  1822. var position = target.position || [0, 0];
  1823. if (origin) {
  1824. // Translate to origin
  1825. m[4] -= origin[0];
  1826. m[5] -= origin[1];
  1827. }
  1828. scale$1(m, m, scale$$1);
  1829. if (rotation) {
  1830. rotate(m, m, rotation);
  1831. }
  1832. if (origin) {
  1833. // Translate back from origin
  1834. m[4] += origin[0];
  1835. m[5] += origin[1];
  1836. }
  1837. m[4] += position[0];
  1838. m[5] += position[1];
  1839. return m;
  1840. };
  1841. /**
  1842. * 缓动代码来自 https://github.com/sole/tween.js/blob/master/src/Tween.js
  1843. * @see http://sole.github.io/tween.js/examples/03_graphs.html
  1844. * @exports zrender/animation/easing
  1845. */
  1846. var easing = {
  1847. /**
  1848. * @param {number} k
  1849. * @return {number}
  1850. */
  1851. linear: function (k) {
  1852. return k;
  1853. },
  1854. /**
  1855. * @param {number} k
  1856. * @return {number}
  1857. */
  1858. quadraticIn: function (k) {
  1859. return k * k;
  1860. },
  1861. /**
  1862. * @param {number} k
  1863. * @return {number}
  1864. */
  1865. quadraticOut: function (k) {
  1866. return k * (2 - k);
  1867. },
  1868. /**
  1869. * @param {number} k
  1870. * @return {number}
  1871. */
  1872. quadraticInOut: function (k) {
  1873. if ((k *= 2) < 1) {
  1874. return 0.5 * k * k;
  1875. }
  1876. return -0.5 * (--k * (k - 2) - 1);
  1877. },
  1878. // 三次方的缓动(t^3)
  1879. /**
  1880. * @param {number} k
  1881. * @return {number}
  1882. */
  1883. cubicIn: function (k) {
  1884. return k * k * k;
  1885. },
  1886. /**
  1887. * @param {number} k
  1888. * @return {number}
  1889. */
  1890. cubicOut: function (k) {
  1891. return --k * k * k + 1;
  1892. },
  1893. /**
  1894. * @param {number} k
  1895. * @return {number}
  1896. */
  1897. cubicInOut: function (k) {
  1898. if ((k *= 2) < 1) {
  1899. return 0.5 * k * k * k;
  1900. }
  1901. return 0.5 * ((k -= 2) * k * k + 2);
  1902. },
  1903. // 四次方的缓动(t^4)
  1904. /**
  1905. * @param {number} k
  1906. * @return {number}
  1907. */
  1908. quarticIn: function (k) {
  1909. return k * k * k * k;
  1910. },
  1911. /**
  1912. * @param {number} k
  1913. * @return {number}
  1914. */
  1915. quarticOut: function (k) {
  1916. return 1 - (--k * k * k * k);
  1917. },
  1918. /**
  1919. * @param {number} k
  1920. * @return {number}
  1921. */
  1922. quarticInOut: function (k) {
  1923. if ((k *= 2) < 1) {
  1924. return 0.5 * k * k * k * k;
  1925. }
  1926. return -0.5 * ((k -= 2) * k * k * k - 2);
  1927. },
  1928. // 五次方的缓动(t^5)
  1929. /**
  1930. * @param {number} k
  1931. * @return {number}
  1932. */
  1933. quinticIn: function (k) {
  1934. return k * k * k * k * k;
  1935. },
  1936. /**
  1937. * @param {number} k
  1938. * @return {number}
  1939. */
  1940. quinticOut: function (k) {
  1941. return --k * k * k * k * k + 1;
  1942. },
  1943. /**
  1944. * @param {number} k
  1945. * @return {number}
  1946. */
  1947. quinticInOut: function (k) {
  1948. if ((k *= 2) < 1) {
  1949. return 0.5 * k * k * k * k * k;
  1950. }
  1951. return 0.5 * ((k -= 2) * k * k * k * k + 2);
  1952. },
  1953. // 正弦曲线的缓动(sin(t))
  1954. /**
  1955. * @param {number} k
  1956. * @return {number}
  1957. */
  1958. sinusoidalIn: function (k) {
  1959. return 1 - Math.cos(k * Math.PI / 2);
  1960. },
  1961. /**
  1962. * @param {number} k
  1963. * @return {number}
  1964. */
  1965. sinusoidalOut: function (k) {
  1966. return Math.sin(k * Math.PI / 2);
  1967. },
  1968. /**
  1969. * @param {number} k
  1970. * @return {number}
  1971. */
  1972. sinusoidalInOut: function (k) {
  1973. return 0.5 * (1 - Math.cos(Math.PI * k));
  1974. },
  1975. // 指数曲线的缓动(2^t)
  1976. /**
  1977. * @param {number} k
  1978. * @return {number}
  1979. */
  1980. exponentialIn: function (k) {
  1981. return k === 0 ? 0 : Math.pow(1024, k - 1);
  1982. },
  1983. /**
  1984. * @param {number} k
  1985. * @return {number}
  1986. */
  1987. exponentialOut: function (k) {
  1988. return k === 1 ? 1 : 1 - Math.pow(2, -10 * k);
  1989. },
  1990. /**
  1991. * @param {number} k
  1992. * @return {number}
  1993. */
  1994. exponentialInOut: function (k) {
  1995. if (k === 0) {
  1996. return 0;
  1997. }
  1998. if (k === 1) {
  1999. return 1;
  2000. }
  2001. if ((k *= 2) < 1) {
  2002. return 0.5 * Math.pow(1024, k - 1);
  2003. }
  2004. return 0.5 * (-Math.pow(2, -10 * (k - 1)) + 2);
  2005. },
  2006. // 圆形曲线的缓动(sqrt(1-t^2))
  2007. /**
  2008. * @param {number} k
  2009. * @return {number}
  2010. */
  2011. circularIn: function (k) {
  2012. return 1 - Math.sqrt(1 - k * k);
  2013. },
  2014. /**
  2015. * @param {number} k
  2016. * @return {number}
  2017. */
  2018. circularOut: function (k) {
  2019. return Math.sqrt(1 - (--k * k));
  2020. },
  2021. /**
  2022. * @param {number} k
  2023. * @return {number}
  2024. */
  2025. circularInOut: function (k) {
  2026. if ((k *= 2) < 1) {
  2027. return -0.5 * (Math.sqrt(1 - k * k) - 1);
  2028. }
  2029. return 0.5 * (Math.sqrt(1 - (k -= 2) * k) + 1);
  2030. },
  2031. // 创建类似于弹簧在停止前来回振荡的动画
  2032. /**
  2033. * @param {number} k
  2034. * @return {number}
  2035. */
  2036. elasticIn: function (k) {
  2037. var s;
  2038. var a = 0.1;
  2039. var p = 0.4;
  2040. if (k === 0) {
  2041. return 0;
  2042. }
  2043. if (k === 1) {
  2044. return 1;
  2045. }
  2046. if (!a || a < 1) {
  2047. a = 1; s = p / 4;
  2048. }
  2049. else {
  2050. s = p * Math.asin(1 / a) / (2 * Math.PI);
  2051. }
  2052. return -(a * Math.pow(2, 10 * (k -= 1)) *
  2053. Math.sin((k - s) * (2 * Math.PI) / p));
  2054. },
  2055. /**
  2056. * @param {number} k
  2057. * @return {number}
  2058. */
  2059. elasticOut: function (k) {
  2060. var s;
  2061. var a = 0.1;
  2062. var p = 0.4;
  2063. if (k === 0) {
  2064. return 0;
  2065. }
  2066. if (k === 1) {
  2067. return 1;
  2068. }
  2069. if (!a || a < 1) {
  2070. a = 1; s = p / 4;
  2071. }
  2072. else {
  2073. s = p * Math.asin(1 / a) / (2 * Math.PI);
  2074. }
  2075. return (a * Math.pow(2, -10 * k) *
  2076. Math.sin((k - s) * (2 * Math.PI) / p) + 1);
  2077. },
  2078. /**
  2079. * @param {number} k
  2080. * @return {number}
  2081. */
  2082. elasticInOut: function (k) {
  2083. var s;
  2084. var a = 0.1;
  2085. var p = 0.4;
  2086. if (k === 0) {
  2087. return 0;
  2088. }
  2089. if (k === 1) {
  2090. return 1;
  2091. }
  2092. if (!a || a < 1) {
  2093. a = 1; s = p / 4;
  2094. }
  2095. else {
  2096. s = p * Math.asin(1 / a) / (2 * Math.PI);
  2097. }
  2098. if ((k *= 2) < 1) {
  2099. return -0.5 * (a * Math.pow(2, 10 * (k -= 1))
  2100. * Math.sin((k - s) * (2 * Math.PI) / p));
  2101. }
  2102. return a * Math.pow(2, -10 * (k -= 1))
  2103. * Math.sin((k - s) * (2 * Math.PI) / p) * 0.5 + 1;
  2104. },
  2105. // 在某一动画开始沿指示的路径进行动画处理前稍稍收回该动画的移动
  2106. /**
  2107. * @param {number} k
  2108. * @return {number}
  2109. */
  2110. backIn: function (k) {
  2111. var s = 1.70158;
  2112. return k * k * ((s + 1) * k - s);
  2113. },
  2114. /**
  2115. * @param {number} k
  2116. * @return {number}
  2117. */
  2118. backOut: function (k) {
  2119. var s = 1.70158;
  2120. return --k * k * ((s + 1) * k + s) + 1;
  2121. },
  2122. /**
  2123. * @param {number} k
  2124. * @return {number}
  2125. */
  2126. backInOut: function (k) {
  2127. var s = 1.70158 * 1.525;
  2128. if ((k *= 2) < 1) {
  2129. return 0.5 * (k * k * ((s + 1) * k - s));
  2130. }
  2131. return 0.5 * ((k -= 2) * k * ((s + 1) * k + s) + 2);
  2132. },
  2133. // 创建弹跳效果
  2134. /**
  2135. * @param {number} k
  2136. * @return {number}
  2137. */
  2138. bounceIn: function (k) {
  2139. return 1 - easing.bounceOut(1 - k);
  2140. },
  2141. /**
  2142. * @param {number} k
  2143. * @return {number}
  2144. */
  2145. bounceOut: function (k) {
  2146. if (k < (1 / 2.75)) {
  2147. return 7.5625 * k * k;
  2148. }
  2149. else if (k < (2 / 2.75)) {
  2150. return 7.5625 * (k -= (1.5 / 2.75)) * k + 0.75;
  2151. }
  2152. else if (k < (2.5 / 2.75)) {
  2153. return 7.5625 * (k -= (2.25 / 2.75)) * k + 0.9375;
  2154. }
  2155. else {
  2156. return 7.5625 * (k -= (2.625 / 2.75)) * k + 0.984375;
  2157. }
  2158. },
  2159. /**
  2160. * @param {number} k
  2161. * @return {number}
  2162. */
  2163. bounceInOut: function (k) {
  2164. if (k < 0.5) {
  2165. return easing.bounceIn(k * 2) * 0.5;
  2166. }
  2167. return easing.bounceOut(k * 2 - 1) * 0.5 + 0.5;
  2168. }
  2169. };
  2170. /**
  2171. * 动画主控制器
  2172. * @config target 动画对象,可以是数组,如果是数组的话会批量分发onframe等事件
  2173. * @config life(1000) 动画时长
  2174. * @config delay(0) 动画延迟时间
  2175. * @config loop(true)
  2176. * @config gap(0) 循环的间隔时间
  2177. * @config onframe
  2178. * @config easing(optional)
  2179. * @config ondestroy(optional)
  2180. * @config onrestart(optional)
  2181. *
  2182. * TODO pause
  2183. */
  2184. function Clip(options) {
  2185. this._target = options.target;
  2186. // 生命周期
  2187. this._life = options.life || 1000;
  2188. // 延时
  2189. this._delay = options.delay || 0;
  2190. // 开始时间
  2191. // this._startTime = new Date().getTime() + this._delay;// 单位毫秒
  2192. this._initialized = false;
  2193. // 是否循环
  2194. this.loop = options.loop == null ? false : options.loop;
  2195. this.gap = options.gap || 0;
  2196. this.easing = options.easing || 'Linear';
  2197. this.onframe = options.onframe;
  2198. this.ondestroy = options.ondestroy;
  2199. this.onrestart = options.onrestart;
  2200. this._pausedTime = 0;
  2201. this._paused = false;
  2202. }
  2203. Clip.prototype = {
  2204. constructor: Clip,
  2205. step: function (globalTime, deltaTime) {
  2206. // Set startTime on first step, or _startTime may has milleseconds different between clips
  2207. // PENDING
  2208. if (!this._initialized) {
  2209. this._startTime = globalTime + this._delay;
  2210. this._initialized = true;
  2211. }
  2212. if (this._paused) {
  2213. this._pausedTime += deltaTime;
  2214. return;
  2215. }
  2216. var percent = (globalTime - this._startTime - this._pausedTime) / this._life;
  2217. // 还没开始
  2218. if (percent < 0) {
  2219. return;
  2220. }
  2221. percent = Math.min(percent, 1);
  2222. var easing$$1 = this.easing;
  2223. var easingFunc = typeof easing$$1 == 'string' ? easing[easing$$1] : easing$$1;
  2224. var schedule = typeof easingFunc === 'function'
  2225. ? easingFunc(percent)
  2226. : percent;
  2227. this.fire('frame', schedule);
  2228. // 结束
  2229. if (percent == 1) {
  2230. if (this.loop) {
  2231. this.restart (globalTime);
  2232. // 重新开始周期
  2233. // 抛出而不是直接调用事件直到 stage.update 后再统一调用这些事件
  2234. return 'restart';
  2235. }
  2236. // 动画完成将这个控制器标识为待删除
  2237. // 在Animation.update中进行批量删除
  2238. this._needsRemove = true;
  2239. return 'destroy';
  2240. }
  2241. return null;
  2242. },
  2243. restart: function (globalTime) {
  2244. var remainder = (globalTime - this._startTime - this._pausedTime) % this._life;
  2245. this._startTime = globalTime - remainder + this.gap;
  2246. this._pausedTime = 0;
  2247. this._needsRemove = false;
  2248. },
  2249. fire: function (eventType, arg) {
  2250. eventType = 'on' + eventType;
  2251. if (this[eventType]) {
  2252. this[eventType](this._target, arg);
  2253. }
  2254. },
  2255. pause: function () {
  2256. this._paused = true;
  2257. },
  2258. resume: function () {
  2259. this._paused = false;
  2260. }
  2261. };
  2262. // Simple LRU cache use doubly linked list
  2263. // @module zrender/core/LRU
  2264. /**
  2265. * Simple double linked list. Compared with array, it has O(1) remove operation.
  2266. * @constructor
  2267. */
  2268. var LinkedList = function () {
  2269. /**
  2270. * @type {module:zrender/core/LRU~Entry}
  2271. */
  2272. this.head = null;
  2273. /**
  2274. * @type {module:zrender/core/LRU~Entry}
  2275. */
  2276. this.tail = null;
  2277. this._len = 0;
  2278. };
  2279. var linkedListProto = LinkedList.prototype;
  2280. /**
  2281. * Insert a new value at the tail
  2282. * @param {} val
  2283. * @return {module:zrender/core/LRU~Entry}
  2284. */
  2285. linkedListProto.insert = function (val) {
  2286. var entry = new Entry(val);
  2287. this.insertEntry(entry);
  2288. return entry;
  2289. };
  2290. /**
  2291. * Insert an entry at the tail
  2292. * @param {module:zrender/core/LRU~Entry} entry
  2293. */
  2294. linkedListProto.insertEntry = function (entry) {
  2295. if (!this.head) {
  2296. this.head = this.tail = entry;
  2297. }
  2298. else {
  2299. this.tail.next = entry;
  2300. entry.prev = this.tail;
  2301. entry.next = null;
  2302. this.tail = entry;
  2303. }
  2304. this._len++;
  2305. };
  2306. /**
  2307. * Remove entry.
  2308. * @param {module:zrender/core/LRU~Entry} entry
  2309. */
  2310. linkedListProto.remove = function (entry) {
  2311. var prev = entry.prev;
  2312. var next = entry.next;
  2313. if (prev) {
  2314. prev.next = next;
  2315. }
  2316. else {
  2317. // Is head
  2318. this.head = next;
  2319. }
  2320. if (next) {
  2321. next.prev = prev;
  2322. }
  2323. else {
  2324. // Is tail
  2325. this.tail = prev;
  2326. }
  2327. entry.next = entry.prev = null;
  2328. this._len--;
  2329. };
  2330. /**
  2331. * @return {number}
  2332. */
  2333. linkedListProto.len = function () {
  2334. return this._len;
  2335. };
  2336. /**
  2337. * Clear list
  2338. */
  2339. linkedListProto.clear = function () {
  2340. this.head = this.tail = null;
  2341. this._len = 0;
  2342. };
  2343. /**
  2344. * @constructor
  2345. * @param {} val
  2346. */
  2347. var Entry = function (val) {
  2348. /**
  2349. * @type {}
  2350. */
  2351. this.value = val;
  2352. /**
  2353. * @type {module:zrender/core/LRU~Entry}
  2354. */
  2355. this.next;
  2356. /**
  2357. * @type {module:zrender/core/LRU~Entry}
  2358. */
  2359. this.prev;
  2360. };
  2361. /**
  2362. * LRU Cache
  2363. * @constructor
  2364. * @alias module:zrender/core/LRU
  2365. */
  2366. var LRU = function (maxSize) {
  2367. this._list = new LinkedList();
  2368. this._map = {};
  2369. this._maxSize = maxSize || 10;
  2370. this._lastRemovedEntry = null;
  2371. };
  2372. var LRUProto = LRU.prototype;
  2373. /**
  2374. * @param {string} key
  2375. * @param {} value
  2376. * @return {} Removed value
  2377. */
  2378. LRUProto.put = function (key, value) {
  2379. var list = this._list;
  2380. var map = this._map;
  2381. var removed = null;
  2382. if (map[key] == null) {
  2383. var len = list.len();
  2384. // Reuse last removed entry
  2385. var entry = this._lastRemovedEntry;
  2386. if (len >= this._maxSize && len > 0) {
  2387. // Remove the least recently used
  2388. var leastUsedEntry = list.head;
  2389. list.remove(leastUsedEntry);
  2390. delete map[leastUsedEntry.key];
  2391. removed = leastUsedEntry.value;
  2392. this._lastRemovedEntry = leastUsedEntry;
  2393. }
  2394. if (entry) {
  2395. entry.value = value;
  2396. }
  2397. else {
  2398. entry = new Entry(value);
  2399. }
  2400. entry.key = key;
  2401. list.insertEntry(entry);
  2402. map[key] = entry;
  2403. }
  2404. return removed;
  2405. };
  2406. /**
  2407. * @param {string} key
  2408. * @return {}
  2409. */
  2410. LRUProto.get = function (key) {
  2411. var entry = this._map[key];
  2412. var list = this._list;
  2413. if (entry != null) {
  2414. // Put the latest used entry in the tail
  2415. if (entry !== list.tail) {
  2416. list.remove(entry);
  2417. list.insertEntry(entry);
  2418. }
  2419. return entry.value;
  2420. }
  2421. };
  2422. /**
  2423. * Clear the cache
  2424. */
  2425. LRUProto.clear = function () {
  2426. this._list.clear();
  2427. this._map = {};
  2428. };
  2429. var kCSSColorTable = {
  2430. 'transparent': [0,0,0,0], 'aliceblue': [240,248,255,1],
  2431. 'antiquewhite': [250,235,215,1], 'aqua': [0,255,255,1],
  2432. 'aquamarine': [127,255,212,1], 'azure': [240,255,255,1],
  2433. 'beige': [245,245,220,1], 'bisque': [255,228,196,1],
  2434. 'black': [0,0,0,1], 'blanchedalmond': [255,235,205,1],
  2435. 'blue': [0,0,255,1], 'blueviolet': [138,43,226,1],
  2436. 'brown': [165,42,42,1], 'burlywood': [222,184,135,1],
  2437. 'cadetblue': [95,158,160,1], 'chartreuse': [127,255,0,1],
  2438. 'chocolate': [210,105,30,1], 'coral': [255,127,80,1],
  2439. 'cornflowerblue': [100,149,237,1], 'cornsilk': [255,248,220,1],
  2440. 'crimson': [220,20,60,1], 'cyan': [0,255,255,1],
  2441. 'darkblue': [0,0,139,1], 'darkcyan': [0,139,139,1],
  2442. 'darkgoldenrod': [184,134,11,1], 'darkgray': [169,169,169,1],
  2443. 'darkgreen': [0,100,0,1], 'darkgrey': [169,169,169,1],
  2444. 'darkkhaki': [189,183,107,1], 'darkmagenta': [139,0,139,1],
  2445. 'darkolivegreen': [85,107,47,1], 'darkorange': [255,140,0,1],
  2446. 'darkorchid': [153,50,204,1], 'darkred': [139,0,0,1],
  2447. 'darksalmon': [233,150,122,1], 'darkseagreen': [143,188,143,1],
  2448. 'darkslateblue': [72,61,139,1], 'darkslategray': [47,79,79,1],
  2449. 'darkslategrey': [47,79,79,1], 'darkturquoise': [0,206,209,1],
  2450. 'darkviolet': [148,0,211,1], 'deeppink': [255,20,147,1],
  2451. 'deepskyblue': [0,191,255,1], 'dimgray': [105,105,105,1],
  2452. 'dimgrey': [105,105,105,1], 'dodgerblue': [30,144,255,1],
  2453. 'firebrick': [178,34,34,1], 'floralwhite': [255,250,240,1],
  2454. 'forestgreen': [34,139,34,1], 'fuchsia': [255,0,255,1],
  2455. 'gainsboro': [220,220,220,1], 'ghostwhite': [248,248,255,1],
  2456. 'gold': [255,215,0,1], 'goldenrod': [218,165,32,1],
  2457. 'gray': [128,128,128,1], 'green': [0,128,0,1],
  2458. 'greenyellow': [173,255,47,1], 'grey': [128,128,128,1],
  2459. 'honeydew': [240,255,240,1], 'hotpink': [255,105,180,1],
  2460. 'indianred': [205,92,92,1], 'indigo': [75,0,130,1],
  2461. 'ivory': [255,255,240,1], 'khaki': [240,230,140,1],
  2462. 'lavender': [230,230,250,1], 'lavenderblush': [255,240,245,1],
  2463. 'lawngreen': [124,252,0,1], 'lemonchiffon': [255,250,205,1],
  2464. 'lightblue': [173,216,230,1], 'lightcoral': [240,128,128,1],
  2465. 'lightcyan': [224,255,255,1], 'lightgoldenrodyellow': [250,250,210,1],
  2466. 'lightgray': [211,211,211,1], 'lightgreen': [144,238,144,1],
  2467. 'lightgrey': [211,211,211,1], 'lightpink': [255,182,193,1],
  2468. 'lightsalmon': [255,160,122,1], 'lightseagreen': [32,178,170,1],
  2469. 'lightskyblue': [135,206,250,1], 'lightslategray': [119,136,153,1],
  2470. 'lightslategrey': [119,136,153,1], 'lightsteelblue': [176,196,222,1],
  2471. 'lightyellow': [255,255,224,1], 'lime': [0,255,0,1],
  2472. 'limegreen': [50,205,50,1], 'linen': [250,240,230,1],
  2473. 'magenta': [255,0,255,1], 'maroon': [128,0,0,1],
  2474. 'mediumaquamarine': [102,205,170,1], 'mediumblue': [0,0,205,1],
  2475. 'mediumorchid': [186,85,211,1], 'mediumpurple': [147,112,219,1],
  2476. 'mediumseagreen': [60,179,113,1], 'mediumslateblue': [123,104,238,1],
  2477. 'mediumspringgreen': [0,250,154,1], 'mediumturquoise': [72,209,204,1],
  2478. 'mediumvioletred': [199,21,133,1], 'midnightblue': [25,25,112,1],
  2479. 'mintcream': [245,255,250,1], 'mistyrose': [255,228,225,1],
  2480. 'moccasin': [255,228,181,1], 'navajowhite': [255,222,173,1],
  2481. 'navy': [0,0,128,1], 'oldlace': [253,245,230,1],
  2482. 'olive': [128,128,0,1], 'olivedrab': [107,142,35,1],
  2483. 'orange': [255,165,0,1], 'orangered': [255,69,0,1],
  2484. 'orchid': [218,112,214,1], 'palegoldenrod': [238,232,170,1],
  2485. 'palegreen': [152,251,152,1], 'paleturquoise': [175,238,238,1],
  2486. 'palevioletred': [219,112,147,1], 'papayawhip': [255,239,213,1],
  2487. 'peachpuff': [255,218,185,1], 'peru': [205,133,63,1],
  2488. 'pink': [255,192,203,1], 'plum': [221,160,221,1],
  2489. 'powderblue': [176,224,230,1], 'purple': [128,0,128,1],
  2490. 'red': [255,0,0,1], 'rosybrown': [188,143,143,1],
  2491. 'royalblue': [65,105,225,1], 'saddlebrown': [139,69,19,1],
  2492. 'salmon': [250,128,114,1], 'sandybrown': [244,164,96,1],
  2493. 'seagreen': [46,139,87,1], 'seashell': [255,245,238,1],
  2494. 'sienna': [160,82,45,1], 'silver': [192,192,192,1],
  2495. 'skyblue': [135,206,235,1], 'slateblue': [106,90,205,1],
  2496. 'slategray': [112,128,144,1], 'slategrey': [112,128,144,1],
  2497. 'snow': [255,250,250,1], 'springgreen': [0,255,127,1],
  2498. 'steelblue': [70,130,180,1], 'tan': [210,180,140,1],
  2499. 'teal': [0,128,128,1], 'thistle': [216,191,216,1],
  2500. 'tomato': [255,99,71,1], 'turquoise': [64,224,208,1],
  2501. 'violet': [238,130,238,1], 'wheat': [245,222,179,1],
  2502. 'white': [255,255,255,1], 'whitesmoke': [245,245,245,1],
  2503. 'yellow': [255,255,0,1], 'yellowgreen': [154,205,50,1]
  2504. };
  2505. function clampCssByte(i) { // Clamp to integer 0 .. 255.
  2506. i = Math.round(i); // Seems to be what Chrome does (vs truncation).
  2507. return i < 0 ? 0 : i > 255 ? 255 : i;
  2508. }
  2509. function clampCssFloat(f) { // Clamp to float 0.0 .. 1.0.
  2510. return f < 0 ? 0 : f > 1 ? 1 : f;
  2511. }
  2512. function parseCssInt(str) { // int or percentage.
  2513. if (str.length && str.charAt(str.length - 1) === '%') {
  2514. return clampCssByte(parseFloat(str) / 100 * 255);
  2515. }
  2516. return clampCssByte(parseInt(str, 10));
  2517. }
  2518. function parseCssFloat(str) { // float or percentage.
  2519. if (str.length && str.charAt(str.length - 1) === '%') {
  2520. return clampCssFloat(parseFloat(str) / 100);
  2521. }
  2522. return clampCssFloat(parseFloat(str));
  2523. }
  2524. function cssHueToRgb(m1, m2, h) {
  2525. if (h < 0) {
  2526. h += 1;
  2527. }
  2528. else if (h > 1) {
  2529. h -= 1;
  2530. }
  2531. if (h * 6 < 1) {
  2532. return m1 + (m2 - m1) * h * 6;
  2533. }
  2534. if (h * 2 < 1) {
  2535. return m2;
  2536. }
  2537. if (h * 3 < 2) {
  2538. return m1 + (m2 - m1) * (2/3 - h) * 6;
  2539. }
  2540. return m1;
  2541. }
  2542. function setRgba(out, r, g, b, a) {
  2543. out[0] = r; out[1] = g; out[2] = b; out[3] = a;
  2544. return out;
  2545. }
  2546. function copyRgba(out, a) {
  2547. out[0] = a[0]; out[1] = a[1]; out[2] = a[2]; out[3] = a[3];
  2548. return out;
  2549. }
  2550. var colorCache = new LRU(20);
  2551. var lastRemovedArr = null;
  2552. function putToCache(colorStr, rgbaArr) {
  2553. // Reuse removed array
  2554. if (lastRemovedArr) {
  2555. copyRgba(lastRemovedArr, rgbaArr);
  2556. }
  2557. lastRemovedArr = colorCache.put(colorStr, lastRemovedArr || (rgbaArr.slice()));
  2558. }
  2559. /**
  2560. * @param {string} colorStr
  2561. * @param {Array.<number>} out
  2562. * @return {Array.<number>}
  2563. * @memberOf module:zrender/util/color
  2564. */
  2565. function parse(colorStr, rgbaArr) {
  2566. if (!colorStr) {
  2567. return;
  2568. }
  2569. rgbaArr = rgbaArr || [];
  2570. var cached = colorCache.get(colorStr);
  2571. if (cached) {
  2572. return copyRgba(rgbaArr, cached);
  2573. }
  2574. // colorStr may be not string
  2575. colorStr = colorStr + '';
  2576. // Remove all whitespace, not compliant, but should just be more accepting.
  2577. var str = colorStr.replace(/ /g, '').toLowerCase();
  2578. // Color keywords (and transparent) lookup.
  2579. if (str in kCSSColorTable) {
  2580. copyRgba(rgbaArr, kCSSColorTable[str]);
  2581. putToCache(colorStr, rgbaArr);
  2582. return rgbaArr;
  2583. }
  2584. // #abc and #abc123 syntax.
  2585. if (str.charAt(0) === '#') {
  2586. if (str.length === 4) {
  2587. var iv = parseInt(str.substr(1), 16); // TODO(deanm): Stricter parsing.
  2588. if (!(iv >= 0 && iv <= 0xfff)) {
  2589. setRgba(rgbaArr, 0, 0, 0, 1);
  2590. return; // Covers NaN.
  2591. }
  2592. setRgba(rgbaArr,
  2593. ((iv & 0xf00) >> 4) | ((iv & 0xf00) >> 8),
  2594. (iv & 0xf0) | ((iv & 0xf0) >> 4),
  2595. (iv & 0xf) | ((iv & 0xf) << 4),
  2596. 1
  2597. );
  2598. putToCache(colorStr, rgbaArr);
  2599. return rgbaArr;
  2600. }
  2601. else if (str.length === 7) {
  2602. var iv = parseInt(str.substr(1), 16); // TODO(deanm): Stricter parsing.
  2603. if (!(iv >= 0 && iv <= 0xffffff)) {
  2604. setRgba(rgbaArr, 0, 0, 0, 1);
  2605. return; // Covers NaN.
  2606. }
  2607. setRgba(rgbaArr,
  2608. (iv & 0xff0000) >> 16,
  2609. (iv & 0xff00) >> 8,
  2610. iv & 0xff,
  2611. 1
  2612. );
  2613. putToCache(colorStr, rgbaArr);
  2614. return rgbaArr;
  2615. }
  2616. return;
  2617. }
  2618. var op = str.indexOf('('), ep = str.indexOf(')');
  2619. if (op !== -1 && ep + 1 === str.length) {
  2620. var fname = str.substr(0, op);
  2621. var params = str.substr(op + 1, ep - (op + 1)).split(',');
  2622. var alpha = 1; // To allow case fallthrough.
  2623. switch (fname) {
  2624. case 'rgba':
  2625. if (params.length !== 4) {
  2626. setRgba(rgbaArr, 0, 0, 0, 1);
  2627. return;
  2628. }
  2629. alpha = parseCssFloat(params.pop()); // jshint ignore:line
  2630. // Fall through.
  2631. case 'rgb':
  2632. if (params.length !== 3) {
  2633. setRgba(rgbaArr, 0, 0, 0, 1);
  2634. return;
  2635. }
  2636. setRgba(rgbaArr,
  2637. parseCssInt(params[0]),
  2638. parseCssInt(params[1]),
  2639. parseCssInt(params[2]),
  2640. alpha
  2641. );
  2642. putToCache(colorStr, rgbaArr);
  2643. return rgbaArr;
  2644. case 'hsla':
  2645. if (params.length !== 4) {
  2646. setRgba(rgbaArr, 0, 0, 0, 1);
  2647. return;
  2648. }
  2649. params[3] = parseCssFloat(params[3]);
  2650. hsla2rgba(params, rgbaArr);
  2651. putToCache(colorStr, rgbaArr);
  2652. return rgbaArr;
  2653. case 'hsl':
  2654. if (params.length !== 3) {
  2655. setRgba(rgbaArr, 0, 0, 0, 1);
  2656. return;
  2657. }
  2658. hsla2rgba(params, rgbaArr);
  2659. putToCache(colorStr, rgbaArr);
  2660. return rgbaArr;
  2661. default:
  2662. return;
  2663. }
  2664. }
  2665. setRgba(rgbaArr, 0, 0, 0, 1);
  2666. return;
  2667. }
  2668. /**
  2669. * @param {Array.<number>} hsla
  2670. * @param {Array.<number>} rgba
  2671. * @return {Array.<number>} rgba
  2672. */
  2673. function hsla2rgba(hsla, rgba) {
  2674. var h = (((parseFloat(hsla[0]) % 360) + 360) % 360) / 360; // 0 .. 1
  2675. // NOTE(deanm): According to the CSS spec s/l should only be
  2676. // percentages, but we don't bother and let float or percentage.
  2677. var s = parseCssFloat(hsla[1]);
  2678. var l = parseCssFloat(hsla[2]);
  2679. var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s;
  2680. var m1 = l * 2 - m2;
  2681. rgba = rgba || [];
  2682. setRgba(rgba,
  2683. clampCssByte(cssHueToRgb(m1, m2, h + 1 / 3) * 255),
  2684. clampCssByte(cssHueToRgb(m1, m2, h) * 255),
  2685. clampCssByte(cssHueToRgb(m1, m2, h - 1 / 3) * 255),
  2686. 1
  2687. );
  2688. if (hsla.length === 4) {
  2689. rgba[3] = hsla[3];
  2690. }
  2691. return rgba;
  2692. }
  2693. /**
  2694. * @param {string} color
  2695. * @param {number} level
  2696. * @return {string}
  2697. * @memberOf module:zrender/util/color
  2698. */
  2699. function lift(color, level) {
  2700. var colorArr = parse(color);
  2701. if (colorArr) {
  2702. for (var i = 0; i < 3; i++) {
  2703. if (level < 0) {
  2704. colorArr[i] = colorArr[i] * (1 - level) | 0;
  2705. }
  2706. else {
  2707. colorArr[i] = ((255 - colorArr[i]) * level + colorArr[i]) | 0;
  2708. }
  2709. }
  2710. return stringify(colorArr, colorArr.length === 4 ? 'rgba' : 'rgb');
  2711. }
  2712. }
  2713. /**
  2714. * @param {string} color
  2715. * @return {string}
  2716. * @memberOf module:zrender/util/color
  2717. */
  2718. /**
  2719. * Map value to color. Faster than lerp methods because color is represented by rgba array.
  2720. * @param {number} normalizedValue A float between 0 and 1.
  2721. * @param {Array.<Array.<number>>} colors List of rgba color array
  2722. * @param {Array.<number>} [out] Mapped gba color array
  2723. * @return {Array.<number>} will be null/undefined if input illegal.
  2724. */
  2725. /**
  2726. * @deprecated
  2727. */
  2728. /**
  2729. * @param {number} normalizedValue A float between 0 and 1.
  2730. * @param {Array.<string>} colors Color list.
  2731. * @param {boolean=} fullOutput Default false.
  2732. * @return {(string|Object)} Result color. If fullOutput,
  2733. * return {color: ..., leftIndex: ..., rightIndex: ..., value: ...},
  2734. * @memberOf module:zrender/util/color
  2735. */
  2736. /**
  2737. * @deprecated
  2738. */
  2739. /**
  2740. * @param {string} color
  2741. * @param {number=} h 0 ~ 360, ignore when null.
  2742. * @param {number=} s 0 ~ 1, ignore when null.
  2743. * @param {number=} l 0 ~ 1, ignore when null.
  2744. * @return {string} Color string in rgba format.
  2745. * @memberOf module:zrender/util/color
  2746. */
  2747. /**
  2748. * @param {string} color
  2749. * @param {number=} alpha 0 ~ 1
  2750. * @return {string} Color string in rgba format.
  2751. * @memberOf module:zrender/util/color
  2752. */
  2753. /**
  2754. * @param {Array.<number>} arrColor like [12,33,44,0.4]
  2755. * @param {string} type 'rgba', 'hsva', ...
  2756. * @return {string} Result color. (If input illegal, return undefined).
  2757. */
  2758. function stringify(arrColor, type) {
  2759. if (!arrColor || !arrColor.length) {
  2760. return;
  2761. }
  2762. var colorStr = arrColor[0] + ',' + arrColor[1] + ',' + arrColor[2];
  2763. if (type === 'rgba' || type === 'hsva' || type === 'hsla') {
  2764. colorStr += ',' + arrColor[3];
  2765. }
  2766. return type + '(' + colorStr + ')';
  2767. }
  2768. /**
  2769. * @module echarts/animation/Animator
  2770. */
  2771. var arraySlice = Array.prototype.slice;
  2772. function defaultGetter(target, key) {
  2773. return target[key];
  2774. }
  2775. function defaultSetter(target, key, value) {
  2776. target[key] = value;
  2777. }
  2778. /**
  2779. * @param {number} p0
  2780. * @param {number} p1
  2781. * @param {number} percent
  2782. * @return {number}
  2783. */
  2784. function interpolateNumber(p0, p1, percent) {
  2785. return (p1 - p0) * percent + p0;
  2786. }
  2787. /**
  2788. * @param {string} p0
  2789. * @param {string} p1
  2790. * @param {number} percent
  2791. * @return {string}
  2792. */
  2793. function interpolateString(p0, p1, percent) {
  2794. return percent > 0.5 ? p1 : p0;
  2795. }
  2796. /**
  2797. * @param {Array} p0
  2798. * @param {Array} p1
  2799. * @param {number} percent
  2800. * @param {Array} out
  2801. * @param {number} arrDim
  2802. */
  2803. function interpolateArray(p0, p1, percent, out, arrDim) {
  2804. var len = p0.length;
  2805. if (arrDim == 1) {
  2806. for (var i = 0; i < len; i++) {
  2807. out[i] = interpolateNumber(p0[i], p1[i], percent);
  2808. }
  2809. }
  2810. else {
  2811. var len2 = len && p0[0].length;
  2812. for (var i = 0; i < len; i++) {
  2813. for (var j = 0; j < len2; j++) {
  2814. out[i][j] = interpolateNumber(
  2815. p0[i][j], p1[i][j], percent
  2816. );
  2817. }
  2818. }
  2819. }
  2820. }
  2821. // arr0 is source array, arr1 is target array.
  2822. // Do some preprocess to avoid error happened when interpolating from arr0 to arr1
  2823. function fillArr(arr0, arr1, arrDim) {
  2824. var arr0Len = arr0.length;
  2825. var arr1Len = arr1.length;
  2826. if (arr0Len !== arr1Len) {
  2827. // FIXME Not work for TypedArray
  2828. var isPreviousLarger = arr0Len > arr1Len;
  2829. if (isPreviousLarger) {
  2830. // Cut the previous
  2831. arr0.length = arr1Len;
  2832. }
  2833. else {
  2834. // Fill the previous
  2835. for (var i = arr0Len; i < arr1Len; i++) {
  2836. arr0.push(
  2837. arrDim === 1 ? arr1[i] : arraySlice.call(arr1[i])
  2838. );
  2839. }
  2840. }
  2841. }
  2842. // Handling NaN value
  2843. var len2 = arr0[0] && arr0[0].length;
  2844. for (var i = 0; i < arr0.length; i++) {
  2845. if (arrDim === 1) {
  2846. if (isNaN(arr0[i])) {
  2847. arr0[i] = arr1[i];
  2848. }
  2849. }
  2850. else {
  2851. for (var j = 0; j < len2; j++) {
  2852. if (isNaN(arr0[i][j])) {
  2853. arr0[i][j] = arr1[i][j];
  2854. }
  2855. }
  2856. }
  2857. }
  2858. }
  2859. /**
  2860. * @param {Array} arr0
  2861. * @param {Array} arr1
  2862. * @param {number} arrDim
  2863. * @return {boolean}
  2864. */
  2865. function isArraySame(arr0, arr1, arrDim) {
  2866. if (arr0 === arr1) {
  2867. return true;
  2868. }
  2869. var len = arr0.length;
  2870. if (len !== arr1.length) {
  2871. return false;
  2872. }
  2873. if (arrDim === 1) {
  2874. for (var i = 0; i < len; i++) {
  2875. if (arr0[i] !== arr1[i]) {
  2876. return false;
  2877. }
  2878. }
  2879. }
  2880. else {
  2881. var len2 = arr0[0].length;
  2882. for (var i = 0; i < len; i++) {
  2883. for (var j = 0; j < len2; j++) {
  2884. if (arr0[i][j] !== arr1[i][j]) {
  2885. return false;
  2886. }
  2887. }
  2888. }
  2889. }
  2890. return true;
  2891. }
  2892. /**
  2893. * Catmull Rom interpolate array
  2894. * @param {Array} p0
  2895. * @param {Array} p1
  2896. * @param {Array} p2
  2897. * @param {Array} p3
  2898. * @param {number} t
  2899. * @param {number} t2
  2900. * @param {number} t3
  2901. * @param {Array} out
  2902. * @param {number} arrDim
  2903. */
  2904. function catmullRomInterpolateArray(
  2905. p0, p1, p2, p3, t, t2, t3, out, arrDim
  2906. ) {
  2907. var len = p0.length;
  2908. if (arrDim == 1) {
  2909. for (var i = 0; i < len; i++) {
  2910. out[i] = catmullRomInterpolate(
  2911. p0[i], p1[i], p2[i], p3[i], t, t2, t3
  2912. );
  2913. }
  2914. }
  2915. else {
  2916. var len2 = p0[0].length;
  2917. for (var i = 0; i < len; i++) {
  2918. for (var j = 0; j < len2; j++) {
  2919. out[i][j] = catmullRomInterpolate(
  2920. p0[i][j], p1[i][j], p2[i][j], p3[i][j],
  2921. t, t2, t3
  2922. );
  2923. }
  2924. }
  2925. }
  2926. }
  2927. /**
  2928. * Catmull Rom interpolate number
  2929. * @param {number} p0
  2930. * @param {number} p1
  2931. * @param {number} p2
  2932. * @param {number} p3
  2933. * @param {number} t
  2934. * @param {number} t2
  2935. * @param {number} t3
  2936. * @return {number}
  2937. */
  2938. function catmullRomInterpolate(p0, p1, p2, p3, t, t2, t3) {
  2939. var v0 = (p2 - p0) * 0.5;
  2940. var v1 = (p3 - p1) * 0.5;
  2941. return (2 * (p1 - p2) + v0 + v1) * t3
  2942. + (-3 * (p1 - p2) - 2 * v0 - v1) * t2
  2943. + v0 * t + p1;
  2944. }
  2945. function cloneValue(value) {
  2946. if (isArrayLike(value)) {
  2947. var len = value.length;
  2948. if (isArrayLike(value[0])) {
  2949. var ret = [];
  2950. for (var i = 0; i < len; i++) {
  2951. ret.push(arraySlice.call(value[i]));
  2952. }
  2953. return ret;
  2954. }
  2955. return arraySlice.call(value);
  2956. }
  2957. return value;
  2958. }
  2959. function rgba2String(rgba) {
  2960. rgba[0] = Math.floor(rgba[0]);
  2961. rgba[1] = Math.floor(rgba[1]);
  2962. rgba[2] = Math.floor(rgba[2]);
  2963. return 'rgba(' + rgba.join(',') + ')';
  2964. }
  2965. function getArrayDim(keyframes) {
  2966. var lastValue = keyframes[keyframes.length - 1].value;
  2967. return isArrayLike(lastValue && lastValue[0]) ? 2 : 1;
  2968. }
  2969. function createTrackClip(animator, easing, oneTrackDone, keyframes, propName, forceAnimate) {
  2970. var getter = animator._getter;
  2971. var setter = animator._setter;
  2972. var useSpline = easing === 'spline';
  2973. var trackLen = keyframes.length;
  2974. if (!trackLen) {
  2975. return;
  2976. }
  2977. // Guess data type
  2978. var firstVal = keyframes[0].value;
  2979. var isValueArray = isArrayLike(firstVal);
  2980. var isValueColor = false;
  2981. var isValueString = false;
  2982. // For vertices morphing
  2983. var arrDim = isValueArray ? getArrayDim(keyframes) : 0;
  2984. var trackMaxTime;
  2985. // Sort keyframe as ascending
  2986. keyframes.sort(function(a, b) {
  2987. return a.time - b.time;
  2988. });
  2989. trackMaxTime = keyframes[trackLen - 1].time;
  2990. // Percents of each keyframe
  2991. var kfPercents = [];
  2992. // Value of each keyframe
  2993. var kfValues = [];
  2994. var prevValue = keyframes[0].value;
  2995. var isAllValueEqual = true;
  2996. for (var i = 0; i < trackLen; i++) {
  2997. kfPercents.push(keyframes[i].time / trackMaxTime);
  2998. // Assume value is a color when it is a string
  2999. var value = keyframes[i].value;
  3000. // Check if value is equal, deep check if value is array
  3001. if (!((isValueArray && isArraySame(value, prevValue, arrDim))
  3002. || (!isValueArray && value === prevValue))) {
  3003. isAllValueEqual = false;
  3004. }
  3005. prevValue = value;
  3006. // Try converting a string to a color array
  3007. if (typeof value == 'string') {
  3008. var colorArray = parse(value);
  3009. if (colorArray) {
  3010. value = colorArray;
  3011. isValueColor = true;
  3012. }
  3013. else {
  3014. isValueString = true;
  3015. }
  3016. }
  3017. kfValues.push(value);
  3018. }
  3019. if (!forceAnimate && isAllValueEqual) {
  3020. return;
  3021. }
  3022. var lastValue = kfValues[trackLen - 1];
  3023. // Polyfill array and NaN value
  3024. for (var i = 0; i < trackLen - 1; i++) {
  3025. if (isValueArray) {
  3026. fillArr(kfValues[i], lastValue, arrDim);
  3027. }
  3028. else {
  3029. if (isNaN(kfValues[i]) && !isNaN(lastValue) && !isValueString && !isValueColor) {
  3030. kfValues[i] = lastValue;
  3031. }
  3032. }
  3033. }
  3034. isValueArray && fillArr(getter(animator._target, propName), lastValue, arrDim);
  3035. // Cache the key of last frame to speed up when
  3036. // animation playback is sequency
  3037. var lastFrame = 0;
  3038. var lastFramePercent = 0;
  3039. var start;
  3040. var w;
  3041. var p0;
  3042. var p1;
  3043. var p2;
  3044. var p3;
  3045. if (isValueColor) {
  3046. var rgba = [0, 0, 0, 0];
  3047. }
  3048. var onframe = function (target, percent) {
  3049. // Find the range keyframes
  3050. // kf1-----kf2---------current--------kf3
  3051. // find kf2 and kf3 and do interpolation
  3052. var frame;
  3053. // In the easing function like elasticOut, percent may less than 0
  3054. if (percent < 0) {
  3055. frame = 0;
  3056. }
  3057. else if (percent < lastFramePercent) {
  3058. // Start from next key
  3059. // PENDING start from lastFrame ?
  3060. start = Math.min(lastFrame + 1, trackLen - 1);
  3061. for (frame = start; frame >= 0; frame--) {
  3062. if (kfPercents[frame] <= percent) {
  3063. break;
  3064. }
  3065. }
  3066. // PENDING really need to do this ?
  3067. frame = Math.min(frame, trackLen - 2);
  3068. }
  3069. else {
  3070. for (frame = lastFrame; frame < trackLen; frame++) {
  3071. if (kfPercents[frame] > percent) {
  3072. break;
  3073. }
  3074. }
  3075. frame = Math.min(frame - 1, trackLen - 2);
  3076. }
  3077. lastFrame = frame;
  3078. lastFramePercent = percent;
  3079. var range = (kfPercents[frame + 1] - kfPercents[frame]);
  3080. if (range === 0) {
  3081. return;
  3082. }
  3083. else {
  3084. w = (percent - kfPercents[frame]) / range;
  3085. }
  3086. if (useSpline) {
  3087. p1 = kfValues[frame];
  3088. p0 = kfValues[frame === 0 ? frame : frame - 1];
  3089. p2 = kfValues[frame > trackLen - 2 ? trackLen - 1 : frame + 1];
  3090. p3 = kfValues[frame > trackLen - 3 ? trackLen - 1 : frame + 2];
  3091. if (isValueArray) {
  3092. catmullRomInterpolateArray(
  3093. p0, p1, p2, p3, w, w * w, w * w * w,
  3094. getter(target, propName),
  3095. arrDim
  3096. );
  3097. }
  3098. else {
  3099. var value;
  3100. if (isValueColor) {
  3101. value = catmullRomInterpolateArray(
  3102. p0, p1, p2, p3, w, w * w, w * w * w,
  3103. rgba, 1
  3104. );
  3105. value = rgba2String(rgba);
  3106. }
  3107. else if (isValueString) {
  3108. // String is step(0.5)
  3109. return interpolateString(p1, p2, w);
  3110. }
  3111. else {
  3112. value = catmullRomInterpolate(
  3113. p0, p1, p2, p3, w, w * w, w * w * w
  3114. );
  3115. }
  3116. setter(
  3117. target,
  3118. propName,
  3119. value
  3120. );
  3121. }
  3122. }
  3123. else {
  3124. if (isValueArray) {
  3125. interpolateArray(
  3126. kfValues[frame], kfValues[frame + 1], w,
  3127. getter(target, propName),
  3128. arrDim
  3129. );
  3130. }
  3131. else {
  3132. var value;
  3133. if (isValueColor) {
  3134. interpolateArray(
  3135. kfValues[frame], kfValues[frame + 1], w,
  3136. rgba, 1
  3137. );
  3138. value = rgba2String(rgba);
  3139. }
  3140. else if (isValueString) {
  3141. // String is step(0.5)
  3142. return interpolateString(kfValues[frame], kfValues[frame + 1], w);
  3143. }
  3144. else {
  3145. value = interpolateNumber(kfValues[frame], kfValues[frame + 1], w);
  3146. }
  3147. setter(
  3148. target,
  3149. propName,
  3150. value
  3151. );
  3152. }
  3153. }
  3154. };
  3155. var clip = new Clip({
  3156. target: animator._target,
  3157. life: trackMaxTime,
  3158. loop: animator._loop,
  3159. delay: animator._delay,
  3160. onframe: onframe,
  3161. ondestroy: oneTrackDone
  3162. });
  3163. if (easing && easing !== 'spline') {
  3164. clip.easing = easing;
  3165. }
  3166. return clip;
  3167. }
  3168. /**
  3169. * @alias module:zrender/animation/Animator
  3170. * @constructor
  3171. * @param {Object} target
  3172. * @param {boolean} loop
  3173. * @param {Function} getter
  3174. * @param {Function} setter
  3175. */
  3176. var Animator = function(target, loop, getter, setter) {
  3177. this._tracks = {};
  3178. this._target = target;
  3179. this._loop = loop || false;
  3180. this._getter = getter || defaultGetter;
  3181. this._setter = setter || defaultSetter;
  3182. this._clipCount = 0;
  3183. this._delay = 0;
  3184. this._doneList = [];
  3185. this._onframeList = [];
  3186. this._clipList = [];
  3187. };
  3188. Animator.prototype = {
  3189. /**
  3190. * 设置动画关键帧
  3191. * @param {number} time 关键帧时间,单位是ms
  3192. * @param {Object} props 关键帧的属性值,key-value表示
  3193. * @return {module:zrender/animation/Animator}
  3194. */
  3195. when: function(time /* ms */, props) {
  3196. var tracks = this._tracks;
  3197. for (var propName in props) {
  3198. if (!props.hasOwnProperty(propName)) {
  3199. continue;
  3200. }
  3201. if (!tracks[propName]) {
  3202. tracks[propName] = [];
  3203. // Invalid value
  3204. var value = this._getter(this._target, propName);
  3205. if (value == null) {
  3206. // zrLog('Invalid property ' + propName);
  3207. continue;
  3208. }
  3209. // If time is 0
  3210. // Then props is given initialize value
  3211. // Else
  3212. // Initialize value from current prop value
  3213. if (time !== 0) {
  3214. tracks[propName].push({
  3215. time: 0,
  3216. value: cloneValue(value)
  3217. });
  3218. }
  3219. }
  3220. tracks[propName].push({
  3221. time: time,
  3222. value: props[propName]
  3223. });
  3224. }
  3225. return this;
  3226. },
  3227. /**
  3228. * 添加动画每一帧的回调函数
  3229. * @param {Function} callback
  3230. * @return {module:zrender/animation/Animator}
  3231. */
  3232. during: function (callback) {
  3233. this._onframeList.push(callback);
  3234. return this;
  3235. },
  3236. pause: function () {
  3237. for (var i = 0; i < this._clipList.length; i++) {
  3238. this._clipList[i].pause();
  3239. }
  3240. this._paused = true;
  3241. },
  3242. resume: function () {
  3243. for (var i = 0; i < this._clipList.length; i++) {
  3244. this._clipList[i].resume();
  3245. }
  3246. this._paused = false;
  3247. },
  3248. isPaused: function () {
  3249. return !!this._paused;
  3250. },
  3251. _doneCallback: function () {
  3252. // Clear all tracks
  3253. this._tracks = {};
  3254. // Clear all clips
  3255. this._clipList.length = 0;
  3256. var doneList = this._doneList;
  3257. var len = doneList.length;
  3258. for (var i = 0; i < len; i++) {
  3259. doneList[i].call(this);
  3260. }
  3261. },
  3262. /**
  3263. * 开始执行动画
  3264. * @param {string|Function} [easing]
  3265. * 动画缓动函数,详见{@link module:zrender/animation/easing}
  3266. * @param {boolean} forceAnimate
  3267. * @return {module:zrender/animation/Animator}
  3268. */
  3269. start: function (easing, forceAnimate) {
  3270. var self = this;
  3271. var clipCount = 0;
  3272. var oneTrackDone = function() {
  3273. clipCount--;
  3274. if (!clipCount) {
  3275. self._doneCallback();
  3276. }
  3277. };
  3278. var lastClip;
  3279. for (var propName in this._tracks) {
  3280. if (!this._tracks.hasOwnProperty(propName)) {
  3281. continue;
  3282. }
  3283. var clip = createTrackClip(
  3284. this, easing, oneTrackDone,
  3285. this._tracks[propName], propName, forceAnimate
  3286. );
  3287. if (clip) {
  3288. this._clipList.push(clip);
  3289. clipCount++;
  3290. // If start after added to animation
  3291. if (this.animation) {
  3292. this.animation.addClip(clip);
  3293. }
  3294. lastClip = clip;
  3295. }
  3296. }
  3297. // Add during callback on the last clip
  3298. if (lastClip) {
  3299. var oldOnFrame = lastClip.onframe;
  3300. lastClip.onframe = function (target, percent) {
  3301. oldOnFrame(target, percent);
  3302. for (var i = 0; i < self._onframeList.length; i++) {
  3303. self._onframeList[i](target, percent);
  3304. }
  3305. };
  3306. }
  3307. // This optimization will help the case that in the upper application
  3308. // the view may be refreshed frequently, where animation will be
  3309. // called repeatly but nothing changed.
  3310. if (!clipCount) {
  3311. this._doneCallback();
  3312. }
  3313. return this;
  3314. },
  3315. /**
  3316. * 停止动画
  3317. * @param {boolean} forwardToLast If move to last frame before stop
  3318. */
  3319. stop: function (forwardToLast) {
  3320. var clipList = this._clipList;
  3321. var animation = this.animation;
  3322. for (var i = 0; i < clipList.length; i++) {
  3323. var clip = clipList[i];
  3324. if (forwardToLast) {
  3325. // Move to last frame before stop
  3326. clip.onframe(this._target, 1);
  3327. }
  3328. animation && animation.removeClip(clip);
  3329. }
  3330. clipList.length = 0;
  3331. },
  3332. /**
  3333. * 设置动画延迟开始的时间
  3334. * @param {number} time 单位ms
  3335. * @return {module:zrender/animation/Animator}
  3336. */
  3337. delay: function (time) {
  3338. this._delay = time;
  3339. return this;
  3340. },
  3341. /**
  3342. * 添加动画结束的回调
  3343. * @param {Function} cb
  3344. * @return {module:zrender/animation/Animator}
  3345. */
  3346. done: function(cb) {
  3347. if (cb) {
  3348. this._doneList.push(cb);
  3349. }
  3350. return this;
  3351. },
  3352. /**
  3353. * @return {Array.<module:zrender/animation/Clip>}
  3354. */
  3355. getClips: function () {
  3356. return this._clipList;
  3357. }
  3358. };
  3359. var dpr = 1;
  3360. // If in browser environment
  3361. if (typeof window !== 'undefined') {
  3362. dpr = Math.max(window.devicePixelRatio || 1, 1);
  3363. }
  3364. /**
  3365. * config默认配置项
  3366. * @exports zrender/config
  3367. * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
  3368. */
  3369. /**
  3370. * debug日志选项:catchBrushException为true下有效
  3371. * 0 : 不生成debug数据,发布用
  3372. * 1 : 异常抛出,调试用
  3373. * 2 : 控制台输出,调试用
  3374. */
  3375. var debugMode = 0;
  3376. // retina 屏幕优化
  3377. var devicePixelRatio = dpr;
  3378. var log = function () {
  3379. };
  3380. if (debugMode === 1) {
  3381. log = function () {
  3382. for (var k in arguments) {
  3383. throw new Error(arguments[k]);
  3384. }
  3385. };
  3386. }
  3387. else if (debugMode > 1) {
  3388. log = function () {
  3389. for (var k in arguments) {
  3390. console.log(arguments[k]);
  3391. }
  3392. };
  3393. }
  3394. var log$1 = log;
  3395. /**
  3396. * @alias modue:zrender/mixin/Animatable
  3397. * @constructor
  3398. */
  3399. var Animatable = function () {
  3400. /**
  3401. * @type {Array.<module:zrender/animation/Animator>}
  3402. * @readOnly
  3403. */
  3404. this.animators = [];
  3405. };
  3406. Animatable.prototype = {
  3407. constructor: Animatable,
  3408. /**
  3409. * 动画
  3410. *
  3411. * @param {string} path The path to fetch value from object, like 'a.b.c'.
  3412. * @param {boolean} [loop] Whether to loop animation.
  3413. * @return {module:zrender/animation/Animator}
  3414. * @example:
  3415. * el.animate('style', false)
  3416. * .when(1000, {x: 10} )
  3417. * .done(function(){ // Animation done })
  3418. * .start()
  3419. */
  3420. animate: function (path, loop) {
  3421. var target;
  3422. var animatingShape = false;
  3423. var el = this;
  3424. var zr = this.__zr;
  3425. if (path) {
  3426. var pathSplitted = path.split('.');
  3427. var prop = el;
  3428. // If animating shape
  3429. animatingShape = pathSplitted[0] === 'shape';
  3430. for (var i = 0, l = pathSplitted.length; i < l; i++) {
  3431. if (!prop) {
  3432. continue;
  3433. }
  3434. prop = prop[pathSplitted[i]];
  3435. }
  3436. if (prop) {
  3437. target = prop;
  3438. }
  3439. }
  3440. else {
  3441. target = el;
  3442. }
  3443. if (!target) {
  3444. log$1(
  3445. 'Property "'
  3446. + path
  3447. + '" is not existed in element '
  3448. + el.id
  3449. );
  3450. return;
  3451. }
  3452. var animators = el.animators;
  3453. var animator = new Animator(target, loop);
  3454. animator.during(function (target) {
  3455. el.dirty(animatingShape);
  3456. })
  3457. .done(function () {
  3458. // FIXME Animator will not be removed if use `Animator#stop` to stop animation
  3459. animators.splice(indexOf(animators, animator), 1);
  3460. });
  3461. animators.push(animator);
  3462. // If animate after added to the zrender
  3463. if (zr) {
  3464. zr.animation.addAnimator(animator);
  3465. }
  3466. return animator;
  3467. },
  3468. /**
  3469. * 停止动画
  3470. * @param {boolean} forwardToLast If move to last frame before stop
  3471. */
  3472. stopAnimation: function (forwardToLast) {
  3473. var animators = this.animators;
  3474. var len = animators.length;
  3475. for (var i = 0; i < len; i++) {
  3476. animators[i].stop(forwardToLast);
  3477. }
  3478. animators.length = 0;
  3479. return this;
  3480. },
  3481. /**
  3482. * Caution: this method will stop previous animation.
  3483. * So do not use this method to one element twice before
  3484. * animation starts, unless you know what you are doing.
  3485. * @param {Object} target
  3486. * @param {number} [time=500] Time in ms
  3487. * @param {string} [easing='linear']
  3488. * @param {number} [delay=0]
  3489. * @param {Function} [callback]
  3490. * @param {Function} [forceAnimate] Prevent stop animation and callback
  3491. * immediently when target values are the same as current values.
  3492. *
  3493. * @example
  3494. * // Animate position
  3495. * el.animateTo({
  3496. * position: [10, 10]
  3497. * }, function () { // done })
  3498. *
  3499. * // Animate shape, style and position in 100ms, delayed 100ms, with cubicOut easing
  3500. * el.animateTo({
  3501. * shape: {
  3502. * width: 500
  3503. * },
  3504. * style: {
  3505. * fill: 'red'
  3506. * }
  3507. * position: [10, 10]
  3508. * }, 100, 100, 'cubicOut', function () { // done })
  3509. */
  3510. // TODO Return animation key
  3511. animateTo: function (target, time, delay, easing, callback, forceAnimate) {
  3512. // animateTo(target, time, easing, callback);
  3513. if (isString(delay)) {
  3514. callback = easing;
  3515. easing = delay;
  3516. delay = 0;
  3517. }
  3518. // animateTo(target, time, delay, callback);
  3519. else if (isFunction(easing)) {
  3520. callback = easing;
  3521. easing = 'linear';
  3522. delay = 0;
  3523. }
  3524. // animateTo(target, time, callback);
  3525. else if (isFunction(delay)) {
  3526. callback = delay;
  3527. delay = 0;
  3528. }
  3529. // animateTo(target, callback)
  3530. else if (isFunction(time)) {
  3531. callback = time;
  3532. time = 500;
  3533. }
  3534. // animateTo(target)
  3535. else if (!time) {
  3536. time = 500;
  3537. }
  3538. // Stop all previous animations
  3539. this.stopAnimation();
  3540. this._animateToShallow('', this, target, time, delay);
  3541. // Animators may be removed immediately after start
  3542. // if there is nothing to animate
  3543. var animators = this.animators.slice();
  3544. var count = animators.length;
  3545. function done() {
  3546. count--;
  3547. if (!count) {
  3548. callback && callback();
  3549. }
  3550. }
  3551. // No animators. This should be checked before animators[i].start(),
  3552. // because 'done' may be executed immediately if no need to animate.
  3553. if (!count) {
  3554. callback && callback();
  3555. }
  3556. // Start after all animators created
  3557. // Incase any animator is done immediately when all animation properties are not changed
  3558. for (var i = 0; i < animators.length; i++) {
  3559. animators[i]
  3560. .done(done)
  3561. .start(easing, forceAnimate);
  3562. }
  3563. },
  3564. /**
  3565. * @private
  3566. * @param {string} path=''
  3567. * @param {Object} source=this
  3568. * @param {Object} target
  3569. * @param {number} [time=500]
  3570. * @param {number} [delay=0]
  3571. *
  3572. * @example
  3573. * // Animate position
  3574. * el._animateToShallow({
  3575. * position: [10, 10]
  3576. * })
  3577. *
  3578. * // Animate shape, style and position in 100ms, delayed 100ms
  3579. * el._animateToShallow({
  3580. * shape: {
  3581. * width: 500
  3582. * },
  3583. * style: {
  3584. * fill: 'red'
  3585. * }
  3586. * position: [10, 10]
  3587. * }, 100, 100)
  3588. */
  3589. _animateToShallow: function (path, source, target, time, delay) {
  3590. var objShallow = {};
  3591. var propertyCount = 0;
  3592. for (var name in target) {
  3593. if (!target.hasOwnProperty(name)) {
  3594. continue;
  3595. }
  3596. if (source[name] != null) {
  3597. if (isObject(target[name]) && !isArrayLike(target[name])) {
  3598. this._animateToShallow(
  3599. path ? path + '.' + name : name,
  3600. source[name],
  3601. target[name],
  3602. time,
  3603. delay
  3604. );
  3605. }
  3606. else {
  3607. objShallow[name] = target[name];
  3608. propertyCount++;
  3609. }
  3610. }
  3611. else if (target[name] != null) {
  3612. // Attr directly if not has property
  3613. // FIXME, if some property not needed for element ?
  3614. if (!path) {
  3615. this.attr(name, target[name]);
  3616. }
  3617. else { // Shape or style
  3618. var props = {};
  3619. props[path] = {};
  3620. props[path][name] = target[name];
  3621. this.attr(props);
  3622. }
  3623. }
  3624. }
  3625. if (propertyCount > 0) {
  3626. this.animate(path, false)
  3627. .when(time == null ? 500 : time, objShallow)
  3628. .delay(delay || 0);
  3629. }
  3630. return this;
  3631. }
  3632. };
  3633. /**
  3634. * @alias module:zrender/Element
  3635. * @constructor
  3636. * @extends {module:zrender/mixin/Animatable}
  3637. * @extends {module:zrender/mixin/Transformable}
  3638. * @extends {module:zrender/mixin/Eventful}
  3639. */
  3640. var Element = function (opts) { // jshint ignore:line
  3641. Transformable.call(this, opts);
  3642. Eventful.call(this, opts);
  3643. Animatable.call(this, opts);
  3644. /**
  3645. * 画布元素ID
  3646. * @type {string}
  3647. */
  3648. this.id = opts.id || guid();
  3649. };
  3650. Element.prototype = {
  3651. /**
  3652. * 元素类型
  3653. * Element type
  3654. * @type {string}
  3655. */
  3656. type: 'element',
  3657. /**
  3658. * 元素名字
  3659. * Element name
  3660. * @type {string}
  3661. */
  3662. name: '',
  3663. /**
  3664. * ZRender 实例对象,会在 element 添加到 zrender 实例中后自动赋值
  3665. * ZRender instance will be assigned when element is associated with zrender
  3666. * @name module:/zrender/Element#__zr
  3667. * @type {module:zrender/ZRender}
  3668. */
  3669. __zr: null,
  3670. /**
  3671. * 图形是否忽略,为true时忽略图形的绘制以及事件触发
  3672. * If ignore drawing and events of the element object
  3673. * @name module:/zrender/Element#ignore
  3674. * @type {boolean}
  3675. * @default false
  3676. */
  3677. ignore: false,
  3678. /**
  3679. * 用于裁剪的路径(shape),所有 Group 内的路径在绘制时都会被这个路径裁剪
  3680. * 该路径会继承被裁减对象的变换
  3681. * @type {module:zrender/graphic/Path}
  3682. * @see http://www.w3.org/TR/2dcontext/#clipping-region
  3683. * @readOnly
  3684. */
  3685. clipPath: null,
  3686. /**
  3687. * Drift element
  3688. * @param {number} dx dx on the global space
  3689. * @param {number} dy dy on the global space
  3690. */
  3691. drift: function (dx, dy) {
  3692. switch (this.draggable) {
  3693. case 'horizontal':
  3694. dy = 0;
  3695. break;
  3696. case 'vertical':
  3697. dx = 0;
  3698. break;
  3699. }
  3700. var m = this.transform;
  3701. if (!m) {
  3702. m = this.transform = [1, 0, 0, 1, 0, 0];
  3703. }
  3704. m[4] += dx;
  3705. m[5] += dy;
  3706. this.decomposeTransform();
  3707. this.dirty(false);
  3708. },
  3709. /**
  3710. * Hook before update
  3711. */
  3712. beforeUpdate: function () {},
  3713. /**
  3714. * Hook after update
  3715. */
  3716. afterUpdate: function () {},
  3717. /**
  3718. * Update each frame
  3719. */
  3720. update: function () {
  3721. this.updateTransform();
  3722. },
  3723. /**
  3724. * @param {Function} cb
  3725. * @param {} context
  3726. */
  3727. traverse: function (cb, context) {},
  3728. /**
  3729. * @protected
  3730. */
  3731. attrKV: function (key, value) {
  3732. if (key === 'position' || key === 'scale' || key === 'origin') {
  3733. // Copy the array
  3734. if (value) {
  3735. var target = this[key];
  3736. if (!target) {
  3737. target = this[key] = [];
  3738. }
  3739. target[0] = value[0];
  3740. target[1] = value[1];
  3741. }
  3742. }
  3743. else {
  3744. this[key] = value;
  3745. }
  3746. },
  3747. /**
  3748. * Hide the element
  3749. */
  3750. hide: function () {
  3751. this.ignore = true;
  3752. this.__zr && this.__zr.refresh();
  3753. },
  3754. /**
  3755. * Show the element
  3756. */
  3757. show: function () {
  3758. this.ignore = false;
  3759. this.__zr && this.__zr.refresh();
  3760. },
  3761. /**
  3762. * @param {string|Object} key
  3763. * @param {*} value
  3764. */
  3765. attr: function (key, value) {
  3766. if (typeof key === 'string') {
  3767. this.attrKV(key, value);
  3768. }
  3769. else if (isObject(key)) {
  3770. for (var name in key) {
  3771. if (key.hasOwnProperty(name)) {
  3772. this.attrKV(name, key[name]);
  3773. }
  3774. }
  3775. }
  3776. this.dirty(false);
  3777. return this;
  3778. },
  3779. /**
  3780. * @param {module:zrender/graphic/Path} clipPath
  3781. */
  3782. setClipPath: function (clipPath) {
  3783. var zr = this.__zr;
  3784. if (zr) {
  3785. clipPath.addSelfToZr(zr);
  3786. }
  3787. // Remove previous clip path
  3788. if (this.clipPath && this.clipPath !== clipPath) {
  3789. this.removeClipPath();
  3790. }
  3791. this.clipPath = clipPath;
  3792. clipPath.__zr = zr;
  3793. clipPath.__clipTarget = this;
  3794. this.dirty(false);
  3795. },
  3796. /**
  3797. */
  3798. removeClipPath: function () {
  3799. var clipPath = this.clipPath;
  3800. if (clipPath) {
  3801. if (clipPath.__zr) {
  3802. clipPath.removeSelfFromZr(clipPath.__zr);
  3803. }
  3804. clipPath.__zr = null;
  3805. clipPath.__clipTarget = null;
  3806. this.clipPath = null;
  3807. this.dirty(false);
  3808. }
  3809. },
  3810. /**
  3811. * Add self from zrender instance.
  3812. * Not recursively because it will be invoked when element added to storage.
  3813. * @param {module:zrender/ZRender} zr
  3814. */
  3815. addSelfToZr: function (zr) {
  3816. this.__zr = zr;
  3817. // 添加动画
  3818. var animators = this.animators;
  3819. if (animators) {
  3820. for (var i = 0; i < animators.length; i++) {
  3821. zr.animation.addAnimator(animators[i]);
  3822. }
  3823. }
  3824. if (this.clipPath) {
  3825. this.clipPath.addSelfToZr(zr);
  3826. }
  3827. },
  3828. /**
  3829. * Remove self from zrender instance.
  3830. * Not recursively because it will be invoked when element added to storage.
  3831. * @param {module:zrender/ZRender} zr
  3832. */
  3833. removeSelfFromZr: function (zr) {
  3834. this.__zr = null;
  3835. // 移除动画
  3836. var animators = this.animators;
  3837. if (animators) {
  3838. for (var i = 0; i < animators.length; i++) {
  3839. zr.animation.removeAnimator(animators[i]);
  3840. }
  3841. }
  3842. if (this.clipPath) {
  3843. this.clipPath.removeSelfFromZr(zr);
  3844. }
  3845. }
  3846. };
  3847. mixin(Element, Animatable);
  3848. mixin(Element, Transformable);
  3849. mixin(Element, Eventful);
  3850. /**
  3851. * @module echarts/core/BoundingRect
  3852. */
  3853. var v2ApplyTransform = applyTransform;
  3854. var mathMin = Math.min;
  3855. var mathMax = Math.max;
  3856. /**
  3857. * @alias module:echarts/core/BoundingRect
  3858. */
  3859. function BoundingRect(x, y, width, height) {
  3860. if (width < 0) {
  3861. x = x + width;
  3862. width = -width;
  3863. }
  3864. if (height < 0) {
  3865. y = y + height;
  3866. height = -height;
  3867. }
  3868. /**
  3869. * @type {number}
  3870. */
  3871. this.x = x;
  3872. /**
  3873. * @type {number}
  3874. */
  3875. this.y = y;
  3876. /**
  3877. * @type {number}
  3878. */
  3879. this.width = width;
  3880. /**
  3881. * @type {number}
  3882. */
  3883. this.height = height;
  3884. }
  3885. BoundingRect.prototype = {
  3886. constructor: BoundingRect,
  3887. /**
  3888. * @param {module:echarts/core/BoundingRect} other
  3889. */
  3890. union: function (other) {
  3891. var x = mathMin(other.x, this.x);
  3892. var y = mathMin(other.y, this.y);
  3893. this.width = mathMax(
  3894. other.x + other.width,
  3895. this.x + this.width
  3896. ) - x;
  3897. this.height = mathMax(
  3898. other.y + other.height,
  3899. this.y + this.height
  3900. ) - y;
  3901. this.x = x;
  3902. this.y = y;
  3903. },
  3904. /**
  3905. * @param {Array.<number>} m
  3906. * @methods
  3907. */
  3908. applyTransform: (function () {
  3909. var lt = [];
  3910. var rb = [];
  3911. var lb = [];
  3912. var rt = [];
  3913. return function (m) {
  3914. // In case usage like this
  3915. // el.getBoundingRect().applyTransform(el.transform)
  3916. // And element has no transform
  3917. if (!m) {
  3918. return;
  3919. }
  3920. lt[0] = lb[0] = this.x;
  3921. lt[1] = rt[1] = this.y;
  3922. rb[0] = rt[0] = this.x + this.width;
  3923. rb[1] = lb[1] = this.y + this.height;
  3924. v2ApplyTransform(lt, lt, m);
  3925. v2ApplyTransform(rb, rb, m);
  3926. v2ApplyTransform(lb, lb, m);
  3927. v2ApplyTransform(rt, rt, m);
  3928. this.x = mathMin(lt[0], rb[0], lb[0], rt[0]);
  3929. this.y = mathMin(lt[1], rb[1], lb[1], rt[1]);
  3930. var maxX = mathMax(lt[0], rb[0], lb[0], rt[0]);
  3931. var maxY = mathMax(lt[1], rb[1], lb[1], rt[1]);
  3932. this.width = maxX - this.x;
  3933. this.height = maxY - this.y;
  3934. };
  3935. })(),
  3936. /**
  3937. * Calculate matrix of transforming from self to target rect
  3938. * @param {module:zrender/core/BoundingRect} b
  3939. * @return {Array.<number>}
  3940. */
  3941. calculateTransform: function (b) {
  3942. var a = this;
  3943. var sx = b.width / a.width;
  3944. var sy = b.height / a.height;
  3945. var m = create$1();
  3946. // 矩阵右乘
  3947. translate(m, m, [-a.x, -a.y]);
  3948. scale$1(m, m, [sx, sy]);
  3949. translate(m, m, [b.x, b.y]);
  3950. return m;
  3951. },
  3952. /**
  3953. * @param {(module:echarts/core/BoundingRect|Object)} b
  3954. * @return {boolean}
  3955. */
  3956. intersect: function (b) {
  3957. if (!b) {
  3958. return false;
  3959. }
  3960. if (!(b instanceof BoundingRect)) {
  3961. // Normalize negative width/height.
  3962. b = BoundingRect.create(b);
  3963. }
  3964. var a = this;
  3965. var ax0 = a.x;
  3966. var ax1 = a.x + a.width;
  3967. var ay0 = a.y;
  3968. var ay1 = a.y + a.height;
  3969. var bx0 = b.x;
  3970. var bx1 = b.x + b.width;
  3971. var by0 = b.y;
  3972. var by1 = b.y + b.height;
  3973. return ! (ax1 < bx0 || bx1 < ax0 || ay1 < by0 || by1 < ay0);
  3974. },
  3975. contain: function (x, y) {
  3976. var rect = this;
  3977. return x >= rect.x
  3978. && x <= (rect.x + rect.width)
  3979. && y >= rect.y
  3980. && y <= (rect.y + rect.height);
  3981. },
  3982. /**
  3983. * @return {module:echarts/core/BoundingRect}
  3984. */
  3985. clone: function () {
  3986. return new BoundingRect(this.x, this.y, this.width, this.height);
  3987. },
  3988. /**
  3989. * Copy from another rect
  3990. */
  3991. copy: function (other) {
  3992. this.x = other.x;
  3993. this.y = other.y;
  3994. this.width = other.width;
  3995. this.height = other.height;
  3996. },
  3997. plain: function () {
  3998. return {
  3999. x: this.x,
  4000. y: this.y,
  4001. width: this.width,
  4002. height: this.height
  4003. };
  4004. }
  4005. };
  4006. /**
  4007. * @param {Object|module:zrender/core/BoundingRect} rect
  4008. * @param {number} rect.x
  4009. * @param {number} rect.y
  4010. * @param {number} rect.width
  4011. * @param {number} rect.height
  4012. * @return {module:zrender/core/BoundingRect}
  4013. */
  4014. BoundingRect.create = function (rect) {
  4015. return new BoundingRect(rect.x, rect.y, rect.width, rect.height);
  4016. };
  4017. /**
  4018. * Group是一个容器,可以插入子节点,Group的变换也会被应用到子节点上
  4019. * @module zrender/graphic/Group
  4020. * @example
  4021. * var Group = require('zrender/container/Group');
  4022. * var Circle = require('zrender/graphic/shape/Circle');
  4023. * var g = new Group();
  4024. * g.position[0] = 100;
  4025. * g.position[1] = 100;
  4026. * g.add(new Circle({
  4027. * style: {
  4028. * x: 100,
  4029. * y: 100,
  4030. * r: 20,
  4031. * }
  4032. * }));
  4033. * zr.add(g);
  4034. */
  4035. /**
  4036. * @alias module:zrender/graphic/Group
  4037. * @constructor
  4038. * @extends module:zrender/mixin/Transformable
  4039. * @extends module:zrender/mixin/Eventful
  4040. */
  4041. var Group = function (opts) {
  4042. opts = opts || {};
  4043. Element.call(this, opts);
  4044. for (var key in opts) {
  4045. if (opts.hasOwnProperty(key)) {
  4046. this[key] = opts[key];
  4047. }
  4048. }
  4049. this._children = [];
  4050. this.__storage = null;
  4051. this.__dirty = true;
  4052. };
  4053. Group.prototype = {
  4054. constructor: Group,
  4055. isGroup: true,
  4056. /**
  4057. * @type {string}
  4058. */
  4059. type: 'group',
  4060. /**
  4061. * 所有子孙元素是否响应鼠标事件
  4062. * @name module:/zrender/container/Group#silent
  4063. * @type {boolean}
  4064. * @default false
  4065. */
  4066. silent: false,
  4067. /**
  4068. * @return {Array.<module:zrender/Element>}
  4069. */
  4070. children: function () {
  4071. return this._children.slice();
  4072. },
  4073. /**
  4074. * 获取指定 index 的儿子节点
  4075. * @param {number} idx
  4076. * @return {module:zrender/Element}
  4077. */
  4078. childAt: function (idx) {
  4079. return this._children[idx];
  4080. },
  4081. /**
  4082. * 获取指定名字的儿子节点
  4083. * @param {string} name
  4084. * @return {module:zrender/Element}
  4085. */
  4086. childOfName: function (name) {
  4087. var children = this._children;
  4088. for (var i = 0; i < children.length; i++) {
  4089. if (children[i].name === name) {
  4090. return children[i];
  4091. }
  4092. }
  4093. },
  4094. /**
  4095. * @return {number}
  4096. */
  4097. childCount: function () {
  4098. return this._children.length;
  4099. },
  4100. /**
  4101. * 添加子节点到最后
  4102. * @param {module:zrender/Element} child
  4103. */
  4104. add: function (child) {
  4105. if (child && child !== this && child.parent !== this) {
  4106. this._children.push(child);
  4107. this._doAdd(child);
  4108. }
  4109. return this;
  4110. },
  4111. /**
  4112. * 添加子节点在 nextSibling 之前
  4113. * @param {module:zrender/Element} child
  4114. * @param {module:zrender/Element} nextSibling
  4115. */
  4116. addBefore: function (child, nextSibling) {
  4117. if (child && child !== this && child.parent !== this
  4118. && nextSibling && nextSibling.parent === this) {
  4119. var children = this._children;
  4120. var idx = children.indexOf(nextSibling);
  4121. if (idx >= 0) {
  4122. children.splice(idx, 0, child);
  4123. this._doAdd(child);
  4124. }
  4125. }
  4126. return this;
  4127. },
  4128. _doAdd: function (child) {
  4129. if (child.parent) {
  4130. child.parent.remove(child);
  4131. }
  4132. child.parent = this;
  4133. var storage = this.__storage;
  4134. var zr = this.__zr;
  4135. if (storage && storage !== child.__storage) {
  4136. storage.addToStorage(child);
  4137. if (child instanceof Group) {
  4138. child.addChildrenToStorage(storage);
  4139. }
  4140. }
  4141. zr && zr.refresh();
  4142. },
  4143. /**
  4144. * 移除子节点
  4145. * @param {module:zrender/Element} child
  4146. */
  4147. remove: function (child) {
  4148. var zr = this.__zr;
  4149. var storage = this.__storage;
  4150. var children = this._children;
  4151. var idx = indexOf(children, child);
  4152. if (idx < 0) {
  4153. return this;
  4154. }
  4155. children.splice(idx, 1);
  4156. child.parent = null;
  4157. if (storage) {
  4158. storage.delFromStorage(child);
  4159. if (child instanceof Group) {
  4160. child.delChildrenFromStorage(storage);
  4161. }
  4162. }
  4163. zr && zr.refresh();
  4164. return this;
  4165. },
  4166. /**
  4167. * 移除所有子节点
  4168. */
  4169. removeAll: function () {
  4170. var children = this._children;
  4171. var storage = this.__storage;
  4172. var child;
  4173. var i;
  4174. for (i = 0; i < children.length; i++) {
  4175. child = children[i];
  4176. if (storage) {
  4177. storage.delFromStorage(child);
  4178. if (child instanceof Group) {
  4179. child.delChildrenFromStorage(storage);
  4180. }
  4181. }
  4182. child.parent = null;
  4183. }
  4184. children.length = 0;
  4185. return this;
  4186. },
  4187. /**
  4188. * 遍历所有子节点
  4189. * @param {Function} cb
  4190. * @param {} context
  4191. */
  4192. eachChild: function (cb, context) {
  4193. var children = this._children;
  4194. for (var i = 0; i < children.length; i++) {
  4195. var child = children[i];
  4196. cb.call(context, child, i);
  4197. }
  4198. return this;
  4199. },
  4200. /**
  4201. * 深度优先遍历所有子孙节点
  4202. * @param {Function} cb
  4203. * @param {} context
  4204. */
  4205. traverse: function (cb, context) {
  4206. for (var i = 0; i < this._children.length; i++) {
  4207. var child = this._children[i];
  4208. cb.call(context, child);
  4209. if (child.type === 'group') {
  4210. child.traverse(cb, context);
  4211. }
  4212. }
  4213. return this;
  4214. },
  4215. addChildrenToStorage: function (storage) {
  4216. for (var i = 0; i < this._children.length; i++) {
  4217. var child = this._children[i];
  4218. storage.addToStorage(child);
  4219. if (child instanceof Group) {
  4220. child.addChildrenToStorage(storage);
  4221. }
  4222. }
  4223. },
  4224. delChildrenFromStorage: function (storage) {
  4225. for (var i = 0; i < this._children.length; i++) {
  4226. var child = this._children[i];
  4227. storage.delFromStorage(child);
  4228. if (child instanceof Group) {
  4229. child.delChildrenFromStorage(storage);
  4230. }
  4231. }
  4232. },
  4233. dirty: function () {
  4234. this.__dirty = true;
  4235. this.__zr && this.__zr.refresh();
  4236. return this;
  4237. },
  4238. /**
  4239. * @return {module:zrender/core/BoundingRect}
  4240. */
  4241. getBoundingRect: function (includeChildren) {
  4242. // TODO Caching
  4243. var rect = null;
  4244. var tmpRect = new BoundingRect(0, 0, 0, 0);
  4245. var children = includeChildren || this._children;
  4246. var tmpMat = [];
  4247. for (var i = 0; i < children.length; i++) {
  4248. var child = children[i];
  4249. if (child.ignore || child.invisible) {
  4250. continue;
  4251. }
  4252. var childRect = child.getBoundingRect();
  4253. var transform = child.getLocalTransform(tmpMat);
  4254. // TODO
  4255. // The boundingRect cacluated by transforming original
  4256. // rect may be bigger than the actual bundingRect when rotation
  4257. // is used. (Consider a circle rotated aginst its center, where
  4258. // the actual boundingRect should be the same as that not be
  4259. // rotated.) But we can not find better approach to calculate
  4260. // actual boundingRect yet, considering performance.
  4261. if (transform) {
  4262. tmpRect.copy(childRect);
  4263. tmpRect.applyTransform(transform);
  4264. rect = rect || tmpRect.clone();
  4265. rect.union(tmpRect);
  4266. }
  4267. else {
  4268. rect = rect || childRect.clone();
  4269. rect.union(childRect);
  4270. }
  4271. }
  4272. return rect || tmpRect;
  4273. }
  4274. };
  4275. inherits(Group, Element);
  4276. // https://github.com/mziccard/node-timsort
  4277. var DEFAULT_MIN_MERGE = 32;
  4278. var DEFAULT_MIN_GALLOPING = 7;
  4279. function minRunLength(n) {
  4280. var r = 0;
  4281. while (n >= DEFAULT_MIN_MERGE) {
  4282. r |= n & 1;
  4283. n >>= 1;
  4284. }
  4285. return n + r;
  4286. }
  4287. function makeAscendingRun(array, lo, hi, compare) {
  4288. var runHi = lo + 1;
  4289. if (runHi === hi) {
  4290. return 1;
  4291. }
  4292. if (compare(array[runHi++], array[lo]) < 0) {
  4293. while (runHi < hi && compare(array[runHi], array[runHi - 1]) < 0) {
  4294. runHi++;
  4295. }
  4296. reverseRun(array, lo, runHi);
  4297. }
  4298. else {
  4299. while (runHi < hi && compare(array[runHi], array[runHi - 1]) >= 0) {
  4300. runHi++;
  4301. }
  4302. }
  4303. return runHi - lo;
  4304. }
  4305. function reverseRun(array, lo, hi) {
  4306. hi--;
  4307. while (lo < hi) {
  4308. var t = array[lo];
  4309. array[lo++] = array[hi];
  4310. array[hi--] = t;
  4311. }
  4312. }
  4313. function binaryInsertionSort(array, lo, hi, start, compare) {
  4314. if (start === lo) {
  4315. start++;
  4316. }
  4317. for (; start < hi; start++) {
  4318. var pivot = array[start];
  4319. var left = lo;
  4320. var right = start;
  4321. var mid;
  4322. while (left < right) {
  4323. mid = left + right >>> 1;
  4324. if (compare(pivot, array[mid]) < 0) {
  4325. right = mid;
  4326. }
  4327. else {
  4328. left = mid + 1;
  4329. }
  4330. }
  4331. var n = start - left;
  4332. switch (n) {
  4333. case 3:
  4334. array[left + 3] = array[left + 2];
  4335. case 2:
  4336. array[left + 2] = array[left + 1];
  4337. case 1:
  4338. array[left + 1] = array[left];
  4339. break;
  4340. default:
  4341. while (n > 0) {
  4342. array[left + n] = array[left + n - 1];
  4343. n--;
  4344. }
  4345. }
  4346. array[left] = pivot;
  4347. }
  4348. }
  4349. function gallopLeft(value, array, start, length, hint, compare) {
  4350. var lastOffset = 0;
  4351. var maxOffset = 0;
  4352. var offset = 1;
  4353. if (compare(value, array[start + hint]) > 0) {
  4354. maxOffset = length - hint;
  4355. while (offset < maxOffset && compare(value, array[start + hint + offset]) > 0) {
  4356. lastOffset = offset;
  4357. offset = (offset << 1) + 1;
  4358. if (offset <= 0) {
  4359. offset = maxOffset;
  4360. }
  4361. }
  4362. if (offset > maxOffset) {
  4363. offset = maxOffset;
  4364. }
  4365. lastOffset += hint;
  4366. offset += hint;
  4367. }
  4368. else {
  4369. maxOffset = hint + 1;
  4370. while (offset < maxOffset && compare(value, array[start + hint - offset]) <= 0) {
  4371. lastOffset = offset;
  4372. offset = (offset << 1) + 1;
  4373. if (offset <= 0) {
  4374. offset = maxOffset;
  4375. }
  4376. }
  4377. if (offset > maxOffset) {
  4378. offset = maxOffset;
  4379. }
  4380. var tmp = lastOffset;
  4381. lastOffset = hint - offset;
  4382. offset = hint - tmp;
  4383. }
  4384. lastOffset++;
  4385. while (lastOffset < offset) {
  4386. var m = lastOffset + (offset - lastOffset >>> 1);
  4387. if (compare(value, array[start + m]) > 0) {
  4388. lastOffset = m + 1;
  4389. }
  4390. else {
  4391. offset = m;
  4392. }
  4393. }
  4394. return offset;
  4395. }
  4396. function gallopRight(value, array, start, length, hint, compare) {
  4397. var lastOffset = 0;
  4398. var maxOffset = 0;
  4399. var offset = 1;
  4400. if (compare(value, array[start + hint]) < 0) {
  4401. maxOffset = hint + 1;
  4402. while (offset < maxOffset && compare(value, array[start + hint - offset]) < 0) {
  4403. lastOffset = offset;
  4404. offset = (offset << 1) + 1;
  4405. if (offset <= 0) {
  4406. offset = maxOffset;
  4407. }
  4408. }
  4409. if (offset > maxOffset) {
  4410. offset = maxOffset;
  4411. }
  4412. var tmp = lastOffset;
  4413. lastOffset = hint - offset;
  4414. offset = hint - tmp;
  4415. }
  4416. else {
  4417. maxOffset = length - hint;
  4418. while (offset < maxOffset && compare(value, array[start + hint + offset]) >= 0) {
  4419. lastOffset = offset;
  4420. offset = (offset << 1) + 1;
  4421. if (offset <= 0) {
  4422. offset = maxOffset;
  4423. }
  4424. }
  4425. if (offset > maxOffset) {
  4426. offset = maxOffset;
  4427. }
  4428. lastOffset += hint;
  4429. offset += hint;
  4430. }
  4431. lastOffset++;
  4432. while (lastOffset < offset) {
  4433. var m = lastOffset + (offset - lastOffset >>> 1);
  4434. if (compare(value, array[start + m]) < 0) {
  4435. offset = m;
  4436. }
  4437. else {
  4438. lastOffset = m + 1;
  4439. }
  4440. }
  4441. return offset;
  4442. }
  4443. function TimSort(array, compare) {
  4444. var minGallop = DEFAULT_MIN_GALLOPING;
  4445. var runStart;
  4446. var runLength;
  4447. var stackSize = 0;
  4448. var tmp = [];
  4449. runStart = [];
  4450. runLength = [];
  4451. function pushRun(_runStart, _runLength) {
  4452. runStart[stackSize] = _runStart;
  4453. runLength[stackSize] = _runLength;
  4454. stackSize += 1;
  4455. }
  4456. function mergeRuns() {
  4457. while (stackSize > 1) {
  4458. var n = stackSize - 2;
  4459. if (n >= 1 && runLength[n - 1] <= runLength[n] + runLength[n + 1] || n >= 2 && runLength[n - 2] <= runLength[n] + runLength[n - 1]) {
  4460. if (runLength[n - 1] < runLength[n + 1]) {
  4461. n--;
  4462. }
  4463. }
  4464. else if (runLength[n] > runLength[n + 1]) {
  4465. break;
  4466. }
  4467. mergeAt(n);
  4468. }
  4469. }
  4470. function forceMergeRuns() {
  4471. while (stackSize > 1) {
  4472. var n = stackSize - 2;
  4473. if (n > 0 && runLength[n - 1] < runLength[n + 1]) {
  4474. n--;
  4475. }
  4476. mergeAt(n);
  4477. }
  4478. }
  4479. function mergeAt(i) {
  4480. var start1 = runStart[i];
  4481. var length1 = runLength[i];
  4482. var start2 = runStart[i + 1];
  4483. var length2 = runLength[i + 1];
  4484. runLength[i] = length1 + length2;
  4485. if (i === stackSize - 3) {
  4486. runStart[i + 1] = runStart[i + 2];
  4487. runLength[i + 1] = runLength[i + 2];
  4488. }
  4489. stackSize--;
  4490. var k = gallopRight(array[start2], array, start1, length1, 0, compare);
  4491. start1 += k;
  4492. length1 -= k;
  4493. if (length1 === 0) {
  4494. return;
  4495. }
  4496. length2 = gallopLeft(array[start1 + length1 - 1], array, start2, length2, length2 - 1, compare);
  4497. if (length2 === 0) {
  4498. return;
  4499. }
  4500. if (length1 <= length2) {
  4501. mergeLow(start1, length1, start2, length2);
  4502. }
  4503. else {
  4504. mergeHigh(start1, length1, start2, length2);
  4505. }
  4506. }
  4507. function mergeLow(start1, length1, start2, length2) {
  4508. var i = 0;
  4509. for (i = 0; i < length1; i++) {
  4510. tmp[i] = array[start1 + i];
  4511. }
  4512. var cursor1 = 0;
  4513. var cursor2 = start2;
  4514. var dest = start1;
  4515. array[dest++] = array[cursor2++];
  4516. if (--length2 === 0) {
  4517. for (i = 0; i < length1; i++) {
  4518. array[dest + i] = tmp[cursor1 + i];
  4519. }
  4520. return;
  4521. }
  4522. if (length1 === 1) {
  4523. for (i = 0; i < length2; i++) {
  4524. array[dest + i] = array[cursor2 + i];
  4525. }
  4526. array[dest + length2] = tmp[cursor1];
  4527. return;
  4528. }
  4529. var _minGallop = minGallop;
  4530. var count1, count2, exit;
  4531. while (1) {
  4532. count1 = 0;
  4533. count2 = 0;
  4534. exit = false;
  4535. do {
  4536. if (compare(array[cursor2], tmp[cursor1]) < 0) {
  4537. array[dest++] = array[cursor2++];
  4538. count2++;
  4539. count1 = 0;
  4540. if (--length2 === 0) {
  4541. exit = true;
  4542. break;
  4543. }
  4544. }
  4545. else {
  4546. array[dest++] = tmp[cursor1++];
  4547. count1++;
  4548. count2 = 0;
  4549. if (--length1 === 1) {
  4550. exit = true;
  4551. break;
  4552. }
  4553. }
  4554. } while ((count1 | count2) < _minGallop);
  4555. if (exit) {
  4556. break;
  4557. }
  4558. do {
  4559. count1 = gallopRight(array[cursor2], tmp, cursor1, length1, 0, compare);
  4560. if (count1 !== 0) {
  4561. for (i = 0; i < count1; i++) {
  4562. array[dest + i] = tmp[cursor1 + i];
  4563. }
  4564. dest += count1;
  4565. cursor1 += count1;
  4566. length1 -= count1;
  4567. if (length1 <= 1) {
  4568. exit = true;
  4569. break;
  4570. }
  4571. }
  4572. array[dest++] = array[cursor2++];
  4573. if (--length2 === 0) {
  4574. exit = true;
  4575. break;
  4576. }
  4577. count2 = gallopLeft(tmp[cursor1], array, cursor2, length2, 0, compare);
  4578. if (count2 !== 0) {
  4579. for (i = 0; i < count2; i++) {
  4580. array[dest + i] = array[cursor2 + i];
  4581. }
  4582. dest += count2;
  4583. cursor2 += count2;
  4584. length2 -= count2;
  4585. if (length2 === 0) {
  4586. exit = true;
  4587. break;
  4588. }
  4589. }
  4590. array[dest++] = tmp[cursor1++];
  4591. if (--length1 === 1) {
  4592. exit = true;
  4593. break;
  4594. }
  4595. _minGallop--;
  4596. } while (count1 >= DEFAULT_MIN_GALLOPING || count2 >= DEFAULT_MIN_GALLOPING);
  4597. if (exit) {
  4598. break;
  4599. }
  4600. if (_minGallop < 0) {
  4601. _minGallop = 0;
  4602. }
  4603. _minGallop += 2;
  4604. }
  4605. minGallop = _minGallop;
  4606. minGallop < 1 && (minGallop = 1);
  4607. if (length1 === 1) {
  4608. for (i = 0; i < length2; i++) {
  4609. array[dest + i] = array[cursor2 + i];
  4610. }
  4611. array[dest + length2] = tmp[cursor1];
  4612. }
  4613. else if (length1 === 0) {
  4614. throw new Error();
  4615. // throw new Error('mergeLow preconditions were not respected');
  4616. }
  4617. else {
  4618. for (i = 0; i < length1; i++) {
  4619. array[dest + i] = tmp[cursor1 + i];
  4620. }
  4621. }
  4622. }
  4623. function mergeHigh (start1, length1, start2, length2) {
  4624. var i = 0;
  4625. for (i = 0; i < length2; i++) {
  4626. tmp[i] = array[start2 + i];
  4627. }
  4628. var cursor1 = start1 + length1 - 1;
  4629. var cursor2 = length2 - 1;
  4630. var dest = start2 + length2 - 1;
  4631. var customCursor = 0;
  4632. var customDest = 0;
  4633. array[dest--] = array[cursor1--];
  4634. if (--length1 === 0) {
  4635. customCursor = dest - (length2 - 1);
  4636. for (i = 0; i < length2; i++) {
  4637. array[customCursor + i] = tmp[i];
  4638. }
  4639. return;
  4640. }
  4641. if (length2 === 1) {
  4642. dest -= length1;
  4643. cursor1 -= length1;
  4644. customDest = dest + 1;
  4645. customCursor = cursor1 + 1;
  4646. for (i = length1 - 1; i >= 0; i--) {
  4647. array[customDest + i] = array[customCursor + i];
  4648. }
  4649. array[dest] = tmp[cursor2];
  4650. return;
  4651. }
  4652. var _minGallop = minGallop;
  4653. while (true) {
  4654. var count1 = 0;
  4655. var count2 = 0;
  4656. var exit = false;
  4657. do {
  4658. if (compare(tmp[cursor2], array[cursor1]) < 0) {
  4659. array[dest--] = array[cursor1--];
  4660. count1++;
  4661. count2 = 0;
  4662. if (--length1 === 0) {
  4663. exit = true;
  4664. break;
  4665. }
  4666. }
  4667. else {
  4668. array[dest--] = tmp[cursor2--];
  4669. count2++;
  4670. count1 = 0;
  4671. if (--length2 === 1) {
  4672. exit = true;
  4673. break;
  4674. }
  4675. }
  4676. } while ((count1 | count2) < _minGallop);
  4677. if (exit) {
  4678. break;
  4679. }
  4680. do {
  4681. count1 = length1 - gallopRight(tmp[cursor2], array, start1, length1, length1 - 1, compare);
  4682. if (count1 !== 0) {
  4683. dest -= count1;
  4684. cursor1 -= count1;
  4685. length1 -= count1;
  4686. customDest = dest + 1;
  4687. customCursor = cursor1 + 1;
  4688. for (i = count1 - 1; i >= 0; i--) {
  4689. array[customDest + i] = array[customCursor + i];
  4690. }
  4691. if (length1 === 0) {
  4692. exit = true;
  4693. break;
  4694. }
  4695. }
  4696. array[dest--] = tmp[cursor2--];
  4697. if (--length2 === 1) {
  4698. exit = true;
  4699. break;
  4700. }
  4701. count2 = length2 - gallopLeft(array[cursor1], tmp, 0, length2, length2 - 1, compare);
  4702. if (count2 !== 0) {
  4703. dest -= count2;
  4704. cursor2 -= count2;
  4705. length2 -= count2;
  4706. customDest = dest + 1;
  4707. customCursor = cursor2 + 1;
  4708. for (i = 0; i < count2; i++) {
  4709. array[customDest + i] = tmp[customCursor + i];
  4710. }
  4711. if (length2 <= 1) {
  4712. exit = true;
  4713. break;
  4714. }
  4715. }
  4716. array[dest--] = array[cursor1--];
  4717. if (--length1 === 0) {
  4718. exit = true;
  4719. break;
  4720. }
  4721. _minGallop--;
  4722. } while (count1 >= DEFAULT_MIN_GALLOPING || count2 >= DEFAULT_MIN_GALLOPING);
  4723. if (exit) {
  4724. break;
  4725. }
  4726. if (_minGallop < 0) {
  4727. _minGallop = 0;
  4728. }
  4729. _minGallop += 2;
  4730. }
  4731. minGallop = _minGallop;
  4732. if (minGallop < 1) {
  4733. minGallop = 1;
  4734. }
  4735. if (length2 === 1) {
  4736. dest -= length1;
  4737. cursor1 -= length1;
  4738. customDest = dest + 1;
  4739. customCursor = cursor1 + 1;
  4740. for (i = length1 - 1; i >= 0; i--) {
  4741. array[customDest + i] = array[customCursor + i];
  4742. }
  4743. array[dest] = tmp[cursor2];
  4744. }
  4745. else if (length2 === 0) {
  4746. throw new Error();
  4747. // throw new Error('mergeHigh preconditions were not respected');
  4748. }
  4749. else {
  4750. customCursor = dest - (length2 - 1);
  4751. for (i = 0; i < length2; i++) {
  4752. array[customCursor + i] = tmp[i];
  4753. }
  4754. }
  4755. }
  4756. this.mergeRuns = mergeRuns;
  4757. this.forceMergeRuns = forceMergeRuns;
  4758. this.pushRun = pushRun;
  4759. }
  4760. function sort(array, compare, lo, hi) {
  4761. if (!lo) {
  4762. lo = 0;
  4763. }
  4764. if (!hi) {
  4765. hi = array.length;
  4766. }
  4767. var remaining = hi - lo;
  4768. if (remaining < 2) {
  4769. return;
  4770. }
  4771. var runLength = 0;
  4772. if (remaining < DEFAULT_MIN_MERGE) {
  4773. runLength = makeAscendingRun(array, lo, hi, compare);
  4774. binaryInsertionSort(array, lo, hi, lo + runLength, compare);
  4775. return;
  4776. }
  4777. var ts = new TimSort(array, compare);
  4778. var minRun = minRunLength(remaining);
  4779. do {
  4780. runLength = makeAscendingRun(array, lo, hi, compare);
  4781. if (runLength < minRun) {
  4782. var force = remaining;
  4783. if (force > minRun) {
  4784. force = minRun;
  4785. }
  4786. binaryInsertionSort(array, lo, lo + force, lo + runLength, compare);
  4787. runLength = force;
  4788. }
  4789. ts.pushRun(lo, runLength);
  4790. ts.mergeRuns();
  4791. remaining -= runLength;
  4792. lo += runLength;
  4793. } while (remaining !== 0);
  4794. ts.forceMergeRuns();
  4795. }
  4796. /**
  4797. * Storage内容仓库模块
  4798. * @module zrender/Storage
  4799. * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
  4800. * @author errorrik (errorrik@gmail.com)
  4801. * @author pissang (https://github.com/pissang/)
  4802. */
  4803. // Use timsort because in most case elements are partially sorted
  4804. // https://jsfiddle.net/pissang/jr4x7mdm/8/
  4805. function shapeCompareFunc(a, b) {
  4806. if (a.zlevel === b.zlevel) {
  4807. if (a.z === b.z) {
  4808. // if (a.z2 === b.z2) {
  4809. // // FIXME Slow has renderidx compare
  4810. // // http://stackoverflow.com/questions/20883421/sorting-in-javascript-should-every-compare-function-have-a-return-0-statement
  4811. // // https://github.com/v8/v8/blob/47cce544a31ed5577ffe2963f67acb4144ee0232/src/js/array.js#L1012
  4812. // return a.__renderidx - b.__renderidx;
  4813. // }
  4814. return a.z2 - b.z2;
  4815. }
  4816. return a.z - b.z;
  4817. }
  4818. return a.zlevel - b.zlevel;
  4819. }
  4820. /**
  4821. * 内容仓库 (M)
  4822. * @alias module:zrender/Storage
  4823. * @constructor
  4824. */
  4825. var Storage = function () { // jshint ignore:line
  4826. this._roots = [];
  4827. this._displayList = [];
  4828. this._displayListLen = 0;
  4829. };
  4830. Storage.prototype = {
  4831. constructor: Storage,
  4832. /**
  4833. * @param {Function} cb
  4834. *
  4835. */
  4836. traverse: function (cb, context) {
  4837. for (var i = 0; i < this._roots.length; i++) {
  4838. this._roots[i].traverse(cb, context);
  4839. }
  4840. },
  4841. /**
  4842. * 返回所有图形的绘制队列
  4843. * @param {boolean} [update=false] 是否在返回前更新该数组
  4844. * @param {boolean} [includeIgnore=false] 是否包含 ignore 的数组, 在 update 为 true 的时候有效
  4845. *
  4846. * 详见{@link module:zrender/graphic/Displayable.prototype.updateDisplayList}
  4847. * @return {Array.<module:zrender/graphic/Displayable>}
  4848. */
  4849. getDisplayList: function (update, includeIgnore) {
  4850. includeIgnore = includeIgnore || false;
  4851. if (update) {
  4852. this.updateDisplayList(includeIgnore);
  4853. }
  4854. return this._displayList;
  4855. },
  4856. /**
  4857. * 更新图形的绘制队列。
  4858. * 每次绘制前都会调用,该方法会先深度优先遍历整个树,更新所有Group和Shape的变换并且把所有可见的Shape保存到数组中,
  4859. * 最后根据绘制的优先级(zlevel > z > 插入顺序)排序得到绘制队列
  4860. * @param {boolean} [includeIgnore=false] 是否包含 ignore 的数组
  4861. */
  4862. updateDisplayList: function (includeIgnore) {
  4863. this._displayListLen = 0;
  4864. var roots = this._roots;
  4865. var displayList = this._displayList;
  4866. for (var i = 0, len = roots.length; i < len; i++) {
  4867. this._updateAndAddDisplayable(roots[i], null, includeIgnore);
  4868. }
  4869. displayList.length = this._displayListLen;
  4870. // for (var i = 0, len = displayList.length; i < len; i++) {
  4871. // displayList[i].__renderidx = i;
  4872. // }
  4873. // displayList.sort(shapeCompareFunc);
  4874. env$1.canvasSupported && sort(displayList, shapeCompareFunc);
  4875. },
  4876. _updateAndAddDisplayable: function (el, clipPaths, includeIgnore) {
  4877. if (el.ignore && !includeIgnore) {
  4878. return;
  4879. }
  4880. el.beforeUpdate();
  4881. if (el.__dirty) {
  4882. el.update();
  4883. }
  4884. el.afterUpdate();
  4885. var userSetClipPath = el.clipPath;
  4886. if (userSetClipPath) {
  4887. // FIXME 效率影响
  4888. if (clipPaths) {
  4889. clipPaths = clipPaths.slice();
  4890. }
  4891. else {
  4892. clipPaths = [];
  4893. }
  4894. var currentClipPath = userSetClipPath;
  4895. var parentClipPath = el;
  4896. // Recursively add clip path
  4897. while (currentClipPath) {
  4898. // clipPath 的变换是基于使用这个 clipPath 的元素
  4899. currentClipPath.parent = parentClipPath;
  4900. currentClipPath.updateTransform();
  4901. clipPaths.push(currentClipPath);
  4902. parentClipPath = currentClipPath;
  4903. currentClipPath = currentClipPath.clipPath;
  4904. }
  4905. }
  4906. if (el.isGroup) {
  4907. var children = el._children;
  4908. for (var i = 0; i < children.length; i++) {
  4909. var child = children[i];
  4910. // Force to mark as dirty if group is dirty
  4911. // FIXME __dirtyPath ?
  4912. if (el.__dirty) {
  4913. child.__dirty = true;
  4914. }
  4915. this._updateAndAddDisplayable(child, clipPaths, includeIgnore);
  4916. }
  4917. // Mark group clean here
  4918. el.__dirty = false;
  4919. }
  4920. else {
  4921. el.__clipPaths = clipPaths;
  4922. this._displayList[this._displayListLen++] = el;
  4923. }
  4924. },
  4925. /**
  4926. * 添加图形(Shape)或者组(Group)到根节点
  4927. * @param {module:zrender/Element} el
  4928. */
  4929. addRoot: function (el) {
  4930. if (el.__storage === this) {
  4931. return;
  4932. }
  4933. if (el instanceof Group) {
  4934. el.addChildrenToStorage(this);
  4935. }
  4936. this.addToStorage(el);
  4937. this._roots.push(el);
  4938. },
  4939. /**
  4940. * 删除指定的图形(Shape)或者组(Group)
  4941. * @param {string|Array.<string>} [el] 如果为空清空整个Storage
  4942. */
  4943. delRoot: function (el) {
  4944. if (el == null) {
  4945. // 不指定el清空
  4946. for (var i = 0; i < this._roots.length; i++) {
  4947. var root = this._roots[i];
  4948. if (root instanceof Group) {
  4949. root.delChildrenFromStorage(this);
  4950. }
  4951. }
  4952. this._roots = [];
  4953. this._displayList = [];
  4954. this._displayListLen = 0;
  4955. return;
  4956. }
  4957. if (el instanceof Array) {
  4958. for (var i = 0, l = el.length; i < l; i++) {
  4959. this.delRoot(el[i]);
  4960. }
  4961. return;
  4962. }
  4963. var idx = indexOf(this._roots, el);
  4964. if (idx >= 0) {
  4965. this.delFromStorage(el);
  4966. this._roots.splice(idx, 1);
  4967. if (el instanceof Group) {
  4968. el.delChildrenFromStorage(this);
  4969. }
  4970. }
  4971. },
  4972. addToStorage: function (el) {
  4973. el.__storage = this;
  4974. el.dirty(false);
  4975. return this;
  4976. },
  4977. delFromStorage: function (el) {
  4978. if (el) {
  4979. el.__storage = null;
  4980. }
  4981. return this;
  4982. },
  4983. /**
  4984. * 清空并且释放Storage
  4985. */
  4986. dispose: function () {
  4987. this._renderList =
  4988. this._roots = null;
  4989. },
  4990. displayableSortFunc: shapeCompareFunc
  4991. };
  4992. var STYLE_COMMON_PROPS = [
  4993. ['shadowBlur', 0], ['shadowOffsetX', 0], ['shadowOffsetY', 0], ['shadowColor', '#000'],
  4994. ['lineCap', 'butt'], ['lineJoin', 'miter'], ['miterLimit', 10]
  4995. ];
  4996. // var SHADOW_PROPS = STYLE_COMMON_PROPS.slice(0, 4);
  4997. // var LINE_PROPS = STYLE_COMMON_PROPS.slice(4);
  4998. var Style = function (opts, host) {
  4999. this.extendFrom(opts, false);
  5000. this.host = host;
  5001. };
  5002. function createLinearGradient(ctx, obj, rect) {
  5003. var x = obj.x == null ? 0 : obj.x;
  5004. var x2 = obj.x2 == null ? 1 : obj.x2;
  5005. var y = obj.y == null ? 0 : obj.y;
  5006. var y2 = obj.y2 == null ? 0 : obj.y2;
  5007. if (!obj.global) {
  5008. x = x * rect.width + rect.x;
  5009. x2 = x2 * rect.width + rect.x;
  5010. y = y * rect.height + rect.y;
  5011. y2 = y2 * rect.height + rect.y;
  5012. }
  5013. var canvasGradient = ctx.createLinearGradient(x, y, x2, y2);
  5014. return canvasGradient;
  5015. }
  5016. function createRadialGradient(ctx, obj, rect) {
  5017. var width = rect.width;
  5018. var height = rect.height;
  5019. var min = Math.min(width, height);
  5020. var x = obj.x == null ? 0.5 : obj.x;
  5021. var y = obj.y == null ? 0.5 : obj.y;
  5022. var r = obj.r == null ? 0.5 : obj.r;
  5023. if (!obj.global) {
  5024. x = x * width + rect.x;
  5025. y = y * height + rect.y;
  5026. r = r * min;
  5027. }
  5028. var canvasGradient = ctx.createRadialGradient(x, y, 0, x, y, r);
  5029. return canvasGradient;
  5030. }
  5031. Style.prototype = {
  5032. constructor: Style,
  5033. /**
  5034. * @type {module:zrender/graphic/Displayable}
  5035. */
  5036. host: null,
  5037. /**
  5038. * @type {string}
  5039. */
  5040. fill: '#000',
  5041. /**
  5042. * @type {string}
  5043. */
  5044. stroke: null,
  5045. /**
  5046. * @type {number}
  5047. */
  5048. opacity: 1,
  5049. /**
  5050. * @type {Array.<number>}
  5051. */
  5052. lineDash: null,
  5053. /**
  5054. * @type {number}
  5055. */
  5056. lineDashOffset: 0,
  5057. /**
  5058. * @type {number}
  5059. */
  5060. shadowBlur: 0,
  5061. /**
  5062. * @type {number}
  5063. */
  5064. shadowOffsetX: 0,
  5065. /**
  5066. * @type {number}
  5067. */
  5068. shadowOffsetY: 0,
  5069. /**
  5070. * @type {number}
  5071. */
  5072. lineWidth: 1,
  5073. /**
  5074. * If stroke ignore scale
  5075. * @type {Boolean}
  5076. */
  5077. strokeNoScale: false,
  5078. // Bounding rect text configuration
  5079. // Not affected by element transform
  5080. /**
  5081. * @type {string}
  5082. */
  5083. text: null,
  5084. /**
  5085. * If `fontSize` or `fontFamily` exists, `font` will be reset by
  5086. * `fontSize`, `fontStyle`, `fontWeight`, `fontFamily`.
  5087. * So do not visit it directly in upper application (like echarts),
  5088. * but use `contain/text#makeFont` instead.
  5089. * @type {string}
  5090. */
  5091. font: null,
  5092. /**
  5093. * The same as font. Use font please.
  5094. * @deprecated
  5095. * @type {string}
  5096. */
  5097. textFont: null,
  5098. /**
  5099. * It helps merging respectively, rather than parsing an entire font string.
  5100. * @type {string}
  5101. */
  5102. fontStyle: null,
  5103. /**
  5104. * It helps merging respectively, rather than parsing an entire font string.
  5105. * @type {string}
  5106. */
  5107. fontWeight: null,
  5108. /**
  5109. * It helps merging respectively, rather than parsing an entire font string.
  5110. * Should be 12 but not '12px'.
  5111. * @type {number}
  5112. */
  5113. fontSize: null,
  5114. /**
  5115. * It helps merging respectively, rather than parsing an entire font string.
  5116. * @type {string}
  5117. */
  5118. fontFamily: null,
  5119. /**
  5120. * Reserved for special functinality, like 'hr'.
  5121. * @type {string}
  5122. */
  5123. textTag: null,
  5124. /**
  5125. * @type {string}
  5126. */
  5127. textFill: '#000',
  5128. /**
  5129. * @type {string}
  5130. */
  5131. textStroke: null,
  5132. /**
  5133. * @type {number}
  5134. */
  5135. textWidth: null,
  5136. /**
  5137. * Only for textBackground.
  5138. * @type {number}
  5139. */
  5140. textHeight: null,
  5141. /**
  5142. * textStroke may be set as some color as a default
  5143. * value in upper applicaion, where the default value
  5144. * of textStrokeWidth should be 0 to make sure that
  5145. * user can choose to do not use text stroke.
  5146. * @type {number}
  5147. */
  5148. textStrokeWidth: 0,
  5149. /**
  5150. * @type {number}
  5151. */
  5152. textLineHeight: null,
  5153. /**
  5154. * 'inside', 'left', 'right', 'top', 'bottom'
  5155. * [x, y]
  5156. * Based on x, y of rect.
  5157. * @type {string|Array.<number>}
  5158. * @default 'inside'
  5159. */
  5160. textPosition: 'inside',
  5161. /**
  5162. * If not specified, use the boundingRect of a `displayable`.
  5163. * @type {Object}
  5164. */
  5165. textRect: null,
  5166. /**
  5167. * [x, y]
  5168. * @type {Array.<number>}
  5169. */
  5170. textOffset: null,
  5171. /**
  5172. * @type {string}
  5173. */
  5174. textAlign: null,
  5175. /**
  5176. * @type {string}
  5177. */
  5178. textVerticalAlign: null,
  5179. /**
  5180. * @type {number}
  5181. */
  5182. textDistance: 5,
  5183. /**
  5184. * @type {string}
  5185. */
  5186. textShadowColor: 'transparent',
  5187. /**
  5188. * @type {number}
  5189. */
  5190. textShadowBlur: 0,
  5191. /**
  5192. * @type {number}
  5193. */
  5194. textShadowOffsetX: 0,
  5195. /**
  5196. * @type {number}
  5197. */
  5198. textShadowOffsetY: 0,
  5199. /**
  5200. * @type {string}
  5201. */
  5202. textBoxShadowColor: 'transparent',
  5203. /**
  5204. * @type {number}
  5205. */
  5206. textBoxShadowBlur: 0,
  5207. /**
  5208. * @type {number}
  5209. */
  5210. textBoxShadowOffsetX: 0,
  5211. /**
  5212. * @type {number}
  5213. */
  5214. textBoxShadowOffsetY: 0,
  5215. /**
  5216. * Whether transform text.
  5217. * Only useful in Path and Image element
  5218. * @type {boolean}
  5219. */
  5220. transformText: false,
  5221. /**
  5222. * Text rotate around position of Path or Image
  5223. * Only useful in Path and Image element and transformText is false.
  5224. */
  5225. textRotation: 0,
  5226. /**
  5227. * Text origin of text rotation, like [10, 40].
  5228. * Based on x, y of rect.
  5229. * Useful in label rotation of circular symbol.
  5230. * By default, this origin is textPosition.
  5231. * Can be 'center'.
  5232. * @type {string|Array.<number>}
  5233. */
  5234. textOrigin: null,
  5235. /**
  5236. * @type {string}
  5237. */
  5238. textBackgroundColor: null,
  5239. /**
  5240. * @type {string}
  5241. */
  5242. textBorderColor: null,
  5243. /**
  5244. * @type {number}
  5245. */
  5246. textBorderWidth: 0,
  5247. /**
  5248. * @type {number}
  5249. */
  5250. textBorderRadius: 0,
  5251. /**
  5252. * Can be `2` or `[2, 4]` or `[2, 3, 4, 5]`
  5253. * @type {number|Array.<number>}
  5254. */
  5255. textPadding: null,
  5256. /**
  5257. * Text styles for rich text.
  5258. * @type {Object}
  5259. */
  5260. rich: null,
  5261. /**
  5262. * {outerWidth, outerHeight, ellipsis, placeholder}
  5263. * @type {Object}
  5264. */
  5265. truncate: null,
  5266. /**
  5267. * https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation
  5268. * @type {string}
  5269. */
  5270. blend: null,
  5271. /**
  5272. * @param {CanvasRenderingContext2D} ctx
  5273. */
  5274. bind: function (ctx, el, prevEl) {
  5275. var style = this;
  5276. var prevStyle = prevEl && prevEl.style;
  5277. var firstDraw = !prevStyle;
  5278. for (var i = 0; i < STYLE_COMMON_PROPS.length; i++) {
  5279. var prop = STYLE_COMMON_PROPS[i];
  5280. var styleName = prop[0];
  5281. if (firstDraw || style[styleName] !== prevStyle[styleName]) {
  5282. // FIXME Invalid property value will cause style leak from previous element.
  5283. ctx[styleName] = style[styleName] || prop[1];
  5284. }
  5285. }
  5286. if ((firstDraw || style.fill !== prevStyle.fill)) {
  5287. ctx.fillStyle = style.fill;
  5288. }
  5289. if ((firstDraw || style.stroke !== prevStyle.stroke)) {
  5290. ctx.strokeStyle = style.stroke;
  5291. }
  5292. if ((firstDraw || style.opacity !== prevStyle.opacity)) {
  5293. ctx.globalAlpha = style.opacity == null ? 1 : style.opacity;
  5294. }
  5295. if ((firstDraw || style.blend !== prevStyle.blend)) {
  5296. ctx.globalCompositeOperation = style.blend || 'source-over';
  5297. }
  5298. if (this.hasStroke()) {
  5299. var lineWidth = style.lineWidth;
  5300. ctx.lineWidth = lineWidth / (
  5301. (this.strokeNoScale && el && el.getLineScale) ? el.getLineScale() : 1
  5302. );
  5303. }
  5304. },
  5305. hasFill: function () {
  5306. var fill = this.fill;
  5307. return fill != null && fill !== 'none';
  5308. },
  5309. hasStroke: function () {
  5310. var stroke = this.stroke;
  5311. return stroke != null && stroke !== 'none' && this.lineWidth > 0;
  5312. },
  5313. /**
  5314. * Extend from other style
  5315. * @param {zrender/graphic/Style} otherStyle
  5316. * @param {boolean} overwrite true: overwrirte any way.
  5317. * false: overwrite only when !target.hasOwnProperty
  5318. * others: overwrite when property is not null/undefined.
  5319. */
  5320. extendFrom: function (otherStyle, overwrite) {
  5321. if (otherStyle) {
  5322. for (var name in otherStyle) {
  5323. if (otherStyle.hasOwnProperty(name)
  5324. && (overwrite === true
  5325. || (
  5326. overwrite === false
  5327. ? !this.hasOwnProperty(name)
  5328. : otherStyle[name] != null
  5329. )
  5330. )
  5331. ) {
  5332. this[name] = otherStyle[name];
  5333. }
  5334. }
  5335. }
  5336. },
  5337. /**
  5338. * Batch setting style with a given object
  5339. * @param {Object|string} obj
  5340. * @param {*} [obj]
  5341. */
  5342. set: function (obj, value) {
  5343. if (typeof obj === 'string') {
  5344. this[obj] = value;
  5345. }
  5346. else {
  5347. this.extendFrom(obj, true);
  5348. }
  5349. },
  5350. /**
  5351. * Clone
  5352. * @return {zrender/graphic/Style} [description]
  5353. */
  5354. clone: function () {
  5355. var newStyle = new this.constructor();
  5356. newStyle.extendFrom(this, true);
  5357. return newStyle;
  5358. },
  5359. getGradient: function (ctx, obj, rect) {
  5360. var method = obj.type === 'radial' ? createRadialGradient : createLinearGradient;
  5361. var canvasGradient = method(ctx, obj, rect);
  5362. var colorStops = obj.colorStops;
  5363. for (var i = 0; i < colorStops.length; i++) {
  5364. canvasGradient.addColorStop(
  5365. colorStops[i].offset, colorStops[i].color
  5366. );
  5367. }
  5368. return canvasGradient;
  5369. }
  5370. };
  5371. var styleProto = Style.prototype;
  5372. for (var i = 0; i < STYLE_COMMON_PROPS.length; i++) {
  5373. var prop = STYLE_COMMON_PROPS[i];
  5374. if (!(prop[0] in styleProto)) {
  5375. styleProto[prop[0]] = prop[1];
  5376. }
  5377. }
  5378. // Provide for others
  5379. Style.getGradient = styleProto.getGradient;
  5380. var Pattern = function (image, repeat) {
  5381. // Should do nothing more in this constructor. Because gradient can be
  5382. // declard by `color: {image: ...}`, where this constructor will not be called.
  5383. this.image = image;
  5384. this.repeat = repeat;
  5385. // Can be cloned
  5386. this.type = 'pattern';
  5387. };
  5388. Pattern.prototype.getCanvasPattern = function (ctx) {
  5389. return ctx.createPattern(this.image, this.repeat || 'repeat');
  5390. };
  5391. /**
  5392. * @module zrender/Layer
  5393. * @author pissang(https://www.github.com/pissang)
  5394. */
  5395. function returnFalse() {
  5396. return false;
  5397. }
  5398. /**
  5399. * 创建dom
  5400. *
  5401. * @inner
  5402. * @param {string} id dom id 待用
  5403. * @param {Painter} painter painter instance
  5404. * @param {number} number
  5405. */
  5406. function createDom(id, painter, dpr) {
  5407. var newDom = createCanvas();
  5408. var width = painter.getWidth();
  5409. var height = painter.getHeight();
  5410. var newDomStyle = newDom.style;
  5411. // 没append呢,请原谅我这样写,清晰~
  5412. newDomStyle.position = 'absolute';
  5413. newDomStyle.left = 0;
  5414. newDomStyle.top = 0;
  5415. newDomStyle.width = width + 'px';
  5416. newDomStyle.height = height + 'px';
  5417. newDom.width = width * dpr;
  5418. newDom.height = height * dpr;
  5419. // id不作为索引用,避免可能造成的重名,定义为私有属性
  5420. newDom.setAttribute('data-zr-dom-id', id);
  5421. return newDom;
  5422. }
  5423. /**
  5424. * @alias module:zrender/Layer
  5425. * @constructor
  5426. * @extends module:zrender/mixin/Transformable
  5427. * @param {string} id
  5428. * @param {module:zrender/Painter} painter
  5429. * @param {number} [dpr]
  5430. */
  5431. var Layer = function(id, painter, dpr) {
  5432. var dom;
  5433. dpr = dpr || devicePixelRatio;
  5434. if (typeof id === 'string') {
  5435. dom = createDom(id, painter, dpr);
  5436. }
  5437. // Not using isDom because in node it will return false
  5438. else if (isObject(id)) {
  5439. dom = id;
  5440. id = dom.id;
  5441. }
  5442. this.id = id;
  5443. this.dom = dom;
  5444. var domStyle = dom.style;
  5445. if (domStyle) { // Not in node
  5446. dom.onselectstart = returnFalse; // 避免页面选中的尴尬
  5447. domStyle['-webkit-user-select'] = 'none';
  5448. domStyle['user-select'] = 'none';
  5449. domStyle['-webkit-touch-callout'] = 'none';
  5450. domStyle['-webkit-tap-highlight-color'] = 'rgba(0,0,0,0)';
  5451. domStyle['padding'] = 0;
  5452. domStyle['margin'] = 0;
  5453. domStyle['border-width'] = 0;
  5454. }
  5455. this.domBack = null;
  5456. this.ctxBack = null;
  5457. this.painter = painter;
  5458. this.config = null;
  5459. // Configs
  5460. /**
  5461. * 每次清空画布的颜色
  5462. * @type {string}
  5463. * @default 0
  5464. */
  5465. this.clearColor = 0;
  5466. /**
  5467. * 是否开启动态模糊
  5468. * @type {boolean}
  5469. * @default false
  5470. */
  5471. this.motionBlur = false;
  5472. /**
  5473. * 在开启动态模糊的时候使用,与上一帧混合的alpha值,值越大尾迹越明显
  5474. * @type {number}
  5475. * @default 0.7
  5476. */
  5477. this.lastFrameAlpha = 0.7;
  5478. /**
  5479. * Layer dpr
  5480. * @type {number}
  5481. */
  5482. this.dpr = dpr;
  5483. };
  5484. Layer.prototype = {
  5485. constructor: Layer,
  5486. elCount: 0,
  5487. __dirty: true,
  5488. initContext: function () {
  5489. this.ctx = this.dom.getContext('2d');
  5490. this.ctx.__currentValues = {};
  5491. this.ctx.dpr = this.dpr;
  5492. },
  5493. createBackBuffer: function () {
  5494. var dpr = this.dpr;
  5495. this.domBack = createDom('back-' + this.id, this.painter, dpr);
  5496. this.ctxBack = this.domBack.getContext('2d');
  5497. this.ctxBack.__currentValues = {};
  5498. if (dpr != 1) {
  5499. this.ctxBack.scale(dpr, dpr);
  5500. }
  5501. },
  5502. /**
  5503. * @param {number} width
  5504. * @param {number} height
  5505. */
  5506. resize: function (width, height) {
  5507. var dpr = this.dpr;
  5508. var dom = this.dom;
  5509. var domStyle = dom.style;
  5510. var domBack = this.domBack;
  5511. domStyle.width = width + 'px';
  5512. domStyle.height = height + 'px';
  5513. dom.width = width * dpr;
  5514. dom.height = height * dpr;
  5515. if (domBack) {
  5516. domBack.width = width * dpr;
  5517. domBack.height = height * dpr;
  5518. if (dpr != 1) {
  5519. this.ctxBack.scale(dpr, dpr);
  5520. }
  5521. }
  5522. },
  5523. /**
  5524. * 清空该层画布
  5525. * @param {boolean} clearAll Clear all with out motion blur
  5526. */
  5527. clear: function (clearAll) {
  5528. var dom = this.dom;
  5529. var ctx = this.ctx;
  5530. var width = dom.width;
  5531. var height = dom.height;
  5532. var clearColor = this.clearColor;
  5533. var haveMotionBLur = this.motionBlur && !clearAll;
  5534. var lastFrameAlpha = this.lastFrameAlpha;
  5535. var dpr = this.dpr;
  5536. if (haveMotionBLur) {
  5537. if (!this.domBack) {
  5538. this.createBackBuffer();
  5539. }
  5540. this.ctxBack.globalCompositeOperation = 'copy';
  5541. this.ctxBack.drawImage(
  5542. dom, 0, 0,
  5543. width / dpr,
  5544. height / dpr
  5545. );
  5546. }
  5547. ctx.clearRect(0, 0, width, height);
  5548. if (clearColor) {
  5549. var clearColorGradientOrPattern;
  5550. // Gradient
  5551. if (clearColor.colorStops) {
  5552. // Cache canvas gradient
  5553. clearColorGradientOrPattern = clearColor.__canvasGradient || Style.getGradient(ctx, clearColor, {
  5554. x: 0,
  5555. y: 0,
  5556. width: width,
  5557. height: height
  5558. });
  5559. clearColor.__canvasGradient = clearColorGradientOrPattern;
  5560. }
  5561. // Pattern
  5562. else if (clearColor.image) {
  5563. clearColorGradientOrPattern = Pattern.prototype.getCanvasPattern.call(clearColor, ctx);
  5564. }
  5565. ctx.save();
  5566. ctx.fillStyle = clearColorGradientOrPattern || clearColor;
  5567. ctx.fillRect(0, 0, width, height);
  5568. ctx.restore();
  5569. }
  5570. if (haveMotionBLur) {
  5571. var domBack = this.domBack;
  5572. ctx.save();
  5573. ctx.globalAlpha = lastFrameAlpha;
  5574. ctx.drawImage(domBack, 0, 0, width, height);
  5575. ctx.restore();
  5576. }
  5577. }
  5578. };
  5579. var requestAnimationFrame = (
  5580. typeof window !== 'undefined'
  5581. && (
  5582. (window.requestAnimationFrame && window.requestAnimationFrame.bind(window))
  5583. // https://github.com/ecomfe/zrender/issues/189#issuecomment-224919809
  5584. || (window.msRequestAnimationFrame && window.msRequestAnimationFrame.bind(window))
  5585. || window.mozRequestAnimationFrame
  5586. || window.webkitRequestAnimationFrame
  5587. )
  5588. ) || function (func) {
  5589. setTimeout(func, 16);
  5590. };
  5591. var globalImageCache = new LRU(50);
  5592. /**
  5593. * @param {string|HTMLImageElement|HTMLCanvasElement|Canvas} newImageOrSrc
  5594. * @return {HTMLImageElement|HTMLCanvasElement|Canvas} image
  5595. */
  5596. function findExistImage(newImageOrSrc) {
  5597. if (typeof newImageOrSrc === 'string') {
  5598. var cachedImgObj = globalImageCache.get(newImageOrSrc);
  5599. return cachedImgObj && cachedImgObj.image;
  5600. }
  5601. else {
  5602. return newImageOrSrc;
  5603. }
  5604. }
  5605. /**
  5606. * Caution: User should cache loaded images, but not just count on LRU.
  5607. * Consider if required images more than LRU size, will dead loop occur?
  5608. *
  5609. * @param {string|HTMLImageElement|HTMLCanvasElement|Canvas} newImageOrSrc
  5610. * @param {HTMLImageElement|HTMLCanvasElement|Canvas} image Existent image.
  5611. * @param {module:zrender/Element} [hostEl] For calling `dirty`.
  5612. * @param {Function} [cb] params: (image, cbPayload)
  5613. * @param {Object} [cbPayload] Payload on cb calling.
  5614. * @return {HTMLImageElement|HTMLCanvasElement|Canvas} image
  5615. */
  5616. function createOrUpdateImage(newImageOrSrc, image, hostEl, cb, cbPayload) {
  5617. if (!newImageOrSrc) {
  5618. return image;
  5619. }
  5620. else if (typeof newImageOrSrc === 'string') {
  5621. // Image should not be loaded repeatly.
  5622. if ((image && image.__zrImageSrc === newImageOrSrc) || !hostEl) {
  5623. return image;
  5624. }
  5625. // Only when there is no existent image or existent image src
  5626. // is different, this method is responsible for load.
  5627. var cachedImgObj = globalImageCache.get(newImageOrSrc);
  5628. var pendingWrap = {hostEl: hostEl, cb: cb, cbPayload: cbPayload};
  5629. if (cachedImgObj) {
  5630. image = cachedImgObj.image;
  5631. !isImageReady(image) && cachedImgObj.pending.push(pendingWrap);
  5632. }
  5633. else {
  5634. !image && (image = new Image());
  5635. image.onload = imageOnLoad;
  5636. globalImageCache.put(
  5637. newImageOrSrc,
  5638. image.__cachedImgObj = {
  5639. image: image,
  5640. pending: [pendingWrap]
  5641. }
  5642. );
  5643. image.src = image.__zrImageSrc = newImageOrSrc;
  5644. }
  5645. return image;
  5646. }
  5647. // newImageOrSrc is an HTMLImageElement or HTMLCanvasElement or Canvas
  5648. else {
  5649. return newImageOrSrc;
  5650. }
  5651. }
  5652. function imageOnLoad() {
  5653. var cachedImgObj = this.__cachedImgObj;
  5654. this.onload = this.__cachedImgObj = null;
  5655. for (var i = 0; i < cachedImgObj.pending.length; i++) {
  5656. var pendingWrap = cachedImgObj.pending[i];
  5657. var cb = pendingWrap.cb;
  5658. cb && cb(this, pendingWrap.cbPayload);
  5659. pendingWrap.hostEl.dirty();
  5660. }
  5661. cachedImgObj.pending.length = 0;
  5662. }
  5663. function isImageReady(image) {
  5664. return image && image.width && image.height;
  5665. }
  5666. var textWidthCache = {};
  5667. var textWidthCacheCounter = 0;
  5668. var TEXT_CACHE_MAX = 5000;
  5669. var STYLE_REG = /\{([a-zA-Z0-9_]+)\|([^}]*)\}/g;
  5670. var DEFAULT_FONT = '12px sans-serif';
  5671. // Avoid assign to an exported variable, for transforming to cjs.
  5672. var methods$1 = {};
  5673. /**
  5674. * @public
  5675. * @param {string} text
  5676. * @param {string} font
  5677. * @return {number} width
  5678. */
  5679. function getWidth(text, font) {
  5680. font = font || DEFAULT_FONT;
  5681. var key = text + ':' + font;
  5682. if (textWidthCache[key]) {
  5683. return textWidthCache[key];
  5684. }
  5685. var textLines = (text + '').split('\n');
  5686. var width = 0;
  5687. for (var i = 0, l = textLines.length; i < l; i++) {
  5688. // textContain.measureText may be overrided in SVG or VML
  5689. width = Math.max(measureText(textLines[i], font).width, width);
  5690. }
  5691. if (textWidthCacheCounter > TEXT_CACHE_MAX) {
  5692. textWidthCacheCounter = 0;
  5693. textWidthCache = {};
  5694. }
  5695. textWidthCacheCounter++;
  5696. textWidthCache[key] = width;
  5697. return width;
  5698. }
  5699. /**
  5700. * @public
  5701. * @param {string} text
  5702. * @param {string} font
  5703. * @param {string} [textAlign='left']
  5704. * @param {string} [textVerticalAlign='top']
  5705. * @param {Array.<number>} [textPadding]
  5706. * @param {Object} [rich]
  5707. * @param {Object} [truncate]
  5708. * @return {Object} {x, y, width, height, lineHeight}
  5709. */
  5710. function getBoundingRect(text, font, textAlign, textVerticalAlign, textPadding, rich, truncate) {
  5711. return rich
  5712. ? getRichTextRect(text, font, textAlign, textVerticalAlign, textPadding, rich, truncate)
  5713. : getPlainTextRect(text, font, textAlign, textVerticalAlign, textPadding, truncate);
  5714. }
  5715. function getPlainTextRect(text, font, textAlign, textVerticalAlign, textPadding, truncate) {
  5716. var contentBlock = parsePlainText(text, font, textPadding, truncate);
  5717. var outerWidth = getWidth(text, font);
  5718. if (textPadding) {
  5719. outerWidth += textPadding[1] + textPadding[3];
  5720. }
  5721. var outerHeight = contentBlock.outerHeight;
  5722. var x = adjustTextX(0, outerWidth, textAlign);
  5723. var y = adjustTextY(0, outerHeight, textVerticalAlign);
  5724. var rect = new BoundingRect(x, y, outerWidth, outerHeight);
  5725. rect.lineHeight = contentBlock.lineHeight;
  5726. return rect;
  5727. }
  5728. function getRichTextRect(text, font, textAlign, textVerticalAlign, textPadding, rich, truncate) {
  5729. var contentBlock = parseRichText(text, {
  5730. rich: rich,
  5731. truncate: truncate,
  5732. font: font,
  5733. textAlign: textAlign,
  5734. textPadding: textPadding
  5735. });
  5736. var outerWidth = contentBlock.outerWidth;
  5737. var outerHeight = contentBlock.outerHeight;
  5738. var x = adjustTextX(0, outerWidth, textAlign);
  5739. var y = adjustTextY(0, outerHeight, textVerticalAlign);
  5740. return new BoundingRect(x, y, outerWidth, outerHeight);
  5741. }
  5742. /**
  5743. * @public
  5744. * @param {number} x
  5745. * @param {number} width
  5746. * @param {string} [textAlign='left']
  5747. * @return {number} Adjusted x.
  5748. */
  5749. function adjustTextX(x, width, textAlign) {
  5750. // FIXME Right to left language
  5751. if (textAlign === 'right') {
  5752. x -= width;
  5753. }
  5754. else if (textAlign === 'center') {
  5755. x -= width / 2;
  5756. }
  5757. return x;
  5758. }
  5759. /**
  5760. * @public
  5761. * @param {number} y
  5762. * @param {number} height
  5763. * @param {string} [textVerticalAlign='top']
  5764. * @return {number} Adjusted y.
  5765. */
  5766. function adjustTextY(y, height, textVerticalAlign) {
  5767. if (textVerticalAlign === 'middle') {
  5768. y -= height / 2;
  5769. }
  5770. else if (textVerticalAlign === 'bottom') {
  5771. y -= height;
  5772. }
  5773. return y;
  5774. }
  5775. /**
  5776. * @public
  5777. * @param {stirng} textPosition
  5778. * @param {Object} rect {x, y, width, height}
  5779. * @param {number} distance
  5780. * @return {Object} {x, y, textAlign, textVerticalAlign}
  5781. */
  5782. function adjustTextPositionOnRect(textPosition, rect, distance) {
  5783. var x = rect.x;
  5784. var y = rect.y;
  5785. var height = rect.height;
  5786. var width = rect.width;
  5787. var halfHeight = height / 2;
  5788. var textAlign = 'left';
  5789. var textVerticalAlign = 'top';
  5790. switch (textPosition) {
  5791. case 'left':
  5792. x -= distance;
  5793. y += halfHeight;
  5794. textAlign = 'right';
  5795. textVerticalAlign = 'middle';
  5796. break;
  5797. case 'right':
  5798. x += distance + width;
  5799. y += halfHeight;
  5800. textVerticalAlign = 'middle';
  5801. break;
  5802. case 'top':
  5803. x += width / 2;
  5804. y -= distance;
  5805. textAlign = 'center';
  5806. textVerticalAlign = 'bottom';
  5807. break;
  5808. case 'bottom':
  5809. x += width / 2;
  5810. y += height + distance;
  5811. textAlign = 'center';
  5812. break;
  5813. case 'inside':
  5814. x += width / 2;
  5815. y += halfHeight;
  5816. textAlign = 'center';
  5817. textVerticalAlign = 'middle';
  5818. break;
  5819. case 'insideLeft':
  5820. x += distance;
  5821. y += halfHeight;
  5822. textVerticalAlign = 'middle';
  5823. break;
  5824. case 'insideRight':
  5825. x += width - distance;
  5826. y += halfHeight;
  5827. textAlign = 'right';
  5828. textVerticalAlign = 'middle';
  5829. break;
  5830. case 'insideTop':
  5831. x += width / 2;
  5832. y += distance;
  5833. textAlign = 'center';
  5834. break;
  5835. case 'insideBottom':
  5836. x += width / 2;
  5837. y += height - distance;
  5838. textAlign = 'center';
  5839. textVerticalAlign = 'bottom';
  5840. break;
  5841. case 'insideTopLeft':
  5842. x += distance;
  5843. y += distance;
  5844. break;
  5845. case 'insideTopRight':
  5846. x += width - distance;
  5847. y += distance;
  5848. textAlign = 'right';
  5849. break;
  5850. case 'insideBottomLeft':
  5851. x += distance;
  5852. y += height - distance;
  5853. textVerticalAlign = 'bottom';
  5854. break;
  5855. case 'insideBottomRight':
  5856. x += width - distance;
  5857. y += height - distance;
  5858. textAlign = 'right';
  5859. textVerticalAlign = 'bottom';
  5860. break;
  5861. }
  5862. return {
  5863. x: x,
  5864. y: y,
  5865. textAlign: textAlign,
  5866. textVerticalAlign: textVerticalAlign
  5867. };
  5868. }
  5869. /**
  5870. * Show ellipsis if overflow.
  5871. *
  5872. * @public
  5873. * @param {string} text
  5874. * @param {string} containerWidth
  5875. * @param {string} font
  5876. * @param {number} [ellipsis='...']
  5877. * @param {Object} [options]
  5878. * @param {number} [options.maxIterations=3]
  5879. * @param {number} [options.minChar=0] If truncate result are less
  5880. * then minChar, ellipsis will not show, which is
  5881. * better for user hint in some cases.
  5882. * @param {number} [options.placeholder=''] When all truncated, use the placeholder.
  5883. * @return {string}
  5884. */
  5885. function truncateText(text, containerWidth, font, ellipsis, options) {
  5886. if (!containerWidth) {
  5887. return '';
  5888. }
  5889. var textLines = (text + '').split('\n');
  5890. options = prepareTruncateOptions(containerWidth, font, ellipsis, options);
  5891. // FIXME
  5892. // It is not appropriate that every line has '...' when truncate multiple lines.
  5893. for (var i = 0, len = textLines.length; i < len; i++) {
  5894. textLines[i] = truncateSingleLine(textLines[i], options);
  5895. }
  5896. return textLines.join('\n');
  5897. }
  5898. function prepareTruncateOptions(containerWidth, font, ellipsis, options) {
  5899. options = extend({}, options);
  5900. options.font = font;
  5901. var ellipsis = retrieve2(ellipsis, '...');
  5902. options.maxIterations = retrieve2(options.maxIterations, 2);
  5903. var minChar = options.minChar = retrieve2(options.minChar, 0);
  5904. // FIXME
  5905. // Other languages?
  5906. options.cnCharWidth = getWidth('国', font);
  5907. // FIXME
  5908. // Consider proportional font?
  5909. var ascCharWidth = options.ascCharWidth = getWidth('a', font);
  5910. options.placeholder = retrieve2(options.placeholder, '');
  5911. // Example 1: minChar: 3, text: 'asdfzxcv', truncate result: 'asdf', but not: 'a...'.
  5912. // Example 2: minChar: 3, text: '维度', truncate result: '维', but not: '...'.
  5913. var contentWidth = containerWidth = Math.max(0, containerWidth - 1); // Reserve some gap.
  5914. for (var i = 0; i < minChar && contentWidth >= ascCharWidth; i++) {
  5915. contentWidth -= ascCharWidth;
  5916. }
  5917. var ellipsisWidth = getWidth(ellipsis);
  5918. if (ellipsisWidth > contentWidth) {
  5919. ellipsis = '';
  5920. ellipsisWidth = 0;
  5921. }
  5922. contentWidth = containerWidth - ellipsisWidth;
  5923. options.ellipsis = ellipsis;
  5924. options.ellipsisWidth = ellipsisWidth;
  5925. options.contentWidth = contentWidth;
  5926. options.containerWidth = containerWidth;
  5927. return options;
  5928. }
  5929. function truncateSingleLine(textLine, options) {
  5930. var containerWidth = options.containerWidth;
  5931. var font = options.font;
  5932. var contentWidth = options.contentWidth;
  5933. if (!containerWidth) {
  5934. return '';
  5935. }
  5936. var lineWidth = getWidth(textLine, font);
  5937. if (lineWidth <= containerWidth) {
  5938. return textLine;
  5939. }
  5940. for (var j = 0;; j++) {
  5941. if (lineWidth <= contentWidth || j >= options.maxIterations) {
  5942. textLine += options.ellipsis;
  5943. break;
  5944. }
  5945. var subLength = j === 0
  5946. ? estimateLength(textLine, contentWidth, options.ascCharWidth, options.cnCharWidth)
  5947. : lineWidth > 0
  5948. ? Math.floor(textLine.length * contentWidth / lineWidth)
  5949. : 0;
  5950. textLine = textLine.substr(0, subLength);
  5951. lineWidth = getWidth(textLine, font);
  5952. }
  5953. if (textLine === '') {
  5954. textLine = options.placeholder;
  5955. }
  5956. return textLine;
  5957. }
  5958. function estimateLength(text, contentWidth, ascCharWidth, cnCharWidth) {
  5959. var width = 0;
  5960. var i = 0;
  5961. for (var len = text.length; i < len && width < contentWidth; i++) {
  5962. var charCode = text.charCodeAt(i);
  5963. width += (0 <= charCode && charCode <= 127) ? ascCharWidth : cnCharWidth;
  5964. }
  5965. return i;
  5966. }
  5967. /**
  5968. * @public
  5969. * @param {string} font
  5970. * @return {number} line height
  5971. */
  5972. function getLineHeight(font) {
  5973. // FIXME A rough approach.
  5974. return getWidth('国', font);
  5975. }
  5976. /**
  5977. * @public
  5978. * @param {string} text
  5979. * @param {string} font
  5980. * @return {Object} width
  5981. */
  5982. function measureText(text, font) {
  5983. return methods$1.measureText(text, font);
  5984. }
  5985. // Avoid assign to an exported variable, for transforming to cjs.
  5986. methods$1.measureText = function (text, font) {
  5987. var ctx = getContext();
  5988. ctx.font = font || DEFAULT_FONT;
  5989. return ctx.measureText(text);
  5990. };
  5991. /**
  5992. * @public
  5993. * @param {string} text
  5994. * @param {string} font
  5995. * @param {Object} [truncate]
  5996. * @return {Object} block: {lineHeight, lines, height, outerHeight}
  5997. * Notice: for performance, do not calculate outerWidth util needed.
  5998. */
  5999. function parsePlainText(text, font, padding, truncate) {
  6000. text != null && (text += '');
  6001. var lineHeight = getLineHeight(font);
  6002. var lines = text ? text.split('\n') : [];
  6003. var height = lines.length * lineHeight;
  6004. var outerHeight = height;
  6005. if (padding) {
  6006. outerHeight += padding[0] + padding[2];
  6007. }
  6008. if (text && truncate) {
  6009. var truncOuterHeight = truncate.outerHeight;
  6010. var truncOuterWidth = truncate.outerWidth;
  6011. if (truncOuterHeight != null && outerHeight > truncOuterHeight) {
  6012. text = '';
  6013. lines = [];
  6014. }
  6015. else if (truncOuterWidth != null) {
  6016. var options = prepareTruncateOptions(
  6017. truncOuterWidth - (padding ? padding[1] + padding[3] : 0),
  6018. font,
  6019. truncate.ellipsis,
  6020. {minChar: truncate.minChar, placeholder: truncate.placeholder}
  6021. );
  6022. // FIXME
  6023. // It is not appropriate that every line has '...' when truncate multiple lines.
  6024. for (var i = 0, len = lines.length; i < len; i++) {
  6025. lines[i] = truncateSingleLine(lines[i], options);
  6026. }
  6027. }
  6028. }
  6029. return {
  6030. lines: lines,
  6031. height: height,
  6032. outerHeight: outerHeight,
  6033. lineHeight: lineHeight
  6034. };
  6035. }
  6036. /**
  6037. * For example: 'some text {a|some text}other text{b|some text}xxx{c|}xxx'
  6038. * Also consider 'bbbb{a|xxx\nzzz}xxxx\naaaa'.
  6039. *
  6040. * @public
  6041. * @param {string} text
  6042. * @param {Object} style
  6043. * @return {Object} block
  6044. * {
  6045. * width,
  6046. * height,
  6047. * lines: [{
  6048. * lineHeight,
  6049. * width,
  6050. * tokens: [[{
  6051. * styleName,
  6052. * text,
  6053. * width, // include textPadding
  6054. * height, // include textPadding
  6055. * textWidth, // pure text width
  6056. * textHeight, // pure text height
  6057. * lineHeihgt,
  6058. * font,
  6059. * textAlign,
  6060. * textVerticalAlign
  6061. * }], [...], ...]
  6062. * }, ...]
  6063. * }
  6064. * If styleName is undefined, it is plain text.
  6065. */
  6066. function parseRichText(text, style) {
  6067. var contentBlock = {lines: [], width: 0, height: 0};
  6068. text != null && (text += '');
  6069. if (!text) {
  6070. return contentBlock;
  6071. }
  6072. var lastIndex = STYLE_REG.lastIndex = 0;
  6073. var result;
  6074. while ((result = STYLE_REG.exec(text)) != null) {
  6075. var matchedIndex = result.index;
  6076. if (matchedIndex > lastIndex) {
  6077. pushTokens(contentBlock, text.substring(lastIndex, matchedIndex));
  6078. }
  6079. pushTokens(contentBlock, result[2], result[1]);
  6080. lastIndex = STYLE_REG.lastIndex;
  6081. }
  6082. if (lastIndex < text.length) {
  6083. pushTokens(contentBlock, text.substring(lastIndex, text.length));
  6084. }
  6085. var lines = contentBlock.lines;
  6086. var contentHeight = 0;
  6087. var contentWidth = 0;
  6088. // For `textWidth: 100%`
  6089. var pendingList = [];
  6090. var stlPadding = style.textPadding;
  6091. var truncate = style.truncate;
  6092. var truncateWidth = truncate && truncate.outerWidth;
  6093. var truncateHeight = truncate && truncate.outerHeight;
  6094. if (stlPadding) {
  6095. truncateWidth != null && (truncateWidth -= stlPadding[1] + stlPadding[3]);
  6096. truncateHeight != null && (truncateHeight -= stlPadding[0] + stlPadding[2]);
  6097. }
  6098. // Calculate layout info of tokens.
  6099. for (var i = 0; i < lines.length; i++) {
  6100. var line = lines[i];
  6101. var lineHeight = 0;
  6102. var lineWidth = 0;
  6103. for (var j = 0; j < line.tokens.length; j++) {
  6104. var token = line.tokens[j];
  6105. var tokenStyle = token.styleName && style.rich[token.styleName] || {};
  6106. // textPadding should not inherit from style.
  6107. var textPadding = token.textPadding = tokenStyle.textPadding;
  6108. // textFont has been asigned to font by `normalizeStyle`.
  6109. var font = token.font = tokenStyle.font || style.font;
  6110. // textHeight can be used when textVerticalAlign is specified in token.
  6111. var tokenHeight = token.textHeight = retrieve2(
  6112. // textHeight should not be inherited, consider it can be specified
  6113. // as box height of the block.
  6114. tokenStyle.textHeight, getLineHeight(font)
  6115. );
  6116. textPadding && (tokenHeight += textPadding[0] + textPadding[2]);
  6117. token.height = tokenHeight;
  6118. token.lineHeight = retrieve3(
  6119. tokenStyle.textLineHeight, style.textLineHeight, tokenHeight
  6120. );
  6121. token.textAlign = tokenStyle && tokenStyle.textAlign || style.textAlign;
  6122. token.textVerticalAlign = tokenStyle && tokenStyle.textVerticalAlign || 'middle';
  6123. if (truncateHeight != null && contentHeight + token.lineHeight > truncateHeight) {
  6124. return {lines: [], width: 0, height: 0};
  6125. }
  6126. token.textWidth = getWidth(token.text, font);
  6127. var tokenWidth = tokenStyle.textWidth;
  6128. var tokenWidthNotSpecified = tokenWidth == null || tokenWidth === 'auto';
  6129. // Percent width, can be `100%`, can be used in drawing separate
  6130. // line when box width is needed to be auto.
  6131. if (typeof tokenWidth === 'string' && tokenWidth.charAt(tokenWidth.length - 1) === '%') {
  6132. token.percentWidth = tokenWidth;
  6133. pendingList.push(token);
  6134. tokenWidth = 0;
  6135. // Do not truncate in this case, because there is no user case
  6136. // and it is too complicated.
  6137. }
  6138. else {
  6139. if (tokenWidthNotSpecified) {
  6140. tokenWidth = token.textWidth;
  6141. // FIXME: If image is not loaded and textWidth is not specified, calling
  6142. // `getBoundingRect()` will not get correct result.
  6143. var textBackgroundColor = tokenStyle.textBackgroundColor;
  6144. var bgImg = textBackgroundColor && textBackgroundColor.image;
  6145. // Use cases:
  6146. // (1) If image is not loaded, it will be loaded at render phase and call
  6147. // `dirty()` and `textBackgroundColor.image` will be replaced with the loaded
  6148. // image, and then the right size will be calculated here at the next tick.
  6149. // See `graphic/helper/text.js`.
  6150. // (2) If image loaded, and `textBackgroundColor.image` is image src string,
  6151. // use `imageHelper.findExistImage` to find cached image.
  6152. // `imageHelper.findExistImage` will always be called here before
  6153. // `imageHelper.createOrUpdateImage` in `graphic/helper/text.js#renderRichText`
  6154. // which ensures that image will not be rendered before correct size calcualted.
  6155. if (bgImg) {
  6156. bgImg = findExistImage(bgImg);
  6157. if (isImageReady(bgImg)) {
  6158. tokenWidth = Math.max(tokenWidth, bgImg.width * tokenHeight / bgImg.height);
  6159. }
  6160. }
  6161. }
  6162. var paddingW = textPadding ? textPadding[1] + textPadding[3] : 0;
  6163. tokenWidth += paddingW;
  6164. var remianTruncWidth = truncateWidth != null ? truncateWidth - lineWidth : null;
  6165. if (remianTruncWidth != null && remianTruncWidth < tokenWidth) {
  6166. if (!tokenWidthNotSpecified || remianTruncWidth < paddingW) {
  6167. token.text = '';
  6168. token.textWidth = tokenWidth = 0;
  6169. }
  6170. else {
  6171. token.text = truncateText(
  6172. token.text, remianTruncWidth - paddingW, font, truncate.ellipsis,
  6173. {minChar: truncate.minChar}
  6174. );
  6175. token.textWidth = getWidth(token.text, font);
  6176. tokenWidth = token.textWidth + paddingW;
  6177. }
  6178. }
  6179. }
  6180. lineWidth += (token.width = tokenWidth);
  6181. tokenStyle && (lineHeight = Math.max(lineHeight, token.lineHeight));
  6182. }
  6183. line.width = lineWidth;
  6184. line.lineHeight = lineHeight;
  6185. contentHeight += lineHeight;
  6186. contentWidth = Math.max(contentWidth, lineWidth);
  6187. }
  6188. contentBlock.outerWidth = contentBlock.width = retrieve2(style.textWidth, contentWidth);
  6189. contentBlock.outerHeight = contentBlock.height = retrieve2(style.textHeight, contentHeight);
  6190. if (stlPadding) {
  6191. contentBlock.outerWidth += stlPadding[1] + stlPadding[3];
  6192. contentBlock.outerHeight += stlPadding[0] + stlPadding[2];
  6193. }
  6194. for (var i = 0; i < pendingList.length; i++) {
  6195. var token = pendingList[i];
  6196. var percentWidth = token.percentWidth;
  6197. // Should not base on outerWidth, because token can not be placed out of padding.
  6198. token.width = parseInt(percentWidth, 10) / 100 * contentWidth;
  6199. }
  6200. return contentBlock;
  6201. }
  6202. function pushTokens(block, str, styleName) {
  6203. var isEmptyStr = str === '';
  6204. var strs = str.split('\n');
  6205. var lines = block.lines;
  6206. for (var i = 0; i < strs.length; i++) {
  6207. var text = strs[i];
  6208. var token = {
  6209. styleName: styleName,
  6210. text: text,
  6211. isLineHolder: !text && !isEmptyStr
  6212. };
  6213. // The first token should be appended to the last line.
  6214. if (!i) {
  6215. var tokens = (lines[lines.length - 1] || (lines[0] = {tokens: []})).tokens;
  6216. // Consider cases:
  6217. // (1) ''.split('\n') => ['', '\n', ''], the '' at the first item
  6218. // (which is a placeholder) should be replaced by new token.
  6219. // (2) A image backage, where token likes {a|}.
  6220. // (3) A redundant '' will affect textAlign in line.
  6221. // (4) tokens with the same tplName should not be merged, because
  6222. // they should be displayed in different box (with border and padding).
  6223. var tokensLen = tokens.length;
  6224. (tokensLen === 1 && tokens[0].isLineHolder)
  6225. ? (tokens[0] = token)
  6226. // Consider text is '', only insert when it is the "lineHolder" or
  6227. // "emptyStr". Otherwise a redundant '' will affect textAlign in line.
  6228. : ((text || !tokensLen || isEmptyStr) && tokens.push(token));
  6229. }
  6230. // Other tokens always start a new line.
  6231. else {
  6232. // If there is '', insert it as a placeholder.
  6233. lines.push({tokens: [token]});
  6234. }
  6235. }
  6236. }
  6237. function makeFont(style) {
  6238. // FIXME in node-canvas fontWeight is before fontStyle
  6239. // Use `fontSize` `fontFamily` to check whether font properties are defined.
  6240. return (style.fontSize || style.fontFamily) && [
  6241. style.fontStyle,
  6242. style.fontWeight,
  6243. (style.fontSize || 12) + 'px',
  6244. // If font properties are defined, `fontFamily` should not be ignored.
  6245. style.fontFamily || 'sans-serif'
  6246. ].join(' ') || style.textFont || style.font;
  6247. }
  6248. function buildPath(ctx, shape) {
  6249. var x = shape.x;
  6250. var y = shape.y;
  6251. var width = shape.width;
  6252. var height = shape.height;
  6253. var r = shape.r;
  6254. var r1;
  6255. var r2;
  6256. var r3;
  6257. var r4;
  6258. // Convert width and height to positive for better borderRadius
  6259. if (width < 0) {
  6260. x = x + width;
  6261. width = -width;
  6262. }
  6263. if (height < 0) {
  6264. y = y + height;
  6265. height = -height;
  6266. }
  6267. if (typeof r === 'number') {
  6268. r1 = r2 = r3 = r4 = r;
  6269. }
  6270. else if (r instanceof Array) {
  6271. if (r.length === 1) {
  6272. r1 = r2 = r3 = r4 = r[0];
  6273. }
  6274. else if (r.length === 2) {
  6275. r1 = r3 = r[0];
  6276. r2 = r4 = r[1];
  6277. }
  6278. else if (r.length === 3) {
  6279. r1 = r[0];
  6280. r2 = r4 = r[1];
  6281. r3 = r[2];
  6282. }
  6283. else {
  6284. r1 = r[0];
  6285. r2 = r[1];
  6286. r3 = r[2];
  6287. r4 = r[3];
  6288. }
  6289. }
  6290. else {
  6291. r1 = r2 = r3 = r4 = 0;
  6292. }
  6293. var total;
  6294. if (r1 + r2 > width) {
  6295. total = r1 + r2;
  6296. r1 *= width / total;
  6297. r2 *= width / total;
  6298. }
  6299. if (r3 + r4 > width) {
  6300. total = r3 + r4;
  6301. r3 *= width / total;
  6302. r4 *= width / total;
  6303. }
  6304. if (r2 + r3 > height) {
  6305. total = r2 + r3;
  6306. r2 *= height / total;
  6307. r3 *= height / total;
  6308. }
  6309. if (r1 + r4 > height) {
  6310. total = r1 + r4;
  6311. r1 *= height / total;
  6312. r4 *= height / total;
  6313. }
  6314. ctx.moveTo(x + r1, y);
  6315. ctx.lineTo(x + width - r2, y);
  6316. r2 !== 0 && ctx.quadraticCurveTo(
  6317. x + width, y, x + width, y + r2
  6318. );
  6319. ctx.lineTo(x + width, y + height - r3);
  6320. r3 !== 0 && ctx.quadraticCurveTo(
  6321. x + width, y + height, x + width - r3, y + height
  6322. );
  6323. ctx.lineTo(x + r4, y + height);
  6324. r4 !== 0 && ctx.quadraticCurveTo(
  6325. x, y + height, x, y + height - r4
  6326. );
  6327. ctx.lineTo(x, y + r1);
  6328. r1 !== 0 && ctx.quadraticCurveTo(x, y, x + r1, y);
  6329. }
  6330. // TODO: Have not support 'start', 'end' yet.
  6331. var VALID_TEXT_ALIGN = {left: 1, right: 1, center: 1};
  6332. var VALID_TEXT_VERTICAL_ALIGN = {top: 1, bottom: 1, middle: 1};
  6333. /**
  6334. * @param {module:zrender/graphic/Style} style
  6335. * @return {module:zrender/graphic/Style} The input style.
  6336. */
  6337. function normalizeTextStyle(style) {
  6338. normalizeStyle(style);
  6339. each$1(style.rich, normalizeStyle);
  6340. return style;
  6341. }
  6342. function normalizeStyle(style) {
  6343. if (style) {
  6344. style.font = makeFont(style);
  6345. var textAlign = style.textAlign;
  6346. textAlign === 'middle' && (textAlign = 'center');
  6347. style.textAlign = (
  6348. textAlign == null || VALID_TEXT_ALIGN[textAlign]
  6349. ) ? textAlign : 'left';
  6350. // Compatible with textBaseline.
  6351. var textVerticalAlign = style.textVerticalAlign || style.textBaseline;
  6352. textVerticalAlign === 'center' && (textVerticalAlign = 'middle');
  6353. style.textVerticalAlign = (
  6354. textVerticalAlign == null || VALID_TEXT_VERTICAL_ALIGN[textVerticalAlign]
  6355. ) ? textVerticalAlign : 'top';
  6356. var textPadding = style.textPadding;
  6357. if (textPadding) {
  6358. style.textPadding = normalizeCssArray(style.textPadding);
  6359. }
  6360. }
  6361. }
  6362. /**
  6363. * @param {CanvasRenderingContext2D} ctx
  6364. * @param {string} text
  6365. * @param {module:zrender/graphic/Style} style
  6366. * @param {Object|boolean} [rect] {x, y, width, height}
  6367. * If set false, rect text is not used.
  6368. */
  6369. function renderText(hostEl, ctx, text, style, rect) {
  6370. style.rich
  6371. ? renderRichText(hostEl, ctx, text, style, rect)
  6372. : renderPlainText(hostEl, ctx, text, style, rect);
  6373. }
  6374. function renderPlainText(hostEl, ctx, text, style, rect) {
  6375. var font = setCtx(ctx, 'font', style.font || DEFAULT_FONT);
  6376. var textPadding = style.textPadding;
  6377. var contentBlock = hostEl.__textCotentBlock;
  6378. if (!contentBlock || hostEl.__dirty) {
  6379. contentBlock = hostEl.__textCotentBlock = parsePlainText(
  6380. text, font, textPadding, style.truncate
  6381. );
  6382. }
  6383. var outerHeight = contentBlock.outerHeight;
  6384. var textLines = contentBlock.lines;
  6385. var lineHeight = contentBlock.lineHeight;
  6386. var boxPos = getBoxPosition(outerHeight, style, rect);
  6387. var baseX = boxPos.baseX;
  6388. var baseY = boxPos.baseY;
  6389. var textAlign = boxPos.textAlign;
  6390. var textVerticalAlign = boxPos.textVerticalAlign;
  6391. // Origin of textRotation should be the base point of text drawing.
  6392. applyTextRotation(ctx, style, rect, baseX, baseY);
  6393. var boxY = adjustTextY(baseY, outerHeight, textVerticalAlign);
  6394. var textX = baseX;
  6395. var textY = boxY;
  6396. var needDrawBg = needDrawBackground(style);
  6397. if (needDrawBg || textPadding) {
  6398. // Consider performance, do not call getTextWidth util necessary.
  6399. var textWidth = getWidth(text, font);
  6400. var outerWidth = textWidth;
  6401. textPadding && (outerWidth += textPadding[1] + textPadding[3]);
  6402. var boxX = adjustTextX(baseX, outerWidth, textAlign);
  6403. needDrawBg && drawBackground(hostEl, ctx, style, boxX, boxY, outerWidth, outerHeight);
  6404. if (textPadding) {
  6405. textX = getTextXForPadding(baseX, textAlign, textPadding);
  6406. textY += textPadding[0];
  6407. }
  6408. }
  6409. setCtx(ctx, 'textAlign', textAlign || 'left');
  6410. // Force baseline to be "middle". Otherwise, if using "top", the
  6411. // text will offset downward a little bit in font "Microsoft YaHei".
  6412. setCtx(ctx, 'textBaseline', 'middle');
  6413. // Always set shadowBlur and shadowOffset to avoid leak from displayable.
  6414. setCtx(ctx, 'shadowBlur', style.textShadowBlur || 0);
  6415. setCtx(ctx, 'shadowColor', style.textShadowColor || 'transparent');
  6416. setCtx(ctx, 'shadowOffsetX', style.textShadowOffsetX || 0);
  6417. setCtx(ctx, 'shadowOffsetY', style.textShadowOffsetY || 0);
  6418. // `textBaseline` is set as 'middle'.
  6419. textY += lineHeight / 2;
  6420. var textStrokeWidth = style.textStrokeWidth;
  6421. var textStroke = getStroke(style.textStroke, textStrokeWidth);
  6422. var textFill = getFill(style.textFill);
  6423. if (textStroke) {
  6424. setCtx(ctx, 'lineWidth', textStrokeWidth);
  6425. setCtx(ctx, 'strokeStyle', textStroke);
  6426. }
  6427. if (textFill) {
  6428. setCtx(ctx, 'fillStyle', textFill);
  6429. }
  6430. for (var i = 0; i < textLines.length; i++) {
  6431. // Fill after stroke so the outline will not cover the main part.
  6432. textStroke && ctx.strokeText(textLines[i], textX, textY);
  6433. textFill && ctx.fillText(textLines[i], textX, textY);
  6434. textY += lineHeight;
  6435. }
  6436. }
  6437. function renderRichText(hostEl, ctx, text, style, rect) {
  6438. var contentBlock = hostEl.__textCotentBlock;
  6439. if (!contentBlock || hostEl.__dirty) {
  6440. contentBlock = hostEl.__textCotentBlock = parseRichText(text, style);
  6441. }
  6442. drawRichText(hostEl, ctx, contentBlock, style, rect);
  6443. }
  6444. function drawRichText(hostEl, ctx, contentBlock, style, rect) {
  6445. var contentWidth = contentBlock.width;
  6446. var outerWidth = contentBlock.outerWidth;
  6447. var outerHeight = contentBlock.outerHeight;
  6448. var textPadding = style.textPadding;
  6449. var boxPos = getBoxPosition(outerHeight, style, rect);
  6450. var baseX = boxPos.baseX;
  6451. var baseY = boxPos.baseY;
  6452. var textAlign = boxPos.textAlign;
  6453. var textVerticalAlign = boxPos.textVerticalAlign;
  6454. // Origin of textRotation should be the base point of text drawing.
  6455. applyTextRotation(ctx, style, rect, baseX, baseY);
  6456. var boxX = adjustTextX(baseX, outerWidth, textAlign);
  6457. var boxY = adjustTextY(baseY, outerHeight, textVerticalAlign);
  6458. var xLeft = boxX;
  6459. var lineTop = boxY;
  6460. if (textPadding) {
  6461. xLeft += textPadding[3];
  6462. lineTop += textPadding[0];
  6463. }
  6464. var xRight = xLeft + contentWidth;
  6465. needDrawBackground(style) && drawBackground(
  6466. hostEl, ctx, style, boxX, boxY, outerWidth, outerHeight
  6467. );
  6468. for (var i = 0; i < contentBlock.lines.length; i++) {
  6469. var line = contentBlock.lines[i];
  6470. var tokens = line.tokens;
  6471. var tokenCount = tokens.length;
  6472. var lineHeight = line.lineHeight;
  6473. var usedWidth = line.width;
  6474. var leftIndex = 0;
  6475. var lineXLeft = xLeft;
  6476. var lineXRight = xRight;
  6477. var rightIndex = tokenCount - 1;
  6478. var token;
  6479. while (
  6480. leftIndex < tokenCount
  6481. && (token = tokens[leftIndex], !token.textAlign || token.textAlign === 'left')
  6482. ) {
  6483. placeToken(hostEl, ctx, token, style, lineHeight, lineTop, lineXLeft, 'left');
  6484. usedWidth -= token.width;
  6485. lineXLeft += token.width;
  6486. leftIndex++;
  6487. }
  6488. while (
  6489. rightIndex >= 0
  6490. && (token = tokens[rightIndex], token.textAlign === 'right')
  6491. ) {
  6492. placeToken(hostEl, ctx, token, style, lineHeight, lineTop, lineXRight, 'right');
  6493. usedWidth -= token.width;
  6494. lineXRight -= token.width;
  6495. rightIndex--;
  6496. }
  6497. // The other tokens are placed as textAlign 'center' if there is enough space.
  6498. lineXLeft += (contentWidth - (lineXLeft - xLeft) - (xRight - lineXRight) - usedWidth) / 2;
  6499. while (leftIndex <= rightIndex) {
  6500. token = tokens[leftIndex];
  6501. // Consider width specified by user, use 'center' rather than 'left'.
  6502. placeToken(hostEl, ctx, token, style, lineHeight, lineTop, lineXLeft + token.width / 2, 'center');
  6503. lineXLeft += token.width;
  6504. leftIndex++;
  6505. }
  6506. lineTop += lineHeight;
  6507. }
  6508. }
  6509. function applyTextRotation(ctx, style, rect, x, y) {
  6510. // textRotation only apply in RectText.
  6511. if (rect && style.textRotation) {
  6512. var origin = style.textOrigin;
  6513. if (origin === 'center') {
  6514. x = rect.width / 2 + rect.x;
  6515. y = rect.height / 2 + rect.y;
  6516. }
  6517. else if (origin) {
  6518. x = origin[0] + rect.x;
  6519. y = origin[1] + rect.y;
  6520. }
  6521. ctx.translate(x, y);
  6522. // Positive: anticlockwise
  6523. ctx.rotate(-style.textRotation);
  6524. ctx.translate(-x, -y);
  6525. }
  6526. }
  6527. function placeToken(hostEl, ctx, token, style, lineHeight, lineTop, x, textAlign) {
  6528. var tokenStyle = style.rich[token.styleName] || {};
  6529. // 'ctx.textBaseline' is always set as 'middle', for sake of
  6530. // the bias of "Microsoft YaHei".
  6531. var textVerticalAlign = token.textVerticalAlign;
  6532. var y = lineTop + lineHeight / 2;
  6533. if (textVerticalAlign === 'top') {
  6534. y = lineTop + token.height / 2;
  6535. }
  6536. else if (textVerticalAlign === 'bottom') {
  6537. y = lineTop + lineHeight - token.height / 2;
  6538. }
  6539. !token.isLineHolder && needDrawBackground(tokenStyle) && drawBackground(
  6540. hostEl,
  6541. ctx,
  6542. tokenStyle,
  6543. textAlign === 'right'
  6544. ? x - token.width
  6545. : textAlign === 'center'
  6546. ? x - token.width / 2
  6547. : x,
  6548. y - token.height / 2,
  6549. token.width,
  6550. token.height
  6551. );
  6552. var textPadding = token.textPadding;
  6553. if (textPadding) {
  6554. x = getTextXForPadding(x, textAlign, textPadding);
  6555. y -= token.height / 2 - textPadding[2] - token.textHeight / 2;
  6556. }
  6557. setCtx(ctx, 'shadowBlur', retrieve3(tokenStyle.textShadowBlur, style.textShadowBlur, 0));
  6558. setCtx(ctx, 'shadowColor', tokenStyle.textShadowColor || style.textShadowColor || 'transparent');
  6559. setCtx(ctx, 'shadowOffsetX', retrieve3(tokenStyle.textShadowOffsetX, style.textShadowOffsetX, 0));
  6560. setCtx(ctx, 'shadowOffsetY', retrieve3(tokenStyle.textShadowOffsetY, style.textShadowOffsetY, 0));
  6561. setCtx(ctx, 'textAlign', textAlign);
  6562. // Force baseline to be "middle". Otherwise, if using "top", the
  6563. // text will offset downward a little bit in font "Microsoft YaHei".
  6564. setCtx(ctx, 'textBaseline', 'middle');
  6565. setCtx(ctx, 'font', token.font || DEFAULT_FONT);
  6566. var textStroke = getStroke(tokenStyle.textStroke || style.textStroke, textStrokeWidth);
  6567. var textFill = getFill(tokenStyle.textFill || style.textFill);
  6568. var textStrokeWidth = retrieve2(tokenStyle.textStrokeWidth, style.textStrokeWidth);
  6569. // Fill after stroke so the outline will not cover the main part.
  6570. if (textStroke) {
  6571. setCtx(ctx, 'lineWidth', textStrokeWidth);
  6572. setCtx(ctx, 'strokeStyle', textStroke);
  6573. ctx.strokeText(token.text, x, y);
  6574. }
  6575. if (textFill) {
  6576. setCtx(ctx, 'fillStyle', textFill);
  6577. ctx.fillText(token.text, x, y);
  6578. }
  6579. }
  6580. function needDrawBackground(style) {
  6581. return style.textBackgroundColor
  6582. || (style.textBorderWidth && style.textBorderColor);
  6583. }
  6584. // style: {textBackgroundColor, textBorderWidth, textBorderColor, textBorderRadius}
  6585. // shape: {x, y, width, height}
  6586. function drawBackground(hostEl, ctx, style, x, y, width, height) {
  6587. var textBackgroundColor = style.textBackgroundColor;
  6588. var textBorderWidth = style.textBorderWidth;
  6589. var textBorderColor = style.textBorderColor;
  6590. var isPlainBg = isString(textBackgroundColor);
  6591. setCtx(ctx, 'shadowBlur', style.textBoxShadowBlur || 0);
  6592. setCtx(ctx, 'shadowColor', style.textBoxShadowColor || 'transparent');
  6593. setCtx(ctx, 'shadowOffsetX', style.textBoxShadowOffsetX || 0);
  6594. setCtx(ctx, 'shadowOffsetY', style.textBoxShadowOffsetY || 0);
  6595. if (isPlainBg || (textBorderWidth && textBorderColor)) {
  6596. ctx.beginPath();
  6597. var textBorderRadius = style.textBorderRadius;
  6598. if (!textBorderRadius) {
  6599. ctx.rect(x, y, width, height);
  6600. }
  6601. else {
  6602. buildPath(ctx, {
  6603. x: x, y: y, width: width, height: height, r: textBorderRadius
  6604. });
  6605. }
  6606. ctx.closePath();
  6607. }
  6608. if (isPlainBg) {
  6609. setCtx(ctx, 'fillStyle', textBackgroundColor);
  6610. ctx.fill();
  6611. }
  6612. else if (isObject(textBackgroundColor)) {
  6613. var image = textBackgroundColor.image;
  6614. image = createOrUpdateImage(
  6615. image, null, hostEl, onBgImageLoaded, textBackgroundColor
  6616. );
  6617. if (image && isImageReady(image)) {
  6618. ctx.drawImage(image, x, y, width, height);
  6619. }
  6620. }
  6621. if (textBorderWidth && textBorderColor) {
  6622. setCtx(ctx, 'lineWidth', textBorderWidth);
  6623. setCtx(ctx, 'strokeStyle', textBorderColor);
  6624. ctx.stroke();
  6625. }
  6626. }
  6627. function onBgImageLoaded(image, textBackgroundColor) {
  6628. // Replace image, so that `contain/text.js#parseRichText`
  6629. // will get correct result in next tick.
  6630. textBackgroundColor.image = image;
  6631. }
  6632. function getBoxPosition(blockHeiht, style, rect) {
  6633. var baseX = style.x || 0;
  6634. var baseY = style.y || 0;
  6635. var textAlign = style.textAlign;
  6636. var textVerticalAlign = style.textVerticalAlign;
  6637. // Text position represented by coord
  6638. if (rect) {
  6639. var textPosition = style.textPosition;
  6640. if (textPosition instanceof Array) {
  6641. // Percent
  6642. baseX = rect.x + parsePercent(textPosition[0], rect.width);
  6643. baseY = rect.y + parsePercent(textPosition[1], rect.height);
  6644. }
  6645. else {
  6646. var res = adjustTextPositionOnRect(
  6647. textPosition, rect, style.textDistance
  6648. );
  6649. baseX = res.x;
  6650. baseY = res.y;
  6651. // Default align and baseline when has textPosition
  6652. textAlign = textAlign || res.textAlign;
  6653. textVerticalAlign = textVerticalAlign || res.textVerticalAlign;
  6654. }
  6655. // textOffset is only support in RectText, otherwise
  6656. // we have to adjust boundingRect for textOffset.
  6657. var textOffset = style.textOffset;
  6658. if (textOffset) {
  6659. baseX += textOffset[0];
  6660. baseY += textOffset[1];
  6661. }
  6662. }
  6663. return {
  6664. baseX: baseX,
  6665. baseY: baseY,
  6666. textAlign: textAlign,
  6667. textVerticalAlign: textVerticalAlign
  6668. };
  6669. }
  6670. function setCtx(ctx, prop, value) {
  6671. // FIXME ??? performance try
  6672. // if (ctx.__currentValues[prop] !== value) {
  6673. // ctx[prop] = ctx.__currentValues[prop] = value;
  6674. ctx[prop] = value;
  6675. // }
  6676. return ctx[prop];
  6677. }
  6678. /**
  6679. * @param {string} [stroke] If specified, do not check style.textStroke.
  6680. * @param {string} [lineWidth] If specified, do not check style.textStroke.
  6681. * @param {number} style
  6682. */
  6683. function getStroke(stroke, lineWidth) {
  6684. return (stroke == null || lineWidth <= 0 || stroke === 'transparent' || stroke === 'none')
  6685. ? null
  6686. // TODO pattern and gradient?
  6687. : (stroke.image || stroke.colorStops)
  6688. ? '#000'
  6689. : stroke;
  6690. }
  6691. function getFill(fill) {
  6692. return (fill == null || fill === 'none')
  6693. ? null
  6694. // TODO pattern and gradient?
  6695. : (fill.image || fill.colorStops)
  6696. ? '#000'
  6697. : fill;
  6698. }
  6699. function parsePercent(value, maxValue) {
  6700. if (typeof value === 'string') {
  6701. if (value.lastIndexOf('%') >= 0) {
  6702. return parseFloat(value) / 100 * maxValue;
  6703. }
  6704. return parseFloat(value);
  6705. }
  6706. return value;
  6707. }
  6708. function getTextXForPadding(x, textAlign, textPadding) {
  6709. return textAlign === 'right'
  6710. ? (x - textPadding[1])
  6711. : textAlign === 'center'
  6712. ? (x + textPadding[3] / 2 - textPadding[1] / 2)
  6713. : (x + textPadding[3]);
  6714. }
  6715. /**
  6716. * @param {string} text
  6717. * @param {module:zrender/Style} style
  6718. * @return {boolean}
  6719. */
  6720. function needDrawText(text, style) {
  6721. return text != null
  6722. && (text
  6723. || style.textBackgroundColor
  6724. || (style.textBorderWidth && style.textBorderColor)
  6725. || style.textPadding
  6726. );
  6727. }
  6728. /**
  6729. * Mixin for drawing text in a element bounding rect
  6730. * @module zrender/mixin/RectText
  6731. */
  6732. var tmpRect$1 = new BoundingRect();
  6733. var RectText = function () {};
  6734. RectText.prototype = {
  6735. constructor: RectText,
  6736. /**
  6737. * Draw text in a rect with specified position.
  6738. * @param {CanvasRenderingContext2D} ctx
  6739. * @param {Object} rect Displayable rect
  6740. */
  6741. drawRectText: function (ctx, rect) {
  6742. var style = this.style;
  6743. rect = style.textRect || rect;
  6744. // Optimize, avoid normalize every time.
  6745. this.__dirty && normalizeTextStyle(style, true);
  6746. var text = style.text;
  6747. // Convert to string
  6748. text != null && (text += '');
  6749. if (!needDrawText(text, style)) {
  6750. return;
  6751. }
  6752. // FIXME
  6753. ctx.save();
  6754. // Transform rect to view space
  6755. var transform = this.transform;
  6756. if (!style.transformText) {
  6757. if (transform) {
  6758. tmpRect$1.copy(rect);
  6759. tmpRect$1.applyTransform(transform);
  6760. rect = tmpRect$1;
  6761. }
  6762. }
  6763. else {
  6764. this.setTransform(ctx);
  6765. }
  6766. // transformText and textRotation can not be used at the same time.
  6767. renderText(this, ctx, text, style, rect);
  6768. ctx.restore();
  6769. }
  6770. };
  6771. /**
  6772. * 可绘制的图形基类
  6773. * Base class of all displayable graphic objects
  6774. * @module zrender/graphic/Displayable
  6775. */
  6776. /**
  6777. * @alias module:zrender/graphic/Displayable
  6778. * @extends module:zrender/Element
  6779. * @extends module:zrender/graphic/mixin/RectText
  6780. */
  6781. function Displayable(opts) {
  6782. opts = opts || {};
  6783. Element.call(this, opts);
  6784. // Extend properties
  6785. for (var name in opts) {
  6786. if (
  6787. opts.hasOwnProperty(name) &&
  6788. name !== 'style'
  6789. ) {
  6790. this[name] = opts[name];
  6791. }
  6792. }
  6793. /**
  6794. * @type {module:zrender/graphic/Style}
  6795. */
  6796. this.style = new Style(opts.style, this);
  6797. this._rect = null;
  6798. // Shapes for cascade clipping.
  6799. this.__clipPaths = [];
  6800. // FIXME Stateful must be mixined after style is setted
  6801. // Stateful.call(this, opts);
  6802. }
  6803. Displayable.prototype = {
  6804. constructor: Displayable,
  6805. type: 'displayable',
  6806. /**
  6807. * Displayable 是否为脏,Painter 中会根据该标记判断是否需要是否需要重新绘制
  6808. * Dirty flag. From which painter will determine if this displayable object needs brush
  6809. * @name module:zrender/graphic/Displayable#__dirty
  6810. * @type {boolean}
  6811. */
  6812. __dirty: true,
  6813. /**
  6814. * 图形是否可见,为true时不绘制图形,但是仍能触发鼠标事件
  6815. * If ignore drawing of the displayable object. Mouse event will still be triggered
  6816. * @name module:/zrender/graphic/Displayable#invisible
  6817. * @type {boolean}
  6818. * @default false
  6819. */
  6820. invisible: false,
  6821. /**
  6822. * @name module:/zrender/graphic/Displayable#z
  6823. * @type {number}
  6824. * @default 0
  6825. */
  6826. z: 0,
  6827. /**
  6828. * @name module:/zrender/graphic/Displayable#z
  6829. * @type {number}
  6830. * @default 0
  6831. */
  6832. z2: 0,
  6833. /**
  6834. * z层level,决定绘画在哪层canvas中
  6835. * @name module:/zrender/graphic/Displayable#zlevel
  6836. * @type {number}
  6837. * @default 0
  6838. */
  6839. zlevel: 0,
  6840. /**
  6841. * 是否可拖拽
  6842. * @name module:/zrender/graphic/Displayable#draggable
  6843. * @type {boolean}
  6844. * @default false
  6845. */
  6846. draggable: false,
  6847. /**
  6848. * 是否正在拖拽
  6849. * @name module:/zrender/graphic/Displayable#draggable
  6850. * @type {boolean}
  6851. * @default false
  6852. */
  6853. dragging: false,
  6854. /**
  6855. * 是否相应鼠标事件
  6856. * @name module:/zrender/graphic/Displayable#silent
  6857. * @type {boolean}
  6858. * @default false
  6859. */
  6860. silent: false,
  6861. /**
  6862. * If enable culling
  6863. * @type {boolean}
  6864. * @default false
  6865. */
  6866. culling: false,
  6867. /**
  6868. * Mouse cursor when hovered
  6869. * @name module:/zrender/graphic/Displayable#cursor
  6870. * @type {string}
  6871. */
  6872. cursor: 'pointer',
  6873. /**
  6874. * If hover area is bounding rect
  6875. * @name module:/zrender/graphic/Displayable#rectHover
  6876. * @type {string}
  6877. */
  6878. rectHover: false,
  6879. /**
  6880. * Render the element progressively when the value >= 0,
  6881. * usefull for large data.
  6882. * @type {number}
  6883. */
  6884. progressive: -1,
  6885. beforeBrush: function (ctx) {},
  6886. afterBrush: function (ctx) {},
  6887. /**
  6888. * 图形绘制方法
  6889. * @param {CanvasRenderingContext2D} ctx
  6890. */
  6891. // Interface
  6892. brush: function (ctx, prevEl) {},
  6893. /**
  6894. * 获取最小包围盒
  6895. * @return {module:zrender/core/BoundingRect}
  6896. */
  6897. // Interface
  6898. getBoundingRect: function () {},
  6899. /**
  6900. * 判断坐标 x, y 是否在图形上
  6901. * If displayable element contain coord x, y
  6902. * @param {number} x
  6903. * @param {number} y
  6904. * @return {boolean}
  6905. */
  6906. contain: function (x, y) {
  6907. return this.rectContain(x, y);
  6908. },
  6909. /**
  6910. * @param {Function} cb
  6911. * @param {} context
  6912. */
  6913. traverse: function (cb, context) {
  6914. cb.call(context, this);
  6915. },
  6916. /**
  6917. * 判断坐标 x, y 是否在图形的包围盒上
  6918. * If bounding rect of element contain coord x, y
  6919. * @param {number} x
  6920. * @param {number} y
  6921. * @return {boolean}
  6922. */
  6923. rectContain: function (x, y) {
  6924. var coord = this.transformCoordToLocal(x, y);
  6925. var rect = this.getBoundingRect();
  6926. return rect.contain(coord[0], coord[1]);
  6927. },
  6928. /**
  6929. * 标记图形元素为脏,并且在下一帧重绘
  6930. * Mark displayable element dirty and refresh next frame
  6931. */
  6932. dirty: function () {
  6933. this.__dirty = true;
  6934. this._rect = null;
  6935. this.__zr && this.__zr.refresh();
  6936. },
  6937. /**
  6938. * 图形是否会触发事件
  6939. * If displayable object binded any event
  6940. * @return {boolean}
  6941. */
  6942. // TODO, 通过 bind 绑定的事件
  6943. // isSilent: function () {
  6944. // return !(
  6945. // this.hoverable || this.draggable
  6946. // || this.onmousemove || this.onmouseover || this.onmouseout
  6947. // || this.onmousedown || this.onmouseup || this.onclick
  6948. // || this.ondragenter || this.ondragover || this.ondragleave
  6949. // || this.ondrop
  6950. // );
  6951. // },
  6952. /**
  6953. * Alias for animate('style')
  6954. * @param {boolean} loop
  6955. */
  6956. animateStyle: function (loop) {
  6957. return this.animate('style', loop);
  6958. },
  6959. attrKV: function (key, value) {
  6960. if (key !== 'style') {
  6961. Element.prototype.attrKV.call(this, key, value);
  6962. }
  6963. else {
  6964. this.style.set(value);
  6965. }
  6966. },
  6967. /**
  6968. * @param {Object|string} key
  6969. * @param {*} value
  6970. */
  6971. setStyle: function (key, value) {
  6972. this.style.set(key, value);
  6973. this.dirty(false);
  6974. return this;
  6975. },
  6976. /**
  6977. * Use given style object
  6978. * @param {Object} obj
  6979. */
  6980. useStyle: function (obj) {
  6981. this.style = new Style(obj, this);
  6982. this.dirty(false);
  6983. return this;
  6984. }
  6985. };
  6986. inherits(Displayable, Element);
  6987. mixin(Displayable, RectText);
  6988. /**
  6989. * @alias zrender/graphic/Image
  6990. * @extends module:zrender/graphic/Displayable
  6991. * @constructor
  6992. * @param {Object} opts
  6993. */
  6994. function ZImage(opts) {
  6995. Displayable.call(this, opts);
  6996. }
  6997. ZImage.prototype = {
  6998. constructor: ZImage,
  6999. type: 'image',
  7000. brush: function (ctx, prevEl) {
  7001. var style = this.style;
  7002. var src = style.image;
  7003. // Must bind each time
  7004. style.bind(ctx, this, prevEl);
  7005. var image = this._image = createOrUpdateImage(
  7006. src,
  7007. this._image,
  7008. this,
  7009. this.onload
  7010. );
  7011. if (!image || !isImageReady(image)) {
  7012. return;
  7013. }
  7014. // 图片已经加载完成
  7015. // if (image.nodeName.toUpperCase() == 'IMG') {
  7016. // if (!image.complete) {
  7017. // return;
  7018. // }
  7019. // }
  7020. // Else is canvas
  7021. var x = style.x || 0;
  7022. var y = style.y || 0;
  7023. var width = style.width;
  7024. var height = style.height;
  7025. var aspect = image.width / image.height;
  7026. if (width == null && height != null) {
  7027. // Keep image/height ratio
  7028. width = height * aspect;
  7029. }
  7030. else if (height == null && width != null) {
  7031. height = width / aspect;
  7032. }
  7033. else if (width == null && height == null) {
  7034. width = image.width;
  7035. height = image.height;
  7036. }
  7037. // 设置transform
  7038. this.setTransform(ctx);
  7039. if (style.sWidth && style.sHeight) {
  7040. var sx = style.sx || 0;
  7041. var sy = style.sy || 0;
  7042. ctx.drawImage(
  7043. image,
  7044. sx, sy, style.sWidth, style.sHeight,
  7045. x, y, width, height
  7046. );
  7047. }
  7048. else if (style.sx && style.sy) {
  7049. var sx = style.sx;
  7050. var sy = style.sy;
  7051. var sWidth = width - sx;
  7052. var sHeight = height - sy;
  7053. ctx.drawImage(
  7054. image,
  7055. sx, sy, sWidth, sHeight,
  7056. x, y, width, height
  7057. );
  7058. }
  7059. else {
  7060. ctx.drawImage(image, x, y, width, height);
  7061. }
  7062. this.restoreTransform(ctx);
  7063. // Draw rect text
  7064. if (style.text != null) {
  7065. this.drawRectText(ctx, this.getBoundingRect());
  7066. }
  7067. },
  7068. getBoundingRect: function () {
  7069. var style = this.style;
  7070. if (! this._rect) {
  7071. this._rect = new BoundingRect(
  7072. style.x || 0, style.y || 0, style.width || 0, style.height || 0
  7073. );
  7074. }
  7075. return this._rect;
  7076. }
  7077. };
  7078. inherits(ZImage, Displayable);
  7079. /**
  7080. * Default canvas painter
  7081. * @module zrender/Painter
  7082. * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
  7083. * errorrik (errorrik@gmail.com)
  7084. * pissang (https://www.github.com/pissang)
  7085. */
  7086. // PENDIGN
  7087. // Layer exceeds MAX_PROGRESSIVE_LAYER_NUMBER may have some problem when flush directly second time.
  7088. //
  7089. // Maximum progressive layer. When exceeding this number. All elements will be drawed in the last layer.
  7090. var MAX_PROGRESSIVE_LAYER_NUMBER = 5;
  7091. function parseInt10(val) {
  7092. return parseInt(val, 10);
  7093. }
  7094. function isLayerValid(layer) {
  7095. if (!layer) {
  7096. return false;
  7097. }
  7098. if (layer.__builtin__) {
  7099. return true;
  7100. }
  7101. if (typeof(layer.resize) !== 'function'
  7102. || typeof(layer.refresh) !== 'function'
  7103. ) {
  7104. return false;
  7105. }
  7106. return true;
  7107. }
  7108. function preProcessLayer(layer) {
  7109. layer.__unusedCount++;
  7110. }
  7111. function postProcessLayer(layer) {
  7112. if (layer.__unusedCount == 1) {
  7113. layer.clear();
  7114. }
  7115. }
  7116. var tmpRect = new BoundingRect(0, 0, 0, 0);
  7117. var viewRect = new BoundingRect(0, 0, 0, 0);
  7118. function isDisplayableCulled(el, width, height) {
  7119. tmpRect.copy(el.getBoundingRect());
  7120. if (el.transform) {
  7121. tmpRect.applyTransform(el.transform);
  7122. }
  7123. viewRect.width = width;
  7124. viewRect.height = height;
  7125. return !tmpRect.intersect(viewRect);
  7126. }
  7127. function isClipPathChanged(clipPaths, prevClipPaths) {
  7128. if (clipPaths == prevClipPaths) { // Can both be null or undefined
  7129. return false;
  7130. }
  7131. if (!clipPaths || !prevClipPaths || (clipPaths.length !== prevClipPaths.length)) {
  7132. return true;
  7133. }
  7134. for (var i = 0; i < clipPaths.length; i++) {
  7135. if (clipPaths[i] !== prevClipPaths[i]) {
  7136. return true;
  7137. }
  7138. }
  7139. }
  7140. function doClip(clipPaths, ctx) {
  7141. for (var i = 0; i < clipPaths.length; i++) {
  7142. var clipPath = clipPaths[i];
  7143. clipPath.setTransform(ctx);
  7144. ctx.beginPath();
  7145. clipPath.buildPath(ctx, clipPath.shape);
  7146. ctx.clip();
  7147. // Transform back
  7148. clipPath.restoreTransform(ctx);
  7149. }
  7150. }
  7151. function createRoot(width, height) {
  7152. var domRoot = document.createElement('div');
  7153. // domRoot.onselectstart = returnFalse; // 避免页面选中的尴尬
  7154. domRoot.style.cssText = [
  7155. 'position:relative',
  7156. 'overflow:hidden',
  7157. 'width:' + width + 'px',
  7158. 'height:' + height + 'px',
  7159. 'padding:0',
  7160. 'margin:0',
  7161. 'border-width:0'
  7162. ].join(';') + ';';
  7163. return domRoot;
  7164. }
  7165. /**
  7166. * @alias module:zrender/Painter
  7167. * @constructor
  7168. * @param {HTMLElement} root 绘图容器
  7169. * @param {module:zrender/Storage} storage
  7170. * @param {Object} opts
  7171. */
  7172. var Painter = function (root, storage, opts) {
  7173. this.type = 'canvas';
  7174. // In node environment using node-canvas
  7175. var singleCanvas = !root.nodeName // In node ?
  7176. || root.nodeName.toUpperCase() === 'CANVAS';
  7177. this._opts = opts = extend({}, opts || {});
  7178. /**
  7179. * @type {number}
  7180. */
  7181. this.dpr = opts.devicePixelRatio || devicePixelRatio;
  7182. /**
  7183. * @type {boolean}
  7184. * @private
  7185. */
  7186. this._singleCanvas = singleCanvas;
  7187. /**
  7188. * 绘图容器
  7189. * @type {HTMLElement}
  7190. */
  7191. this.root = root;
  7192. var rootStyle = root.style;
  7193. if (rootStyle) {
  7194. rootStyle['-webkit-tap-highlight-color'] = 'transparent';
  7195. rootStyle['-webkit-user-select'] =
  7196. rootStyle['user-select'] =
  7197. rootStyle['-webkit-touch-callout'] = 'none';
  7198. root.innerHTML = '';
  7199. }
  7200. /**
  7201. * @type {module:zrender/Storage}
  7202. */
  7203. this.storage = storage;
  7204. /**
  7205. * @type {Array.<number>}
  7206. * @private
  7207. */
  7208. var zlevelList = this._zlevelList = [];
  7209. /**
  7210. * @type {Object.<string, module:zrender/Layer>}
  7211. * @private
  7212. */
  7213. var layers = this._layers = {};
  7214. /**
  7215. * @type {Object.<string, Object>}
  7216. * @type {private}
  7217. */
  7218. this._layerConfig = {};
  7219. if (!singleCanvas) {
  7220. this._width = this._getSize(0);
  7221. this._height = this._getSize(1);
  7222. var domRoot = this._domRoot = createRoot(
  7223. this._width, this._height
  7224. );
  7225. root.appendChild(domRoot);
  7226. }
  7227. else {
  7228. if (opts.width != null) {
  7229. root.width = opts.width;
  7230. }
  7231. if (opts.height != null) {
  7232. root.height = opts.height;
  7233. }
  7234. // Use canvas width and height directly
  7235. var width = root.width;
  7236. var height = root.height;
  7237. this._width = width;
  7238. this._height = height;
  7239. // Create layer if only one given canvas
  7240. // Device pixel ratio is fixed to 1 because given canvas has its specified width and height
  7241. var mainLayer = new Layer(root, this, 1);
  7242. mainLayer.initContext();
  7243. // FIXME Use canvas width and height
  7244. // mainLayer.resize(width, height);
  7245. layers[0] = mainLayer;
  7246. zlevelList.push(0);
  7247. this._domRoot = root;
  7248. }
  7249. // Layers for progressive rendering
  7250. this._progressiveLayers = [];
  7251. /**
  7252. * @type {module:zrender/Layer}
  7253. * @private
  7254. */
  7255. this._hoverlayer;
  7256. this._hoverElements = [];
  7257. };
  7258. Painter.prototype = {
  7259. constructor: Painter,
  7260. getType: function () {
  7261. return 'canvas';
  7262. },
  7263. /**
  7264. * If painter use a single canvas
  7265. * @return {boolean}
  7266. */
  7267. isSingleCanvas: function () {
  7268. return this._singleCanvas;
  7269. },
  7270. /**
  7271. * @return {HTMLDivElement}
  7272. */
  7273. getViewportRoot: function () {
  7274. return this._domRoot;
  7275. },
  7276. getViewportRootOffset: function () {
  7277. var viewportRoot = this.getViewportRoot();
  7278. if (viewportRoot) {
  7279. return {
  7280. offsetLeft: viewportRoot.offsetLeft || 0,
  7281. offsetTop: viewportRoot.offsetTop || 0
  7282. };
  7283. }
  7284. },
  7285. /**
  7286. * 刷新
  7287. * @param {boolean} [paintAll=false] 强制绘制所有displayable
  7288. */
  7289. refresh: function (paintAll) {
  7290. var list = this.storage.getDisplayList(true);
  7291. var zlevelList = this._zlevelList;
  7292. this._paintList(list, paintAll);
  7293. // Paint custum layers
  7294. for (var i = 0; i < zlevelList.length; i++) {
  7295. var z = zlevelList[i];
  7296. var layer = this._layers[z];
  7297. if (!layer.__builtin__ && layer.refresh) {
  7298. layer.refresh();
  7299. }
  7300. }
  7301. this.refreshHover();
  7302. if (this._progressiveLayers.length) {
  7303. this._startProgessive();
  7304. }
  7305. return this;
  7306. },
  7307. addHover: function (el, hoverStyle) {
  7308. if (el.__hoverMir) {
  7309. return;
  7310. }
  7311. var elMirror = new el.constructor({
  7312. style: el.style,
  7313. shape: el.shape
  7314. });
  7315. elMirror.__from = el;
  7316. el.__hoverMir = elMirror;
  7317. elMirror.setStyle(hoverStyle);
  7318. this._hoverElements.push(elMirror);
  7319. },
  7320. removeHover: function (el) {
  7321. var elMirror = el.__hoverMir;
  7322. var hoverElements = this._hoverElements;
  7323. var idx = indexOf(hoverElements, elMirror);
  7324. if (idx >= 0) {
  7325. hoverElements.splice(idx, 1);
  7326. }
  7327. el.__hoverMir = null;
  7328. },
  7329. clearHover: function (el) {
  7330. var hoverElements = this._hoverElements;
  7331. for (var i = 0; i < hoverElements.length; i++) {
  7332. var from = hoverElements[i].__from;
  7333. if (from) {
  7334. from.__hoverMir = null;
  7335. }
  7336. }
  7337. hoverElements.length = 0;
  7338. },
  7339. refreshHover: function () {
  7340. var hoverElements = this._hoverElements;
  7341. var len = hoverElements.length;
  7342. var hoverLayer = this._hoverlayer;
  7343. hoverLayer && hoverLayer.clear();
  7344. if (!len) {
  7345. return;
  7346. }
  7347. sort(hoverElements, this.storage.displayableSortFunc);
  7348. // Use a extream large zlevel
  7349. // FIXME?
  7350. if (!hoverLayer) {
  7351. hoverLayer = this._hoverlayer = this.getLayer(1e5);
  7352. }
  7353. var scope = {};
  7354. hoverLayer.ctx.save();
  7355. for (var i = 0; i < len;) {
  7356. var el = hoverElements[i];
  7357. var originalEl = el.__from;
  7358. // Original el is removed
  7359. // PENDING
  7360. if (!(originalEl && originalEl.__zr)) {
  7361. hoverElements.splice(i, 1);
  7362. originalEl.__hoverMir = null;
  7363. len--;
  7364. continue;
  7365. }
  7366. i++;
  7367. // Use transform
  7368. // FIXME style and shape ?
  7369. if (!originalEl.invisible) {
  7370. el.transform = originalEl.transform;
  7371. el.invTransform = originalEl.invTransform;
  7372. el.__clipPaths = originalEl.__clipPaths;
  7373. // el.
  7374. this._doPaintEl(el, hoverLayer, true, scope);
  7375. }
  7376. }
  7377. hoverLayer.ctx.restore();
  7378. },
  7379. _startProgessive: function () {
  7380. var self = this;
  7381. if (!self._furtherProgressive) {
  7382. return;
  7383. }
  7384. // Use a token to stop progress steps triggered by
  7385. // previous zr.refresh calling.
  7386. var token = self._progressiveToken = +new Date();
  7387. self._progress++;
  7388. requestAnimationFrame(step);
  7389. function step() {
  7390. // In case refreshed or disposed
  7391. if (token === self._progressiveToken && self.storage) {
  7392. self._doPaintList(self.storage.getDisplayList());
  7393. if (self._furtherProgressive) {
  7394. self._progress++;
  7395. requestAnimationFrame(step);
  7396. }
  7397. else {
  7398. self._progressiveToken = -1;
  7399. }
  7400. }
  7401. }
  7402. },
  7403. _clearProgressive: function () {
  7404. this._progressiveToken = -1;
  7405. this._progress = 0;
  7406. each$1(this._progressiveLayers, function (layer) {
  7407. layer.__dirty && layer.clear();
  7408. });
  7409. },
  7410. _paintList: function (list, paintAll) {
  7411. if (paintAll == null) {
  7412. paintAll = false;
  7413. }
  7414. this._updateLayerStatus(list);
  7415. this._clearProgressive();
  7416. this.eachBuiltinLayer(preProcessLayer);
  7417. this._doPaintList(list, paintAll);
  7418. this.eachBuiltinLayer(postProcessLayer);
  7419. },
  7420. _doPaintList: function (list, paintAll) {
  7421. var currentLayer;
  7422. var currentZLevel;
  7423. var ctx;
  7424. // var invTransform = [];
  7425. var scope;
  7426. var progressiveLayerIdx = 0;
  7427. var currentProgressiveLayer;
  7428. var width = this._width;
  7429. var height = this._height;
  7430. var layerProgress;
  7431. var frame = this._progress;
  7432. function flushProgressiveLayer(layer) {
  7433. var dpr = ctx.dpr || 1;
  7434. ctx.save();
  7435. ctx.globalAlpha = 1;
  7436. ctx.shadowBlur = 0;
  7437. // Avoid layer don't clear in next progressive frame
  7438. currentLayer.__dirty = true;
  7439. ctx.setTransform(1, 0, 0, 1, 0, 0);
  7440. ctx.drawImage(layer.dom, 0, 0, width * dpr, height * dpr);
  7441. ctx.restore();
  7442. }
  7443. for (var i = 0, l = list.length; i < l; i++) {
  7444. var el = list[i];
  7445. var elZLevel = this._singleCanvas ? 0 : el.zlevel;
  7446. var elFrame = el.__frame;
  7447. // Flush at current context
  7448. // PENDING
  7449. if (elFrame < 0 && currentProgressiveLayer) {
  7450. flushProgressiveLayer(currentProgressiveLayer);
  7451. currentProgressiveLayer = null;
  7452. }
  7453. // Change draw layer
  7454. if (currentZLevel !== elZLevel) {
  7455. if (ctx) {
  7456. ctx.restore();
  7457. }
  7458. // Reset scope
  7459. scope = {};
  7460. // Only 0 zlevel if only has one canvas
  7461. currentZLevel = elZLevel;
  7462. currentLayer = this.getLayer(currentZLevel);
  7463. if (!currentLayer.__builtin__) {
  7464. log$1(
  7465. 'ZLevel ' + currentZLevel
  7466. + ' has been used by unkown layer ' + currentLayer.id
  7467. );
  7468. }
  7469. ctx = currentLayer.ctx;
  7470. ctx.save();
  7471. // Reset the count
  7472. currentLayer.__unusedCount = 0;
  7473. if (currentLayer.__dirty || paintAll) {
  7474. currentLayer.clear();
  7475. }
  7476. }
  7477. if (!(currentLayer.__dirty || paintAll)) {
  7478. continue;
  7479. }
  7480. if (elFrame >= 0) {
  7481. // Progressive layer changed
  7482. if (!currentProgressiveLayer) {
  7483. currentProgressiveLayer = this._progressiveLayers[
  7484. Math.min(progressiveLayerIdx++, MAX_PROGRESSIVE_LAYER_NUMBER - 1)
  7485. ];
  7486. currentProgressiveLayer.ctx.save();
  7487. currentProgressiveLayer.renderScope = {};
  7488. if (currentProgressiveLayer
  7489. && (currentProgressiveLayer.__progress > currentProgressiveLayer.__maxProgress)
  7490. ) {
  7491. // flushProgressiveLayer(currentProgressiveLayer);
  7492. // Quick jump all progressive elements
  7493. // All progressive element are not dirty, jump over and flush directly
  7494. i = currentProgressiveLayer.__nextIdxNotProg - 1;
  7495. // currentProgressiveLayer = null;
  7496. continue;
  7497. }
  7498. layerProgress = currentProgressiveLayer.__progress;
  7499. if (!currentProgressiveLayer.__dirty) {
  7500. // Keep rendering
  7501. frame = layerProgress;
  7502. }
  7503. currentProgressiveLayer.__progress = frame + 1;
  7504. }
  7505. if (elFrame === frame) {
  7506. this._doPaintEl(el, currentProgressiveLayer, true, currentProgressiveLayer.renderScope);
  7507. }
  7508. }
  7509. else {
  7510. this._doPaintEl(el, currentLayer, paintAll, scope);
  7511. }
  7512. el.__dirty = false;
  7513. }
  7514. if (currentProgressiveLayer) {
  7515. flushProgressiveLayer(currentProgressiveLayer);
  7516. }
  7517. // Restore the lastLayer ctx
  7518. ctx && ctx.restore();
  7519. // If still has clipping state
  7520. // if (scope.prevElClipPaths) {
  7521. // ctx.restore();
  7522. // }
  7523. this._furtherProgressive = false;
  7524. each$1(this._progressiveLayers, function (layer) {
  7525. if (layer.__maxProgress >= layer.__progress) {
  7526. this._furtherProgressive = true;
  7527. }
  7528. }, this);
  7529. },
  7530. _doPaintEl: function (el, currentLayer, forcePaint, scope) {
  7531. var ctx = currentLayer.ctx;
  7532. var m = el.transform;
  7533. if (
  7534. (currentLayer.__dirty || forcePaint)
  7535. // Ignore invisible element
  7536. && !el.invisible
  7537. // Ignore transparent element
  7538. && el.style.opacity !== 0
  7539. // Ignore scale 0 element, in some environment like node-canvas
  7540. // Draw a scale 0 element can cause all following draw wrong
  7541. // And setTransform with scale 0 will cause set back transform failed.
  7542. && !(m && !m[0] && !m[3])
  7543. // Ignore culled element
  7544. && !(el.culling && isDisplayableCulled(el, this._width, this._height))
  7545. ) {
  7546. var clipPaths = el.__clipPaths;
  7547. // Optimize when clipping on group with several elements
  7548. if (scope.prevClipLayer !== currentLayer
  7549. || isClipPathChanged(clipPaths, scope.prevElClipPaths)
  7550. ) {
  7551. // If has previous clipping state, restore from it
  7552. if (scope.prevElClipPaths) {
  7553. scope.prevClipLayer.ctx.restore();
  7554. scope.prevClipLayer = scope.prevElClipPaths = null;
  7555. // Reset prevEl since context has been restored
  7556. scope.prevEl = null;
  7557. }
  7558. // New clipping state
  7559. if (clipPaths) {
  7560. ctx.save();
  7561. doClip(clipPaths, ctx);
  7562. scope.prevClipLayer = currentLayer;
  7563. scope.prevElClipPaths = clipPaths;
  7564. }
  7565. }
  7566. el.beforeBrush && el.beforeBrush(ctx);
  7567. el.brush(ctx, scope.prevEl || null);
  7568. scope.prevEl = el;
  7569. el.afterBrush && el.afterBrush(ctx);
  7570. }
  7571. },
  7572. /**
  7573. * 获取 zlevel 所在层,如果不存在则会创建一个新的层
  7574. * @param {number} zlevel
  7575. * @return {module:zrender/Layer}
  7576. */
  7577. getLayer: function (zlevel) {
  7578. if (this._singleCanvas) {
  7579. return this._layers[0];
  7580. }
  7581. var layer = this._layers[zlevel];
  7582. if (!layer) {
  7583. // Create a new layer
  7584. layer = new Layer('zr_' + zlevel, this, this.dpr);
  7585. layer.__builtin__ = true;
  7586. if (this._layerConfig[zlevel]) {
  7587. merge(layer, this._layerConfig[zlevel], true);
  7588. }
  7589. this.insertLayer(zlevel, layer);
  7590. // Context is created after dom inserted to document
  7591. // Or excanvas will get 0px clientWidth and clientHeight
  7592. layer.initContext();
  7593. }
  7594. return layer;
  7595. },
  7596. insertLayer: function (zlevel, layer) {
  7597. var layersMap = this._layers;
  7598. var zlevelList = this._zlevelList;
  7599. var len = zlevelList.length;
  7600. var prevLayer = null;
  7601. var i = -1;
  7602. var domRoot = this._domRoot;
  7603. if (layersMap[zlevel]) {
  7604. log$1('ZLevel ' + zlevel + ' has been used already');
  7605. return;
  7606. }
  7607. // Check if is a valid layer
  7608. if (!isLayerValid(layer)) {
  7609. log$1('Layer of zlevel ' + zlevel + ' is not valid');
  7610. return;
  7611. }
  7612. if (len > 0 && zlevel > zlevelList[0]) {
  7613. for (i = 0; i < len - 1; i++) {
  7614. if (
  7615. zlevelList[i] < zlevel
  7616. && zlevelList[i + 1] > zlevel
  7617. ) {
  7618. break;
  7619. }
  7620. }
  7621. prevLayer = layersMap[zlevelList[i]];
  7622. }
  7623. zlevelList.splice(i + 1, 0, zlevel);
  7624. layersMap[zlevel] = layer;
  7625. // Vitual layer will not directly show on the screen.
  7626. // (It can be a WebGL layer and assigned to a ZImage element)
  7627. // But it still under management of zrender.
  7628. if (!layer.virtual) {
  7629. if (prevLayer) {
  7630. var prevDom = prevLayer.dom;
  7631. if (prevDom.nextSibling) {
  7632. domRoot.insertBefore(
  7633. layer.dom,
  7634. prevDom.nextSibling
  7635. );
  7636. }
  7637. else {
  7638. domRoot.appendChild(layer.dom);
  7639. }
  7640. }
  7641. else {
  7642. if (domRoot.firstChild) {
  7643. domRoot.insertBefore(layer.dom, domRoot.firstChild);
  7644. }
  7645. else {
  7646. domRoot.appendChild(layer.dom);
  7647. }
  7648. }
  7649. }
  7650. },
  7651. // Iterate each layer
  7652. eachLayer: function (cb, context) {
  7653. var zlevelList = this._zlevelList;
  7654. var z;
  7655. var i;
  7656. for (i = 0; i < zlevelList.length; i++) {
  7657. z = zlevelList[i];
  7658. cb.call(context, this._layers[z], z);
  7659. }
  7660. },
  7661. // Iterate each buildin layer
  7662. eachBuiltinLayer: function (cb, context) {
  7663. var zlevelList = this._zlevelList;
  7664. var layer;
  7665. var z;
  7666. var i;
  7667. for (i = 0; i < zlevelList.length; i++) {
  7668. z = zlevelList[i];
  7669. layer = this._layers[z];
  7670. if (layer.__builtin__) {
  7671. cb.call(context, layer, z);
  7672. }
  7673. }
  7674. },
  7675. // Iterate each other layer except buildin layer
  7676. eachOtherLayer: function (cb, context) {
  7677. var zlevelList = this._zlevelList;
  7678. var layer;
  7679. var z;
  7680. var i;
  7681. for (i = 0; i < zlevelList.length; i++) {
  7682. z = zlevelList[i];
  7683. layer = this._layers[z];
  7684. if (!layer.__builtin__) {
  7685. cb.call(context, layer, z);
  7686. }
  7687. }
  7688. },
  7689. /**
  7690. * 获取所有已创建的层
  7691. * @param {Array.<module:zrender/Layer>} [prevLayer]
  7692. */
  7693. getLayers: function () {
  7694. return this._layers;
  7695. },
  7696. _updateLayerStatus: function (list) {
  7697. var layers = this._layers;
  7698. var progressiveLayers = this._progressiveLayers;
  7699. var elCountsLastFrame = {};
  7700. var progressiveElCountsLastFrame = {};
  7701. this.eachBuiltinLayer(function (layer, z) {
  7702. elCountsLastFrame[z] = layer.elCount;
  7703. layer.elCount = 0;
  7704. layer.__dirty = false;
  7705. });
  7706. each$1(progressiveLayers, function (layer, idx) {
  7707. progressiveElCountsLastFrame[idx] = layer.elCount;
  7708. layer.elCount = 0;
  7709. layer.__dirty = false;
  7710. });
  7711. var progressiveLayerCount = 0;
  7712. var currentProgressiveLayer;
  7713. var lastProgressiveKey;
  7714. var frameCount = 0;
  7715. for (var i = 0, l = list.length; i < l; i++) {
  7716. var el = list[i];
  7717. var zlevel = this._singleCanvas ? 0 : el.zlevel;
  7718. var layer = layers[zlevel];
  7719. var elProgress = el.progressive;
  7720. if (layer) {
  7721. layer.elCount++;
  7722. layer.__dirty = layer.__dirty || el.__dirty;
  7723. }
  7724. /////// Update progressive
  7725. if (elProgress >= 0) {
  7726. // Fix wrong progressive sequence problem.
  7727. if (lastProgressiveKey !== elProgress) {
  7728. lastProgressiveKey = elProgress;
  7729. frameCount++;
  7730. }
  7731. var elFrame = el.__frame = frameCount - 1;
  7732. if (!currentProgressiveLayer) {
  7733. var idx = Math.min(progressiveLayerCount, MAX_PROGRESSIVE_LAYER_NUMBER - 1);
  7734. currentProgressiveLayer = progressiveLayers[idx];
  7735. if (!currentProgressiveLayer) {
  7736. currentProgressiveLayer = progressiveLayers[idx] = new Layer(
  7737. 'progressive', this, this.dpr
  7738. );
  7739. currentProgressiveLayer.initContext();
  7740. }
  7741. currentProgressiveLayer.__maxProgress = 0;
  7742. }
  7743. currentProgressiveLayer.__dirty = currentProgressiveLayer.__dirty || el.__dirty;
  7744. currentProgressiveLayer.elCount++;
  7745. currentProgressiveLayer.__maxProgress = Math.max(
  7746. currentProgressiveLayer.__maxProgress, elFrame
  7747. );
  7748. if (currentProgressiveLayer.__maxProgress >= currentProgressiveLayer.__progress) {
  7749. // Should keep rendering this layer because progressive rendering is not finished yet
  7750. layer.__dirty = true;
  7751. }
  7752. }
  7753. else {
  7754. el.__frame = -1;
  7755. if (currentProgressiveLayer) {
  7756. currentProgressiveLayer.__nextIdxNotProg = i;
  7757. progressiveLayerCount++;
  7758. currentProgressiveLayer = null;
  7759. }
  7760. }
  7761. }
  7762. if (currentProgressiveLayer) {
  7763. progressiveLayerCount++;
  7764. currentProgressiveLayer.__nextIdxNotProg = i;
  7765. }
  7766. // 层中的元素数量有发生变化
  7767. this.eachBuiltinLayer(function (layer, z) {
  7768. if (elCountsLastFrame[z] !== layer.elCount) {
  7769. layer.__dirty = true;
  7770. }
  7771. });
  7772. progressiveLayers.length = Math.min(progressiveLayerCount, MAX_PROGRESSIVE_LAYER_NUMBER);
  7773. each$1(progressiveLayers, function (layer, idx) {
  7774. if (progressiveElCountsLastFrame[idx] !== layer.elCount) {
  7775. el.__dirty = true;
  7776. }
  7777. if (layer.__dirty) {
  7778. layer.__progress = 0;
  7779. }
  7780. });
  7781. },
  7782. /**
  7783. * 清除hover层外所有内容
  7784. */
  7785. clear: function () {
  7786. this.eachBuiltinLayer(this._clearLayer);
  7787. return this;
  7788. },
  7789. _clearLayer: function (layer) {
  7790. layer.clear();
  7791. },
  7792. /**
  7793. * 修改指定zlevel的绘制参数
  7794. *
  7795. * @param {string} zlevel
  7796. * @param {Object} config 配置对象
  7797. * @param {string} [config.clearColor=0] 每次清空画布的颜色
  7798. * @param {string} [config.motionBlur=false] 是否开启动态模糊
  7799. * @param {number} [config.lastFrameAlpha=0.7]
  7800. * 在开启动态模糊的时候使用,与上一帧混合的alpha值,值越大尾迹越明显
  7801. */
  7802. configLayer: function (zlevel, config) {
  7803. if (config) {
  7804. var layerConfig = this._layerConfig;
  7805. if (!layerConfig[zlevel]) {
  7806. layerConfig[zlevel] = config;
  7807. }
  7808. else {
  7809. merge(layerConfig[zlevel], config, true);
  7810. }
  7811. var layer = this._layers[zlevel];
  7812. if (layer) {
  7813. merge(layer, layerConfig[zlevel], true);
  7814. }
  7815. }
  7816. },
  7817. /**
  7818. * 删除指定层
  7819. * @param {number} zlevel 层所在的zlevel
  7820. */
  7821. delLayer: function (zlevel) {
  7822. var layers = this._layers;
  7823. var zlevelList = this._zlevelList;
  7824. var layer = layers[zlevel];
  7825. if (!layer) {
  7826. return;
  7827. }
  7828. layer.dom.parentNode.removeChild(layer.dom);
  7829. delete layers[zlevel];
  7830. zlevelList.splice(indexOf(zlevelList, zlevel), 1);
  7831. },
  7832. /**
  7833. * 区域大小变化后重绘
  7834. */
  7835. resize: function (width, height) {
  7836. var domRoot = this._domRoot;
  7837. // FIXME Why ?
  7838. domRoot.style.display = 'none';
  7839. // Save input w/h
  7840. var opts = this._opts;
  7841. width != null && (opts.width = width);
  7842. height != null && (opts.height = height);
  7843. width = this._getSize(0);
  7844. height = this._getSize(1);
  7845. domRoot.style.display = '';
  7846. // 优化没有实际改变的resize
  7847. if (this._width != width || height != this._height) {
  7848. domRoot.style.width = width + 'px';
  7849. domRoot.style.height = height + 'px';
  7850. for (var id in this._layers) {
  7851. if (this._layers.hasOwnProperty(id)) {
  7852. this._layers[id].resize(width, height);
  7853. }
  7854. }
  7855. each$1(this._progressiveLayers, function (layer) {
  7856. layer.resize(width, height);
  7857. });
  7858. this.refresh(true);
  7859. }
  7860. this._width = width;
  7861. this._height = height;
  7862. return this;
  7863. },
  7864. /**
  7865. * 清除单独的一个层
  7866. * @param {number} zlevel
  7867. */
  7868. clearLayer: function (zlevel) {
  7869. var layer = this._layers[zlevel];
  7870. if (layer) {
  7871. layer.clear();
  7872. }
  7873. },
  7874. /**
  7875. * 释放
  7876. */
  7877. dispose: function () {
  7878. this.root.innerHTML = '';
  7879. this.root =
  7880. this.storage =
  7881. this._domRoot =
  7882. this._layers = null;
  7883. },
  7884. /**
  7885. * Get canvas which has all thing rendered
  7886. * @param {Object} opts
  7887. * @param {string} [opts.backgroundColor]
  7888. * @param {number} [opts.pixelRatio]
  7889. */
  7890. getRenderedCanvas: function (opts) {
  7891. opts = opts || {};
  7892. if (this._singleCanvas) {
  7893. return this._layers[0].dom;
  7894. }
  7895. var imageLayer = new Layer('image', this, opts.pixelRatio || this.dpr);
  7896. imageLayer.initContext();
  7897. imageLayer.clearColor = opts.backgroundColor;
  7898. imageLayer.clear();
  7899. var displayList = this.storage.getDisplayList(true);
  7900. var scope = {};
  7901. var zlevel;
  7902. var self = this;
  7903. function findAndDrawOtherLayer(smaller, larger) {
  7904. var zlevelList = self._zlevelList;
  7905. if (smaller == null) {
  7906. smaller = -Infinity;
  7907. }
  7908. var intermediateLayer;
  7909. for (var i = 0; i < zlevelList.length; i++) {
  7910. var z = zlevelList[i];
  7911. var layer = self._layers[z];
  7912. if (!layer.__builtin__ && z > smaller && z < larger) {
  7913. intermediateLayer = layer;
  7914. break;
  7915. }
  7916. }
  7917. if (intermediateLayer && intermediateLayer.renderToCanvas) {
  7918. imageLayer.ctx.save();
  7919. intermediateLayer.renderToCanvas(imageLayer.ctx);
  7920. imageLayer.ctx.restore();
  7921. }
  7922. }
  7923. for (var i = 0; i < displayList.length; i++) {
  7924. var el = displayList[i];
  7925. if (el.zlevel !== zlevel) {
  7926. findAndDrawOtherLayer(zlevel, el.zlevel);
  7927. zlevel = el.zlevel;
  7928. }
  7929. this._doPaintEl(el, imageLayer, true, scope);
  7930. }
  7931. findAndDrawOtherLayer(zlevel, Infinity);
  7932. return imageLayer.dom;
  7933. },
  7934. /**
  7935. * 获取绘图区域宽度
  7936. */
  7937. getWidth: function () {
  7938. return this._width;
  7939. },
  7940. /**
  7941. * 获取绘图区域高度
  7942. */
  7943. getHeight: function () {
  7944. return this._height;
  7945. },
  7946. _getSize: function (whIdx) {
  7947. var opts = this._opts;
  7948. var wh = ['width', 'height'][whIdx];
  7949. var cwh = ['clientWidth', 'clientHeight'][whIdx];
  7950. var plt = ['paddingLeft', 'paddingTop'][whIdx];
  7951. var prb = ['paddingRight', 'paddingBottom'][whIdx];
  7952. if (opts[wh] != null && opts[wh] !== 'auto') {
  7953. return parseFloat(opts[wh]);
  7954. }
  7955. var root = this.root;
  7956. // IE8 does not support getComputedStyle, but it use VML.
  7957. var stl = document.defaultView.getComputedStyle(root);
  7958. return (
  7959. (root[cwh] || parseInt10(stl[wh]) || parseInt10(root.style[wh]))
  7960. - (parseInt10(stl[plt]) || 0)
  7961. - (parseInt10(stl[prb]) || 0)
  7962. ) | 0;
  7963. },
  7964. pathToImage: function (path, dpr) {
  7965. dpr = dpr || this.dpr;
  7966. var canvas = document.createElement('canvas');
  7967. var ctx = canvas.getContext('2d');
  7968. var rect = path.getBoundingRect();
  7969. var style = path.style;
  7970. var shadowBlurSize = style.shadowBlur;
  7971. var shadowOffsetX = style.shadowOffsetX;
  7972. var shadowOffsetY = style.shadowOffsetY;
  7973. var lineWidth = style.hasStroke() ? style.lineWidth : 0;
  7974. var leftMargin = Math.max(lineWidth / 2, -shadowOffsetX + shadowBlurSize);
  7975. var rightMargin = Math.max(lineWidth / 2, shadowOffsetX + shadowBlurSize);
  7976. var topMargin = Math.max(lineWidth / 2, -shadowOffsetY + shadowBlurSize);
  7977. var bottomMargin = Math.max(lineWidth / 2, shadowOffsetY + shadowBlurSize);
  7978. var width = rect.width + leftMargin + rightMargin;
  7979. var height = rect.height + topMargin + bottomMargin;
  7980. canvas.width = width * dpr;
  7981. canvas.height = height * dpr;
  7982. ctx.scale(dpr, dpr);
  7983. ctx.clearRect(0, 0, width, height);
  7984. ctx.dpr = dpr;
  7985. var pathTransform = {
  7986. position: path.position,
  7987. rotation: path.rotation,
  7988. scale: path.scale
  7989. };
  7990. path.position = [leftMargin - rect.x, topMargin - rect.y];
  7991. path.rotation = 0;
  7992. path.scale = [1, 1];
  7993. path.updateTransform();
  7994. if (path) {
  7995. path.brush(ctx);
  7996. }
  7997. var ImageShape = ZImage;
  7998. var imgShape = new ImageShape({
  7999. style: {
  8000. x: 0,
  8001. y: 0,
  8002. image: canvas
  8003. }
  8004. });
  8005. if (pathTransform.position != null) {
  8006. imgShape.position = path.position = pathTransform.position;
  8007. }
  8008. if (pathTransform.rotation != null) {
  8009. imgShape.rotation = path.rotation = pathTransform.rotation;
  8010. }
  8011. if (pathTransform.scale != null) {
  8012. imgShape.scale = path.scale = pathTransform.scale;
  8013. }
  8014. return imgShape;
  8015. }
  8016. };
  8017. /**
  8018. * 事件辅助类
  8019. * @module zrender/core/event
  8020. * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
  8021. */
  8022. var isDomLevel2 = (typeof window !== 'undefined') && !!window.addEventListener;
  8023. var MOUSE_EVENT_REG = /^(?:mouse|pointer|contextmenu|drag|drop)|click/;
  8024. function getBoundingClientRect(el) {
  8025. // BlackBerry 5, iOS 3 (original iPhone) don't have getBoundingRect
  8026. return el.getBoundingClientRect ? el.getBoundingClientRect() : {left: 0, top: 0};
  8027. }
  8028. // `calculate` is optional, default false
  8029. function clientToLocal(el, e, out, calculate) {
  8030. out = out || {};
  8031. // According to the W3C Working Draft, offsetX and offsetY should be relative
  8032. // to the padding edge of the target element. The only browser using this convention
  8033. // is IE. Webkit uses the border edge, Opera uses the content edge, and FireFox does
  8034. // not support the properties.
  8035. // (see http://www.jacklmoore.com/notes/mouse-position/)
  8036. // In zr painter.dom, padding edge equals to border edge.
  8037. // FIXME
  8038. // When mousemove event triggered on ec tooltip, target is not zr painter.dom, and
  8039. // offsetX/Y is relative to e.target, where the calculation of zrX/Y via offsetX/Y
  8040. // is too complex. So css-transfrom dont support in this case temporarily.
  8041. if (calculate || !env$1.canvasSupported) {
  8042. defaultGetZrXY(el, e, out);
  8043. }
  8044. // Caution: In FireFox, layerX/layerY Mouse position relative to the closest positioned
  8045. // ancestor element, so we should make sure el is positioned (e.g., not position:static).
  8046. // BTW1, Webkit don't return the same results as FF in non-simple cases (like add
  8047. // zoom-factor, overflow / opacity layers, transforms ...)
  8048. // BTW2, (ev.offsetY || ev.pageY - $(ev.target).offset().top) is not correct in preserve-3d.
  8049. // <https://bugs.jquery.com/ticket/8523#comment:14>
  8050. // BTW3, In ff, offsetX/offsetY is always 0.
  8051. else if (env$1.browser.firefox && e.layerX != null && e.layerX !== e.offsetX) {
  8052. out.zrX = e.layerX;
  8053. out.zrY = e.layerY;
  8054. }
  8055. // For IE6+, chrome, safari, opera. (When will ff support offsetX?)
  8056. else if (e.offsetX != null) {
  8057. out.zrX = e.offsetX;
  8058. out.zrY = e.offsetY;
  8059. }
  8060. // For some other device, e.g., IOS safari.
  8061. else {
  8062. defaultGetZrXY(el, e, out);
  8063. }
  8064. return out;
  8065. }
  8066. function defaultGetZrXY(el, e, out) {
  8067. // This well-known method below does not support css transform.
  8068. var box = getBoundingClientRect(el);
  8069. out.zrX = e.clientX - box.left;
  8070. out.zrY = e.clientY - box.top;
  8071. }
  8072. /**
  8073. * 如果存在第三方嵌入的一些dom触发的事件,或touch事件,需要转换一下事件坐标.
  8074. * `calculate` is optional, default false.
  8075. */
  8076. function normalizeEvent(el, e, calculate) {
  8077. e = e || window.event;
  8078. if (e.zrX != null) {
  8079. return e;
  8080. }
  8081. var eventType = e.type;
  8082. var isTouch = eventType && eventType.indexOf('touch') >= 0;
  8083. if (!isTouch) {
  8084. clientToLocal(el, e, e, calculate);
  8085. e.zrDelta = (e.wheelDelta) ? e.wheelDelta / 120 : -(e.detail || 0) / 3;
  8086. }
  8087. else {
  8088. var touch = eventType != 'touchend'
  8089. ? e.targetTouches[0]
  8090. : e.changedTouches[0];
  8091. touch && clientToLocal(el, touch, e, calculate);
  8092. }
  8093. // Add which for click: 1 === left; 2 === middle; 3 === right; otherwise: 0;
  8094. // See jQuery: https://github.com/jquery/jquery/blob/master/src/event.js
  8095. // If e.which has been defined, if may be readonly,
  8096. // see: https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/which
  8097. var button = e.button;
  8098. if (e.which == null && button !== undefined && MOUSE_EVENT_REG.test(e.type)) {
  8099. e.which = (button & 1 ? 1 : (button & 2 ? 3 : (button & 4 ? 2 : 0)));
  8100. }
  8101. return e;
  8102. }
  8103. function addEventListener(el, name, handler) {
  8104. if (isDomLevel2) {
  8105. el.addEventListener(name, handler);
  8106. }
  8107. else {
  8108. el.attachEvent('on' + name, handler);
  8109. }
  8110. }
  8111. function removeEventListener(el, name, handler) {
  8112. if (isDomLevel2) {
  8113. el.removeEventListener(name, handler);
  8114. }
  8115. else {
  8116. el.detachEvent('on' + name, handler);
  8117. }
  8118. }
  8119. /**
  8120. * preventDefault and stopPropagation.
  8121. * Notice: do not do that in zrender. Upper application
  8122. * do that if necessary.
  8123. *
  8124. * @memberOf module:zrender/core/event
  8125. * @method
  8126. * @param {Event} e : event对象
  8127. */
  8128. /**
  8129. * 动画主类, 调度和管理所有动画控制器
  8130. *
  8131. * @module zrender/animation/Animation
  8132. * @author pissang(https://github.com/pissang)
  8133. */
  8134. // TODO Additive animation
  8135. // http://iosoteric.com/additive-animations-animatewithduration-in-ios-8/
  8136. // https://developer.apple.com/videos/wwdc2014/#236
  8137. /**
  8138. * @typedef {Object} IZRenderStage
  8139. * @property {Function} update
  8140. */
  8141. /**
  8142. * @alias module:zrender/animation/Animation
  8143. * @constructor
  8144. * @param {Object} [options]
  8145. * @param {Function} [options.onframe]
  8146. * @param {IZRenderStage} [options.stage]
  8147. * @example
  8148. * var animation = new Animation();
  8149. * var obj = {
  8150. * x: 100,
  8151. * y: 100
  8152. * };
  8153. * animation.animate(node.position)
  8154. * .when(1000, {
  8155. * x: 500,
  8156. * y: 500
  8157. * })
  8158. * .when(2000, {
  8159. * x: 100,
  8160. * y: 100
  8161. * })
  8162. * .start('spline');
  8163. */
  8164. var Animation = function (options) {
  8165. options = options || {};
  8166. this.stage = options.stage || {};
  8167. this.onframe = options.onframe || function() {};
  8168. // private properties
  8169. this._clips = [];
  8170. this._running = false;
  8171. this._time;
  8172. this._pausedTime;
  8173. this._pauseStart;
  8174. this._paused = false;
  8175. Eventful.call(this);
  8176. };
  8177. Animation.prototype = {
  8178. constructor: Animation,
  8179. /**
  8180. * 添加 clip
  8181. * @param {module:zrender/animation/Clip} clip
  8182. */
  8183. addClip: function (clip) {
  8184. this._clips.push(clip);
  8185. },
  8186. /**
  8187. * 添加 animator
  8188. * @param {module:zrender/animation/Animator} animator
  8189. */
  8190. addAnimator: function (animator) {
  8191. animator.animation = this;
  8192. var clips = animator.getClips();
  8193. for (var i = 0; i < clips.length; i++) {
  8194. this.addClip(clips[i]);
  8195. }
  8196. },
  8197. /**
  8198. * 删除动画片段
  8199. * @param {module:zrender/animation/Clip} clip
  8200. */
  8201. removeClip: function(clip) {
  8202. var idx = indexOf(this._clips, clip);
  8203. if (idx >= 0) {
  8204. this._clips.splice(idx, 1);
  8205. }
  8206. },
  8207. /**
  8208. * 删除动画片段
  8209. * @param {module:zrender/animation/Animator} animator
  8210. */
  8211. removeAnimator: function (animator) {
  8212. var clips = animator.getClips();
  8213. for (var i = 0; i < clips.length; i++) {
  8214. this.removeClip(clips[i]);
  8215. }
  8216. animator.animation = null;
  8217. },
  8218. _update: function() {
  8219. var time = new Date().getTime() - this._pausedTime;
  8220. var delta = time - this._time;
  8221. var clips = this._clips;
  8222. var len = clips.length;
  8223. var deferredEvents = [];
  8224. var deferredClips = [];
  8225. for (var i = 0; i < len; i++) {
  8226. var clip = clips[i];
  8227. var e = clip.step(time, delta);
  8228. // Throw out the events need to be called after
  8229. // stage.update, like destroy
  8230. if (e) {
  8231. deferredEvents.push(e);
  8232. deferredClips.push(clip);
  8233. }
  8234. }
  8235. // Remove the finished clip
  8236. for (var i = 0; i < len;) {
  8237. if (clips[i]._needsRemove) {
  8238. clips[i] = clips[len - 1];
  8239. clips.pop();
  8240. len--;
  8241. }
  8242. else {
  8243. i++;
  8244. }
  8245. }
  8246. len = deferredEvents.length;
  8247. for (var i = 0; i < len; i++) {
  8248. deferredClips[i].fire(deferredEvents[i]);
  8249. }
  8250. this._time = time;
  8251. this.onframe(delta);
  8252. this.trigger('frame', delta);
  8253. if (this.stage.update) {
  8254. this.stage.update();
  8255. }
  8256. },
  8257. _startLoop: function () {
  8258. var self = this;
  8259. this._running = true;
  8260. function step() {
  8261. if (self._running) {
  8262. requestAnimationFrame(step);
  8263. !self._paused && self._update();
  8264. }
  8265. }
  8266. requestAnimationFrame(step);
  8267. },
  8268. /**
  8269. * 开始运行动画
  8270. */
  8271. start: function () {
  8272. this._time = new Date().getTime();
  8273. this._pausedTime = 0;
  8274. this._startLoop();
  8275. },
  8276. /**
  8277. * 停止运行动画
  8278. */
  8279. stop: function () {
  8280. this._running = false;
  8281. },
  8282. /**
  8283. * Pause
  8284. */
  8285. pause: function () {
  8286. if (!this._paused) {
  8287. this._pauseStart = new Date().getTime();
  8288. this._paused = true;
  8289. }
  8290. },
  8291. /**
  8292. * Resume
  8293. */
  8294. resume: function () {
  8295. if (this._paused) {
  8296. this._pausedTime += (new Date().getTime()) - this._pauseStart;
  8297. this._paused = false;
  8298. }
  8299. },
  8300. /**
  8301. * 清除所有动画片段
  8302. */
  8303. clear: function () {
  8304. this._clips = [];
  8305. },
  8306. /**
  8307. * 对一个目标创建一个animator对象,可以指定目标中的属性使用动画
  8308. * @param {Object} target
  8309. * @param {Object} options
  8310. * @param {boolean} [options.loop=false] 是否循环播放动画
  8311. * @param {Function} [options.getter=null]
  8312. * 如果指定getter函数,会通过getter函数取属性值
  8313. * @param {Function} [options.setter=null]
  8314. * 如果指定setter函数,会通过setter函数设置属性值
  8315. * @return {module:zrender/animation/Animation~Animator}
  8316. */
  8317. // TODO Gap
  8318. animate: function (target, options) {
  8319. options = options || {};
  8320. var animator = new Animator(
  8321. target,
  8322. options.loop,
  8323. options.getter,
  8324. options.setter
  8325. );
  8326. this.addAnimator(animator);
  8327. return animator;
  8328. }
  8329. };
  8330. mixin(Animation, Eventful);
  8331. /**
  8332. * Only implements needed gestures for mobile.
  8333. */
  8334. var GestureMgr = function () {
  8335. /**
  8336. * @private
  8337. * @type {Array.<Object>}
  8338. */
  8339. this._track = [];
  8340. };
  8341. GestureMgr.prototype = {
  8342. constructor: GestureMgr,
  8343. recognize: function (event, target, root) {
  8344. this._doTrack(event, target, root);
  8345. return this._recognize(event);
  8346. },
  8347. clear: function () {
  8348. this._track.length = 0;
  8349. return this;
  8350. },
  8351. _doTrack: function (event, target, root) {
  8352. var touches = event.touches;
  8353. if (!touches) {
  8354. return;
  8355. }
  8356. var trackItem = {
  8357. points: [],
  8358. touches: [],
  8359. target: target,
  8360. event: event
  8361. };
  8362. for (var i = 0, len = touches.length; i < len; i++) {
  8363. var touch = touches[i];
  8364. var pos = clientToLocal(root, touch, {});
  8365. trackItem.points.push([pos.zrX, pos.zrY]);
  8366. trackItem.touches.push(touch);
  8367. }
  8368. this._track.push(trackItem);
  8369. },
  8370. _recognize: function (event) {
  8371. for (var eventName in recognizers) {
  8372. if (recognizers.hasOwnProperty(eventName)) {
  8373. var gestureInfo = recognizers[eventName](this._track, event);
  8374. if (gestureInfo) {
  8375. return gestureInfo;
  8376. }
  8377. }
  8378. }
  8379. }
  8380. };
  8381. function dist$1(pointPair) {
  8382. var dx = pointPair[1][0] - pointPair[0][0];
  8383. var dy = pointPair[1][1] - pointPair[0][1];
  8384. return Math.sqrt(dx * dx + dy * dy);
  8385. }
  8386. function center(pointPair) {
  8387. return [
  8388. (pointPair[0][0] + pointPair[1][0]) / 2,
  8389. (pointPair[0][1] + pointPair[1][1]) / 2
  8390. ];
  8391. }
  8392. var recognizers = {
  8393. pinch: function (track, event) {
  8394. var trackLen = track.length;
  8395. if (!trackLen) {
  8396. return;
  8397. }
  8398. var pinchEnd = (track[trackLen - 1] || {}).points;
  8399. var pinchPre = (track[trackLen - 2] || {}).points || pinchEnd;
  8400. if (pinchPre
  8401. && pinchPre.length > 1
  8402. && pinchEnd
  8403. && pinchEnd.length > 1
  8404. ) {
  8405. var pinchScale = dist$1(pinchEnd) / dist$1(pinchPre);
  8406. !isFinite(pinchScale) && (pinchScale = 1);
  8407. event.pinchScale = pinchScale;
  8408. var pinchCenter = center(pinchEnd);
  8409. event.pinchX = pinchCenter[0];
  8410. event.pinchY = pinchCenter[1];
  8411. return {
  8412. type: 'pinch',
  8413. target: track[0].target,
  8414. event: event
  8415. };
  8416. }
  8417. }
  8418. // Only pinch currently.
  8419. };
  8420. var TOUCH_CLICK_DELAY = 300;
  8421. var mouseHandlerNames = [
  8422. 'click', 'dblclick', 'mousewheel', 'mouseout',
  8423. 'mouseup', 'mousedown', 'mousemove', 'contextmenu'
  8424. ];
  8425. var touchHandlerNames = [
  8426. 'touchstart', 'touchend', 'touchmove'
  8427. ];
  8428. var pointerEventNames = {
  8429. pointerdown: 1, pointerup: 1, pointermove: 1, pointerout: 1
  8430. };
  8431. var pointerHandlerNames = map(mouseHandlerNames, function (name) {
  8432. var nm = name.replace('mouse', 'pointer');
  8433. return pointerEventNames[nm] ? nm : name;
  8434. });
  8435. function eventNameFix(name) {
  8436. return (name === 'mousewheel' && env$1.browser.firefox) ? 'DOMMouseScroll' : name;
  8437. }
  8438. function processGesture(proxy, event, stage) {
  8439. var gestureMgr = proxy._gestureMgr;
  8440. stage === 'start' && gestureMgr.clear();
  8441. var gestureInfo = gestureMgr.recognize(
  8442. event,
  8443. proxy.handler.findHover(event.zrX, event.zrY, null).target,
  8444. proxy.dom
  8445. );
  8446. stage === 'end' && gestureMgr.clear();
  8447. // Do not do any preventDefault here. Upper application do that if necessary.
  8448. if (gestureInfo) {
  8449. var type = gestureInfo.type;
  8450. event.gestureEvent = type;
  8451. proxy.handler.dispatchToElement({target: gestureInfo.target}, type, gestureInfo.event);
  8452. }
  8453. }
  8454. // function onMSGestureChange(proxy, event) {
  8455. // if (event.translationX || event.translationY) {
  8456. // // mousemove is carried by MSGesture to reduce the sensitivity.
  8457. // proxy.handler.dispatchToElement(event.target, 'mousemove', event);
  8458. // }
  8459. // if (event.scale !== 1) {
  8460. // event.pinchX = event.offsetX;
  8461. // event.pinchY = event.offsetY;
  8462. // event.pinchScale = event.scale;
  8463. // proxy.handler.dispatchToElement(event.target, 'pinch', event);
  8464. // }
  8465. // }
  8466. /**
  8467. * Prevent mouse event from being dispatched after Touch Events action
  8468. * @see <https://github.com/deltakosh/handjs/blob/master/src/hand.base.js>
  8469. * 1. Mobile browsers dispatch mouse events 300ms after touchend.
  8470. * 2. Chrome for Android dispatch mousedown for long-touch about 650ms
  8471. * Result: Blocking Mouse Events for 700ms.
  8472. */
  8473. function setTouchTimer(instance) {
  8474. instance._touching = true;
  8475. clearTimeout(instance._touchTimer);
  8476. instance._touchTimer = setTimeout(function () {
  8477. instance._touching = false;
  8478. }, 700);
  8479. }
  8480. var domHandlers = {
  8481. /**
  8482. * Mouse move handler
  8483. * @inner
  8484. * @param {Event} event
  8485. */
  8486. mousemove: function (event) {
  8487. event = normalizeEvent(this.dom, event);
  8488. this.trigger('mousemove', event);
  8489. },
  8490. /**
  8491. * Mouse out handler
  8492. * @inner
  8493. * @param {Event} event
  8494. */
  8495. mouseout: function (event) {
  8496. event = normalizeEvent(this.dom, event);
  8497. var element = event.toElement || event.relatedTarget;
  8498. if (element != this.dom) {
  8499. while (element && element.nodeType != 9) {
  8500. // 忽略包含在root中的dom引起的mouseOut
  8501. if (element === this.dom) {
  8502. return;
  8503. }
  8504. element = element.parentNode;
  8505. }
  8506. }
  8507. this.trigger('mouseout', event);
  8508. },
  8509. /**
  8510. * Touch开始响应函数
  8511. * @inner
  8512. * @param {Event} event
  8513. */
  8514. touchstart: function (event) {
  8515. // Default mouse behaviour should not be disabled here.
  8516. // For example, page may needs to be slided.
  8517. event = normalizeEvent(this.dom, event);
  8518. // Mark touch, which is useful in distinguish touch and
  8519. // mouse event in upper applicatoin.
  8520. event.zrByTouch = true;
  8521. this._lastTouchMoment = new Date();
  8522. processGesture(this, event, 'start');
  8523. // In touch device, trigger `mousemove`(`mouseover`) should
  8524. // be triggered, and must before `mousedown` triggered.
  8525. domHandlers.mousemove.call(this, event);
  8526. domHandlers.mousedown.call(this, event);
  8527. setTouchTimer(this);
  8528. },
  8529. /**
  8530. * Touch移动响应函数
  8531. * @inner
  8532. * @param {Event} event
  8533. */
  8534. touchmove: function (event) {
  8535. event = normalizeEvent(this.dom, event);
  8536. // Mark touch, which is useful in distinguish touch and
  8537. // mouse event in upper applicatoin.
  8538. event.zrByTouch = true;
  8539. processGesture(this, event, 'change');
  8540. // Mouse move should always be triggered no matter whether
  8541. // there is gestrue event, because mouse move and pinch may
  8542. // be used at the same time.
  8543. domHandlers.mousemove.call(this, event);
  8544. setTouchTimer(this);
  8545. },
  8546. /**
  8547. * Touch结束响应函数
  8548. * @inner
  8549. * @param {Event} event
  8550. */
  8551. touchend: function (event) {
  8552. event = normalizeEvent(this.dom, event);
  8553. // Mark touch, which is useful in distinguish touch and
  8554. // mouse event in upper applicatoin.
  8555. event.zrByTouch = true;
  8556. processGesture(this, event, 'end');
  8557. domHandlers.mouseup.call(this, event);
  8558. // Do not trigger `mouseout` here, in spite of `mousemove`(`mouseover`) is
  8559. // triggered in `touchstart`. This seems to be illogical, but by this mechanism,
  8560. // we can conveniently implement "hover style" in both PC and touch device just
  8561. // by listening to `mouseover` to add "hover style" and listening to `mouseout`
  8562. // to remove "hover style" on an element, without any additional code for
  8563. // compatibility. (`mouseout` will not be triggered in `touchend`, so "hover
  8564. // style" will remain for user view)
  8565. // click event should always be triggered no matter whether
  8566. // there is gestrue event. System click can not be prevented.
  8567. if (+new Date() - this._lastTouchMoment < TOUCH_CLICK_DELAY) {
  8568. domHandlers.click.call(this, event);
  8569. }
  8570. setTouchTimer(this);
  8571. },
  8572. pointerdown: function (event) {
  8573. domHandlers.mousedown.call(this, event);
  8574. // if (useMSGuesture(this, event)) {
  8575. // this._msGesture.addPointer(event.pointerId);
  8576. // }
  8577. },
  8578. pointermove: function (event) {
  8579. // FIXME
  8580. // pointermove is so sensitive that it always triggered when
  8581. // tap(click) on touch screen, which affect some judgement in
  8582. // upper application. So, we dont support mousemove on MS touch
  8583. // device yet.
  8584. if (!isPointerFromTouch(event)) {
  8585. domHandlers.mousemove.call(this, event);
  8586. }
  8587. },
  8588. pointerup: function (event) {
  8589. domHandlers.mouseup.call(this, event);
  8590. },
  8591. pointerout: function (event) {
  8592. // pointerout will be triggered when tap on touch screen
  8593. // (IE11+/Edge on MS Surface) after click event triggered,
  8594. // which is inconsistent with the mousout behavior we defined
  8595. // in touchend. So we unify them.
  8596. // (check domHandlers.touchend for detailed explanation)
  8597. if (!isPointerFromTouch(event)) {
  8598. domHandlers.mouseout.call(this, event);
  8599. }
  8600. }
  8601. };
  8602. function isPointerFromTouch(event) {
  8603. var pointerType = event.pointerType;
  8604. return pointerType === 'pen' || pointerType === 'touch';
  8605. }
  8606. // function useMSGuesture(handlerProxy, event) {
  8607. // return isPointerFromTouch(event) && !!handlerProxy._msGesture;
  8608. // }
  8609. // Common handlers
  8610. each$1(['click', 'mousedown', 'mouseup', 'mousewheel', 'dblclick', 'contextmenu'], function (name) {
  8611. domHandlers[name] = function (event) {
  8612. event = normalizeEvent(this.dom, event);
  8613. this.trigger(name, event);
  8614. };
  8615. });
  8616. /**
  8617. * 为控制类实例初始化dom 事件处理函数
  8618. *
  8619. * @inner
  8620. * @param {module:zrender/Handler} instance 控制类实例
  8621. */
  8622. function initDomHandler(instance) {
  8623. each$1(touchHandlerNames, function (name) {
  8624. instance._handlers[name] = bind(domHandlers[name], instance);
  8625. });
  8626. each$1(pointerHandlerNames, function (name) {
  8627. instance._handlers[name] = bind(domHandlers[name], instance);
  8628. });
  8629. each$1(mouseHandlerNames, function (name) {
  8630. instance._handlers[name] = makeMouseHandler(domHandlers[name], instance);
  8631. });
  8632. function makeMouseHandler(fn, instance) {
  8633. return function () {
  8634. if (instance._touching) {
  8635. return;
  8636. }
  8637. return fn.apply(instance, arguments);
  8638. };
  8639. }
  8640. }
  8641. function HandlerDomProxy(dom) {
  8642. Eventful.call(this);
  8643. this.dom = dom;
  8644. /**
  8645. * @private
  8646. * @type {boolean}
  8647. */
  8648. this._touching = false;
  8649. /**
  8650. * @private
  8651. * @type {number}
  8652. */
  8653. this._touchTimer;
  8654. /**
  8655. * @private
  8656. * @type {module:zrender/core/GestureMgr}
  8657. */
  8658. this._gestureMgr = new GestureMgr();
  8659. this._handlers = {};
  8660. initDomHandler(this);
  8661. if (env$1.pointerEventsSupported) { // Only IE11+/Edge
  8662. // 1. On devices that both enable touch and mouse (e.g., MS Surface and lenovo X240),
  8663. // IE11+/Edge do not trigger touch event, but trigger pointer event and mouse event
  8664. // at the same time.
  8665. // 2. On MS Surface, it probablely only trigger mousedown but no mouseup when tap on
  8666. // screen, which do not occurs in pointer event.
  8667. // So we use pointer event to both detect touch gesture and mouse behavior.
  8668. mountHandlers(pointerHandlerNames, this);
  8669. // FIXME
  8670. // Note: MS Gesture require CSS touch-action set. But touch-action is not reliable,
  8671. // which does not prevent defuault behavior occasionally (which may cause view port
  8672. // zoomed in but use can not zoom it back). And event.preventDefault() does not work.
  8673. // So we have to not to use MSGesture and not to support touchmove and pinch on MS
  8674. // touch screen. And we only support click behavior on MS touch screen now.
  8675. // MS Gesture Event is only supported on IE11+/Edge and on Windows 8+.
  8676. // We dont support touch on IE on win7.
  8677. // See <https://msdn.microsoft.com/en-us/library/dn433243(v=vs.85).aspx>
  8678. // if (typeof MSGesture === 'function') {
  8679. // (this._msGesture = new MSGesture()).target = dom; // jshint ignore:line
  8680. // dom.addEventListener('MSGestureChange', onMSGestureChange);
  8681. // }
  8682. }
  8683. else {
  8684. if (env$1.touchEventsSupported) {
  8685. mountHandlers(touchHandlerNames, this);
  8686. // Handler of 'mouseout' event is needed in touch mode, which will be mounted below.
  8687. // addEventListener(root, 'mouseout', this._mouseoutHandler);
  8688. }
  8689. // 1. Considering some devices that both enable touch and mouse event (like on MS Surface
  8690. // and lenovo X240, @see #2350), we make mouse event be always listened, otherwise
  8691. // mouse event can not be handle in those devices.
  8692. // 2. On MS Surface, Chrome will trigger both touch event and mouse event. How to prevent
  8693. // mouseevent after touch event triggered, see `setTouchTimer`.
  8694. mountHandlers(mouseHandlerNames, this);
  8695. }
  8696. function mountHandlers(handlerNames, instance) {
  8697. each$1(handlerNames, function (name) {
  8698. addEventListener(dom, eventNameFix(name), instance._handlers[name]);
  8699. }, instance);
  8700. }
  8701. }
  8702. var handlerDomProxyProto = HandlerDomProxy.prototype;
  8703. handlerDomProxyProto.dispose = function () {
  8704. var handlerNames = mouseHandlerNames.concat(touchHandlerNames);
  8705. for (var i = 0; i < handlerNames.length; i++) {
  8706. var name = handlerNames[i];
  8707. removeEventListener(this.dom, eventNameFix(name), this._handlers[name]);
  8708. }
  8709. };
  8710. handlerDomProxyProto.setCursor = function (cursorStyle) {
  8711. this.dom.style.cursor = cursorStyle || 'default';
  8712. };
  8713. mixin(HandlerDomProxy, Eventful);
  8714. /*!
  8715. * ZRender, a high performance 2d drawing library.
  8716. *
  8717. * Copyright (c) 2013, Baidu Inc.
  8718. * All rights reserved.
  8719. *
  8720. * LICENSE
  8721. * https://github.com/ecomfe/zrender/blob/master/LICENSE.txt
  8722. */
  8723. var useVML = !env$1.canvasSupported;
  8724. var painterCtors = {
  8725. canvas: Painter
  8726. };
  8727. /**
  8728. * @type {string}
  8729. */
  8730. var version$1 = '3.7.4';
  8731. /**
  8732. * Initializing a zrender instance
  8733. * @param {HTMLElement} dom
  8734. * @param {Object} opts
  8735. * @param {string} [opts.renderer='canvas'] 'canvas' or 'svg'
  8736. * @param {number} [opts.devicePixelRatio]
  8737. * @param {number|string} [opts.width] Can be 'auto' (the same as null/undefined)
  8738. * @param {number|string} [opts.height] Can be 'auto' (the same as null/undefined)
  8739. * @return {module:zrender/ZRender}
  8740. */
  8741. function init$1(dom, opts) {
  8742. var zr = new ZRender(guid(), dom, opts);
  8743. return zr;
  8744. }
  8745. /**
  8746. * Dispose zrender instance
  8747. * @param {module:zrender/ZRender} zr
  8748. */
  8749. /**
  8750. * Get zrender instance by id
  8751. * @param {string} id zrender instance id
  8752. * @return {module:zrender/ZRender}
  8753. */
  8754. /**
  8755. * @module zrender/ZRender
  8756. */
  8757. /**
  8758. * @constructor
  8759. * @alias module:zrender/ZRender
  8760. * @param {string} id
  8761. * @param {HTMLElement} dom
  8762. * @param {Object} opts
  8763. * @param {string} [opts.renderer='canvas'] 'canvas' or 'svg'
  8764. * @param {number} [opts.devicePixelRatio]
  8765. * @param {number} [opts.width] Can be 'auto' (the same as null/undefined)
  8766. * @param {number} [opts.height] Can be 'auto' (the same as null/undefined)
  8767. */
  8768. var ZRender = function (id, dom, opts) {
  8769. opts = opts || {};
  8770. /**
  8771. * @type {HTMLDomElement}
  8772. */
  8773. this.dom = dom;
  8774. /**
  8775. * @type {string}
  8776. */
  8777. this.id = id;
  8778. var self = this;
  8779. var storage = new Storage();
  8780. var rendererType = opts.renderer;
  8781. // TODO WebGL
  8782. if (useVML) {
  8783. if (!painterCtors.vml) {
  8784. throw new Error('You need to require \'zrender/vml/vml\' to support IE8');
  8785. }
  8786. rendererType = 'vml';
  8787. }
  8788. else if (!rendererType || !painterCtors[rendererType]) {
  8789. rendererType = 'canvas';
  8790. }
  8791. var painter = new painterCtors[rendererType](dom, storage, opts);
  8792. this.storage = storage;
  8793. this.painter = painter;
  8794. var handerProxy = !env$1.node ? new HandlerDomProxy(painter.getViewportRoot()) : null;
  8795. this.handler = new Handler(storage, painter, handerProxy, painter.root);
  8796. /**
  8797. * @type {module:zrender/animation/Animation}
  8798. */
  8799. this.animation = new Animation({
  8800. stage: {
  8801. update: bind(this.flush, this)
  8802. }
  8803. });
  8804. this.animation.start();
  8805. /**
  8806. * @type {boolean}
  8807. * @private
  8808. */
  8809. this._needsRefresh;
  8810. // 修改 storage.delFromStorage, 每次删除元素之前删除动画
  8811. // FIXME 有点ugly
  8812. var oldDelFromStorage = storage.delFromStorage;
  8813. var oldAddToStorage = storage.addToStorage;
  8814. storage.delFromStorage = function (el) {
  8815. oldDelFromStorage.call(storage, el);
  8816. el && el.removeSelfFromZr(self);
  8817. };
  8818. storage.addToStorage = function (el) {
  8819. oldAddToStorage.call(storage, el);
  8820. el.addSelfToZr(self);
  8821. };
  8822. };
  8823. ZRender.prototype = {
  8824. constructor: ZRender,
  8825. /**
  8826. * 获取实例唯一标识
  8827. * @return {string}
  8828. */
  8829. getId: function () {
  8830. return this.id;
  8831. },
  8832. /**
  8833. * 添加元素
  8834. * @param {module:zrender/Element} el
  8835. */
  8836. add: function (el) {
  8837. this.storage.addRoot(el);
  8838. this._needsRefresh = true;
  8839. },
  8840. /**
  8841. * 删除元素
  8842. * @param {module:zrender/Element} el
  8843. */
  8844. remove: function (el) {
  8845. this.storage.delRoot(el);
  8846. this._needsRefresh = true;
  8847. },
  8848. /**
  8849. * Change configuration of layer
  8850. * @param {string} zLevel
  8851. * @param {Object} config
  8852. * @param {string} [config.clearColor=0] Clear color
  8853. * @param {string} [config.motionBlur=false] If enable motion blur
  8854. * @param {number} [config.lastFrameAlpha=0.7] Motion blur factor. Larger value cause longer trailer
  8855. */
  8856. configLayer: function (zLevel, config) {
  8857. this.painter.configLayer(zLevel, config);
  8858. this._needsRefresh = true;
  8859. },
  8860. /**
  8861. * Repaint the canvas immediately
  8862. */
  8863. refreshImmediately: function () {
  8864. // var start = new Date();
  8865. // Clear needsRefresh ahead to avoid something wrong happens in refresh
  8866. // Or it will cause zrender refreshes again and again.
  8867. this._needsRefresh = false;
  8868. this.painter.refresh();
  8869. /**
  8870. * Avoid trigger zr.refresh in Element#beforeUpdate hook
  8871. */
  8872. this._needsRefresh = false;
  8873. // var end = new Date();
  8874. // var log = document.getElementById('log');
  8875. // if (log) {
  8876. // log.innerHTML = log.innerHTML + '<br>' + (end - start);
  8877. // }
  8878. },
  8879. /**
  8880. * Mark and repaint the canvas in the next frame of browser
  8881. */
  8882. refresh: function() {
  8883. this._needsRefresh = true;
  8884. },
  8885. /**
  8886. * Perform all refresh
  8887. */
  8888. flush: function () {
  8889. if (this._needsRefresh) {
  8890. this.refreshImmediately();
  8891. }
  8892. if (this._needsRefreshHover) {
  8893. this.refreshHoverImmediately();
  8894. }
  8895. },
  8896. /**
  8897. * Add element to hover layer
  8898. * @param {module:zrender/Element} el
  8899. * @param {Object} style
  8900. */
  8901. addHover: function (el, style) {
  8902. if (this.painter.addHover) {
  8903. this.painter.addHover(el, style);
  8904. this.refreshHover();
  8905. }
  8906. },
  8907. /**
  8908. * Add element from hover layer
  8909. * @param {module:zrender/Element} el
  8910. */
  8911. removeHover: function (el) {
  8912. if (this.painter.removeHover) {
  8913. this.painter.removeHover(el);
  8914. this.refreshHover();
  8915. }
  8916. },
  8917. /**
  8918. * Clear all hover elements in hover layer
  8919. * @param {module:zrender/Element} el
  8920. */
  8921. clearHover: function () {
  8922. if (this.painter.clearHover) {
  8923. this.painter.clearHover();
  8924. this.refreshHover();
  8925. }
  8926. },
  8927. /**
  8928. * Refresh hover in next frame
  8929. */
  8930. refreshHover: function () {
  8931. this._needsRefreshHover = true;
  8932. },
  8933. /**
  8934. * Refresh hover immediately
  8935. */
  8936. refreshHoverImmediately: function () {
  8937. this._needsRefreshHover = false;
  8938. this.painter.refreshHover && this.painter.refreshHover();
  8939. },
  8940. /**
  8941. * Resize the canvas.
  8942. * Should be invoked when container size is changed
  8943. * @param {Object} [opts]
  8944. * @param {number|string} [opts.width] Can be 'auto' (the same as null/undefined)
  8945. * @param {number|string} [opts.height] Can be 'auto' (the same as null/undefined)
  8946. */
  8947. resize: function(opts) {
  8948. opts = opts || {};
  8949. this.painter.resize(opts.width, opts.height);
  8950. this.handler.resize();
  8951. },
  8952. /**
  8953. * Stop and clear all animation immediately
  8954. */
  8955. clearAnimation: function () {
  8956. this.animation.clear();
  8957. },
  8958. /**
  8959. * Get container width
  8960. */
  8961. getWidth: function() {
  8962. return this.painter.getWidth();
  8963. },
  8964. /**
  8965. * Get container height
  8966. */
  8967. getHeight: function() {
  8968. return this.painter.getHeight();
  8969. },
  8970. /**
  8971. * Export the canvas as Base64 URL
  8972. * @param {string} type
  8973. * @param {string} [backgroundColor='#fff']
  8974. * @return {string} Base64 URL
  8975. */
  8976. // toDataURL: function(type, backgroundColor) {
  8977. // return this.painter.getRenderedCanvas({
  8978. // backgroundColor: backgroundColor
  8979. // }).toDataURL(type);
  8980. // },
  8981. /**
  8982. * Converting a path to image.
  8983. * It has much better performance of drawing image rather than drawing a vector path.
  8984. * @param {module:zrender/graphic/Path} e
  8985. * @param {number} width
  8986. * @param {number} height
  8987. */
  8988. pathToImage: function(e, dpr) {
  8989. return this.painter.pathToImage(e, dpr);
  8990. },
  8991. /**
  8992. * Set default cursor
  8993. * @param {string} [cursorStyle='default'] 例如 crosshair
  8994. */
  8995. setCursorStyle: function (cursorStyle) {
  8996. this.handler.setCursorStyle(cursorStyle);
  8997. },
  8998. /**
  8999. * Find hovered element
  9000. * @param {number} x
  9001. * @param {number} y
  9002. * @return {Object} {target, topTarget}
  9003. */
  9004. findHover: function (x, y) {
  9005. return this.handler.findHover(x, y);
  9006. },
  9007. /**
  9008. * Bind event
  9009. *
  9010. * @param {string} eventName Event name
  9011. * @param {Function} eventHandler Handler function
  9012. * @param {Object} [context] Context object
  9013. */
  9014. on: function(eventName, eventHandler, context) {
  9015. this.handler.on(eventName, eventHandler, context);
  9016. },
  9017. /**
  9018. * Unbind event
  9019. * @param {string} eventName Event name
  9020. * @param {Function} [eventHandler] Handler function
  9021. */
  9022. off: function(eventName, eventHandler) {
  9023. this.handler.off(eventName, eventHandler);
  9024. },
  9025. /**
  9026. * Trigger event manually
  9027. *
  9028. * @param {string} eventName Event name
  9029. * @param {event=} event Event object
  9030. */
  9031. trigger: function (eventName, event) {
  9032. this.handler.trigger(eventName, event);
  9033. },
  9034. /**
  9035. * Clear all objects and the canvas.
  9036. */
  9037. clear: function () {
  9038. this.storage.delRoot();
  9039. this.painter.clear();
  9040. },
  9041. /**
  9042. * Dispose self.
  9043. */
  9044. dispose: function () {
  9045. this.animation.stop();
  9046. this.clear();
  9047. this.storage.dispose();
  9048. this.painter.dispose();
  9049. this.handler.dispose();
  9050. this.animation =
  9051. this.storage =
  9052. this.painter =
  9053. this.handler = null;
  9054. }
  9055. };
  9056. var RADIAN_EPSILON = 1e-4;
  9057. function _trim(str) {
  9058. return str.replace(/^\s+/, '').replace(/\s+$/, '');
  9059. }
  9060. /**
  9061. * Linear mapping a value from domain to range
  9062. * @memberOf module:echarts/util/number
  9063. * @param {(number|Array.<number>)} val
  9064. * @param {Array.<number>} domain Domain extent domain[0] can be bigger than domain[1]
  9065. * @param {Array.<number>} range Range extent range[0] can be bigger than range[1]
  9066. * @param {boolean} clamp
  9067. * @return {(number|Array.<number>}
  9068. */
  9069. function linearMap(val, domain, range, clamp) {
  9070. var subDomain = domain[1] - domain[0];
  9071. var subRange = range[1] - range[0];
  9072. if (subDomain === 0) {
  9073. return subRange === 0
  9074. ? range[0]
  9075. : (range[0] + range[1]) / 2;
  9076. }
  9077. // Avoid accuracy problem in edge, such as
  9078. // 146.39 - 62.83 === 83.55999999999999.
  9079. // See echarts/test/ut/spec/util/number.js#linearMap#accuracyError
  9080. // It is a little verbose for efficiency considering this method
  9081. // is a hotspot.
  9082. if (clamp) {
  9083. if (subDomain > 0) {
  9084. if (val <= domain[0]) {
  9085. return range[0];
  9086. }
  9087. else if (val >= domain[1]) {
  9088. return range[1];
  9089. }
  9090. }
  9091. else {
  9092. if (val >= domain[0]) {
  9093. return range[0];
  9094. }
  9095. else if (val <= domain[1]) {
  9096. return range[1];
  9097. }
  9098. }
  9099. }
  9100. else {
  9101. if (val === domain[0]) {
  9102. return range[0];
  9103. }
  9104. if (val === domain[1]) {
  9105. return range[1];
  9106. }
  9107. }
  9108. return (val - domain[0]) / subDomain * subRange + range[0];
  9109. }
  9110. /**
  9111. * Convert a percent string to absolute number.
  9112. * Returns NaN if percent is not a valid string or number
  9113. * @memberOf module:echarts/util/number
  9114. * @param {string|number} percent
  9115. * @param {number} all
  9116. * @return {number}
  9117. */
  9118. function parsePercent$1(percent, all) {
  9119. switch (percent) {
  9120. case 'center':
  9121. case 'middle':
  9122. percent = '50%';
  9123. break;
  9124. case 'left':
  9125. case 'top':
  9126. percent = '0%';
  9127. break;
  9128. case 'right':
  9129. case 'bottom':
  9130. percent = '100%';
  9131. break;
  9132. }
  9133. if (typeof percent === 'string') {
  9134. if (_trim(percent).match(/%$/)) {
  9135. return parseFloat(percent) / 100 * all;
  9136. }
  9137. return parseFloat(percent);
  9138. }
  9139. return percent == null ? NaN : +percent;
  9140. }
  9141. /**
  9142. * (1) Fix rounding error of float numbers.
  9143. * (2) Support return string to avoid scientific notation like '3.5e-7'.
  9144. *
  9145. * @param {number} x
  9146. * @param {number} [precision]
  9147. * @param {boolean} [returnStr]
  9148. * @return {number|string}
  9149. */
  9150. function round(x, precision, returnStr) {
  9151. if (precision == null) {
  9152. precision = 10;
  9153. }
  9154. // Avoid range error
  9155. precision = Math.min(Math.max(0, precision), 20);
  9156. x = (+x).toFixed(precision);
  9157. return returnStr ? x : +x;
  9158. }
  9159. /**
  9160. * Get precision
  9161. * @param {number} val
  9162. */
  9163. /**
  9164. * @param {string|number} val
  9165. * @return {number}
  9166. */
  9167. function getPrecisionSafe(val) {
  9168. var str = val.toString();
  9169. // Consider scientific notation: '3.4e-12' '3.4e+12'
  9170. var eIndex = str.indexOf('e');
  9171. if (eIndex > 0) {
  9172. var precision = +str.slice(eIndex + 1);
  9173. return precision < 0 ? -precision : 0;
  9174. }
  9175. else {
  9176. var dotIndex = str.indexOf('.');
  9177. return dotIndex < 0 ? 0 : str.length - 1 - dotIndex;
  9178. }
  9179. }
  9180. /**
  9181. * Minimal dicernible data precisioin according to a single pixel.
  9182. *
  9183. * @param {Array.<number>} dataExtent
  9184. * @param {Array.<number>} pixelExtent
  9185. * @return {number} precision
  9186. */
  9187. function getPixelPrecision(dataExtent, pixelExtent) {
  9188. var log = Math.log;
  9189. var LN10 = Math.LN10;
  9190. var dataQuantity = Math.floor(log(dataExtent[1] - dataExtent[0]) / LN10);
  9191. var sizeQuantity = Math.round(log(Math.abs(pixelExtent[1] - pixelExtent[0])) / LN10);
  9192. // toFixed() digits argument must be between 0 and 20.
  9193. var precision = Math.min(Math.max(-dataQuantity + sizeQuantity, 0), 20);
  9194. return !isFinite(precision) ? 20 : precision;
  9195. }
  9196. /**
  9197. * Get a data of given precision, assuring the sum of percentages
  9198. * in valueList is 1.
  9199. * The largest remainer method is used.
  9200. * https://en.wikipedia.org/wiki/Largest_remainder_method
  9201. *
  9202. * @param {Array.<number>} valueList a list of all data
  9203. * @param {number} idx index of the data to be processed in valueList
  9204. * @param {number} precision integer number showing digits of precision
  9205. * @return {number} percent ranging from 0 to 100
  9206. */
  9207. function getPercentWithPrecision(valueList, idx, precision) {
  9208. if (!valueList[idx]) {
  9209. return 0;
  9210. }
  9211. var sum = reduce(valueList, function (acc, val) {
  9212. return acc + (isNaN(val) ? 0 : val);
  9213. }, 0);
  9214. if (sum === 0) {
  9215. return 0;
  9216. }
  9217. var digits = Math.pow(10, precision);
  9218. var votesPerQuota = map(valueList, function (val) {
  9219. return (isNaN(val) ? 0 : val) / sum * digits * 100;
  9220. });
  9221. var targetSeats = digits * 100;
  9222. var seats = map(votesPerQuota, function (votes) {
  9223. // Assign automatic seats.
  9224. return Math.floor(votes);
  9225. });
  9226. var currentSum = reduce(seats, function (acc, val) {
  9227. return acc + val;
  9228. }, 0);
  9229. var remainder = map(votesPerQuota, function (votes, idx) {
  9230. return votes - seats[idx];
  9231. });
  9232. // Has remainding votes.
  9233. while (currentSum < targetSeats) {
  9234. // Find next largest remainder.
  9235. var max = Number.NEGATIVE_INFINITY;
  9236. var maxId = null;
  9237. for (var i = 0, len = remainder.length; i < len; ++i) {
  9238. if (remainder[i] > max) {
  9239. max = remainder[i];
  9240. maxId = i;
  9241. }
  9242. }
  9243. // Add a vote to max remainder.
  9244. ++seats[maxId];
  9245. remainder[maxId] = 0;
  9246. ++currentSum;
  9247. }
  9248. return seats[idx] / digits;
  9249. }
  9250. // Number.MAX_SAFE_INTEGER, ie do not support.
  9251. /**
  9252. * To 0 - 2 * PI, considering negative radian.
  9253. * @param {number} radian
  9254. * @return {number}
  9255. */
  9256. function remRadian(radian) {
  9257. var pi2 = Math.PI * 2;
  9258. return (radian % pi2 + pi2) % pi2;
  9259. }
  9260. /**
  9261. * @param {type} radian
  9262. * @return {boolean}
  9263. */
  9264. function isRadianAroundZero(val) {
  9265. return val > -RADIAN_EPSILON && val < RADIAN_EPSILON;
  9266. }
  9267. var TIME_REG = /^(?:(\d{4})(?:[-\/](\d{1,2})(?:[-\/](\d{1,2})(?:[T ](\d{1,2})(?::(\d\d)(?::(\d\d)(?:[.,](\d+))?)?)?(Z|[\+\-]\d\d:?\d\d)?)?)?)?)?$/; // jshint ignore:line
  9268. /**
  9269. * @param {string|Date|number} value These values can be accepted:
  9270. * + An instance of Date, represent a time in its own time zone.
  9271. * + Or string in a subset of ISO 8601, only including:
  9272. * + only year, month, date: '2012-03', '2012-03-01', '2012-03-01 05', '2012-03-01 05:06',
  9273. * + separated with T or space: '2012-03-01T12:22:33.123', '2012-03-01 12:22:33.123',
  9274. * + time zone: '2012-03-01T12:22:33Z', '2012-03-01T12:22:33+8000', '2012-03-01T12:22:33-05:00',
  9275. * all of which will be treated as local time if time zone is not specified
  9276. * (see <https://momentjs.com/>).
  9277. * + Or other string format, including (all of which will be treated as loacal time):
  9278. * '2012', '2012-3-1', '2012/3/1', '2012/03/01',
  9279. * '2009/6/12 2:00', '2009/6/12 2:05:08', '2009/6/12 2:05:08.123'
  9280. * + a timestamp, which represent a time in UTC.
  9281. * @return {Date} date
  9282. */
  9283. function parseDate(value) {
  9284. if (value instanceof Date) {
  9285. return value;
  9286. }
  9287. else if (typeof value === 'string') {
  9288. // Different browsers parse date in different way, so we parse it manually.
  9289. // Some other issues:
  9290. // new Date('1970-01-01') is UTC,
  9291. // new Date('1970/01/01') and new Date('1970-1-01') is local.
  9292. // See issue #3623
  9293. var match = TIME_REG.exec(value);
  9294. if (!match) {
  9295. // return Invalid Date.
  9296. return new Date(NaN);
  9297. }
  9298. // Use local time when no timezone offset specifed.
  9299. if (!match[8]) {
  9300. // match[n] can only be string or undefined.
  9301. // But take care of '12' + 1 => '121'.
  9302. return new Date(
  9303. +match[1],
  9304. +(match[2] || 1) - 1,
  9305. +match[3] || 1,
  9306. +match[4] || 0,
  9307. +(match[5] || 0),
  9308. +match[6] || 0,
  9309. +match[7] || 0
  9310. );
  9311. }
  9312. // Timezoneoffset of Javascript Date has considered DST (Daylight Saving Time,
  9313. // https://tc39.github.io/ecma262/#sec-daylight-saving-time-adjustment).
  9314. // For example, system timezone is set as "Time Zone: America/Toronto",
  9315. // then these code will get different result:
  9316. // `new Date(1478411999999).getTimezoneOffset(); // get 240`
  9317. // `new Date(1478412000000).getTimezoneOffset(); // get 300`
  9318. // So we should not use `new Date`, but use `Date.UTC`.
  9319. else {
  9320. var hour = +match[4] || 0;
  9321. if (match[8].toUpperCase() !== 'Z') {
  9322. hour -= match[8].slice(0, 3);
  9323. }
  9324. return new Date(Date.UTC(
  9325. +match[1],
  9326. +(match[2] || 1) - 1,
  9327. +match[3] || 1,
  9328. hour,
  9329. +(match[5] || 0),
  9330. +match[6] || 0,
  9331. +match[7] || 0
  9332. ));
  9333. }
  9334. }
  9335. else if (value == null) {
  9336. return new Date(NaN);
  9337. }
  9338. return new Date(Math.round(value));
  9339. }
  9340. /**
  9341. * Quantity of a number. e.g. 0.1, 1, 10, 100
  9342. *
  9343. * @param {number} val
  9344. * @return {number}
  9345. */
  9346. function quantity(val) {
  9347. return Math.pow(10, quantityExponent(val));
  9348. }
  9349. function quantityExponent(val) {
  9350. return Math.floor(Math.log(val) / Math.LN10);
  9351. }
  9352. /**
  9353. * find a “nice” number approximately equal to x. Round the number if round = true,
  9354. * take ceiling if round = false. The primary observation is that the “nicest”
  9355. * numbers in decimal are 1, 2, and 5, and all power-of-ten multiples of these numbers.
  9356. *
  9357. * See "Nice Numbers for Graph Labels" of Graphic Gems.
  9358. *
  9359. * @param {number} val Non-negative value.
  9360. * @param {boolean} round
  9361. * @return {number}
  9362. */
  9363. function nice(val, round) {
  9364. var exponent = quantityExponent(val);
  9365. var exp10 = Math.pow(10, exponent);
  9366. var f = val / exp10; // 1 <= f < 10
  9367. var nf;
  9368. if (round) {
  9369. if (f < 1.5) { nf = 1; }
  9370. else if (f < 2.5) { nf = 2; }
  9371. else if (f < 4) { nf = 3; }
  9372. else if (f < 7) { nf = 5; }
  9373. else { nf = 10; }
  9374. }
  9375. else {
  9376. if (f < 1) { nf = 1; }
  9377. else if (f < 2) { nf = 2; }
  9378. else if (f < 3) { nf = 3; }
  9379. else if (f < 5) { nf = 5; }
  9380. else { nf = 10; }
  9381. }
  9382. val = nf * exp10;
  9383. // Fix 3 * 0.1 === 0.30000000000000004 issue (see IEEE 754).
  9384. // 20 is the uppper bound of toFixed.
  9385. return exponent >= -20 ? +val.toFixed(exponent < 0 ? -exponent : 0) : val;
  9386. }
  9387. /**
  9388. * Order intervals asc, and split them when overlap.
  9389. * expect(numberUtil.reformIntervals([
  9390. * {interval: [18, 62], close: [1, 1]},
  9391. * {interval: [-Infinity, -70], close: [0, 0]},
  9392. * {interval: [-70, -26], close: [1, 1]},
  9393. * {interval: [-26, 18], close: [1, 1]},
  9394. * {interval: [62, 150], close: [1, 1]},
  9395. * {interval: [106, 150], close: [1, 1]},
  9396. * {interval: [150, Infinity], close: [0, 0]}
  9397. * ])).toEqual([
  9398. * {interval: [-Infinity, -70], close: [0, 0]},
  9399. * {interval: [-70, -26], close: [1, 1]},
  9400. * {interval: [-26, 18], close: [0, 1]},
  9401. * {interval: [18, 62], close: [0, 1]},
  9402. * {interval: [62, 150], close: [0, 1]},
  9403. * {interval: [150, Infinity], close: [0, 0]}
  9404. * ]);
  9405. * @param {Array.<Object>} list, where `close` mean open or close
  9406. * of the interval, and Infinity can be used.
  9407. * @return {Array.<Object>} The origin list, which has been reformed.
  9408. */
  9409. /**
  9410. * parseFloat NaNs numeric-cast false positives (null|true|false|"")
  9411. * ...but misinterprets leading-number strings, particularly hex literals ("0x...")
  9412. * subtraction forces infinities to NaN
  9413. *
  9414. * @param {*} v
  9415. * @return {boolean}
  9416. */
  9417. /**
  9418. * 每三位默认加,格式化
  9419. * @param {string|number} x
  9420. * @return {string}
  9421. */
  9422. function addCommas(x) {
  9423. if (isNaN(x)) {
  9424. return '-';
  9425. }
  9426. x = (x + '').split('.');
  9427. return x[0].replace(/(\d{1,3})(?=(?:\d{3})+(?!\d))/g,'$1,')
  9428. + (x.length > 1 ? ('.' + x[1]) : '');
  9429. }
  9430. /**
  9431. * @param {string} str
  9432. * @param {boolean} [upperCaseFirst=false]
  9433. * @return {string} str
  9434. */
  9435. var normalizeCssArray$1 = normalizeCssArray;
  9436. function encodeHTML(source) {
  9437. return String(source)
  9438. .replace(/&/g, '&amp;')
  9439. .replace(/</g, '&lt;')
  9440. .replace(/>/g, '&gt;')
  9441. .replace(/"/g, '&quot;')
  9442. .replace(/'/g, '&#39;');
  9443. }
  9444. var TPL_VAR_ALIAS = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
  9445. var wrapVar = function (varName, seriesIdx) {
  9446. return '{' + varName + (seriesIdx == null ? '' : seriesIdx) + '}';
  9447. };
  9448. /**
  9449. * Template formatter
  9450. * @param {string} tpl
  9451. * @param {Array.<Object>|Object} paramsList
  9452. * @param {boolean} [encode=false]
  9453. * @return {string}
  9454. */
  9455. function formatTpl(tpl, paramsList, encode) {
  9456. if (!isArray(paramsList)) {
  9457. paramsList = [paramsList];
  9458. }
  9459. var seriesLen = paramsList.length;
  9460. if (!seriesLen) {
  9461. return '';
  9462. }
  9463. var $vars = paramsList[0].$vars || [];
  9464. for (var i = 0; i < $vars.length; i++) {
  9465. var alias = TPL_VAR_ALIAS[i];
  9466. var val = wrapVar(alias, 0);
  9467. tpl = tpl.replace(wrapVar(alias), encode ? encodeHTML(val) : val);
  9468. }
  9469. for (var seriesIdx = 0; seriesIdx < seriesLen; seriesIdx++) {
  9470. for (var k = 0; k < $vars.length; k++) {
  9471. var val = paramsList[seriesIdx][$vars[k]];
  9472. tpl = tpl.replace(
  9473. wrapVar(TPL_VAR_ALIAS[k], seriesIdx),
  9474. encode ? encodeHTML(val) : val
  9475. );
  9476. }
  9477. }
  9478. return tpl;
  9479. }
  9480. /**
  9481. * simple Template formatter
  9482. *
  9483. * @param {string} tpl
  9484. * @param {Object} param
  9485. * @param {boolean} [encode=false]
  9486. * @return {string}
  9487. */
  9488. /**
  9489. * @param {string} color
  9490. * @param {string} [extraCssText]
  9491. * @return {string}
  9492. */
  9493. function getTooltipMarker(color, extraCssText) {
  9494. return color
  9495. ? '<span style="display:inline-block;margin-right:5px;'
  9496. + 'border-radius:10px;width:9px;height:9px;background-color:'
  9497. + encodeHTML(color) + ';' + (extraCssText || '') + '"></span>'
  9498. : '';
  9499. }
  9500. /**
  9501. * @param {string} str
  9502. * @return {string}
  9503. * @inner
  9504. */
  9505. var s2d = function (str) {
  9506. return str < 10 ? ('0' + str) : str;
  9507. };
  9508. /**
  9509. * ISO Date format
  9510. * @param {string} tpl
  9511. * @param {number} value
  9512. * @param {boolean} [isUTC=false] Default in local time.
  9513. * see `module:echarts/scale/Time`
  9514. * and `module:echarts/util/number#parseDate`.
  9515. * @inner
  9516. */
  9517. function formatTime(tpl, value, isUTC) {
  9518. if (tpl === 'week'
  9519. || tpl === 'month'
  9520. || tpl === 'quarter'
  9521. || tpl === 'half-year'
  9522. || tpl === 'year'
  9523. ) {
  9524. tpl = 'MM-dd\nyyyy';
  9525. }
  9526. var date = parseDate(value);
  9527. var utc = isUTC ? 'UTC' : '';
  9528. var y = date['get' + utc + 'FullYear']();
  9529. var M = date['get' + utc + 'Month']() + 1;
  9530. var d = date['get' + utc + 'Date']();
  9531. var h = date['get' + utc + 'Hours']();
  9532. var m = date['get' + utc + 'Minutes']();
  9533. var s = date['get' + utc + 'Seconds']();
  9534. tpl = tpl.replace('MM', s2d(M))
  9535. .replace('M', M)
  9536. .replace('yyyy', y)
  9537. .replace('yy', y % 100)
  9538. .replace('dd', s2d(d))
  9539. .replace('d', d)
  9540. .replace('hh', s2d(h))
  9541. .replace('h', h)
  9542. .replace('mm', s2d(m))
  9543. .replace('m', m)
  9544. .replace('ss', s2d(s))
  9545. .replace('s', s);
  9546. return tpl;
  9547. }
  9548. /**
  9549. * Capital first
  9550. * @param {string} str
  9551. * @return {string}
  9552. */
  9553. var truncateText$1 = truncateText;
  9554. var TYPE_DELIMITER = '.';
  9555. var IS_CONTAINER = '___EC__COMPONENT__CONTAINER___';
  9556. var MEMBER_PRIFIX = '\0ec_\0';
  9557. /**
  9558. * Hide private class member.
  9559. * The same behavior as `host[name] = value;` (can be right-value)
  9560. * @public
  9561. */
  9562. function set$1(host, name, value) {
  9563. return (host[MEMBER_PRIFIX + name] = value);
  9564. }
  9565. /**
  9566. * Hide private class member.
  9567. * The same behavior as `host[name];`
  9568. * @public
  9569. */
  9570. function get(host, name) {
  9571. return host[MEMBER_PRIFIX + name];
  9572. }
  9573. /**
  9574. * For hidden private class member.
  9575. * The same behavior as `host.hasOwnProperty(name);`
  9576. * @public
  9577. */
  9578. function hasOwn(host, name) {
  9579. return host.hasOwnProperty(MEMBER_PRIFIX + name);
  9580. }
  9581. /**
  9582. * Notice, parseClassType('') should returns {main: '', sub: ''}
  9583. * @public
  9584. */
  9585. function parseClassType$1(componentType) {
  9586. var ret = {main: '', sub: ''};
  9587. if (componentType) {
  9588. componentType = componentType.split(TYPE_DELIMITER);
  9589. ret.main = componentType[0] || '';
  9590. ret.sub = componentType[1] || '';
  9591. }
  9592. return ret;
  9593. }
  9594. /**
  9595. * @public
  9596. */
  9597. function checkClassType(componentType) {
  9598. assert(
  9599. /^[a-zA-Z0-9_]+([.][a-zA-Z0-9_]+)?$/.test(componentType),
  9600. 'componentType "' + componentType + '" illegal'
  9601. );
  9602. }
  9603. /**
  9604. * @public
  9605. */
  9606. function enableClassExtend(RootClass, mandatoryMethods) {
  9607. RootClass.$constructor = RootClass;
  9608. RootClass.extend = function (proto) {
  9609. if (__DEV__) {
  9610. each$1(mandatoryMethods, function (method) {
  9611. if (!proto[method]) {
  9612. console.warn(
  9613. 'Method `' + method + '` should be implemented'
  9614. + (proto.type ? ' in ' + proto.type : '') + '.'
  9615. );
  9616. }
  9617. });
  9618. }
  9619. var superClass = this;
  9620. var ExtendedClass = function () {
  9621. if (!proto.$constructor) {
  9622. superClass.apply(this, arguments);
  9623. }
  9624. else {
  9625. proto.$constructor.apply(this, arguments);
  9626. }
  9627. };
  9628. extend(ExtendedClass.prototype, proto);
  9629. ExtendedClass.extend = this.extend;
  9630. ExtendedClass.superCall = superCall;
  9631. ExtendedClass.superApply = superApply;
  9632. inherits(ExtendedClass, this);
  9633. ExtendedClass.superClass = superClass;
  9634. return ExtendedClass;
  9635. };
  9636. }
  9637. // superCall should have class info, which can not be fetch from 'this'.
  9638. // Consider this case:
  9639. // class A has method f,
  9640. // class B inherits class A, overrides method f, f call superApply('f'),
  9641. // class C inherits class B, do not overrides method f,
  9642. // then when method of class C is called, dead loop occured.
  9643. function superCall(context, methodName) {
  9644. var args = slice(arguments, 2);
  9645. return this.superClass.prototype[methodName].apply(context, args);
  9646. }
  9647. function superApply(context, methodName, args) {
  9648. return this.superClass.prototype[methodName].apply(context, args);
  9649. }
  9650. /**
  9651. * @param {Object} entity
  9652. * @param {Object} options
  9653. * @param {boolean} [options.registerWhenExtend]
  9654. * @public
  9655. */
  9656. function enableClassManagement(entity, options) {
  9657. options = options || {};
  9658. /**
  9659. * Component model classes
  9660. * key: componentType,
  9661. * value:
  9662. * componentClass, when componentType is 'xxx'
  9663. * or Object.<subKey, componentClass>, when componentType is 'xxx.yy'
  9664. * @type {Object}
  9665. */
  9666. var storage = {};
  9667. entity.registerClass = function (Clazz, componentType) {
  9668. if (componentType) {
  9669. checkClassType(componentType);
  9670. componentType = parseClassType$1(componentType);
  9671. if (!componentType.sub) {
  9672. if (__DEV__) {
  9673. if (storage[componentType.main]) {
  9674. console.warn(componentType.main + ' exists.');
  9675. }
  9676. }
  9677. storage[componentType.main] = Clazz;
  9678. }
  9679. else if (componentType.sub !== IS_CONTAINER) {
  9680. var container = makeContainer(componentType);
  9681. container[componentType.sub] = Clazz;
  9682. }
  9683. }
  9684. return Clazz;
  9685. };
  9686. entity.getClass = function (componentMainType, subType, throwWhenNotFound) {
  9687. var Clazz = storage[componentMainType];
  9688. if (Clazz && Clazz[IS_CONTAINER]) {
  9689. Clazz = subType ? Clazz[subType] : null;
  9690. }
  9691. if (throwWhenNotFound && !Clazz) {
  9692. throw new Error(
  9693. !subType
  9694. ? componentMainType + '.' + 'type should be specified.'
  9695. : 'Component ' + componentMainType + '.' + (subType || '') + ' not exists. Load it first.'
  9696. );
  9697. }
  9698. return Clazz;
  9699. };
  9700. entity.getClassesByMainType = function (componentType) {
  9701. componentType = parseClassType$1(componentType);
  9702. var result = [];
  9703. var obj = storage[componentType.main];
  9704. if (obj && obj[IS_CONTAINER]) {
  9705. each$1(obj, function (o, type) {
  9706. type !== IS_CONTAINER && result.push(o);
  9707. });
  9708. }
  9709. else {
  9710. result.push(obj);
  9711. }
  9712. return result;
  9713. };
  9714. entity.hasClass = function (componentType) {
  9715. // Just consider componentType.main.
  9716. componentType = parseClassType$1(componentType);
  9717. return !!storage[componentType.main];
  9718. };
  9719. /**
  9720. * @return {Array.<string>} Like ['aa', 'bb'], but can not be ['aa.xx']
  9721. */
  9722. entity.getAllClassMainTypes = function () {
  9723. var types = [];
  9724. each$1(storage, function (obj, type) {
  9725. types.push(type);
  9726. });
  9727. return types;
  9728. };
  9729. /**
  9730. * If a main type is container and has sub types
  9731. * @param {string} mainType
  9732. * @return {boolean}
  9733. */
  9734. entity.hasSubTypes = function (componentType) {
  9735. componentType = parseClassType$1(componentType);
  9736. var obj = storage[componentType.main];
  9737. return obj && obj[IS_CONTAINER];
  9738. };
  9739. entity.parseClassType = parseClassType$1;
  9740. function makeContainer(componentType) {
  9741. var container = storage[componentType.main];
  9742. if (!container || !container[IS_CONTAINER]) {
  9743. container = storage[componentType.main] = {};
  9744. container[IS_CONTAINER] = true;
  9745. }
  9746. return container;
  9747. }
  9748. if (options.registerWhenExtend) {
  9749. var originalExtend = entity.extend;
  9750. if (originalExtend) {
  9751. entity.extend = function (proto) {
  9752. var ExtendedClass = originalExtend.call(this, proto);
  9753. return entity.registerClass(ExtendedClass, proto.type);
  9754. };
  9755. }
  9756. }
  9757. return entity;
  9758. }
  9759. /**
  9760. * @param {string|Array.<string>} properties
  9761. */
  9762. // TODO Parse shadow style
  9763. // TODO Only shallow path support
  9764. var makeStyleMapper = function (properties) {
  9765. // Normalize
  9766. for (var i = 0; i < properties.length; i++) {
  9767. if (!properties[i][1]) {
  9768. properties[i][1] = properties[i][0];
  9769. }
  9770. }
  9771. return function (model, excludes, includes) {
  9772. var style = {};
  9773. for (var i = 0; i < properties.length; i++) {
  9774. var propName = properties[i][1];
  9775. if ((excludes && indexOf(excludes, propName) >= 0)
  9776. || (includes && indexOf(includes, propName) < 0)
  9777. ) {
  9778. continue;
  9779. }
  9780. var val = model.getShallow(propName);
  9781. if (val != null) {
  9782. style[properties[i][0]] = val;
  9783. }
  9784. }
  9785. return style;
  9786. };
  9787. };
  9788. var getLineStyle = makeStyleMapper(
  9789. [
  9790. ['lineWidth', 'width'],
  9791. ['stroke', 'color'],
  9792. ['opacity'],
  9793. ['shadowBlur'],
  9794. ['shadowOffsetX'],
  9795. ['shadowOffsetY'],
  9796. ['shadowColor']
  9797. ]
  9798. );
  9799. var lineStyleMixin = {
  9800. getLineStyle: function (excludes) {
  9801. var style = getLineStyle(this, excludes);
  9802. var lineDash = this.getLineDash(style.lineWidth);
  9803. lineDash && (style.lineDash = lineDash);
  9804. return style;
  9805. },
  9806. getLineDash: function (lineWidth) {
  9807. if (lineWidth == null) {
  9808. lineWidth = 1;
  9809. }
  9810. var lineType = this.get('type');
  9811. var dotSize = Math.max(lineWidth, 2);
  9812. var dashSize = lineWidth * 4;
  9813. return (lineType === 'solid' || lineType == null) ? null
  9814. : (lineType === 'dashed' ? [dashSize, dashSize] : [dotSize, dotSize]);
  9815. }
  9816. };
  9817. var getAreaStyle = makeStyleMapper(
  9818. [
  9819. ['fill', 'color'],
  9820. ['shadowBlur'],
  9821. ['shadowOffsetX'],
  9822. ['shadowOffsetY'],
  9823. ['opacity'],
  9824. ['shadowColor']
  9825. ]
  9826. );
  9827. var areaStyleMixin = {
  9828. getAreaStyle: function (excludes, includes) {
  9829. return getAreaStyle(this, excludes, includes);
  9830. }
  9831. };
  9832. /**
  9833. * 曲线辅助模块
  9834. * @module zrender/core/curve
  9835. * @author pissang(https://www.github.com/pissang)
  9836. */
  9837. var mathPow = Math.pow;
  9838. var mathSqrt$2 = Math.sqrt;
  9839. var EPSILON$1 = 1e-8;
  9840. var EPSILON_NUMERIC = 1e-4;
  9841. var THREE_SQRT = mathSqrt$2(3);
  9842. var ONE_THIRD = 1 / 3;
  9843. // 临时变量
  9844. var _v0 = create();
  9845. var _v1 = create();
  9846. var _v2 = create();
  9847. function isAroundZero(val) {
  9848. return val > -EPSILON$1 && val < EPSILON$1;
  9849. }
  9850. function isNotAroundZero$1(val) {
  9851. return val > EPSILON$1 || val < -EPSILON$1;
  9852. }
  9853. /**
  9854. * 计算三次贝塞尔值
  9855. * @memberOf module:zrender/core/curve
  9856. * @param {number} p0
  9857. * @param {number} p1
  9858. * @param {number} p2
  9859. * @param {number} p3
  9860. * @param {number} t
  9861. * @return {number}
  9862. */
  9863. function cubicAt(p0, p1, p2, p3, t) {
  9864. var onet = 1 - t;
  9865. return onet * onet * (onet * p0 + 3 * t * p1)
  9866. + t * t * (t * p3 + 3 * onet * p2);
  9867. }
  9868. /**
  9869. * 计算三次贝塞尔导数值
  9870. * @memberOf module:zrender/core/curve
  9871. * @param {number} p0
  9872. * @param {number} p1
  9873. * @param {number} p2
  9874. * @param {number} p3
  9875. * @param {number} t
  9876. * @return {number}
  9877. */
  9878. function cubicDerivativeAt(p0, p1, p2, p3, t) {
  9879. var onet = 1 - t;
  9880. return 3 * (
  9881. ((p1 - p0) * onet + 2 * (p2 - p1) * t) * onet
  9882. + (p3 - p2) * t * t
  9883. );
  9884. }
  9885. /**
  9886. * 计算三次贝塞尔方程根,使用盛金公式
  9887. * @memberOf module:zrender/core/curve
  9888. * @param {number} p0
  9889. * @param {number} p1
  9890. * @param {number} p2
  9891. * @param {number} p3
  9892. * @param {number} val
  9893. * @param {Array.<number>} roots
  9894. * @return {number} 有效根数目
  9895. */
  9896. function cubicRootAt(p0, p1, p2, p3, val, roots) {
  9897. // Evaluate roots of cubic functions
  9898. var a = p3 + 3 * (p1 - p2) - p0;
  9899. var b = 3 * (p2 - p1 * 2 + p0);
  9900. var c = 3 * (p1 - p0);
  9901. var d = p0 - val;
  9902. var A = b * b - 3 * a * c;
  9903. var B = b * c - 9 * a * d;
  9904. var C = c * c - 3 * b * d;
  9905. var n = 0;
  9906. if (isAroundZero(A) && isAroundZero(B)) {
  9907. if (isAroundZero(b)) {
  9908. roots[0] = 0;
  9909. }
  9910. else {
  9911. var t1 = -c / b; //t1, t2, t3, b is not zero
  9912. if (t1 >= 0 && t1 <= 1) {
  9913. roots[n++] = t1;
  9914. }
  9915. }
  9916. }
  9917. else {
  9918. var disc = B * B - 4 * A * C;
  9919. if (isAroundZero(disc)) {
  9920. var K = B / A;
  9921. var t1 = -b / a + K; // t1, a is not zero
  9922. var t2 = -K / 2; // t2, t3
  9923. if (t1 >= 0 && t1 <= 1) {
  9924. roots[n++] = t1;
  9925. }
  9926. if (t2 >= 0 && t2 <= 1) {
  9927. roots[n++] = t2;
  9928. }
  9929. }
  9930. else if (disc > 0) {
  9931. var discSqrt = mathSqrt$2(disc);
  9932. var Y1 = A * b + 1.5 * a * (-B + discSqrt);
  9933. var Y2 = A * b + 1.5 * a * (-B - discSqrt);
  9934. if (Y1 < 0) {
  9935. Y1 = -mathPow(-Y1, ONE_THIRD);
  9936. }
  9937. else {
  9938. Y1 = mathPow(Y1, ONE_THIRD);
  9939. }
  9940. if (Y2 < 0) {
  9941. Y2 = -mathPow(-Y2, ONE_THIRD);
  9942. }
  9943. else {
  9944. Y2 = mathPow(Y2, ONE_THIRD);
  9945. }
  9946. var t1 = (-b - (Y1 + Y2)) / (3 * a);
  9947. if (t1 >= 0 && t1 <= 1) {
  9948. roots[n++] = t1;
  9949. }
  9950. }
  9951. else {
  9952. var T = (2 * A * b - 3 * a * B) / (2 * mathSqrt$2(A * A * A));
  9953. var theta = Math.acos(T) / 3;
  9954. var ASqrt = mathSqrt$2(A);
  9955. var tmp = Math.cos(theta);
  9956. var t1 = (-b - 2 * ASqrt * tmp) / (3 * a);
  9957. var t2 = (-b + ASqrt * (tmp + THREE_SQRT * Math.sin(theta))) / (3 * a);
  9958. var t3 = (-b + ASqrt * (tmp - THREE_SQRT * Math.sin(theta))) / (3 * a);
  9959. if (t1 >= 0 && t1 <= 1) {
  9960. roots[n++] = t1;
  9961. }
  9962. if (t2 >= 0 && t2 <= 1) {
  9963. roots[n++] = t2;
  9964. }
  9965. if (t3 >= 0 && t3 <= 1) {
  9966. roots[n++] = t3;
  9967. }
  9968. }
  9969. }
  9970. return n;
  9971. }
  9972. /**
  9973. * 计算三次贝塞尔方程极限值的位置
  9974. * @memberOf module:zrender/core/curve
  9975. * @param {number} p0
  9976. * @param {number} p1
  9977. * @param {number} p2
  9978. * @param {number} p3
  9979. * @param {Array.<number>} extrema
  9980. * @return {number} 有效数目
  9981. */
  9982. function cubicExtrema(p0, p1, p2, p3, extrema) {
  9983. var b = 6 * p2 - 12 * p1 + 6 * p0;
  9984. var a = 9 * p1 + 3 * p3 - 3 * p0 - 9 * p2;
  9985. var c = 3 * p1 - 3 * p0;
  9986. var n = 0;
  9987. if (isAroundZero(a)) {
  9988. if (isNotAroundZero$1(b)) {
  9989. var t1 = -c / b;
  9990. if (t1 >= 0 && t1 <=1) {
  9991. extrema[n++] = t1;
  9992. }
  9993. }
  9994. }
  9995. else {
  9996. var disc = b * b - 4 * a * c;
  9997. if (isAroundZero(disc)) {
  9998. extrema[0] = -b / (2 * a);
  9999. }
  10000. else if (disc > 0) {
  10001. var discSqrt = mathSqrt$2(disc);
  10002. var t1 = (-b + discSqrt) / (2 * a);
  10003. var t2 = (-b - discSqrt) / (2 * a);
  10004. if (t1 >= 0 && t1 <= 1) {
  10005. extrema[n++] = t1;
  10006. }
  10007. if (t2 >= 0 && t2 <= 1) {
  10008. extrema[n++] = t2;
  10009. }
  10010. }
  10011. }
  10012. return n;
  10013. }
  10014. /**
  10015. * 细分三次贝塞尔曲线
  10016. * @memberOf module:zrender/core/curve
  10017. * @param {number} p0
  10018. * @param {number} p1
  10019. * @param {number} p2
  10020. * @param {number} p3
  10021. * @param {number} t
  10022. * @param {Array.<number>} out
  10023. */
  10024. function cubicSubdivide(p0, p1, p2, p3, t, out) {
  10025. var p01 = (p1 - p0) * t + p0;
  10026. var p12 = (p2 - p1) * t + p1;
  10027. var p23 = (p3 - p2) * t + p2;
  10028. var p012 = (p12 - p01) * t + p01;
  10029. var p123 = (p23 - p12) * t + p12;
  10030. var p0123 = (p123 - p012) * t + p012;
  10031. // Seg0
  10032. out[0] = p0;
  10033. out[1] = p01;
  10034. out[2] = p012;
  10035. out[3] = p0123;
  10036. // Seg1
  10037. out[4] = p0123;
  10038. out[5] = p123;
  10039. out[6] = p23;
  10040. out[7] = p3;
  10041. }
  10042. /**
  10043. * 投射点到三次贝塞尔曲线上,返回投射距离。
  10044. * 投射点有可能会有一个或者多个,这里只返回其中距离最短的一个。
  10045. * @param {number} x0
  10046. * @param {number} y0
  10047. * @param {number} x1
  10048. * @param {number} y1
  10049. * @param {number} x2
  10050. * @param {number} y2
  10051. * @param {number} x3
  10052. * @param {number} y3
  10053. * @param {number} x
  10054. * @param {number} y
  10055. * @param {Array.<number>} [out] 投射点
  10056. * @return {number}
  10057. */
  10058. function cubicProjectPoint(
  10059. x0, y0, x1, y1, x2, y2, x3, y3,
  10060. x, y, out
  10061. ) {
  10062. // http://pomax.github.io/bezierinfo/#projections
  10063. var t;
  10064. var interval = 0.005;
  10065. var d = Infinity;
  10066. var prev;
  10067. var next;
  10068. var d1;
  10069. var d2;
  10070. _v0[0] = x;
  10071. _v0[1] = y;
  10072. // 先粗略估计一下可能的最小距离的 t 值
  10073. // PENDING
  10074. for (var _t = 0; _t < 1; _t += 0.05) {
  10075. _v1[0] = cubicAt(x0, x1, x2, x3, _t);
  10076. _v1[1] = cubicAt(y0, y1, y2, y3, _t);
  10077. d1 = distSquare(_v0, _v1);
  10078. if (d1 < d) {
  10079. t = _t;
  10080. d = d1;
  10081. }
  10082. }
  10083. d = Infinity;
  10084. // At most 32 iteration
  10085. for (var i = 0; i < 32; i++) {
  10086. if (interval < EPSILON_NUMERIC) {
  10087. break;
  10088. }
  10089. prev = t - interval;
  10090. next = t + interval;
  10091. // t - interval
  10092. _v1[0] = cubicAt(x0, x1, x2, x3, prev);
  10093. _v1[1] = cubicAt(y0, y1, y2, y3, prev);
  10094. d1 = distSquare(_v1, _v0);
  10095. if (prev >= 0 && d1 < d) {
  10096. t = prev;
  10097. d = d1;
  10098. }
  10099. else {
  10100. // t + interval
  10101. _v2[0] = cubicAt(x0, x1, x2, x3, next);
  10102. _v2[1] = cubicAt(y0, y1, y2, y3, next);
  10103. d2 = distSquare(_v2, _v0);
  10104. if (next <= 1 && d2 < d) {
  10105. t = next;
  10106. d = d2;
  10107. }
  10108. else {
  10109. interval *= 0.5;
  10110. }
  10111. }
  10112. }
  10113. // t
  10114. if (out) {
  10115. out[0] = cubicAt(x0, x1, x2, x3, t);
  10116. out[1] = cubicAt(y0, y1, y2, y3, t);
  10117. }
  10118. // console.log(interval, i);
  10119. return mathSqrt$2(d);
  10120. }
  10121. /**
  10122. * 计算二次方贝塞尔值
  10123. * @param {number} p0
  10124. * @param {number} p1
  10125. * @param {number} p2
  10126. * @param {number} t
  10127. * @return {number}
  10128. */
  10129. function quadraticAt(p0, p1, p2, t) {
  10130. var onet = 1 - t;
  10131. return onet * (onet * p0 + 2 * t * p1) + t * t * p2;
  10132. }
  10133. /**
  10134. * 计算二次方贝塞尔导数值
  10135. * @param {number} p0
  10136. * @param {number} p1
  10137. * @param {number} p2
  10138. * @param {number} t
  10139. * @return {number}
  10140. */
  10141. function quadraticDerivativeAt(p0, p1, p2, t) {
  10142. return 2 * ((1 - t) * (p1 - p0) + t * (p2 - p1));
  10143. }
  10144. /**
  10145. * 计算二次方贝塞尔方程根
  10146. * @param {number} p0
  10147. * @param {number} p1
  10148. * @param {number} p2
  10149. * @param {number} t
  10150. * @param {Array.<number>} roots
  10151. * @return {number} 有效根数目
  10152. */
  10153. function quadraticRootAt(p0, p1, p2, val, roots) {
  10154. var a = p0 - 2 * p1 + p2;
  10155. var b = 2 * (p1 - p0);
  10156. var c = p0 - val;
  10157. var n = 0;
  10158. if (isAroundZero(a)) {
  10159. if (isNotAroundZero$1(b)) {
  10160. var t1 = -c / b;
  10161. if (t1 >= 0 && t1 <= 1) {
  10162. roots[n++] = t1;
  10163. }
  10164. }
  10165. }
  10166. else {
  10167. var disc = b * b - 4 * a * c;
  10168. if (isAroundZero(disc)) {
  10169. var t1 = -b / (2 * a);
  10170. if (t1 >= 0 && t1 <= 1) {
  10171. roots[n++] = t1;
  10172. }
  10173. }
  10174. else if (disc > 0) {
  10175. var discSqrt = mathSqrt$2(disc);
  10176. var t1 = (-b + discSqrt) / (2 * a);
  10177. var t2 = (-b - discSqrt) / (2 * a);
  10178. if (t1 >= 0 && t1 <= 1) {
  10179. roots[n++] = t1;
  10180. }
  10181. if (t2 >= 0 && t2 <= 1) {
  10182. roots[n++] = t2;
  10183. }
  10184. }
  10185. }
  10186. return n;
  10187. }
  10188. /**
  10189. * 计算二次贝塞尔方程极限值
  10190. * @memberOf module:zrender/core/curve
  10191. * @param {number} p0
  10192. * @param {number} p1
  10193. * @param {number} p2
  10194. * @return {number}
  10195. */
  10196. function quadraticExtremum(p0, p1, p2) {
  10197. var divider = p0 + p2 - 2 * p1;
  10198. if (divider === 0) {
  10199. // p1 is center of p0 and p2
  10200. return 0.5;
  10201. }
  10202. else {
  10203. return (p0 - p1) / divider;
  10204. }
  10205. }
  10206. /**
  10207. * 细分二次贝塞尔曲线
  10208. * @memberOf module:zrender/core/curve
  10209. * @param {number} p0
  10210. * @param {number} p1
  10211. * @param {number} p2
  10212. * @param {number} t
  10213. * @param {Array.<number>} out
  10214. */
  10215. function quadraticSubdivide(p0, p1, p2, t, out) {
  10216. var p01 = (p1 - p0) * t + p0;
  10217. var p12 = (p2 - p1) * t + p1;
  10218. var p012 = (p12 - p01) * t + p01;
  10219. // Seg0
  10220. out[0] = p0;
  10221. out[1] = p01;
  10222. out[2] = p012;
  10223. // Seg1
  10224. out[3] = p012;
  10225. out[4] = p12;
  10226. out[5] = p2;
  10227. }
  10228. /**
  10229. * 投射点到二次贝塞尔曲线上,返回投射距离。
  10230. * 投射点有可能会有一个或者多个,这里只返回其中距离最短的一个。
  10231. * @param {number} x0
  10232. * @param {number} y0
  10233. * @param {number} x1
  10234. * @param {number} y1
  10235. * @param {number} x2
  10236. * @param {number} y2
  10237. * @param {number} x
  10238. * @param {number} y
  10239. * @param {Array.<number>} out 投射点
  10240. * @return {number}
  10241. */
  10242. function quadraticProjectPoint(
  10243. x0, y0, x1, y1, x2, y2,
  10244. x, y, out
  10245. ) {
  10246. // http://pomax.github.io/bezierinfo/#projections
  10247. var t;
  10248. var interval = 0.005;
  10249. var d = Infinity;
  10250. _v0[0] = x;
  10251. _v0[1] = y;
  10252. // 先粗略估计一下可能的最小距离的 t 值
  10253. // PENDING
  10254. for (var _t = 0; _t < 1; _t += 0.05) {
  10255. _v1[0] = quadraticAt(x0, x1, x2, _t);
  10256. _v1[1] = quadraticAt(y0, y1, y2, _t);
  10257. var d1 = distSquare(_v0, _v1);
  10258. if (d1 < d) {
  10259. t = _t;
  10260. d = d1;
  10261. }
  10262. }
  10263. d = Infinity;
  10264. // At most 32 iteration
  10265. for (var i = 0; i < 32; i++) {
  10266. if (interval < EPSILON_NUMERIC) {
  10267. break;
  10268. }
  10269. var prev = t - interval;
  10270. var next = t + interval;
  10271. // t - interval
  10272. _v1[0] = quadraticAt(x0, x1, x2, prev);
  10273. _v1[1] = quadraticAt(y0, y1, y2, prev);
  10274. var d1 = distSquare(_v1, _v0);
  10275. if (prev >= 0 && d1 < d) {
  10276. t = prev;
  10277. d = d1;
  10278. }
  10279. else {
  10280. // t + interval
  10281. _v2[0] = quadraticAt(x0, x1, x2, next);
  10282. _v2[1] = quadraticAt(y0, y1, y2, next);
  10283. var d2 = distSquare(_v2, _v0);
  10284. if (next <= 1 && d2 < d) {
  10285. t = next;
  10286. d = d2;
  10287. }
  10288. else {
  10289. interval *= 0.5;
  10290. }
  10291. }
  10292. }
  10293. // t
  10294. if (out) {
  10295. out[0] = quadraticAt(x0, x1, x2, t);
  10296. out[1] = quadraticAt(y0, y1, y2, t);
  10297. }
  10298. // console.log(interval, i);
  10299. return mathSqrt$2(d);
  10300. }
  10301. /**
  10302. * @author Yi Shen(https://github.com/pissang)
  10303. */
  10304. var mathMin$3 = Math.min;
  10305. var mathMax$3 = Math.max;
  10306. var mathSin$2 = Math.sin;
  10307. var mathCos$2 = Math.cos;
  10308. var PI2 = Math.PI * 2;
  10309. var start = create();
  10310. var end = create();
  10311. var extremity = create();
  10312. /**
  10313. * 从顶点数组中计算出最小包围盒,写入`min`和`max`中
  10314. * @module zrender/core/bbox
  10315. * @param {Array<Object>} points 顶点数组
  10316. * @param {number} min
  10317. * @param {number} max
  10318. */
  10319. /**
  10320. * @memberOf module:zrender/core/bbox
  10321. * @param {number} x0
  10322. * @param {number} y0
  10323. * @param {number} x1
  10324. * @param {number} y1
  10325. * @param {Array.<number>} min
  10326. * @param {Array.<number>} max
  10327. */
  10328. function fromLine(x0, y0, x1, y1, min$$1, max$$1) {
  10329. min$$1[0] = mathMin$3(x0, x1);
  10330. min$$1[1] = mathMin$3(y0, y1);
  10331. max$$1[0] = mathMax$3(x0, x1);
  10332. max$$1[1] = mathMax$3(y0, y1);
  10333. }
  10334. var xDim = [];
  10335. var yDim = [];
  10336. /**
  10337. * 从三阶贝塞尔曲线(p0, p1, p2, p3)中计算出最小包围盒,写入`min`和`max`中
  10338. * @memberOf module:zrender/core/bbox
  10339. * @param {number} x0
  10340. * @param {number} y0
  10341. * @param {number} x1
  10342. * @param {number} y1
  10343. * @param {number} x2
  10344. * @param {number} y2
  10345. * @param {number} x3
  10346. * @param {number} y3
  10347. * @param {Array.<number>} min
  10348. * @param {Array.<number>} max
  10349. */
  10350. function fromCubic(
  10351. x0, y0, x1, y1, x2, y2, x3, y3, min$$1, max$$1
  10352. ) {
  10353. var cubicExtrema$$1 = cubicExtrema;
  10354. var cubicAt$$1 = cubicAt;
  10355. var i;
  10356. var n = cubicExtrema$$1(x0, x1, x2, x3, xDim);
  10357. min$$1[0] = Infinity;
  10358. min$$1[1] = Infinity;
  10359. max$$1[0] = -Infinity;
  10360. max$$1[1] = -Infinity;
  10361. for (i = 0; i < n; i++) {
  10362. var x = cubicAt$$1(x0, x1, x2, x3, xDim[i]);
  10363. min$$1[0] = mathMin$3(x, min$$1[0]);
  10364. max$$1[0] = mathMax$3(x, max$$1[0]);
  10365. }
  10366. n = cubicExtrema$$1(y0, y1, y2, y3, yDim);
  10367. for (i = 0; i < n; i++) {
  10368. var y = cubicAt$$1(y0, y1, y2, y3, yDim[i]);
  10369. min$$1[1] = mathMin$3(y, min$$1[1]);
  10370. max$$1[1] = mathMax$3(y, max$$1[1]);
  10371. }
  10372. min$$1[0] = mathMin$3(x0, min$$1[0]);
  10373. max$$1[0] = mathMax$3(x0, max$$1[0]);
  10374. min$$1[0] = mathMin$3(x3, min$$1[0]);
  10375. max$$1[0] = mathMax$3(x3, max$$1[0]);
  10376. min$$1[1] = mathMin$3(y0, min$$1[1]);
  10377. max$$1[1] = mathMax$3(y0, max$$1[1]);
  10378. min$$1[1] = mathMin$3(y3, min$$1[1]);
  10379. max$$1[1] = mathMax$3(y3, max$$1[1]);
  10380. }
  10381. /**
  10382. * 从二阶贝塞尔曲线(p0, p1, p2)中计算出最小包围盒,写入`min`和`max`中
  10383. * @memberOf module:zrender/core/bbox
  10384. * @param {number} x0
  10385. * @param {number} y0
  10386. * @param {number} x1
  10387. * @param {number} y1
  10388. * @param {number} x2
  10389. * @param {number} y2
  10390. * @param {Array.<number>} min
  10391. * @param {Array.<number>} max
  10392. */
  10393. function fromQuadratic(x0, y0, x1, y1, x2, y2, min$$1, max$$1) {
  10394. var quadraticExtremum$$1 = quadraticExtremum;
  10395. var quadraticAt$$1 = quadraticAt;
  10396. // Find extremities, where derivative in x dim or y dim is zero
  10397. var tx =
  10398. mathMax$3(
  10399. mathMin$3(quadraticExtremum$$1(x0, x1, x2), 1), 0
  10400. );
  10401. var ty =
  10402. mathMax$3(
  10403. mathMin$3(quadraticExtremum$$1(y0, y1, y2), 1), 0
  10404. );
  10405. var x = quadraticAt$$1(x0, x1, x2, tx);
  10406. var y = quadraticAt$$1(y0, y1, y2, ty);
  10407. min$$1[0] = mathMin$3(x0, x2, x);
  10408. min$$1[1] = mathMin$3(y0, y2, y);
  10409. max$$1[0] = mathMax$3(x0, x2, x);
  10410. max$$1[1] = mathMax$3(y0, y2, y);
  10411. }
  10412. /**
  10413. * 从圆弧中计算出最小包围盒,写入`min`和`max`中
  10414. * @method
  10415. * @memberOf module:zrender/core/bbox
  10416. * @param {number} x
  10417. * @param {number} y
  10418. * @param {number} rx
  10419. * @param {number} ry
  10420. * @param {number} startAngle
  10421. * @param {number} endAngle
  10422. * @param {number} anticlockwise
  10423. * @param {Array.<number>} min
  10424. * @param {Array.<number>} max
  10425. */
  10426. function fromArc(
  10427. x, y, rx, ry, startAngle, endAngle, anticlockwise, min$$1, max$$1
  10428. ) {
  10429. var vec2Min = min;
  10430. var vec2Max = max;
  10431. var diff = Math.abs(startAngle - endAngle);
  10432. if (diff % PI2 < 1e-4 && diff > 1e-4) {
  10433. // Is a circle
  10434. min$$1[0] = x - rx;
  10435. min$$1[1] = y - ry;
  10436. max$$1[0] = x + rx;
  10437. max$$1[1] = y + ry;
  10438. return;
  10439. }
  10440. start[0] = mathCos$2(startAngle) * rx + x;
  10441. start[1] = mathSin$2(startAngle) * ry + y;
  10442. end[0] = mathCos$2(endAngle) * rx + x;
  10443. end[1] = mathSin$2(endAngle) * ry + y;
  10444. vec2Min(min$$1, start, end);
  10445. vec2Max(max$$1, start, end);
  10446. // Thresh to [0, Math.PI * 2]
  10447. startAngle = startAngle % (PI2);
  10448. if (startAngle < 0) {
  10449. startAngle = startAngle + PI2;
  10450. }
  10451. endAngle = endAngle % (PI2);
  10452. if (endAngle < 0) {
  10453. endAngle = endAngle + PI2;
  10454. }
  10455. if (startAngle > endAngle && !anticlockwise) {
  10456. endAngle += PI2;
  10457. }
  10458. else if (startAngle < endAngle && anticlockwise) {
  10459. startAngle += PI2;
  10460. }
  10461. if (anticlockwise) {
  10462. var tmp = endAngle;
  10463. endAngle = startAngle;
  10464. startAngle = tmp;
  10465. }
  10466. // var number = 0;
  10467. // var step = (anticlockwise ? -Math.PI : Math.PI) / 2;
  10468. for (var angle = 0; angle < endAngle; angle += Math.PI / 2) {
  10469. if (angle > startAngle) {
  10470. extremity[0] = mathCos$2(angle) * rx + x;
  10471. extremity[1] = mathSin$2(angle) * ry + y;
  10472. vec2Min(min$$1, extremity, min$$1);
  10473. vec2Max(max$$1, extremity, max$$1);
  10474. }
  10475. }
  10476. }
  10477. /**
  10478. * Path 代理,可以在`buildPath`中用于替代`ctx`, 会保存每个path操作的命令到pathCommands属性中
  10479. * 可以用于 isInsidePath 判断以及获取boundingRect
  10480. *
  10481. * @module zrender/core/PathProxy
  10482. * @author Yi Shen (http://www.github.com/pissang)
  10483. */
  10484. // TODO getTotalLength, getPointAtLength
  10485. var CMD = {
  10486. M: 1,
  10487. L: 2,
  10488. C: 3,
  10489. Q: 4,
  10490. A: 5,
  10491. Z: 6,
  10492. // Rect
  10493. R: 7
  10494. };
  10495. // var CMD_MEM_SIZE = {
  10496. // M: 3,
  10497. // L: 3,
  10498. // C: 7,
  10499. // Q: 5,
  10500. // A: 9,
  10501. // R: 5,
  10502. // Z: 1
  10503. // };
  10504. var min$1 = [];
  10505. var max$1 = [];
  10506. var min2 = [];
  10507. var max2 = [];
  10508. var mathMin$2 = Math.min;
  10509. var mathMax$2 = Math.max;
  10510. var mathCos$1 = Math.cos;
  10511. var mathSin$1 = Math.sin;
  10512. var mathSqrt$1 = Math.sqrt;
  10513. var mathAbs = Math.abs;
  10514. var hasTypedArray = typeof Float32Array != 'undefined';
  10515. /**
  10516. * @alias module:zrender/core/PathProxy
  10517. * @constructor
  10518. */
  10519. var PathProxy = function (notSaveData) {
  10520. this._saveData = !(notSaveData || false);
  10521. if (this._saveData) {
  10522. /**
  10523. * Path data. Stored as flat array
  10524. * @type {Array.<Object>}
  10525. */
  10526. this.data = [];
  10527. }
  10528. this._ctx = null;
  10529. };
  10530. /**
  10531. * 快速计算Path包围盒(并不是最小包围盒)
  10532. * @return {Object}
  10533. */
  10534. PathProxy.prototype = {
  10535. constructor: PathProxy,
  10536. _xi: 0,
  10537. _yi: 0,
  10538. _x0: 0,
  10539. _y0: 0,
  10540. // Unit x, Unit y. Provide for avoiding drawing that too short line segment
  10541. _ux: 0,
  10542. _uy: 0,
  10543. _len: 0,
  10544. _lineDash: null,
  10545. _dashOffset: 0,
  10546. _dashIdx: 0,
  10547. _dashSum: 0,
  10548. /**
  10549. * @readOnly
  10550. */
  10551. setScale: function (sx, sy) {
  10552. this._ux = mathAbs(1 / devicePixelRatio / sx) || 0;
  10553. this._uy = mathAbs(1 / devicePixelRatio / sy) || 0;
  10554. },
  10555. getContext: function () {
  10556. return this._ctx;
  10557. },
  10558. /**
  10559. * @param {CanvasRenderingContext2D} ctx
  10560. * @return {module:zrender/core/PathProxy}
  10561. */
  10562. beginPath: function (ctx) {
  10563. this._ctx = ctx;
  10564. ctx && ctx.beginPath();
  10565. ctx && (this.dpr = ctx.dpr);
  10566. // Reset
  10567. if (this._saveData) {
  10568. this._len = 0;
  10569. }
  10570. if (this._lineDash) {
  10571. this._lineDash = null;
  10572. this._dashOffset = 0;
  10573. }
  10574. return this;
  10575. },
  10576. /**
  10577. * @param {number} x
  10578. * @param {number} y
  10579. * @return {module:zrender/core/PathProxy}
  10580. */
  10581. moveTo: function (x, y) {
  10582. this.addData(CMD.M, x, y);
  10583. this._ctx && this._ctx.moveTo(x, y);
  10584. // x0, y0, xi, yi 是记录在 _dashedXXXXTo 方法中使用
  10585. // xi, yi 记录当前点, x0, y0 在 closePath 的时候回到起始点。
  10586. // 有可能在 beginPath 之后直接调用 lineTo,这时候 x0, y0 需要
  10587. // 在 lineTo 方法中记录,这里先不考虑这种情况,dashed line 也只在 IE10- 中不支持
  10588. this._x0 = x;
  10589. this._y0 = y;
  10590. this._xi = x;
  10591. this._yi = y;
  10592. return this;
  10593. },
  10594. /**
  10595. * @param {number} x
  10596. * @param {number} y
  10597. * @return {module:zrender/core/PathProxy}
  10598. */
  10599. lineTo: function (x, y) {
  10600. var exceedUnit = mathAbs(x - this._xi) > this._ux
  10601. || mathAbs(y - this._yi) > this._uy
  10602. // Force draw the first segment
  10603. || this._len < 5;
  10604. this.addData(CMD.L, x, y);
  10605. if (this._ctx && exceedUnit) {
  10606. this._needsDash() ? this._dashedLineTo(x, y)
  10607. : this._ctx.lineTo(x, y);
  10608. }
  10609. if (exceedUnit) {
  10610. this._xi = x;
  10611. this._yi = y;
  10612. }
  10613. return this;
  10614. },
  10615. /**
  10616. * @param {number} x1
  10617. * @param {number} y1
  10618. * @param {number} x2
  10619. * @param {number} y2
  10620. * @param {number} x3
  10621. * @param {number} y3
  10622. * @return {module:zrender/core/PathProxy}
  10623. */
  10624. bezierCurveTo: function (x1, y1, x2, y2, x3, y3) {
  10625. this.addData(CMD.C, x1, y1, x2, y2, x3, y3);
  10626. if (this._ctx) {
  10627. this._needsDash() ? this._dashedBezierTo(x1, y1, x2, y2, x3, y3)
  10628. : this._ctx.bezierCurveTo(x1, y1, x2, y2, x3, y3);
  10629. }
  10630. this._xi = x3;
  10631. this._yi = y3;
  10632. return this;
  10633. },
  10634. /**
  10635. * @param {number} x1
  10636. * @param {number} y1
  10637. * @param {number} x2
  10638. * @param {number} y2
  10639. * @return {module:zrender/core/PathProxy}
  10640. */
  10641. quadraticCurveTo: function (x1, y1, x2, y2) {
  10642. this.addData(CMD.Q, x1, y1, x2, y2);
  10643. if (this._ctx) {
  10644. this._needsDash() ? this._dashedQuadraticTo(x1, y1, x2, y2)
  10645. : this._ctx.quadraticCurveTo(x1, y1, x2, y2);
  10646. }
  10647. this._xi = x2;
  10648. this._yi = y2;
  10649. return this;
  10650. },
  10651. /**
  10652. * @param {number} cx
  10653. * @param {number} cy
  10654. * @param {number} r
  10655. * @param {number} startAngle
  10656. * @param {number} endAngle
  10657. * @param {boolean} anticlockwise
  10658. * @return {module:zrender/core/PathProxy}
  10659. */
  10660. arc: function (cx, cy, r, startAngle, endAngle, anticlockwise) {
  10661. this.addData(
  10662. CMD.A, cx, cy, r, r, startAngle, endAngle - startAngle, 0, anticlockwise ? 0 : 1
  10663. );
  10664. this._ctx && this._ctx.arc(cx, cy, r, startAngle, endAngle, anticlockwise);
  10665. this._xi = mathCos$1(endAngle) * r + cx;
  10666. this._yi = mathSin$1(endAngle) * r + cx;
  10667. return this;
  10668. },
  10669. // TODO
  10670. arcTo: function (x1, y1, x2, y2, radius) {
  10671. if (this._ctx) {
  10672. this._ctx.arcTo(x1, y1, x2, y2, radius);
  10673. }
  10674. return this;
  10675. },
  10676. // TODO
  10677. rect: function (x, y, w, h) {
  10678. this._ctx && this._ctx.rect(x, y, w, h);
  10679. this.addData(CMD.R, x, y, w, h);
  10680. return this;
  10681. },
  10682. /**
  10683. * @return {module:zrender/core/PathProxy}
  10684. */
  10685. closePath: function () {
  10686. this.addData(CMD.Z);
  10687. var ctx = this._ctx;
  10688. var x0 = this._x0;
  10689. var y0 = this._y0;
  10690. if (ctx) {
  10691. this._needsDash() && this._dashedLineTo(x0, y0);
  10692. ctx.closePath();
  10693. }
  10694. this._xi = x0;
  10695. this._yi = y0;
  10696. return this;
  10697. },
  10698. /**
  10699. * Context 从外部传入,因为有可能是 rebuildPath 完之后再 fill。
  10700. * stroke 同样
  10701. * @param {CanvasRenderingContext2D} ctx
  10702. * @return {module:zrender/core/PathProxy}
  10703. */
  10704. fill: function (ctx) {
  10705. ctx && ctx.fill();
  10706. this.toStatic();
  10707. },
  10708. /**
  10709. * @param {CanvasRenderingContext2D} ctx
  10710. * @return {module:zrender/core/PathProxy}
  10711. */
  10712. stroke: function (ctx) {
  10713. ctx && ctx.stroke();
  10714. this.toStatic();
  10715. },
  10716. /**
  10717. * 必须在其它绘制命令前调用
  10718. * Must be invoked before all other path drawing methods
  10719. * @return {module:zrender/core/PathProxy}
  10720. */
  10721. setLineDash: function (lineDash) {
  10722. if (lineDash instanceof Array) {
  10723. this._lineDash = lineDash;
  10724. this._dashIdx = 0;
  10725. var lineDashSum = 0;
  10726. for (var i = 0; i < lineDash.length; i++) {
  10727. lineDashSum += lineDash[i];
  10728. }
  10729. this._dashSum = lineDashSum;
  10730. }
  10731. return this;
  10732. },
  10733. /**
  10734. * 必须在其它绘制命令前调用
  10735. * Must be invoked before all other path drawing methods
  10736. * @return {module:zrender/core/PathProxy}
  10737. */
  10738. setLineDashOffset: function (offset) {
  10739. this._dashOffset = offset;
  10740. return this;
  10741. },
  10742. /**
  10743. *
  10744. * @return {boolean}
  10745. */
  10746. len: function () {
  10747. return this._len;
  10748. },
  10749. /**
  10750. * 直接设置 Path 数据
  10751. */
  10752. setData: function (data) {
  10753. var len$$1 = data.length;
  10754. if (! (this.data && this.data.length == len$$1) && hasTypedArray) {
  10755. this.data = new Float32Array(len$$1);
  10756. }
  10757. for (var i = 0; i < len$$1; i++) {
  10758. this.data[i] = data[i];
  10759. }
  10760. this._len = len$$1;
  10761. },
  10762. /**
  10763. * 添加子路径
  10764. * @param {module:zrender/core/PathProxy|Array.<module:zrender/core/PathProxy>} path
  10765. */
  10766. appendPath: function (path) {
  10767. if (!(path instanceof Array)) {
  10768. path = [path];
  10769. }
  10770. var len$$1 = path.length;
  10771. var appendSize = 0;
  10772. var offset = this._len;
  10773. for (var i = 0; i < len$$1; i++) {
  10774. appendSize += path[i].len();
  10775. }
  10776. if (hasTypedArray && (this.data instanceof Float32Array)) {
  10777. this.data = new Float32Array(offset + appendSize);
  10778. }
  10779. for (var i = 0; i < len$$1; i++) {
  10780. var appendPathData = path[i].data;
  10781. for (var k = 0; k < appendPathData.length; k++) {
  10782. this.data[offset++] = appendPathData[k];
  10783. }
  10784. }
  10785. this._len = offset;
  10786. },
  10787. /**
  10788. * 填充 Path 数据。
  10789. * 尽量复用而不申明新的数组。大部分图形重绘的指令数据长度都是不变的。
  10790. */
  10791. addData: function (cmd) {
  10792. if (!this._saveData) {
  10793. return;
  10794. }
  10795. var data = this.data;
  10796. if (this._len + arguments.length > data.length) {
  10797. // 因为之前的数组已经转换成静态的 Float32Array
  10798. // 所以不够用时需要扩展一个新的动态数组
  10799. this._expandData();
  10800. data = this.data;
  10801. }
  10802. for (var i = 0; i < arguments.length; i++) {
  10803. data[this._len++] = arguments[i];
  10804. }
  10805. this._prevCmd = cmd;
  10806. },
  10807. _expandData: function () {
  10808. // Only if data is Float32Array
  10809. if (!(this.data instanceof Array)) {
  10810. var newData = [];
  10811. for (var i = 0; i < this._len; i++) {
  10812. newData[i] = this.data[i];
  10813. }
  10814. this.data = newData;
  10815. }
  10816. },
  10817. /**
  10818. * If needs js implemented dashed line
  10819. * @return {boolean}
  10820. * @private
  10821. */
  10822. _needsDash: function () {
  10823. return this._lineDash;
  10824. },
  10825. _dashedLineTo: function (x1, y1) {
  10826. var dashSum = this._dashSum;
  10827. var offset = this._dashOffset;
  10828. var lineDash = this._lineDash;
  10829. var ctx = this._ctx;
  10830. var x0 = this._xi;
  10831. var y0 = this._yi;
  10832. var dx = x1 - x0;
  10833. var dy = y1 - y0;
  10834. var dist$$1 = mathSqrt$1(dx * dx + dy * dy);
  10835. var x = x0;
  10836. var y = y0;
  10837. var dash;
  10838. var nDash = lineDash.length;
  10839. var idx;
  10840. dx /= dist$$1;
  10841. dy /= dist$$1;
  10842. if (offset < 0) {
  10843. // Convert to positive offset
  10844. offset = dashSum + offset;
  10845. }
  10846. offset %= dashSum;
  10847. x -= offset * dx;
  10848. y -= offset * dy;
  10849. while ((dx > 0 && x <= x1) || (dx < 0 && x >= x1)
  10850. || (dx == 0 && ((dy > 0 && y <= y1) || (dy < 0 && y >= y1)))) {
  10851. idx = this._dashIdx;
  10852. dash = lineDash[idx];
  10853. x += dx * dash;
  10854. y += dy * dash;
  10855. this._dashIdx = (idx + 1) % nDash;
  10856. // Skip positive offset
  10857. if ((dx > 0 && x < x0) || (dx < 0 && x > x0) || (dy > 0 && y < y0) || (dy < 0 && y > y0)) {
  10858. continue;
  10859. }
  10860. ctx[idx % 2 ? 'moveTo' : 'lineTo'](
  10861. dx >= 0 ? mathMin$2(x, x1) : mathMax$2(x, x1),
  10862. dy >= 0 ? mathMin$2(y, y1) : mathMax$2(y, y1)
  10863. );
  10864. }
  10865. // Offset for next lineTo
  10866. dx = x - x1;
  10867. dy = y - y1;
  10868. this._dashOffset = -mathSqrt$1(dx * dx + dy * dy);
  10869. },
  10870. // Not accurate dashed line to
  10871. _dashedBezierTo: function (x1, y1, x2, y2, x3, y3) {
  10872. var dashSum = this._dashSum;
  10873. var offset = this._dashOffset;
  10874. var lineDash = this._lineDash;
  10875. var ctx = this._ctx;
  10876. var x0 = this._xi;
  10877. var y0 = this._yi;
  10878. var t;
  10879. var dx;
  10880. var dy;
  10881. var cubicAt$$1 = cubicAt;
  10882. var bezierLen = 0;
  10883. var idx = this._dashIdx;
  10884. var nDash = lineDash.length;
  10885. var x;
  10886. var y;
  10887. var tmpLen = 0;
  10888. if (offset < 0) {
  10889. // Convert to positive offset
  10890. offset = dashSum + offset;
  10891. }
  10892. offset %= dashSum;
  10893. // Bezier approx length
  10894. for (t = 0; t < 1; t += 0.1) {
  10895. dx = cubicAt$$1(x0, x1, x2, x3, t + 0.1)
  10896. - cubicAt$$1(x0, x1, x2, x3, t);
  10897. dy = cubicAt$$1(y0, y1, y2, y3, t + 0.1)
  10898. - cubicAt$$1(y0, y1, y2, y3, t);
  10899. bezierLen += mathSqrt$1(dx * dx + dy * dy);
  10900. }
  10901. // Find idx after add offset
  10902. for (; idx < nDash; idx++) {
  10903. tmpLen += lineDash[idx];
  10904. if (tmpLen > offset) {
  10905. break;
  10906. }
  10907. }
  10908. t = (tmpLen - offset) / bezierLen;
  10909. while (t <= 1) {
  10910. x = cubicAt$$1(x0, x1, x2, x3, t);
  10911. y = cubicAt$$1(y0, y1, y2, y3, t);
  10912. // Use line to approximate dashed bezier
  10913. // Bad result if dash is long
  10914. idx % 2 ? ctx.moveTo(x, y)
  10915. : ctx.lineTo(x, y);
  10916. t += lineDash[idx] / bezierLen;
  10917. idx = (idx + 1) % nDash;
  10918. }
  10919. // Finish the last segment and calculate the new offset
  10920. (idx % 2 !== 0) && ctx.lineTo(x3, y3);
  10921. dx = x3 - x;
  10922. dy = y3 - y;
  10923. this._dashOffset = -mathSqrt$1(dx * dx + dy * dy);
  10924. },
  10925. _dashedQuadraticTo: function (x1, y1, x2, y2) {
  10926. // Convert quadratic to cubic using degree elevation
  10927. var x3 = x2;
  10928. var y3 = y2;
  10929. x2 = (x2 + 2 * x1) / 3;
  10930. y2 = (y2 + 2 * y1) / 3;
  10931. x1 = (this._xi + 2 * x1) / 3;
  10932. y1 = (this._yi + 2 * y1) / 3;
  10933. this._dashedBezierTo(x1, y1, x2, y2, x3, y3);
  10934. },
  10935. /**
  10936. * 转成静态的 Float32Array 减少堆内存占用
  10937. * Convert dynamic array to static Float32Array
  10938. */
  10939. toStatic: function () {
  10940. var data = this.data;
  10941. if (data instanceof Array) {
  10942. data.length = this._len;
  10943. if (hasTypedArray) {
  10944. this.data = new Float32Array(data);
  10945. }
  10946. }
  10947. },
  10948. /**
  10949. * @return {module:zrender/core/BoundingRect}
  10950. */
  10951. getBoundingRect: function () {
  10952. min$1[0] = min$1[1] = min2[0] = min2[1] = Number.MAX_VALUE;
  10953. max$1[0] = max$1[1] = max2[0] = max2[1] = -Number.MAX_VALUE;
  10954. var data = this.data;
  10955. var xi = 0;
  10956. var yi = 0;
  10957. var x0 = 0;
  10958. var y0 = 0;
  10959. for (var i = 0; i < data.length;) {
  10960. var cmd = data[i++];
  10961. if (i == 1) {
  10962. // 如果第一个命令是 L, C, Q
  10963. // 则 previous point 同绘制命令的第一个 point
  10964. //
  10965. // 第一个命令为 Arc 的情况下会在后面特殊处理
  10966. xi = data[i];
  10967. yi = data[i + 1];
  10968. x0 = xi;
  10969. y0 = yi;
  10970. }
  10971. switch (cmd) {
  10972. case CMD.M:
  10973. // moveTo 命令重新创建一个新的 subpath, 并且更新新的起点
  10974. // 在 closePath 的时候使用
  10975. x0 = data[i++];
  10976. y0 = data[i++];
  10977. xi = x0;
  10978. yi = y0;
  10979. min2[0] = x0;
  10980. min2[1] = y0;
  10981. max2[0] = x0;
  10982. max2[1] = y0;
  10983. break;
  10984. case CMD.L:
  10985. fromLine(xi, yi, data[i], data[i + 1], min2, max2);
  10986. xi = data[i++];
  10987. yi = data[i++];
  10988. break;
  10989. case CMD.C:
  10990. fromCubic(
  10991. xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1],
  10992. min2, max2
  10993. );
  10994. xi = data[i++];
  10995. yi = data[i++];
  10996. break;
  10997. case CMD.Q:
  10998. fromQuadratic(
  10999. xi, yi, data[i++], data[i++], data[i], data[i + 1],
  11000. min2, max2
  11001. );
  11002. xi = data[i++];
  11003. yi = data[i++];
  11004. break;
  11005. case CMD.A:
  11006. // TODO Arc 判断的开销比较大
  11007. var cx = data[i++];
  11008. var cy = data[i++];
  11009. var rx = data[i++];
  11010. var ry = data[i++];
  11011. var startAngle = data[i++];
  11012. var endAngle = data[i++] + startAngle;
  11013. // TODO Arc 旋转
  11014. var psi = data[i++];
  11015. var anticlockwise = 1 - data[i++];
  11016. if (i == 1) {
  11017. // 直接使用 arc 命令
  11018. // 第一个命令起点还未定义
  11019. x0 = mathCos$1(startAngle) * rx + cx;
  11020. y0 = mathSin$1(startAngle) * ry + cy;
  11021. }
  11022. fromArc(
  11023. cx, cy, rx, ry, startAngle, endAngle,
  11024. anticlockwise, min2, max2
  11025. );
  11026. xi = mathCos$1(endAngle) * rx + cx;
  11027. yi = mathSin$1(endAngle) * ry + cy;
  11028. break;
  11029. case CMD.R:
  11030. x0 = xi = data[i++];
  11031. y0 = yi = data[i++];
  11032. var width = data[i++];
  11033. var height = data[i++];
  11034. // Use fromLine
  11035. fromLine(x0, y0, x0 + width, y0 + height, min2, max2);
  11036. break;
  11037. case CMD.Z:
  11038. xi = x0;
  11039. yi = y0;
  11040. break;
  11041. }
  11042. // Union
  11043. min(min$1, min$1, min2);
  11044. max(max$1, max$1, max2);
  11045. }
  11046. // No data
  11047. if (i === 0) {
  11048. min$1[0] = min$1[1] = max$1[0] = max$1[1] = 0;
  11049. }
  11050. return new BoundingRect(
  11051. min$1[0], min$1[1], max$1[0] - min$1[0], max$1[1] - min$1[1]
  11052. );
  11053. },
  11054. /**
  11055. * Rebuild path from current data
  11056. * Rebuild path will not consider javascript implemented line dash.
  11057. * @param {CanvasRenderingContext2D} ctx
  11058. */
  11059. rebuildPath: function (ctx) {
  11060. var d = this.data;
  11061. var x0, y0;
  11062. var xi, yi;
  11063. var x, y;
  11064. var ux = this._ux;
  11065. var uy = this._uy;
  11066. var len$$1 = this._len;
  11067. for (var i = 0; i < len$$1;) {
  11068. var cmd = d[i++];
  11069. if (i == 1) {
  11070. // 如果第一个命令是 L, C, Q
  11071. // 则 previous point 同绘制命令的第一个 point
  11072. //
  11073. // 第一个命令为 Arc 的情况下会在后面特殊处理
  11074. xi = d[i];
  11075. yi = d[i + 1];
  11076. x0 = xi;
  11077. y0 = yi;
  11078. }
  11079. switch (cmd) {
  11080. case CMD.M:
  11081. x0 = xi = d[i++];
  11082. y0 = yi = d[i++];
  11083. ctx.moveTo(xi, yi);
  11084. break;
  11085. case CMD.L:
  11086. x = d[i++];
  11087. y = d[i++];
  11088. // Not draw too small seg between
  11089. if (mathAbs(x - xi) > ux || mathAbs(y - yi) > uy || i === len$$1 - 1) {
  11090. ctx.lineTo(x, y);
  11091. xi = x;
  11092. yi = y;
  11093. }
  11094. break;
  11095. case CMD.C:
  11096. ctx.bezierCurveTo(
  11097. d[i++], d[i++], d[i++], d[i++], d[i++], d[i++]
  11098. );
  11099. xi = d[i - 2];
  11100. yi = d[i - 1];
  11101. break;
  11102. case CMD.Q:
  11103. ctx.quadraticCurveTo(d[i++], d[i++], d[i++], d[i++]);
  11104. xi = d[i - 2];
  11105. yi = d[i - 1];
  11106. break;
  11107. case CMD.A:
  11108. var cx = d[i++];
  11109. var cy = d[i++];
  11110. var rx = d[i++];
  11111. var ry = d[i++];
  11112. var theta = d[i++];
  11113. var dTheta = d[i++];
  11114. var psi = d[i++];
  11115. var fs = d[i++];
  11116. var r = (rx > ry) ? rx : ry;
  11117. var scaleX = (rx > ry) ? 1 : rx / ry;
  11118. var scaleY = (rx > ry) ? ry / rx : 1;
  11119. var isEllipse = Math.abs(rx - ry) > 1e-3;
  11120. var endAngle = theta + dTheta;
  11121. if (isEllipse) {
  11122. ctx.translate(cx, cy);
  11123. ctx.rotate(psi);
  11124. ctx.scale(scaleX, scaleY);
  11125. ctx.arc(0, 0, r, theta, endAngle, 1 - fs);
  11126. ctx.scale(1 / scaleX, 1 / scaleY);
  11127. ctx.rotate(-psi);
  11128. ctx.translate(-cx, -cy);
  11129. }
  11130. else {
  11131. ctx.arc(cx, cy, r, theta, endAngle, 1 - fs);
  11132. }
  11133. if (i == 1) {
  11134. // 直接使用 arc 命令
  11135. // 第一个命令起点还未定义
  11136. x0 = mathCos$1(theta) * rx + cx;
  11137. y0 = mathSin$1(theta) * ry + cy;
  11138. }
  11139. xi = mathCos$1(endAngle) * rx + cx;
  11140. yi = mathSin$1(endAngle) * ry + cy;
  11141. break;
  11142. case CMD.R:
  11143. x0 = xi = d[i];
  11144. y0 = yi = d[i + 1];
  11145. ctx.rect(d[i++], d[i++], d[i++], d[i++]);
  11146. break;
  11147. case CMD.Z:
  11148. ctx.closePath();
  11149. xi = x0;
  11150. yi = y0;
  11151. }
  11152. }
  11153. }
  11154. };
  11155. PathProxy.CMD = CMD;
  11156. /**
  11157. * 线段包含判断
  11158. * @param {number} x0
  11159. * @param {number} y0
  11160. * @param {number} x1
  11161. * @param {number} y1
  11162. * @param {number} lineWidth
  11163. * @param {number} x
  11164. * @param {number} y
  11165. * @return {boolean}
  11166. */
  11167. function containStroke$1(x0, y0, x1, y1, lineWidth, x, y) {
  11168. if (lineWidth === 0) {
  11169. return false;
  11170. }
  11171. var _l = lineWidth;
  11172. var _a = 0;
  11173. var _b = x0;
  11174. // Quick reject
  11175. if (
  11176. (y > y0 + _l && y > y1 + _l)
  11177. || (y < y0 - _l && y < y1 - _l)
  11178. || (x > x0 + _l && x > x1 + _l)
  11179. || (x < x0 - _l && x < x1 - _l)
  11180. ) {
  11181. return false;
  11182. }
  11183. if (x0 !== x1) {
  11184. _a = (y0 - y1) / (x0 - x1);
  11185. _b = (x0 * y1 - x1 * y0) / (x0 - x1) ;
  11186. }
  11187. else {
  11188. return Math.abs(x - x0) <= _l / 2;
  11189. }
  11190. var tmp = _a * x - y + _b;
  11191. var _s = tmp * tmp / (_a * _a + 1);
  11192. return _s <= _l / 2 * _l / 2;
  11193. }
  11194. /**
  11195. * 三次贝塞尔曲线描边包含判断
  11196. * @param {number} x0
  11197. * @param {number} y0
  11198. * @param {number} x1
  11199. * @param {number} y1
  11200. * @param {number} x2
  11201. * @param {number} y2
  11202. * @param {number} x3
  11203. * @param {number} y3
  11204. * @param {number} lineWidth
  11205. * @param {number} x
  11206. * @param {number} y
  11207. * @return {boolean}
  11208. */
  11209. function containStroke$2(x0, y0, x1, y1, x2, y2, x3, y3, lineWidth, x, y) {
  11210. if (lineWidth === 0) {
  11211. return false;
  11212. }
  11213. var _l = lineWidth;
  11214. // Quick reject
  11215. if (
  11216. (y > y0 + _l && y > y1 + _l && y > y2 + _l && y > y3 + _l)
  11217. || (y < y0 - _l && y < y1 - _l && y < y2 - _l && y < y3 - _l)
  11218. || (x > x0 + _l && x > x1 + _l && x > x2 + _l && x > x3 + _l)
  11219. || (x < x0 - _l && x < x1 - _l && x < x2 - _l && x < x3 - _l)
  11220. ) {
  11221. return false;
  11222. }
  11223. var d = cubicProjectPoint(
  11224. x0, y0, x1, y1, x2, y2, x3, y3,
  11225. x, y, null
  11226. );
  11227. return d <= _l / 2;
  11228. }
  11229. /**
  11230. * 二次贝塞尔曲线描边包含判断
  11231. * @param {number} x0
  11232. * @param {number} y0
  11233. * @param {number} x1
  11234. * @param {number} y1
  11235. * @param {number} x2
  11236. * @param {number} y2
  11237. * @param {number} lineWidth
  11238. * @param {number} x
  11239. * @param {number} y
  11240. * @return {boolean}
  11241. */
  11242. function containStroke$3(x0, y0, x1, y1, x2, y2, lineWidth, x, y) {
  11243. if (lineWidth === 0) {
  11244. return false;
  11245. }
  11246. var _l = lineWidth;
  11247. // Quick reject
  11248. if (
  11249. (y > y0 + _l && y > y1 + _l && y > y2 + _l)
  11250. || (y < y0 - _l && y < y1 - _l && y < y2 - _l)
  11251. || (x > x0 + _l && x > x1 + _l && x > x2 + _l)
  11252. || (x < x0 - _l && x < x1 - _l && x < x2 - _l)
  11253. ) {
  11254. return false;
  11255. }
  11256. var d = quadraticProjectPoint(
  11257. x0, y0, x1, y1, x2, y2,
  11258. x, y, null
  11259. );
  11260. return d <= _l / 2;
  11261. }
  11262. var PI2$3 = Math.PI * 2;
  11263. function normalizeRadian(angle) {
  11264. angle %= PI2$3;
  11265. if (angle < 0) {
  11266. angle += PI2$3;
  11267. }
  11268. return angle;
  11269. }
  11270. var PI2$2 = Math.PI * 2;
  11271. /**
  11272. * 圆弧描边包含判断
  11273. * @param {number} cx
  11274. * @param {number} cy
  11275. * @param {number} r
  11276. * @param {number} startAngle
  11277. * @param {number} endAngle
  11278. * @param {boolean} anticlockwise
  11279. * @param {number} lineWidth
  11280. * @param {number} x
  11281. * @param {number} y
  11282. * @return {Boolean}
  11283. */
  11284. function containStroke$4(
  11285. cx, cy, r, startAngle, endAngle, anticlockwise,
  11286. lineWidth, x, y
  11287. ) {
  11288. if (lineWidth === 0) {
  11289. return false;
  11290. }
  11291. var _l = lineWidth;
  11292. x -= cx;
  11293. y -= cy;
  11294. var d = Math.sqrt(x * x + y * y);
  11295. if ((d - _l > r) || (d + _l < r)) {
  11296. return false;
  11297. }
  11298. if (Math.abs(startAngle - endAngle) % PI2$2 < 1e-4) {
  11299. // Is a circle
  11300. return true;
  11301. }
  11302. if (anticlockwise) {
  11303. var tmp = startAngle;
  11304. startAngle = normalizeRadian(endAngle);
  11305. endAngle = normalizeRadian(tmp);
  11306. } else {
  11307. startAngle = normalizeRadian(startAngle);
  11308. endAngle = normalizeRadian(endAngle);
  11309. }
  11310. if (startAngle > endAngle) {
  11311. endAngle += PI2$2;
  11312. }
  11313. var angle = Math.atan2(y, x);
  11314. if (angle < 0) {
  11315. angle += PI2$2;
  11316. }
  11317. return (angle >= startAngle && angle <= endAngle)
  11318. || (angle + PI2$2 >= startAngle && angle + PI2$2 <= endAngle);
  11319. }
  11320. function windingLine(x0, y0, x1, y1, x, y) {
  11321. if ((y > y0 && y > y1) || (y < y0 && y < y1)) {
  11322. return 0;
  11323. }
  11324. // Ignore horizontal line
  11325. if (y1 === y0) {
  11326. return 0;
  11327. }
  11328. var dir = y1 < y0 ? 1 : -1;
  11329. var t = (y - y0) / (y1 - y0);
  11330. // Avoid winding error when intersection point is the connect point of two line of polygon
  11331. if (t === 1 || t === 0) {
  11332. dir = y1 < y0 ? 0.5 : -0.5;
  11333. }
  11334. var x_ = t * (x1 - x0) + x0;
  11335. return x_ > x ? dir : 0;
  11336. }
  11337. var CMD$1 = PathProxy.CMD;
  11338. var PI2$1 = Math.PI * 2;
  11339. var EPSILON$2 = 1e-4;
  11340. function isAroundEqual(a, b) {
  11341. return Math.abs(a - b) < EPSILON$2;
  11342. }
  11343. // 临时数组
  11344. var roots = [-1, -1, -1];
  11345. var extrema = [-1, -1];
  11346. function swapExtrema() {
  11347. var tmp = extrema[0];
  11348. extrema[0] = extrema[1];
  11349. extrema[1] = tmp;
  11350. }
  11351. function windingCubic(x0, y0, x1, y1, x2, y2, x3, y3, x, y) {
  11352. // Quick reject
  11353. if (
  11354. (y > y0 && y > y1 && y > y2 && y > y3)
  11355. || (y < y0 && y < y1 && y < y2 && y < y3)
  11356. ) {
  11357. return 0;
  11358. }
  11359. var nRoots = cubicRootAt(y0, y1, y2, y3, y, roots);
  11360. if (nRoots === 0) {
  11361. return 0;
  11362. }
  11363. else {
  11364. var w = 0;
  11365. var nExtrema = -1;
  11366. var y0_, y1_;
  11367. for (var i = 0; i < nRoots; i++) {
  11368. var t = roots[i];
  11369. // Avoid winding error when intersection point is the connect point of two line of polygon
  11370. var unit = (t === 0 || t === 1) ? 0.5 : 1;
  11371. var x_ = cubicAt(x0, x1, x2, x3, t);
  11372. if (x_ < x) { // Quick reject
  11373. continue;
  11374. }
  11375. if (nExtrema < 0) {
  11376. nExtrema = cubicExtrema(y0, y1, y2, y3, extrema);
  11377. if (extrema[1] < extrema[0] && nExtrema > 1) {
  11378. swapExtrema();
  11379. }
  11380. y0_ = cubicAt(y0, y1, y2, y3, extrema[0]);
  11381. if (nExtrema > 1) {
  11382. y1_ = cubicAt(y0, y1, y2, y3, extrema[1]);
  11383. }
  11384. }
  11385. if (nExtrema == 2) {
  11386. // 分成三段单调函数
  11387. if (t < extrema[0]) {
  11388. w += y0_ < y0 ? unit : -unit;
  11389. }
  11390. else if (t < extrema[1]) {
  11391. w += y1_ < y0_ ? unit : -unit;
  11392. }
  11393. else {
  11394. w += y3 < y1_ ? unit : -unit;
  11395. }
  11396. }
  11397. else {
  11398. // 分成两段单调函数
  11399. if (t < extrema[0]) {
  11400. w += y0_ < y0 ? unit : -unit;
  11401. }
  11402. else {
  11403. w += y3 < y0_ ? unit : -unit;
  11404. }
  11405. }
  11406. }
  11407. return w;
  11408. }
  11409. }
  11410. function windingQuadratic(x0, y0, x1, y1, x2, y2, x, y) {
  11411. // Quick reject
  11412. if (
  11413. (y > y0 && y > y1 && y > y2)
  11414. || (y < y0 && y < y1 && y < y2)
  11415. ) {
  11416. return 0;
  11417. }
  11418. var nRoots = quadraticRootAt(y0, y1, y2, y, roots);
  11419. if (nRoots === 0) {
  11420. return 0;
  11421. }
  11422. else {
  11423. var t = quadraticExtremum(y0, y1, y2);
  11424. if (t >= 0 && t <= 1) {
  11425. var w = 0;
  11426. var y_ = quadraticAt(y0, y1, y2, t);
  11427. for (var i = 0; i < nRoots; i++) {
  11428. // Remove one endpoint.
  11429. var unit = (roots[i] === 0 || roots[i] === 1) ? 0.5 : 1;
  11430. var x_ = quadraticAt(x0, x1, x2, roots[i]);
  11431. if (x_ < x) { // Quick reject
  11432. continue;
  11433. }
  11434. if (roots[i] < t) {
  11435. w += y_ < y0 ? unit : -unit;
  11436. }
  11437. else {
  11438. w += y2 < y_ ? unit : -unit;
  11439. }
  11440. }
  11441. return w;
  11442. }
  11443. else {
  11444. // Remove one endpoint.
  11445. var unit = (roots[0] === 0 || roots[0] === 1) ? 0.5 : 1;
  11446. var x_ = quadraticAt(x0, x1, x2, roots[0]);
  11447. if (x_ < x) { // Quick reject
  11448. return 0;
  11449. }
  11450. return y2 < y0 ? unit : -unit;
  11451. }
  11452. }
  11453. }
  11454. // TODO
  11455. // Arc 旋转
  11456. function windingArc(
  11457. cx, cy, r, startAngle, endAngle, anticlockwise, x, y
  11458. ) {
  11459. y -= cy;
  11460. if (y > r || y < -r) {
  11461. return 0;
  11462. }
  11463. var tmp = Math.sqrt(r * r - y * y);
  11464. roots[0] = -tmp;
  11465. roots[1] = tmp;
  11466. var diff = Math.abs(startAngle - endAngle);
  11467. if (diff < 1e-4) {
  11468. return 0;
  11469. }
  11470. if (diff % PI2$1 < 1e-4) {
  11471. // Is a circle
  11472. startAngle = 0;
  11473. endAngle = PI2$1;
  11474. var dir = anticlockwise ? 1 : -1;
  11475. if (x >= roots[0] + cx && x <= roots[1] + cx) {
  11476. return dir;
  11477. } else {
  11478. return 0;
  11479. }
  11480. }
  11481. if (anticlockwise) {
  11482. var tmp = startAngle;
  11483. startAngle = normalizeRadian(endAngle);
  11484. endAngle = normalizeRadian(tmp);
  11485. }
  11486. else {
  11487. startAngle = normalizeRadian(startAngle);
  11488. endAngle = normalizeRadian(endAngle);
  11489. }
  11490. if (startAngle > endAngle) {
  11491. endAngle += PI2$1;
  11492. }
  11493. var w = 0;
  11494. for (var i = 0; i < 2; i++) {
  11495. var x_ = roots[i];
  11496. if (x_ + cx > x) {
  11497. var angle = Math.atan2(y, x_);
  11498. var dir = anticlockwise ? 1 : -1;
  11499. if (angle < 0) {
  11500. angle = PI2$1 + angle;
  11501. }
  11502. if (
  11503. (angle >= startAngle && angle <= endAngle)
  11504. || (angle + PI2$1 >= startAngle && angle + PI2$1 <= endAngle)
  11505. ) {
  11506. if (angle > Math.PI / 2 && angle < Math.PI * 1.5) {
  11507. dir = -dir;
  11508. }
  11509. w += dir;
  11510. }
  11511. }
  11512. }
  11513. return w;
  11514. }
  11515. function containPath(data, lineWidth, isStroke, x, y) {
  11516. var w = 0;
  11517. var xi = 0;
  11518. var yi = 0;
  11519. var x0 = 0;
  11520. var y0 = 0;
  11521. for (var i = 0; i < data.length;) {
  11522. var cmd = data[i++];
  11523. // Begin a new subpath
  11524. if (cmd === CMD$1.M && i > 1) {
  11525. // Close previous subpath
  11526. if (!isStroke) {
  11527. w += windingLine(xi, yi, x0, y0, x, y);
  11528. }
  11529. // 如果被任何一个 subpath 包含
  11530. // if (w !== 0) {
  11531. // return true;
  11532. // }
  11533. }
  11534. if (i == 1) {
  11535. // 如果第一个命令是 L, C, Q
  11536. // 则 previous point 同绘制命令的第一个 point
  11537. //
  11538. // 第一个命令为 Arc 的情况下会在后面特殊处理
  11539. xi = data[i];
  11540. yi = data[i + 1];
  11541. x0 = xi;
  11542. y0 = yi;
  11543. }
  11544. switch (cmd) {
  11545. case CMD$1.M:
  11546. // moveTo 命令重新创建一个新的 subpath, 并且更新新的起点
  11547. // 在 closePath 的时候使用
  11548. x0 = data[i++];
  11549. y0 = data[i++];
  11550. xi = x0;
  11551. yi = y0;
  11552. break;
  11553. case CMD$1.L:
  11554. if (isStroke) {
  11555. if (containStroke$1(xi, yi, data[i], data[i + 1], lineWidth, x, y)) {
  11556. return true;
  11557. }
  11558. }
  11559. else {
  11560. // NOTE 在第一个命令为 L, C, Q 的时候会计算出 NaN
  11561. w += windingLine(xi, yi, data[i], data[i + 1], x, y) || 0;
  11562. }
  11563. xi = data[i++];
  11564. yi = data[i++];
  11565. break;
  11566. case CMD$1.C:
  11567. if (isStroke) {
  11568. if (containStroke$2(xi, yi,
  11569. data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1],
  11570. lineWidth, x, y
  11571. )) {
  11572. return true;
  11573. }
  11574. }
  11575. else {
  11576. w += windingCubic(
  11577. xi, yi,
  11578. data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1],
  11579. x, y
  11580. ) || 0;
  11581. }
  11582. xi = data[i++];
  11583. yi = data[i++];
  11584. break;
  11585. case CMD$1.Q:
  11586. if (isStroke) {
  11587. if (containStroke$3(xi, yi,
  11588. data[i++], data[i++], data[i], data[i + 1],
  11589. lineWidth, x, y
  11590. )) {
  11591. return true;
  11592. }
  11593. }
  11594. else {
  11595. w += windingQuadratic(
  11596. xi, yi,
  11597. data[i++], data[i++], data[i], data[i + 1],
  11598. x, y
  11599. ) || 0;
  11600. }
  11601. xi = data[i++];
  11602. yi = data[i++];
  11603. break;
  11604. case CMD$1.A:
  11605. // TODO Arc 判断的开销比较大
  11606. var cx = data[i++];
  11607. var cy = data[i++];
  11608. var rx = data[i++];
  11609. var ry = data[i++];
  11610. var theta = data[i++];
  11611. var dTheta = data[i++];
  11612. // TODO Arc 旋转
  11613. var psi = data[i++];
  11614. var anticlockwise = 1 - data[i++];
  11615. var x1 = Math.cos(theta) * rx + cx;
  11616. var y1 = Math.sin(theta) * ry + cy;
  11617. // 不是直接使用 arc 命令
  11618. if (i > 1) {
  11619. w += windingLine(xi, yi, x1, y1, x, y);
  11620. }
  11621. else {
  11622. // 第一个命令起点还未定义
  11623. x0 = x1;
  11624. y0 = y1;
  11625. }
  11626. // zr 使用scale来模拟椭圆, 这里也对x做一定的缩放
  11627. var _x = (x - cx) * ry / rx + cx;
  11628. if (isStroke) {
  11629. if (containStroke$4(
  11630. cx, cy, ry, theta, theta + dTheta, anticlockwise,
  11631. lineWidth, _x, y
  11632. )) {
  11633. return true;
  11634. }
  11635. }
  11636. else {
  11637. w += windingArc(
  11638. cx, cy, ry, theta, theta + dTheta, anticlockwise,
  11639. _x, y
  11640. );
  11641. }
  11642. xi = Math.cos(theta + dTheta) * rx + cx;
  11643. yi = Math.sin(theta + dTheta) * ry + cy;
  11644. break;
  11645. case CMD$1.R:
  11646. x0 = xi = data[i++];
  11647. y0 = yi = data[i++];
  11648. var width = data[i++];
  11649. var height = data[i++];
  11650. var x1 = x0 + width;
  11651. var y1 = y0 + height;
  11652. if (isStroke) {
  11653. if (containStroke$1(x0, y0, x1, y0, lineWidth, x, y)
  11654. || containStroke$1(x1, y0, x1, y1, lineWidth, x, y)
  11655. || containStroke$1(x1, y1, x0, y1, lineWidth, x, y)
  11656. || containStroke$1(x0, y1, x0, y0, lineWidth, x, y)
  11657. ) {
  11658. return true;
  11659. }
  11660. }
  11661. else {
  11662. // FIXME Clockwise ?
  11663. w += windingLine(x1, y0, x1, y1, x, y);
  11664. w += windingLine(x0, y1, x0, y0, x, y);
  11665. }
  11666. break;
  11667. case CMD$1.Z:
  11668. if (isStroke) {
  11669. if (containStroke$1(
  11670. xi, yi, x0, y0, lineWidth, x, y
  11671. )) {
  11672. return true;
  11673. }
  11674. }
  11675. else {
  11676. // Close a subpath
  11677. w += windingLine(xi, yi, x0, y0, x, y);
  11678. // 如果被任何一个 subpath 包含
  11679. // FIXME subpaths may overlap
  11680. // if (w !== 0) {
  11681. // return true;
  11682. // }
  11683. }
  11684. xi = x0;
  11685. yi = y0;
  11686. break;
  11687. }
  11688. }
  11689. if (!isStroke && !isAroundEqual(yi, y0)) {
  11690. w += windingLine(xi, yi, x0, y0, x, y) || 0;
  11691. }
  11692. return w !== 0;
  11693. }
  11694. function contain(pathData, x, y) {
  11695. return containPath(pathData, 0, false, x, y);
  11696. }
  11697. function containStroke(pathData, lineWidth, x, y) {
  11698. return containPath(pathData, lineWidth, true, x, y);
  11699. }
  11700. var getCanvasPattern = Pattern.prototype.getCanvasPattern;
  11701. var abs = Math.abs;
  11702. var pathProxyForDraw = new PathProxy(true);
  11703. /**
  11704. * @alias module:zrender/graphic/Path
  11705. * @extends module:zrender/graphic/Displayable
  11706. * @constructor
  11707. * @param {Object} opts
  11708. */
  11709. function Path(opts) {
  11710. Displayable.call(this, opts);
  11711. /**
  11712. * @type {module:zrender/core/PathProxy}
  11713. * @readOnly
  11714. */
  11715. this.path = null;
  11716. }
  11717. Path.prototype = {
  11718. constructor: Path,
  11719. type: 'path',
  11720. __dirtyPath: true,
  11721. strokeContainThreshold: 5,
  11722. brush: function (ctx, prevEl) {
  11723. var style = this.style;
  11724. var path = this.path || pathProxyForDraw;
  11725. var hasStroke = style.hasStroke();
  11726. var hasFill = style.hasFill();
  11727. var fill = style.fill;
  11728. var stroke = style.stroke;
  11729. var hasFillGradient = hasFill && !!(fill.colorStops);
  11730. var hasStrokeGradient = hasStroke && !!(stroke.colorStops);
  11731. var hasFillPattern = hasFill && !!(fill.image);
  11732. var hasStrokePattern = hasStroke && !!(stroke.image);
  11733. style.bind(ctx, this, prevEl);
  11734. this.setTransform(ctx);
  11735. if (this.__dirty) {
  11736. var rect;
  11737. // Update gradient because bounding rect may changed
  11738. if (hasFillGradient) {
  11739. rect = rect || this.getBoundingRect();
  11740. this._fillGradient = style.getGradient(ctx, fill, rect);
  11741. }
  11742. if (hasStrokeGradient) {
  11743. rect = rect || this.getBoundingRect();
  11744. this._strokeGradient = style.getGradient(ctx, stroke, rect);
  11745. }
  11746. }
  11747. // Use the gradient or pattern
  11748. if (hasFillGradient) {
  11749. // PENDING If may have affect the state
  11750. ctx.fillStyle = this._fillGradient;
  11751. }
  11752. else if (hasFillPattern) {
  11753. ctx.fillStyle = getCanvasPattern.call(fill, ctx);
  11754. }
  11755. if (hasStrokeGradient) {
  11756. ctx.strokeStyle = this._strokeGradient;
  11757. }
  11758. else if (hasStrokePattern) {
  11759. ctx.strokeStyle = getCanvasPattern.call(stroke, ctx);
  11760. }
  11761. var lineDash = style.lineDash;
  11762. var lineDashOffset = style.lineDashOffset;
  11763. var ctxLineDash = !!ctx.setLineDash;
  11764. // Update path sx, sy
  11765. var scale = this.getGlobalScale();
  11766. path.setScale(scale[0], scale[1]);
  11767. // Proxy context
  11768. // Rebuild path in following 2 cases
  11769. // 1. Path is dirty
  11770. // 2. Path needs javascript implemented lineDash stroking.
  11771. // In this case, lineDash information will not be saved in PathProxy
  11772. if (this.__dirtyPath
  11773. || (lineDash && !ctxLineDash && hasStroke)
  11774. ) {
  11775. path.beginPath(ctx);
  11776. // Setting line dash before build path
  11777. if (lineDash && !ctxLineDash) {
  11778. path.setLineDash(lineDash);
  11779. path.setLineDashOffset(lineDashOffset);
  11780. }
  11781. this.buildPath(path, this.shape, false);
  11782. // Clear path dirty flag
  11783. if (this.path) {
  11784. this.__dirtyPath = false;
  11785. }
  11786. }
  11787. else {
  11788. // Replay path building
  11789. ctx.beginPath();
  11790. this.path.rebuildPath(ctx);
  11791. }
  11792. hasFill && path.fill(ctx);
  11793. if (lineDash && ctxLineDash) {
  11794. ctx.setLineDash(lineDash);
  11795. ctx.lineDashOffset = lineDashOffset;
  11796. }
  11797. hasStroke && path.stroke(ctx);
  11798. if (lineDash && ctxLineDash) {
  11799. // PENDING
  11800. // Remove lineDash
  11801. ctx.setLineDash([]);
  11802. }
  11803. this.restoreTransform(ctx);
  11804. // Draw rect text
  11805. if (style.text != null) {
  11806. this.drawRectText(ctx, this.getBoundingRect());
  11807. }
  11808. },
  11809. // When bundling path, some shape may decide if use moveTo to begin a new subpath or closePath
  11810. // Like in circle
  11811. buildPath: function (ctx, shapeCfg, inBundle) {},
  11812. createPathProxy: function () {
  11813. this.path = new PathProxy();
  11814. },
  11815. getBoundingRect: function () {
  11816. var rect = this._rect;
  11817. var style = this.style;
  11818. var needsUpdateRect = !rect;
  11819. if (needsUpdateRect) {
  11820. var path = this.path;
  11821. if (!path) {
  11822. // Create path on demand.
  11823. path = this.path = new PathProxy();
  11824. }
  11825. if (this.__dirtyPath) {
  11826. path.beginPath();
  11827. this.buildPath(path, this.shape, false);
  11828. }
  11829. rect = path.getBoundingRect();
  11830. }
  11831. this._rect = rect;
  11832. if (style.hasStroke()) {
  11833. // Needs update rect with stroke lineWidth when
  11834. // 1. Element changes scale or lineWidth
  11835. // 2. Shape is changed
  11836. var rectWithStroke = this._rectWithStroke || (this._rectWithStroke = rect.clone());
  11837. if (this.__dirty || needsUpdateRect) {
  11838. rectWithStroke.copy(rect);
  11839. // FIXME Must after updateTransform
  11840. var w = style.lineWidth;
  11841. // PENDING, Min line width is needed when line is horizontal or vertical
  11842. var lineScale = style.strokeNoScale ? this.getLineScale() : 1;
  11843. // Only add extra hover lineWidth when there are no fill
  11844. if (!style.hasFill()) {
  11845. w = Math.max(w, this.strokeContainThreshold || 4);
  11846. }
  11847. // Consider line width
  11848. // Line scale can't be 0;
  11849. if (lineScale > 1e-10) {
  11850. rectWithStroke.width += w / lineScale;
  11851. rectWithStroke.height += w / lineScale;
  11852. rectWithStroke.x -= w / lineScale / 2;
  11853. rectWithStroke.y -= w / lineScale / 2;
  11854. }
  11855. }
  11856. // Return rect with stroke
  11857. return rectWithStroke;
  11858. }
  11859. return rect;
  11860. },
  11861. contain: function (x, y) {
  11862. var localPos = this.transformCoordToLocal(x, y);
  11863. var rect = this.getBoundingRect();
  11864. var style = this.style;
  11865. x = localPos[0];
  11866. y = localPos[1];
  11867. if (rect.contain(x, y)) {
  11868. var pathData = this.path.data;
  11869. if (style.hasStroke()) {
  11870. var lineWidth = style.lineWidth;
  11871. var lineScale = style.strokeNoScale ? this.getLineScale() : 1;
  11872. // Line scale can't be 0;
  11873. if (lineScale > 1e-10) {
  11874. // Only add extra hover lineWidth when there are no fill
  11875. if (!style.hasFill()) {
  11876. lineWidth = Math.max(lineWidth, this.strokeContainThreshold);
  11877. }
  11878. if (containStroke(
  11879. pathData, lineWidth / lineScale, x, y
  11880. )) {
  11881. return true;
  11882. }
  11883. }
  11884. }
  11885. if (style.hasFill()) {
  11886. return contain(pathData, x, y);
  11887. }
  11888. }
  11889. return false;
  11890. },
  11891. /**
  11892. * @param {boolean} dirtyPath
  11893. */
  11894. dirty: function (dirtyPath) {
  11895. if (dirtyPath == null) {
  11896. dirtyPath = true;
  11897. }
  11898. // Only mark dirty, not mark clean
  11899. if (dirtyPath) {
  11900. this.__dirtyPath = dirtyPath;
  11901. this._rect = null;
  11902. }
  11903. this.__dirty = true;
  11904. this.__zr && this.__zr.refresh();
  11905. // Used as a clipping path
  11906. if (this.__clipTarget) {
  11907. this.__clipTarget.dirty();
  11908. }
  11909. },
  11910. /**
  11911. * Alias for animate('shape')
  11912. * @param {boolean} loop
  11913. */
  11914. animateShape: function (loop) {
  11915. return this.animate('shape', loop);
  11916. },
  11917. // Overwrite attrKV
  11918. attrKV: function (key, value) {
  11919. // FIXME
  11920. if (key === 'shape') {
  11921. this.setShape(value);
  11922. this.__dirtyPath = true;
  11923. this._rect = null;
  11924. }
  11925. else {
  11926. Displayable.prototype.attrKV.call(this, key, value);
  11927. }
  11928. },
  11929. /**
  11930. * @param {Object|string} key
  11931. * @param {*} value
  11932. */
  11933. setShape: function (key, value) {
  11934. var shape = this.shape;
  11935. // Path from string may not have shape
  11936. if (shape) {
  11937. if (isObject(key)) {
  11938. for (var name in key) {
  11939. if (key.hasOwnProperty(name)) {
  11940. shape[name] = key[name];
  11941. }
  11942. }
  11943. }
  11944. else {
  11945. shape[key] = value;
  11946. }
  11947. this.dirty(true);
  11948. }
  11949. return this;
  11950. },
  11951. getLineScale: function () {
  11952. var m = this.transform;
  11953. // Get the line scale.
  11954. // Determinant of `m` means how much the area is enlarged by the
  11955. // transformation. So its square root can be used as a scale factor
  11956. // for width.
  11957. return m && abs(m[0] - 1) > 1e-10 && abs(m[3] - 1) > 1e-10
  11958. ? Math.sqrt(abs(m[0] * m[3] - m[2] * m[1]))
  11959. : 1;
  11960. }
  11961. };
  11962. /**
  11963. * 扩展一个 Path element, 比如星形,圆等。
  11964. * Extend a path element
  11965. * @param {Object} props
  11966. * @param {string} props.type Path type
  11967. * @param {Function} props.init Initialize
  11968. * @param {Function} props.buildPath Overwrite buildPath method
  11969. * @param {Object} [props.style] Extended default style config
  11970. * @param {Object} [props.shape] Extended default shape config
  11971. */
  11972. Path.extend = function (defaults$$1) {
  11973. var Sub = function (opts) {
  11974. Path.call(this, opts);
  11975. if (defaults$$1.style) {
  11976. // Extend default style
  11977. this.style.extendFrom(defaults$$1.style, false);
  11978. }
  11979. // Extend default shape
  11980. var defaultShape = defaults$$1.shape;
  11981. if (defaultShape) {
  11982. this.shape = this.shape || {};
  11983. var thisShape = this.shape;
  11984. for (var name in defaultShape) {
  11985. if (
  11986. ! thisShape.hasOwnProperty(name)
  11987. && defaultShape.hasOwnProperty(name)
  11988. ) {
  11989. thisShape[name] = defaultShape[name];
  11990. }
  11991. }
  11992. }
  11993. defaults$$1.init && defaults$$1.init.call(this, opts);
  11994. };
  11995. inherits(Sub, Path);
  11996. // FIXME 不能 extend position, rotation 等引用对象
  11997. for (var name in defaults$$1) {
  11998. // Extending prototype values and methods
  11999. if (name !== 'style' && name !== 'shape') {
  12000. Sub.prototype[name] = defaults$$1[name];
  12001. }
  12002. }
  12003. return Sub;
  12004. };
  12005. inherits(Path, Displayable);
  12006. var CMD$2 = PathProxy.CMD;
  12007. var points = [[], [], []];
  12008. var mathSqrt$3 = Math.sqrt;
  12009. var mathAtan2 = Math.atan2;
  12010. var transformPath = function (path, m) {
  12011. var data = path.data;
  12012. var cmd;
  12013. var nPoint;
  12014. var i;
  12015. var j;
  12016. var k;
  12017. var p;
  12018. var M = CMD$2.M;
  12019. var C = CMD$2.C;
  12020. var L = CMD$2.L;
  12021. var R = CMD$2.R;
  12022. var A = CMD$2.A;
  12023. var Q = CMD$2.Q;
  12024. for (i = 0, j = 0; i < data.length;) {
  12025. cmd = data[i++];
  12026. j = i;
  12027. nPoint = 0;
  12028. switch (cmd) {
  12029. case M:
  12030. nPoint = 1;
  12031. break;
  12032. case L:
  12033. nPoint = 1;
  12034. break;
  12035. case C:
  12036. nPoint = 3;
  12037. break;
  12038. case Q:
  12039. nPoint = 2;
  12040. break;
  12041. case A:
  12042. var x = m[4];
  12043. var y = m[5];
  12044. var sx = mathSqrt$3(m[0] * m[0] + m[1] * m[1]);
  12045. var sy = mathSqrt$3(m[2] * m[2] + m[3] * m[3]);
  12046. var angle = mathAtan2(-m[1] / sy, m[0] / sx);
  12047. // cx
  12048. data[i] *= sx;
  12049. data[i++] += x;
  12050. // cy
  12051. data[i] *= sy;
  12052. data[i++] += y;
  12053. // Scale rx and ry
  12054. // FIXME Assume psi is 0 here
  12055. data[i++] *= sx;
  12056. data[i++] *= sy;
  12057. // Start angle
  12058. data[i++] += angle;
  12059. // end angle
  12060. data[i++] += angle;
  12061. // FIXME psi
  12062. i += 2;
  12063. j = i;
  12064. break;
  12065. case R:
  12066. // x0, y0
  12067. p[0] = data[i++];
  12068. p[1] = data[i++];
  12069. applyTransform(p, p, m);
  12070. data[j++] = p[0];
  12071. data[j++] = p[1];
  12072. // x1, y1
  12073. p[0] += data[i++];
  12074. p[1] += data[i++];
  12075. applyTransform(p, p, m);
  12076. data[j++] = p[0];
  12077. data[j++] = p[1];
  12078. }
  12079. for (k = 0; k < nPoint; k++) {
  12080. var p = points[k];
  12081. p[0] = data[i++];
  12082. p[1] = data[i++];
  12083. applyTransform(p, p, m);
  12084. // Write back
  12085. data[j++] = p[0];
  12086. data[j++] = p[1];
  12087. }
  12088. }
  12089. };
  12090. // command chars
  12091. var cc = [
  12092. 'm', 'M', 'l', 'L', 'v', 'V', 'h', 'H', 'z', 'Z',
  12093. 'c', 'C', 'q', 'Q', 't', 'T', 's', 'S', 'a', 'A'
  12094. ];
  12095. var mathSqrt = Math.sqrt;
  12096. var mathSin = Math.sin;
  12097. var mathCos = Math.cos;
  12098. var PI = Math.PI;
  12099. var vMag = function(v) {
  12100. return Math.sqrt(v[0] * v[0] + v[1] * v[1]);
  12101. };
  12102. var vRatio = function(u, v) {
  12103. return (u[0] * v[0] + u[1] * v[1]) / (vMag(u) * vMag(v));
  12104. };
  12105. var vAngle = function(u, v) {
  12106. return (u[0] * v[1] < u[1] * v[0] ? -1 : 1)
  12107. * Math.acos(vRatio(u, v));
  12108. };
  12109. function processArc(x1, y1, x2, y2, fa, fs, rx, ry, psiDeg, cmd, path) {
  12110. var psi = psiDeg * (PI / 180.0);
  12111. var xp = mathCos(psi) * (x1 - x2) / 2.0
  12112. + mathSin(psi) * (y1 - y2) / 2.0;
  12113. var yp = -1 * mathSin(psi) * (x1 - x2) / 2.0
  12114. + mathCos(psi) * (y1 - y2) / 2.0;
  12115. var lambda = (xp * xp) / (rx * rx) + (yp * yp) / (ry * ry);
  12116. if (lambda > 1) {
  12117. rx *= mathSqrt(lambda);
  12118. ry *= mathSqrt(lambda);
  12119. }
  12120. var f = (fa === fs ? -1 : 1)
  12121. * mathSqrt((((rx * rx) * (ry * ry))
  12122. - ((rx * rx) * (yp * yp))
  12123. - ((ry * ry) * (xp * xp))) / ((rx * rx) * (yp * yp)
  12124. + (ry * ry) * (xp * xp))
  12125. ) || 0;
  12126. var cxp = f * rx * yp / ry;
  12127. var cyp = f * -ry * xp / rx;
  12128. var cx = (x1 + x2) / 2.0
  12129. + mathCos(psi) * cxp
  12130. - mathSin(psi) * cyp;
  12131. var cy = (y1 + y2) / 2.0
  12132. + mathSin(psi) * cxp
  12133. + mathCos(psi) * cyp;
  12134. var theta = vAngle([ 1, 0 ], [ (xp - cxp) / rx, (yp - cyp) / ry ]);
  12135. var u = [ (xp - cxp) / rx, (yp - cyp) / ry ];
  12136. var v = [ (-1 * xp - cxp) / rx, (-1 * yp - cyp) / ry ];
  12137. var dTheta = vAngle(u, v);
  12138. if (vRatio(u, v) <= -1) {
  12139. dTheta = PI;
  12140. }
  12141. if (vRatio(u, v) >= 1) {
  12142. dTheta = 0;
  12143. }
  12144. if (fs === 0 && dTheta > 0) {
  12145. dTheta = dTheta - 2 * PI;
  12146. }
  12147. if (fs === 1 && dTheta < 0) {
  12148. dTheta = dTheta + 2 * PI;
  12149. }
  12150. path.addData(cmd, cx, cy, rx, ry, theta, dTheta, psi, fs);
  12151. }
  12152. function createPathProxyFromString(data) {
  12153. if (!data) {
  12154. return [];
  12155. }
  12156. // command string
  12157. var cs = data.replace(/-/g, ' -')
  12158. .replace(/ /g, ' ')
  12159. .replace(/ /g, ',')
  12160. .replace(/,,/g, ',');
  12161. var n;
  12162. // create pipes so that we can split the data
  12163. for (n = 0; n < cc.length; n++) {
  12164. cs = cs.replace(new RegExp(cc[n], 'g'), '|' + cc[n]);
  12165. }
  12166. // create array
  12167. var arr = cs.split('|');
  12168. // init context point
  12169. var cpx = 0;
  12170. var cpy = 0;
  12171. var path = new PathProxy();
  12172. var CMD = PathProxy.CMD;
  12173. var prevCmd;
  12174. for (n = 1; n < arr.length; n++) {
  12175. var str = arr[n];
  12176. var c = str.charAt(0);
  12177. var off = 0;
  12178. var p = str.slice(1).replace(/e,-/g, 'e-').split(',');
  12179. var cmd;
  12180. if (p.length > 0 && p[0] === '') {
  12181. p.shift();
  12182. }
  12183. for (var i = 0; i < p.length; i++) {
  12184. p[i] = parseFloat(p[i]);
  12185. }
  12186. while (off < p.length && !isNaN(p[off])) {
  12187. if (isNaN(p[0])) {
  12188. break;
  12189. }
  12190. var ctlPtx;
  12191. var ctlPty;
  12192. var rx;
  12193. var ry;
  12194. var psi;
  12195. var fa;
  12196. var fs;
  12197. var x1 = cpx;
  12198. var y1 = cpy;
  12199. // convert l, H, h, V, and v to L
  12200. switch (c) {
  12201. case 'l':
  12202. cpx += p[off++];
  12203. cpy += p[off++];
  12204. cmd = CMD.L;
  12205. path.addData(cmd, cpx, cpy);
  12206. break;
  12207. case 'L':
  12208. cpx = p[off++];
  12209. cpy = p[off++];
  12210. cmd = CMD.L;
  12211. path.addData(cmd, cpx, cpy);
  12212. break;
  12213. case 'm':
  12214. cpx += p[off++];
  12215. cpy += p[off++];
  12216. cmd = CMD.M;
  12217. path.addData(cmd, cpx, cpy);
  12218. c = 'l';
  12219. break;
  12220. case 'M':
  12221. cpx = p[off++];
  12222. cpy = p[off++];
  12223. cmd = CMD.M;
  12224. path.addData(cmd, cpx, cpy);
  12225. c = 'L';
  12226. break;
  12227. case 'h':
  12228. cpx += p[off++];
  12229. cmd = CMD.L;
  12230. path.addData(cmd, cpx, cpy);
  12231. break;
  12232. case 'H':
  12233. cpx = p[off++];
  12234. cmd = CMD.L;
  12235. path.addData(cmd, cpx, cpy);
  12236. break;
  12237. case 'v':
  12238. cpy += p[off++];
  12239. cmd = CMD.L;
  12240. path.addData(cmd, cpx, cpy);
  12241. break;
  12242. case 'V':
  12243. cpy = p[off++];
  12244. cmd = CMD.L;
  12245. path.addData(cmd, cpx, cpy);
  12246. break;
  12247. case 'C':
  12248. cmd = CMD.C;
  12249. path.addData(
  12250. cmd, p[off++], p[off++], p[off++], p[off++], p[off++], p[off++]
  12251. );
  12252. cpx = p[off - 2];
  12253. cpy = p[off - 1];
  12254. break;
  12255. case 'c':
  12256. cmd = CMD.C;
  12257. path.addData(
  12258. cmd,
  12259. p[off++] + cpx, p[off++] + cpy,
  12260. p[off++] + cpx, p[off++] + cpy,
  12261. p[off++] + cpx, p[off++] + cpy
  12262. );
  12263. cpx += p[off - 2];
  12264. cpy += p[off - 1];
  12265. break;
  12266. case 'S':
  12267. ctlPtx = cpx;
  12268. ctlPty = cpy;
  12269. var len = path.len();
  12270. var pathData = path.data;
  12271. if (prevCmd === CMD.C) {
  12272. ctlPtx += cpx - pathData[len - 4];
  12273. ctlPty += cpy - pathData[len - 3];
  12274. }
  12275. cmd = CMD.C;
  12276. x1 = p[off++];
  12277. y1 = p[off++];
  12278. cpx = p[off++];
  12279. cpy = p[off++];
  12280. path.addData(cmd, ctlPtx, ctlPty, x1, y1, cpx, cpy);
  12281. break;
  12282. case 's':
  12283. ctlPtx = cpx;
  12284. ctlPty = cpy;
  12285. var len = path.len();
  12286. var pathData = path.data;
  12287. if (prevCmd === CMD.C) {
  12288. ctlPtx += cpx - pathData[len - 4];
  12289. ctlPty += cpy - pathData[len - 3];
  12290. }
  12291. cmd = CMD.C;
  12292. x1 = cpx + p[off++];
  12293. y1 = cpy + p[off++];
  12294. cpx += p[off++];
  12295. cpy += p[off++];
  12296. path.addData(cmd, ctlPtx, ctlPty, x1, y1, cpx, cpy);
  12297. break;
  12298. case 'Q':
  12299. x1 = p[off++];
  12300. y1 = p[off++];
  12301. cpx = p[off++];
  12302. cpy = p[off++];
  12303. cmd = CMD.Q;
  12304. path.addData(cmd, x1, y1, cpx, cpy);
  12305. break;
  12306. case 'q':
  12307. x1 = p[off++] + cpx;
  12308. y1 = p[off++] + cpy;
  12309. cpx += p[off++];
  12310. cpy += p[off++];
  12311. cmd = CMD.Q;
  12312. path.addData(cmd, x1, y1, cpx, cpy);
  12313. break;
  12314. case 'T':
  12315. ctlPtx = cpx;
  12316. ctlPty = cpy;
  12317. var len = path.len();
  12318. var pathData = path.data;
  12319. if (prevCmd === CMD.Q) {
  12320. ctlPtx += cpx - pathData[len - 4];
  12321. ctlPty += cpy - pathData[len - 3];
  12322. }
  12323. cpx = p[off++];
  12324. cpy = p[off++];
  12325. cmd = CMD.Q;
  12326. path.addData(cmd, ctlPtx, ctlPty, cpx, cpy);
  12327. break;
  12328. case 't':
  12329. ctlPtx = cpx;
  12330. ctlPty = cpy;
  12331. var len = path.len();
  12332. var pathData = path.data;
  12333. if (prevCmd === CMD.Q) {
  12334. ctlPtx += cpx - pathData[len - 4];
  12335. ctlPty += cpy - pathData[len - 3];
  12336. }
  12337. cpx += p[off++];
  12338. cpy += p[off++];
  12339. cmd = CMD.Q;
  12340. path.addData(cmd, ctlPtx, ctlPty, cpx, cpy);
  12341. break;
  12342. case 'A':
  12343. rx = p[off++];
  12344. ry = p[off++];
  12345. psi = p[off++];
  12346. fa = p[off++];
  12347. fs = p[off++];
  12348. x1 = cpx, y1 = cpy;
  12349. cpx = p[off++];
  12350. cpy = p[off++];
  12351. cmd = CMD.A;
  12352. processArc(
  12353. x1, y1, cpx, cpy, fa, fs, rx, ry, psi, cmd, path
  12354. );
  12355. break;
  12356. case 'a':
  12357. rx = p[off++];
  12358. ry = p[off++];
  12359. psi = p[off++];
  12360. fa = p[off++];
  12361. fs = p[off++];
  12362. x1 = cpx, y1 = cpy;
  12363. cpx += p[off++];
  12364. cpy += p[off++];
  12365. cmd = CMD.A;
  12366. processArc(
  12367. x1, y1, cpx, cpy, fa, fs, rx, ry, psi, cmd, path
  12368. );
  12369. break;
  12370. }
  12371. }
  12372. if (c === 'z' || c === 'Z') {
  12373. cmd = CMD.Z;
  12374. path.addData(cmd);
  12375. }
  12376. prevCmd = cmd;
  12377. }
  12378. path.toStatic();
  12379. return path;
  12380. }
  12381. // TODO Optimize double memory cost problem
  12382. function createPathOptions(str, opts) {
  12383. var pathProxy = createPathProxyFromString(str);
  12384. opts = opts || {};
  12385. opts.buildPath = function (path) {
  12386. if (path.setData) {
  12387. path.setData(pathProxy.data);
  12388. // Svg and vml renderer don't have context
  12389. var ctx = path.getContext();
  12390. if (ctx) {
  12391. path.rebuildPath(ctx);
  12392. }
  12393. }
  12394. else {
  12395. var ctx = path;
  12396. pathProxy.rebuildPath(ctx);
  12397. }
  12398. };
  12399. opts.applyTransform = function (m) {
  12400. transformPath(pathProxy, m);
  12401. this.dirty(true);
  12402. };
  12403. return opts;
  12404. }
  12405. /**
  12406. * Create a Path object from path string data
  12407. * http://www.w3.org/TR/SVG/paths.html#PathData
  12408. * @param {Object} opts Other options
  12409. */
  12410. function createFromString(str, opts) {
  12411. return new Path(createPathOptions(str, opts));
  12412. }
  12413. /**
  12414. * Create a Path class from path string data
  12415. * @param {string} str
  12416. * @param {Object} opts Other options
  12417. */
  12418. function extendFromString(str, opts) {
  12419. return Path.extend(createPathOptions(str, opts));
  12420. }
  12421. /**
  12422. * Merge multiple paths
  12423. */
  12424. // TODO Apply transform
  12425. // TODO stroke dash
  12426. // TODO Optimize double memory cost problem
  12427. function mergePath$1(pathEls, opts) {
  12428. var pathList = [];
  12429. var len = pathEls.length;
  12430. for (var i = 0; i < len; i++) {
  12431. var pathEl = pathEls[i];
  12432. if (!pathEl.path) {
  12433. pathEl.createPathProxy();
  12434. }
  12435. if (pathEl.__dirtyPath) {
  12436. pathEl.buildPath(pathEl.path, pathEl.shape, true);
  12437. }
  12438. pathList.push(pathEl.path);
  12439. }
  12440. var pathBundle = new Path(opts);
  12441. // Need path proxy.
  12442. pathBundle.createPathProxy();
  12443. pathBundle.buildPath = function (path) {
  12444. path.appendPath(pathList);
  12445. // Svg and vml renderer don't have context
  12446. var ctx = path.getContext();
  12447. if (ctx) {
  12448. path.rebuildPath(ctx);
  12449. }
  12450. };
  12451. return pathBundle;
  12452. }
  12453. /**
  12454. * @alias zrender/graphic/Text
  12455. * @extends module:zrender/graphic/Displayable
  12456. * @constructor
  12457. * @param {Object} opts
  12458. */
  12459. var Text = function (opts) { // jshint ignore:line
  12460. Displayable.call(this, opts);
  12461. };
  12462. Text.prototype = {
  12463. constructor: Text,
  12464. type: 'text',
  12465. brush: function (ctx, prevEl) {
  12466. var style = this.style;
  12467. // Optimize, avoid normalize every time.
  12468. this.__dirty && normalizeTextStyle(style, true);
  12469. // Use props with prefix 'text'.
  12470. style.fill = style.stroke = style.shadowBlur = style.shadowColor =
  12471. style.shadowOffsetX = style.shadowOffsetY = null;
  12472. var text = style.text;
  12473. // Convert to string
  12474. text != null && (text += '');
  12475. // Always bind style
  12476. style.bind(ctx, this, prevEl);
  12477. if (!needDrawText(text, style)) {
  12478. return;
  12479. }
  12480. this.setTransform(ctx);
  12481. renderText(this, ctx, text, style);
  12482. this.restoreTransform(ctx);
  12483. },
  12484. getBoundingRect: function () {
  12485. var style = this.style;
  12486. // Optimize, avoid normalize every time.
  12487. this.__dirty && normalizeTextStyle(style, true);
  12488. if (!this._rect) {
  12489. var text = style.text;
  12490. text != null ? (text += '') : (text = '');
  12491. var rect = getBoundingRect(
  12492. style.text + '',
  12493. style.font,
  12494. style.textAlign,
  12495. style.textVerticalAlign,
  12496. style.textPadding,
  12497. style.rich
  12498. );
  12499. rect.x += style.x || 0;
  12500. rect.y += style.y || 0;
  12501. if (getStroke(style.textStroke, style.textStrokeWidth)) {
  12502. var w = style.textStrokeWidth;
  12503. rect.x -= w / 2;
  12504. rect.y -= w / 2;
  12505. rect.width += w;
  12506. rect.height += w;
  12507. }
  12508. this._rect = rect;
  12509. }
  12510. return this._rect;
  12511. }
  12512. };
  12513. inherits(Text, Displayable);
  12514. /**
  12515. * 圆形
  12516. * @module zrender/shape/Circle
  12517. */
  12518. var Circle = Path.extend({
  12519. type: 'circle',
  12520. shape: {
  12521. cx: 0,
  12522. cy: 0,
  12523. r: 0
  12524. },
  12525. buildPath : function (ctx, shape, inBundle) {
  12526. // Better stroking in ShapeBundle
  12527. // Always do it may have performence issue ( fill may be 2x more cost)
  12528. if (inBundle) {
  12529. ctx.moveTo(shape.cx + shape.r, shape.cy);
  12530. }
  12531. // else {
  12532. // if (ctx.allocate && !ctx.data.length) {
  12533. // ctx.allocate(ctx.CMD_MEM_SIZE.A);
  12534. // }
  12535. // }
  12536. // Better stroking in ShapeBundle
  12537. // ctx.moveTo(shape.cx + shape.r, shape.cy);
  12538. ctx.arc(shape.cx, shape.cy, shape.r, 0, Math.PI * 2, true);
  12539. }
  12540. });
  12541. // Fix weird bug in some version of IE11 (like 11.0.9600.178**),
  12542. // where exception "unexpected call to method or property access"
  12543. // might be thrown when calling ctx.fill or ctx.stroke after a path
  12544. // whose area size is zero is drawn and ctx.clip() is called and
  12545. // shadowBlur is set. See #4572, #3112, #5777.
  12546. // (e.g.,
  12547. // ctx.moveTo(10, 10);
  12548. // ctx.lineTo(20, 10);
  12549. // ctx.closePath();
  12550. // ctx.clip();
  12551. // ctx.shadowBlur = 10;
  12552. // ...
  12553. // ctx.fill();
  12554. // )
  12555. var shadowTemp = [
  12556. ['shadowBlur', 0],
  12557. ['shadowColor', '#000'],
  12558. ['shadowOffsetX', 0],
  12559. ['shadowOffsetY', 0]
  12560. ];
  12561. var fixClipWithShadow = function (orignalBrush) {
  12562. // version string can be: '11.0'
  12563. return (env$1.browser.ie && env$1.browser.version >= 11)
  12564. ? function () {
  12565. var clipPaths = this.__clipPaths;
  12566. var style = this.style;
  12567. var modified;
  12568. if (clipPaths) {
  12569. for (var i = 0; i < clipPaths.length; i++) {
  12570. var clipPath = clipPaths[i];
  12571. var shape = clipPath && clipPath.shape;
  12572. var type = clipPath && clipPath.type;
  12573. if (shape && (
  12574. (type === 'sector' && shape.startAngle === shape.endAngle)
  12575. || (type === 'rect' && (!shape.width || !shape.height))
  12576. )) {
  12577. for (var j = 0; j < shadowTemp.length; j++) {
  12578. // It is save to put shadowTemp static, because shadowTemp
  12579. // will be all modified each item brush called.
  12580. shadowTemp[j][2] = style[shadowTemp[j][0]];
  12581. style[shadowTemp[j][0]] = shadowTemp[j][1];
  12582. }
  12583. modified = true;
  12584. break;
  12585. }
  12586. }
  12587. }
  12588. orignalBrush.apply(this, arguments);
  12589. if (modified) {
  12590. for (var j = 0; j < shadowTemp.length; j++) {
  12591. style[shadowTemp[j][0]] = shadowTemp[j][2];
  12592. }
  12593. }
  12594. }
  12595. : orignalBrush;
  12596. };
  12597. /**
  12598. * 扇形
  12599. * @module zrender/graphic/shape/Sector
  12600. */
  12601. var Sector = Path.extend({
  12602. type: 'sector',
  12603. shape: {
  12604. cx: 0,
  12605. cy: 0,
  12606. r0: 0,
  12607. r: 0,
  12608. startAngle: 0,
  12609. endAngle: Math.PI * 2,
  12610. clockwise: true
  12611. },
  12612. brush: fixClipWithShadow(Path.prototype.brush),
  12613. buildPath: function (ctx, shape) {
  12614. var x = shape.cx;
  12615. var y = shape.cy;
  12616. var r0 = Math.max(shape.r0 || 0, 0);
  12617. var r = Math.max(shape.r, 0);
  12618. var startAngle = shape.startAngle;
  12619. var endAngle = shape.endAngle;
  12620. var clockwise = shape.clockwise;
  12621. var unitX = Math.cos(startAngle);
  12622. var unitY = Math.sin(startAngle);
  12623. ctx.moveTo(unitX * r0 + x, unitY * r0 + y);
  12624. ctx.lineTo(unitX * r + x, unitY * r + y);
  12625. ctx.arc(x, y, r, startAngle, endAngle, !clockwise);
  12626. ctx.lineTo(
  12627. Math.cos(endAngle) * r0 + x,
  12628. Math.sin(endAngle) * r0 + y
  12629. );
  12630. if (r0 !== 0) {
  12631. ctx.arc(x, y, r0, endAngle, startAngle, clockwise);
  12632. }
  12633. ctx.closePath();
  12634. }
  12635. });
  12636. /**
  12637. * 圆环
  12638. * @module zrender/graphic/shape/Ring
  12639. */
  12640. var Ring = Path.extend({
  12641. type: 'ring',
  12642. shape: {
  12643. cx: 0,
  12644. cy: 0,
  12645. r: 0,
  12646. r0: 0
  12647. },
  12648. buildPath: function (ctx, shape) {
  12649. var x = shape.cx;
  12650. var y = shape.cy;
  12651. var PI2 = Math.PI * 2;
  12652. ctx.moveTo(x + shape.r, y);
  12653. ctx.arc(x, y, shape.r, 0, PI2, false);
  12654. ctx.moveTo(x + shape.r0, y);
  12655. ctx.arc(x, y, shape.r0, 0, PI2, true);
  12656. }
  12657. });
  12658. /**
  12659. * Catmull-Rom spline 插值折线
  12660. * @module zrender/shape/util/smoothSpline
  12661. * @author pissang (https://www.github.com/pissang)
  12662. * Kener (@Kener-林峰, kener.linfeng@gmail.com)
  12663. * errorrik (errorrik@gmail.com)
  12664. */
  12665. /**
  12666. * @inner
  12667. */
  12668. function interpolate(p0, p1, p2, p3, t, t2, t3) {
  12669. var v0 = (p2 - p0) * 0.5;
  12670. var v1 = (p3 - p1) * 0.5;
  12671. return (2 * (p1 - p2) + v0 + v1) * t3
  12672. + (-3 * (p1 - p2) - 2 * v0 - v1) * t2
  12673. + v0 * t + p1;
  12674. }
  12675. /**
  12676. * @alias module:zrender/shape/util/smoothSpline
  12677. * @param {Array} points 线段顶点数组
  12678. * @param {boolean} isLoop
  12679. * @return {Array}
  12680. */
  12681. var smoothSpline = function (points, isLoop) {
  12682. var len$$1 = points.length;
  12683. var ret = [];
  12684. var distance$$1 = 0;
  12685. for (var i = 1; i < len$$1; i++) {
  12686. distance$$1 += distance(points[i - 1], points[i]);
  12687. }
  12688. var segs = distance$$1 / 2;
  12689. segs = segs < len$$1 ? len$$1 : segs;
  12690. for (var i = 0; i < segs; i++) {
  12691. var pos = i / (segs - 1) * (isLoop ? len$$1 : len$$1 - 1);
  12692. var idx = Math.floor(pos);
  12693. var w = pos - idx;
  12694. var p0;
  12695. var p1 = points[idx % len$$1];
  12696. var p2;
  12697. var p3;
  12698. if (!isLoop) {
  12699. p0 = points[idx === 0 ? idx : idx - 1];
  12700. p2 = points[idx > len$$1 - 2 ? len$$1 - 1 : idx + 1];
  12701. p3 = points[idx > len$$1 - 3 ? len$$1 - 1 : idx + 2];
  12702. }
  12703. else {
  12704. p0 = points[(idx - 1 + len$$1) % len$$1];
  12705. p2 = points[(idx + 1) % len$$1];
  12706. p3 = points[(idx + 2) % len$$1];
  12707. }
  12708. var w2 = w * w;
  12709. var w3 = w * w2;
  12710. ret.push([
  12711. interpolate(p0[0], p1[0], p2[0], p3[0], w, w2, w3),
  12712. interpolate(p0[1], p1[1], p2[1], p3[1], w, w2, w3)
  12713. ]);
  12714. }
  12715. return ret;
  12716. };
  12717. /**
  12718. * 贝塞尔平滑曲线
  12719. * @module zrender/shape/util/smoothBezier
  12720. * @author pissang (https://www.github.com/pissang)
  12721. * Kener (@Kener-林峰, kener.linfeng@gmail.com)
  12722. * errorrik (errorrik@gmail.com)
  12723. */
  12724. /**
  12725. * 贝塞尔平滑曲线
  12726. * @alias module:zrender/shape/util/smoothBezier
  12727. * @param {Array} points 线段顶点数组
  12728. * @param {number} smooth 平滑等级, 0-1
  12729. * @param {boolean} isLoop
  12730. * @param {Array} constraint 将计算出来的控制点约束在一个包围盒内
  12731. * 比如 [[0, 0], [100, 100]], 这个包围盒会与
  12732. * 整个折线的包围盒做一个并集用来约束控制点。
  12733. * @param {Array} 计算出来的控制点数组
  12734. */
  12735. var smoothBezier = function (points, smooth, isLoop, constraint) {
  12736. var cps = [];
  12737. var v = [];
  12738. var v1 = [];
  12739. var v2 = [];
  12740. var prevPoint;
  12741. var nextPoint;
  12742. var min$$1, max$$1;
  12743. if (constraint) {
  12744. min$$1 = [Infinity, Infinity];
  12745. max$$1 = [-Infinity, -Infinity];
  12746. for (var i = 0, len$$1 = points.length; i < len$$1; i++) {
  12747. min(min$$1, min$$1, points[i]);
  12748. max(max$$1, max$$1, points[i]);
  12749. }
  12750. // 与指定的包围盒做并集
  12751. min(min$$1, min$$1, constraint[0]);
  12752. max(max$$1, max$$1, constraint[1]);
  12753. }
  12754. for (var i = 0, len$$1 = points.length; i < len$$1; i++) {
  12755. var point = points[i];
  12756. if (isLoop) {
  12757. prevPoint = points[i ? i - 1 : len$$1 - 1];
  12758. nextPoint = points[(i + 1) % len$$1];
  12759. }
  12760. else {
  12761. if (i === 0 || i === len$$1 - 1) {
  12762. cps.push(clone$1(points[i]));
  12763. continue;
  12764. }
  12765. else {
  12766. prevPoint = points[i - 1];
  12767. nextPoint = points[i + 1];
  12768. }
  12769. }
  12770. sub(v, nextPoint, prevPoint);
  12771. // use degree to scale the handle length
  12772. scale(v, v, smooth);
  12773. var d0 = distance(point, prevPoint);
  12774. var d1 = distance(point, nextPoint);
  12775. var sum = d0 + d1;
  12776. if (sum !== 0) {
  12777. d0 /= sum;
  12778. d1 /= sum;
  12779. }
  12780. scale(v1, v, -d0);
  12781. scale(v2, v, d1);
  12782. var cp0 = add([], point, v1);
  12783. var cp1 = add([], point, v2);
  12784. if (constraint) {
  12785. max(cp0, cp0, min$$1);
  12786. min(cp0, cp0, max$$1);
  12787. max(cp1, cp1, min$$1);
  12788. min(cp1, cp1, max$$1);
  12789. }
  12790. cps.push(cp0);
  12791. cps.push(cp1);
  12792. }
  12793. if (isLoop) {
  12794. cps.push(cps.shift());
  12795. }
  12796. return cps;
  12797. };
  12798. function buildPath$1(ctx, shape, closePath) {
  12799. var points = shape.points;
  12800. var smooth = shape.smooth;
  12801. if (points && points.length >= 2) {
  12802. if (smooth && smooth !== 'spline') {
  12803. var controlPoints = smoothBezier(
  12804. points, smooth, closePath, shape.smoothConstraint
  12805. );
  12806. ctx.moveTo(points[0][0], points[0][1]);
  12807. var len = points.length;
  12808. for (var i = 0; i < (closePath ? len : len - 1); i++) {
  12809. var cp1 = controlPoints[i * 2];
  12810. var cp2 = controlPoints[i * 2 + 1];
  12811. var p = points[(i + 1) % len];
  12812. ctx.bezierCurveTo(
  12813. cp1[0], cp1[1], cp2[0], cp2[1], p[0], p[1]
  12814. );
  12815. }
  12816. }
  12817. else {
  12818. if (smooth === 'spline') {
  12819. points = smoothSpline(points, closePath);
  12820. }
  12821. ctx.moveTo(points[0][0], points[0][1]);
  12822. for (var i = 1, l = points.length; i < l; i++) {
  12823. ctx.lineTo(points[i][0], points[i][1]);
  12824. }
  12825. }
  12826. closePath && ctx.closePath();
  12827. }
  12828. }
  12829. /**
  12830. * 多边形
  12831. * @module zrender/shape/Polygon
  12832. */
  12833. var Polygon = Path.extend({
  12834. type: 'polygon',
  12835. shape: {
  12836. points: null,
  12837. smooth: false,
  12838. smoothConstraint: null
  12839. },
  12840. buildPath: function (ctx, shape) {
  12841. buildPath$1(ctx, shape, true);
  12842. }
  12843. });
  12844. /**
  12845. * @module zrender/graphic/shape/Polyline
  12846. */
  12847. var Polyline = Path.extend({
  12848. type: 'polyline',
  12849. shape: {
  12850. points: null,
  12851. smooth: false,
  12852. smoothConstraint: null
  12853. },
  12854. style: {
  12855. stroke: '#000',
  12856. fill: null
  12857. },
  12858. buildPath: function (ctx, shape) {
  12859. buildPath$1(ctx, shape, false);
  12860. }
  12861. });
  12862. /**
  12863. * 矩形
  12864. * @module zrender/graphic/shape/Rect
  12865. */
  12866. var Rect = Path.extend({
  12867. type: 'rect',
  12868. shape: {
  12869. // 左上、右上、右下、左下角的半径依次为r1、r2、r3、r4
  12870. // r缩写为1 相当于 [1, 1, 1, 1]
  12871. // r缩写为[1] 相当于 [1, 1, 1, 1]
  12872. // r缩写为[1, 2] 相当于 [1, 2, 1, 2]
  12873. // r缩写为[1, 2, 3] 相当于 [1, 2, 3, 2]
  12874. r: 0,
  12875. x: 0,
  12876. y: 0,
  12877. width: 0,
  12878. height: 0
  12879. },
  12880. buildPath: function (ctx, shape) {
  12881. var x = shape.x;
  12882. var y = shape.y;
  12883. var width = shape.width;
  12884. var height = shape.height;
  12885. if (!shape.r) {
  12886. ctx.rect(x, y, width, height);
  12887. }
  12888. else {
  12889. buildPath(ctx, shape);
  12890. }
  12891. ctx.closePath();
  12892. return;
  12893. }
  12894. });
  12895. /**
  12896. * 直线
  12897. * @module zrender/graphic/shape/Line
  12898. */
  12899. var Line = Path.extend({
  12900. type: 'line',
  12901. shape: {
  12902. // Start point
  12903. x1: 0,
  12904. y1: 0,
  12905. // End point
  12906. x2: 0,
  12907. y2: 0,
  12908. percent: 1
  12909. },
  12910. style: {
  12911. stroke: '#000',
  12912. fill: null
  12913. },
  12914. buildPath: function (ctx, shape) {
  12915. var x1 = shape.x1;
  12916. var y1 = shape.y1;
  12917. var x2 = shape.x2;
  12918. var y2 = shape.y2;
  12919. var percent = shape.percent;
  12920. if (percent === 0) {
  12921. return;
  12922. }
  12923. ctx.moveTo(x1, y1);
  12924. if (percent < 1) {
  12925. x2 = x1 * (1 - percent) + x2 * percent;
  12926. y2 = y1 * (1 - percent) + y2 * percent;
  12927. }
  12928. ctx.lineTo(x2, y2);
  12929. },
  12930. /**
  12931. * Get point at percent
  12932. * @param {number} percent
  12933. * @return {Array.<number>}
  12934. */
  12935. pointAt: function (p) {
  12936. var shape = this.shape;
  12937. return [
  12938. shape.x1 * (1 - p) + shape.x2 * p,
  12939. shape.y1 * (1 - p) + shape.y2 * p
  12940. ];
  12941. }
  12942. });
  12943. /**
  12944. * 贝塞尔曲线
  12945. * @module zrender/shape/BezierCurve
  12946. */
  12947. var out = [];
  12948. function someVectorAt(shape, t, isTangent) {
  12949. var cpx2 = shape.cpx2;
  12950. var cpy2 = shape.cpy2;
  12951. if (cpx2 === null || cpy2 === null) {
  12952. return [
  12953. (isTangent ? cubicDerivativeAt : cubicAt)(shape.x1, shape.cpx1, shape.cpx2, shape.x2, t),
  12954. (isTangent ? cubicDerivativeAt : cubicAt)(shape.y1, shape.cpy1, shape.cpy2, shape.y2, t)
  12955. ];
  12956. }
  12957. else {
  12958. return [
  12959. (isTangent ? quadraticDerivativeAt : quadraticAt)(shape.x1, shape.cpx1, shape.x2, t),
  12960. (isTangent ? quadraticDerivativeAt : quadraticAt)(shape.y1, shape.cpy1, shape.y2, t)
  12961. ];
  12962. }
  12963. }
  12964. var BezierCurve = Path.extend({
  12965. type: 'bezier-curve',
  12966. shape: {
  12967. x1: 0,
  12968. y1: 0,
  12969. x2: 0,
  12970. y2: 0,
  12971. cpx1: 0,
  12972. cpy1: 0,
  12973. // cpx2: 0,
  12974. // cpy2: 0
  12975. // Curve show percent, for animating
  12976. percent: 1
  12977. },
  12978. style: {
  12979. stroke: '#000',
  12980. fill: null
  12981. },
  12982. buildPath: function (ctx, shape) {
  12983. var x1 = shape.x1;
  12984. var y1 = shape.y1;
  12985. var x2 = shape.x2;
  12986. var y2 = shape.y2;
  12987. var cpx1 = shape.cpx1;
  12988. var cpy1 = shape.cpy1;
  12989. var cpx2 = shape.cpx2;
  12990. var cpy2 = shape.cpy2;
  12991. var percent = shape.percent;
  12992. if (percent === 0) {
  12993. return;
  12994. }
  12995. ctx.moveTo(x1, y1);
  12996. if (cpx2 == null || cpy2 == null) {
  12997. if (percent < 1) {
  12998. quadraticSubdivide(
  12999. x1, cpx1, x2, percent, out
  13000. );
  13001. cpx1 = out[1];
  13002. x2 = out[2];
  13003. quadraticSubdivide(
  13004. y1, cpy1, y2, percent, out
  13005. );
  13006. cpy1 = out[1];
  13007. y2 = out[2];
  13008. }
  13009. ctx.quadraticCurveTo(
  13010. cpx1, cpy1,
  13011. x2, y2
  13012. );
  13013. }
  13014. else {
  13015. if (percent < 1) {
  13016. cubicSubdivide(
  13017. x1, cpx1, cpx2, x2, percent, out
  13018. );
  13019. cpx1 = out[1];
  13020. cpx2 = out[2];
  13021. x2 = out[3];
  13022. cubicSubdivide(
  13023. y1, cpy1, cpy2, y2, percent, out
  13024. );
  13025. cpy1 = out[1];
  13026. cpy2 = out[2];
  13027. y2 = out[3];
  13028. }
  13029. ctx.bezierCurveTo(
  13030. cpx1, cpy1,
  13031. cpx2, cpy2,
  13032. x2, y2
  13033. );
  13034. }
  13035. },
  13036. /**
  13037. * Get point at percent
  13038. * @param {number} t
  13039. * @return {Array.<number>}
  13040. */
  13041. pointAt: function (t) {
  13042. return someVectorAt(this.shape, t, false);
  13043. },
  13044. /**
  13045. * Get tangent at percent
  13046. * @param {number} t
  13047. * @return {Array.<number>}
  13048. */
  13049. tangentAt: function (t) {
  13050. var p = someVectorAt(this.shape, t, true);
  13051. return normalize(p, p);
  13052. }
  13053. });
  13054. /**
  13055. * 圆弧
  13056. * @module zrender/graphic/shape/Arc
  13057. */
  13058. var Arc = Path.extend({
  13059. type: 'arc',
  13060. shape: {
  13061. cx: 0,
  13062. cy: 0,
  13063. r: 0,
  13064. startAngle: 0,
  13065. endAngle: Math.PI * 2,
  13066. clockwise: true
  13067. },
  13068. style: {
  13069. stroke: '#000',
  13070. fill: null
  13071. },
  13072. buildPath: function (ctx, shape) {
  13073. var x = shape.cx;
  13074. var y = shape.cy;
  13075. var r = Math.max(shape.r, 0);
  13076. var startAngle = shape.startAngle;
  13077. var endAngle = shape.endAngle;
  13078. var clockwise = shape.clockwise;
  13079. var unitX = Math.cos(startAngle);
  13080. var unitY = Math.sin(startAngle);
  13081. ctx.moveTo(unitX * r + x, unitY * r + y);
  13082. ctx.arc(x, y, r, startAngle, endAngle, !clockwise);
  13083. }
  13084. });
  13085. // CompoundPath to improve performance
  13086. var CompoundPath = Path.extend({
  13087. type: 'compound',
  13088. shape: {
  13089. paths: null
  13090. },
  13091. _updatePathDirty: function () {
  13092. var dirtyPath = this.__dirtyPath;
  13093. var paths = this.shape.paths;
  13094. for (var i = 0; i < paths.length; i++) {
  13095. // Mark as dirty if any subpath is dirty
  13096. dirtyPath = dirtyPath || paths[i].__dirtyPath;
  13097. }
  13098. this.__dirtyPath = dirtyPath;
  13099. this.__dirty = this.__dirty || dirtyPath;
  13100. },
  13101. beforeBrush: function () {
  13102. this._updatePathDirty();
  13103. var paths = this.shape.paths || [];
  13104. var scale = this.getGlobalScale();
  13105. // Update path scale
  13106. for (var i = 0; i < paths.length; i++) {
  13107. if (!paths[i].path) {
  13108. paths[i].createPathProxy();
  13109. }
  13110. paths[i].path.setScale(scale[0], scale[1]);
  13111. }
  13112. },
  13113. buildPath: function (ctx, shape) {
  13114. var paths = shape.paths || [];
  13115. for (var i = 0; i < paths.length; i++) {
  13116. paths[i].buildPath(ctx, paths[i].shape, true);
  13117. }
  13118. },
  13119. afterBrush: function () {
  13120. var paths = this.shape.paths || [];
  13121. for (var i = 0; i < paths.length; i++) {
  13122. paths[i].__dirtyPath = false;
  13123. }
  13124. },
  13125. getBoundingRect: function () {
  13126. this._updatePathDirty();
  13127. return Path.prototype.getBoundingRect.call(this);
  13128. }
  13129. });
  13130. /**
  13131. * @param {Array.<Object>} colorStops
  13132. */
  13133. var Gradient = function (colorStops) {
  13134. this.colorStops = colorStops || [];
  13135. };
  13136. Gradient.prototype = {
  13137. constructor: Gradient,
  13138. addColorStop: function (offset, color) {
  13139. this.colorStops.push({
  13140. offset: offset,
  13141. color: color
  13142. });
  13143. }
  13144. };
  13145. /**
  13146. * x, y, x2, y2 are all percent from 0 to 1
  13147. * @param {number} [x=0]
  13148. * @param {number} [y=0]
  13149. * @param {number} [x2=1]
  13150. * @param {number} [y2=0]
  13151. * @param {Array.<Object>} colorStops
  13152. * @param {boolean} [globalCoord=false]
  13153. */
  13154. var LinearGradient = function (x, y, x2, y2, colorStops, globalCoord) {
  13155. // Should do nothing more in this constructor. Because gradient can be
  13156. // declard by `color: {type: 'linear', colorStops: ...}`, where
  13157. // this constructor will not be called.
  13158. this.x = x == null ? 0 : x;
  13159. this.y = y == null ? 0 : y;
  13160. this.x2 = x2 == null ? 1 : x2;
  13161. this.y2 = y2 == null ? 0 : y2;
  13162. // Can be cloned
  13163. this.type = 'linear';
  13164. // If use global coord
  13165. this.global = globalCoord || false;
  13166. Gradient.call(this, colorStops);
  13167. };
  13168. LinearGradient.prototype = {
  13169. constructor: LinearGradient
  13170. };
  13171. inherits(LinearGradient, Gradient);
  13172. /**
  13173. * x, y, r are all percent from 0 to 1
  13174. * @param {number} [x=0.5]
  13175. * @param {number} [y=0.5]
  13176. * @param {number} [r=0.5]
  13177. * @param {Array.<Object>} [colorStops]
  13178. * @param {boolean} [globalCoord=false]
  13179. */
  13180. var RadialGradient = function (x, y, r, colorStops, globalCoord) {
  13181. // Should do nothing more in this constructor. Because gradient can be
  13182. // declard by `color: {type: 'radial', colorStops: ...}`, where
  13183. // this constructor will not be called.
  13184. this.x = x == null ? 0.5 : x;
  13185. this.y = y == null ? 0.5 : y;
  13186. this.r = r == null ? 0.5 : r;
  13187. // Can be cloned
  13188. this.type = 'radial';
  13189. // If use global coord
  13190. this.global = globalCoord || false;
  13191. Gradient.call(this, colorStops);
  13192. };
  13193. RadialGradient.prototype = {
  13194. constructor: RadialGradient
  13195. };
  13196. inherits(RadialGradient, Gradient);
  13197. var round$1 = Math.round;
  13198. var mathMax$1 = Math.max;
  13199. var mathMin$1 = Math.min;
  13200. var EMPTY_OBJ = {};
  13201. /**
  13202. * Extend shape with parameters
  13203. */
  13204. function extendShape(opts) {
  13205. return Path.extend(opts);
  13206. }
  13207. /**
  13208. * Extend path
  13209. */
  13210. function extendPath(pathData, opts) {
  13211. return extendFromString(pathData, opts);
  13212. }
  13213. /**
  13214. * Create a path element from path data string
  13215. * @param {string} pathData
  13216. * @param {Object} opts
  13217. * @param {module:zrender/core/BoundingRect} rect
  13218. * @param {string} [layout=cover] 'center' or 'cover'
  13219. */
  13220. function makePath(pathData, opts, rect, layout) {
  13221. var path = createFromString(pathData, opts);
  13222. var boundingRect = path.getBoundingRect();
  13223. if (rect) {
  13224. if (layout === 'center') {
  13225. rect = centerGraphic(rect, boundingRect);
  13226. }
  13227. resizePath(path, rect);
  13228. }
  13229. return path;
  13230. }
  13231. /**
  13232. * Create a image element from image url
  13233. * @param {string} imageUrl image url
  13234. * @param {Object} opts options
  13235. * @param {module:zrender/core/BoundingRect} rect constrain rect
  13236. * @param {string} [layout=cover] 'center' or 'cover'
  13237. */
  13238. function makeImage(imageUrl, rect, layout) {
  13239. var path = new ZImage({
  13240. style: {
  13241. image: imageUrl,
  13242. x: rect.x,
  13243. y: rect.y,
  13244. width: rect.width,
  13245. height: rect.height
  13246. },
  13247. onload: function (img) {
  13248. if (layout === 'center') {
  13249. var boundingRect = {
  13250. width: img.width,
  13251. height: img.height
  13252. };
  13253. path.setStyle(centerGraphic(rect, boundingRect));
  13254. }
  13255. }
  13256. });
  13257. return path;
  13258. }
  13259. /**
  13260. * Get position of centered element in bounding box.
  13261. *
  13262. * @param {Object} rect element local bounding box
  13263. * @param {Object} boundingRect constraint bounding box
  13264. * @return {Object} element position containing x, y, width, and height
  13265. */
  13266. function centerGraphic(rect, boundingRect) {
  13267. // Set rect to center, keep width / height ratio.
  13268. var aspect = boundingRect.width / boundingRect.height;
  13269. var width = rect.height * aspect;
  13270. var height;
  13271. if (width <= rect.width) {
  13272. height = rect.height;
  13273. }
  13274. else {
  13275. width = rect.width;
  13276. height = width / aspect;
  13277. }
  13278. var cx = rect.x + rect.width / 2;
  13279. var cy = rect.y + rect.height / 2;
  13280. return {
  13281. x: cx - width / 2,
  13282. y: cy - height / 2,
  13283. width: width,
  13284. height: height
  13285. };
  13286. }
  13287. var mergePath = mergePath$1;
  13288. /**
  13289. * Resize a path to fit the rect
  13290. * @param {module:zrender/graphic/Path} path
  13291. * @param {Object} rect
  13292. */
  13293. function resizePath(path, rect) {
  13294. if (!path.applyTransform) {
  13295. return;
  13296. }
  13297. var pathRect = path.getBoundingRect();
  13298. var m = pathRect.calculateTransform(rect);
  13299. path.applyTransform(m);
  13300. }
  13301. /**
  13302. * Sub pixel optimize line for canvas
  13303. *
  13304. * @param {Object} param
  13305. * @param {Object} [param.shape]
  13306. * @param {number} [param.shape.x1]
  13307. * @param {number} [param.shape.y1]
  13308. * @param {number} [param.shape.x2]
  13309. * @param {number} [param.shape.y2]
  13310. * @param {Object} [param.style]
  13311. * @param {number} [param.style.lineWidth]
  13312. * @return {Object} Modified param
  13313. */
  13314. function subPixelOptimizeLine(param) {
  13315. var shape = param.shape;
  13316. var lineWidth = param.style.lineWidth;
  13317. if (round$1(shape.x1 * 2) === round$1(shape.x2 * 2)) {
  13318. shape.x1 = shape.x2 = subPixelOptimize(shape.x1, lineWidth, true);
  13319. }
  13320. if (round$1(shape.y1 * 2) === round$1(shape.y2 * 2)) {
  13321. shape.y1 = shape.y2 = subPixelOptimize(shape.y1, lineWidth, true);
  13322. }
  13323. return param;
  13324. }
  13325. /**
  13326. * Sub pixel optimize rect for canvas
  13327. *
  13328. * @param {Object} param
  13329. * @param {Object} [param.shape]
  13330. * @param {number} [param.shape.x]
  13331. * @param {number} [param.shape.y]
  13332. * @param {number} [param.shape.width]
  13333. * @param {number} [param.shape.height]
  13334. * @param {Object} [param.style]
  13335. * @param {number} [param.style.lineWidth]
  13336. * @return {Object} Modified param
  13337. */
  13338. function subPixelOptimizeRect(param) {
  13339. var shape = param.shape;
  13340. var lineWidth = param.style.lineWidth;
  13341. var originX = shape.x;
  13342. var originY = shape.y;
  13343. var originWidth = shape.width;
  13344. var originHeight = shape.height;
  13345. shape.x = subPixelOptimize(shape.x, lineWidth, true);
  13346. shape.y = subPixelOptimize(shape.y, lineWidth, true);
  13347. shape.width = Math.max(
  13348. subPixelOptimize(originX + originWidth, lineWidth, false) - shape.x,
  13349. originWidth === 0 ? 0 : 1
  13350. );
  13351. shape.height = Math.max(
  13352. subPixelOptimize(originY + originHeight, lineWidth, false) - shape.y,
  13353. originHeight === 0 ? 0 : 1
  13354. );
  13355. return param;
  13356. }
  13357. /**
  13358. * Sub pixel optimize for canvas
  13359. *
  13360. * @param {number} position Coordinate, such as x, y
  13361. * @param {number} lineWidth Should be nonnegative integer.
  13362. * @param {boolean=} positiveOrNegative Default false (negative).
  13363. * @return {number} Optimized position.
  13364. */
  13365. function subPixelOptimize(position, lineWidth, positiveOrNegative) {
  13366. // Assure that (position + lineWidth / 2) is near integer edge,
  13367. // otherwise line will be fuzzy in canvas.
  13368. var doubledPosition = round$1(position * 2);
  13369. return (doubledPosition + round$1(lineWidth)) % 2 === 0
  13370. ? doubledPosition / 2
  13371. : (doubledPosition + (positiveOrNegative ? 1 : -1)) / 2;
  13372. }
  13373. function hasFillOrStroke(fillOrStroke) {
  13374. return fillOrStroke != null && fillOrStroke != 'none';
  13375. }
  13376. function liftColor(color) {
  13377. return typeof color === 'string' ? lift(color, -0.1) : color;
  13378. }
  13379. /**
  13380. * @private
  13381. */
  13382. function cacheElementStl(el) {
  13383. if (el.__hoverStlDirty) {
  13384. var stroke = el.style.stroke;
  13385. var fill = el.style.fill;
  13386. // Create hoverStyle on mouseover
  13387. var hoverStyle = el.__hoverStl;
  13388. hoverStyle.fill = hoverStyle.fill
  13389. || (hasFillOrStroke(fill) ? liftColor(fill) : null);
  13390. hoverStyle.stroke = hoverStyle.stroke
  13391. || (hasFillOrStroke(stroke) ? liftColor(stroke) : null);
  13392. var normalStyle = {};
  13393. for (var name in hoverStyle) {
  13394. // See comment in `doSingleEnterHover`.
  13395. if (hoverStyle[name] != null) {
  13396. normalStyle[name] = el.style[name];
  13397. }
  13398. }
  13399. el.__normalStl = normalStyle;
  13400. el.__hoverStlDirty = false;
  13401. }
  13402. }
  13403. /**
  13404. * @private
  13405. */
  13406. function doSingleEnterHover(el) {
  13407. if (el.__isHover) {
  13408. return;
  13409. }
  13410. cacheElementStl(el);
  13411. if (el.useHoverLayer) {
  13412. el.__zr && el.__zr.addHover(el, el.__hoverStl);
  13413. }
  13414. else {
  13415. var style = el.style;
  13416. var insideRollbackOpt = style.insideRollbackOpt;
  13417. // Consider case: only `position: 'top'` is set on emphasis, then text
  13418. // color should be returned to `autoColor`, rather than remain '#fff'.
  13419. // So we should rollback then apply again after style merging.
  13420. insideRollbackOpt && rollbackInsideStyle(style);
  13421. // styles can be:
  13422. // {
  13423. // label: {
  13424. // normal: {
  13425. // show: false,
  13426. // position: 'outside',
  13427. // fontSize: 18
  13428. // },
  13429. // emphasis: {
  13430. // show: true
  13431. // }
  13432. // }
  13433. // },
  13434. // where properties of `emphasis` may not appear in `normal`. We previously use
  13435. // module:echarts/util/model#defaultEmphasis to merge `normal` to `emphasis`.
  13436. // But consider rich text and setOption in merge mode, it is impossible to cover
  13437. // all properties in merge. So we use merge mode when setting style here, where
  13438. // only properties that is not `null/undefined` can be set. The disadventage:
  13439. // null/undefined can not be used to remove style any more in `emphasis`.
  13440. style.extendFrom(el.__hoverStl);
  13441. // Do not save `insideRollback`.
  13442. if (insideRollbackOpt) {
  13443. applyInsideStyle(style, style.insideOriginalTextPosition, insideRollbackOpt);
  13444. // textFill may be rollbacked to null.
  13445. if (style.textFill == null) {
  13446. style.textFill = insideRollbackOpt.autoColor;
  13447. }
  13448. }
  13449. el.dirty(false);
  13450. el.z2 += 1;
  13451. }
  13452. el.__isHover = true;
  13453. }
  13454. /**
  13455. * @inner
  13456. */
  13457. function doSingleLeaveHover(el) {
  13458. if (!el.__isHover) {
  13459. return;
  13460. }
  13461. var normalStl = el.__normalStl;
  13462. if (el.useHoverLayer) {
  13463. el.__zr && el.__zr.removeHover(el);
  13464. }
  13465. else {
  13466. // Consider null/undefined value, should use
  13467. // `setStyle` but not `extendFrom(stl, true)`.
  13468. normalStl && el.setStyle(normalStl);
  13469. el.z2 -= 1;
  13470. }
  13471. el.__isHover = false;
  13472. }
  13473. /**
  13474. * @inner
  13475. */
  13476. function doEnterHover(el) {
  13477. el.type === 'group'
  13478. ? el.traverse(function (child) {
  13479. if (child.type !== 'group') {
  13480. doSingleEnterHover(child);
  13481. }
  13482. })
  13483. : doSingleEnterHover(el);
  13484. }
  13485. function doLeaveHover(el) {
  13486. el.type === 'group'
  13487. ? el.traverse(function (child) {
  13488. if (child.type !== 'group') {
  13489. doSingleLeaveHover(child);
  13490. }
  13491. })
  13492. : doSingleLeaveHover(el);
  13493. }
  13494. /**
  13495. * @inner
  13496. */
  13497. function setElementHoverStl(el, hoverStl) {
  13498. // If element has sepcified hoverStyle, then use it instead of given hoverStyle
  13499. // Often used when item group has a label element and it's hoverStyle is different
  13500. el.__hoverStl = el.hoverStyle || hoverStl || {};
  13501. el.__hoverStlDirty = true;
  13502. if (el.__isHover) {
  13503. cacheElementStl(el);
  13504. }
  13505. }
  13506. /**
  13507. * @inner
  13508. */
  13509. function onElementMouseOver(e) {
  13510. if (this.__hoverSilentOnTouch && e.zrByTouch) {
  13511. return;
  13512. }
  13513. // Only if element is not in emphasis status
  13514. !this.__isEmphasis && doEnterHover(this);
  13515. }
  13516. /**
  13517. * @inner
  13518. */
  13519. function onElementMouseOut(e) {
  13520. if (this.__hoverSilentOnTouch && e.zrByTouch) {
  13521. return;
  13522. }
  13523. // Only if element is not in emphasis status
  13524. !this.__isEmphasis && doLeaveHover(this);
  13525. }
  13526. /**
  13527. * @inner
  13528. */
  13529. function enterEmphasis() {
  13530. this.__isEmphasis = true;
  13531. doEnterHover(this);
  13532. }
  13533. /**
  13534. * @inner
  13535. */
  13536. function leaveEmphasis() {
  13537. this.__isEmphasis = false;
  13538. doLeaveHover(this);
  13539. }
  13540. /**
  13541. * Set hover style of element.
  13542. * This method can be called repeatly without side-effects.
  13543. * @param {module:zrender/Element} el
  13544. * @param {Object} [hoverStyle]
  13545. * @param {Object} [opt]
  13546. * @param {boolean} [opt.hoverSilentOnTouch=false]
  13547. * In touch device, mouseover event will be trigger on touchstart event
  13548. * (see module:zrender/dom/HandlerProxy). By this mechanism, we can
  13549. * conviniently use hoverStyle when tap on touch screen without additional
  13550. * code for compatibility.
  13551. * But if the chart/component has select feature, which usually also use
  13552. * hoverStyle, there might be conflict between 'select-highlight' and
  13553. * 'hover-highlight' especially when roam is enabled (see geo for example).
  13554. * In this case, hoverSilentOnTouch should be used to disable hover-highlight
  13555. * on touch device.
  13556. */
  13557. function setHoverStyle(el, hoverStyle, opt) {
  13558. el.__hoverSilentOnTouch = opt && opt.hoverSilentOnTouch;
  13559. el.type === 'group'
  13560. ? el.traverse(function (child) {
  13561. if (child.type !== 'group') {
  13562. setElementHoverStl(child, hoverStyle);
  13563. }
  13564. })
  13565. : setElementHoverStl(el, hoverStyle);
  13566. // Duplicated function will be auto-ignored, see Eventful.js.
  13567. el.on('mouseover', onElementMouseOver)
  13568. .on('mouseout', onElementMouseOut);
  13569. // Emphasis, normal can be triggered manually
  13570. el.on('emphasis', enterEmphasis)
  13571. .on('normal', leaveEmphasis);
  13572. }
  13573. /**
  13574. * @param {Object|module:zrender/graphic/Style} normalStyle
  13575. * @param {Object} emphasisStyle
  13576. * @param {module:echarts/model/Model} normalModel
  13577. * @param {module:echarts/model/Model} emphasisModel
  13578. * @param {Object} opt Check `opt` of `setTextStyleCommon` to find other props.
  13579. * @param {Object} [opt.defaultText]
  13580. * @param {module:echarts/model/Model} [opt.labelFetcher] Fetch text by
  13581. * `opt.labelFetcher.getFormattedLabel(opt.labelDataIndex, 'normal'/'emphasis', null, opt.labelDimIndex)`
  13582. * @param {module:echarts/model/Model} [opt.labelDataIndex] Fetch text by
  13583. * `opt.textFetcher.getFormattedLabel(opt.labelDataIndex, 'normal'/'emphasis', null, opt.labelDimIndex)`
  13584. * @param {module:echarts/model/Model} [opt.labelDimIndex] Fetch text by
  13585. * `opt.textFetcher.getFormattedLabel(opt.labelDataIndex, 'normal'/'emphasis', null, opt.labelDimIndex)`
  13586. * @param {Object} [normalSpecified]
  13587. * @param {Object} [emphasisSpecified]
  13588. */
  13589. function setLabelStyle(
  13590. normalStyle, emphasisStyle,
  13591. normalModel, emphasisModel,
  13592. opt,
  13593. normalSpecified, emphasisSpecified
  13594. ) {
  13595. opt = opt || EMPTY_OBJ;
  13596. var labelFetcher = opt.labelFetcher;
  13597. var labelDataIndex = opt.labelDataIndex;
  13598. var labelDimIndex = opt.labelDimIndex;
  13599. // This scenario, `label.normal.show = true; label.emphasis.show = false`,
  13600. // is not supported util someone requests.
  13601. var showNormal = normalModel.getShallow('show');
  13602. var showEmphasis = emphasisModel.getShallow('show');
  13603. // Consider performance, only fetch label when necessary.
  13604. // If `normal.show` is `false` and `emphasis.show` is `true` and `emphasis.formatter` is not set,
  13605. // label should be displayed, where text is fetched by `normal.formatter` or `opt.defaultText`.
  13606. var baseText = (showNormal || showEmphasis)
  13607. ? retrieve2(
  13608. labelFetcher
  13609. ? labelFetcher.getFormattedLabel(labelDataIndex, 'normal', null, labelDimIndex)
  13610. : null,
  13611. opt.defaultText
  13612. )
  13613. : null;
  13614. var normalStyleText = showNormal ? baseText : null;
  13615. var emphasisStyleText = showEmphasis
  13616. ? retrieve2(
  13617. labelFetcher
  13618. ? labelFetcher.getFormattedLabel(labelDataIndex, 'emphasis', null, labelDimIndex)
  13619. : null,
  13620. baseText
  13621. )
  13622. : null;
  13623. // Optimize: If style.text is null, text will not be drawn.
  13624. if (normalStyleText != null || emphasisStyleText != null) {
  13625. // Always set `textStyle` even if `normalStyle.text` is null, because default
  13626. // values have to be set on `normalStyle`.
  13627. // If we set default values on `emphasisStyle`, consider case:
  13628. // Firstly, `setOption(... label: {normal: {text: null}, emphasis: {show: true}} ...);`
  13629. // Secondly, `setOption(... label: {noraml: {show: true, text: 'abc', color: 'red'} ...);`
  13630. // Then the 'red' will not work on emphasis.
  13631. setTextStyle(normalStyle, normalModel, normalSpecified, opt);
  13632. setTextStyle(emphasisStyle, emphasisModel, emphasisSpecified, opt, true);
  13633. }
  13634. normalStyle.text = normalStyleText;
  13635. emphasisStyle.text = emphasisStyleText;
  13636. }
  13637. /**
  13638. * Set basic textStyle properties.
  13639. * @param {Object|module:zrender/graphic/Style} textStyle
  13640. * @param {module:echarts/model/Model} model
  13641. * @param {Object} [specifiedTextStyle] Can be overrided by settings in model.
  13642. * @param {Object} [opt] See `opt` of `setTextStyleCommon`.
  13643. * @param {boolean} [isEmphasis]
  13644. */
  13645. function setTextStyle(
  13646. textStyle, textStyleModel, specifiedTextStyle, opt, isEmphasis
  13647. ) {
  13648. setTextStyleCommon(textStyle, textStyleModel, opt, isEmphasis);
  13649. specifiedTextStyle && extend(textStyle, specifiedTextStyle);
  13650. textStyle.host && textStyle.host.dirty && textStyle.host.dirty(false);
  13651. return textStyle;
  13652. }
  13653. /**
  13654. * Set text option in the style.
  13655. * @deprecated
  13656. * @param {Object} textStyle
  13657. * @param {module:echarts/model/Model} labelModel
  13658. * @param {string|boolean} defaultColor Default text color.
  13659. * If set as false, it will be processed as a emphasis style.
  13660. */
  13661. function setText(textStyle, labelModel, defaultColor) {
  13662. var opt = {isRectText: true};
  13663. var isEmphasis;
  13664. if (defaultColor === false) {
  13665. isEmphasis = true;
  13666. }
  13667. else {
  13668. // Support setting color as 'auto' to get visual color.
  13669. opt.autoColor = defaultColor;
  13670. }
  13671. setTextStyleCommon(textStyle, labelModel, opt, isEmphasis);
  13672. textStyle.host && textStyle.host.dirty && textStyle.host.dirty(false);
  13673. }
  13674. /**
  13675. * {
  13676. * disableBox: boolean, Whether diable drawing box of block (outer most).
  13677. * isRectText: boolean,
  13678. * autoColor: string, specify a color when color is 'auto',
  13679. * for textFill, textStroke, textBackgroundColor, and textBorderColor.
  13680. * If autoColor specified, it is used as default textFill.
  13681. * useInsideStyle:
  13682. * `true`: Use inside style (textFill, textStroke, textStrokeWidth)
  13683. * if `textFill` is not specified.
  13684. * `false`: Do not use inside style.
  13685. * `null/undefined`: use inside style if `isRectText` is true and
  13686. * `textFill` is not specified and textPosition contains `'inside'`.
  13687. * forceRich: boolean
  13688. * }
  13689. */
  13690. function setTextStyleCommon(textStyle, textStyleModel, opt, isEmphasis) {
  13691. // Consider there will be abnormal when merge hover style to normal style if given default value.
  13692. opt = opt || EMPTY_OBJ;
  13693. if (opt.isRectText) {
  13694. var textPosition = textStyleModel.getShallow('position')
  13695. || (isEmphasis ? null : 'inside');
  13696. // 'outside' is not a valid zr textPostion value, but used
  13697. // in bar series, and magric type should be considered.
  13698. textPosition === 'outside' && (textPosition = 'top');
  13699. textStyle.textPosition = textPosition;
  13700. textStyle.textOffset = textStyleModel.getShallow('offset');
  13701. var labelRotate = textStyleModel.getShallow('rotate');
  13702. labelRotate != null && (labelRotate *= Math.PI / 180);
  13703. textStyle.textRotation = labelRotate;
  13704. textStyle.textDistance = retrieve2(
  13705. textStyleModel.getShallow('distance'), isEmphasis ? null : 5
  13706. );
  13707. }
  13708. var ecModel = textStyleModel.ecModel;
  13709. var globalTextStyle = ecModel && ecModel.option.textStyle;
  13710. // Consider case:
  13711. // {
  13712. // data: [{
  13713. // value: 12,
  13714. // label: {
  13715. // normal: {
  13716. // rich: {
  13717. // // no 'a' here but using parent 'a'.
  13718. // }
  13719. // }
  13720. // }
  13721. // }],
  13722. // rich: {
  13723. // a: { ... }
  13724. // }
  13725. // }
  13726. var richItemNames = getRichItemNames(textStyleModel);
  13727. var richResult;
  13728. if (richItemNames) {
  13729. richResult = {};
  13730. for (var name in richItemNames) {
  13731. if (richItemNames.hasOwnProperty(name)) {
  13732. // Cascade is supported in rich.
  13733. var richTextStyle = textStyleModel.getModel(['rich', name]);
  13734. // In rich, never `disableBox`.
  13735. setTokenTextStyle(richResult[name] = {}, richTextStyle, globalTextStyle, opt, isEmphasis);
  13736. }
  13737. }
  13738. }
  13739. textStyle.rich = richResult;
  13740. setTokenTextStyle(textStyle, textStyleModel, globalTextStyle, opt, isEmphasis, true);
  13741. if (opt.forceRich && !opt.textStyle) {
  13742. opt.textStyle = {};
  13743. }
  13744. return textStyle;
  13745. }
  13746. // Consider case:
  13747. // {
  13748. // data: [{
  13749. // value: 12,
  13750. // label: {
  13751. // normal: {
  13752. // rich: {
  13753. // // no 'a' here but using parent 'a'.
  13754. // }
  13755. // }
  13756. // }
  13757. // }],
  13758. // rich: {
  13759. // a: { ... }
  13760. // }
  13761. // }
  13762. function getRichItemNames(textStyleModel) {
  13763. // Use object to remove duplicated names.
  13764. var richItemNameMap;
  13765. while (textStyleModel && textStyleModel !== textStyleModel.ecModel) {
  13766. var rich = (textStyleModel.option || EMPTY_OBJ).rich;
  13767. if (rich) {
  13768. richItemNameMap = richItemNameMap || {};
  13769. for (var name in rich) {
  13770. if (rich.hasOwnProperty(name)) {
  13771. richItemNameMap[name] = 1;
  13772. }
  13773. }
  13774. }
  13775. textStyleModel = textStyleModel.parentModel;
  13776. }
  13777. return richItemNameMap;
  13778. }
  13779. function setTokenTextStyle(textStyle, textStyleModel, globalTextStyle, opt, isEmphasis, isBlock) {
  13780. // In merge mode, default value should not be given.
  13781. globalTextStyle = !isEmphasis && globalTextStyle || EMPTY_OBJ;
  13782. textStyle.textFill = getAutoColor(textStyleModel.getShallow('color'), opt)
  13783. || globalTextStyle.color;
  13784. textStyle.textStroke = getAutoColor(textStyleModel.getShallow('textBorderColor'), opt)
  13785. || globalTextStyle.textBorderColor;
  13786. textStyle.textStrokeWidth = retrieve2(
  13787. textStyleModel.getShallow('textBorderWidth'),
  13788. globalTextStyle.textBorderWidth
  13789. );
  13790. if (!isEmphasis) {
  13791. if (isBlock) {
  13792. // Always set `insideRollback`, for clearing previous.
  13793. var originalTextPosition = textStyle.textPosition;
  13794. textStyle.insideRollback = applyInsideStyle(textStyle, originalTextPosition, opt);
  13795. // Save original textPosition, because style.textPosition will be repalced by
  13796. // real location (like [10, 30]) in zrender.
  13797. textStyle.insideOriginalTextPosition = originalTextPosition;
  13798. textStyle.insideRollbackOpt = opt;
  13799. }
  13800. // Set default finally.
  13801. if (textStyle.textFill == null) {
  13802. textStyle.textFill = opt.autoColor;
  13803. }
  13804. }
  13805. // Do not use `getFont` here, because merge should be supported, where
  13806. // part of these properties may be changed in emphasis style, and the
  13807. // others should remain their original value got from normal style.
  13808. textStyle.fontStyle = textStyleModel.getShallow('fontStyle') || globalTextStyle.fontStyle;
  13809. textStyle.fontWeight = textStyleModel.getShallow('fontWeight') || globalTextStyle.fontWeight;
  13810. textStyle.fontSize = textStyleModel.getShallow('fontSize') || globalTextStyle.fontSize;
  13811. textStyle.fontFamily = textStyleModel.getShallow('fontFamily') || globalTextStyle.fontFamily;
  13812. textStyle.textAlign = textStyleModel.getShallow('align');
  13813. textStyle.textVerticalAlign = textStyleModel.getShallow('verticalAlign')
  13814. || textStyleModel.getShallow('baseline');
  13815. textStyle.textLineHeight = textStyleModel.getShallow('lineHeight');
  13816. textStyle.textWidth = textStyleModel.getShallow('width');
  13817. textStyle.textHeight = textStyleModel.getShallow('height');
  13818. textStyle.textTag = textStyleModel.getShallow('tag');
  13819. if (!isBlock || !opt.disableBox) {
  13820. textStyle.textBackgroundColor = getAutoColor(textStyleModel.getShallow('backgroundColor'), opt);
  13821. textStyle.textPadding = textStyleModel.getShallow('padding');
  13822. textStyle.textBorderColor = getAutoColor(textStyleModel.getShallow('borderColor'), opt);
  13823. textStyle.textBorderWidth = textStyleModel.getShallow('borderWidth');
  13824. textStyle.textBorderRadius = textStyleModel.getShallow('borderRadius');
  13825. textStyle.textBoxShadowColor = textStyleModel.getShallow('shadowColor');
  13826. textStyle.textBoxShadowBlur = textStyleModel.getShallow('shadowBlur');
  13827. textStyle.textBoxShadowOffsetX = textStyleModel.getShallow('shadowOffsetX');
  13828. textStyle.textBoxShadowOffsetY = textStyleModel.getShallow('shadowOffsetY');
  13829. }
  13830. textStyle.textShadowColor = textStyleModel.getShallow('textShadowColor')
  13831. || globalTextStyle.textShadowColor;
  13832. textStyle.textShadowBlur = textStyleModel.getShallow('textShadowBlur')
  13833. || globalTextStyle.textShadowBlur;
  13834. textStyle.textShadowOffsetX = textStyleModel.getShallow('textShadowOffsetX')
  13835. || globalTextStyle.textShadowOffsetX;
  13836. textStyle.textShadowOffsetY = textStyleModel.getShallow('textShadowOffsetY')
  13837. || globalTextStyle.textShadowOffsetY;
  13838. }
  13839. function getAutoColor(color, opt) {
  13840. return color !== 'auto' ? color : (opt && opt.autoColor) ? opt.autoColor : null;
  13841. }
  13842. function applyInsideStyle(textStyle, textPosition, opt) {
  13843. var useInsideStyle = opt.useInsideStyle;
  13844. var insideRollback;
  13845. if (textStyle.textFill == null
  13846. && useInsideStyle !== false
  13847. && (useInsideStyle === true
  13848. || (opt.isRectText
  13849. && textPosition
  13850. // textPosition can be [10, 30]
  13851. && typeof textPosition === 'string'
  13852. && textPosition.indexOf('inside') >= 0
  13853. )
  13854. )
  13855. ) {
  13856. insideRollback = {
  13857. textFill: null,
  13858. textStroke: textStyle.textStroke,
  13859. textStrokeWidth: textStyle.textStrokeWidth
  13860. };
  13861. textStyle.textFill = '#fff';
  13862. // Consider text with #fff overflow its container.
  13863. if (textStyle.textStroke == null) {
  13864. textStyle.textStroke = opt.autoColor;
  13865. textStyle.textStrokeWidth == null && (textStyle.textStrokeWidth = 2);
  13866. }
  13867. }
  13868. return insideRollback;
  13869. }
  13870. function rollbackInsideStyle(style) {
  13871. var insideRollback = style.insideRollback;
  13872. if (insideRollback) {
  13873. style.textFill = insideRollback.textFill;
  13874. style.textStroke = insideRollback.textStroke;
  13875. style.textStrokeWidth = insideRollback.textStrokeWidth;
  13876. }
  13877. }
  13878. function getFont(opt, ecModel) {
  13879. // ecModel or default text style model.
  13880. var gTextStyleModel = ecModel || ecModel.getModel('textStyle');
  13881. return [
  13882. // FIXME in node-canvas fontWeight is before fontStyle
  13883. opt.fontStyle || gTextStyleModel && gTextStyleModel.getShallow('fontStyle') || '',
  13884. opt.fontWeight || gTextStyleModel && gTextStyleModel.getShallow('fontWeight') || '',
  13885. (opt.fontSize || gTextStyleModel && gTextStyleModel.getShallow('fontSize') || 12) + 'px',
  13886. opt.fontFamily || gTextStyleModel && gTextStyleModel.getShallow('fontFamily') || 'sans-serif'
  13887. ].join(' ');
  13888. }
  13889. function animateOrSetProps(isUpdate, el, props, animatableModel, dataIndex, cb) {
  13890. if (typeof dataIndex === 'function') {
  13891. cb = dataIndex;
  13892. dataIndex = null;
  13893. }
  13894. // Do not check 'animation' property directly here. Consider this case:
  13895. // animation model is an `itemModel`, whose does not have `isAnimationEnabled`
  13896. // but its parent model (`seriesModel`) does.
  13897. var animationEnabled = animatableModel && animatableModel.isAnimationEnabled();
  13898. if (animationEnabled) {
  13899. var postfix = isUpdate ? 'Update' : '';
  13900. var duration = animatableModel.getShallow('animationDuration' + postfix);
  13901. var animationEasing = animatableModel.getShallow('animationEasing' + postfix);
  13902. var animationDelay = animatableModel.getShallow('animationDelay' + postfix);
  13903. if (typeof animationDelay === 'function') {
  13904. animationDelay = animationDelay(
  13905. dataIndex,
  13906. animatableModel.getAnimationDelayParams
  13907. ? animatableModel.getAnimationDelayParams(el, dataIndex)
  13908. : null
  13909. );
  13910. }
  13911. if (typeof duration === 'function') {
  13912. duration = duration(dataIndex);
  13913. }
  13914. duration > 0
  13915. ? el.animateTo(props, duration, animationDelay || 0, animationEasing, cb, !!cb)
  13916. : (el.stopAnimation(), el.attr(props), cb && cb());
  13917. }
  13918. else {
  13919. el.stopAnimation();
  13920. el.attr(props);
  13921. cb && cb();
  13922. }
  13923. }
  13924. /**
  13925. * Update graphic element properties with or without animation according to the
  13926. * configuration in series.
  13927. *
  13928. * Caution: this method will stop previous animation.
  13929. * So if do not use this method to one element twice before
  13930. * animation starts, unless you know what you are doing.
  13931. *
  13932. * @param {module:zrender/Element} el
  13933. * @param {Object} props
  13934. * @param {module:echarts/model/Model} [animatableModel]
  13935. * @param {number} [dataIndex]
  13936. * @param {Function} [cb]
  13937. * @example
  13938. * graphic.updateProps(el, {
  13939. * position: [100, 100]
  13940. * }, seriesModel, dataIndex, function () { console.log('Animation done!'); });
  13941. * // Or
  13942. * graphic.updateProps(el, {
  13943. * position: [100, 100]
  13944. * }, seriesModel, function () { console.log('Animation done!'); });
  13945. */
  13946. function updateProps(el, props, animatableModel, dataIndex, cb) {
  13947. animateOrSetProps(true, el, props, animatableModel, dataIndex, cb);
  13948. }
  13949. /**
  13950. * Init graphic element properties with or without animation according to the
  13951. * configuration in series.
  13952. *
  13953. * Caution: this method will stop previous animation.
  13954. * So if do not use this method to one element twice before
  13955. * animation starts, unless you know what you are doing.
  13956. *
  13957. * @param {module:zrender/Element} el
  13958. * @param {Object} props
  13959. * @param {module:echarts/model/Model} [animatableModel]
  13960. * @param {number} [dataIndex]
  13961. * @param {Function} cb
  13962. */
  13963. function initProps(el, props, animatableModel, dataIndex, cb) {
  13964. animateOrSetProps(false, el, props, animatableModel, dataIndex, cb);
  13965. }
  13966. /**
  13967. * Get transform matrix of target (param target),
  13968. * in coordinate of its ancestor (param ancestor)
  13969. *
  13970. * @param {module:zrender/mixin/Transformable} target
  13971. * @param {module:zrender/mixin/Transformable} [ancestor]
  13972. */
  13973. function getTransform(target, ancestor) {
  13974. var mat = identity([]);
  13975. while (target && target !== ancestor) {
  13976. mul$1(mat, target.getLocalTransform(), mat);
  13977. target = target.parent;
  13978. }
  13979. return mat;
  13980. }
  13981. /**
  13982. * Apply transform to an vertex.
  13983. * @param {Array.<number>} target [x, y]
  13984. * @param {Array.<number>|TypedArray.<number>|Object} transform Can be:
  13985. * + Transform matrix: like [1, 0, 0, 1, 0, 0]
  13986. * + {position, rotation, scale}, the same as `zrender/Transformable`.
  13987. * @param {boolean=} invert Whether use invert matrix.
  13988. * @return {Array.<number>} [x, y]
  13989. */
  13990. function applyTransform$1(target, transform, invert$$1) {
  13991. if (transform && !isArrayLike(transform)) {
  13992. transform = Transformable.getLocalTransform(transform);
  13993. }
  13994. if (invert$$1) {
  13995. transform = invert([], transform);
  13996. }
  13997. return applyTransform([], target, transform);
  13998. }
  13999. /**
  14000. * @param {string} direction 'left' 'right' 'top' 'bottom'
  14001. * @param {Array.<number>} transform Transform matrix: like [1, 0, 0, 1, 0, 0]
  14002. * @param {boolean=} invert Whether use invert matrix.
  14003. * @return {string} Transformed direction. 'left' 'right' 'top' 'bottom'
  14004. */
  14005. function transformDirection(direction, transform, invert$$1) {
  14006. // Pick a base, ensure that transform result will not be (0, 0).
  14007. var hBase = (transform[4] === 0 || transform[5] === 0 || transform[0] === 0)
  14008. ? 1 : Math.abs(2 * transform[4] / transform[0]);
  14009. var vBase = (transform[4] === 0 || transform[5] === 0 || transform[2] === 0)
  14010. ? 1 : Math.abs(2 * transform[4] / transform[2]);
  14011. var vertex = [
  14012. direction === 'left' ? -hBase : direction === 'right' ? hBase : 0,
  14013. direction === 'top' ? -vBase : direction === 'bottom' ? vBase : 0
  14014. ];
  14015. vertex = applyTransform$1(vertex, transform, invert$$1);
  14016. return Math.abs(vertex[0]) > Math.abs(vertex[1])
  14017. ? (vertex[0] > 0 ? 'right' : 'left')
  14018. : (vertex[1] > 0 ? 'bottom' : 'top');
  14019. }
  14020. /**
  14021. * Apply group transition animation from g1 to g2.
  14022. * If no animatableModel, no animation.
  14023. */
  14024. function groupTransition(g1, g2, animatableModel, cb) {
  14025. if (!g1 || !g2) {
  14026. return;
  14027. }
  14028. function getElMap(g) {
  14029. var elMap = {};
  14030. g.traverse(function (el) {
  14031. if (!el.isGroup && el.anid) {
  14032. elMap[el.anid] = el;
  14033. }
  14034. });
  14035. return elMap;
  14036. }
  14037. function getAnimatableProps(el) {
  14038. var obj = {
  14039. position: clone$1(el.position),
  14040. rotation: el.rotation
  14041. };
  14042. if (el.shape) {
  14043. obj.shape = extend({}, el.shape);
  14044. }
  14045. return obj;
  14046. }
  14047. var elMap1 = getElMap(g1);
  14048. g2.traverse(function (el) {
  14049. if (!el.isGroup && el.anid) {
  14050. var oldEl = elMap1[el.anid];
  14051. if (oldEl) {
  14052. var newProp = getAnimatableProps(el);
  14053. el.attr(getAnimatableProps(oldEl));
  14054. updateProps(el, newProp, animatableModel, el.dataIndex);
  14055. }
  14056. // else {
  14057. // if (el.previousProps) {
  14058. // graphic.updateProps
  14059. // }
  14060. // }
  14061. }
  14062. });
  14063. }
  14064. /**
  14065. * @param {Array.<Array.<number>>} points Like: [[23, 44], [53, 66], ...]
  14066. * @param {Object} rect {x, y, width, height}
  14067. * @return {Array.<Array.<number>>} A new clipped points.
  14068. */
  14069. function clipPointsByRect(points, rect) {
  14070. return map(points, function (point) {
  14071. var x = point[0];
  14072. x = mathMax$1(x, rect.x);
  14073. x = mathMin$1(x, rect.x + rect.width);
  14074. var y = point[1];
  14075. y = mathMax$1(y, rect.y);
  14076. y = mathMin$1(y, rect.y + rect.height);
  14077. return [x, y];
  14078. });
  14079. }
  14080. /**
  14081. * @param {Object} targetRect {x, y, width, height}
  14082. * @param {Object} rect {x, y, width, height}
  14083. * @return {Object} A new clipped rect. If rect size are negative, return undefined.
  14084. */
  14085. function clipRectByRect(targetRect, rect) {
  14086. var x = mathMax$1(targetRect.x, rect.x);
  14087. var x2 = mathMin$1(targetRect.x + targetRect.width, rect.x + rect.width);
  14088. var y = mathMax$1(targetRect.y, rect.y);
  14089. var y2 = mathMin$1(targetRect.y + targetRect.height, rect.y + rect.height);
  14090. if (x2 >= x && y2 >= y) {
  14091. return {
  14092. x: x,
  14093. y: y,
  14094. width: x2 - x,
  14095. height: y2 - y
  14096. };
  14097. }
  14098. }
  14099. /**
  14100. * @param {string} iconStr Support 'image://' or 'path://' or direct svg path.
  14101. * @param {Object} [opt] Properties of `module:zrender/Element`, except `style`.
  14102. * @param {Object} [rect] {x, y, width, height}
  14103. * @return {module:zrender/Element} Icon path or image element.
  14104. */
  14105. function createIcon(iconStr, opt, rect) {
  14106. opt = extend({rectHover: true}, opt);
  14107. var style = opt.style = {strokeNoScale: true};
  14108. rect = rect || {x: -1, y: -1, width: 2, height: 2};
  14109. if (iconStr) {
  14110. return iconStr.indexOf('image://') === 0
  14111. ? (
  14112. style.image = iconStr.slice(8),
  14113. defaults(style, rect),
  14114. new ZImage(opt)
  14115. )
  14116. : (
  14117. makePath(
  14118. iconStr.replace('path://', ''),
  14119. opt,
  14120. rect,
  14121. 'center'
  14122. )
  14123. );
  14124. }
  14125. }
  14126. var graphic = (Object.freeze || Object)({
  14127. extendShape: extendShape,
  14128. extendPath: extendPath,
  14129. makePath: makePath,
  14130. makeImage: makeImage,
  14131. mergePath: mergePath,
  14132. resizePath: resizePath,
  14133. subPixelOptimizeLine: subPixelOptimizeLine,
  14134. subPixelOptimizeRect: subPixelOptimizeRect,
  14135. subPixelOptimize: subPixelOptimize,
  14136. setHoverStyle: setHoverStyle,
  14137. setLabelStyle: setLabelStyle,
  14138. setTextStyle: setTextStyle,
  14139. setText: setText,
  14140. getFont: getFont,
  14141. updateProps: updateProps,
  14142. initProps: initProps,
  14143. getTransform: getTransform,
  14144. applyTransform: applyTransform$1,
  14145. transformDirection: transformDirection,
  14146. groupTransition: groupTransition,
  14147. clipPointsByRect: clipPointsByRect,
  14148. clipRectByRect: clipRectByRect,
  14149. createIcon: createIcon,
  14150. Group: Group,
  14151. Image: ZImage,
  14152. Text: Text,
  14153. Circle: Circle,
  14154. Sector: Sector,
  14155. Ring: Ring,
  14156. Polygon: Polygon,
  14157. Polyline: Polyline,
  14158. Rect: Rect,
  14159. Line: Line,
  14160. BezierCurve: BezierCurve,
  14161. Arc: Arc,
  14162. CompoundPath: CompoundPath,
  14163. LinearGradient: LinearGradient,
  14164. RadialGradient: RadialGradient,
  14165. BoundingRect: BoundingRect
  14166. });
  14167. var PATH_COLOR = ['textStyle', 'color'];
  14168. var textStyleMixin = {
  14169. /**
  14170. * Get color property or get color from option.textStyle.color
  14171. * @param {boolean} [isEmphasis]
  14172. * @return {string}
  14173. */
  14174. getTextColor: function (isEmphasis) {
  14175. var ecModel = this.ecModel;
  14176. return this.getShallow('color')
  14177. || (
  14178. (!isEmphasis && ecModel) ? ecModel.get(PATH_COLOR) : null
  14179. );
  14180. },
  14181. /**
  14182. * Create font string from fontStyle, fontWeight, fontSize, fontFamily
  14183. * @return {string}
  14184. */
  14185. getFont: function () {
  14186. return getFont({
  14187. fontStyle: this.getShallow('fontStyle'),
  14188. fontWeight: this.getShallow('fontWeight'),
  14189. fontSize: this.getShallow('fontSize'),
  14190. fontFamily: this.getShallow('fontFamily')
  14191. }, this.ecModel);
  14192. },
  14193. getTextRect: function (text) {
  14194. return getBoundingRect(
  14195. text,
  14196. this.getFont(),
  14197. this.getShallow('align'),
  14198. this.getShallow('verticalAlign') || this.getShallow('baseline'),
  14199. this.getShallow('padding'),
  14200. this.getShallow('rich'),
  14201. this.getShallow('truncateText')
  14202. );
  14203. }
  14204. };
  14205. var getItemStyle = makeStyleMapper(
  14206. [
  14207. ['fill', 'color'],
  14208. ['stroke', 'borderColor'],
  14209. ['lineWidth', 'borderWidth'],
  14210. ['opacity'],
  14211. ['shadowBlur'],
  14212. ['shadowOffsetX'],
  14213. ['shadowOffsetY'],
  14214. ['shadowColor'],
  14215. ['textPosition'],
  14216. ['textAlign']
  14217. ]
  14218. );
  14219. var itemStyleMixin = {
  14220. getItemStyle: function (excludes, includes) {
  14221. var style = getItemStyle(this, excludes, includes);
  14222. var lineDash = this.getBorderLineDash();
  14223. lineDash && (style.lineDash = lineDash);
  14224. return style;
  14225. },
  14226. getBorderLineDash: function () {
  14227. var lineType = this.get('borderType');
  14228. return (lineType === 'solid' || lineType == null) ? null
  14229. : (lineType === 'dashed' ? [5, 5] : [1, 1]);
  14230. }
  14231. };
  14232. /**
  14233. * @module echarts/model/Model
  14234. */
  14235. var mixin$1 = mixin;
  14236. /**
  14237. * @alias module:echarts/model/Model
  14238. * @constructor
  14239. * @param {Object} option
  14240. * @param {module:echarts/model/Model} [parentModel]
  14241. * @param {module:echarts/model/Global} [ecModel]
  14242. */
  14243. function Model(option, parentModel, ecModel) {
  14244. /**
  14245. * @type {module:echarts/model/Model}
  14246. * @readOnly
  14247. */
  14248. this.parentModel = parentModel;
  14249. /**
  14250. * @type {module:echarts/model/Global}
  14251. * @readOnly
  14252. */
  14253. this.ecModel = ecModel;
  14254. /**
  14255. * @type {Object}
  14256. * @protected
  14257. */
  14258. this.option = option;
  14259. // Simple optimization
  14260. // if (this.init) {
  14261. // if (arguments.length <= 4) {
  14262. // this.init(option, parentModel, ecModel, extraOpt);
  14263. // }
  14264. // else {
  14265. // this.init.apply(this, arguments);
  14266. // }
  14267. // }
  14268. }
  14269. Model.prototype = {
  14270. constructor: Model,
  14271. /**
  14272. * Model 的初始化函数
  14273. * @param {Object} option
  14274. */
  14275. init: null,
  14276. /**
  14277. * 从新的 Option merge
  14278. */
  14279. mergeOption: function (option) {
  14280. merge(this.option, option, true);
  14281. },
  14282. /**
  14283. * @param {string|Array.<string>} path
  14284. * @param {boolean} [ignoreParent=false]
  14285. * @return {*}
  14286. */
  14287. get: function (path, ignoreParent) {
  14288. if (path == null) {
  14289. return this.option;
  14290. }
  14291. return doGet(
  14292. this.option,
  14293. this.parsePath(path),
  14294. !ignoreParent && getParent(this, path)
  14295. );
  14296. },
  14297. /**
  14298. * @param {string} key
  14299. * @param {boolean} [ignoreParent=false]
  14300. * @return {*}
  14301. */
  14302. getShallow: function (key, ignoreParent) {
  14303. var option = this.option;
  14304. var val = option == null ? option : option[key];
  14305. var parentModel = !ignoreParent && getParent(this, key);
  14306. if (val == null && parentModel) {
  14307. val = parentModel.getShallow(key);
  14308. }
  14309. return val;
  14310. },
  14311. /**
  14312. * @param {string|Array.<string>} [path]
  14313. * @param {module:echarts/model/Model} [parentModel]
  14314. * @return {module:echarts/model/Model}
  14315. */
  14316. getModel: function (path, parentModel) {
  14317. var obj = path == null
  14318. ? this.option
  14319. : doGet(this.option, path = this.parsePath(path));
  14320. var thisParentModel;
  14321. parentModel = parentModel || (
  14322. (thisParentModel = getParent(this, path))
  14323. && thisParentModel.getModel(path)
  14324. );
  14325. return new Model(obj, parentModel, this.ecModel);
  14326. },
  14327. /**
  14328. * If model has option
  14329. */
  14330. isEmpty: function () {
  14331. return this.option == null;
  14332. },
  14333. restoreData: function () {},
  14334. // Pending
  14335. clone: function () {
  14336. var Ctor = this.constructor;
  14337. return new Ctor(clone(this.option));
  14338. },
  14339. setReadOnly: function (properties) {
  14340. },
  14341. // If path is null/undefined, return null/undefined.
  14342. parsePath: function(path) {
  14343. if (typeof path === 'string') {
  14344. path = path.split('.');
  14345. }
  14346. return path;
  14347. },
  14348. /**
  14349. * @param {Function} getParentMethod
  14350. * param {Array.<string>|string} path
  14351. * return {module:echarts/model/Model}
  14352. */
  14353. customizeGetParent: function (getParentMethod) {
  14354. set$1(this, 'getParent', getParentMethod);
  14355. },
  14356. isAnimationEnabled: function () {
  14357. if (!env$1.node) {
  14358. if (this.option.animation != null) {
  14359. return !!this.option.animation;
  14360. }
  14361. else if (this.parentModel) {
  14362. return this.parentModel.isAnimationEnabled();
  14363. }
  14364. }
  14365. }
  14366. };
  14367. function doGet(obj, pathArr, parentModel) {
  14368. for (var i = 0; i < pathArr.length; i++) {
  14369. // Ignore empty
  14370. if (!pathArr[i]) {
  14371. continue;
  14372. }
  14373. // obj could be number/string/... (like 0)
  14374. obj = (obj && typeof obj === 'object') ? obj[pathArr[i]] : null;
  14375. if (obj == null) {
  14376. break;
  14377. }
  14378. }
  14379. if (obj == null && parentModel) {
  14380. obj = parentModel.get(pathArr);
  14381. }
  14382. return obj;
  14383. }
  14384. // `path` can be null/undefined
  14385. function getParent(model, path) {
  14386. var getParentMethod = get(model, 'getParent');
  14387. return getParentMethod ? getParentMethod.call(model, path) : model.parentModel;
  14388. }
  14389. // Enable Model.extend.
  14390. enableClassExtend(Model);
  14391. mixin$1(Model, lineStyleMixin);
  14392. mixin$1(Model, areaStyleMixin);
  14393. mixin$1(Model, textStyleMixin);
  14394. mixin$1(Model, itemStyleMixin);
  14395. var each$3 = each$1;
  14396. var isObject$2 = isObject;
  14397. /**
  14398. * If value is not array, then translate it to array.
  14399. * @param {*} value
  14400. * @return {Array} [value] or value
  14401. */
  14402. function normalizeToArray(value) {
  14403. return value instanceof Array
  14404. ? value
  14405. : value == null
  14406. ? []
  14407. : [value];
  14408. }
  14409. /**
  14410. * Sync default option between normal and emphasis like `position` and `show`
  14411. * In case some one will write code like
  14412. * label: {
  14413. * normal: {
  14414. * show: false,
  14415. * position: 'outside',
  14416. * fontSize: 18
  14417. * },
  14418. * emphasis: {
  14419. * show: true
  14420. * }
  14421. * }
  14422. * @param {Object} opt
  14423. * @param {Array.<string>} subOpts
  14424. */
  14425. function defaultEmphasis(opt, subOpts) {
  14426. if (opt) {
  14427. var emphasisOpt = opt.emphasis = opt.emphasis || {};
  14428. var normalOpt = opt.normal = opt.normal || {};
  14429. // Default emphasis option from normal
  14430. for (var i = 0, len = subOpts.length; i < len; i++) {
  14431. var subOptName = subOpts[i];
  14432. if (!emphasisOpt.hasOwnProperty(subOptName)
  14433. && normalOpt.hasOwnProperty(subOptName)
  14434. ) {
  14435. emphasisOpt[subOptName] = normalOpt[subOptName];
  14436. }
  14437. }
  14438. }
  14439. }
  14440. var TEXT_STYLE_OPTIONS = [
  14441. 'fontStyle', 'fontWeight', 'fontSize', 'fontFamily',
  14442. 'rich', 'tag', 'color', 'textBorderColor', 'textBorderWidth',
  14443. 'width', 'height', 'lineHeight', 'align', 'verticalAlign', 'baseline',
  14444. 'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY',
  14445. 'textShadowColor', 'textShadowBlur', 'textShadowOffsetX', 'textShadowOffsetY',
  14446. 'backgroundColor', 'borderColor', 'borderWidth', 'borderRadius', 'padding'
  14447. ];
  14448. // modelUtil.LABEL_OPTIONS = modelUtil.TEXT_STYLE_OPTIONS.concat([
  14449. // 'position', 'offset', 'rotate', 'origin', 'show', 'distance', 'formatter',
  14450. // 'fontStyle', 'fontWeight', 'fontSize', 'fontFamily',
  14451. // // FIXME: deprecated, check and remove it.
  14452. // 'textStyle'
  14453. // ]);
  14454. /**
  14455. * data could be [12, 2323, {value: 223}, [1221, 23], {value: [2, 23]}]
  14456. * This helper method retieves value from data.
  14457. * @param {string|number|Date|Array|Object} dataItem
  14458. * @return {number|string|Date|Array.<number|string|Date>}
  14459. */
  14460. function getDataItemValue(dataItem) {
  14461. // Performance sensitive.
  14462. return dataItem && (dataItem.value == null ? dataItem : dataItem.value);
  14463. }
  14464. /**
  14465. * data could be [12, 2323, {value: 223}, [1221, 23], {value: [2, 23]}]
  14466. * This helper method determine if dataItem has extra option besides value
  14467. * @param {string|number|Date|Array|Object} dataItem
  14468. */
  14469. function isDataItemOption(dataItem) {
  14470. return isObject$2(dataItem)
  14471. && !(dataItem instanceof Array);
  14472. // // markLine data can be array
  14473. // && !(dataItem[0] && isObject(dataItem[0]) && !(dataItem[0] instanceof Array));
  14474. }
  14475. /**
  14476. * This helper method convert value in data.
  14477. * @param {string|number|Date} value
  14478. * @param {Object|string} [dimInfo] If string (like 'x'), dimType defaults 'number'.
  14479. */
  14480. function converDataValue(value, dimInfo) {
  14481. // Performance sensitive.
  14482. var dimType = dimInfo && dimInfo.type;
  14483. if (dimType === 'ordinal') {
  14484. return value;
  14485. }
  14486. if (dimType === 'time'
  14487. // spead up when using timestamp
  14488. && typeof value !== 'number'
  14489. && value != null
  14490. && value !== '-'
  14491. ) {
  14492. value = +parseDate(value);
  14493. }
  14494. // dimType defaults 'number'.
  14495. // If dimType is not ordinal and value is null or undefined or NaN or '-',
  14496. // parse to NaN.
  14497. return (value == null || value === '')
  14498. ? NaN : +value; // If string (like '-'), using '+' parse to NaN
  14499. }
  14500. /**
  14501. * Create a model proxy to be used in tooltip for edge data, markLine data, markPoint data.
  14502. * @param {module:echarts/data/List} data
  14503. * @param {Object} opt
  14504. * @param {string} [opt.seriesIndex]
  14505. * @param {Object} [opt.name]
  14506. * @param {Object} [opt.mainType]
  14507. * @param {Object} [opt.subType]
  14508. */
  14509. // PENDING A little ugly
  14510. var dataFormatMixin = {
  14511. /**
  14512. * Get params for formatter
  14513. * @param {number} dataIndex
  14514. * @param {string} [dataType]
  14515. * @return {Object}
  14516. */
  14517. getDataParams: function (dataIndex, dataType) {
  14518. var data = this.getData(dataType);
  14519. var rawValue = this.getRawValue(dataIndex, dataType);
  14520. var rawDataIndex = data.getRawIndex(dataIndex);
  14521. var name = data.getName(dataIndex, true);
  14522. var itemOpt = data.getRawDataItem(dataIndex);
  14523. var color = data.getItemVisual(dataIndex, 'color');
  14524. return {
  14525. componentType: this.mainType,
  14526. componentSubType: this.subType,
  14527. seriesType: this.mainType === 'series' ? this.subType : null,
  14528. seriesIndex: this.seriesIndex,
  14529. seriesId: this.id,
  14530. seriesName: this.name,
  14531. name: name,
  14532. dataIndex: rawDataIndex,
  14533. data: itemOpt,
  14534. dataType: dataType,
  14535. value: rawValue,
  14536. color: color,
  14537. marker: getTooltipMarker(color),
  14538. // Param name list for mapping `a`, `b`, `c`, `d`, `e`
  14539. $vars: ['seriesName', 'name', 'value']
  14540. };
  14541. },
  14542. /**
  14543. * Format label
  14544. * @param {number} dataIndex
  14545. * @param {string} [status='normal'] 'normal' or 'emphasis'
  14546. * @param {string} [dataType]
  14547. * @param {number} [dimIndex]
  14548. * @param {string} [labelProp='label']
  14549. * @return {string}
  14550. */
  14551. getFormattedLabel: function (dataIndex, status, dataType, dimIndex, labelProp) {
  14552. status = status || 'normal';
  14553. var data = this.getData(dataType);
  14554. var itemModel = data.getItemModel(dataIndex);
  14555. var params = this.getDataParams(dataIndex, dataType);
  14556. if (dimIndex != null && (params.value instanceof Array)) {
  14557. params.value = params.value[dimIndex];
  14558. }
  14559. var formatter = itemModel.get([labelProp || 'label', status, 'formatter']);
  14560. if (typeof formatter === 'function') {
  14561. params.status = status;
  14562. return formatter(params);
  14563. }
  14564. else if (typeof formatter === 'string') {
  14565. return formatTpl(formatter, params);
  14566. }
  14567. },
  14568. /**
  14569. * Get raw value in option
  14570. * @param {number} idx
  14571. * @param {string} [dataType]
  14572. * @return {Object}
  14573. */
  14574. getRawValue: function (idx, dataType) {
  14575. var data = this.getData(dataType);
  14576. var dataItem = data.getRawDataItem(idx);
  14577. if (dataItem != null) {
  14578. return (isObject$2(dataItem) && !(dataItem instanceof Array))
  14579. ? dataItem.value : dataItem;
  14580. }
  14581. },
  14582. /**
  14583. * Should be implemented.
  14584. * @param {number} dataIndex
  14585. * @param {boolean} [multipleSeries=false]
  14586. * @param {number} [dataType]
  14587. * @return {string} tooltip string
  14588. */
  14589. formatTooltip: noop
  14590. };
  14591. /**
  14592. * Mapping to exists for merge.
  14593. *
  14594. * @public
  14595. * @param {Array.<Object>|Array.<module:echarts/model/Component>} exists
  14596. * @param {Object|Array.<Object>} newCptOptions
  14597. * @return {Array.<Object>} Result, like [{exist: ..., option: ...}, {}],
  14598. * index of which is the same as exists.
  14599. */
  14600. function mappingToExists(exists, newCptOptions) {
  14601. // Mapping by the order by original option (but not order of
  14602. // new option) in merge mode. Because we should ensure
  14603. // some specified index (like xAxisIndex) is consistent with
  14604. // original option, which is easy to understand, espatially in
  14605. // media query. And in most case, merge option is used to
  14606. // update partial option but not be expected to change order.
  14607. newCptOptions = (newCptOptions || []).slice();
  14608. var result = map(exists || [], function (obj, index) {
  14609. return {exist: obj};
  14610. });
  14611. // Mapping by id or name if specified.
  14612. each$3(newCptOptions, function (cptOption, index) {
  14613. if (!isObject$2(cptOption)) {
  14614. return;
  14615. }
  14616. // id has highest priority.
  14617. for (var i = 0; i < result.length; i++) {
  14618. if (!result[i].option // Consider name: two map to one.
  14619. && cptOption.id != null
  14620. && result[i].exist.id === cptOption.id + ''
  14621. ) {
  14622. result[i].option = cptOption;
  14623. newCptOptions[index] = null;
  14624. return;
  14625. }
  14626. }
  14627. for (var i = 0; i < result.length; i++) {
  14628. var exist = result[i].exist;
  14629. if (!result[i].option // Consider name: two map to one.
  14630. // Can not match when both ids exist but different.
  14631. && (exist.id == null || cptOption.id == null)
  14632. && cptOption.name != null
  14633. && !isIdInner(cptOption)
  14634. && !isIdInner(exist)
  14635. && exist.name === cptOption.name + ''
  14636. ) {
  14637. result[i].option = cptOption;
  14638. newCptOptions[index] = null;
  14639. return;
  14640. }
  14641. }
  14642. });
  14643. // Otherwise mapping by index.
  14644. each$3(newCptOptions, function (cptOption, index) {
  14645. if (!isObject$2(cptOption)) {
  14646. return;
  14647. }
  14648. var i = 0;
  14649. for (; i < result.length; i++) {
  14650. var exist = result[i].exist;
  14651. if (!result[i].option
  14652. // Existing model that already has id should be able to
  14653. // mapped to (because after mapping performed model may
  14654. // be assigned with a id, whish should not affect next
  14655. // mapping), except those has inner id.
  14656. && !isIdInner(exist)
  14657. // Caution:
  14658. // Do not overwrite id. But name can be overwritten,
  14659. // because axis use name as 'show label text'.
  14660. // 'exist' always has id and name and we dont
  14661. // need to check it.
  14662. && cptOption.id == null
  14663. ) {
  14664. result[i].option = cptOption;
  14665. break;
  14666. }
  14667. }
  14668. if (i >= result.length) {
  14669. result.push({option: cptOption});
  14670. }
  14671. });
  14672. return result;
  14673. }
  14674. /**
  14675. * Make id and name for mapping result (result of mappingToExists)
  14676. * into `keyInfo` field.
  14677. *
  14678. * @public
  14679. * @param {Array.<Object>} Result, like [{exist: ..., option: ...}, {}],
  14680. * which order is the same as exists.
  14681. * @return {Array.<Object>} The input.
  14682. */
  14683. function makeIdAndName(mapResult) {
  14684. // We use this id to hash component models and view instances
  14685. // in echarts. id can be specified by user, or auto generated.
  14686. // The id generation rule ensures new view instance are able
  14687. // to mapped to old instance when setOption are called in
  14688. // no-merge mode. So we generate model id by name and plus
  14689. // type in view id.
  14690. // name can be duplicated among components, which is convenient
  14691. // to specify multi components (like series) by one name.
  14692. // Ensure that each id is distinct.
  14693. var idMap = createHashMap();
  14694. each$3(mapResult, function (item, index) {
  14695. var existCpt = item.exist;
  14696. existCpt && idMap.set(existCpt.id, item);
  14697. });
  14698. each$3(mapResult, function (item, index) {
  14699. var opt = item.option;
  14700. assert(
  14701. !opt || opt.id == null || !idMap.get(opt.id) || idMap.get(opt.id) === item,
  14702. 'id duplicates: ' + (opt && opt.id)
  14703. );
  14704. opt && opt.id != null && idMap.set(opt.id, item);
  14705. !item.keyInfo && (item.keyInfo = {});
  14706. });
  14707. // Make name and id.
  14708. each$3(mapResult, function (item, index) {
  14709. var existCpt = item.exist;
  14710. var opt = item.option;
  14711. var keyInfo = item.keyInfo;
  14712. if (!isObject$2(opt)) {
  14713. return;
  14714. }
  14715. // name can be overwitten. Consider case: axis.name = '20km'.
  14716. // But id generated by name will not be changed, which affect
  14717. // only in that case: setOption with 'not merge mode' and view
  14718. // instance will be recreated, which can be accepted.
  14719. keyInfo.name = opt.name != null
  14720. ? opt.name + ''
  14721. : existCpt
  14722. ? existCpt.name
  14723. : '\0-'; // name may be displayed on screen, so use '-'.
  14724. if (existCpt) {
  14725. keyInfo.id = existCpt.id;
  14726. }
  14727. else if (opt.id != null) {
  14728. keyInfo.id = opt.id + '';
  14729. }
  14730. else {
  14731. // Consider this situatoin:
  14732. // optionA: [{name: 'a'}, {name: 'a'}, {..}]
  14733. // optionB [{..}, {name: 'a'}, {name: 'a'}]
  14734. // Series with the same name between optionA and optionB
  14735. // should be mapped.
  14736. var idNum = 0;
  14737. do {
  14738. keyInfo.id = '\0' + keyInfo.name + '\0' + idNum++;
  14739. }
  14740. while (idMap.get(keyInfo.id));
  14741. }
  14742. idMap.set(keyInfo.id, item);
  14743. });
  14744. }
  14745. /**
  14746. * @public
  14747. * @param {Object} cptOption
  14748. * @return {boolean}
  14749. */
  14750. function isIdInner(cptOption) {
  14751. return isObject$2(cptOption)
  14752. && cptOption.id
  14753. && (cptOption.id + '').indexOf('\0_ec_\0') === 0;
  14754. }
  14755. /**
  14756. * A helper for removing duplicate items between batchA and batchB,
  14757. * and in themselves, and categorize by series.
  14758. *
  14759. * @param {Array.<Object>} batchA Like: [{seriesId: 2, dataIndex: [32, 4, 5]}, ...]
  14760. * @param {Array.<Object>} batchB Like: [{seriesId: 2, dataIndex: [32, 4, 5]}, ...]
  14761. * @return {Array.<Array.<Object>, Array.<Object>>} result: [resultBatchA, resultBatchB]
  14762. */
  14763. /**
  14764. * @param {module:echarts/data/List} data
  14765. * @param {Object} payload Contains dataIndex (means rawIndex) / dataIndexInside / name
  14766. * each of which can be Array or primary type.
  14767. * @return {number|Array.<number>} dataIndex If not found, return undefined/null.
  14768. */
  14769. function queryDataIndex(data, payload) {
  14770. if (payload.dataIndexInside != null) {
  14771. return payload.dataIndexInside;
  14772. }
  14773. else if (payload.dataIndex != null) {
  14774. return isArray(payload.dataIndex)
  14775. ? map(payload.dataIndex, function (value) {
  14776. return data.indexOfRawIndex(value);
  14777. })
  14778. : data.indexOfRawIndex(payload.dataIndex);
  14779. }
  14780. else if (payload.name != null) {
  14781. return isArray(payload.name)
  14782. ? map(payload.name, function (value) {
  14783. return data.indexOfName(value);
  14784. })
  14785. : data.indexOfName(payload.name);
  14786. }
  14787. }
  14788. /**
  14789. * Enable property storage to any host object.
  14790. * Notice: Serialization is not supported.
  14791. *
  14792. * For example:
  14793. * var get = modelUitl.makeGetter();
  14794. *
  14795. * function some(hostObj) {
  14796. * get(hostObj)._someProperty = 1212;
  14797. * ...
  14798. * }
  14799. *
  14800. * @return {Function}
  14801. */
  14802. /**
  14803. * @param {module:echarts/model/Global} ecModel
  14804. * @param {string|Object} finder
  14805. * If string, e.g., 'geo', means {geoIndex: 0}.
  14806. * If Object, could contain some of these properties below:
  14807. * {
  14808. * seriesIndex, seriesId, seriesName,
  14809. * geoIndex, geoId, geoName,
  14810. * bmapIndex, bmapId, bmapName,
  14811. * xAxisIndex, xAxisId, xAxisName,
  14812. * yAxisIndex, yAxisId, yAxisName,
  14813. * gridIndex, gridId, gridName,
  14814. * ... (can be extended)
  14815. * }
  14816. * Each properties can be number|string|Array.<number>|Array.<string>
  14817. * For example, a finder could be
  14818. * {
  14819. * seriesIndex: 3,
  14820. * geoId: ['aa', 'cc'],
  14821. * gridName: ['xx', 'rr']
  14822. * }
  14823. * xxxIndex can be set as 'all' (means all xxx) or 'none' (means not specify)
  14824. * If nothing or null/undefined specified, return nothing.
  14825. * @param {Object} [opt]
  14826. * @param {string} [opt.defaultMainType]
  14827. * @param {Array.<string>} [opt.includeMainTypes]
  14828. * @return {Object} result like:
  14829. * {
  14830. * seriesModels: [seriesModel1, seriesModel2],
  14831. * seriesModel: seriesModel1, // The first model
  14832. * geoModels: [geoModel1, geoModel2],
  14833. * geoModel: geoModel1, // The first model
  14834. * ...
  14835. * }
  14836. */
  14837. function parseFinder(ecModel, finder, opt) {
  14838. if (isString(finder)) {
  14839. var obj = {};
  14840. obj[finder + 'Index'] = 0;
  14841. finder = obj;
  14842. }
  14843. var defaultMainType = opt && opt.defaultMainType;
  14844. if (defaultMainType
  14845. && !has(finder, defaultMainType + 'Index')
  14846. && !has(finder, defaultMainType + 'Id')
  14847. && !has(finder, defaultMainType + 'Name')
  14848. ) {
  14849. finder[defaultMainType + 'Index'] = 0;
  14850. }
  14851. var result = {};
  14852. each$3(finder, function (value, key) {
  14853. var value = finder[key];
  14854. // Exclude 'dataIndex' and other illgal keys.
  14855. if (key === 'dataIndex' || key === 'dataIndexInside') {
  14856. result[key] = value;
  14857. return;
  14858. }
  14859. var parsedKey = key.match(/^(\w+)(Index|Id|Name)$/) || [];
  14860. var mainType = parsedKey[1];
  14861. var queryType = (parsedKey[2] || '').toLowerCase();
  14862. if (!mainType
  14863. || !queryType
  14864. || value == null
  14865. || (queryType === 'index' && value === 'none')
  14866. || (opt && opt.includeMainTypes && indexOf(opt.includeMainTypes, mainType) < 0)
  14867. ) {
  14868. return;
  14869. }
  14870. var queryParam = {mainType: mainType};
  14871. if (queryType !== 'index' || value !== 'all') {
  14872. queryParam[queryType] = value;
  14873. }
  14874. var models = ecModel.queryComponents(queryParam);
  14875. result[mainType + 'Models'] = models;
  14876. result[mainType + 'Model'] = models[0];
  14877. });
  14878. return result;
  14879. }
  14880. /**
  14881. * @see {module:echarts/data/helper/completeDimensions}
  14882. * @param {module:echarts/data/List} data
  14883. * @param {string|number} dataDim
  14884. * @return {string}
  14885. */
  14886. function dataDimToCoordDim(data, dataDim) {
  14887. var dimensions = data.dimensions;
  14888. dataDim = data.getDimension(dataDim);
  14889. for (var i = 0; i < dimensions.length; i++) {
  14890. var dimItem = data.getDimensionInfo(dimensions[i]);
  14891. if (dimItem.name === dataDim) {
  14892. return dimItem.coordDim;
  14893. }
  14894. }
  14895. }
  14896. /**
  14897. * @see {module:echarts/data/helper/completeDimensions}
  14898. * @param {module:echarts/data/List} data
  14899. * @param {string} coordDim
  14900. * @return {Array.<string>} data dimensions on the coordDim.
  14901. */
  14902. function coordDimToDataDim(data, coordDim) {
  14903. var dataDim = [];
  14904. each$3(data.dimensions, function (dimName) {
  14905. var dimItem = data.getDimensionInfo(dimName);
  14906. if (dimItem.coordDim === coordDim) {
  14907. dataDim[dimItem.coordDimIndex] = dimItem.name;
  14908. }
  14909. });
  14910. return dataDim;
  14911. }
  14912. /**
  14913. * @see {module:echarts/data/helper/completeDimensions}
  14914. * @param {module:echarts/data/List} data
  14915. * @param {string} otherDim Can be `otherDims`
  14916. * like 'label' or 'tooltip'.
  14917. * @return {Array.<string>} data dimensions on the otherDim.
  14918. */
  14919. function otherDimToDataDim(data, otherDim) {
  14920. var dataDim = [];
  14921. each$3(data.dimensions, function (dimName) {
  14922. var dimItem = data.getDimensionInfo(dimName);
  14923. var otherDims = dimItem.otherDims;
  14924. var dimIndex = otherDims[otherDim];
  14925. if (dimIndex != null && dimIndex !== false) {
  14926. dataDim[dimIndex] = dimItem.name;
  14927. }
  14928. });
  14929. return dataDim;
  14930. }
  14931. function has(obj, prop) {
  14932. return obj && obj.hasOwnProperty(prop);
  14933. }
  14934. var base = 0;
  14935. var DELIMITER = '_';
  14936. /**
  14937. * @public
  14938. * @param {string} type
  14939. * @return {string}
  14940. */
  14941. function getUID(type) {
  14942. // Considering the case of crossing js context,
  14943. // use Math.random to make id as unique as possible.
  14944. return [(type || ''), base++, Math.random()].join(DELIMITER);
  14945. }
  14946. /**
  14947. * @inner
  14948. */
  14949. function enableSubTypeDefaulter(entity) {
  14950. var subTypeDefaulters = {};
  14951. entity.registerSubTypeDefaulter = function (componentType, defaulter) {
  14952. componentType = parseClassType$1(componentType);
  14953. subTypeDefaulters[componentType.main] = defaulter;
  14954. };
  14955. entity.determineSubType = function (componentType, option) {
  14956. var type = option.type;
  14957. if (!type) {
  14958. var componentTypeMain = parseClassType$1(componentType).main;
  14959. if (entity.hasSubTypes(componentType) && subTypeDefaulters[componentTypeMain]) {
  14960. type = subTypeDefaulters[componentTypeMain](option);
  14961. }
  14962. }
  14963. return type;
  14964. };
  14965. return entity;
  14966. }
  14967. /**
  14968. * Topological travel on Activity Network (Activity On Vertices).
  14969. * Dependencies is defined in Model.prototype.dependencies, like ['xAxis', 'yAxis'].
  14970. *
  14971. * If 'xAxis' or 'yAxis' is absent in componentTypeList, just ignore it in topology.
  14972. *
  14973. * If there is circle dependencey, Error will be thrown.
  14974. *
  14975. */
  14976. function enableTopologicalTravel(entity, dependencyGetter) {
  14977. /**
  14978. * @public
  14979. * @param {Array.<string>} targetNameList Target Component type list.
  14980. * Can be ['aa', 'bb', 'aa.xx']
  14981. * @param {Array.<string>} fullNameList By which we can build dependency graph.
  14982. * @param {Function} callback Params: componentType, dependencies.
  14983. * @param {Object} context Scope of callback.
  14984. */
  14985. entity.topologicalTravel = function (targetNameList, fullNameList, callback, context) {
  14986. if (!targetNameList.length) {
  14987. return;
  14988. }
  14989. var result = makeDepndencyGraph(fullNameList);
  14990. var graph = result.graph;
  14991. var stack = result.noEntryList;
  14992. var targetNameSet = {};
  14993. each$1(targetNameList, function (name) {
  14994. targetNameSet[name] = true;
  14995. });
  14996. while (stack.length) {
  14997. var currComponentType = stack.pop();
  14998. var currVertex = graph[currComponentType];
  14999. var isInTargetNameSet = !!targetNameSet[currComponentType];
  15000. if (isInTargetNameSet) {
  15001. callback.call(context, currComponentType, currVertex.originalDeps.slice());
  15002. delete targetNameSet[currComponentType];
  15003. }
  15004. each$1(
  15005. currVertex.successor,
  15006. isInTargetNameSet ? removeEdgeAndAdd : removeEdge
  15007. );
  15008. }
  15009. each$1(targetNameSet, function () {
  15010. throw new Error('Circle dependency may exists');
  15011. });
  15012. function removeEdge(succComponentType) {
  15013. graph[succComponentType].entryCount--;
  15014. if (graph[succComponentType].entryCount === 0) {
  15015. stack.push(succComponentType);
  15016. }
  15017. }
  15018. // Consider this case: legend depends on series, and we call
  15019. // chart.setOption({series: [...]}), where only series is in option.
  15020. // If we do not have 'removeEdgeAndAdd', legendModel.mergeOption will
  15021. // not be called, but only sereis.mergeOption is called. Thus legend
  15022. // have no chance to update its local record about series (like which
  15023. // name of series is available in legend).
  15024. function removeEdgeAndAdd(succComponentType) {
  15025. targetNameSet[succComponentType] = true;
  15026. removeEdge(succComponentType);
  15027. }
  15028. };
  15029. /**
  15030. * DepndencyGraph: {Object}
  15031. * key: conponentType,
  15032. * value: {
  15033. * successor: [conponentTypes...],
  15034. * originalDeps: [conponentTypes...],
  15035. * entryCount: {number}
  15036. * }
  15037. */
  15038. function makeDepndencyGraph(fullNameList) {
  15039. var graph = {};
  15040. var noEntryList = [];
  15041. each$1(fullNameList, function (name) {
  15042. var thisItem = createDependencyGraphItem(graph, name);
  15043. var originalDeps = thisItem.originalDeps = dependencyGetter(name);
  15044. var availableDeps = getAvailableDependencies(originalDeps, fullNameList);
  15045. thisItem.entryCount = availableDeps.length;
  15046. if (thisItem.entryCount === 0) {
  15047. noEntryList.push(name);
  15048. }
  15049. each$1(availableDeps, function (dependentName) {
  15050. if (indexOf(thisItem.predecessor, dependentName) < 0) {
  15051. thisItem.predecessor.push(dependentName);
  15052. }
  15053. var thatItem = createDependencyGraphItem(graph, dependentName);
  15054. if (indexOf(thatItem.successor, dependentName) < 0) {
  15055. thatItem.successor.push(name);
  15056. }
  15057. });
  15058. });
  15059. return {graph: graph, noEntryList: noEntryList};
  15060. }
  15061. function createDependencyGraphItem(graph, name) {
  15062. if (!graph[name]) {
  15063. graph[name] = {predecessor: [], successor: []};
  15064. }
  15065. return graph[name];
  15066. }
  15067. function getAvailableDependencies(originalDeps, fullNameList) {
  15068. var availableDeps = [];
  15069. each$1(originalDeps, function (dep) {
  15070. indexOf(fullNameList, dep) >= 0 && availableDeps.push(dep);
  15071. });
  15072. return availableDeps;
  15073. }
  15074. }
  15075. // Layout helpers for each component positioning
  15076. var each$4 = each$1;
  15077. /**
  15078. * @public
  15079. */
  15080. var LOCATION_PARAMS = [
  15081. 'left', 'right', 'top', 'bottom', 'width', 'height'
  15082. ];
  15083. /**
  15084. * @public
  15085. */
  15086. var HV_NAMES = [
  15087. ['width', 'left', 'right'],
  15088. ['height', 'top', 'bottom']
  15089. ];
  15090. function boxLayout(orient, group, gap, maxWidth, maxHeight) {
  15091. var x = 0;
  15092. var y = 0;
  15093. if (maxWidth == null) {
  15094. maxWidth = Infinity;
  15095. }
  15096. if (maxHeight == null) {
  15097. maxHeight = Infinity;
  15098. }
  15099. var currentLineMaxSize = 0;
  15100. group.eachChild(function (child, idx) {
  15101. var position = child.position;
  15102. var rect = child.getBoundingRect();
  15103. var nextChild = group.childAt(idx + 1);
  15104. var nextChildRect = nextChild && nextChild.getBoundingRect();
  15105. var nextX;
  15106. var nextY;
  15107. if (orient === 'horizontal') {
  15108. var moveX = rect.width + (nextChildRect ? (-nextChildRect.x + rect.x) : 0);
  15109. nextX = x + moveX;
  15110. // Wrap when width exceeds maxWidth or meet a `newline` group
  15111. // FIXME compare before adding gap?
  15112. if (nextX > maxWidth || child.newline) {
  15113. x = 0;
  15114. nextX = moveX;
  15115. y += currentLineMaxSize + gap;
  15116. currentLineMaxSize = rect.height;
  15117. }
  15118. else {
  15119. // FIXME: consider rect.y is not `0`?
  15120. currentLineMaxSize = Math.max(currentLineMaxSize, rect.height);
  15121. }
  15122. }
  15123. else {
  15124. var moveY = rect.height + (nextChildRect ? (-nextChildRect.y + rect.y) : 0);
  15125. nextY = y + moveY;
  15126. // Wrap when width exceeds maxHeight or meet a `newline` group
  15127. if (nextY > maxHeight || child.newline) {
  15128. x += currentLineMaxSize + gap;
  15129. y = 0;
  15130. nextY = moveY;
  15131. currentLineMaxSize = rect.width;
  15132. }
  15133. else {
  15134. currentLineMaxSize = Math.max(currentLineMaxSize, rect.width);
  15135. }
  15136. }
  15137. if (child.newline) {
  15138. return;
  15139. }
  15140. position[0] = x;
  15141. position[1] = y;
  15142. orient === 'horizontal'
  15143. ? (x = nextX + gap)
  15144. : (y = nextY + gap);
  15145. });
  15146. }
  15147. /**
  15148. * VBox or HBox layouting
  15149. * @param {string} orient
  15150. * @param {module:zrender/container/Group} group
  15151. * @param {number} gap
  15152. * @param {number} [width=Infinity]
  15153. * @param {number} [height=Infinity]
  15154. */
  15155. /**
  15156. * VBox layouting
  15157. * @param {module:zrender/container/Group} group
  15158. * @param {number} gap
  15159. * @param {number} [width=Infinity]
  15160. * @param {number} [height=Infinity]
  15161. */
  15162. var vbox = curry(boxLayout, 'vertical');
  15163. /**
  15164. * HBox layouting
  15165. * @param {module:zrender/container/Group} group
  15166. * @param {number} gap
  15167. * @param {number} [width=Infinity]
  15168. * @param {number} [height=Infinity]
  15169. */
  15170. var hbox = curry(boxLayout, 'horizontal');
  15171. /**
  15172. * If x or x2 is not specified or 'center' 'left' 'right',
  15173. * the width would be as long as possible.
  15174. * If y or y2 is not specified or 'middle' 'top' 'bottom',
  15175. * the height would be as long as possible.
  15176. *
  15177. * @param {Object} positionInfo
  15178. * @param {number|string} [positionInfo.x]
  15179. * @param {number|string} [positionInfo.y]
  15180. * @param {number|string} [positionInfo.x2]
  15181. * @param {number|string} [positionInfo.y2]
  15182. * @param {Object} containerRect {width, height}
  15183. * @param {string|number} margin
  15184. * @return {Object} {width, height}
  15185. */
  15186. /**
  15187. * Parse position info.
  15188. *
  15189. * @param {Object} positionInfo
  15190. * @param {number|string} [positionInfo.left]
  15191. * @param {number|string} [positionInfo.top]
  15192. * @param {number|string} [positionInfo.right]
  15193. * @param {number|string} [positionInfo.bottom]
  15194. * @param {number|string} [positionInfo.width]
  15195. * @param {number|string} [positionInfo.height]
  15196. * @param {number|string} [positionInfo.aspect] Aspect is width / height
  15197. * @param {Object} containerRect
  15198. * @param {string|number} [margin]
  15199. *
  15200. * @return {module:zrender/core/BoundingRect}
  15201. */
  15202. function getLayoutRect(
  15203. positionInfo, containerRect, margin
  15204. ) {
  15205. margin = normalizeCssArray$1(margin || 0);
  15206. var containerWidth = containerRect.width;
  15207. var containerHeight = containerRect.height;
  15208. var left = parsePercent$1(positionInfo.left, containerWidth);
  15209. var top = parsePercent$1(positionInfo.top, containerHeight);
  15210. var right = parsePercent$1(positionInfo.right, containerWidth);
  15211. var bottom = parsePercent$1(positionInfo.bottom, containerHeight);
  15212. var width = parsePercent$1(positionInfo.width, containerWidth);
  15213. var height = parsePercent$1(positionInfo.height, containerHeight);
  15214. var verticalMargin = margin[2] + margin[0];
  15215. var horizontalMargin = margin[1] + margin[3];
  15216. var aspect = positionInfo.aspect;
  15217. // If width is not specified, calculate width from left and right
  15218. if (isNaN(width)) {
  15219. width = containerWidth - right - horizontalMargin - left;
  15220. }
  15221. if (isNaN(height)) {
  15222. height = containerHeight - bottom - verticalMargin - top;
  15223. }
  15224. if (aspect != null) {
  15225. // If width and height are not given
  15226. // 1. Graph should not exceeds the container
  15227. // 2. Aspect must be keeped
  15228. // 3. Graph should take the space as more as possible
  15229. // FIXME
  15230. // Margin is not considered, because there is no case that both
  15231. // using margin and aspect so far.
  15232. if (isNaN(width) && isNaN(height)) {
  15233. if (aspect > containerWidth / containerHeight) {
  15234. width = containerWidth * 0.8;
  15235. }
  15236. else {
  15237. height = containerHeight * 0.8;
  15238. }
  15239. }
  15240. // Calculate width or height with given aspect
  15241. if (isNaN(width)) {
  15242. width = aspect * height;
  15243. }
  15244. if (isNaN(height)) {
  15245. height = width / aspect;
  15246. }
  15247. }
  15248. // If left is not specified, calculate left from right and width
  15249. if (isNaN(left)) {
  15250. left = containerWidth - right - width - horizontalMargin;
  15251. }
  15252. if (isNaN(top)) {
  15253. top = containerHeight - bottom - height - verticalMargin;
  15254. }
  15255. // Align left and top
  15256. switch (positionInfo.left || positionInfo.right) {
  15257. case 'center':
  15258. left = containerWidth / 2 - width / 2 - margin[3];
  15259. break;
  15260. case 'right':
  15261. left = containerWidth - width - horizontalMargin;
  15262. break;
  15263. }
  15264. switch (positionInfo.top || positionInfo.bottom) {
  15265. case 'middle':
  15266. case 'center':
  15267. top = containerHeight / 2 - height / 2 - margin[0];
  15268. break;
  15269. case 'bottom':
  15270. top = containerHeight - height - verticalMargin;
  15271. break;
  15272. }
  15273. // If something is wrong and left, top, width, height are calculated as NaN
  15274. left = left || 0;
  15275. top = top || 0;
  15276. if (isNaN(width)) {
  15277. // Width may be NaN if only one value is given except width
  15278. width = containerWidth - horizontalMargin - left - (right || 0);
  15279. }
  15280. if (isNaN(height)) {
  15281. // Height may be NaN if only one value is given except height
  15282. height = containerHeight - verticalMargin - top - (bottom || 0);
  15283. }
  15284. var rect = new BoundingRect(left + margin[3], top + margin[0], width, height);
  15285. rect.margin = margin;
  15286. return rect;
  15287. }
  15288. /**
  15289. * Position a zr element in viewport
  15290. * Group position is specified by either
  15291. * {left, top}, {right, bottom}
  15292. * If all properties exists, right and bottom will be igonred.
  15293. *
  15294. * Logic:
  15295. * 1. Scale (against origin point in parent coord)
  15296. * 2. Rotate (against origin point in parent coord)
  15297. * 3. Traslate (with el.position by this method)
  15298. * So this method only fixes the last step 'Traslate', which does not affect
  15299. * scaling and rotating.
  15300. *
  15301. * If be called repeatly with the same input el, the same result will be gotten.
  15302. *
  15303. * @param {module:zrender/Element} el Should have `getBoundingRect` method.
  15304. * @param {Object} positionInfo
  15305. * @param {number|string} [positionInfo.left]
  15306. * @param {number|string} [positionInfo.top]
  15307. * @param {number|string} [positionInfo.right]
  15308. * @param {number|string} [positionInfo.bottom]
  15309. * @param {number|string} [positionInfo.width] Only for opt.boundingModel: 'raw'
  15310. * @param {number|string} [positionInfo.height] Only for opt.boundingModel: 'raw'
  15311. * @param {Object} containerRect
  15312. * @param {string|number} margin
  15313. * @param {Object} [opt]
  15314. * @param {Array.<number>} [opt.hv=[1,1]] Only horizontal or only vertical.
  15315. * @param {Array.<number>} [opt.boundingMode='all']
  15316. * Specify how to calculate boundingRect when locating.
  15317. * 'all': Position the boundingRect that is transformed and uioned
  15318. * both itself and its descendants.
  15319. * This mode simplies confine the elements in the bounding
  15320. * of their container (e.g., using 'right: 0').
  15321. * 'raw': Position the boundingRect that is not transformed and only itself.
  15322. * This mode is useful when you want a element can overflow its
  15323. * container. (Consider a rotated circle needs to be located in a corner.)
  15324. * In this mode positionInfo.width/height can only be number.
  15325. */
  15326. /**
  15327. * @param {Object} option Contains some of the properties in HV_NAMES.
  15328. * @param {number} hvIdx 0: horizontal; 1: vertical.
  15329. */
  15330. /**
  15331. * Consider Case:
  15332. * When defulat option has {left: 0, width: 100}, and we set {right: 0}
  15333. * through setOption or media query, using normal zrUtil.merge will cause
  15334. * {right: 0} does not take effect.
  15335. *
  15336. * @example
  15337. * ComponentModel.extend({
  15338. * init: function () {
  15339. * ...
  15340. * var inputPositionParams = layout.getLayoutParams(option);
  15341. * this.mergeOption(inputPositionParams);
  15342. * },
  15343. * mergeOption: function (newOption) {
  15344. * newOption && zrUtil.merge(thisOption, newOption, true);
  15345. * layout.mergeLayoutParam(thisOption, newOption);
  15346. * }
  15347. * });
  15348. *
  15349. * @param {Object} targetOption
  15350. * @param {Object} newOption
  15351. * @param {Object|string} [opt]
  15352. * @param {boolean|Array.<boolean>} [opt.ignoreSize=false] Used for the components
  15353. * that width (or height) should not be calculated by left and right (or top and bottom).
  15354. */
  15355. function mergeLayoutParam(targetOption, newOption, opt) {
  15356. !isObject(opt) && (opt = {});
  15357. var ignoreSize = opt.ignoreSize;
  15358. !isArray(ignoreSize) && (ignoreSize = [ignoreSize, ignoreSize]);
  15359. var hResult = merge$$1(HV_NAMES[0], 0);
  15360. var vResult = merge$$1(HV_NAMES[1], 1);
  15361. copy(HV_NAMES[0], targetOption, hResult);
  15362. copy(HV_NAMES[1], targetOption, vResult);
  15363. function merge$$1(names, hvIdx) {
  15364. var newParams = {};
  15365. var newValueCount = 0;
  15366. var merged = {};
  15367. var mergedValueCount = 0;
  15368. var enoughParamNumber = 2;
  15369. each$4(names, function (name) {
  15370. merged[name] = targetOption[name];
  15371. });
  15372. each$4(names, function (name) {
  15373. // Consider case: newOption.width is null, which is
  15374. // set by user for removing width setting.
  15375. hasProp(newOption, name) && (newParams[name] = merged[name] = newOption[name]);
  15376. hasValue(newParams, name) && newValueCount++;
  15377. hasValue(merged, name) && mergedValueCount++;
  15378. });
  15379. if (ignoreSize[hvIdx]) {
  15380. // Only one of left/right is premitted to exist.
  15381. if (hasValue(newOption, names[1])) {
  15382. merged[names[2]] = null;
  15383. }
  15384. else if (hasValue(newOption, names[2])) {
  15385. merged[names[1]] = null;
  15386. }
  15387. return merged;
  15388. }
  15389. // Case: newOption: {width: ..., right: ...},
  15390. // or targetOption: {right: ...} and newOption: {width: ...},
  15391. // There is no conflict when merged only has params count
  15392. // little than enoughParamNumber.
  15393. if (mergedValueCount === enoughParamNumber || !newValueCount) {
  15394. return merged;
  15395. }
  15396. // Case: newOption: {width: ..., right: ...},
  15397. // Than we can make sure user only want those two, and ignore
  15398. // all origin params in targetOption.
  15399. else if (newValueCount >= enoughParamNumber) {
  15400. return newParams;
  15401. }
  15402. else {
  15403. // Chose another param from targetOption by priority.
  15404. for (var i = 0; i < names.length; i++) {
  15405. var name = names[i];
  15406. if (!hasProp(newParams, name) && hasProp(targetOption, name)) {
  15407. newParams[name] = targetOption[name];
  15408. break;
  15409. }
  15410. }
  15411. return newParams;
  15412. }
  15413. }
  15414. function hasProp(obj, name) {
  15415. return obj.hasOwnProperty(name);
  15416. }
  15417. function hasValue(obj, name) {
  15418. return obj[name] != null && obj[name] !== 'auto';
  15419. }
  15420. function copy(names, target, source) {
  15421. each$4(names, function (name) {
  15422. target[name] = source[name];
  15423. });
  15424. }
  15425. }
  15426. /**
  15427. * Retrieve 'left', 'right', 'top', 'bottom', 'width', 'height' from object.
  15428. * @param {Object} source
  15429. * @return {Object} Result contains those props.
  15430. */
  15431. function getLayoutParams(source) {
  15432. return copyLayoutParams({}, source);
  15433. }
  15434. /**
  15435. * Retrieve 'left', 'right', 'top', 'bottom', 'width', 'height' from object.
  15436. * @param {Object} source
  15437. * @return {Object} Result contains those props.
  15438. */
  15439. function copyLayoutParams(target, source) {
  15440. source && target && each$4(LOCATION_PARAMS, function (name) {
  15441. source.hasOwnProperty(name) && (target[name] = source[name]);
  15442. });
  15443. return target;
  15444. }
  15445. var boxLayoutMixin = {
  15446. getBoxLayoutParams: function () {
  15447. return {
  15448. left: this.get('left'),
  15449. top: this.get('top'),
  15450. right: this.get('right'),
  15451. bottom: this.get('bottom'),
  15452. width: this.get('width'),
  15453. height: this.get('height')
  15454. };
  15455. }
  15456. };
  15457. /**
  15458. * Component model
  15459. *
  15460. * @module echarts/model/Component
  15461. */
  15462. var arrayPush = Array.prototype.push;
  15463. /**
  15464. * @alias module:echarts/model/Component
  15465. * @constructor
  15466. * @param {Object} option
  15467. * @param {module:echarts/model/Model} parentModel
  15468. * @param {module:echarts/model/Model} ecModel
  15469. */
  15470. var ComponentModel = Model.extend({
  15471. type: 'component',
  15472. /**
  15473. * @readOnly
  15474. * @type {string}
  15475. */
  15476. id: '',
  15477. /**
  15478. * @readOnly
  15479. */
  15480. name: '',
  15481. /**
  15482. * @readOnly
  15483. * @type {string}
  15484. */
  15485. mainType: '',
  15486. /**
  15487. * @readOnly
  15488. * @type {string}
  15489. */
  15490. subType: '',
  15491. /**
  15492. * @readOnly
  15493. * @type {number}
  15494. */
  15495. componentIndex: 0,
  15496. /**
  15497. * @type {Object}
  15498. * @protected
  15499. */
  15500. defaultOption: null,
  15501. /**
  15502. * @type {module:echarts/model/Global}
  15503. * @readOnly
  15504. */
  15505. ecModel: null,
  15506. /**
  15507. * key: componentType
  15508. * value: Component model list, can not be null.
  15509. * @type {Object.<string, Array.<module:echarts/model/Model>>}
  15510. * @readOnly
  15511. */
  15512. dependentModels: [],
  15513. /**
  15514. * @type {string}
  15515. * @readOnly
  15516. */
  15517. uid: null,
  15518. /**
  15519. * Support merge layout params.
  15520. * Only support 'box' now (left/right/top/bottom/width/height).
  15521. * @type {string|Object} Object can be {ignoreSize: true}
  15522. * @readOnly
  15523. */
  15524. layoutMode: null,
  15525. $constructor: function (option, parentModel, ecModel, extraOpt) {
  15526. Model.call(this, option, parentModel, ecModel, extraOpt);
  15527. this.uid = getUID('componentModel');
  15528. },
  15529. init: function (option, parentModel, ecModel, extraOpt) {
  15530. this.mergeDefaultAndTheme(option, ecModel);
  15531. },
  15532. mergeDefaultAndTheme: function (option, ecModel) {
  15533. var layoutMode = this.layoutMode;
  15534. var inputPositionParams = layoutMode
  15535. ? getLayoutParams(option) : {};
  15536. var themeModel = ecModel.getTheme();
  15537. merge(option, themeModel.get(this.mainType));
  15538. merge(option, this.getDefaultOption());
  15539. if (layoutMode) {
  15540. mergeLayoutParam(option, inputPositionParams, layoutMode);
  15541. }
  15542. },
  15543. mergeOption: function (option, extraOpt) {
  15544. merge(this.option, option, true);
  15545. var layoutMode = this.layoutMode;
  15546. if (layoutMode) {
  15547. mergeLayoutParam(this.option, option, layoutMode);
  15548. }
  15549. },
  15550. // Hooker after init or mergeOption
  15551. optionUpdated: function (newCptOption, isInit) {},
  15552. getDefaultOption: function () {
  15553. if (!hasOwn(this, '__defaultOption')) {
  15554. var optList = [];
  15555. var Class = this.constructor;
  15556. while (Class) {
  15557. var opt = Class.prototype.defaultOption;
  15558. opt && optList.push(opt);
  15559. Class = Class.superClass;
  15560. }
  15561. var defaultOption = {};
  15562. for (var i = optList.length - 1; i >= 0; i--) {
  15563. defaultOption = merge(defaultOption, optList[i], true);
  15564. }
  15565. set$1(this, '__defaultOption', defaultOption);
  15566. }
  15567. return get(this, '__defaultOption');
  15568. },
  15569. getReferringComponents: function (mainType) {
  15570. return this.ecModel.queryComponents({
  15571. mainType: mainType,
  15572. index: this.get(mainType + 'Index', true),
  15573. id: this.get(mainType + 'Id', true)
  15574. });
  15575. }
  15576. });
  15577. // Reset ComponentModel.extend, add preConstruct.
  15578. // clazzUtil.enableClassExtend(
  15579. // ComponentModel,
  15580. // function (option, parentModel, ecModel, extraOpt) {
  15581. // // Set dependentModels, componentIndex, name, id, mainType, subType.
  15582. // zrUtil.extend(this, extraOpt);
  15583. // this.uid = componentUtil.getUID('componentModel');
  15584. // // this.setReadOnly([
  15585. // // 'type', 'id', 'uid', 'name', 'mainType', 'subType',
  15586. // // 'dependentModels', 'componentIndex'
  15587. // // ]);
  15588. // }
  15589. // );
  15590. // Add capability of registerClass, getClass, hasClass, registerSubTypeDefaulter and so on.
  15591. enableClassManagement(
  15592. ComponentModel, {registerWhenExtend: true}
  15593. );
  15594. enableSubTypeDefaulter(ComponentModel);
  15595. // Add capability of ComponentModel.topologicalTravel.
  15596. enableTopologicalTravel(ComponentModel, getDependencies);
  15597. function getDependencies(componentType) {
  15598. var deps = [];
  15599. each$1(ComponentModel.getClassesByMainType(componentType), function (Clazz) {
  15600. arrayPush.apply(deps, Clazz.prototype.dependencies || []);
  15601. });
  15602. // Ensure main type
  15603. return map(deps, function (type) {
  15604. return parseClassType$1(type).main;
  15605. });
  15606. }
  15607. mixin(ComponentModel, boxLayoutMixin);
  15608. var platform = '';
  15609. // Navigator not exists in node
  15610. if (typeof navigator !== 'undefined') {
  15611. platform = navigator.platform || '';
  15612. }
  15613. var globalDefault = {
  15614. // 全图默认背景
  15615. // backgroundColor: 'rgba(0,0,0,0)',
  15616. // https://dribbble.com/shots/1065960-Infographic-Pie-chart-visualization
  15617. // color: ['#5793f3', '#d14a61', '#fd9c35', '#675bba', '#fec42c', '#dd4444', '#d4df5a', '#cd4870'],
  15618. // 浅色
  15619. // color: ['#bcd3bb', '#e88f70', '#edc1a5', '#9dc5c8', '#e1e8c8', '#7b7c68', '#e5b5b5', '#f0b489', '#928ea8', '#bda29a'],
  15620. // color: ['#cc5664', '#9bd6ec', '#ea946e', '#8acaaa', '#f1ec64', '#ee8686', '#a48dc1', '#5da6bc', '#b9dcae'],
  15621. // 深色
  15622. color: ['#c23531','#2f4554', '#61a0a8', '#d48265', '#91c7ae','#749f83', '#ca8622', '#bda29a','#6e7074', '#546570', '#c4ccd3'],
  15623. // 默认需要 Grid 配置项
  15624. // grid: {},
  15625. // 主题,主题
  15626. textStyle: {
  15627. // color: '#000',
  15628. // decoration: 'none',
  15629. // PENDING
  15630. fontFamily: platform.match(/^Win/) ? 'Microsoft YaHei' : 'sans-serif',
  15631. // fontFamily: 'Arial, Verdana, sans-serif',
  15632. fontSize: 12,
  15633. fontStyle: 'normal',
  15634. fontWeight: 'normal'
  15635. },
  15636. // http://blogs.adobe.com/webplatform/2014/02/24/using-blend-modes-in-html-canvas/
  15637. // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation
  15638. // Default is source-over
  15639. blendMode: null,
  15640. animation: 'auto',
  15641. animationDuration: 1000,
  15642. animationDurationUpdate: 300,
  15643. animationEasing: 'exponentialOut',
  15644. animationEasingUpdate: 'cubicOut',
  15645. animationThreshold: 2000,
  15646. // Configuration for progressive/incremental rendering
  15647. progressiveThreshold: 3000,
  15648. progressive: 400,
  15649. // Threshold of if use single hover layer to optimize.
  15650. // It is recommended that `hoverLayerThreshold` is equivalent to or less than
  15651. // `progressiveThreshold`, otherwise hover will cause restart of progressive,
  15652. // which is unexpected.
  15653. // see example <echarts/test/heatmap-large.html>.
  15654. hoverLayerThreshold: 3000,
  15655. // See: module:echarts/scale/Time
  15656. useUTC: false
  15657. };
  15658. var colorPaletteMixin = {
  15659. clearColorPalette: function () {
  15660. set$1(this, 'colorIdx', 0);
  15661. set$1(this, 'colorNameMap', {});
  15662. },
  15663. getColorFromPalette: function (name, scope) {
  15664. scope = scope || this;
  15665. var colorIdx = get(scope, 'colorIdx') || 0;
  15666. var colorNameMap = get(scope, 'colorNameMap') || set$1(scope, 'colorNameMap', {});
  15667. // Use `hasOwnProperty` to avoid conflict with Object.prototype.
  15668. if (colorNameMap.hasOwnProperty(name)) {
  15669. return colorNameMap[name];
  15670. }
  15671. var colorPalette = this.get('color', true) || [];
  15672. if (!colorPalette.length) {
  15673. return;
  15674. }
  15675. var color = colorPalette[colorIdx];
  15676. if (name) {
  15677. colorNameMap[name] = color;
  15678. }
  15679. set$1(scope, 'colorIdx', (colorIdx + 1) % colorPalette.length);
  15680. return color;
  15681. }
  15682. };
  15683. /**
  15684. * ECharts global model
  15685. *
  15686. * @module {echarts/model/Global}
  15687. */
  15688. /**
  15689. * Caution: If the mechanism should be changed some day, these cases
  15690. * should be considered:
  15691. *
  15692. * (1) In `merge option` mode, if using the same option to call `setOption`
  15693. * many times, the result should be the same (try our best to ensure that).
  15694. * (2) In `merge option` mode, if a component has no id/name specified, it
  15695. * will be merged by index, and the result sequence of the components is
  15696. * consistent to the original sequence.
  15697. * (3) `reset` feature (in toolbox). Find detailed info in comments about
  15698. * `mergeOption` in module:echarts/model/OptionManager.
  15699. */
  15700. var each$2 = each$1;
  15701. var filter$1 = filter;
  15702. var map$1 = map;
  15703. var isArray$1 = isArray;
  15704. var indexOf$1 = indexOf;
  15705. var isObject$1 = isObject;
  15706. var OPTION_INNER_KEY = '\0_ec_inner';
  15707. /**
  15708. * @alias module:echarts/model/Global
  15709. *
  15710. * @param {Object} option
  15711. * @param {module:echarts/model/Model} parentModel
  15712. * @param {Object} theme
  15713. */
  15714. var GlobalModel = Model.extend({
  15715. constructor: GlobalModel,
  15716. init: function (option, parentModel, theme, optionManager) {
  15717. theme = theme || {};
  15718. this.option = null; // Mark as not initialized.
  15719. /**
  15720. * @type {module:echarts/model/Model}
  15721. * @private
  15722. */
  15723. this._theme = new Model(theme);
  15724. /**
  15725. * @type {module:echarts/model/OptionManager}
  15726. */
  15727. this._optionManager = optionManager;
  15728. },
  15729. setOption: function (option, optionPreprocessorFuncs) {
  15730. assert(
  15731. !(OPTION_INNER_KEY in option),
  15732. 'please use chart.getOption()'
  15733. );
  15734. this._optionManager.setOption(option, optionPreprocessorFuncs);
  15735. this.resetOption(null);
  15736. },
  15737. /**
  15738. * @param {string} type null/undefined: reset all.
  15739. * 'recreate': force recreate all.
  15740. * 'timeline': only reset timeline option
  15741. * 'media': only reset media query option
  15742. * @return {boolean} Whether option changed.
  15743. */
  15744. resetOption: function (type) {
  15745. var optionChanged = false;
  15746. var optionManager = this._optionManager;
  15747. if (!type || type === 'recreate') {
  15748. var baseOption = optionManager.mountOption(type === 'recreate');
  15749. if (!this.option || type === 'recreate') {
  15750. initBase.call(this, baseOption);
  15751. }
  15752. else {
  15753. this.restoreData();
  15754. this.mergeOption(baseOption);
  15755. }
  15756. optionChanged = true;
  15757. }
  15758. if (type === 'timeline' || type === 'media') {
  15759. this.restoreData();
  15760. }
  15761. if (!type || type === 'recreate' || type === 'timeline') {
  15762. var timelineOption = optionManager.getTimelineOption(this);
  15763. timelineOption && (this.mergeOption(timelineOption), optionChanged = true);
  15764. }
  15765. if (!type || type === 'recreate' || type === 'media') {
  15766. var mediaOptions = optionManager.getMediaOption(this, this._api);
  15767. if (mediaOptions.length) {
  15768. each$2(mediaOptions, function (mediaOption) {
  15769. this.mergeOption(mediaOption, optionChanged = true);
  15770. }, this);
  15771. }
  15772. }
  15773. return optionChanged;
  15774. },
  15775. /**
  15776. * @protected
  15777. */
  15778. mergeOption: function (newOption) {
  15779. var option = this.option;
  15780. var componentsMap = this._componentsMap;
  15781. var newCptTypes = [];
  15782. // 如果不存在对应的 component model 则直接 merge
  15783. each$2(newOption, function (componentOption, mainType) {
  15784. if (componentOption == null) {
  15785. return;
  15786. }
  15787. if (!ComponentModel.hasClass(mainType)) {
  15788. option[mainType] = option[mainType] == null
  15789. ? clone(componentOption)
  15790. : merge(option[mainType], componentOption, true);
  15791. }
  15792. else {
  15793. newCptTypes.push(mainType);
  15794. }
  15795. });
  15796. // FIXME OPTION 同步是否要改回原来的
  15797. ComponentModel.topologicalTravel(
  15798. newCptTypes, ComponentModel.getAllClassMainTypes(), visitComponent, this
  15799. );
  15800. this._seriesIndices = this._seriesIndices || [];
  15801. function visitComponent(mainType, dependencies) {
  15802. var newCptOptionList = normalizeToArray(newOption[mainType]);
  15803. var mapResult = mappingToExists(
  15804. componentsMap.get(mainType), newCptOptionList
  15805. );
  15806. makeIdAndName(mapResult);
  15807. // Set mainType and complete subType.
  15808. each$2(mapResult, function (item, index) {
  15809. var opt = item.option;
  15810. if (isObject$1(opt)) {
  15811. item.keyInfo.mainType = mainType;
  15812. item.keyInfo.subType = determineSubType(mainType, opt, item.exist);
  15813. }
  15814. });
  15815. var dependentModels = getComponentsByTypes(
  15816. componentsMap, dependencies
  15817. );
  15818. option[mainType] = [];
  15819. componentsMap.set(mainType, []);
  15820. each$2(mapResult, function (resultItem, index) {
  15821. var componentModel = resultItem.exist;
  15822. var newCptOption = resultItem.option;
  15823. assert(
  15824. isObject$1(newCptOption) || componentModel,
  15825. 'Empty component definition'
  15826. );
  15827. // Consider where is no new option and should be merged using {},
  15828. // see removeEdgeAndAdd in topologicalTravel and
  15829. // ComponentModel.getAllClassMainTypes.
  15830. if (!newCptOption) {
  15831. componentModel.mergeOption({}, this);
  15832. componentModel.optionUpdated({}, false);
  15833. }
  15834. else {
  15835. var ComponentModelClass = ComponentModel.getClass(
  15836. mainType, resultItem.keyInfo.subType, true
  15837. );
  15838. if (componentModel && componentModel instanceof ComponentModelClass) {
  15839. componentModel.name = resultItem.keyInfo.name;
  15840. componentModel.mergeOption(newCptOption, this);
  15841. componentModel.optionUpdated(newCptOption, false);
  15842. }
  15843. else {
  15844. // PENDING Global as parent ?
  15845. var extraOpt = extend(
  15846. {
  15847. dependentModels: dependentModels,
  15848. componentIndex: index
  15849. },
  15850. resultItem.keyInfo
  15851. );
  15852. componentModel = new ComponentModelClass(
  15853. newCptOption, this, this, extraOpt
  15854. );
  15855. extend(componentModel, extraOpt);
  15856. componentModel.init(newCptOption, this, this, extraOpt);
  15857. // Call optionUpdated after init.
  15858. // newCptOption has been used as componentModel.option
  15859. // and may be merged with theme and default, so pass null
  15860. // to avoid confusion.
  15861. componentModel.optionUpdated(null, true);
  15862. }
  15863. }
  15864. componentsMap.get(mainType)[index] = componentModel;
  15865. option[mainType][index] = componentModel.option;
  15866. }, this);
  15867. // Backup series for filtering.
  15868. if (mainType === 'series') {
  15869. this._seriesIndices = createSeriesIndices(componentsMap.get('series'));
  15870. }
  15871. }
  15872. },
  15873. /**
  15874. * Get option for output (cloned option and inner info removed)
  15875. * @public
  15876. * @return {Object}
  15877. */
  15878. getOption: function () {
  15879. var option = clone(this.option);
  15880. each$2(option, function (opts, mainType) {
  15881. if (ComponentModel.hasClass(mainType)) {
  15882. var opts = normalizeToArray(opts);
  15883. for (var i = opts.length - 1; i >= 0; i--) {
  15884. // Remove options with inner id.
  15885. if (isIdInner(opts[i])) {
  15886. opts.splice(i, 1);
  15887. }
  15888. }
  15889. option[mainType] = opts;
  15890. }
  15891. });
  15892. delete option[OPTION_INNER_KEY];
  15893. return option;
  15894. },
  15895. /**
  15896. * @return {module:echarts/model/Model}
  15897. */
  15898. getTheme: function () {
  15899. return this._theme;
  15900. },
  15901. /**
  15902. * @param {string} mainType
  15903. * @param {number} [idx=0]
  15904. * @return {module:echarts/model/Component}
  15905. */
  15906. getComponent: function (mainType, idx) {
  15907. var list = this._componentsMap.get(mainType);
  15908. if (list) {
  15909. return list[idx || 0];
  15910. }
  15911. },
  15912. /**
  15913. * If none of index and id and name used, return all components with mainType.
  15914. * @param {Object} condition
  15915. * @param {string} condition.mainType
  15916. * @param {string} [condition.subType] If ignore, only query by mainType
  15917. * @param {number|Array.<number>} [condition.index] Either input index or id or name.
  15918. * @param {string|Array.<string>} [condition.id] Either input index or id or name.
  15919. * @param {string|Array.<string>} [condition.name] Either input index or id or name.
  15920. * @return {Array.<module:echarts/model/Component>}
  15921. */
  15922. queryComponents: function (condition) {
  15923. var mainType = condition.mainType;
  15924. if (!mainType) {
  15925. return [];
  15926. }
  15927. var index = condition.index;
  15928. var id = condition.id;
  15929. var name = condition.name;
  15930. var cpts = this._componentsMap.get(mainType);
  15931. if (!cpts || !cpts.length) {
  15932. return [];
  15933. }
  15934. var result;
  15935. if (index != null) {
  15936. if (!isArray$1(index)) {
  15937. index = [index];
  15938. }
  15939. result = filter$1(map$1(index, function (idx) {
  15940. return cpts[idx];
  15941. }), function (val) {
  15942. return !!val;
  15943. });
  15944. }
  15945. else if (id != null) {
  15946. var isIdArray = isArray$1(id);
  15947. result = filter$1(cpts, function (cpt) {
  15948. return (isIdArray && indexOf$1(id, cpt.id) >= 0)
  15949. || (!isIdArray && cpt.id === id);
  15950. });
  15951. }
  15952. else if (name != null) {
  15953. var isNameArray = isArray$1(name);
  15954. result = filter$1(cpts, function (cpt) {
  15955. return (isNameArray && indexOf$1(name, cpt.name) >= 0)
  15956. || (!isNameArray && cpt.name === name);
  15957. });
  15958. }
  15959. else {
  15960. // Return all components with mainType
  15961. result = cpts.slice();
  15962. }
  15963. return filterBySubType(result, condition);
  15964. },
  15965. /**
  15966. * The interface is different from queryComponents,
  15967. * which is convenient for inner usage.
  15968. *
  15969. * @usage
  15970. * var result = findComponents(
  15971. * {mainType: 'dataZoom', query: {dataZoomId: 'abc'}}
  15972. * );
  15973. * var result = findComponents(
  15974. * {mainType: 'series', subType: 'pie', query: {seriesName: 'uio'}}
  15975. * );
  15976. * var result = findComponents(
  15977. * {mainType: 'series'},
  15978. * function (model, index) {...}
  15979. * );
  15980. * // result like [component0, componnet1, ...]
  15981. *
  15982. * @param {Object} condition
  15983. * @param {string} condition.mainType Mandatory.
  15984. * @param {string} [condition.subType] Optional.
  15985. * @param {Object} [condition.query] like {xxxIndex, xxxId, xxxName},
  15986. * where xxx is mainType.
  15987. * If query attribute is null/undefined or has no index/id/name,
  15988. * do not filtering by query conditions, which is convenient for
  15989. * no-payload situations or when target of action is global.
  15990. * @param {Function} [condition.filter] parameter: component, return boolean.
  15991. * @return {Array.<module:echarts/model/Component>}
  15992. */
  15993. findComponents: function (condition) {
  15994. var query = condition.query;
  15995. var mainType = condition.mainType;
  15996. var queryCond = getQueryCond(query);
  15997. var result = queryCond
  15998. ? this.queryComponents(queryCond)
  15999. : this._componentsMap.get(mainType);
  16000. return doFilter(filterBySubType(result, condition));
  16001. function getQueryCond(q) {
  16002. var indexAttr = mainType + 'Index';
  16003. var idAttr = mainType + 'Id';
  16004. var nameAttr = mainType + 'Name';
  16005. return q && (
  16006. q[indexAttr] != null
  16007. || q[idAttr] != null
  16008. || q[nameAttr] != null
  16009. )
  16010. ? {
  16011. mainType: mainType,
  16012. // subType will be filtered finally.
  16013. index: q[indexAttr],
  16014. id: q[idAttr],
  16015. name: q[nameAttr]
  16016. }
  16017. : null;
  16018. }
  16019. function doFilter(res) {
  16020. return condition.filter
  16021. ? filter$1(res, condition.filter)
  16022. : res;
  16023. }
  16024. },
  16025. /**
  16026. * @usage
  16027. * eachComponent('legend', function (legendModel, index) {
  16028. * ...
  16029. * });
  16030. * eachComponent(function (componentType, model, index) {
  16031. * // componentType does not include subType
  16032. * // (componentType is 'xxx' but not 'xxx.aa')
  16033. * });
  16034. * eachComponent(
  16035. * {mainType: 'dataZoom', query: {dataZoomId: 'abc'}},
  16036. * function (model, index) {...}
  16037. * );
  16038. * eachComponent(
  16039. * {mainType: 'series', subType: 'pie', query: {seriesName: 'uio'}},
  16040. * function (model, index) {...}
  16041. * );
  16042. *
  16043. * @param {string|Object=} mainType When mainType is object, the definition
  16044. * is the same as the method 'findComponents'.
  16045. * @param {Function} cb
  16046. * @param {*} context
  16047. */
  16048. eachComponent: function (mainType, cb, context) {
  16049. var componentsMap = this._componentsMap;
  16050. if (typeof mainType === 'function') {
  16051. context = cb;
  16052. cb = mainType;
  16053. componentsMap.each(function (components, componentType) {
  16054. each$2(components, function (component, index) {
  16055. cb.call(context, componentType, component, index);
  16056. });
  16057. });
  16058. }
  16059. else if (isString(mainType)) {
  16060. each$2(componentsMap.get(mainType), cb, context);
  16061. }
  16062. else if (isObject$1(mainType)) {
  16063. var queryResult = this.findComponents(mainType);
  16064. each$2(queryResult, cb, context);
  16065. }
  16066. },
  16067. /**
  16068. * @param {string} name
  16069. * @return {Array.<module:echarts/model/Series>}
  16070. */
  16071. getSeriesByName: function (name) {
  16072. var series = this._componentsMap.get('series');
  16073. return filter$1(series, function (oneSeries) {
  16074. return oneSeries.name === name;
  16075. });
  16076. },
  16077. /**
  16078. * @param {number} seriesIndex
  16079. * @return {module:echarts/model/Series}
  16080. */
  16081. getSeriesByIndex: function (seriesIndex) {
  16082. return this._componentsMap.get('series')[seriesIndex];
  16083. },
  16084. /**
  16085. * @param {string} subType
  16086. * @return {Array.<module:echarts/model/Series>}
  16087. */
  16088. getSeriesByType: function (subType) {
  16089. var series = this._componentsMap.get('series');
  16090. return filter$1(series, function (oneSeries) {
  16091. return oneSeries.subType === subType;
  16092. });
  16093. },
  16094. /**
  16095. * @return {Array.<module:echarts/model/Series>}
  16096. */
  16097. getSeries: function () {
  16098. return this._componentsMap.get('series').slice();
  16099. },
  16100. /**
  16101. * After filtering, series may be different
  16102. * frome raw series.
  16103. *
  16104. * @param {Function} cb
  16105. * @param {*} context
  16106. */
  16107. eachSeries: function (cb, context) {
  16108. assertSeriesInitialized(this);
  16109. each$2(this._seriesIndices, function (rawSeriesIndex) {
  16110. var series = this._componentsMap.get('series')[rawSeriesIndex];
  16111. cb.call(context, series, rawSeriesIndex);
  16112. }, this);
  16113. },
  16114. /**
  16115. * Iterate raw series before filtered.
  16116. *
  16117. * @param {Function} cb
  16118. * @param {*} context
  16119. */
  16120. eachRawSeries: function (cb, context) {
  16121. each$2(this._componentsMap.get('series'), cb, context);
  16122. },
  16123. /**
  16124. * After filtering, series may be different.
  16125. * frome raw series.
  16126. *
  16127. * @parma {string} subType
  16128. * @param {Function} cb
  16129. * @param {*} context
  16130. */
  16131. eachSeriesByType: function (subType, cb, context) {
  16132. assertSeriesInitialized(this);
  16133. each$2(this._seriesIndices, function (rawSeriesIndex) {
  16134. var series = this._componentsMap.get('series')[rawSeriesIndex];
  16135. if (series.subType === subType) {
  16136. cb.call(context, series, rawSeriesIndex);
  16137. }
  16138. }, this);
  16139. },
  16140. /**
  16141. * Iterate raw series before filtered of given type.
  16142. *
  16143. * @parma {string} subType
  16144. * @param {Function} cb
  16145. * @param {*} context
  16146. */
  16147. eachRawSeriesByType: function (subType, cb, context) {
  16148. return each$2(this.getSeriesByType(subType), cb, context);
  16149. },
  16150. /**
  16151. * @param {module:echarts/model/Series} seriesModel
  16152. */
  16153. isSeriesFiltered: function (seriesModel) {
  16154. assertSeriesInitialized(this);
  16155. return indexOf(this._seriesIndices, seriesModel.componentIndex) < 0;
  16156. },
  16157. /**
  16158. * @return {Array.<number>}
  16159. */
  16160. getCurrentSeriesIndices: function () {
  16161. return (this._seriesIndices || []).slice();
  16162. },
  16163. /**
  16164. * @param {Function} cb
  16165. * @param {*} context
  16166. */
  16167. filterSeries: function (cb, context) {
  16168. assertSeriesInitialized(this);
  16169. var filteredSeries = filter$1(
  16170. this._componentsMap.get('series'), cb, context
  16171. );
  16172. this._seriesIndices = createSeriesIndices(filteredSeries);
  16173. },
  16174. restoreData: function () {
  16175. var componentsMap = this._componentsMap;
  16176. this._seriesIndices = createSeriesIndices(componentsMap.get('series'));
  16177. var componentTypes = [];
  16178. componentsMap.each(function (components, componentType) {
  16179. componentTypes.push(componentType);
  16180. });
  16181. ComponentModel.topologicalTravel(
  16182. componentTypes,
  16183. ComponentModel.getAllClassMainTypes(),
  16184. function (componentType, dependencies) {
  16185. each$2(componentsMap.get(componentType), function (component) {
  16186. component.restoreData();
  16187. });
  16188. }
  16189. );
  16190. }
  16191. });
  16192. /**
  16193. * @inner
  16194. */
  16195. function mergeTheme(option, theme) {
  16196. each$1(theme, function (themeItem, name) {
  16197. // 如果有 component model 则把具体的 merge 逻辑交给该 model 处理
  16198. if (!ComponentModel.hasClass(name)) {
  16199. if (typeof themeItem === 'object') {
  16200. option[name] = !option[name]
  16201. ? clone(themeItem)
  16202. : merge(option[name], themeItem, false);
  16203. }
  16204. else {
  16205. if (option[name] == null) {
  16206. option[name] = themeItem;
  16207. }
  16208. }
  16209. }
  16210. });
  16211. }
  16212. function initBase(baseOption) {
  16213. baseOption = baseOption;
  16214. // Using OPTION_INNER_KEY to mark that this option can not be used outside,
  16215. // i.e. `chart.setOption(chart.getModel().option);` is forbiden.
  16216. this.option = {};
  16217. this.option[OPTION_INNER_KEY] = 1;
  16218. /**
  16219. * Init with series: [], in case of calling findSeries method
  16220. * before series initialized.
  16221. * @type {Object.<string, Array.<module:echarts/model/Model>>}
  16222. * @private
  16223. */
  16224. this._componentsMap = createHashMap({series: []});
  16225. /**
  16226. * Mapping between filtered series list and raw series list.
  16227. * key: filtered series indices, value: raw series indices.
  16228. * @type {Array.<nubmer>}
  16229. * @private
  16230. */
  16231. this._seriesIndices = null;
  16232. mergeTheme(baseOption, this._theme.option);
  16233. // TODO Needs clone when merging to the unexisted property
  16234. merge(baseOption, globalDefault, false);
  16235. this.mergeOption(baseOption);
  16236. }
  16237. /**
  16238. * @inner
  16239. * @param {Array.<string>|string} types model types
  16240. * @return {Object} key: {string} type, value: {Array.<Object>} models
  16241. */
  16242. function getComponentsByTypes(componentsMap, types) {
  16243. if (!isArray(types)) {
  16244. types = types ? [types] : [];
  16245. }
  16246. var ret = {};
  16247. each$2(types, function (type) {
  16248. ret[type] = (componentsMap.get(type) || []).slice();
  16249. });
  16250. return ret;
  16251. }
  16252. /**
  16253. * @inner
  16254. */
  16255. function determineSubType(mainType, newCptOption, existComponent) {
  16256. var subType = newCptOption.type
  16257. ? newCptOption.type
  16258. : existComponent
  16259. ? existComponent.subType
  16260. // Use determineSubType only when there is no existComponent.
  16261. : ComponentModel.determineSubType(mainType, newCptOption);
  16262. // tooltip, markline, markpoint may always has no subType
  16263. return subType;
  16264. }
  16265. /**
  16266. * @inner
  16267. */
  16268. function createSeriesIndices(seriesModels) {
  16269. return map$1(seriesModels, function (series) {
  16270. return series.componentIndex;
  16271. }) || [];
  16272. }
  16273. /**
  16274. * @inner
  16275. */
  16276. function filterBySubType(components, condition) {
  16277. // Using hasOwnProperty for restrict. Consider
  16278. // subType is undefined in user payload.
  16279. return condition.hasOwnProperty('subType')
  16280. ? filter$1(components, function (cpt) {
  16281. return cpt.subType === condition.subType;
  16282. })
  16283. : components;
  16284. }
  16285. /**
  16286. * @inner
  16287. */
  16288. function assertSeriesInitialized(ecModel) {
  16289. // Components that use _seriesIndices should depends on series component,
  16290. // which make sure that their initialization is after series.
  16291. if (__DEV__) {
  16292. if (!ecModel._seriesIndices) {
  16293. throw new Error('Option should contains series.');
  16294. }
  16295. }
  16296. }
  16297. mixin(GlobalModel, colorPaletteMixin);
  16298. var echartsAPIList = [
  16299. 'getDom', 'getZr', 'getWidth', 'getHeight', 'getDevicePixelRatio', 'dispatchAction', 'isDisposed',
  16300. 'on', 'off', 'getDataURL', 'getConnectedDataURL', 'getModel', 'getOption',
  16301. 'getViewOfComponentModel', 'getViewOfSeriesModel'
  16302. ];
  16303. // And `getCoordinateSystems` and `getComponentByElement` will be injected in echarts.js
  16304. function ExtensionAPI(chartInstance) {
  16305. each$1(echartsAPIList, function (name) {
  16306. this[name] = bind(chartInstance[name], chartInstance);
  16307. }, this);
  16308. }
  16309. var coordinateSystemCreators = {};
  16310. function CoordinateSystemManager() {
  16311. this._coordinateSystems = [];
  16312. }
  16313. CoordinateSystemManager.prototype = {
  16314. constructor: CoordinateSystemManager,
  16315. create: function (ecModel, api) {
  16316. var coordinateSystems = [];
  16317. each$1(coordinateSystemCreators, function (creater, type) {
  16318. var list = creater.create(ecModel, api);
  16319. coordinateSystems = coordinateSystems.concat(list || []);
  16320. });
  16321. this._coordinateSystems = coordinateSystems;
  16322. },
  16323. update: function (ecModel, api) {
  16324. each$1(this._coordinateSystems, function (coordSys) {
  16325. // FIXME MUST have
  16326. coordSys.update && coordSys.update(ecModel, api);
  16327. });
  16328. },
  16329. getCoordinateSystems: function () {
  16330. return this._coordinateSystems.slice();
  16331. }
  16332. };
  16333. CoordinateSystemManager.register = function (type, coordinateSystemCreator) {
  16334. coordinateSystemCreators[type] = coordinateSystemCreator;
  16335. };
  16336. CoordinateSystemManager.get = function (type) {
  16337. return coordinateSystemCreators[type];
  16338. };
  16339. /**
  16340. * ECharts option manager
  16341. *
  16342. * @module {echarts/model/OptionManager}
  16343. */
  16344. var each$5 = each$1;
  16345. var clone$2 = clone;
  16346. var map$2 = map;
  16347. var merge$1 = merge;
  16348. var QUERY_REG = /^(min|max)?(.+)$/;
  16349. /**
  16350. * TERM EXPLANATIONS:
  16351. *
  16352. * [option]:
  16353. *
  16354. * An object that contains definitions of components. For example:
  16355. * var option = {
  16356. * title: {...},
  16357. * legend: {...},
  16358. * visualMap: {...},
  16359. * series: [
  16360. * {data: [...]},
  16361. * {data: [...]},
  16362. * ...
  16363. * ]
  16364. * };
  16365. *
  16366. * [rawOption]:
  16367. *
  16368. * An object input to echarts.setOption. 'rawOption' may be an
  16369. * 'option', or may be an object contains multi-options. For example:
  16370. * var option = {
  16371. * baseOption: {
  16372. * title: {...},
  16373. * legend: {...},
  16374. * series: [
  16375. * {data: [...]},
  16376. * {data: [...]},
  16377. * ...
  16378. * ]
  16379. * },
  16380. * timeline: {...},
  16381. * options: [
  16382. * {title: {...}, series: {data: [...]}},
  16383. * {title: {...}, series: {data: [...]}},
  16384. * ...
  16385. * ],
  16386. * media: [
  16387. * {
  16388. * query: {maxWidth: 320},
  16389. * option: {series: {x: 20}, visualMap: {show: false}}
  16390. * },
  16391. * {
  16392. * query: {minWidth: 320, maxWidth: 720},
  16393. * option: {series: {x: 500}, visualMap: {show: true}}
  16394. * },
  16395. * {
  16396. * option: {series: {x: 1200}, visualMap: {show: true}}
  16397. * }
  16398. * ]
  16399. * };
  16400. *
  16401. * @alias module:echarts/model/OptionManager
  16402. * @param {module:echarts/ExtensionAPI} api
  16403. */
  16404. function OptionManager(api) {
  16405. /**
  16406. * @private
  16407. * @type {module:echarts/ExtensionAPI}
  16408. */
  16409. this._api = api;
  16410. /**
  16411. * @private
  16412. * @type {Array.<number>}
  16413. */
  16414. this._timelineOptions = [];
  16415. /**
  16416. * @private
  16417. * @type {Array.<Object>}
  16418. */
  16419. this._mediaList = [];
  16420. /**
  16421. * @private
  16422. * @type {Object}
  16423. */
  16424. this._mediaDefault;
  16425. /**
  16426. * -1, means default.
  16427. * empty means no media.
  16428. * @private
  16429. * @type {Array.<number>}
  16430. */
  16431. this._currentMediaIndices = [];
  16432. /**
  16433. * @private
  16434. * @type {Object}
  16435. */
  16436. this._optionBackup;
  16437. /**
  16438. * @private
  16439. * @type {Object}
  16440. */
  16441. this._newBaseOption;
  16442. }
  16443. // timeline.notMerge is not supported in ec3. Firstly there is rearly
  16444. // case that notMerge is needed. Secondly supporting 'notMerge' requires
  16445. // rawOption cloned and backuped when timeline changed, which does no
  16446. // good to performance. What's more, that both timeline and setOption
  16447. // method supply 'notMerge' brings complex and some problems.
  16448. // Consider this case:
  16449. // (step1) chart.setOption({timeline: {notMerge: false}, ...}, false);
  16450. // (step2) chart.setOption({timeline: {notMerge: true}, ...}, false);
  16451. OptionManager.prototype = {
  16452. constructor: OptionManager,
  16453. /**
  16454. * @public
  16455. * @param {Object} rawOption Raw option.
  16456. * @param {module:echarts/model/Global} ecModel
  16457. * @param {Array.<Function>} optionPreprocessorFuncs
  16458. * @return {Object} Init option
  16459. */
  16460. setOption: function (rawOption, optionPreprocessorFuncs) {
  16461. rawOption = clone$2(rawOption, true);
  16462. // FIXME
  16463. // 如果 timeline options 或者 media 中设置了某个属性,而baseOption中没有设置,则进行警告。
  16464. var oldOptionBackup = this._optionBackup;
  16465. var newParsedOption = parseRawOption.call(
  16466. this, rawOption, optionPreprocessorFuncs, !oldOptionBackup
  16467. );
  16468. this._newBaseOption = newParsedOption.baseOption;
  16469. // For setOption at second time (using merge mode);
  16470. if (oldOptionBackup) {
  16471. // Only baseOption can be merged.
  16472. mergeOption(oldOptionBackup.baseOption, newParsedOption.baseOption);
  16473. // For simplicity, timeline options and media options do not support merge,
  16474. // that is, if you `setOption` twice and both has timeline options, the latter
  16475. // timeline opitons will not be merged to the formers, but just substitude them.
  16476. if (newParsedOption.timelineOptions.length) {
  16477. oldOptionBackup.timelineOptions = newParsedOption.timelineOptions;
  16478. }
  16479. if (newParsedOption.mediaList.length) {
  16480. oldOptionBackup.mediaList = newParsedOption.mediaList;
  16481. }
  16482. if (newParsedOption.mediaDefault) {
  16483. oldOptionBackup.mediaDefault = newParsedOption.mediaDefault;
  16484. }
  16485. }
  16486. else {
  16487. this._optionBackup = newParsedOption;
  16488. }
  16489. },
  16490. /**
  16491. * @param {boolean} isRecreate
  16492. * @return {Object}
  16493. */
  16494. mountOption: function (isRecreate) {
  16495. var optionBackup = this._optionBackup;
  16496. // TODO
  16497. // 如果没有reset功能则不clone。
  16498. this._timelineOptions = map$2(optionBackup.timelineOptions, clone$2);
  16499. this._mediaList = map$2(optionBackup.mediaList, clone$2);
  16500. this._mediaDefault = clone$2(optionBackup.mediaDefault);
  16501. this._currentMediaIndices = [];
  16502. return clone$2(isRecreate
  16503. // this._optionBackup.baseOption, which is created at the first `setOption`
  16504. // called, and is merged into every new option by inner method `mergeOption`
  16505. // each time `setOption` called, can be only used in `isRecreate`, because
  16506. // its reliability is under suspicion. In other cases option merge is
  16507. // performed by `model.mergeOption`.
  16508. ? optionBackup.baseOption : this._newBaseOption
  16509. );
  16510. },
  16511. /**
  16512. * @param {module:echarts/model/Global} ecModel
  16513. * @return {Object}
  16514. */
  16515. getTimelineOption: function (ecModel) {
  16516. var option;
  16517. var timelineOptions = this._timelineOptions;
  16518. if (timelineOptions.length) {
  16519. // getTimelineOption can only be called after ecModel inited,
  16520. // so we can get currentIndex from timelineModel.
  16521. var timelineModel = ecModel.getComponent('timeline');
  16522. if (timelineModel) {
  16523. option = clone$2(
  16524. timelineOptions[timelineModel.getCurrentIndex()],
  16525. true
  16526. );
  16527. }
  16528. }
  16529. return option;
  16530. },
  16531. /**
  16532. * @param {module:echarts/model/Global} ecModel
  16533. * @return {Array.<Object>}
  16534. */
  16535. getMediaOption: function (ecModel) {
  16536. var ecWidth = this._api.getWidth();
  16537. var ecHeight = this._api.getHeight();
  16538. var mediaList = this._mediaList;
  16539. var mediaDefault = this._mediaDefault;
  16540. var indices = [];
  16541. var result = [];
  16542. // No media defined.
  16543. if (!mediaList.length && !mediaDefault) {
  16544. return result;
  16545. }
  16546. // Multi media may be applied, the latter defined media has higher priority.
  16547. for (var i = 0, len = mediaList.length; i < len; i++) {
  16548. if (applyMediaQuery(mediaList[i].query, ecWidth, ecHeight)) {
  16549. indices.push(i);
  16550. }
  16551. }
  16552. // FIXME
  16553. // 是否mediaDefault应该强制用户设置,否则可能修改不能回归。
  16554. if (!indices.length && mediaDefault) {
  16555. indices = [-1];
  16556. }
  16557. if (indices.length && !indicesEquals(indices, this._currentMediaIndices)) {
  16558. result = map$2(indices, function (index) {
  16559. return clone$2(
  16560. index === -1 ? mediaDefault.option : mediaList[index].option
  16561. );
  16562. });
  16563. }
  16564. // Otherwise return nothing.
  16565. this._currentMediaIndices = indices;
  16566. return result;
  16567. }
  16568. };
  16569. function parseRawOption(rawOption, optionPreprocessorFuncs, isNew) {
  16570. var timelineOptions = [];
  16571. var mediaList = [];
  16572. var mediaDefault;
  16573. var baseOption;
  16574. // Compatible with ec2.
  16575. var timelineOpt = rawOption.timeline;
  16576. if (rawOption.baseOption) {
  16577. baseOption = rawOption.baseOption;
  16578. }
  16579. // For timeline
  16580. if (timelineOpt || rawOption.options) {
  16581. baseOption = baseOption || {};
  16582. timelineOptions = (rawOption.options || []).slice();
  16583. }
  16584. // For media query
  16585. if (rawOption.media) {
  16586. baseOption = baseOption || {};
  16587. var media = rawOption.media;
  16588. each$5(media, function (singleMedia) {
  16589. if (singleMedia && singleMedia.option) {
  16590. if (singleMedia.query) {
  16591. mediaList.push(singleMedia);
  16592. }
  16593. else if (!mediaDefault) {
  16594. // Use the first media default.
  16595. mediaDefault = singleMedia;
  16596. }
  16597. }
  16598. });
  16599. }
  16600. // For normal option
  16601. if (!baseOption) {
  16602. baseOption = rawOption;
  16603. }
  16604. // Set timelineOpt to baseOption in ec3,
  16605. // which is convenient for merge option.
  16606. if (!baseOption.timeline) {
  16607. baseOption.timeline = timelineOpt;
  16608. }
  16609. // Preprocess.
  16610. each$5([baseOption].concat(timelineOptions)
  16611. .concat(map(mediaList, function (media) {
  16612. return media.option;
  16613. })),
  16614. function (option) {
  16615. each$5(optionPreprocessorFuncs, function (preProcess) {
  16616. preProcess(option, isNew);
  16617. });
  16618. }
  16619. );
  16620. return {
  16621. baseOption: baseOption,
  16622. timelineOptions: timelineOptions,
  16623. mediaDefault: mediaDefault,
  16624. mediaList: mediaList
  16625. };
  16626. }
  16627. /**
  16628. * @see <http://www.w3.org/TR/css3-mediaqueries/#media1>
  16629. * Support: width, height, aspectRatio
  16630. * Can use max or min as prefix.
  16631. */
  16632. function applyMediaQuery(query, ecWidth, ecHeight) {
  16633. var realMap = {
  16634. width: ecWidth,
  16635. height: ecHeight,
  16636. aspectratio: ecWidth / ecHeight // lowser case for convenientce.
  16637. };
  16638. var applicatable = true;
  16639. each$1(query, function (value, attr) {
  16640. var matched = attr.match(QUERY_REG);
  16641. if (!matched || !matched[1] || !matched[2]) {
  16642. return;
  16643. }
  16644. var operator = matched[1];
  16645. var realAttr = matched[2].toLowerCase();
  16646. if (!compare(realMap[realAttr], value, operator)) {
  16647. applicatable = false;
  16648. }
  16649. });
  16650. return applicatable;
  16651. }
  16652. function compare(real, expect, operator) {
  16653. if (operator === 'min') {
  16654. return real >= expect;
  16655. }
  16656. else if (operator === 'max') {
  16657. return real <= expect;
  16658. }
  16659. else { // Equals
  16660. return real === expect;
  16661. }
  16662. }
  16663. function indicesEquals(indices1, indices2) {
  16664. // indices is always order by asc and has only finite number.
  16665. return indices1.join(',') === indices2.join(',');
  16666. }
  16667. /**
  16668. * Consider case:
  16669. * `chart.setOption(opt1);`
  16670. * Then user do some interaction like dataZoom, dataView changing.
  16671. * `chart.setOption(opt2);`
  16672. * Then user press 'reset button' in toolbox.
  16673. *
  16674. * After doing that all of the interaction effects should be reset, the
  16675. * chart should be the same as the result of invoke
  16676. * `chart.setOption(opt1); chart.setOption(opt2);`.
  16677. *
  16678. * Although it is not able ensure that
  16679. * `chart.setOption(opt1); chart.setOption(opt2);` is equivalents to
  16680. * `chart.setOption(merge(opt1, opt2));` exactly,
  16681. * this might be the only simple way to implement that feature.
  16682. *
  16683. * MEMO: We've considered some other approaches:
  16684. * 1. Each model handle its self restoration but not uniform treatment.
  16685. * (Too complex in logic and error-prone)
  16686. * 2. Use a shadow ecModel. (Performace expensive)
  16687. */
  16688. function mergeOption(oldOption, newOption) {
  16689. newOption = newOption || {};
  16690. each$5(newOption, function (newCptOpt, mainType) {
  16691. if (newCptOpt == null) {
  16692. return;
  16693. }
  16694. var oldCptOpt = oldOption[mainType];
  16695. if (!ComponentModel.hasClass(mainType)) {
  16696. oldOption[mainType] = merge$1(oldCptOpt, newCptOpt, true);
  16697. }
  16698. else {
  16699. newCptOpt = normalizeToArray(newCptOpt);
  16700. oldCptOpt = normalizeToArray(oldCptOpt);
  16701. var mapResult = mappingToExists(oldCptOpt, newCptOpt);
  16702. oldOption[mainType] = map$2(mapResult, function (item) {
  16703. return (item.option && item.exist)
  16704. ? merge$1(item.exist, item.option, true)
  16705. : (item.exist || item.option);
  16706. });
  16707. }
  16708. });
  16709. }
  16710. var each$6 = each$1;
  16711. var isObject$3 = isObject;
  16712. var POSSIBLE_STYLES = [
  16713. 'areaStyle', 'lineStyle', 'nodeStyle', 'linkStyle',
  16714. 'chordStyle', 'label', 'labelLine'
  16715. ];
  16716. function compatItemStyle(opt) {
  16717. var itemStyleOpt = opt && opt.itemStyle;
  16718. if (!itemStyleOpt) {
  16719. return;
  16720. }
  16721. for (var i = 0, len = POSSIBLE_STYLES.length; i < len; i++) {
  16722. var styleName = POSSIBLE_STYLES[i];
  16723. var normalItemStyleOpt = itemStyleOpt.normal;
  16724. var emphasisItemStyleOpt = itemStyleOpt.emphasis;
  16725. if (normalItemStyleOpt && normalItemStyleOpt[styleName]) {
  16726. opt[styleName] = opt[styleName] || {};
  16727. if (!opt[styleName].normal) {
  16728. opt[styleName].normal = normalItemStyleOpt[styleName];
  16729. }
  16730. else {
  16731. merge(opt[styleName].normal, normalItemStyleOpt[styleName]);
  16732. }
  16733. normalItemStyleOpt[styleName] = null;
  16734. }
  16735. if (emphasisItemStyleOpt && emphasisItemStyleOpt[styleName]) {
  16736. opt[styleName] = opt[styleName] || {};
  16737. if (!opt[styleName].emphasis) {
  16738. opt[styleName].emphasis = emphasisItemStyleOpt[styleName];
  16739. }
  16740. else {
  16741. merge(opt[styleName].emphasis, emphasisItemStyleOpt[styleName]);
  16742. }
  16743. emphasisItemStyleOpt[styleName] = null;
  16744. }
  16745. }
  16746. }
  16747. function compatTextStyle(opt, propName) {
  16748. var labelOptSingle = isObject$3(opt) && opt[propName];
  16749. var textStyle = isObject$3(labelOptSingle) && labelOptSingle.textStyle;
  16750. if (textStyle) {
  16751. for (var i = 0, len = TEXT_STYLE_OPTIONS.length; i < len; i++) {
  16752. var propName = TEXT_STYLE_OPTIONS[i];
  16753. if (textStyle.hasOwnProperty(propName)) {
  16754. labelOptSingle[propName] = textStyle[propName];
  16755. }
  16756. }
  16757. }
  16758. }
  16759. function compatLabelTextStyle(labelOpt) {
  16760. if (isObject$3(labelOpt)) {
  16761. compatTextStyle(labelOpt, 'normal');
  16762. compatTextStyle(labelOpt, 'emphasis');
  16763. }
  16764. }
  16765. function processSeries(seriesOpt) {
  16766. if (!isObject$3(seriesOpt)) {
  16767. return;
  16768. }
  16769. compatItemStyle(seriesOpt);
  16770. compatLabelTextStyle(seriesOpt.label);
  16771. // treemap
  16772. compatLabelTextStyle(seriesOpt.upperLabel);
  16773. // graph
  16774. compatLabelTextStyle(seriesOpt.edgeLabel);
  16775. var markPoint = seriesOpt.markPoint;
  16776. compatItemStyle(markPoint);
  16777. compatLabelTextStyle(markPoint && markPoint.label);
  16778. var markLine = seriesOpt.markLine;
  16779. compatItemStyle(seriesOpt.markLine);
  16780. compatLabelTextStyle(markLine && markLine.label);
  16781. var markArea = seriesOpt.markArea;
  16782. compatLabelTextStyle(markArea && markArea.label);
  16783. // For gauge
  16784. compatTextStyle(seriesOpt, 'axisLabel');
  16785. compatTextStyle(seriesOpt, 'title');
  16786. compatTextStyle(seriesOpt, 'detail');
  16787. var data = seriesOpt.data;
  16788. if (data) {
  16789. for (var i = 0; i < data.length; i++) {
  16790. compatItemStyle(data[i]);
  16791. compatLabelTextStyle(data[i] && data[i].label);
  16792. }
  16793. }
  16794. // mark point data
  16795. var markPoint = seriesOpt.markPoint;
  16796. if (markPoint && markPoint.data) {
  16797. var mpData = markPoint.data;
  16798. for (var i = 0; i < mpData.length; i++) {
  16799. compatItemStyle(mpData[i]);
  16800. compatLabelTextStyle(mpData[i] && mpData[i].label);
  16801. }
  16802. }
  16803. // mark line data
  16804. var markLine = seriesOpt.markLine;
  16805. if (markLine && markLine.data) {
  16806. var mlData = markLine.data;
  16807. for (var i = 0; i < mlData.length; i++) {
  16808. if (isArray(mlData[i])) {
  16809. compatItemStyle(mlData[i][0]);
  16810. compatLabelTextStyle(mlData[i][0] && mlData[i][0].label);
  16811. compatItemStyle(mlData[i][1]);
  16812. compatLabelTextStyle(mlData[i][1] && mlData[i][1].label);
  16813. }
  16814. else {
  16815. compatItemStyle(mlData[i]);
  16816. compatLabelTextStyle(mlData[i] && mlData[i].label);
  16817. }
  16818. }
  16819. }
  16820. }
  16821. function toArr(o) {
  16822. return isArray(o) ? o : o ? [o] : [];
  16823. }
  16824. function toObj(o) {
  16825. return (isArray(o) ? o[0] : o) || {};
  16826. }
  16827. var compatStyle = function (option, isTheme) {
  16828. each$6(toArr(option.series), function (seriesOpt) {
  16829. isObject$3(seriesOpt) && processSeries(seriesOpt);
  16830. });
  16831. var axes = ['xAxis', 'yAxis', 'radiusAxis', 'angleAxis', 'singleAxis', 'parallelAxis', 'radar'];
  16832. isTheme && axes.push('valueAxis', 'categoryAxis', 'logAxis', 'timeAxis');
  16833. each$6(
  16834. axes,
  16835. function (axisName) {
  16836. each$6(toArr(option[axisName]), function (axisOpt) {
  16837. if (axisOpt) {
  16838. compatTextStyle(axisOpt, 'axisLabel');
  16839. compatTextStyle(axisOpt.axisPointer, 'label');
  16840. }
  16841. });
  16842. }
  16843. );
  16844. each$6(toArr(option.parallel), function (parallelOpt) {
  16845. var parallelAxisDefault = parallelOpt && parallelOpt.parallelAxisDefault;
  16846. compatTextStyle(parallelAxisDefault, 'axisLabel');
  16847. compatTextStyle(parallelAxisDefault && parallelAxisDefault.axisPointer, 'label');
  16848. });
  16849. each$6(toArr(option.calendar), function (calendarOpt) {
  16850. compatTextStyle(calendarOpt, 'dayLabel');
  16851. compatTextStyle(calendarOpt, 'monthLabel');
  16852. compatTextStyle(calendarOpt, 'yearLabel');
  16853. });
  16854. // radar.name.textStyle
  16855. each$6(toArr(option.radar), function (radarOpt) {
  16856. compatTextStyle(radarOpt, 'name');
  16857. });
  16858. each$6(toArr(option.geo), function (geoOpt) {
  16859. if (isObject$3(geoOpt)) {
  16860. compatLabelTextStyle(geoOpt.label);
  16861. each$6(toArr(geoOpt.regions), function (regionObj) {
  16862. compatLabelTextStyle(regionObj.label);
  16863. });
  16864. }
  16865. });
  16866. compatLabelTextStyle(toObj(option.timeline).label);
  16867. compatTextStyle(toObj(option.axisPointer), 'label');
  16868. compatTextStyle(toObj(option.tooltip).axisPointer, 'label');
  16869. };
  16870. // Compatitable with 2.0
  16871. function get$1(opt, path) {
  16872. path = path.split(',');
  16873. var obj = opt;
  16874. for (var i = 0; i < path.length; i++) {
  16875. obj = obj && obj[path[i]];
  16876. if (obj == null) {
  16877. break;
  16878. }
  16879. }
  16880. return obj;
  16881. }
  16882. function set$2(opt, path, val, overwrite) {
  16883. path = path.split(',');
  16884. var obj = opt;
  16885. var key;
  16886. for (var i = 0; i < path.length - 1; i++) {
  16887. key = path[i];
  16888. if (obj[key] == null) {
  16889. obj[key] = {};
  16890. }
  16891. obj = obj[key];
  16892. }
  16893. if (overwrite || obj[path[i]] == null) {
  16894. obj[path[i]] = val;
  16895. }
  16896. }
  16897. function compatLayoutProperties(option) {
  16898. each$1(LAYOUT_PROPERTIES, function (prop) {
  16899. if (prop[0] in option && !(prop[1] in option)) {
  16900. option[prop[1]] = option[prop[0]];
  16901. }
  16902. });
  16903. }
  16904. var LAYOUT_PROPERTIES = [
  16905. ['x', 'left'], ['y', 'top'], ['x2', 'right'], ['y2', 'bottom']
  16906. ];
  16907. var COMPATITABLE_COMPONENTS = [
  16908. 'grid', 'geo', 'parallel', 'legend', 'toolbox', 'title', 'visualMap', 'dataZoom', 'timeline'
  16909. ];
  16910. var COMPATITABLE_SERIES = [
  16911. 'bar', 'boxplot', 'candlestick', 'chord', 'effectScatter',
  16912. 'funnel', 'gauge', 'lines', 'graph', 'heatmap', 'line', 'map', 'parallel',
  16913. 'pie', 'radar', 'sankey', 'scatter', 'treemap'
  16914. ];
  16915. var backwardCompat = function (option, isTheme) {
  16916. compatStyle(option, isTheme);
  16917. // Make sure series array for model initialization.
  16918. option.series = normalizeToArray(option.series);
  16919. each$1(option.series, function (seriesOpt) {
  16920. if (!isObject(seriesOpt)) {
  16921. return;
  16922. }
  16923. var seriesType = seriesOpt.type;
  16924. if (seriesType === 'pie' || seriesType === 'gauge') {
  16925. if (seriesOpt.clockWise != null) {
  16926. seriesOpt.clockwise = seriesOpt.clockWise;
  16927. }
  16928. }
  16929. if (seriesType === 'gauge') {
  16930. var pointerColor = get$1(seriesOpt, 'pointer.color');
  16931. pointerColor != null
  16932. && set$2(seriesOpt, 'itemStyle.normal.color', pointerColor);
  16933. }
  16934. for (var i = 0; i < COMPATITABLE_SERIES.length; i++) {
  16935. if (COMPATITABLE_SERIES[i] === seriesOpt.type) {
  16936. compatLayoutProperties(seriesOpt);
  16937. break;
  16938. }
  16939. }
  16940. });
  16941. // dataRange has changed to visualMap
  16942. if (option.dataRange) {
  16943. option.visualMap = option.dataRange;
  16944. }
  16945. each$1(COMPATITABLE_COMPONENTS, function (componentName) {
  16946. var options = option[componentName];
  16947. if (options) {
  16948. if (!isArray(options)) {
  16949. options = [options];
  16950. }
  16951. each$1(options, function (option) {
  16952. compatLayoutProperties(option);
  16953. });
  16954. }
  16955. });
  16956. };
  16957. var SeriesModel = ComponentModel.extend({
  16958. type: 'series.__base__',
  16959. /**
  16960. * @readOnly
  16961. */
  16962. seriesIndex: 0,
  16963. // coodinateSystem will be injected in the echarts/CoordinateSystem
  16964. coordinateSystem: null,
  16965. /**
  16966. * @type {Object}
  16967. * @protected
  16968. */
  16969. defaultOption: null,
  16970. /**
  16971. * Data provided for legend
  16972. * @type {Function}
  16973. */
  16974. // PENDING
  16975. legendDataProvider: null,
  16976. /**
  16977. * Access path of color for visual
  16978. */
  16979. visualColorAccessPath: 'itemStyle.normal.color',
  16980. /**
  16981. * Support merge layout params.
  16982. * Only support 'box' now (left/right/top/bottom/width/height).
  16983. * @type {string|Object} Object can be {ignoreSize: true}
  16984. * @readOnly
  16985. */
  16986. layoutMode: null,
  16987. init: function (option, parentModel, ecModel, extraOpt) {
  16988. /**
  16989. * @type {number}
  16990. * @readOnly
  16991. */
  16992. this.seriesIndex = this.componentIndex;
  16993. this.mergeDefaultAndTheme(option, ecModel);
  16994. var data = this.getInitialData(option, ecModel);
  16995. if (__DEV__) {
  16996. assert(data, 'getInitialData returned invalid data.');
  16997. }
  16998. /**
  16999. * @type {module:echarts/data/List|module:echarts/data/Tree|module:echarts/data/Graph}
  17000. * @private
  17001. */
  17002. set$1(this, 'dataBeforeProcessed', data);
  17003. // If we reverse the order (make data firstly, and then make
  17004. // dataBeforeProcessed by cloneShallow), cloneShallow will
  17005. // cause data.graph.data !== data when using
  17006. // module:echarts/data/Graph or module:echarts/data/Tree.
  17007. // See module:echarts/data/helper/linkList
  17008. this.restoreData();
  17009. },
  17010. /**
  17011. * Util for merge default and theme to option
  17012. * @param {Object} option
  17013. * @param {module:echarts/model/Global} ecModel
  17014. */
  17015. mergeDefaultAndTheme: function (option, ecModel) {
  17016. var layoutMode = this.layoutMode;
  17017. var inputPositionParams = layoutMode
  17018. ? getLayoutParams(option) : {};
  17019. // Backward compat: using subType on theme.
  17020. // But if name duplicate between series subType
  17021. // (for example: parallel) add component mainType,
  17022. // add suffix 'Series'.
  17023. var themeSubType = this.subType;
  17024. if (ComponentModel.hasClass(themeSubType)) {
  17025. themeSubType += 'Series';
  17026. }
  17027. merge(
  17028. option,
  17029. ecModel.getTheme().get(this.subType)
  17030. );
  17031. merge(option, this.getDefaultOption());
  17032. // Default label emphasis `show`
  17033. defaultEmphasis(option.label, ['show']);
  17034. this.fillDataTextStyle(option.data);
  17035. if (layoutMode) {
  17036. mergeLayoutParam(option, inputPositionParams, layoutMode);
  17037. }
  17038. },
  17039. mergeOption: function (newSeriesOption, ecModel) {
  17040. newSeriesOption = merge(this.option, newSeriesOption, true);
  17041. this.fillDataTextStyle(newSeriesOption.data);
  17042. var layoutMode = this.layoutMode;
  17043. if (layoutMode) {
  17044. mergeLayoutParam(this.option, newSeriesOption, layoutMode);
  17045. }
  17046. var data = this.getInitialData(newSeriesOption, ecModel);
  17047. // TODO Merge data?
  17048. if (data) {
  17049. set$1(this, 'data', data);
  17050. set$1(this, 'dataBeforeProcessed', data.cloneShallow());
  17051. }
  17052. },
  17053. fillDataTextStyle: function (data) {
  17054. // Default data label emphasis `show`
  17055. // FIXME Tree structure data ?
  17056. // FIXME Performance ?
  17057. if (data) {
  17058. var props = ['show'];
  17059. for (var i = 0; i < data.length; i++) {
  17060. if (data[i] && data[i].label) {
  17061. defaultEmphasis(data[i].label, props);
  17062. }
  17063. }
  17064. }
  17065. },
  17066. /**
  17067. * Init a data structure from data related option in series
  17068. * Must be overwritten
  17069. */
  17070. getInitialData: function () {},
  17071. /**
  17072. * @param {string} [dataType]
  17073. * @return {module:echarts/data/List}
  17074. */
  17075. getData: function (dataType) {
  17076. var data = get(this, 'data');
  17077. return dataType == null ? data : data.getLinkedData(dataType);
  17078. },
  17079. /**
  17080. * @param {module:echarts/data/List} data
  17081. */
  17082. setData: function (data) {
  17083. set$1(this, 'data', data);
  17084. },
  17085. /**
  17086. * Get data before processed
  17087. * @return {module:echarts/data/List}
  17088. */
  17089. getRawData: function () {
  17090. return get(this, 'dataBeforeProcessed');
  17091. },
  17092. /**
  17093. * Coord dimension to data dimension.
  17094. *
  17095. * By default the result is the same as dimensions of series data.
  17096. * But in some series data dimensions are different from coord dimensions (i.e.
  17097. * candlestick and boxplot). Override this method to handle those cases.
  17098. *
  17099. * Coord dimension to data dimension can be one-to-many
  17100. *
  17101. * @param {string} coordDim
  17102. * @return {Array.<string>} dimensions on the axis.
  17103. */
  17104. coordDimToDataDim: function (coordDim) {
  17105. return coordDimToDataDim(this.getData(), coordDim);
  17106. },
  17107. /**
  17108. * Convert data dimension to coord dimension.
  17109. *
  17110. * @param {string|number} dataDim
  17111. * @return {string}
  17112. */
  17113. dataDimToCoordDim: function (dataDim) {
  17114. return dataDimToCoordDim(this.getData(), dataDim);
  17115. },
  17116. /**
  17117. * Get base axis if has coordinate system and has axis.
  17118. * By default use coordSys.getBaseAxis();
  17119. * Can be overrided for some chart.
  17120. * @return {type} description
  17121. */
  17122. getBaseAxis: function () {
  17123. var coordSys = this.coordinateSystem;
  17124. return coordSys && coordSys.getBaseAxis && coordSys.getBaseAxis();
  17125. },
  17126. // FIXME
  17127. /**
  17128. * Default tooltip formatter
  17129. *
  17130. * @param {number} dataIndex
  17131. * @param {boolean} [multipleSeries=false]
  17132. * @param {number} [dataType]
  17133. */
  17134. formatTooltip: function (dataIndex, multipleSeries, dataType) {
  17135. function formatArrayValue(value) {
  17136. var vertially = reduce(value, function (vertially, val, idx) {
  17137. var dimItem = data.getDimensionInfo(idx);
  17138. return vertially |= dimItem && dimItem.tooltip !== false && dimItem.tooltipName != null;
  17139. }, 0);
  17140. var result = [];
  17141. var tooltipDims = otherDimToDataDim(data, 'tooltip');
  17142. tooltipDims.length
  17143. ? each$1(tooltipDims, function (dimIdx) {
  17144. setEachItem(data.get(dimIdx, dataIndex), dimIdx);
  17145. })
  17146. // By default, all dims is used on tooltip.
  17147. : each$1(value, setEachItem);
  17148. function setEachItem(val, dimIdx) {
  17149. var dimInfo = data.getDimensionInfo(dimIdx);
  17150. // If `dimInfo.tooltip` is not set, show tooltip.
  17151. if (!dimInfo || dimInfo.otherDims.tooltip === false) {
  17152. return;
  17153. }
  17154. var dimType = dimInfo.type;
  17155. var valStr = (vertially ? '- ' + (dimInfo.tooltipName || dimInfo.name) + ': ' : '')
  17156. + (dimType === 'ordinal'
  17157. ? val + ''
  17158. : dimType === 'time'
  17159. ? (multipleSeries ? '' : formatTime('yyyy/MM/dd hh:mm:ss', val))
  17160. : addCommas(val)
  17161. );
  17162. valStr && result.push(encodeHTML(valStr));
  17163. }
  17164. return (vertially ? '<br/>' : '') + result.join(vertially ? '<br/>' : ', ');
  17165. }
  17166. var data = get(this, 'data');
  17167. var value = this.getRawValue(dataIndex);
  17168. var formattedValue = isArray(value)
  17169. ? formatArrayValue(value) : encodeHTML(addCommas(value));
  17170. var name = data.getName(dataIndex);
  17171. var color = data.getItemVisual(dataIndex, 'color');
  17172. if (isObject(color) && color.colorStops) {
  17173. color = (color.colorStops[0] || {}).color;
  17174. }
  17175. color = color || 'transparent';
  17176. var colorEl = getTooltipMarker(color);
  17177. var seriesName = this.name;
  17178. // FIXME
  17179. if (seriesName === '\0-') {
  17180. // Not show '-'
  17181. seriesName = '';
  17182. }
  17183. seriesName = seriesName
  17184. ? encodeHTML(seriesName) + (!multipleSeries ? '<br/>' : ': ')
  17185. : '';
  17186. return !multipleSeries
  17187. ? seriesName + colorEl
  17188. + (name
  17189. ? encodeHTML(name) + ': ' + formattedValue
  17190. : formattedValue
  17191. )
  17192. : colorEl + seriesName + formattedValue;
  17193. },
  17194. /**
  17195. * @return {boolean}
  17196. */
  17197. isAnimationEnabled: function () {
  17198. if (env$1.node) {
  17199. return false;
  17200. }
  17201. var animationEnabled = this.getShallow('animation');
  17202. if (animationEnabled) {
  17203. if (this.getData().count() > this.getShallow('animationThreshold')) {
  17204. animationEnabled = false;
  17205. }
  17206. }
  17207. return animationEnabled;
  17208. },
  17209. restoreData: function () {
  17210. set$1(this, 'data', get(this, 'dataBeforeProcessed').cloneShallow());
  17211. },
  17212. getColorFromPalette: function (name, scope) {
  17213. var ecModel = this.ecModel;
  17214. // PENDING
  17215. var color = colorPaletteMixin.getColorFromPalette.call(this, name, scope);
  17216. if (!color) {
  17217. color = ecModel.getColorFromPalette(name, scope);
  17218. }
  17219. return color;
  17220. },
  17221. /**
  17222. * Get data indices for show tooltip content. See tooltip.
  17223. * @abstract
  17224. * @param {Array.<string>|string} dim
  17225. * @param {Array.<number>} value
  17226. * @param {module:echarts/coord/single/SingleAxis} baseAxis
  17227. * @return {Object} {dataIndices, nestestValue}.
  17228. */
  17229. getAxisTooltipData: null,
  17230. /**
  17231. * See tooltip.
  17232. * @abstract
  17233. * @param {number} dataIndex
  17234. * @return {Array.<number>} Point of tooltip. null/undefined can be returned.
  17235. */
  17236. getTooltipPosition: null
  17237. });
  17238. mixin(SeriesModel, dataFormatMixin);
  17239. mixin(SeriesModel, colorPaletteMixin);
  17240. var Component = function () {
  17241. /**
  17242. * @type {module:zrender/container/Group}
  17243. * @readOnly
  17244. */
  17245. this.group = new Group();
  17246. /**
  17247. * @type {string}
  17248. * @readOnly
  17249. */
  17250. this.uid = getUID('viewComponent');
  17251. };
  17252. Component.prototype = {
  17253. constructor: Component,
  17254. init: function (ecModel, api) {},
  17255. render: function (componentModel, ecModel, api, payload) {},
  17256. dispose: function () {}
  17257. };
  17258. var componentProto = Component.prototype;
  17259. componentProto.updateView
  17260. = componentProto.updateLayout
  17261. = componentProto.updateVisual
  17262. = function (seriesModel, ecModel, api, payload) {
  17263. // Do nothing;
  17264. };
  17265. // Enable Component.extend.
  17266. enableClassExtend(Component);
  17267. // Enable capability of registerClass, getClass, hasClass, registerSubTypeDefaulter and so on.
  17268. enableClassManagement(Component, {registerWhenExtend: true});
  17269. function Chart() {
  17270. /**
  17271. * @type {module:zrender/container/Group}
  17272. * @readOnly
  17273. */
  17274. this.group = new Group();
  17275. /**
  17276. * @type {string}
  17277. * @readOnly
  17278. */
  17279. this.uid = getUID('viewChart');
  17280. }
  17281. Chart.prototype = {
  17282. type: 'chart',
  17283. /**
  17284. * Init the chart
  17285. * @param {module:echarts/model/Global} ecModel
  17286. * @param {module:echarts/ExtensionAPI} api
  17287. */
  17288. init: function (ecModel, api) {},
  17289. /**
  17290. * Render the chart
  17291. * @param {module:echarts/model/Series} seriesModel
  17292. * @param {module:echarts/model/Global} ecModel
  17293. * @param {module:echarts/ExtensionAPI} api
  17294. * @param {Object} payload
  17295. */
  17296. render: function (seriesModel, ecModel, api, payload) {},
  17297. /**
  17298. * Highlight series or specified data item
  17299. * @param {module:echarts/model/Series} seriesModel
  17300. * @param {module:echarts/model/Global} ecModel
  17301. * @param {module:echarts/ExtensionAPI} api
  17302. * @param {Object} payload
  17303. */
  17304. highlight: function (seriesModel, ecModel, api, payload) {
  17305. toggleHighlight(seriesModel.getData(), payload, 'emphasis');
  17306. },
  17307. /**
  17308. * Downplay series or specified data item
  17309. * @param {module:echarts/model/Series} seriesModel
  17310. * @param {module:echarts/model/Global} ecModel
  17311. * @param {module:echarts/ExtensionAPI} api
  17312. * @param {Object} payload
  17313. */
  17314. downplay: function (seriesModel, ecModel, api, payload) {
  17315. toggleHighlight(seriesModel.getData(), payload, 'normal');
  17316. },
  17317. /**
  17318. * Remove self
  17319. * @param {module:echarts/model/Global} ecModel
  17320. * @param {module:echarts/ExtensionAPI} api
  17321. */
  17322. remove: function (ecModel, api) {
  17323. this.group.removeAll();
  17324. },
  17325. /**
  17326. * Dispose self
  17327. * @param {module:echarts/model/Global} ecModel
  17328. * @param {module:echarts/ExtensionAPI} api
  17329. */
  17330. dispose: function () {}
  17331. /**
  17332. * The view contains the given point.
  17333. * @interface
  17334. * @param {Array.<number>} point
  17335. * @return {boolean}
  17336. */
  17337. // containPoint: function () {}
  17338. };
  17339. var chartProto = Chart.prototype;
  17340. chartProto.updateView
  17341. = chartProto.updateLayout
  17342. = chartProto.updateVisual
  17343. = function (seriesModel, ecModel, api, payload) {
  17344. this.render(seriesModel, ecModel, api, payload);
  17345. };
  17346. /**
  17347. * Set state of single element
  17348. * @param {module:zrender/Element} el
  17349. * @param {string} state
  17350. */
  17351. function elSetState(el, state) {
  17352. if (el) {
  17353. el.trigger(state);
  17354. if (el.type === 'group') {
  17355. for (var i = 0; i < el.childCount(); i++) {
  17356. elSetState(el.childAt(i), state);
  17357. }
  17358. }
  17359. }
  17360. }
  17361. /**
  17362. * @param {module:echarts/data/List} data
  17363. * @param {Object} payload
  17364. * @param {string} state 'normal'|'emphasis'
  17365. * @inner
  17366. */
  17367. function toggleHighlight(data, payload, state) {
  17368. var dataIndex = queryDataIndex(data, payload);
  17369. if (dataIndex != null) {
  17370. each$1(normalizeToArray(dataIndex), function (dataIdx) {
  17371. elSetState(data.getItemGraphicEl(dataIdx), state);
  17372. });
  17373. }
  17374. else {
  17375. data.eachItemGraphicEl(function (el) {
  17376. elSetState(el, state);
  17377. });
  17378. }
  17379. }
  17380. // Enable Chart.extend.
  17381. enableClassExtend(Chart, ['dispose']);
  17382. // Add capability of registerClass, getClass, hasClass, registerSubTypeDefaulter and so on.
  17383. enableClassManagement(Chart, {registerWhenExtend: true});
  17384. /**
  17385. * @public
  17386. * @param {(Function)} fn
  17387. * @param {number} [delay=0] Unit: ms.
  17388. * @param {boolean} [debounce=false]
  17389. * true: If call interval less than `delay`, only the last call works.
  17390. * false: If call interval less than `delay, call works on fixed rate.
  17391. * @return {(Function)} throttled fn.
  17392. */
  17393. function throttle(fn, delay, debounce) {
  17394. var currCall;
  17395. var lastCall = 0;
  17396. var lastExec = 0;
  17397. var timer = null;
  17398. var diff;
  17399. var scope;
  17400. var args;
  17401. var debounceNextCall;
  17402. delay = delay || 0;
  17403. function exec() {
  17404. lastExec = (new Date()).getTime();
  17405. timer = null;
  17406. fn.apply(scope, args || []);
  17407. }
  17408. var cb = function () {
  17409. currCall = (new Date()).getTime();
  17410. scope = this;
  17411. args = arguments;
  17412. var thisDelay = debounceNextCall || delay;
  17413. var thisDebounce = debounceNextCall || debounce;
  17414. debounceNextCall = null;
  17415. diff = currCall - (thisDebounce ? lastCall : lastExec) - thisDelay;
  17416. clearTimeout(timer);
  17417. if (thisDebounce) {
  17418. timer = setTimeout(exec, thisDelay);
  17419. }
  17420. else {
  17421. if (diff >= 0) {
  17422. exec();
  17423. }
  17424. else {
  17425. timer = setTimeout(exec, -diff);
  17426. }
  17427. }
  17428. lastCall = currCall;
  17429. };
  17430. /**
  17431. * Clear throttle.
  17432. * @public
  17433. */
  17434. cb.clear = function () {
  17435. if (timer) {
  17436. clearTimeout(timer);
  17437. timer = null;
  17438. }
  17439. };
  17440. /**
  17441. * Enable debounce once.
  17442. */
  17443. cb.debounceNextCall = function (debounceDelay) {
  17444. debounceNextCall = debounceDelay;
  17445. };
  17446. return cb;
  17447. }
  17448. /**
  17449. * Create throttle method or update throttle rate.
  17450. *
  17451. * @example
  17452. * ComponentView.prototype.render = function () {
  17453. * ...
  17454. * throttle.createOrUpdate(
  17455. * this,
  17456. * '_dispatchAction',
  17457. * this.model.get('throttle'),
  17458. * 'fixRate'
  17459. * );
  17460. * };
  17461. * ComponentView.prototype.remove = function () {
  17462. * throttle.clear(this, '_dispatchAction');
  17463. * };
  17464. * ComponentView.prototype.dispose = function () {
  17465. * throttle.clear(this, '_dispatchAction');
  17466. * };
  17467. *
  17468. * @public
  17469. * @param {Object} obj
  17470. * @param {string} fnAttr
  17471. * @param {number} [rate]
  17472. * @param {string} [throttleType='fixRate'] 'fixRate' or 'debounce'
  17473. * @return {Function} throttled function.
  17474. */
  17475. /**
  17476. * Clear throttle. Example see throttle.createOrUpdate.
  17477. *
  17478. * @public
  17479. * @param {Object} obj
  17480. * @param {string} fnAttr
  17481. */
  17482. var seriesColor = function (ecModel) {
  17483. function encodeColor(seriesModel) {
  17484. var colorAccessPath = (seriesModel.visualColorAccessPath || 'itemStyle.normal.color').split('.');
  17485. var data = seriesModel.getData();
  17486. var color = seriesModel.get(colorAccessPath) // Set in itemStyle
  17487. || seriesModel.getColorFromPalette(seriesModel.get('name')); // Default color
  17488. // FIXME Set color function or use the platte color
  17489. data.setVisual('color', color);
  17490. // Only visible series has each data be visual encoded
  17491. if (!ecModel.isSeriesFiltered(seriesModel)) {
  17492. if (typeof color === 'function' && !(color instanceof Gradient)) {
  17493. data.each(function (idx) {
  17494. data.setItemVisual(
  17495. idx, 'color', color(seriesModel.getDataParams(idx))
  17496. );
  17497. });
  17498. }
  17499. // itemStyle in each data item
  17500. data.each(function (idx) {
  17501. var itemModel = data.getItemModel(idx);
  17502. var color = itemModel.get(colorAccessPath, true);
  17503. if (color != null) {
  17504. data.setItemVisual(idx, 'color', color);
  17505. }
  17506. });
  17507. }
  17508. }
  17509. ecModel.eachRawSeries(encodeColor);
  17510. };
  17511. var PI$1 = Math.PI;
  17512. /**
  17513. * @param {module:echarts/ExtensionAPI} api
  17514. * @param {Object} [opts]
  17515. * @param {string} [opts.text]
  17516. * @param {string} [opts.color]
  17517. * @param {string} [opts.textColor]
  17518. * @return {module:zrender/Element}
  17519. */
  17520. var loadingDefault = function (api, opts) {
  17521. opts = opts || {};
  17522. defaults(opts, {
  17523. text: 'loading',
  17524. color: '#c23531',
  17525. textColor: '#000',
  17526. maskColor: 'rgba(255, 255, 255, 0.8)',
  17527. zlevel: 0
  17528. });
  17529. var mask = new Rect({
  17530. style: {
  17531. fill: opts.maskColor
  17532. },
  17533. zlevel: opts.zlevel,
  17534. z: 10000
  17535. });
  17536. var arc = new Arc({
  17537. shape: {
  17538. startAngle: -PI$1 / 2,
  17539. endAngle: -PI$1 / 2 + 0.1,
  17540. r: 10
  17541. },
  17542. style: {
  17543. stroke: opts.color,
  17544. lineCap: 'round',
  17545. lineWidth: 5
  17546. },
  17547. zlevel: opts.zlevel,
  17548. z: 10001
  17549. });
  17550. var labelRect = new Rect({
  17551. style: {
  17552. fill: 'none',
  17553. text: opts.text,
  17554. textPosition: 'right',
  17555. textDistance: 10,
  17556. textFill: opts.textColor
  17557. },
  17558. zlevel: opts.zlevel,
  17559. z: 10001
  17560. });
  17561. arc.animateShape(true)
  17562. .when(1000, {
  17563. endAngle: PI$1 * 3 / 2
  17564. })
  17565. .start('circularInOut');
  17566. arc.animateShape(true)
  17567. .when(1000, {
  17568. startAngle: PI$1 * 3 / 2
  17569. })
  17570. .delay(300)
  17571. .start('circularInOut');
  17572. var group = new Group();
  17573. group.add(arc);
  17574. group.add(labelRect);
  17575. group.add(mask);
  17576. // Inject resize
  17577. group.resize = function () {
  17578. var cx = api.getWidth() / 2;
  17579. var cy = api.getHeight() / 2;
  17580. arc.setShape({
  17581. cx: cx,
  17582. cy: cy
  17583. });
  17584. var r = arc.shape.r;
  17585. labelRect.setShape({
  17586. x: cx - r,
  17587. y: cy - r,
  17588. width: r * 2,
  17589. height: r * 2
  17590. });
  17591. mask.setShape({
  17592. x: 0,
  17593. y: 0,
  17594. width: api.getWidth(),
  17595. height: api.getHeight()
  17596. });
  17597. };
  17598. group.resize();
  17599. return group;
  17600. };
  17601. /*!
  17602. * ECharts, a javascript interactive chart library.
  17603. *
  17604. * Copyright (c) 2015, Baidu Inc.
  17605. * All rights reserved.
  17606. *
  17607. * LICENSE
  17608. * https://github.com/ecomfe/echarts/blob/master/LICENSE.txt
  17609. */
  17610. var each = each$1;
  17611. var parseClassType = ComponentModel.parseClassType;
  17612. var version = '3.8.5';
  17613. var dependencies = {
  17614. zrender: '3.7.4'
  17615. };
  17616. var PRIORITY_PROCESSOR_FILTER = 1000;
  17617. var PRIORITY_PROCESSOR_STATISTIC = 5000;
  17618. var PRIORITY_VISUAL_LAYOUT = 1000;
  17619. var PRIORITY_VISUAL_GLOBAL = 2000;
  17620. var PRIORITY_VISUAL_CHART = 3000;
  17621. var PRIORITY_VISUAL_COMPONENT = 4000;
  17622. // FIXME
  17623. // necessary?
  17624. var PRIORITY_VISUAL_BRUSH = 5000;
  17625. var PRIORITY = {
  17626. PROCESSOR: {
  17627. FILTER: PRIORITY_PROCESSOR_FILTER,
  17628. STATISTIC: PRIORITY_PROCESSOR_STATISTIC
  17629. },
  17630. VISUAL: {
  17631. LAYOUT: PRIORITY_VISUAL_LAYOUT,
  17632. GLOBAL: PRIORITY_VISUAL_GLOBAL,
  17633. CHART: PRIORITY_VISUAL_CHART,
  17634. COMPONENT: PRIORITY_VISUAL_COMPONENT,
  17635. BRUSH: PRIORITY_VISUAL_BRUSH
  17636. }
  17637. };
  17638. // Main process have three entries: `setOption`, `dispatchAction` and `resize`,
  17639. // where they must not be invoked nestedly, except the only case: invoke
  17640. // dispatchAction with updateMethod "none" in main process.
  17641. // This flag is used to carry out this rule.
  17642. // All events will be triggered out side main process (i.e. when !this[IN_MAIN_PROCESS]).
  17643. var IN_MAIN_PROCESS = '__flagInMainProcess';
  17644. var HAS_GRADIENT_OR_PATTERN_BG = '__hasGradientOrPatternBg';
  17645. var OPTION_UPDATED = '__optionUpdated';
  17646. var ACTION_REG = /^[a-zA-Z0-9_]+$/;
  17647. function createRegisterEventWithLowercaseName(method) {
  17648. return function (eventName, handler, context) {
  17649. // Event name is all lowercase
  17650. eventName = eventName && eventName.toLowerCase();
  17651. Eventful.prototype[method].call(this, eventName, handler, context);
  17652. };
  17653. }
  17654. /**
  17655. * @module echarts~MessageCenter
  17656. */
  17657. function MessageCenter() {
  17658. Eventful.call(this);
  17659. }
  17660. MessageCenter.prototype.on = createRegisterEventWithLowercaseName('on');
  17661. MessageCenter.prototype.off = createRegisterEventWithLowercaseName('off');
  17662. MessageCenter.prototype.one = createRegisterEventWithLowercaseName('one');
  17663. mixin(MessageCenter, Eventful);
  17664. /**
  17665. * @module echarts~ECharts
  17666. */
  17667. function ECharts(dom, theme, opts) {
  17668. opts = opts || {};
  17669. // Get theme by name
  17670. if (typeof theme === 'string') {
  17671. theme = themeStorage[theme];
  17672. }
  17673. /**
  17674. * @type {string}
  17675. */
  17676. this.id;
  17677. /**
  17678. * Group id
  17679. * @type {string}
  17680. */
  17681. this.group;
  17682. /**
  17683. * @type {HTMLElement}
  17684. * @private
  17685. */
  17686. this._dom = dom;
  17687. var defaultRenderer = 'canvas';
  17688. if (__DEV__) {
  17689. defaultRenderer = (
  17690. typeof window === 'undefined' ? global : window
  17691. ).__ECHARTS__DEFAULT__RENDERER__ || defaultRenderer;
  17692. }
  17693. /**
  17694. * @type {module:zrender/ZRender}
  17695. * @private
  17696. */
  17697. var zr = this._zr = init$1(dom, {
  17698. renderer: opts.renderer || defaultRenderer,
  17699. devicePixelRatio: opts.devicePixelRatio,
  17700. width: opts.width,
  17701. height: opts.height
  17702. });
  17703. /**
  17704. * Expect 60 pfs.
  17705. * @type {Function}
  17706. * @private
  17707. */
  17708. this._throttledZrFlush = throttle(bind(zr.flush, zr), 17);
  17709. var theme = clone(theme);
  17710. theme && backwardCompat(theme, true);
  17711. /**
  17712. * @type {Object}
  17713. * @private
  17714. */
  17715. this._theme = theme;
  17716. /**
  17717. * @type {Array.<module:echarts/view/Chart>}
  17718. * @private
  17719. */
  17720. this._chartsViews = [];
  17721. /**
  17722. * @type {Object.<string, module:echarts/view/Chart>}
  17723. * @private
  17724. */
  17725. this._chartsMap = {};
  17726. /**
  17727. * @type {Array.<module:echarts/view/Component>}
  17728. * @private
  17729. */
  17730. this._componentsViews = [];
  17731. /**
  17732. * @type {Object.<string, module:echarts/view/Component>}
  17733. * @private
  17734. */
  17735. this._componentsMap = {};
  17736. /**
  17737. * @type {module:echarts/CoordinateSystem}
  17738. * @private
  17739. */
  17740. this._coordSysMgr = new CoordinateSystemManager();
  17741. /**
  17742. * @type {module:echarts/ExtensionAPI}
  17743. * @private
  17744. */
  17745. this._api = createExtensionAPI(this);
  17746. Eventful.call(this);
  17747. /**
  17748. * @type {module:echarts~MessageCenter}
  17749. * @private
  17750. */
  17751. this._messageCenter = new MessageCenter();
  17752. // Init mouse events
  17753. this._initEvents();
  17754. // In case some people write `window.onresize = chart.resize`
  17755. this.resize = bind(this.resize, this);
  17756. // Can't dispatch action during rendering procedure
  17757. this._pendingActions = [];
  17758. // Sort on demand
  17759. function prioritySortFunc(a, b) {
  17760. return a.prio - b.prio;
  17761. }
  17762. sort(visualFuncs, prioritySortFunc);
  17763. sort(dataProcessorFuncs, prioritySortFunc);
  17764. zr.animation.on('frame', this._onframe, this);
  17765. // ECharts instance can be used as value.
  17766. setAsPrimitive(this);
  17767. }
  17768. var echartsProto = ECharts.prototype;
  17769. echartsProto._onframe = function () {
  17770. // Lazy update
  17771. if (this[OPTION_UPDATED]) {
  17772. var silent = this[OPTION_UPDATED].silent;
  17773. this[IN_MAIN_PROCESS] = true;
  17774. updateMethods.prepareAndUpdate.call(this);
  17775. this[IN_MAIN_PROCESS] = false;
  17776. this[OPTION_UPDATED] = false;
  17777. flushPendingActions.call(this, silent);
  17778. triggerUpdatedEvent.call(this, silent);
  17779. }
  17780. };
  17781. /**
  17782. * @return {HTMLElement}
  17783. */
  17784. echartsProto.getDom = function () {
  17785. return this._dom;
  17786. };
  17787. /**
  17788. * @return {module:zrender~ZRender}
  17789. */
  17790. echartsProto.getZr = function () {
  17791. return this._zr;
  17792. };
  17793. /**
  17794. * Usage:
  17795. * chart.setOption(option, notMerge, lazyUpdate);
  17796. * chart.setOption(option, {
  17797. * notMerge: ...,
  17798. * lazyUpdate: ...,
  17799. * silent: ...
  17800. * });
  17801. *
  17802. * @param {Object} option
  17803. * @param {Object|boolean} [opts] opts or notMerge.
  17804. * @param {boolean} [opts.notMerge=false]
  17805. * @param {boolean} [opts.lazyUpdate=false] Useful when setOption frequently.
  17806. */
  17807. echartsProto.setOption = function (option, notMerge, lazyUpdate) {
  17808. if (__DEV__) {
  17809. assert(!this[IN_MAIN_PROCESS], '`setOption` should not be called during main process.');
  17810. }
  17811. var silent;
  17812. if (isObject(notMerge)) {
  17813. lazyUpdate = notMerge.lazyUpdate;
  17814. silent = notMerge.silent;
  17815. notMerge = notMerge.notMerge;
  17816. }
  17817. this[IN_MAIN_PROCESS] = true;
  17818. if (!this._model || notMerge) {
  17819. var optionManager = new OptionManager(this._api);
  17820. var theme = this._theme;
  17821. var ecModel = this._model = new GlobalModel(null, null, theme, optionManager);
  17822. ecModel.init(null, null, theme, optionManager);
  17823. }
  17824. this._model.setOption(option, optionPreprocessorFuncs);
  17825. if (lazyUpdate) {
  17826. this[OPTION_UPDATED] = {silent: silent};
  17827. this[IN_MAIN_PROCESS] = false;
  17828. }
  17829. else {
  17830. updateMethods.prepareAndUpdate.call(this);
  17831. // Ensure zr refresh sychronously, and then pixel in canvas can be
  17832. // fetched after `setOption`.
  17833. this._zr.flush();
  17834. this[OPTION_UPDATED] = false;
  17835. this[IN_MAIN_PROCESS] = false;
  17836. flushPendingActions.call(this, silent);
  17837. triggerUpdatedEvent.call(this, silent);
  17838. }
  17839. };
  17840. /**
  17841. * @DEPRECATED
  17842. */
  17843. echartsProto.setTheme = function () {
  17844. console.log('ECharts#setTheme() is DEPRECATED in ECharts 3.0');
  17845. };
  17846. /**
  17847. * @return {module:echarts/model/Global}
  17848. */
  17849. echartsProto.getModel = function () {
  17850. return this._model;
  17851. };
  17852. /**
  17853. * @return {Object}
  17854. */
  17855. echartsProto.getOption = function () {
  17856. return this._model && this._model.getOption();
  17857. };
  17858. /**
  17859. * @return {number}
  17860. */
  17861. echartsProto.getWidth = function () {
  17862. return this._zr.getWidth();
  17863. };
  17864. /**
  17865. * @return {number}
  17866. */
  17867. echartsProto.getHeight = function () {
  17868. return this._zr.getHeight();
  17869. };
  17870. /**
  17871. * @return {number}
  17872. */
  17873. echartsProto.getDevicePixelRatio = function () {
  17874. return this._zr.painter.dpr || window.devicePixelRatio || 1;
  17875. };
  17876. /**
  17877. * Get canvas which has all thing rendered
  17878. * @param {Object} opts
  17879. * @param {string} [opts.backgroundColor]
  17880. * @return {string}
  17881. */
  17882. echartsProto.getRenderedCanvas = function (opts) {
  17883. if (!env$1.canvasSupported) {
  17884. return;
  17885. }
  17886. opts = opts || {};
  17887. opts.pixelRatio = opts.pixelRatio || 1;
  17888. opts.backgroundColor = opts.backgroundColor
  17889. || this._model.get('backgroundColor');
  17890. var zr = this._zr;
  17891. var list = zr.storage.getDisplayList();
  17892. // Stop animations
  17893. each$1(list, function (el) {
  17894. el.stopAnimation(true);
  17895. });
  17896. return zr.painter.getRenderedCanvas(opts);
  17897. };
  17898. /**
  17899. * Get svg data url
  17900. * @return {string}
  17901. */
  17902. echartsProto.getSvgDataUrl = function () {
  17903. if (!env$1.svgSupported) {
  17904. return;
  17905. }
  17906. var zr = this._zr;
  17907. var list = zr.storage.getDisplayList();
  17908. // Stop animations
  17909. each$1(list, function (el) {
  17910. el.stopAnimation(true);
  17911. });
  17912. return zr.painter.pathToSvg();
  17913. };
  17914. /**
  17915. * @return {string}
  17916. * @param {Object} opts
  17917. * @param {string} [opts.type='png']
  17918. * @param {string} [opts.pixelRatio=1]
  17919. * @param {string} [opts.backgroundColor]
  17920. * @param {string} [opts.excludeComponents]
  17921. */
  17922. echartsProto.getDataURL = function (opts) {
  17923. opts = opts || {};
  17924. var excludeComponents = opts.excludeComponents;
  17925. var ecModel = this._model;
  17926. var excludesComponentViews = [];
  17927. var self = this;
  17928. each(excludeComponents, function (componentType) {
  17929. ecModel.eachComponent({
  17930. mainType: componentType
  17931. }, function (component) {
  17932. var view = self._componentsMap[component.__viewId];
  17933. if (!view.group.ignore) {
  17934. excludesComponentViews.push(view);
  17935. view.group.ignore = true;
  17936. }
  17937. });
  17938. });
  17939. var url = this._zr.painter.getType() === 'svg'
  17940. ? this.getSvgDataUrl()
  17941. : this.getRenderedCanvas(opts).toDataURL(
  17942. 'image/' + (opts && opts.type || 'png')
  17943. );
  17944. each(excludesComponentViews, function (view) {
  17945. view.group.ignore = false;
  17946. });
  17947. return url;
  17948. };
  17949. /**
  17950. * @return {string}
  17951. * @param {Object} opts
  17952. * @param {string} [opts.type='png']
  17953. * @param {string} [opts.pixelRatio=1]
  17954. * @param {string} [opts.backgroundColor]
  17955. */
  17956. echartsProto.getConnectedDataURL = function (opts) {
  17957. if (!env$1.canvasSupported) {
  17958. return;
  17959. }
  17960. var groupId = this.group;
  17961. var mathMin = Math.min;
  17962. var mathMax = Math.max;
  17963. var MAX_NUMBER = Infinity;
  17964. if (connectedGroups[groupId]) {
  17965. var left = MAX_NUMBER;
  17966. var top = MAX_NUMBER;
  17967. var right = -MAX_NUMBER;
  17968. var bottom = -MAX_NUMBER;
  17969. var canvasList = [];
  17970. var dpr = (opts && opts.pixelRatio) || 1;
  17971. each$1(instances, function (chart, id) {
  17972. if (chart.group === groupId) {
  17973. var canvas = chart.getRenderedCanvas(
  17974. clone(opts)
  17975. );
  17976. var boundingRect = chart.getDom().getBoundingClientRect();
  17977. left = mathMin(boundingRect.left, left);
  17978. top = mathMin(boundingRect.top, top);
  17979. right = mathMax(boundingRect.right, right);
  17980. bottom = mathMax(boundingRect.bottom, bottom);
  17981. canvasList.push({
  17982. dom: canvas,
  17983. left: boundingRect.left,
  17984. top: boundingRect.top
  17985. });
  17986. }
  17987. });
  17988. left *= dpr;
  17989. top *= dpr;
  17990. right *= dpr;
  17991. bottom *= dpr;
  17992. var width = right - left;
  17993. var height = bottom - top;
  17994. var targetCanvas = createCanvas();
  17995. targetCanvas.width = width;
  17996. targetCanvas.height = height;
  17997. var zr = init$1(targetCanvas);
  17998. each(canvasList, function (item) {
  17999. var img = new ZImage({
  18000. style: {
  18001. x: item.left * dpr - left,
  18002. y: item.top * dpr - top,
  18003. image: item.dom
  18004. }
  18005. });
  18006. zr.add(img);
  18007. });
  18008. zr.refreshImmediately();
  18009. return targetCanvas.toDataURL('image/' + (opts && opts.type || 'png'));
  18010. }
  18011. else {
  18012. return this.getDataURL(opts);
  18013. }
  18014. };
  18015. /**
  18016. * Convert from logical coordinate system to pixel coordinate system.
  18017. * See CoordinateSystem#convertToPixel.
  18018. * @param {string|Object} finder
  18019. * If string, e.g., 'geo', means {geoIndex: 0}.
  18020. * If Object, could contain some of these properties below:
  18021. * {
  18022. * seriesIndex / seriesId / seriesName,
  18023. * geoIndex / geoId, geoName,
  18024. * bmapIndex / bmapId / bmapName,
  18025. * xAxisIndex / xAxisId / xAxisName,
  18026. * yAxisIndex / yAxisId / yAxisName,
  18027. * gridIndex / gridId / gridName,
  18028. * ... (can be extended)
  18029. * }
  18030. * @param {Array|number} value
  18031. * @return {Array|number} result
  18032. */
  18033. echartsProto.convertToPixel = curry(doConvertPixel, 'convertToPixel');
  18034. /**
  18035. * Convert from pixel coordinate system to logical coordinate system.
  18036. * See CoordinateSystem#convertFromPixel.
  18037. * @param {string|Object} finder
  18038. * If string, e.g., 'geo', means {geoIndex: 0}.
  18039. * If Object, could contain some of these properties below:
  18040. * {
  18041. * seriesIndex / seriesId / seriesName,
  18042. * geoIndex / geoId / geoName,
  18043. * bmapIndex / bmapId / bmapName,
  18044. * xAxisIndex / xAxisId / xAxisName,
  18045. * yAxisIndex / yAxisId / yAxisName
  18046. * gridIndex / gridId / gridName,
  18047. * ... (can be extended)
  18048. * }
  18049. * @param {Array|number} value
  18050. * @return {Array|number} result
  18051. */
  18052. echartsProto.convertFromPixel = curry(doConvertPixel, 'convertFromPixel');
  18053. function doConvertPixel(methodName, finder, value) {
  18054. var ecModel = this._model;
  18055. var coordSysList = this._coordSysMgr.getCoordinateSystems();
  18056. var result;
  18057. finder = parseFinder(ecModel, finder);
  18058. for (var i = 0; i < coordSysList.length; i++) {
  18059. var coordSys = coordSysList[i];
  18060. if (coordSys[methodName]
  18061. && (result = coordSys[methodName](ecModel, finder, value)) != null
  18062. ) {
  18063. return result;
  18064. }
  18065. }
  18066. if (__DEV__) {
  18067. console.warn(
  18068. 'No coordinate system that supports ' + methodName + ' found by the given finder.'
  18069. );
  18070. }
  18071. }
  18072. /**
  18073. * Is the specified coordinate systems or components contain the given pixel point.
  18074. * @param {string|Object} finder
  18075. * If string, e.g., 'geo', means {geoIndex: 0}.
  18076. * If Object, could contain some of these properties below:
  18077. * {
  18078. * seriesIndex / seriesId / seriesName,
  18079. * geoIndex / geoId / geoName,
  18080. * bmapIndex / bmapId / bmapName,
  18081. * xAxisIndex / xAxisId / xAxisName,
  18082. * yAxisIndex / yAxisId / yAxisName,
  18083. * gridIndex / gridId / gridName,
  18084. * ... (can be extended)
  18085. * }
  18086. * @param {Array|number} value
  18087. * @return {boolean} result
  18088. */
  18089. echartsProto.containPixel = function (finder, value) {
  18090. var ecModel = this._model;
  18091. var result;
  18092. finder = parseFinder(ecModel, finder);
  18093. each$1(finder, function (models, key) {
  18094. key.indexOf('Models') >= 0 && each$1(models, function (model) {
  18095. var coordSys = model.coordinateSystem;
  18096. if (coordSys && coordSys.containPoint) {
  18097. result |= !!coordSys.containPoint(value);
  18098. }
  18099. else if (key === 'seriesModels') {
  18100. var view = this._chartsMap[model.__viewId];
  18101. if (view && view.containPoint) {
  18102. result |= view.containPoint(value, model);
  18103. }
  18104. else {
  18105. if (__DEV__) {
  18106. console.warn(key + ': ' + (view
  18107. ? 'The found component do not support containPoint.'
  18108. : 'No view mapping to the found component.'
  18109. ));
  18110. }
  18111. }
  18112. }
  18113. else {
  18114. if (__DEV__) {
  18115. console.warn(key + ': containPoint is not supported');
  18116. }
  18117. }
  18118. }, this);
  18119. }, this);
  18120. return !!result;
  18121. };
  18122. /**
  18123. * Get visual from series or data.
  18124. * @param {string|Object} finder
  18125. * If string, e.g., 'series', means {seriesIndex: 0}.
  18126. * If Object, could contain some of these properties below:
  18127. * {
  18128. * seriesIndex / seriesId / seriesName,
  18129. * dataIndex / dataIndexInside
  18130. * }
  18131. * If dataIndex is not specified, series visual will be fetched,
  18132. * but not data item visual.
  18133. * If all of seriesIndex, seriesId, seriesName are not specified,
  18134. * visual will be fetched from first series.
  18135. * @param {string} visualType 'color', 'symbol', 'symbolSize'
  18136. */
  18137. echartsProto.getVisual = function (finder, visualType) {
  18138. var ecModel = this._model;
  18139. finder = parseFinder(ecModel, finder, {defaultMainType: 'series'});
  18140. var seriesModel = finder.seriesModel;
  18141. if (__DEV__) {
  18142. if (!seriesModel) {
  18143. console.warn('There is no specified seires model');
  18144. }
  18145. }
  18146. var data = seriesModel.getData();
  18147. var dataIndexInside = finder.hasOwnProperty('dataIndexInside')
  18148. ? finder.dataIndexInside
  18149. : finder.hasOwnProperty('dataIndex')
  18150. ? data.indexOfRawIndex(finder.dataIndex)
  18151. : null;
  18152. return dataIndexInside != null
  18153. ? data.getItemVisual(dataIndexInside, visualType)
  18154. : data.getVisual(visualType);
  18155. };
  18156. /**
  18157. * Get view of corresponding component model
  18158. * @param {module:echarts/model/Component} componentModel
  18159. * @return {module:echarts/view/Component}
  18160. */
  18161. echartsProto.getViewOfComponentModel = function (componentModel) {
  18162. return this._componentsMap[componentModel.__viewId];
  18163. };
  18164. /**
  18165. * Get view of corresponding series model
  18166. * @param {module:echarts/model/Series} seriesModel
  18167. * @return {module:echarts/view/Chart}
  18168. */
  18169. echartsProto.getViewOfSeriesModel = function (seriesModel) {
  18170. return this._chartsMap[seriesModel.__viewId];
  18171. };
  18172. var updateMethods = {
  18173. /**
  18174. * @param {Object} payload
  18175. * @private
  18176. */
  18177. update: function (payload) {
  18178. // console.profile && console.profile('update');
  18179. var ecModel = this._model;
  18180. var api = this._api;
  18181. var coordSysMgr = this._coordSysMgr;
  18182. var zr = this._zr;
  18183. // update before setOption
  18184. if (!ecModel) {
  18185. return;
  18186. }
  18187. // Fixme First time update ?
  18188. ecModel.restoreData();
  18189. // TODO
  18190. // Save total ecModel here for undo/redo (after restoring data and before processing data).
  18191. // Undo (restoration of total ecModel) can be carried out in 'action' or outside API call.
  18192. // Create new coordinate system each update
  18193. // In LineView may save the old coordinate system and use it to get the orignal point
  18194. coordSysMgr.create(this._model, this._api);
  18195. processData.call(this, ecModel, api);
  18196. stackSeriesData.call(this, ecModel);
  18197. coordSysMgr.update(ecModel, api);
  18198. doVisualEncoding.call(this, ecModel, payload);
  18199. doRender.call(this, ecModel, payload);
  18200. // Set background
  18201. var backgroundColor = ecModel.get('backgroundColor') || 'transparent';
  18202. var painter = zr.painter;
  18203. // TODO all use clearColor ?
  18204. if (painter.isSingleCanvas && painter.isSingleCanvas()) {
  18205. zr.configLayer(0, {
  18206. clearColor: backgroundColor
  18207. });
  18208. }
  18209. else {
  18210. // In IE8
  18211. if (!env$1.canvasSupported) {
  18212. var colorArr = parse(backgroundColor);
  18213. backgroundColor = stringify(colorArr, 'rgb');
  18214. if (colorArr[3] === 0) {
  18215. backgroundColor = 'transparent';
  18216. }
  18217. }
  18218. if (backgroundColor.colorStops || backgroundColor.image) {
  18219. // Gradient background
  18220. // FIXME Fixed layer?
  18221. zr.configLayer(0, {
  18222. clearColor: backgroundColor
  18223. });
  18224. this[HAS_GRADIENT_OR_PATTERN_BG] = true;
  18225. this._dom.style.background = 'transparent';
  18226. }
  18227. else {
  18228. if (this[HAS_GRADIENT_OR_PATTERN_BG]) {
  18229. zr.configLayer(0, {
  18230. clearColor: null
  18231. });
  18232. }
  18233. this[HAS_GRADIENT_OR_PATTERN_BG] = false;
  18234. this._dom.style.background = backgroundColor;
  18235. }
  18236. }
  18237. each(postUpdateFuncs, function (func) {
  18238. func(ecModel, api);
  18239. });
  18240. // console.profile && console.profileEnd('update');
  18241. },
  18242. /**
  18243. * @param {Object} payload
  18244. * @private
  18245. */
  18246. updateView: function (payload) {
  18247. var ecModel = this._model;
  18248. // update before setOption
  18249. if (!ecModel) {
  18250. return;
  18251. }
  18252. ecModel.eachSeries(function (seriesModel) {
  18253. seriesModel.getData().clearAllVisual();
  18254. });
  18255. doVisualEncoding.call(this, ecModel, payload);
  18256. invokeUpdateMethod.call(this, 'updateView', ecModel, payload);
  18257. },
  18258. /**
  18259. * @param {Object} payload
  18260. * @private
  18261. */
  18262. updateVisual: function (payload) {
  18263. var ecModel = this._model;
  18264. // update before setOption
  18265. if (!ecModel) {
  18266. return;
  18267. }
  18268. ecModel.eachSeries(function (seriesModel) {
  18269. seriesModel.getData().clearAllVisual();
  18270. });
  18271. doVisualEncoding.call(this, ecModel, payload, true);
  18272. invokeUpdateMethod.call(this, 'updateVisual', ecModel, payload);
  18273. },
  18274. /**
  18275. * @param {Object} payload
  18276. * @private
  18277. */
  18278. updateLayout: function (payload) {
  18279. var ecModel = this._model;
  18280. // update before setOption
  18281. if (!ecModel) {
  18282. return;
  18283. }
  18284. doLayout.call(this, ecModel, payload);
  18285. invokeUpdateMethod.call(this, 'updateLayout', ecModel, payload);
  18286. },
  18287. /**
  18288. * @param {Object} payload
  18289. * @private
  18290. */
  18291. prepareAndUpdate: function (payload) {
  18292. var ecModel = this._model;
  18293. prepareView.call(this, 'component', ecModel);
  18294. prepareView.call(this, 'chart', ecModel);
  18295. updateMethods.update.call(this, payload);
  18296. }
  18297. };
  18298. /**
  18299. * @private
  18300. */
  18301. function updateDirectly(ecIns, method, payload, mainType, subType) {
  18302. var ecModel = ecIns._model;
  18303. // broadcast
  18304. if (!mainType) {
  18305. each(ecIns._componentsViews.concat(ecIns._chartsViews), callView);
  18306. return;
  18307. }
  18308. var query = {};
  18309. query[mainType + 'Id'] = payload[mainType + 'Id'];
  18310. query[mainType + 'Index'] = payload[mainType + 'Index'];
  18311. query[mainType + 'Name'] = payload[mainType + 'Name'];
  18312. var condition = {mainType: mainType, query: query};
  18313. subType && (condition.subType = subType); // subType may be '' by parseClassType;
  18314. // If dispatchAction before setOption, do nothing.
  18315. ecModel && ecModel.eachComponent(condition, function (model, index) {
  18316. callView(ecIns[
  18317. mainType === 'series' ? '_chartsMap' : '_componentsMap'
  18318. ][model.__viewId]);
  18319. }, ecIns);
  18320. function callView(view) {
  18321. view && view.__alive && view[method] && view[method](
  18322. view.__model, ecModel, ecIns._api, payload
  18323. );
  18324. }
  18325. }
  18326. /**
  18327. * Resize the chart
  18328. * @param {Object} opts
  18329. * @param {number} [opts.width] Can be 'auto' (the same as null/undefined)
  18330. * @param {number} [opts.height] Can be 'auto' (the same as null/undefined)
  18331. * @param {boolean} [opts.silent=false]
  18332. */
  18333. echartsProto.resize = function (opts) {
  18334. if (__DEV__) {
  18335. assert(!this[IN_MAIN_PROCESS], '`resize` should not be called during main process.');
  18336. }
  18337. this[IN_MAIN_PROCESS] = true;
  18338. this._zr.resize(opts);
  18339. var optionChanged = this._model && this._model.resetOption('media');
  18340. var updateMethod = optionChanged ? 'prepareAndUpdate' : 'update';
  18341. updateMethods[updateMethod].call(this);
  18342. // Resize loading effect
  18343. this._loadingFX && this._loadingFX.resize();
  18344. this[IN_MAIN_PROCESS] = false;
  18345. var silent = opts && opts.silent;
  18346. flushPendingActions.call(this, silent);
  18347. triggerUpdatedEvent.call(this, silent);
  18348. };
  18349. /**
  18350. * Show loading effect
  18351. * @param {string} [name='default']
  18352. * @param {Object} [cfg]
  18353. */
  18354. echartsProto.showLoading = function (name, cfg) {
  18355. if (isObject(name)) {
  18356. cfg = name;
  18357. name = '';
  18358. }
  18359. name = name || 'default';
  18360. this.hideLoading();
  18361. if (!loadingEffects[name]) {
  18362. if (__DEV__) {
  18363. console.warn('Loading effects ' + name + ' not exists.');
  18364. }
  18365. return;
  18366. }
  18367. var el = loadingEffects[name](this._api, cfg);
  18368. var zr = this._zr;
  18369. this._loadingFX = el;
  18370. zr.add(el);
  18371. };
  18372. /**
  18373. * Hide loading effect
  18374. */
  18375. echartsProto.hideLoading = function () {
  18376. this._loadingFX && this._zr.remove(this._loadingFX);
  18377. this._loadingFX = null;
  18378. };
  18379. /**
  18380. * @param {Object} eventObj
  18381. * @return {Object}
  18382. */
  18383. echartsProto.makeActionFromEvent = function (eventObj) {
  18384. var payload = extend({}, eventObj);
  18385. payload.type = eventActionMap[eventObj.type];
  18386. return payload;
  18387. };
  18388. /**
  18389. * @pubilc
  18390. * @param {Object} payload
  18391. * @param {string} [payload.type] Action type
  18392. * @param {Object|boolean} [opt] If pass boolean, means opt.silent
  18393. * @param {boolean} [opt.silent=false] Whether trigger events.
  18394. * @param {boolean} [opt.flush=undefined]
  18395. * true: Flush immediately, and then pixel in canvas can be fetched
  18396. * immediately. Caution: it might affect performance.
  18397. * false: Not not flush.
  18398. * undefined: Auto decide whether perform flush.
  18399. */
  18400. echartsProto.dispatchAction = function (payload, opt) {
  18401. if (!isObject(opt)) {
  18402. opt = {silent: !!opt};
  18403. }
  18404. if (!actions[payload.type]) {
  18405. return;
  18406. }
  18407. // Avoid dispatch action before setOption. Especially in `connect`.
  18408. if (!this._model) {
  18409. return;
  18410. }
  18411. // May dispatchAction in rendering procedure
  18412. if (this[IN_MAIN_PROCESS]) {
  18413. this._pendingActions.push(payload);
  18414. return;
  18415. }
  18416. doDispatchAction.call(this, payload, opt.silent);
  18417. if (opt.flush) {
  18418. this._zr.flush(true);
  18419. }
  18420. else if (opt.flush !== false && env$1.browser.weChat) {
  18421. // In WeChat embeded browser, `requestAnimationFrame` and `setInterval`
  18422. // hang when sliding page (on touch event), which cause that zr does not
  18423. // refresh util user interaction finished, which is not expected.
  18424. // But `dispatchAction` may be called too frequently when pan on touch
  18425. // screen, which impacts performance if do not throttle them.
  18426. this._throttledZrFlush();
  18427. }
  18428. flushPendingActions.call(this, opt.silent);
  18429. triggerUpdatedEvent.call(this, opt.silent);
  18430. };
  18431. function doDispatchAction(payload, silent) {
  18432. var payloadType = payload.type;
  18433. var escapeConnect = payload.escapeConnect;
  18434. var actionWrap = actions[payloadType];
  18435. var actionInfo = actionWrap.actionInfo;
  18436. var cptType = (actionInfo.update || 'update').split(':');
  18437. var updateMethod = cptType.pop();
  18438. cptType = cptType[0] != null && parseClassType(cptType[0]);
  18439. this[IN_MAIN_PROCESS] = true;
  18440. var payloads = [payload];
  18441. var batched = false;
  18442. // Batch action
  18443. if (payload.batch) {
  18444. batched = true;
  18445. payloads = map(payload.batch, function (item) {
  18446. item = defaults(extend({}, item), payload);
  18447. item.batch = null;
  18448. return item;
  18449. });
  18450. }
  18451. var eventObjBatch = [];
  18452. var eventObj;
  18453. var isHighDown = payloadType === 'highlight' || payloadType === 'downplay';
  18454. each(payloads, function (batchItem) {
  18455. // Action can specify the event by return it.
  18456. eventObj = actionWrap.action(batchItem, this._model, this._api);
  18457. // Emit event outside
  18458. eventObj = eventObj || extend({}, batchItem);
  18459. // Convert type to eventType
  18460. eventObj.type = actionInfo.event || eventObj.type;
  18461. eventObjBatch.push(eventObj);
  18462. // light update does not perform data process, layout and visual.
  18463. if (isHighDown) {
  18464. // method, payload, mainType, subType
  18465. updateDirectly(this, updateMethod, batchItem, 'series');
  18466. }
  18467. else if (cptType) {
  18468. updateDirectly(this, updateMethod, batchItem, cptType.main, cptType.sub);
  18469. }
  18470. }, this);
  18471. if (updateMethod !== 'none' && !isHighDown && !cptType) {
  18472. // Still dirty
  18473. if (this[OPTION_UPDATED]) {
  18474. // FIXME Pass payload ?
  18475. updateMethods.prepareAndUpdate.call(this, payload);
  18476. this[OPTION_UPDATED] = false;
  18477. }
  18478. else {
  18479. updateMethods[updateMethod].call(this, payload);
  18480. }
  18481. }
  18482. // Follow the rule of action batch
  18483. if (batched) {
  18484. eventObj = {
  18485. type: actionInfo.event || payloadType,
  18486. escapeConnect: escapeConnect,
  18487. batch: eventObjBatch
  18488. };
  18489. }
  18490. else {
  18491. eventObj = eventObjBatch[0];
  18492. }
  18493. this[IN_MAIN_PROCESS] = false;
  18494. !silent && this._messageCenter.trigger(eventObj.type, eventObj);
  18495. }
  18496. function flushPendingActions(silent) {
  18497. var pendingActions = this._pendingActions;
  18498. while (pendingActions.length) {
  18499. var payload = pendingActions.shift();
  18500. doDispatchAction.call(this, payload, silent);
  18501. }
  18502. }
  18503. function triggerUpdatedEvent(silent) {
  18504. !silent && this.trigger('updated');
  18505. }
  18506. /**
  18507. * Register event
  18508. * @method
  18509. */
  18510. echartsProto.on = createRegisterEventWithLowercaseName('on');
  18511. echartsProto.off = createRegisterEventWithLowercaseName('off');
  18512. echartsProto.one = createRegisterEventWithLowercaseName('one');
  18513. /**
  18514. * @param {string} methodName
  18515. * @private
  18516. */
  18517. function invokeUpdateMethod(methodName, ecModel, payload) {
  18518. var api = this._api;
  18519. // Update all components
  18520. each(this._componentsViews, function (component) {
  18521. var componentModel = component.__model;
  18522. component[methodName](componentModel, ecModel, api, payload);
  18523. updateZ(componentModel, component);
  18524. }, this);
  18525. // Upate all charts
  18526. ecModel.eachSeries(function (seriesModel, idx) {
  18527. var chart = this._chartsMap[seriesModel.__viewId];
  18528. chart[methodName](seriesModel, ecModel, api, payload);
  18529. updateZ(seriesModel, chart);
  18530. updateProgressiveAndBlend(seriesModel, chart);
  18531. }, this);
  18532. // If use hover layer
  18533. updateHoverLayerStatus(this._zr, ecModel);
  18534. // Post render
  18535. each(postUpdateFuncs, function (func) {
  18536. func(ecModel, api);
  18537. });
  18538. }
  18539. /**
  18540. * Prepare view instances of charts and components
  18541. * @param {module:echarts/model/Global} ecModel
  18542. * @private
  18543. */
  18544. function prepareView(type, ecModel) {
  18545. var isComponent = type === 'component';
  18546. var viewList = isComponent ? this._componentsViews : this._chartsViews;
  18547. var viewMap = isComponent ? this._componentsMap : this._chartsMap;
  18548. var zr = this._zr;
  18549. for (var i = 0; i < viewList.length; i++) {
  18550. viewList[i].__alive = false;
  18551. }
  18552. ecModel[isComponent ? 'eachComponent' : 'eachSeries'](function (componentType, model) {
  18553. if (isComponent) {
  18554. if (componentType === 'series') {
  18555. return;
  18556. }
  18557. }
  18558. else {
  18559. model = componentType;
  18560. }
  18561. // Consider: id same and type changed.
  18562. var viewId = '_ec_' + model.id + '_' + model.type;
  18563. var view = viewMap[viewId];
  18564. if (!view) {
  18565. var classType = parseClassType(model.type);
  18566. var Clazz = isComponent
  18567. ? Component.getClass(classType.main, classType.sub)
  18568. : Chart.getClass(classType.sub);
  18569. if (Clazz) {
  18570. view = new Clazz();
  18571. view.init(ecModel, this._api);
  18572. viewMap[viewId] = view;
  18573. viewList.push(view);
  18574. zr.add(view.group);
  18575. }
  18576. else {
  18577. // Error
  18578. return;
  18579. }
  18580. }
  18581. model.__viewId = view.__id = viewId;
  18582. view.__alive = true;
  18583. view.__model = model;
  18584. view.group.__ecComponentInfo = {
  18585. mainType: model.mainType,
  18586. index: model.componentIndex
  18587. };
  18588. }, this);
  18589. for (var i = 0; i < viewList.length;) {
  18590. var view = viewList[i];
  18591. if (!view.__alive) {
  18592. zr.remove(view.group);
  18593. view.dispose(ecModel, this._api);
  18594. viewList.splice(i, 1);
  18595. delete viewMap[view.__id];
  18596. view.__id = view.group.__ecComponentInfo = null;
  18597. }
  18598. else {
  18599. i++;
  18600. }
  18601. }
  18602. }
  18603. /**
  18604. * Processor data in each series
  18605. *
  18606. * @param {module:echarts/model/Global} ecModel
  18607. * @private
  18608. */
  18609. function processData(ecModel, api) {
  18610. each(dataProcessorFuncs, function (process) {
  18611. process.func(ecModel, api);
  18612. });
  18613. }
  18614. /**
  18615. * @private
  18616. */
  18617. function stackSeriesData(ecModel) {
  18618. var stackedDataMap = {};
  18619. ecModel.eachSeries(function (series) {
  18620. var stack = series.get('stack');
  18621. var data = series.getData();
  18622. if (stack && data.type === 'list') {
  18623. var previousStack = stackedDataMap[stack];
  18624. // Avoid conflict with Object.prototype
  18625. if (stackedDataMap.hasOwnProperty(stack) && previousStack) {
  18626. data.stackedOn = previousStack;
  18627. }
  18628. stackedDataMap[stack] = data;
  18629. }
  18630. });
  18631. }
  18632. /**
  18633. * Layout before each chart render there series, special visual encoding stage
  18634. *
  18635. * @param {module:echarts/model/Global} ecModel
  18636. * @private
  18637. */
  18638. function doLayout(ecModel, payload) {
  18639. var api = this._api;
  18640. each(visualFuncs, function (visual) {
  18641. if (visual.isLayout) {
  18642. visual.func(ecModel, api, payload);
  18643. }
  18644. });
  18645. }
  18646. /**
  18647. * Encode visual infomation from data after data processing
  18648. *
  18649. * @param {module:echarts/model/Global} ecModel
  18650. * @param {object} layout
  18651. * @param {boolean} [excludesLayout]
  18652. * @private
  18653. */
  18654. function doVisualEncoding(ecModel, payload, excludesLayout) {
  18655. var api = this._api;
  18656. ecModel.clearColorPalette();
  18657. ecModel.eachSeries(function (seriesModel) {
  18658. seriesModel.clearColorPalette();
  18659. });
  18660. each(visualFuncs, function (visual) {
  18661. (!excludesLayout || !visual.isLayout)
  18662. && visual.func(ecModel, api, payload);
  18663. });
  18664. }
  18665. /**
  18666. * Render each chart and component
  18667. * @private
  18668. */
  18669. function doRender(ecModel, payload) {
  18670. var api = this._api;
  18671. // Render all components
  18672. each(this._componentsViews, function (componentView) {
  18673. var componentModel = componentView.__model;
  18674. componentView.render(componentModel, ecModel, api, payload);
  18675. updateZ(componentModel, componentView);
  18676. }, this);
  18677. each(this._chartsViews, function (chart) {
  18678. chart.__alive = false;
  18679. }, this);
  18680. // Render all charts
  18681. ecModel.eachSeries(function (seriesModel, idx) {
  18682. var chartView = this._chartsMap[seriesModel.__viewId];
  18683. chartView.__alive = true;
  18684. chartView.render(seriesModel, ecModel, api, payload);
  18685. chartView.group.silent = !!seriesModel.get('silent');
  18686. updateZ(seriesModel, chartView);
  18687. updateProgressiveAndBlend(seriesModel, chartView);
  18688. }, this);
  18689. // If use hover layer
  18690. updateHoverLayerStatus(this._zr, ecModel);
  18691. // Remove groups of unrendered charts
  18692. each(this._chartsViews, function (chart) {
  18693. if (!chart.__alive) {
  18694. chart.remove(ecModel, api);
  18695. }
  18696. }, this);
  18697. }
  18698. var MOUSE_EVENT_NAMES = [
  18699. 'click', 'dblclick', 'mouseover', 'mouseout', 'mousemove',
  18700. 'mousedown', 'mouseup', 'globalout', 'contextmenu'
  18701. ];
  18702. /**
  18703. * @private
  18704. */
  18705. echartsProto._initEvents = function () {
  18706. each(MOUSE_EVENT_NAMES, function (eveName) {
  18707. this._zr.on(eveName, function (e) {
  18708. var ecModel = this.getModel();
  18709. var el = e.target;
  18710. var params;
  18711. // no e.target when 'globalout'.
  18712. if (eveName === 'globalout') {
  18713. params = {};
  18714. }
  18715. else if (el && el.dataIndex != null) {
  18716. var dataModel = el.dataModel || ecModel.getSeriesByIndex(el.seriesIndex);
  18717. params = dataModel && dataModel.getDataParams(el.dataIndex, el.dataType) || {};
  18718. }
  18719. // If element has custom eventData of components
  18720. else if (el && el.eventData) {
  18721. params = extend({}, el.eventData);
  18722. }
  18723. if (params) {
  18724. params.event = e;
  18725. params.type = eveName;
  18726. this.trigger(eveName, params);
  18727. }
  18728. }, this);
  18729. }, this);
  18730. each(eventActionMap, function (actionType, eventType) {
  18731. this._messageCenter.on(eventType, function (event) {
  18732. this.trigger(eventType, event);
  18733. }, this);
  18734. }, this);
  18735. };
  18736. /**
  18737. * @return {boolean}
  18738. */
  18739. echartsProto.isDisposed = function () {
  18740. return this._disposed;
  18741. };
  18742. /**
  18743. * Clear
  18744. */
  18745. echartsProto.clear = function () {
  18746. this.setOption({ series: [] }, true);
  18747. };
  18748. /**
  18749. * Dispose instance
  18750. */
  18751. echartsProto.dispose = function () {
  18752. if (this._disposed) {
  18753. if (__DEV__) {
  18754. console.warn('Instance ' + this.id + ' has been disposed');
  18755. }
  18756. return;
  18757. }
  18758. this._disposed = true;
  18759. var api = this._api;
  18760. var ecModel = this._model;
  18761. each(this._componentsViews, function (component) {
  18762. component.dispose(ecModel, api);
  18763. });
  18764. each(this._chartsViews, function (chart) {
  18765. chart.dispose(ecModel, api);
  18766. });
  18767. // Dispose after all views disposed
  18768. this._zr.dispose();
  18769. delete instances[this.id];
  18770. };
  18771. mixin(ECharts, Eventful);
  18772. function updateHoverLayerStatus(zr, ecModel) {
  18773. var storage = zr.storage;
  18774. var elCount = 0;
  18775. storage.traverse(function (el) {
  18776. if (!el.isGroup) {
  18777. elCount++;
  18778. }
  18779. });
  18780. if (elCount > ecModel.get('hoverLayerThreshold') && !env$1.node) {
  18781. storage.traverse(function (el) {
  18782. if (!el.isGroup) {
  18783. el.useHoverLayer = true;
  18784. }
  18785. });
  18786. }
  18787. }
  18788. /**
  18789. * Update chart progressive and blend.
  18790. * @param {module:echarts/model/Series|module:echarts/model/Component} model
  18791. * @param {module:echarts/view/Component|module:echarts/view/Chart} view
  18792. */
  18793. function updateProgressiveAndBlend(seriesModel, chartView) {
  18794. // Progressive configuration
  18795. var elCount = 0;
  18796. chartView.group.traverse(function (el) {
  18797. if (el.type !== 'group' && !el.ignore) {
  18798. elCount++;
  18799. }
  18800. });
  18801. var frameDrawNum = +seriesModel.get('progressive');
  18802. var needProgressive = elCount > seriesModel.get('progressiveThreshold') && frameDrawNum && !env$1.node;
  18803. if (needProgressive) {
  18804. chartView.group.traverse(function (el) {
  18805. // FIXME marker and other components
  18806. if (!el.isGroup) {
  18807. el.progressive = needProgressive ?
  18808. Math.floor(elCount++ / frameDrawNum) : -1;
  18809. if (needProgressive) {
  18810. el.stopAnimation(true);
  18811. }
  18812. }
  18813. });
  18814. }
  18815. // Blend configration
  18816. var blendMode = seriesModel.get('blendMode') || null;
  18817. if (__DEV__) {
  18818. if (!env$1.canvasSupported && blendMode && blendMode !== 'source-over') {
  18819. console.warn('Only canvas support blendMode');
  18820. }
  18821. }
  18822. chartView.group.traverse(function (el) {
  18823. // FIXME marker and other components
  18824. if (!el.isGroup) {
  18825. el.setStyle('blend', blendMode);
  18826. }
  18827. });
  18828. }
  18829. /**
  18830. * @param {module:echarts/model/Series|module:echarts/model/Component} model
  18831. * @param {module:echarts/view/Component|module:echarts/view/Chart} view
  18832. */
  18833. function updateZ(model, view) {
  18834. var z = model.get('z');
  18835. var zlevel = model.get('zlevel');
  18836. // Set z and zlevel
  18837. view.group.traverse(function (el) {
  18838. if (el.type !== 'group') {
  18839. z != null && (el.z = z);
  18840. zlevel != null && (el.zlevel = zlevel);
  18841. }
  18842. });
  18843. }
  18844. function createExtensionAPI(ecInstance) {
  18845. var coordSysMgr = ecInstance._coordSysMgr;
  18846. return extend(new ExtensionAPI(ecInstance), {
  18847. // Inject methods
  18848. getCoordinateSystems: bind(
  18849. coordSysMgr.getCoordinateSystems, coordSysMgr
  18850. ),
  18851. getComponentByElement: function (el) {
  18852. while (el) {
  18853. var modelInfo = el.__ecComponentInfo;
  18854. if (modelInfo != null) {
  18855. return ecInstance._model.getComponent(modelInfo.mainType, modelInfo.index);
  18856. }
  18857. el = el.parent;
  18858. }
  18859. }
  18860. });
  18861. }
  18862. /**
  18863. * @type {Object} key: actionType.
  18864. * @inner
  18865. */
  18866. var actions = {};
  18867. /**
  18868. * Map eventType to actionType
  18869. * @type {Object}
  18870. */
  18871. var eventActionMap = {};
  18872. /**
  18873. * Data processor functions of each stage
  18874. * @type {Array.<Object.<string, Function>>}
  18875. * @inner
  18876. */
  18877. var dataProcessorFuncs = [];
  18878. /**
  18879. * @type {Array.<Function>}
  18880. * @inner
  18881. */
  18882. var optionPreprocessorFuncs = [];
  18883. /**
  18884. * @type {Array.<Function>}
  18885. * @inner
  18886. */
  18887. var postUpdateFuncs = [];
  18888. /**
  18889. * Visual encoding functions of each stage
  18890. * @type {Array.<Object.<string, Function>>}
  18891. * @inner
  18892. */
  18893. var visualFuncs = [];
  18894. /**
  18895. * Theme storage
  18896. * @type {Object.<key, Object>}
  18897. */
  18898. var themeStorage = {};
  18899. /**
  18900. * Loading effects
  18901. */
  18902. var loadingEffects = {};
  18903. var instances = {};
  18904. var connectedGroups = {};
  18905. var idBase = new Date() - 0;
  18906. var groupIdBase = new Date() - 0;
  18907. var DOM_ATTRIBUTE_KEY = '_echarts_instance_';
  18908. var mapDataStores = {};
  18909. function enableConnect(chart) {
  18910. var STATUS_PENDING = 0;
  18911. var STATUS_UPDATING = 1;
  18912. var STATUS_UPDATED = 2;
  18913. var STATUS_KEY = '__connectUpdateStatus';
  18914. function updateConnectedChartsStatus(charts, status) {
  18915. for (var i = 0; i < charts.length; i++) {
  18916. var otherChart = charts[i];
  18917. otherChart[STATUS_KEY] = status;
  18918. }
  18919. }
  18920. each$1(eventActionMap, function (actionType, eventType) {
  18921. chart._messageCenter.on(eventType, function (event) {
  18922. if (connectedGroups[chart.group] && chart[STATUS_KEY] !== STATUS_PENDING) {
  18923. if (event && event.escapeConnect) {
  18924. return;
  18925. }
  18926. var action = chart.makeActionFromEvent(event);
  18927. var otherCharts = [];
  18928. each$1(instances, function (otherChart) {
  18929. if (otherChart !== chart && otherChart.group === chart.group) {
  18930. otherCharts.push(otherChart);
  18931. }
  18932. });
  18933. updateConnectedChartsStatus(otherCharts, STATUS_PENDING);
  18934. each(otherCharts, function (otherChart) {
  18935. if (otherChart[STATUS_KEY] !== STATUS_UPDATING) {
  18936. otherChart.dispatchAction(action);
  18937. }
  18938. });
  18939. updateConnectedChartsStatus(otherCharts, STATUS_UPDATED);
  18940. }
  18941. });
  18942. });
  18943. }
  18944. /**
  18945. * @param {HTMLElement} dom
  18946. * @param {Object} [theme]
  18947. * @param {Object} opts
  18948. * @param {number} [opts.devicePixelRatio] Use window.devicePixelRatio by default
  18949. * @param {string} [opts.renderer] Currently only 'canvas' is supported.
  18950. * @param {number} [opts.width] Use clientWidth of the input `dom` by default.
  18951. * Can be 'auto' (the same as null/undefined)
  18952. * @param {number} [opts.height] Use clientHeight of the input `dom` by default.
  18953. * Can be 'auto' (the same as null/undefined)
  18954. */
  18955. function init(dom, theme, opts) {
  18956. if (__DEV__) {
  18957. // Check version
  18958. if ((version$1.replace('.', '') - 0) < (dependencies.zrender.replace('.', '') - 0)) {
  18959. throw new Error(
  18960. 'zrender/src ' + version$1
  18961. + ' is too old for ECharts ' + version
  18962. + '. Current version need ZRender '
  18963. + dependencies.zrender + '+'
  18964. );
  18965. }
  18966. if (!dom) {
  18967. throw new Error('Initialize failed: invalid dom.');
  18968. }
  18969. }
  18970. var existInstance = getInstanceByDom(dom);
  18971. if (existInstance) {
  18972. if (__DEV__) {
  18973. console.warn('There is a chart instance already initialized on the dom.');
  18974. }
  18975. return existInstance;
  18976. }
  18977. if (__DEV__) {
  18978. if (isDom(dom)
  18979. && dom.nodeName.toUpperCase() !== 'CANVAS'
  18980. && (
  18981. (!dom.clientWidth && (!opts || opts.width == null))
  18982. || (!dom.clientHeight && (!opts || opts.height == null))
  18983. )
  18984. ) {
  18985. console.warn('Can\'t get dom width or height');
  18986. }
  18987. }
  18988. var chart = new ECharts(dom, theme, opts);
  18989. chart.id = 'ec_' + idBase++;
  18990. instances[chart.id] = chart;
  18991. if (dom.setAttribute) {
  18992. dom.setAttribute(DOM_ATTRIBUTE_KEY, chart.id);
  18993. }
  18994. else {
  18995. dom[DOM_ATTRIBUTE_KEY] = chart.id;
  18996. }
  18997. enableConnect(chart);
  18998. return chart;
  18999. }
  19000. /**
  19001. * @return {string|Array.<module:echarts~ECharts>} groupId
  19002. */
  19003. function connect(groupId) {
  19004. // Is array of charts
  19005. if (isArray(groupId)) {
  19006. var charts = groupId;
  19007. groupId = null;
  19008. // If any chart has group
  19009. each$1(charts, function (chart) {
  19010. if (chart.group != null) {
  19011. groupId = chart.group;
  19012. }
  19013. });
  19014. groupId = groupId || ('g_' + groupIdBase++);
  19015. each$1(charts, function (chart) {
  19016. chart.group = groupId;
  19017. });
  19018. }
  19019. connectedGroups[groupId] = true;
  19020. return groupId;
  19021. }
  19022. /**
  19023. * @DEPRECATED
  19024. * @return {string} groupId
  19025. */
  19026. function disConnect(groupId) {
  19027. connectedGroups[groupId] = false;
  19028. }
  19029. /**
  19030. * @return {string} groupId
  19031. */
  19032. var disconnect = disConnect;
  19033. /**
  19034. * Dispose a chart instance
  19035. * @param {module:echarts~ECharts|HTMLDomElement|string} chart
  19036. */
  19037. function dispose(chart) {
  19038. if (typeof chart === 'string') {
  19039. chart = instances[chart];
  19040. }
  19041. else if (!(chart instanceof ECharts)){
  19042. // Try to treat as dom
  19043. chart = getInstanceByDom(chart);
  19044. }
  19045. if ((chart instanceof ECharts) && !chart.isDisposed()) {
  19046. chart.dispose();
  19047. }
  19048. }
  19049. /**
  19050. * @param {HTMLElement} dom
  19051. * @return {echarts~ECharts}
  19052. */
  19053. function getInstanceByDom(dom) {
  19054. var key;
  19055. if (dom.getAttribute) {
  19056. key = dom.getAttribute(DOM_ATTRIBUTE_KEY);
  19057. }
  19058. else {
  19059. key = dom[DOM_ATTRIBUTE_KEY];
  19060. }
  19061. return instances[key];
  19062. }
  19063. /**
  19064. * @param {string} key
  19065. * @return {echarts~ECharts}
  19066. */
  19067. function getInstanceById(key) {
  19068. return instances[key];
  19069. }
  19070. /**
  19071. * Register theme
  19072. */
  19073. function registerTheme(name, theme) {
  19074. themeStorage[name] = theme;
  19075. }
  19076. /**
  19077. * Register option preprocessor
  19078. * @param {Function} preprocessorFunc
  19079. */
  19080. function registerPreprocessor(preprocessorFunc) {
  19081. optionPreprocessorFuncs.push(preprocessorFunc);
  19082. }
  19083. /**
  19084. * @param {number} [priority=1000]
  19085. * @param {Function} processorFunc
  19086. */
  19087. function registerProcessor(priority, processorFunc) {
  19088. if (typeof priority === 'function') {
  19089. processorFunc = priority;
  19090. priority = PRIORITY_PROCESSOR_FILTER;
  19091. }
  19092. if (__DEV__) {
  19093. if (isNaN(priority)) {
  19094. throw new Error('Unkown processor priority');
  19095. }
  19096. }
  19097. dataProcessorFuncs.push({
  19098. prio: priority,
  19099. func: processorFunc
  19100. });
  19101. }
  19102. /**
  19103. * Register postUpdater
  19104. * @param {Function} postUpdateFunc
  19105. */
  19106. function registerPostUpdate(postUpdateFunc) {
  19107. postUpdateFuncs.push(postUpdateFunc);
  19108. }
  19109. /**
  19110. * Usage:
  19111. * registerAction('someAction', 'someEvent', function () { ... });
  19112. * registerAction('someAction', function () { ... });
  19113. * registerAction(
  19114. * {type: 'someAction', event: 'someEvent', update: 'updateView'},
  19115. * function () { ... }
  19116. * );
  19117. *
  19118. * @param {(string|Object)} actionInfo
  19119. * @param {string} actionInfo.type
  19120. * @param {string} [actionInfo.event]
  19121. * @param {string} [actionInfo.update]
  19122. * @param {string} [eventName]
  19123. * @param {Function} action
  19124. */
  19125. function registerAction(actionInfo, eventName, action) {
  19126. if (typeof eventName === 'function') {
  19127. action = eventName;
  19128. eventName = '';
  19129. }
  19130. var actionType = isObject(actionInfo)
  19131. ? actionInfo.type
  19132. : ([actionInfo, actionInfo = {
  19133. event: eventName
  19134. }][0]);
  19135. // Event name is all lowercase
  19136. actionInfo.event = (actionInfo.event || actionType).toLowerCase();
  19137. eventName = actionInfo.event;
  19138. // Validate action type and event name.
  19139. assert(ACTION_REG.test(actionType) && ACTION_REG.test(eventName));
  19140. if (!actions[actionType]) {
  19141. actions[actionType] = {action: action, actionInfo: actionInfo};
  19142. }
  19143. eventActionMap[eventName] = actionType;
  19144. }
  19145. /**
  19146. * @param {string} type
  19147. * @param {*} CoordinateSystem
  19148. */
  19149. function registerCoordinateSystem(type, CoordinateSystem$$1) {
  19150. CoordinateSystemManager.register(type, CoordinateSystem$$1);
  19151. }
  19152. /**
  19153. * Get dimensions of specified coordinate system.
  19154. * @param {string} type
  19155. * @return {Array.<string|Object>}
  19156. */
  19157. function getCoordinateSystemDimensions(type) {
  19158. var coordSysCreator = CoordinateSystemManager.get(type);
  19159. if (coordSysCreator) {
  19160. return coordSysCreator.getDimensionsInfo
  19161. ? coordSysCreator.getDimensionsInfo()
  19162. : coordSysCreator.dimensions.slice();
  19163. }
  19164. }
  19165. /**
  19166. * Layout is a special stage of visual encoding
  19167. * Most visual encoding like color are common for different chart
  19168. * But each chart has it's own layout algorithm
  19169. *
  19170. * @param {number} [priority=1000]
  19171. * @param {Function} layoutFunc
  19172. */
  19173. function registerLayout(priority, layoutFunc) {
  19174. if (typeof priority === 'function') {
  19175. layoutFunc = priority;
  19176. priority = PRIORITY_VISUAL_LAYOUT;
  19177. }
  19178. if (__DEV__) {
  19179. if (isNaN(priority)) {
  19180. throw new Error('Unkown layout priority');
  19181. }
  19182. }
  19183. visualFuncs.push({
  19184. prio: priority,
  19185. func: layoutFunc,
  19186. isLayout: true
  19187. });
  19188. }
  19189. /**
  19190. * @param {number} [priority=3000]
  19191. * @param {Function} visualFunc
  19192. */
  19193. function registerVisual(priority, visualFunc) {
  19194. if (typeof priority === 'function') {
  19195. visualFunc = priority;
  19196. priority = PRIORITY_VISUAL_CHART;
  19197. }
  19198. if (__DEV__) {
  19199. if (isNaN(priority)) {
  19200. throw new Error('Unkown visual priority');
  19201. }
  19202. }
  19203. visualFuncs.push({
  19204. prio: priority,
  19205. func: visualFunc
  19206. });
  19207. }
  19208. /**
  19209. * @param {string} name
  19210. */
  19211. function registerLoading(name, loadingFx) {
  19212. loadingEffects[name] = loadingFx;
  19213. }
  19214. /**
  19215. * @param {Object} opts
  19216. * @param {string} [superClass]
  19217. */
  19218. function extendComponentModel(opts/*, superClass*/) {
  19219. // var Clazz = ComponentModel;
  19220. // if (superClass) {
  19221. // var classType = parseClassType(superClass);
  19222. // Clazz = ComponentModel.getClass(classType.main, classType.sub, true);
  19223. // }
  19224. return ComponentModel.extend(opts);
  19225. }
  19226. /**
  19227. * @param {Object} opts
  19228. * @param {string} [superClass]
  19229. */
  19230. function extendComponentView(opts/*, superClass*/) {
  19231. // var Clazz = ComponentView;
  19232. // if (superClass) {
  19233. // var classType = parseClassType(superClass);
  19234. // Clazz = ComponentView.getClass(classType.main, classType.sub, true);
  19235. // }
  19236. return Component.extend(opts);
  19237. }
  19238. /**
  19239. * @param {Object} opts
  19240. * @param {string} [superClass]
  19241. */
  19242. function extendSeriesModel(opts/*, superClass*/) {
  19243. // var Clazz = SeriesModel;
  19244. // if (superClass) {
  19245. // superClass = 'series.' + superClass.replace('series.', '');
  19246. // var classType = parseClassType(superClass);
  19247. // Clazz = ComponentModel.getClass(classType.main, classType.sub, true);
  19248. // }
  19249. return SeriesModel.extend(opts);
  19250. }
  19251. /**
  19252. * @param {Object} opts
  19253. * @param {string} [superClass]
  19254. */
  19255. function extendChartView(opts/*, superClass*/) {
  19256. // var Clazz = ChartView;
  19257. // if (superClass) {
  19258. // superClass = superClass.replace('series.', '');
  19259. // var classType = parseClassType(superClass);
  19260. // Clazz = ChartView.getClass(classType.main, true);
  19261. // }
  19262. return Chart.extend(opts);
  19263. }
  19264. /**
  19265. * ZRender need a canvas context to do measureText.
  19266. * But in node environment canvas may be created by node-canvas.
  19267. * So we need to specify how to create a canvas instead of using document.createElement('canvas')
  19268. *
  19269. * Be careful of using it in the browser.
  19270. *
  19271. * @param {Function} creator
  19272. * @example
  19273. * var Canvas = require('canvas');
  19274. * var echarts = require('echarts');
  19275. * echarts.setCanvasCreator(function () {
  19276. * // Small size is enough.
  19277. * return new Canvas(32, 32);
  19278. * });
  19279. */
  19280. function setCanvasCreator(creator) {
  19281. $override('createCanvas', creator);
  19282. }
  19283. /**
  19284. * @param {string} mapName
  19285. * @param {Object|string} geoJson
  19286. * @param {Object} [specialAreas]
  19287. *
  19288. * @example
  19289. * $.get('USA.json', function (geoJson) {
  19290. * echarts.registerMap('USA', geoJson);
  19291. * // Or
  19292. * echarts.registerMap('USA', {
  19293. * geoJson: geoJson,
  19294. * specialAreas: {}
  19295. * })
  19296. * });
  19297. */
  19298. function registerMap(mapName, geoJson, specialAreas) {
  19299. if (geoJson.geoJson && !geoJson.features) {
  19300. specialAreas = geoJson.specialAreas;
  19301. geoJson = geoJson.geoJson;
  19302. }
  19303. if (typeof geoJson === 'string') {
  19304. geoJson = (typeof JSON !== 'undefined' && JSON.parse)
  19305. ? JSON.parse(geoJson) : (new Function('return (' + geoJson + ');'))();
  19306. }
  19307. mapDataStores[mapName] = {
  19308. geoJson: geoJson,
  19309. specialAreas: specialAreas
  19310. };
  19311. }
  19312. /**
  19313. * @param {string} mapName
  19314. * @return {Object}
  19315. */
  19316. function getMap(mapName) {
  19317. return mapDataStores[mapName];
  19318. }
  19319. registerVisual(PRIORITY_VISUAL_GLOBAL, seriesColor);
  19320. registerPreprocessor(backwardCompat);
  19321. registerLoading('default', loadingDefault);
  19322. // Default actions
  19323. registerAction({
  19324. type: 'highlight',
  19325. event: 'highlight',
  19326. update: 'highlight'
  19327. }, noop);
  19328. registerAction({
  19329. type: 'downplay',
  19330. event: 'downplay',
  19331. update: 'downplay'
  19332. }, noop);
  19333. // For backward compatibility, where the namespace `dataTool` will
  19334. // be mounted on `echarts` is the extension `dataTool` is imported.
  19335. var dataTool = {};
  19336. function defaultKeyGetter(item) {
  19337. return item;
  19338. }
  19339. /**
  19340. * @param {Array} oldArr
  19341. * @param {Array} newArr
  19342. * @param {Function} oldKeyGetter
  19343. * @param {Function} newKeyGetter
  19344. * @param {Object} [context] Can be visited by this.context in callback.
  19345. */
  19346. function DataDiffer(oldArr, newArr, oldKeyGetter, newKeyGetter, context) {
  19347. this._old = oldArr;
  19348. this._new = newArr;
  19349. this._oldKeyGetter = oldKeyGetter || defaultKeyGetter;
  19350. this._newKeyGetter = newKeyGetter || defaultKeyGetter;
  19351. this.context = context;
  19352. }
  19353. DataDiffer.prototype = {
  19354. constructor: DataDiffer,
  19355. /**
  19356. * Callback function when add a data
  19357. */
  19358. add: function (func) {
  19359. this._add = func;
  19360. return this;
  19361. },
  19362. /**
  19363. * Callback function when update a data
  19364. */
  19365. update: function (func) {
  19366. this._update = func;
  19367. return this;
  19368. },
  19369. /**
  19370. * Callback function when remove a data
  19371. */
  19372. remove: function (func) {
  19373. this._remove = func;
  19374. return this;
  19375. },
  19376. execute: function () {
  19377. var oldArr = this._old;
  19378. var newArr = this._new;
  19379. var oldDataIndexMap = {};
  19380. var newDataIndexMap = {};
  19381. var oldDataKeyArr = [];
  19382. var newDataKeyArr = [];
  19383. var i;
  19384. initIndexMap(oldArr, oldDataIndexMap, oldDataKeyArr, '_oldKeyGetter', this);
  19385. initIndexMap(newArr, newDataIndexMap, newDataKeyArr, '_newKeyGetter', this);
  19386. // Travel by inverted order to make sure order consistency
  19387. // when duplicate keys exists (consider newDataIndex.pop() below).
  19388. // For performance consideration, these code below do not look neat.
  19389. for (i = 0; i < oldArr.length; i++) {
  19390. var key = oldDataKeyArr[i];
  19391. var idx = newDataIndexMap[key];
  19392. // idx can never be empty array here. see 'set null' logic below.
  19393. if (idx != null) {
  19394. // Consider there is duplicate key (for example, use dataItem.name as key).
  19395. // We should make sure every item in newArr and oldArr can be visited.
  19396. var len = idx.length;
  19397. if (len) {
  19398. len === 1 && (newDataIndexMap[key] = null);
  19399. idx = idx.unshift();
  19400. }
  19401. else {
  19402. newDataIndexMap[key] = null;
  19403. }
  19404. this._update && this._update(idx, i);
  19405. }
  19406. else {
  19407. this._remove && this._remove(i);
  19408. }
  19409. }
  19410. for (var i = 0; i < newDataKeyArr.length; i++) {
  19411. var key = newDataKeyArr[i];
  19412. if (newDataIndexMap.hasOwnProperty(key)) {
  19413. var idx = newDataIndexMap[key];
  19414. if (idx == null) {
  19415. continue;
  19416. }
  19417. // idx can never be empty array here. see 'set null' logic above.
  19418. if (!idx.length) {
  19419. this._add && this._add(idx);
  19420. }
  19421. else {
  19422. for (var j = 0, len = idx.length; j < len; j++) {
  19423. this._add && this._add(idx[j]);
  19424. }
  19425. }
  19426. }
  19427. }
  19428. }
  19429. };
  19430. function initIndexMap(arr, map, keyArr, keyGetterName, dataDiffer) {
  19431. for (var i = 0; i < arr.length; i++) {
  19432. // Add prefix to avoid conflict with Object.prototype.
  19433. var key = '_ec_' + dataDiffer[keyGetterName](arr[i], i);
  19434. var existence = map[key];
  19435. if (existence == null) {
  19436. keyArr.push(key);
  19437. map[key] = i;
  19438. }
  19439. else {
  19440. if (!existence.length) {
  19441. map[key] = existence = [existence];
  19442. }
  19443. existence.push(i);
  19444. }
  19445. }
  19446. }
  19447. /**
  19448. * List for data storage
  19449. * @module echarts/data/List
  19450. */
  19451. var isObject$4 = isObject;
  19452. var UNDEFINED = 'undefined';
  19453. var globalObj = typeof window === UNDEFINED ? global : window;
  19454. var dataCtors = {
  19455. 'float': typeof globalObj.Float64Array === UNDEFINED
  19456. ? Array : globalObj.Float64Array,
  19457. 'int': typeof globalObj.Int32Array === UNDEFINED
  19458. ? Array : globalObj.Int32Array,
  19459. // Ordinal data type can be string or int
  19460. 'ordinal': Array,
  19461. 'number': Array,
  19462. 'time': Array
  19463. };
  19464. var TRANSFERABLE_PROPERTIES = [
  19465. 'stackedOn', 'hasItemOption', '_nameList', '_idList', '_rawData'
  19466. ];
  19467. function transferProperties(a, b) {
  19468. each$1(TRANSFERABLE_PROPERTIES.concat(b.__wrappedMethods || []), function (propName) {
  19469. if (b.hasOwnProperty(propName)) {
  19470. a[propName] = b[propName];
  19471. }
  19472. });
  19473. a.__wrappedMethods = b.__wrappedMethods;
  19474. }
  19475. function DefaultDataProvider(dataArray) {
  19476. this._array = dataArray || [];
  19477. }
  19478. DefaultDataProvider.prototype.pure = false;
  19479. DefaultDataProvider.prototype.count = function () {
  19480. return this._array.length;
  19481. };
  19482. DefaultDataProvider.prototype.getItem = function (idx) {
  19483. return this._array[idx];
  19484. };
  19485. /**
  19486. * @constructor
  19487. * @alias module:echarts/data/List
  19488. *
  19489. * @param {Array.<string|Object>} dimensions
  19490. * For example, ['someDimName', {name: 'someDimName', type: 'someDimType'}, ...].
  19491. * Dimensions should be concrete names like x, y, z, lng, lat, angle, radius
  19492. * @param {module:echarts/model/Model} hostModel
  19493. */
  19494. var List = function (dimensions, hostModel) {
  19495. dimensions = dimensions || ['x', 'y'];
  19496. var dimensionInfos = {};
  19497. var dimensionNames = [];
  19498. for (var i = 0; i < dimensions.length; i++) {
  19499. var dimensionName;
  19500. var dimensionInfo = {};
  19501. if (typeof dimensions[i] === 'string') {
  19502. dimensionName = dimensions[i];
  19503. dimensionInfo = {
  19504. name: dimensionName,
  19505. coordDim: dimensionName,
  19506. coordDimIndex: 0,
  19507. stackable: false,
  19508. // Type can be 'float', 'int', 'number'
  19509. // Default is number, Precision of float may not enough
  19510. type: 'number'
  19511. };
  19512. }
  19513. else {
  19514. dimensionInfo = dimensions[i];
  19515. dimensionName = dimensionInfo.name;
  19516. dimensionInfo.type = dimensionInfo.type || 'number';
  19517. if (!dimensionInfo.coordDim) {
  19518. dimensionInfo.coordDim = dimensionName;
  19519. dimensionInfo.coordDimIndex = 0;
  19520. }
  19521. }
  19522. dimensionInfo.otherDims = dimensionInfo.otherDims || {};
  19523. dimensionNames.push(dimensionName);
  19524. dimensionInfos[dimensionName] = dimensionInfo;
  19525. }
  19526. /**
  19527. * @readOnly
  19528. * @type {Array.<string>}
  19529. */
  19530. this.dimensions = dimensionNames;
  19531. /**
  19532. * Infomation of each data dimension, like data type.
  19533. * @type {Object}
  19534. */
  19535. this._dimensionInfos = dimensionInfos;
  19536. /**
  19537. * @type {module:echarts/model/Model}
  19538. */
  19539. this.hostModel = hostModel;
  19540. /**
  19541. * @type {module:echarts/model/Model}
  19542. */
  19543. this.dataType;
  19544. /**
  19545. * Indices stores the indices of data subset after filtered.
  19546. * This data subset will be used in chart.
  19547. * @type {Array.<number>}
  19548. * @readOnly
  19549. */
  19550. this.indices = [];
  19551. /**
  19552. * Data storage
  19553. * @type {Object.<key, TypedArray|Array>}
  19554. * @private
  19555. */
  19556. this._storage = {};
  19557. /**
  19558. * @type {Array.<string>}
  19559. */
  19560. this._nameList = [];
  19561. /**
  19562. * @type {Array.<string>}
  19563. */
  19564. this._idList = [];
  19565. /**
  19566. * Models of data option is stored sparse for optimizing memory cost
  19567. * @type {Array.<module:echarts/model/Model>}
  19568. * @private
  19569. */
  19570. this._optionModels = [];
  19571. /**
  19572. * @param {module:echarts/data/List}
  19573. */
  19574. this.stackedOn = null;
  19575. /**
  19576. * Global visual properties after visual coding
  19577. * @type {Object}
  19578. * @private
  19579. */
  19580. this._visual = {};
  19581. /**
  19582. * Globel layout properties.
  19583. * @type {Object}
  19584. * @private
  19585. */
  19586. this._layout = {};
  19587. /**
  19588. * Item visual properties after visual coding
  19589. * @type {Array.<Object>}
  19590. * @private
  19591. */
  19592. this._itemVisuals = [];
  19593. /**
  19594. * Item layout properties after layout
  19595. * @type {Array.<Object>}
  19596. * @private
  19597. */
  19598. this._itemLayouts = [];
  19599. /**
  19600. * Graphic elemnents
  19601. * @type {Array.<module:zrender/Element>}
  19602. * @private
  19603. */
  19604. this._graphicEls = [];
  19605. /**
  19606. * @type {Array.<Array|Object>}
  19607. * @private
  19608. */
  19609. this._rawData;
  19610. /**
  19611. * @type {Object}
  19612. * @private
  19613. */
  19614. this._extent;
  19615. };
  19616. var listProto = List.prototype;
  19617. listProto.type = 'list';
  19618. /**
  19619. * If each data item has it's own option
  19620. * @type {boolean}
  19621. */
  19622. listProto.hasItemOption = true;
  19623. /**
  19624. * Get dimension name
  19625. * @param {string|number} dim
  19626. * Dimension can be concrete names like x, y, z, lng, lat, angle, radius
  19627. * Or a ordinal number. For example getDimensionInfo(0) will return 'x' or 'lng' or 'radius'
  19628. * @return {string} Concrete dim name.
  19629. */
  19630. listProto.getDimension = function (dim) {
  19631. if (!isNaN(dim)) {
  19632. dim = this.dimensions[dim] || dim;
  19633. }
  19634. return dim;
  19635. };
  19636. /**
  19637. * Get type and stackable info of particular dimension
  19638. * @param {string|number} dim
  19639. * Dimension can be concrete names like x, y, z, lng, lat, angle, radius
  19640. * Or a ordinal number. For example getDimensionInfo(0) will return 'x' or 'lng' or 'radius'
  19641. */
  19642. listProto.getDimensionInfo = function (dim) {
  19643. return clone(this._dimensionInfos[this.getDimension(dim)]);
  19644. };
  19645. /**
  19646. * Initialize from data
  19647. * @param {Array.<Object|number|Array>} data
  19648. * @param {Array.<string>} [nameList]
  19649. * @param {Function} [dimValueGetter] (dataItem, dimName, dataIndex, dimIndex) => number
  19650. */
  19651. listProto.initData = function (data, nameList, dimValueGetter) {
  19652. data = data || [];
  19653. var isDataArray = isArray(data);
  19654. if (isDataArray) {
  19655. data = new DefaultDataProvider(data);
  19656. }
  19657. if (__DEV__) {
  19658. if (!isDataArray && (typeof data.getItem != 'function' || typeof data.count != 'function')) {
  19659. throw new Error('Inavlid data provider.');
  19660. }
  19661. }
  19662. this._rawData = data;
  19663. // Clear
  19664. var storage = this._storage = {};
  19665. var indices = this.indices = [];
  19666. var dimensions = this.dimensions;
  19667. var dimensionInfoMap = this._dimensionInfos;
  19668. var size = data.count();
  19669. var idList = [];
  19670. var nameRepeatCount = {};
  19671. var nameDimIdx;
  19672. nameList = nameList || [];
  19673. // Init storage
  19674. for (var i = 0; i < dimensions.length; i++) {
  19675. var dimInfo = dimensionInfoMap[dimensions[i]];
  19676. dimInfo.otherDims.itemName === 0 && (nameDimIdx = i);
  19677. var DataCtor = dataCtors[dimInfo.type];
  19678. storage[dimensions[i]] = new DataCtor(size);
  19679. }
  19680. var self = this;
  19681. if (!dimValueGetter) {
  19682. self.hasItemOption = false;
  19683. }
  19684. // Default dim value getter
  19685. dimValueGetter = dimValueGetter || function (dataItem, dimName, dataIndex, dimIndex) {
  19686. var value = getDataItemValue(dataItem);
  19687. // If any dataItem is like { value: 10 }
  19688. if (isDataItemOption(dataItem)) {
  19689. self.hasItemOption = true;
  19690. }
  19691. return converDataValue(
  19692. (value instanceof Array)
  19693. ? value[dimIndex]
  19694. // If value is a single number or something else not array.
  19695. : value,
  19696. dimensionInfoMap[dimName]
  19697. );
  19698. };
  19699. for (var i = 0; i < size; i++) {
  19700. // NOTICE: Try not to write things into dataItem
  19701. var dataItem = data.getItem(i);
  19702. // Each data item is value
  19703. // [1, 2]
  19704. // 2
  19705. // Bar chart, line chart which uses category axis
  19706. // only gives the 'y' value. 'x' value is the indices of cateogry
  19707. // Use a tempValue to normalize the value to be a (x, y) value
  19708. // Store the data by dimensions
  19709. for (var k = 0; k < dimensions.length; k++) {
  19710. var dim = dimensions[k];
  19711. var dimStorage = storage[dim];
  19712. // PENDING NULL is empty or zero
  19713. dimStorage[i] = dimValueGetter(dataItem, dim, i, k);
  19714. }
  19715. indices.push(i);
  19716. }
  19717. // Use the name in option and create id
  19718. for (var i = 0; i < size; i++) {
  19719. var dataItem = data.getItem(i);
  19720. if (!nameList[i] && dataItem) {
  19721. if (dataItem.name != null) {
  19722. nameList[i] = dataItem.name;
  19723. }
  19724. else if (nameDimIdx != null) {
  19725. nameList[i] = storage[dimensions[nameDimIdx]][i];
  19726. }
  19727. }
  19728. var name = nameList[i] || '';
  19729. // Try using the id in option
  19730. var id = dataItem && dataItem.id;
  19731. if (!id && name) {
  19732. // Use name as id and add counter to avoid same name
  19733. nameRepeatCount[name] = nameRepeatCount[name] || 0;
  19734. id = name;
  19735. if (nameRepeatCount[name] > 0) {
  19736. id += '__ec__' + nameRepeatCount[name];
  19737. }
  19738. nameRepeatCount[name]++;
  19739. }
  19740. id && (idList[i] = id);
  19741. }
  19742. this._nameList = nameList;
  19743. this._idList = idList;
  19744. };
  19745. /**
  19746. * @return {number}
  19747. */
  19748. listProto.count = function () {
  19749. return this.indices.length;
  19750. };
  19751. /**
  19752. * Get value. Return NaN if idx is out of range.
  19753. * @param {string} dim Dim must be concrete name.
  19754. * @param {number} idx
  19755. * @param {boolean} stack
  19756. * @return {number}
  19757. */
  19758. listProto.get = function (dim, idx, stack) {
  19759. var storage = this._storage;
  19760. var dataIndex = this.indices[idx];
  19761. // If value not exists
  19762. if (dataIndex == null || !storage[dim]) {
  19763. return NaN;
  19764. }
  19765. var value = storage[dim][dataIndex];
  19766. // FIXME ordinal data type is not stackable
  19767. if (stack) {
  19768. var dimensionInfo = this._dimensionInfos[dim];
  19769. if (dimensionInfo && dimensionInfo.stackable) {
  19770. var stackedOn = this.stackedOn;
  19771. while (stackedOn) {
  19772. // Get no stacked data of stacked on
  19773. var stackedValue = stackedOn.get(dim, idx);
  19774. // Considering positive stack, negative stack and empty data
  19775. if ((value >= 0 && stackedValue > 0) // Positive stack
  19776. || (value <= 0 && stackedValue < 0) // Negative stack
  19777. ) {
  19778. value += stackedValue;
  19779. }
  19780. stackedOn = stackedOn.stackedOn;
  19781. }
  19782. }
  19783. }
  19784. return value;
  19785. };
  19786. /**
  19787. * Get value for multi dimensions.
  19788. * @param {Array.<string>} [dimensions] If ignored, using all dimensions.
  19789. * @param {number} idx
  19790. * @param {boolean} stack
  19791. * @return {number}
  19792. */
  19793. listProto.getValues = function (dimensions, idx, stack) {
  19794. var values = [];
  19795. if (!isArray(dimensions)) {
  19796. stack = idx;
  19797. idx = dimensions;
  19798. dimensions = this.dimensions;
  19799. }
  19800. for (var i = 0, len = dimensions.length; i < len; i++) {
  19801. values.push(this.get(dimensions[i], idx, stack));
  19802. }
  19803. return values;
  19804. };
  19805. /**
  19806. * If value is NaN. Inlcuding '-'
  19807. * @param {string} dim
  19808. * @param {number} idx
  19809. * @return {number}
  19810. */
  19811. listProto.hasValue = function (idx) {
  19812. var dimensions = this.dimensions;
  19813. var dimensionInfos = this._dimensionInfos;
  19814. for (var i = 0, len = dimensions.length; i < len; i++) {
  19815. if (
  19816. // Ordinal type can be string or number
  19817. dimensionInfos[dimensions[i]].type !== 'ordinal'
  19818. && isNaN(this.get(dimensions[i], idx))
  19819. ) {
  19820. return false;
  19821. }
  19822. }
  19823. return true;
  19824. };
  19825. /**
  19826. * Get extent of data in one dimension
  19827. * @param {string} dim
  19828. * @param {boolean} stack
  19829. * @param {Function} filter
  19830. */
  19831. listProto.getDataExtent = function (dim, stack, filter$$1) {
  19832. dim = this.getDimension(dim);
  19833. var dimData = this._storage[dim];
  19834. var dimInfo = this.getDimensionInfo(dim);
  19835. stack = (dimInfo && dimInfo.stackable) && stack;
  19836. var dimExtent = (this._extent || (this._extent = {}))[dim + (!!stack)];
  19837. var value;
  19838. if (dimExtent) {
  19839. return dimExtent;
  19840. }
  19841. // var dimInfo = this._dimensionInfos[dim];
  19842. if (dimData) {
  19843. var min = Infinity;
  19844. var max = -Infinity;
  19845. // var isOrdinal = dimInfo.type === 'ordinal';
  19846. for (var i = 0, len = this.count(); i < len; i++) {
  19847. value = this.get(dim, i, stack);
  19848. // FIXME
  19849. // if (isOrdinal && typeof value === 'string') {
  19850. // value = zrUtil.indexOf(dimData, value);
  19851. // }
  19852. if (!filter$$1 || filter$$1(value, dim, i)) {
  19853. value < min && (min = value);
  19854. value > max && (max = value);
  19855. }
  19856. }
  19857. return (this._extent[dim + !!stack] = [min, max]);
  19858. }
  19859. else {
  19860. return [Infinity, -Infinity];
  19861. }
  19862. };
  19863. /**
  19864. * Get sum of data in one dimension
  19865. * @param {string} dim
  19866. * @param {boolean} stack
  19867. */
  19868. listProto.getSum = function (dim, stack) {
  19869. var dimData = this._storage[dim];
  19870. var sum = 0;
  19871. if (dimData) {
  19872. for (var i = 0, len = this.count(); i < len; i++) {
  19873. var value = this.get(dim, i, stack);
  19874. if (!isNaN(value)) {
  19875. sum += value;
  19876. }
  19877. }
  19878. }
  19879. return sum;
  19880. };
  19881. /**
  19882. * Retreive the index with given value
  19883. * @param {number} idx
  19884. * @param {number} value
  19885. * @return {number}
  19886. */
  19887. // FIXME Precision of float value
  19888. listProto.indexOf = function (dim, value) {
  19889. var storage = this._storage;
  19890. var dimData = storage[dim];
  19891. var indices = this.indices;
  19892. if (dimData) {
  19893. for (var i = 0, len = indices.length; i < len; i++) {
  19894. var rawIndex = indices[i];
  19895. if (dimData[rawIndex] === value) {
  19896. return i;
  19897. }
  19898. }
  19899. }
  19900. return -1;
  19901. };
  19902. /**
  19903. * Retreive the index with given name
  19904. * @param {number} idx
  19905. * @param {number} name
  19906. * @return {number}
  19907. */
  19908. listProto.indexOfName = function (name) {
  19909. var indices = this.indices;
  19910. var nameList = this._nameList;
  19911. for (var i = 0, len = indices.length; i < len; i++) {
  19912. var rawIndex = indices[i];
  19913. if (nameList[rawIndex] === name) {
  19914. return i;
  19915. }
  19916. }
  19917. return -1;
  19918. };
  19919. /**
  19920. * Retreive the index with given raw data index
  19921. * @param {number} idx
  19922. * @param {number} name
  19923. * @return {number}
  19924. */
  19925. listProto.indexOfRawIndex = function (rawIndex) {
  19926. // Indices are ascending
  19927. var indices = this.indices;
  19928. // If rawIndex === dataIndex
  19929. var rawDataIndex = indices[rawIndex];
  19930. if (rawDataIndex != null && rawDataIndex === rawIndex) {
  19931. return rawIndex;
  19932. }
  19933. var left = 0;
  19934. var right = indices.length - 1;
  19935. while (left <= right) {
  19936. var mid = (left + right) / 2 | 0;
  19937. if (indices[mid] < rawIndex) {
  19938. left = mid + 1;
  19939. }
  19940. else if (indices[mid] > rawIndex) {
  19941. right = mid - 1;
  19942. }
  19943. else {
  19944. return mid;
  19945. }
  19946. }
  19947. return -1;
  19948. };
  19949. /**
  19950. * Retreive the index of nearest value
  19951. * @param {string} dim
  19952. * @param {number} value
  19953. * @param {boolean} stack If given value is after stacked
  19954. * @param {number} [maxDistance=Infinity]
  19955. * @return {Array.<number>} Considere multiple points has the same value.
  19956. */
  19957. listProto.indicesOfNearest = function (dim, value, stack, maxDistance) {
  19958. var storage = this._storage;
  19959. var dimData = storage[dim];
  19960. var nearestIndices = [];
  19961. if (!dimData) {
  19962. return nearestIndices;
  19963. }
  19964. if (maxDistance == null) {
  19965. maxDistance = Infinity;
  19966. }
  19967. var minDist = Number.MAX_VALUE;
  19968. var minDiff = -1;
  19969. for (var i = 0, len = this.count(); i < len; i++) {
  19970. var diff = value - this.get(dim, i, stack);
  19971. var dist = Math.abs(diff);
  19972. if (diff <= maxDistance && dist <= minDist) {
  19973. // For the case of two data are same on xAxis, which has sequence data.
  19974. // Show the nearest index
  19975. // https://github.com/ecomfe/echarts/issues/2869
  19976. if (dist < minDist || (diff >= 0 && minDiff < 0)) {
  19977. minDist = dist;
  19978. minDiff = diff;
  19979. nearestIndices.length = 0;
  19980. }
  19981. nearestIndices.push(i);
  19982. }
  19983. }
  19984. return nearestIndices;
  19985. };
  19986. /**
  19987. * Get raw data index
  19988. * @param {number} idx
  19989. * @return {number}
  19990. */
  19991. listProto.getRawIndex = function (idx) {
  19992. var rawIdx = this.indices[idx];
  19993. return rawIdx == null ? -1 : rawIdx;
  19994. };
  19995. /**
  19996. * Get raw data item
  19997. * @param {number} idx
  19998. * @return {number}
  19999. */
  20000. listProto.getRawDataItem = function (idx) {
  20001. return this._rawData.getItem(this.getRawIndex(idx));
  20002. };
  20003. /**
  20004. * @param {number} idx
  20005. * @param {boolean} [notDefaultIdx=false]
  20006. * @return {string}
  20007. */
  20008. listProto.getName = function (idx) {
  20009. return this._nameList[this.indices[idx]] || '';
  20010. };
  20011. /**
  20012. * @param {number} idx
  20013. * @param {boolean} [notDefaultIdx=false]
  20014. * @return {string}
  20015. */
  20016. listProto.getId = function (idx) {
  20017. return this._idList[this.indices[idx]] || (this.getRawIndex(idx) + '');
  20018. };
  20019. function normalizeDimensions(dimensions) {
  20020. if (!isArray(dimensions)) {
  20021. dimensions = [dimensions];
  20022. }
  20023. return dimensions;
  20024. }
  20025. /**
  20026. * Data iteration
  20027. * @param {string|Array.<string>}
  20028. * @param {Function} cb
  20029. * @param {boolean} [stack=false]
  20030. * @param {*} [context=this]
  20031. *
  20032. * @example
  20033. * list.each('x', function (x, idx) {});
  20034. * list.each(['x', 'y'], function (x, y, idx) {});
  20035. * list.each(function (idx) {})
  20036. */
  20037. listProto.each = function (dims, cb, stack, context) {
  20038. if (typeof dims === 'function') {
  20039. context = stack;
  20040. stack = cb;
  20041. cb = dims;
  20042. dims = [];
  20043. }
  20044. dims = map(normalizeDimensions(dims), this.getDimension, this);
  20045. var value = [];
  20046. var dimSize = dims.length;
  20047. var indices = this.indices;
  20048. context = context || this;
  20049. for (var i = 0; i < indices.length; i++) {
  20050. // Simple optimization
  20051. switch (dimSize) {
  20052. case 0:
  20053. cb.call(context, i);
  20054. break;
  20055. case 1:
  20056. cb.call(context, this.get(dims[0], i, stack), i);
  20057. break;
  20058. case 2:
  20059. cb.call(context, this.get(dims[0], i, stack), this.get(dims[1], i, stack), i);
  20060. break;
  20061. default:
  20062. for (var k = 0; k < dimSize; k++) {
  20063. value[k] = this.get(dims[k], i, stack);
  20064. }
  20065. // Index
  20066. value[k] = i;
  20067. cb.apply(context, value);
  20068. }
  20069. }
  20070. };
  20071. /**
  20072. * Data filter
  20073. * @param {string|Array.<string>}
  20074. * @param {Function} cb
  20075. * @param {boolean} [stack=false]
  20076. * @param {*} [context=this]
  20077. */
  20078. listProto.filterSelf = function (dimensions, cb, stack, context) {
  20079. if (typeof dimensions === 'function') {
  20080. context = stack;
  20081. stack = cb;
  20082. cb = dimensions;
  20083. dimensions = [];
  20084. }
  20085. dimensions = map(
  20086. normalizeDimensions(dimensions), this.getDimension, this
  20087. );
  20088. var newIndices = [];
  20089. var value = [];
  20090. var dimSize = dimensions.length;
  20091. var indices = this.indices;
  20092. context = context || this;
  20093. for (var i = 0; i < indices.length; i++) {
  20094. var keep;
  20095. // Simple optimization
  20096. if (!dimSize) {
  20097. keep = cb.call(context, i);
  20098. }
  20099. else if (dimSize === 1) {
  20100. keep = cb.call(
  20101. context, this.get(dimensions[0], i, stack), i
  20102. );
  20103. }
  20104. else {
  20105. for (var k = 0; k < dimSize; k++) {
  20106. value[k] = this.get(dimensions[k], i, stack);
  20107. }
  20108. value[k] = i;
  20109. keep = cb.apply(context, value);
  20110. }
  20111. if (keep) {
  20112. newIndices.push(indices[i]);
  20113. }
  20114. }
  20115. this.indices = newIndices;
  20116. // Reset data extent
  20117. this._extent = {};
  20118. return this;
  20119. };
  20120. /**
  20121. * Data mapping to a plain array
  20122. * @param {string|Array.<string>} [dimensions]
  20123. * @param {Function} cb
  20124. * @param {boolean} [stack=false]
  20125. * @param {*} [context=this]
  20126. * @return {Array}
  20127. */
  20128. listProto.mapArray = function (dimensions, cb, stack, context) {
  20129. if (typeof dimensions === 'function') {
  20130. context = stack;
  20131. stack = cb;
  20132. cb = dimensions;
  20133. dimensions = [];
  20134. }
  20135. var result = [];
  20136. this.each(dimensions, function () {
  20137. result.push(cb && cb.apply(this, arguments));
  20138. }, stack, context);
  20139. return result;
  20140. };
  20141. function cloneListForMapAndSample(original, excludeDimensions) {
  20142. var allDimensions = original.dimensions;
  20143. var list = new List(
  20144. map(allDimensions, original.getDimensionInfo, original),
  20145. original.hostModel
  20146. );
  20147. // FIXME If needs stackedOn, value may already been stacked
  20148. transferProperties(list, original);
  20149. var storage = list._storage = {};
  20150. var originalStorage = original._storage;
  20151. // Init storage
  20152. for (var i = 0; i < allDimensions.length; i++) {
  20153. var dim = allDimensions[i];
  20154. var dimStore = originalStorage[dim];
  20155. if (indexOf(excludeDimensions, dim) >= 0) {
  20156. storage[dim] = new dimStore.constructor(
  20157. originalStorage[dim].length
  20158. );
  20159. }
  20160. else {
  20161. // Direct reference for other dimensions
  20162. storage[dim] = originalStorage[dim];
  20163. }
  20164. }
  20165. return list;
  20166. }
  20167. /**
  20168. * Data mapping to a new List with given dimensions
  20169. * @param {string|Array.<string>} dimensions
  20170. * @param {Function} cb
  20171. * @param {boolean} [stack=false]
  20172. * @param {*} [context=this]
  20173. * @return {Array}
  20174. */
  20175. listProto.map = function (dimensions, cb, stack, context) {
  20176. dimensions = map(
  20177. normalizeDimensions(dimensions), this.getDimension, this
  20178. );
  20179. var list = cloneListForMapAndSample(this, dimensions);
  20180. // Following properties are all immutable.
  20181. // So we can reference to the same value
  20182. var indices = list.indices = this.indices;
  20183. var storage = list._storage;
  20184. var tmpRetValue = [];
  20185. this.each(dimensions, function () {
  20186. var idx = arguments[arguments.length - 1];
  20187. var retValue = cb && cb.apply(this, arguments);
  20188. if (retValue != null) {
  20189. // a number
  20190. if (typeof retValue === 'number') {
  20191. tmpRetValue[0] = retValue;
  20192. retValue = tmpRetValue;
  20193. }
  20194. for (var i = 0; i < retValue.length; i++) {
  20195. var dim = dimensions[i];
  20196. var dimStore = storage[dim];
  20197. var rawIdx = indices[idx];
  20198. if (dimStore) {
  20199. dimStore[rawIdx] = retValue[i];
  20200. }
  20201. }
  20202. }
  20203. }, stack, context);
  20204. return list;
  20205. };
  20206. /**
  20207. * Large data down sampling on given dimension
  20208. * @param {string} dimension
  20209. * @param {number} rate
  20210. * @param {Function} sampleValue
  20211. * @param {Function} sampleIndex Sample index for name and id
  20212. */
  20213. listProto.downSample = function (dimension, rate, sampleValue, sampleIndex) {
  20214. var list = cloneListForMapAndSample(this, [dimension]);
  20215. var storage = this._storage;
  20216. var targetStorage = list._storage;
  20217. var originalIndices = this.indices;
  20218. var indices = list.indices = [];
  20219. var frameValues = [];
  20220. var frameIndices = [];
  20221. var frameSize = Math.floor(1 / rate);
  20222. var dimStore = targetStorage[dimension];
  20223. var len = this.count();
  20224. // Copy data from original data
  20225. for (var i = 0; i < storage[dimension].length; i++) {
  20226. targetStorage[dimension][i] = storage[dimension][i];
  20227. }
  20228. for (var i = 0; i < len; i += frameSize) {
  20229. // Last frame
  20230. if (frameSize > len - i) {
  20231. frameSize = len - i;
  20232. frameValues.length = frameSize;
  20233. }
  20234. for (var k = 0; k < frameSize; k++) {
  20235. var idx = originalIndices[i + k];
  20236. frameValues[k] = dimStore[idx];
  20237. frameIndices[k] = idx;
  20238. }
  20239. var value = sampleValue(frameValues);
  20240. var idx = frameIndices[sampleIndex(frameValues, value) || 0];
  20241. // Only write value on the filtered data
  20242. dimStore[idx] = value;
  20243. indices.push(idx);
  20244. }
  20245. return list;
  20246. };
  20247. /**
  20248. * Get model of one data item.
  20249. *
  20250. * @param {number} idx
  20251. */
  20252. // FIXME Model proxy ?
  20253. listProto.getItemModel = function (idx) {
  20254. var hostModel = this.hostModel;
  20255. idx = this.indices[idx];
  20256. return new Model(this._rawData.getItem(idx), hostModel, hostModel && hostModel.ecModel);
  20257. };
  20258. /**
  20259. * Create a data differ
  20260. * @param {module:echarts/data/List} otherList
  20261. * @return {module:echarts/data/DataDiffer}
  20262. */
  20263. listProto.diff = function (otherList) {
  20264. var idList = this._idList;
  20265. var otherIdList = otherList && otherList._idList;
  20266. var val;
  20267. // Use prefix to avoid index to be the same as otherIdList[idx],
  20268. // which will cause weird udpate animation.
  20269. var prefix = 'e\0\0';
  20270. return new DataDiffer(
  20271. otherList ? otherList.indices : [],
  20272. this.indices,
  20273. function (idx) {
  20274. return (val = otherIdList[idx]) != null ? val : prefix + idx;
  20275. },
  20276. function (idx) {
  20277. return (val = idList[idx]) != null ? val : prefix + idx;
  20278. }
  20279. );
  20280. };
  20281. /**
  20282. * Get visual property.
  20283. * @param {string} key
  20284. */
  20285. listProto.getVisual = function (key) {
  20286. var visual = this._visual;
  20287. return visual && visual[key];
  20288. };
  20289. /**
  20290. * Set visual property
  20291. * @param {string|Object} key
  20292. * @param {*} [value]
  20293. *
  20294. * @example
  20295. * setVisual('color', color);
  20296. * setVisual({
  20297. * 'color': color
  20298. * });
  20299. */
  20300. listProto.setVisual = function (key, val) {
  20301. if (isObject$4(key)) {
  20302. for (var name in key) {
  20303. if (key.hasOwnProperty(name)) {
  20304. this.setVisual(name, key[name]);
  20305. }
  20306. }
  20307. return;
  20308. }
  20309. this._visual = this._visual || {};
  20310. this._visual[key] = val;
  20311. };
  20312. /**
  20313. * Set layout property.
  20314. * @param {string|Object} key
  20315. * @param {*} [val]
  20316. */
  20317. listProto.setLayout = function (key, val) {
  20318. if (isObject$4(key)) {
  20319. for (var name in key) {
  20320. if (key.hasOwnProperty(name)) {
  20321. this.setLayout(name, key[name]);
  20322. }
  20323. }
  20324. return;
  20325. }
  20326. this._layout[key] = val;
  20327. };
  20328. /**
  20329. * Get layout property.
  20330. * @param {string} key.
  20331. * @return {*}
  20332. */
  20333. listProto.getLayout = function (key) {
  20334. return this._layout[key];
  20335. };
  20336. /**
  20337. * Get layout of single data item
  20338. * @param {number} idx
  20339. */
  20340. listProto.getItemLayout = function (idx) {
  20341. return this._itemLayouts[idx];
  20342. };
  20343. /**
  20344. * Set layout of single data item
  20345. * @param {number} idx
  20346. * @param {Object} layout
  20347. * @param {boolean=} [merge=false]
  20348. */
  20349. listProto.setItemLayout = function (idx, layout, merge$$1) {
  20350. this._itemLayouts[idx] = merge$$1
  20351. ? extend(this._itemLayouts[idx] || {}, layout)
  20352. : layout;
  20353. };
  20354. /**
  20355. * Clear all layout of single data item
  20356. */
  20357. listProto.clearItemLayouts = function () {
  20358. this._itemLayouts.length = 0;
  20359. };
  20360. /**
  20361. * Get visual property of single data item
  20362. * @param {number} idx
  20363. * @param {string} key
  20364. * @param {boolean} [ignoreParent=false]
  20365. */
  20366. listProto.getItemVisual = function (idx, key, ignoreParent) {
  20367. var itemVisual = this._itemVisuals[idx];
  20368. var val = itemVisual && itemVisual[key];
  20369. if (val == null && !ignoreParent) {
  20370. // Use global visual property
  20371. return this.getVisual(key);
  20372. }
  20373. return val;
  20374. };
  20375. /**
  20376. * Set visual property of single data item
  20377. *
  20378. * @param {number} idx
  20379. * @param {string|Object} key
  20380. * @param {*} [value]
  20381. *
  20382. * @example
  20383. * setItemVisual(0, 'color', color);
  20384. * setItemVisual(0, {
  20385. * 'color': color
  20386. * });
  20387. */
  20388. listProto.setItemVisual = function (idx, key, value) {
  20389. var itemVisual = this._itemVisuals[idx] || {};
  20390. this._itemVisuals[idx] = itemVisual;
  20391. if (isObject$4(key)) {
  20392. for (var name in key) {
  20393. if (key.hasOwnProperty(name)) {
  20394. itemVisual[name] = key[name];
  20395. }
  20396. }
  20397. return;
  20398. }
  20399. itemVisual[key] = value;
  20400. };
  20401. /**
  20402. * Clear itemVisuals and list visual.
  20403. */
  20404. listProto.clearAllVisual = function () {
  20405. this._visual = {};
  20406. this._itemVisuals = [];
  20407. };
  20408. var setItemDataAndSeriesIndex = function (child) {
  20409. child.seriesIndex = this.seriesIndex;
  20410. child.dataIndex = this.dataIndex;
  20411. child.dataType = this.dataType;
  20412. };
  20413. /**
  20414. * Set graphic element relative to data. It can be set as null
  20415. * @param {number} idx
  20416. * @param {module:zrender/Element} [el]
  20417. */
  20418. listProto.setItemGraphicEl = function (idx, el) {
  20419. var hostModel = this.hostModel;
  20420. if (el) {
  20421. // Add data index and series index for indexing the data by element
  20422. // Useful in tooltip
  20423. el.dataIndex = idx;
  20424. el.dataType = this.dataType;
  20425. el.seriesIndex = hostModel && hostModel.seriesIndex;
  20426. if (el.type === 'group') {
  20427. el.traverse(setItemDataAndSeriesIndex, el);
  20428. }
  20429. }
  20430. this._graphicEls[idx] = el;
  20431. };
  20432. /**
  20433. * @param {number} idx
  20434. * @return {module:zrender/Element}
  20435. */
  20436. listProto.getItemGraphicEl = function (idx) {
  20437. return this._graphicEls[idx];
  20438. };
  20439. /**
  20440. * @param {Function} cb
  20441. * @param {*} context
  20442. */
  20443. listProto.eachItemGraphicEl = function (cb, context) {
  20444. each$1(this._graphicEls, function (el, idx) {
  20445. if (el) {
  20446. cb && cb.call(context, el, idx);
  20447. }
  20448. });
  20449. };
  20450. /**
  20451. * Shallow clone a new list except visual and layout properties, and graph elements.
  20452. * New list only change the indices.
  20453. */
  20454. listProto.cloneShallow = function () {
  20455. var dimensionInfoList = map(this.dimensions, this.getDimensionInfo, this);
  20456. var list = new List(dimensionInfoList, this.hostModel);
  20457. // FIXME
  20458. list._storage = this._storage;
  20459. transferProperties(list, this);
  20460. // Clone will not change the data extent and indices
  20461. list.indices = this.indices.slice();
  20462. if (this._extent) {
  20463. list._extent = extend({}, this._extent);
  20464. }
  20465. return list;
  20466. };
  20467. /**
  20468. * Wrap some method to add more feature
  20469. * @param {string} methodName
  20470. * @param {Function} injectFunction
  20471. */
  20472. listProto.wrapMethod = function (methodName, injectFunction) {
  20473. var originalMethod = this[methodName];
  20474. if (typeof originalMethod !== 'function') {
  20475. return;
  20476. }
  20477. this.__wrappedMethods = this.__wrappedMethods || [];
  20478. this.__wrappedMethods.push(methodName);
  20479. this[methodName] = function () {
  20480. var res = originalMethod.apply(this, arguments);
  20481. return injectFunction.apply(this, [res].concat(slice(arguments)));
  20482. };
  20483. };
  20484. // Methods that create a new list based on this list should be listed here.
  20485. // Notice that those method should `RETURN` the new list.
  20486. listProto.TRANSFERABLE_METHODS = ['cloneShallow', 'downSample', 'map'];
  20487. // Methods that change indices of this list should be listed here.
  20488. listProto.CHANGABLE_METHODS = ['filterSelf'];
  20489. /**
  20490. * Complete dimensions by data (guess dimension).
  20491. */
  20492. var each$7 = each$1;
  20493. var isString$1 = isString;
  20494. var defaults$1 = defaults;
  20495. var OTHER_DIMS = {tooltip: 1, label: 1, itemName: 1};
  20496. /**
  20497. * Complete the dimensions array, by user defined `dimension` and `encode`,
  20498. * and guessing from the data structure.
  20499. * If no 'value' dimension specified, the first no-named dimension will be
  20500. * named as 'value'.
  20501. *
  20502. * @param {Array.<string>} sysDims Necessary dimensions, like ['x', 'y'], which
  20503. * provides not only dim template, but also default order.
  20504. * `name` of each item provides default coord name.
  20505. * [{dimsDef: []}, ...] can be specified to give names.
  20506. * @param {Array} data Data list. [[1, 2, 3], [2, 3, 4]].
  20507. * @param {Object} [opt]
  20508. * @param {Array.<Object|string>} [opt.dimsDef] option.series.dimensions User defined dimensions
  20509. * For example: ['asdf', {name, type}, ...].
  20510. * @param {Object} [opt.encodeDef] option.series.encode {x: 2, y: [3, 1], tooltip: [1, 2], label: 3}
  20511. * @param {string} [opt.extraPrefix] Prefix of name when filling the left dimensions.
  20512. * @param {string} [opt.extraFromZero] If specified, extra dim names will be:
  20513. * extraPrefix + 0, extraPrefix + extraBaseIndex + 1 ...
  20514. * If not specified, extra dim names will be:
  20515. * extraPrefix, extraPrefix + 0, extraPrefix + 1 ...
  20516. * @param {number} [opt.dimCount] If not specified, guess by the first data item.
  20517. * @return {Array.<Object>} [{
  20518. * name: string mandatory,
  20519. * coordDim: string mandatory,
  20520. * coordDimIndex: number mandatory,
  20521. * type: string optional,
  20522. * tooltipName: string optional,
  20523. * otherDims: {
  20524. * tooltip: number optional,
  20525. * label: number optional
  20526. * },
  20527. * isExtraCoord: boolean true or undefined.
  20528. * other props ...
  20529. * }]
  20530. */
  20531. function completeDimensions(sysDims, data, opt) {
  20532. data = data || [];
  20533. opt = opt || {};
  20534. sysDims = (sysDims || []).slice();
  20535. var dimsDef = (opt.dimsDef || []).slice();
  20536. var encodeDef = createHashMap(opt.encodeDef);
  20537. var dataDimNameMap = createHashMap();
  20538. var coordDimNameMap = createHashMap();
  20539. // var valueCandidate;
  20540. var result = [];
  20541. var dimCount = opt.dimCount;
  20542. if (dimCount == null) {
  20543. var value0 = retrieveValue(data[0]);
  20544. dimCount = Math.max(
  20545. isArray(value0) && value0.length || 1,
  20546. sysDims.length,
  20547. dimsDef.length
  20548. );
  20549. each$7(sysDims, function (sysDimItem) {
  20550. var sysDimItemDimsDef = sysDimItem.dimsDef;
  20551. sysDimItemDimsDef && (dimCount = Math.max(dimCount, sysDimItemDimsDef.length));
  20552. });
  20553. }
  20554. // Apply user defined dims (`name` and `type`) and init result.
  20555. for (var i = 0; i < dimCount; i++) {
  20556. var dimDefItem = isString$1(dimsDef[i]) ? {name: dimsDef[i]} : (dimsDef[i] || {});
  20557. var userDimName = dimDefItem.name;
  20558. var resultItem = result[i] = {otherDims: {}};
  20559. // Name will be applied later for avoiding duplication.
  20560. if (userDimName != null && dataDimNameMap.get(userDimName) == null) {
  20561. // Only if `series.dimensions` is defined in option, tooltipName
  20562. // will be set, and dimension will be diplayed vertically in
  20563. // tooltip by default.
  20564. resultItem.name = resultItem.tooltipName = userDimName;
  20565. dataDimNameMap.set(userDimName, i);
  20566. }
  20567. dimDefItem.type != null && (resultItem.type = dimDefItem.type);
  20568. }
  20569. // Set `coordDim` and `coordDimIndex` by `encodeDef` and normalize `encodeDef`.
  20570. encodeDef.each(function (dataDims, coordDim) {
  20571. dataDims = encodeDef.set(coordDim, normalizeToArray(dataDims).slice());
  20572. each$7(dataDims, function (resultDimIdx, coordDimIndex) {
  20573. // The input resultDimIdx can be dim name or index.
  20574. isString$1(resultDimIdx) && (resultDimIdx = dataDimNameMap.get(resultDimIdx));
  20575. if (resultDimIdx != null && resultDimIdx < dimCount) {
  20576. dataDims[coordDimIndex] = resultDimIdx;
  20577. applyDim(result[resultDimIdx], coordDim, coordDimIndex);
  20578. }
  20579. });
  20580. });
  20581. // Apply templetes and default order from `sysDims`.
  20582. var availDimIdx = 0;
  20583. each$7(sysDims, function (sysDimItem, sysDimIndex) {
  20584. var coordDim;
  20585. var sysDimItem;
  20586. var sysDimItemDimsDef;
  20587. var sysDimItemOtherDims;
  20588. if (isString$1(sysDimItem)) {
  20589. coordDim = sysDimItem;
  20590. sysDimItem = {};
  20591. }
  20592. else {
  20593. coordDim = sysDimItem.name;
  20594. sysDimItem = clone(sysDimItem);
  20595. // `coordDimIndex` should not be set directly.
  20596. sysDimItemDimsDef = sysDimItem.dimsDef;
  20597. sysDimItemOtherDims = sysDimItem.otherDims;
  20598. sysDimItem.name = sysDimItem.coordDim = sysDimItem.coordDimIndex
  20599. = sysDimItem.dimsDef = sysDimItem.otherDims = null;
  20600. }
  20601. var dataDims = normalizeToArray(encodeDef.get(coordDim));
  20602. // dimensions provides default dim sequences.
  20603. if (!dataDims.length) {
  20604. for (var i = 0; i < (sysDimItemDimsDef && sysDimItemDimsDef.length || 1); i++) {
  20605. while (availDimIdx < result.length && result[availDimIdx].coordDim != null) {
  20606. availDimIdx++;
  20607. }
  20608. availDimIdx < result.length && dataDims.push(availDimIdx++);
  20609. }
  20610. }
  20611. // Apply templates.
  20612. each$7(dataDims, function (resultDimIdx, coordDimIndex) {
  20613. var resultItem = result[resultDimIdx];
  20614. applyDim(defaults$1(resultItem, sysDimItem), coordDim, coordDimIndex);
  20615. if (resultItem.name == null && sysDimItemDimsDef) {
  20616. resultItem.name = resultItem.tooltipName = sysDimItemDimsDef[coordDimIndex];
  20617. }
  20618. sysDimItemOtherDims && defaults$1(resultItem.otherDims, sysDimItemOtherDims);
  20619. });
  20620. });
  20621. // Make sure the first extra dim is 'value'.
  20622. var extra = opt.extraPrefix || 'value';
  20623. // Set dim `name` and other `coordDim` and other props.
  20624. for (var resultDimIdx = 0; resultDimIdx < dimCount; resultDimIdx++) {
  20625. var resultItem = result[resultDimIdx] = result[resultDimIdx] || {};
  20626. var coordDim = resultItem.coordDim;
  20627. coordDim == null && (
  20628. resultItem.coordDim = genName(extra, coordDimNameMap, opt.extraFromZero),
  20629. resultItem.coordDimIndex = 0,
  20630. resultItem.isExtraCoord = true
  20631. );
  20632. resultItem.name == null && (resultItem.name = genName(
  20633. resultItem.coordDim,
  20634. dataDimNameMap
  20635. ));
  20636. resultItem.type == null && guessOrdinal(data, resultDimIdx)
  20637. && (resultItem.type = 'ordinal');
  20638. }
  20639. return result;
  20640. function applyDim(resultItem, coordDim, coordDimIndex) {
  20641. if (OTHER_DIMS[coordDim]) {
  20642. resultItem.otherDims[coordDim] = coordDimIndex;
  20643. }
  20644. else {
  20645. resultItem.coordDim = coordDim;
  20646. resultItem.coordDimIndex = coordDimIndex;
  20647. coordDimNameMap.set(coordDim, true);
  20648. }
  20649. }
  20650. function genName(name, map$$1, fromZero) {
  20651. if (fromZero || map$$1.get(name) != null) {
  20652. var i = 0;
  20653. while (map$$1.get(name + i) != null) {
  20654. i++;
  20655. }
  20656. name += i;
  20657. }
  20658. map$$1.set(name, true);
  20659. return name;
  20660. }
  20661. }
  20662. // The rule should not be complex, otherwise user might not
  20663. // be able to known where the data is wrong.
  20664. var guessOrdinal = completeDimensions.guessOrdinal = function (data, dimIndex) {
  20665. for (var i = 0, len = data.length; i < len; i++) {
  20666. var value = retrieveValue(data[i]);
  20667. if (!isArray(value)) {
  20668. return false;
  20669. }
  20670. var value = value[dimIndex];
  20671. // Consider usage convenience, '1', '2' will be treated as "number".
  20672. // `isFinit('')` get `true`.
  20673. if (value != null && isFinite(value) && value !== '') {
  20674. return false;
  20675. }
  20676. else if (isString$1(value) && value !== '-') {
  20677. return true;
  20678. }
  20679. }
  20680. return false;
  20681. };
  20682. function retrieveValue(o) {
  20683. return isArray(o) ? o : isObject(o) ? o.value: o;
  20684. }
  20685. function firstDataNotNull(data) {
  20686. var i = 0;
  20687. while (i < data.length && data[i] == null) {
  20688. i++;
  20689. }
  20690. return data[i];
  20691. }
  20692. function ifNeedCompleteOrdinalData(data) {
  20693. var sampleItem = firstDataNotNull(data);
  20694. return sampleItem != null
  20695. && !isArray(getDataItemValue(sampleItem));
  20696. }
  20697. /**
  20698. * Helper function to create a list from option data
  20699. */
  20700. function createListFromArray(data, seriesModel, ecModel) {
  20701. // If data is undefined
  20702. data = data || [];
  20703. if (__DEV__) {
  20704. if (!isArray(data)) {
  20705. throw new Error('Invalid data.');
  20706. }
  20707. }
  20708. var coordSysName = seriesModel.get('coordinateSystem');
  20709. var creator = creators[coordSysName];
  20710. var registeredCoordSys = CoordinateSystemManager.get(coordSysName);
  20711. var completeDimOpt = {
  20712. encodeDef: seriesModel.get('encode'),
  20713. dimsDef: seriesModel.get('dimensions')
  20714. };
  20715. // FIXME
  20716. var axesInfo = creator && creator(data, seriesModel, ecModel, completeDimOpt);
  20717. var dimensions = axesInfo && axesInfo.dimensions;
  20718. if (!dimensions) {
  20719. // Get dimensions from registered coordinate system
  20720. dimensions = (registeredCoordSys && (
  20721. registeredCoordSys.getDimensionsInfo
  20722. ? registeredCoordSys.getDimensionsInfo()
  20723. : registeredCoordSys.dimensions.slice()
  20724. )) || ['x', 'y'];
  20725. dimensions = completeDimensions(dimensions, data, completeDimOpt);
  20726. }
  20727. var categoryIndex = axesInfo ? axesInfo.categoryIndex : -1;
  20728. var list = new List(dimensions, seriesModel);
  20729. var nameList = createNameList(axesInfo, data);
  20730. var categories = {};
  20731. var dimValueGetter = (categoryIndex >= 0 && ifNeedCompleteOrdinalData(data))
  20732. ? function (itemOpt, dimName, dataIndex, dimIndex) {
  20733. // If any dataItem is like { value: 10 }
  20734. if (isDataItemOption(itemOpt)) {
  20735. list.hasItemOption = true;
  20736. }
  20737. // Use dataIndex as ordinal value in categoryAxis
  20738. return dimIndex === categoryIndex
  20739. ? dataIndex
  20740. : converDataValue(getDataItemValue(itemOpt), dimensions[dimIndex]);
  20741. }
  20742. : function (itemOpt, dimName, dataIndex, dimIndex) {
  20743. var value = getDataItemValue(itemOpt);
  20744. var val = converDataValue(value && value[dimIndex], dimensions[dimIndex]);
  20745. // If any dataItem is like { value: 10 }
  20746. if (isDataItemOption(itemOpt)) {
  20747. list.hasItemOption = true;
  20748. }
  20749. var categoryAxesModels = axesInfo && axesInfo.categoryAxesModels;
  20750. if (categoryAxesModels && categoryAxesModels[dimName]) {
  20751. // If given value is a category string
  20752. if (typeof val === 'string') {
  20753. // Lazy get categories
  20754. categories[dimName] = categories[dimName]
  20755. || categoryAxesModels[dimName].getCategories();
  20756. val = indexOf(categories[dimName], val);
  20757. if (val < 0 && !isNaN(val)) {
  20758. // In case some one write '1', '2' istead of 1, 2
  20759. val = +val;
  20760. }
  20761. }
  20762. }
  20763. return val;
  20764. };
  20765. list.hasItemOption = false;
  20766. list.initData(data, nameList, dimValueGetter);
  20767. return list;
  20768. }
  20769. function isStackable(axisType) {
  20770. return axisType !== 'category' && axisType !== 'time';
  20771. }
  20772. function getDimTypeByAxis(axisType) {
  20773. return axisType === 'category'
  20774. ? 'ordinal'
  20775. : axisType === 'time'
  20776. ? 'time'
  20777. : 'float';
  20778. }
  20779. /**
  20780. * Creaters for each coord system.
  20781. */
  20782. var creators = {
  20783. cartesian2d: function (data, seriesModel, ecModel, completeDimOpt) {
  20784. var axesModels = map(['xAxis', 'yAxis'], function (name) {
  20785. return ecModel.queryComponents({
  20786. mainType: name,
  20787. index: seriesModel.get(name + 'Index'),
  20788. id: seriesModel.get(name + 'Id')
  20789. })[0];
  20790. });
  20791. var xAxisModel = axesModels[0];
  20792. var yAxisModel = axesModels[1];
  20793. if (__DEV__) {
  20794. if (!xAxisModel) {
  20795. throw new Error('xAxis "' + retrieve(
  20796. seriesModel.get('xAxisIndex'),
  20797. seriesModel.get('xAxisId'),
  20798. 0
  20799. ) + '" not found');
  20800. }
  20801. if (!yAxisModel) {
  20802. throw new Error('yAxis "' + retrieve(
  20803. seriesModel.get('xAxisIndex'),
  20804. seriesModel.get('yAxisId'),
  20805. 0
  20806. ) + '" not found');
  20807. }
  20808. }
  20809. var xAxisType = xAxisModel.get('type');
  20810. var yAxisType = yAxisModel.get('type');
  20811. var dimensions = [
  20812. {
  20813. name: 'x',
  20814. type: getDimTypeByAxis(xAxisType),
  20815. stackable: isStackable(xAxisType)
  20816. },
  20817. {
  20818. name: 'y',
  20819. // If two category axes
  20820. type: getDimTypeByAxis(yAxisType),
  20821. stackable: isStackable(yAxisType)
  20822. }
  20823. ];
  20824. var isXAxisCateogry = xAxisType === 'category';
  20825. var isYAxisCategory = yAxisType === 'category';
  20826. dimensions = completeDimensions(dimensions, data, completeDimOpt);
  20827. var categoryAxesModels = {};
  20828. if (isXAxisCateogry) {
  20829. categoryAxesModels.x = xAxisModel;
  20830. }
  20831. if (isYAxisCategory) {
  20832. categoryAxesModels.y = yAxisModel;
  20833. }
  20834. return {
  20835. dimensions: dimensions,
  20836. categoryIndex: isXAxisCateogry ? 0 : (isYAxisCategory ? 1 : -1),
  20837. categoryAxesModels: categoryAxesModels
  20838. };
  20839. },
  20840. singleAxis: function (data, seriesModel, ecModel, completeDimOpt) {
  20841. var singleAxisModel = ecModel.queryComponents({
  20842. mainType: 'singleAxis',
  20843. index: seriesModel.get('singleAxisIndex'),
  20844. id: seriesModel.get('singleAxisId')
  20845. })[0];
  20846. if (__DEV__) {
  20847. if (!singleAxisModel) {
  20848. throw new Error('singleAxis should be specified.');
  20849. }
  20850. }
  20851. var singleAxisType = singleAxisModel.get('type');
  20852. var isCategory = singleAxisType === 'category';
  20853. var dimensions = [{
  20854. name: 'single',
  20855. type: getDimTypeByAxis(singleAxisType),
  20856. stackable: isStackable(singleAxisType)
  20857. }];
  20858. dimensions = completeDimensions(dimensions, data, completeDimOpt);
  20859. var categoryAxesModels = {};
  20860. if (isCategory) {
  20861. categoryAxesModels.single = singleAxisModel;
  20862. }
  20863. return {
  20864. dimensions: dimensions,
  20865. categoryIndex: isCategory ? 0 : -1,
  20866. categoryAxesModels: categoryAxesModels
  20867. };
  20868. },
  20869. polar: function (data, seriesModel, ecModel, completeDimOpt) {
  20870. var polarModel = ecModel.queryComponents({
  20871. mainType: 'polar',
  20872. index: seriesModel.get('polarIndex'),
  20873. id: seriesModel.get('polarId')
  20874. })[0];
  20875. var angleAxisModel = polarModel.findAxisModel('angleAxis');
  20876. var radiusAxisModel = polarModel.findAxisModel('radiusAxis');
  20877. if (__DEV__) {
  20878. if (!angleAxisModel) {
  20879. throw new Error('angleAxis option not found');
  20880. }
  20881. if (!radiusAxisModel) {
  20882. throw new Error('radiusAxis option not found');
  20883. }
  20884. }
  20885. var radiusAxisType = radiusAxisModel.get('type');
  20886. var angleAxisType = angleAxisModel.get('type');
  20887. var dimensions = [
  20888. {
  20889. name: 'radius',
  20890. type: getDimTypeByAxis(radiusAxisType),
  20891. stackable: isStackable(radiusAxisType)
  20892. },
  20893. {
  20894. name: 'angle',
  20895. type: getDimTypeByAxis(angleAxisType),
  20896. stackable: isStackable(angleAxisType)
  20897. }
  20898. ];
  20899. var isAngleAxisCateogry = angleAxisType === 'category';
  20900. var isRadiusAxisCateogry = radiusAxisType === 'category';
  20901. dimensions = completeDimensions(dimensions, data, completeDimOpt);
  20902. var categoryAxesModels = {};
  20903. if (isRadiusAxisCateogry) {
  20904. categoryAxesModels.radius = radiusAxisModel;
  20905. }
  20906. if (isAngleAxisCateogry) {
  20907. categoryAxesModels.angle = angleAxisModel;
  20908. }
  20909. return {
  20910. dimensions: dimensions,
  20911. categoryIndex: isAngleAxisCateogry ? 1 : (isRadiusAxisCateogry ? 0 : -1),
  20912. categoryAxesModels: categoryAxesModels
  20913. };
  20914. },
  20915. geo: function (data, seriesModel, ecModel, completeDimOpt) {
  20916. // TODO Region
  20917. // 多个散点图系列在同一个地区的时候
  20918. return {
  20919. dimensions: completeDimensions([
  20920. {name: 'lng'},
  20921. {name: 'lat'}
  20922. ], data, completeDimOpt)
  20923. };
  20924. }
  20925. };
  20926. function createNameList(result, data) {
  20927. var nameList = [];
  20928. var categoryDim = result && result.dimensions[result.categoryIndex];
  20929. var categoryAxisModel;
  20930. if (categoryDim) {
  20931. categoryAxisModel = result.categoryAxesModels[categoryDim.name];
  20932. }
  20933. if (categoryAxisModel) {
  20934. // FIXME Two category axis
  20935. var categories = categoryAxisModel.getCategories();
  20936. if (categories) {
  20937. var dataLen = data.length;
  20938. // Ordered data is given explicitly like
  20939. // [[3, 0.2], [1, 0.3], [2, 0.15]]
  20940. // or given scatter data,
  20941. // pick the category
  20942. if (isArray(data[0]) && data[0].length > 1) {
  20943. nameList = [];
  20944. for (var i = 0; i < dataLen; i++) {
  20945. nameList[i] = categories[data[i][result.categoryIndex || 0]];
  20946. }
  20947. }
  20948. else {
  20949. nameList = categories.slice(0);
  20950. }
  20951. }
  20952. }
  20953. return nameList;
  20954. }
  20955. SeriesModel.extend({
  20956. type: 'series.line',
  20957. dependencies: ['grid', 'polar'],
  20958. getInitialData: function (option, ecModel) {
  20959. if (__DEV__) {
  20960. var coordSys = option.coordinateSystem;
  20961. if (coordSys !== 'polar' && coordSys !== 'cartesian2d') {
  20962. throw new Error('Line not support coordinateSystem besides cartesian and polar');
  20963. }
  20964. }
  20965. return createListFromArray(option.data, this, ecModel);
  20966. },
  20967. defaultOption: {
  20968. zlevel: 0, // 一级层叠
  20969. z: 2, // 二级层叠
  20970. coordinateSystem: 'cartesian2d',
  20971. legendHoverLink: true,
  20972. hoverAnimation: true,
  20973. // stack: null
  20974. // xAxisIndex: 0,
  20975. // yAxisIndex: 0,
  20976. // polarIndex: 0,
  20977. // If clip the overflow value
  20978. clipOverflow: true,
  20979. // cursor: null,
  20980. label: {
  20981. normal: {
  20982. position: 'top'
  20983. }
  20984. },
  20985. // itemStyle: {
  20986. // normal: {},
  20987. // emphasis: {}
  20988. // },
  20989. lineStyle: {
  20990. normal: {
  20991. width: 2,
  20992. type: 'solid'
  20993. }
  20994. },
  20995. // areaStyle: {},
  20996. // false, 'start', 'end', 'middle'
  20997. step: false,
  20998. // Disabled if step is true
  20999. smooth: false,
  21000. smoothMonotone: null,
  21001. // 拐点图形类型
  21002. symbol: 'emptyCircle',
  21003. // 拐点图形大小
  21004. symbolSize: 4,
  21005. // 拐点图形旋转控制
  21006. symbolRotate: null,
  21007. // 是否显示 symbol, 只有在 tooltip hover 的时候显示
  21008. showSymbol: true,
  21009. // 标志图形默认只有主轴显示(随主轴标签间隔隐藏策略)
  21010. showAllSymbol: false,
  21011. // 是否连接断点
  21012. connectNulls: false,
  21013. // 数据过滤,'average', 'max', 'min', 'sum'
  21014. sampling: 'none',
  21015. animationEasing: 'linear',
  21016. // Disable progressive
  21017. progressive: 0,
  21018. hoverLayerThreshold: Infinity
  21019. }
  21020. });
  21021. // Symbol factory
  21022. /**
  21023. * Triangle shape
  21024. * @inner
  21025. */
  21026. var Triangle = extendShape({
  21027. type: 'triangle',
  21028. shape: {
  21029. cx: 0,
  21030. cy: 0,
  21031. width: 0,
  21032. height: 0
  21033. },
  21034. buildPath: function (path, shape) {
  21035. var cx = shape.cx;
  21036. var cy = shape.cy;
  21037. var width = shape.width / 2;
  21038. var height = shape.height / 2;
  21039. path.moveTo(cx, cy - height);
  21040. path.lineTo(cx + width, cy + height);
  21041. path.lineTo(cx - width, cy + height);
  21042. path.closePath();
  21043. }
  21044. });
  21045. /**
  21046. * Diamond shape
  21047. * @inner
  21048. */
  21049. var Diamond = extendShape({
  21050. type: 'diamond',
  21051. shape: {
  21052. cx: 0,
  21053. cy: 0,
  21054. width: 0,
  21055. height: 0
  21056. },
  21057. buildPath: function (path, shape) {
  21058. var cx = shape.cx;
  21059. var cy = shape.cy;
  21060. var width = shape.width / 2;
  21061. var height = shape.height / 2;
  21062. path.moveTo(cx, cy - height);
  21063. path.lineTo(cx + width, cy);
  21064. path.lineTo(cx, cy + height);
  21065. path.lineTo(cx - width, cy);
  21066. path.closePath();
  21067. }
  21068. });
  21069. /**
  21070. * Pin shape
  21071. * @inner
  21072. */
  21073. var Pin = extendShape({
  21074. type: 'pin',
  21075. shape: {
  21076. // x, y on the cusp
  21077. x: 0,
  21078. y: 0,
  21079. width: 0,
  21080. height: 0
  21081. },
  21082. buildPath: function (path, shape) {
  21083. var x = shape.x;
  21084. var y = shape.y;
  21085. var w = shape.width / 5 * 3;
  21086. // Height must be larger than width
  21087. var h = Math.max(w, shape.height);
  21088. var r = w / 2;
  21089. // Dist on y with tangent point and circle center
  21090. var dy = r * r / (h - r);
  21091. var cy = y - h + r + dy;
  21092. var angle = Math.asin(dy / r);
  21093. // Dist on x with tangent point and circle center
  21094. var dx = Math.cos(angle) * r;
  21095. var tanX = Math.sin(angle);
  21096. var tanY = Math.cos(angle);
  21097. var cpLen = r * 0.6;
  21098. var cpLen2 = r * 0.7;
  21099. path.moveTo(x - dx, cy + dy);
  21100. path.arc(
  21101. x, cy, r,
  21102. Math.PI - angle,
  21103. Math.PI * 2 + angle
  21104. );
  21105. path.bezierCurveTo(
  21106. x + dx - tanX * cpLen, cy + dy + tanY * cpLen,
  21107. x, y - cpLen2,
  21108. x, y
  21109. );
  21110. path.bezierCurveTo(
  21111. x, y - cpLen2,
  21112. x - dx + tanX * cpLen, cy + dy + tanY * cpLen,
  21113. x - dx, cy + dy
  21114. );
  21115. path.closePath();
  21116. }
  21117. });
  21118. /**
  21119. * Arrow shape
  21120. * @inner
  21121. */
  21122. var Arrow = extendShape({
  21123. type: 'arrow',
  21124. shape: {
  21125. x: 0,
  21126. y: 0,
  21127. width: 0,
  21128. height: 0
  21129. },
  21130. buildPath: function (ctx, shape) {
  21131. var height = shape.height;
  21132. var width = shape.width;
  21133. var x = shape.x;
  21134. var y = shape.y;
  21135. var dx = width / 3 * 2;
  21136. ctx.moveTo(x, y);
  21137. ctx.lineTo(x + dx, y + height);
  21138. ctx.lineTo(x, y + height / 4 * 3);
  21139. ctx.lineTo(x - dx, y + height);
  21140. ctx.lineTo(x, y);
  21141. ctx.closePath();
  21142. }
  21143. });
  21144. /**
  21145. * Map of path contructors
  21146. * @type {Object.<string, module:zrender/graphic/Path>}
  21147. */
  21148. var symbolCtors = {
  21149. line: Line,
  21150. rect: Rect,
  21151. roundRect: Rect,
  21152. square: Rect,
  21153. circle: Circle,
  21154. diamond: Diamond,
  21155. pin: Pin,
  21156. arrow: Arrow,
  21157. triangle: Triangle
  21158. };
  21159. var symbolShapeMakers = {
  21160. line: function (x, y, w, h, shape) {
  21161. // FIXME
  21162. shape.x1 = x;
  21163. shape.y1 = y + h / 2;
  21164. shape.x2 = x + w;
  21165. shape.y2 = y + h / 2;
  21166. },
  21167. rect: function (x, y, w, h, shape) {
  21168. shape.x = x;
  21169. shape.y = y;
  21170. shape.width = w;
  21171. shape.height = h;
  21172. },
  21173. roundRect: function (x, y, w, h, shape) {
  21174. shape.x = x;
  21175. shape.y = y;
  21176. shape.width = w;
  21177. shape.height = h;
  21178. shape.r = Math.min(w, h) / 4;
  21179. },
  21180. square: function (x, y, w, h, shape) {
  21181. var size = Math.min(w, h);
  21182. shape.x = x;
  21183. shape.y = y;
  21184. shape.width = size;
  21185. shape.height = size;
  21186. },
  21187. circle: function (x, y, w, h, shape) {
  21188. // Put circle in the center of square
  21189. shape.cx = x + w / 2;
  21190. shape.cy = y + h / 2;
  21191. shape.r = Math.min(w, h) / 2;
  21192. },
  21193. diamond: function (x, y, w, h, shape) {
  21194. shape.cx = x + w / 2;
  21195. shape.cy = y + h / 2;
  21196. shape.width = w;
  21197. shape.height = h;
  21198. },
  21199. pin: function (x, y, w, h, shape) {
  21200. shape.x = x + w / 2;
  21201. shape.y = y + h / 2;
  21202. shape.width = w;
  21203. shape.height = h;
  21204. },
  21205. arrow: function (x, y, w, h, shape) {
  21206. shape.x = x + w / 2;
  21207. shape.y = y + h / 2;
  21208. shape.width = w;
  21209. shape.height = h;
  21210. },
  21211. triangle: function (x, y, w, h, shape) {
  21212. shape.cx = x + w / 2;
  21213. shape.cy = y + h / 2;
  21214. shape.width = w;
  21215. shape.height = h;
  21216. }
  21217. };
  21218. var symbolBuildProxies = {};
  21219. each$1(symbolCtors, function (Ctor, name) {
  21220. symbolBuildProxies[name] = new Ctor();
  21221. });
  21222. var SymbolClz$2 = extendShape({
  21223. type: 'symbol',
  21224. shape: {
  21225. symbolType: '',
  21226. x: 0,
  21227. y: 0,
  21228. width: 0,
  21229. height: 0
  21230. },
  21231. beforeBrush: function () {
  21232. var style = this.style;
  21233. var shape = this.shape;
  21234. // FIXME
  21235. if (shape.symbolType === 'pin' && style.textPosition === 'inside') {
  21236. style.textPosition = ['50%', '40%'];
  21237. style.textAlign = 'center';
  21238. style.textVerticalAlign = 'middle';
  21239. }
  21240. },
  21241. buildPath: function (ctx, shape, inBundle) {
  21242. var symbolType = shape.symbolType;
  21243. var proxySymbol = symbolBuildProxies[symbolType];
  21244. if (shape.symbolType !== 'none') {
  21245. if (!proxySymbol) {
  21246. // Default rect
  21247. symbolType = 'rect';
  21248. proxySymbol = symbolBuildProxies[symbolType];
  21249. }
  21250. symbolShapeMakers[symbolType](
  21251. shape.x, shape.y, shape.width, shape.height, proxySymbol.shape
  21252. );
  21253. proxySymbol.buildPath(ctx, proxySymbol.shape, inBundle);
  21254. }
  21255. }
  21256. });
  21257. // Provide setColor helper method to avoid determine if set the fill or stroke outside
  21258. function symbolPathSetColor(color, innerColor) {
  21259. if (this.type !== 'image') {
  21260. var symbolStyle = this.style;
  21261. var symbolShape = this.shape;
  21262. if (symbolShape && symbolShape.symbolType === 'line') {
  21263. symbolStyle.stroke = color;
  21264. }
  21265. else if (this.__isEmptyBrush) {
  21266. symbolStyle.stroke = color;
  21267. symbolStyle.fill = innerColor || '#fff';
  21268. }
  21269. else {
  21270. // FIXME 判断图形默认是填充还是描边,使用 onlyStroke ?
  21271. symbolStyle.fill && (symbolStyle.fill = color);
  21272. symbolStyle.stroke && (symbolStyle.stroke = color);
  21273. }
  21274. this.dirty(false);
  21275. }
  21276. }
  21277. /**
  21278. * Create a symbol element with given symbol configuration: shape, x, y, width, height, color
  21279. * @param {string} symbolType
  21280. * @param {number} x
  21281. * @param {number} y
  21282. * @param {number} w
  21283. * @param {number} h
  21284. * @param {string} color
  21285. * @param {boolean} [keepAspect=false] whether to keep the ratio of w/h,
  21286. * for path and image only.
  21287. */
  21288. function createSymbol(symbolType, x, y, w, h, color, keepAspect) {
  21289. // TODO Support image object, DynamicImage.
  21290. var isEmpty = symbolType.indexOf('empty') === 0;
  21291. if (isEmpty) {
  21292. symbolType = symbolType.substr(5, 1).toLowerCase() + symbolType.substr(6);
  21293. }
  21294. var symbolPath;
  21295. if (symbolType.indexOf('image://') === 0) {
  21296. symbolPath = makeImage(
  21297. symbolType.slice(8),
  21298. new BoundingRect(x, y, w, h),
  21299. keepAspect ? 'center' : 'cover'
  21300. );
  21301. }
  21302. else if (symbolType.indexOf('path://') === 0) {
  21303. symbolPath = makePath(
  21304. symbolType.slice(7),
  21305. {},
  21306. new BoundingRect(x, y, w, h),
  21307. keepAspect ? 'center' : 'cover'
  21308. );
  21309. }
  21310. else {
  21311. symbolPath = new SymbolClz$2({
  21312. shape: {
  21313. symbolType: symbolType,
  21314. x: x,
  21315. y: y,
  21316. width: w,
  21317. height: h
  21318. }
  21319. });
  21320. }
  21321. symbolPath.__isEmptyBrush = isEmpty;
  21322. symbolPath.setColor = symbolPathSetColor;
  21323. symbolPath.setColor(color);
  21324. return symbolPath;
  21325. }
  21326. /**
  21327. * @module echarts/chart/helper/Symbol
  21328. */
  21329. function findLabelValueDim(data) {
  21330. var valueDim;
  21331. var labelDims = otherDimToDataDim(data, 'label');
  21332. if (labelDims.length) {
  21333. valueDim = labelDims[0];
  21334. }
  21335. else {
  21336. // Get last value dim
  21337. var dimensions = data.dimensions.slice();
  21338. var dataType;
  21339. while (dimensions.length && (
  21340. valueDim = dimensions.pop(),
  21341. dataType = data.getDimensionInfo(valueDim).type,
  21342. dataType === 'ordinal' || dataType === 'time'
  21343. )) {} // jshint ignore:line
  21344. }
  21345. return valueDim;
  21346. }
  21347. /**
  21348. * @module echarts/chart/helper/Symbol
  21349. */
  21350. function getSymbolSize(data, idx) {
  21351. var symbolSize = data.getItemVisual(idx, 'symbolSize');
  21352. return symbolSize instanceof Array
  21353. ? symbolSize.slice()
  21354. : [+symbolSize, +symbolSize];
  21355. }
  21356. function getScale(symbolSize) {
  21357. return [symbolSize[0] / 2, symbolSize[1] / 2];
  21358. }
  21359. /**
  21360. * @constructor
  21361. * @alias {module:echarts/chart/helper/Symbol}
  21362. * @param {module:echarts/data/List} data
  21363. * @param {number} idx
  21364. * @extends {module:zrender/graphic/Group}
  21365. */
  21366. function SymbolClz(data, idx, seriesScope) {
  21367. Group.call(this);
  21368. this.updateData(data, idx, seriesScope);
  21369. }
  21370. var symbolProto = SymbolClz.prototype;
  21371. function driftSymbol(dx, dy) {
  21372. this.parent.drift(dx, dy);
  21373. }
  21374. symbolProto._createSymbol = function (symbolType, data, idx, symbolSize) {
  21375. // Remove paths created before
  21376. this.removeAll();
  21377. var color = data.getItemVisual(idx, 'color');
  21378. // var symbolPath = createSymbol(
  21379. // symbolType, -0.5, -0.5, 1, 1, color
  21380. // );
  21381. // If width/height are set too small (e.g., set to 1) on ios10
  21382. // and macOS Sierra, a circle stroke become a rect, no matter what
  21383. // the scale is set. So we set width/height as 2. See #4150.
  21384. var symbolPath = createSymbol(
  21385. symbolType, -1, -1, 2, 2, color
  21386. );
  21387. symbolPath.attr({
  21388. z2: 100,
  21389. culling: true,
  21390. scale: getScale(symbolSize)
  21391. });
  21392. // Rewrite drift method
  21393. symbolPath.drift = driftSymbol;
  21394. this._symbolType = symbolType;
  21395. this.add(symbolPath);
  21396. };
  21397. /**
  21398. * Stop animation
  21399. * @param {boolean} toLastFrame
  21400. */
  21401. symbolProto.stopSymbolAnimation = function (toLastFrame) {
  21402. this.childAt(0).stopAnimation(toLastFrame);
  21403. };
  21404. /**
  21405. * FIXME:
  21406. * Caution: This method breaks the encapsulation of this module,
  21407. * but it indeed brings convenience. So do not use the method
  21408. * unless you detailedly know all the implements of `Symbol`,
  21409. * especially animation.
  21410. *
  21411. * Get symbol path element.
  21412. */
  21413. symbolProto.getSymbolPath = function () {
  21414. return this.childAt(0);
  21415. };
  21416. /**
  21417. * Get scale(aka, current symbol size).
  21418. * Including the change caused by animation
  21419. */
  21420. symbolProto.getScale = function () {
  21421. return this.childAt(0).scale;
  21422. };
  21423. /**
  21424. * Highlight symbol
  21425. */
  21426. symbolProto.highlight = function () {
  21427. this.childAt(0).trigger('emphasis');
  21428. };
  21429. /**
  21430. * Downplay symbol
  21431. */
  21432. symbolProto.downplay = function () {
  21433. this.childAt(0).trigger('normal');
  21434. };
  21435. /**
  21436. * @param {number} zlevel
  21437. * @param {number} z
  21438. */
  21439. symbolProto.setZ = function (zlevel, z) {
  21440. var symbolPath = this.childAt(0);
  21441. symbolPath.zlevel = zlevel;
  21442. symbolPath.z = z;
  21443. };
  21444. symbolProto.setDraggable = function (draggable) {
  21445. var symbolPath = this.childAt(0);
  21446. symbolPath.draggable = draggable;
  21447. symbolPath.cursor = draggable ? 'move' : 'pointer';
  21448. };
  21449. /**
  21450. * Update symbol properties
  21451. * @param {module:echarts/data/List} data
  21452. * @param {number} idx
  21453. * @param {Object} [seriesScope]
  21454. * @param {Object} [seriesScope.itemStyle]
  21455. * @param {Object} [seriesScope.hoverItemStyle]
  21456. * @param {Object} [seriesScope.symbolRotate]
  21457. * @param {Object} [seriesScope.symbolOffset]
  21458. * @param {module:echarts/model/Model} [seriesScope.labelModel]
  21459. * @param {module:echarts/model/Model} [seriesScope.hoverLabelModel]
  21460. * @param {boolean} [seriesScope.hoverAnimation]
  21461. * @param {Object} [seriesScope.cursorStyle]
  21462. * @param {module:echarts/model/Model} [seriesScope.itemModel]
  21463. * @param {string} [seriesScope.symbolInnerColor]
  21464. * @param {Object} [seriesScope.fadeIn=false]
  21465. */
  21466. symbolProto.updateData = function (data, idx, seriesScope) {
  21467. this.silent = false;
  21468. var symbolType = data.getItemVisual(idx, 'symbol') || 'circle';
  21469. var seriesModel = data.hostModel;
  21470. var symbolSize = getSymbolSize(data, idx);
  21471. var isInit = symbolType !== this._symbolType;
  21472. if (isInit) {
  21473. this._createSymbol(symbolType, data, idx, symbolSize);
  21474. }
  21475. else {
  21476. var symbolPath = this.childAt(0);
  21477. symbolPath.silent = false;
  21478. updateProps(symbolPath, {
  21479. scale: getScale(symbolSize)
  21480. }, seriesModel, idx);
  21481. }
  21482. this._updateCommon(data, idx, symbolSize, seriesScope);
  21483. if (isInit) {
  21484. var symbolPath = this.childAt(0);
  21485. var fadeIn = seriesScope && seriesScope.fadeIn;
  21486. var target = {scale: symbolPath.scale.slice()};
  21487. fadeIn && (target.style = {opacity: symbolPath.style.opacity});
  21488. symbolPath.scale = [0, 0];
  21489. fadeIn && (symbolPath.style.opacity = 0);
  21490. initProps(symbolPath, target, seriesModel, idx);
  21491. }
  21492. this._seriesModel = seriesModel;
  21493. };
  21494. // Update common properties
  21495. var normalStyleAccessPath = ['itemStyle', 'normal'];
  21496. var emphasisStyleAccessPath = ['itemStyle', 'emphasis'];
  21497. var normalLabelAccessPath = ['label', 'normal'];
  21498. var emphasisLabelAccessPath = ['label', 'emphasis'];
  21499. /**
  21500. * @param {module:echarts/data/List} data
  21501. * @param {number} idx
  21502. * @param {Array.<number>} symbolSize
  21503. * @param {Object} [seriesScope]
  21504. */
  21505. symbolProto._updateCommon = function (data, idx, symbolSize, seriesScope) {
  21506. var symbolPath = this.childAt(0);
  21507. var seriesModel = data.hostModel;
  21508. var color = data.getItemVisual(idx, 'color');
  21509. // Reset style
  21510. if (symbolPath.type !== 'image') {
  21511. symbolPath.useStyle({
  21512. strokeNoScale: true
  21513. });
  21514. }
  21515. var itemStyle = seriesScope && seriesScope.itemStyle;
  21516. var hoverItemStyle = seriesScope && seriesScope.hoverItemStyle;
  21517. var symbolRotate = seriesScope && seriesScope.symbolRotate;
  21518. var symbolOffset = seriesScope && seriesScope.symbolOffset;
  21519. var labelModel = seriesScope && seriesScope.labelModel;
  21520. var hoverLabelModel = seriesScope && seriesScope.hoverLabelModel;
  21521. var hoverAnimation = seriesScope && seriesScope.hoverAnimation;
  21522. var cursorStyle = seriesScope && seriesScope.cursorStyle;
  21523. if (!seriesScope || data.hasItemOption) {
  21524. var itemModel = (seriesScope && seriesScope.itemModel)
  21525. ? seriesScope.itemModel : data.getItemModel(idx);
  21526. // Color must be excluded.
  21527. // Because symbol provide setColor individually to set fill and stroke
  21528. itemStyle = itemModel.getModel(normalStyleAccessPath).getItemStyle(['color']);
  21529. hoverItemStyle = itemModel.getModel(emphasisStyleAccessPath).getItemStyle();
  21530. symbolRotate = itemModel.getShallow('symbolRotate');
  21531. symbolOffset = itemModel.getShallow('symbolOffset');
  21532. labelModel = itemModel.getModel(normalLabelAccessPath);
  21533. hoverLabelModel = itemModel.getModel(emphasisLabelAccessPath);
  21534. hoverAnimation = itemModel.getShallow('hoverAnimation');
  21535. cursorStyle = itemModel.getShallow('cursor');
  21536. }
  21537. else {
  21538. hoverItemStyle = extend({}, hoverItemStyle);
  21539. }
  21540. var elStyle = symbolPath.style;
  21541. symbolPath.attr('rotation', (symbolRotate || 0) * Math.PI / 180 || 0);
  21542. if (symbolOffset) {
  21543. symbolPath.attr('position', [
  21544. parsePercent$1(symbolOffset[0], symbolSize[0]),
  21545. parsePercent$1(symbolOffset[1], symbolSize[1])
  21546. ]);
  21547. }
  21548. cursorStyle && symbolPath.attr('cursor', cursorStyle);
  21549. // PENDING setColor before setStyle!!!
  21550. symbolPath.setColor(color, seriesScope && seriesScope.symbolInnerColor);
  21551. symbolPath.setStyle(itemStyle);
  21552. var opacity = data.getItemVisual(idx, 'opacity');
  21553. if (opacity != null) {
  21554. elStyle.opacity = opacity;
  21555. }
  21556. var useNameLabel = seriesScope && seriesScope.useNameLabel;
  21557. var valueDim = !useNameLabel && findLabelValueDim(data);
  21558. if (useNameLabel || valueDim != null) {
  21559. setLabelStyle(
  21560. elStyle, hoverItemStyle, labelModel, hoverLabelModel,
  21561. {
  21562. labelFetcher: seriesModel,
  21563. labelDataIndex: idx,
  21564. defaultText: useNameLabel ? data.getName(idx) : data.get(valueDim, idx),
  21565. isRectText: true,
  21566. autoColor: color
  21567. }
  21568. );
  21569. }
  21570. symbolPath.off('mouseover')
  21571. .off('mouseout')
  21572. .off('emphasis')
  21573. .off('normal');
  21574. symbolPath.hoverStyle = hoverItemStyle;
  21575. // FIXME
  21576. // Do not use symbol.trigger('emphasis'), but use symbol.highlight() instead.
  21577. setHoverStyle(symbolPath);
  21578. var scale = getScale(symbolSize);
  21579. if (hoverAnimation && seriesModel.isAnimationEnabled()) {
  21580. var onEmphasis = function() {
  21581. var ratio = scale[1] / scale[0];
  21582. this.animateTo({
  21583. scale: [
  21584. Math.max(scale[0] * 1.1, scale[0] + 3),
  21585. Math.max(scale[1] * 1.1, scale[1] + 3 * ratio)
  21586. ]
  21587. }, 400, 'elasticOut');
  21588. };
  21589. var onNormal = function() {
  21590. this.animateTo({
  21591. scale: scale
  21592. }, 400, 'elasticOut');
  21593. };
  21594. symbolPath.on('mouseover', onEmphasis)
  21595. .on('mouseout', onNormal)
  21596. .on('emphasis', onEmphasis)
  21597. .on('normal', onNormal);
  21598. }
  21599. };
  21600. /**
  21601. * @param {Function} cb
  21602. * @param {Object} [opt]
  21603. * @param {Object} [opt.keepLabel=true]
  21604. */
  21605. symbolProto.fadeOut = function (cb, opt) {
  21606. var symbolPath = this.childAt(0);
  21607. // Avoid mistaken hover when fading out
  21608. this.silent = symbolPath.silent = true;
  21609. // Not show text when animating
  21610. !(opt && opt.keepLabel) && (symbolPath.style.text = null);
  21611. updateProps(
  21612. symbolPath,
  21613. {
  21614. style: {opacity: 0},
  21615. scale: [0, 0]
  21616. },
  21617. this._seriesModel,
  21618. this.dataIndex,
  21619. cb
  21620. );
  21621. };
  21622. inherits(SymbolClz, Group);
  21623. /**
  21624. * @module echarts/chart/helper/SymbolDraw
  21625. */
  21626. /**
  21627. * @constructor
  21628. * @alias module:echarts/chart/helper/SymbolDraw
  21629. * @param {module:zrender/graphic/Group} [symbolCtor]
  21630. */
  21631. function SymbolDraw(symbolCtor) {
  21632. this.group = new Group();
  21633. this._symbolCtor = symbolCtor || SymbolClz;
  21634. }
  21635. var symbolDrawProto = SymbolDraw.prototype;
  21636. function symbolNeedsDraw(data, idx, isIgnore) {
  21637. var point = data.getItemLayout(idx);
  21638. // Is an object
  21639. // if (point && point.hasOwnProperty('point')) {
  21640. // point = point.point;
  21641. // }
  21642. return point && !isNaN(point[0]) && !isNaN(point[1]) && !(isIgnore && isIgnore(idx))
  21643. && data.getItemVisual(idx, 'symbol') !== 'none';
  21644. }
  21645. /**
  21646. * Update symbols draw by new data
  21647. * @param {module:echarts/data/List} data
  21648. * @param {Array.<boolean>} [isIgnore]
  21649. */
  21650. symbolDrawProto.updateData = function (data, isIgnore) {
  21651. var group = this.group;
  21652. var seriesModel = data.hostModel;
  21653. var oldData = this._data;
  21654. var SymbolCtor = this._symbolCtor;
  21655. var seriesScope = {
  21656. itemStyle: seriesModel.getModel('itemStyle.normal').getItemStyle(['color']),
  21657. hoverItemStyle: seriesModel.getModel('itemStyle.emphasis').getItemStyle(),
  21658. symbolRotate: seriesModel.get('symbolRotate'),
  21659. symbolOffset: seriesModel.get('symbolOffset'),
  21660. hoverAnimation: seriesModel.get('hoverAnimation'),
  21661. labelModel: seriesModel.getModel('label.normal'),
  21662. hoverLabelModel: seriesModel.getModel('label.emphasis'),
  21663. cursorStyle: seriesModel.get('cursor')
  21664. };
  21665. data.diff(oldData)
  21666. .add(function (newIdx) {
  21667. var point = data.getItemLayout(newIdx);
  21668. if (symbolNeedsDraw(data, newIdx, isIgnore)) {
  21669. var symbolEl = new SymbolCtor(data, newIdx, seriesScope);
  21670. symbolEl.attr('position', point);
  21671. data.setItemGraphicEl(newIdx, symbolEl);
  21672. group.add(symbolEl);
  21673. }
  21674. })
  21675. .update(function (newIdx, oldIdx) {
  21676. var symbolEl = oldData.getItemGraphicEl(oldIdx);
  21677. var point = data.getItemLayout(newIdx);
  21678. if (!symbolNeedsDraw(data, newIdx, isIgnore)) {
  21679. group.remove(symbolEl);
  21680. return;
  21681. }
  21682. if (!symbolEl) {
  21683. symbolEl = new SymbolCtor(data, newIdx);
  21684. symbolEl.attr('position', point);
  21685. }
  21686. else {
  21687. symbolEl.updateData(data, newIdx, seriesScope);
  21688. updateProps(symbolEl, {
  21689. position: point
  21690. }, seriesModel);
  21691. }
  21692. // Add back
  21693. group.add(symbolEl);
  21694. data.setItemGraphicEl(newIdx, symbolEl);
  21695. })
  21696. .remove(function (oldIdx) {
  21697. var el = oldData.getItemGraphicEl(oldIdx);
  21698. el && el.fadeOut(function () {
  21699. group.remove(el);
  21700. });
  21701. })
  21702. .execute();
  21703. this._data = data;
  21704. };
  21705. symbolDrawProto.updateLayout = function () {
  21706. var data = this._data;
  21707. if (data) {
  21708. // Not use animation
  21709. data.eachItemGraphicEl(function (el, idx) {
  21710. var point = data.getItemLayout(idx);
  21711. el.attr('position', point);
  21712. });
  21713. }
  21714. };
  21715. symbolDrawProto.remove = function (enableAnimation) {
  21716. var group = this.group;
  21717. var data = this._data;
  21718. if (data) {
  21719. if (enableAnimation) {
  21720. data.eachItemGraphicEl(function (el) {
  21721. el.fadeOut(function () {
  21722. group.remove(el);
  21723. });
  21724. });
  21725. }
  21726. else {
  21727. group.removeAll();
  21728. }
  21729. }
  21730. };
  21731. // var arrayDiff = require('zrender/src/core/arrayDiff');
  21732. // 'zrender/src/core/arrayDiff' has been used before, but it did
  21733. // not do well in performance when roam with fixed dataZoom window.
  21734. function sign$1(val) {
  21735. return val >= 0 ? 1 : -1;
  21736. }
  21737. function getStackedOnPoint(coordSys, data, idx) {
  21738. var baseAxis = coordSys.getBaseAxis();
  21739. var valueAxis = coordSys.getOtherAxis(baseAxis);
  21740. var valueStart = baseAxis.onZero
  21741. ? 0 : valueAxis.scale.getExtent()[0];
  21742. var valueDim = valueAxis.dim;
  21743. var baseDataOffset = valueDim === 'x' || valueDim === 'radius' ? 1 : 0;
  21744. var stackedOnSameSign;
  21745. var stackedOn = data.stackedOn;
  21746. var val = data.get(valueDim, idx);
  21747. // Find first stacked value with same sign
  21748. while (stackedOn &&
  21749. sign$1(stackedOn.get(valueDim, idx)) === sign$1(val)
  21750. ) {
  21751. stackedOnSameSign = stackedOn;
  21752. break;
  21753. }
  21754. var stackedData = [];
  21755. stackedData[baseDataOffset] = data.get(baseAxis.dim, idx);
  21756. stackedData[1 - baseDataOffset] = stackedOnSameSign
  21757. ? stackedOnSameSign.get(valueDim, idx, true) : valueStart;
  21758. return coordSys.dataToPoint(stackedData);
  21759. }
  21760. // function convertToIntId(newIdList, oldIdList) {
  21761. // // Generate int id instead of string id.
  21762. // // Compare string maybe slow in score function of arrDiff
  21763. // // Assume id in idList are all unique
  21764. // var idIndicesMap = {};
  21765. // var idx = 0;
  21766. // for (var i = 0; i < newIdList.length; i++) {
  21767. // idIndicesMap[newIdList[i]] = idx;
  21768. // newIdList[i] = idx++;
  21769. // }
  21770. // for (var i = 0; i < oldIdList.length; i++) {
  21771. // var oldId = oldIdList[i];
  21772. // // Same with newIdList
  21773. // if (idIndicesMap[oldId]) {
  21774. // oldIdList[i] = idIndicesMap[oldId];
  21775. // }
  21776. // else {
  21777. // oldIdList[i] = idx++;
  21778. // }
  21779. // }
  21780. // }
  21781. function diffData(oldData, newData) {
  21782. var diffResult = [];
  21783. newData.diff(oldData)
  21784. .add(function (idx) {
  21785. diffResult.push({cmd: '+', idx: idx});
  21786. })
  21787. .update(function (newIdx, oldIdx) {
  21788. diffResult.push({cmd: '=', idx: oldIdx, idx1: newIdx});
  21789. })
  21790. .remove(function (idx) {
  21791. diffResult.push({cmd: '-', idx: idx});
  21792. })
  21793. .execute();
  21794. return diffResult;
  21795. }
  21796. var lineAnimationDiff = function (
  21797. oldData, newData,
  21798. oldStackedOnPoints, newStackedOnPoints,
  21799. oldCoordSys, newCoordSys
  21800. ) {
  21801. var diff = diffData(oldData, newData);
  21802. // var newIdList = newData.mapArray(newData.getId);
  21803. // var oldIdList = oldData.mapArray(oldData.getId);
  21804. // convertToIntId(newIdList, oldIdList);
  21805. // // FIXME One data ?
  21806. // diff = arrayDiff(oldIdList, newIdList);
  21807. var currPoints = [];
  21808. var nextPoints = [];
  21809. // Points for stacking base line
  21810. var currStackedPoints = [];
  21811. var nextStackedPoints = [];
  21812. var status = [];
  21813. var sortedIndices = [];
  21814. var rawIndices = [];
  21815. var dims = newCoordSys.dimensions;
  21816. for (var i = 0; i < diff.length; i++) {
  21817. var diffItem = diff[i];
  21818. var pointAdded = true;
  21819. // FIXME, animation is not so perfect when dataZoom window moves fast
  21820. // Which is in case remvoing or add more than one data in the tail or head
  21821. switch (diffItem.cmd) {
  21822. case '=':
  21823. var currentPt = oldData.getItemLayout(diffItem.idx);
  21824. var nextPt = newData.getItemLayout(diffItem.idx1);
  21825. // If previous data is NaN, use next point directly
  21826. if (isNaN(currentPt[0]) || isNaN(currentPt[1])) {
  21827. currentPt = nextPt.slice();
  21828. }
  21829. currPoints.push(currentPt);
  21830. nextPoints.push(nextPt);
  21831. currStackedPoints.push(oldStackedOnPoints[diffItem.idx]);
  21832. nextStackedPoints.push(newStackedOnPoints[diffItem.idx1]);
  21833. rawIndices.push(newData.getRawIndex(diffItem.idx1));
  21834. break;
  21835. case '+':
  21836. var idx = diffItem.idx;
  21837. currPoints.push(
  21838. oldCoordSys.dataToPoint([
  21839. newData.get(dims[0], idx, true), newData.get(dims[1], idx, true)
  21840. ])
  21841. );
  21842. nextPoints.push(newData.getItemLayout(idx).slice());
  21843. currStackedPoints.push(
  21844. getStackedOnPoint(oldCoordSys, newData, idx)
  21845. );
  21846. nextStackedPoints.push(newStackedOnPoints[idx]);
  21847. rawIndices.push(newData.getRawIndex(idx));
  21848. break;
  21849. case '-':
  21850. var idx = diffItem.idx;
  21851. var rawIndex = oldData.getRawIndex(idx);
  21852. // Data is replaced. In the case of dynamic data queue
  21853. // FIXME FIXME FIXME
  21854. if (rawIndex !== idx) {
  21855. currPoints.push(oldData.getItemLayout(idx));
  21856. nextPoints.push(newCoordSys.dataToPoint([
  21857. oldData.get(dims[0], idx, true), oldData.get(dims[1], idx, true)
  21858. ]));
  21859. currStackedPoints.push(oldStackedOnPoints[idx]);
  21860. nextStackedPoints.push(
  21861. getStackedOnPoint(
  21862. newCoordSys, oldData, idx
  21863. )
  21864. );
  21865. rawIndices.push(rawIndex);
  21866. }
  21867. else {
  21868. pointAdded = false;
  21869. }
  21870. }
  21871. // Original indices
  21872. if (pointAdded) {
  21873. status.push(diffItem);
  21874. sortedIndices.push(sortedIndices.length);
  21875. }
  21876. }
  21877. // Diff result may be crossed if all items are changed
  21878. // Sort by data index
  21879. sortedIndices.sort(function (a, b) {
  21880. return rawIndices[a] - rawIndices[b];
  21881. });
  21882. var sortedCurrPoints = [];
  21883. var sortedNextPoints = [];
  21884. var sortedCurrStackedPoints = [];
  21885. var sortedNextStackedPoints = [];
  21886. var sortedStatus = [];
  21887. for (var i = 0; i < sortedIndices.length; i++) {
  21888. var idx = sortedIndices[i];
  21889. sortedCurrPoints[i] = currPoints[idx];
  21890. sortedNextPoints[i] = nextPoints[idx];
  21891. sortedCurrStackedPoints[i] = currStackedPoints[idx];
  21892. sortedNextStackedPoints[i] = nextStackedPoints[idx];
  21893. sortedStatus[i] = status[idx];
  21894. }
  21895. return {
  21896. current: sortedCurrPoints,
  21897. next: sortedNextPoints,
  21898. stackedOnCurrent: sortedCurrStackedPoints,
  21899. stackedOnNext: sortedNextStackedPoints,
  21900. status: sortedStatus
  21901. };
  21902. };
  21903. // Poly path support NaN point
  21904. var vec2Min = min;
  21905. var vec2Max = max;
  21906. var scaleAndAdd$1 = scaleAndAdd;
  21907. var v2Copy = copy;
  21908. // Temporary variable
  21909. var v = [];
  21910. var cp0 = [];
  21911. var cp1 = [];
  21912. function isPointNull(p) {
  21913. return isNaN(p[0]) || isNaN(p[1]);
  21914. }
  21915. function drawSegment(
  21916. ctx, points, start, segLen, allLen,
  21917. dir, smoothMin, smoothMax, smooth, smoothMonotone, connectNulls
  21918. ) {
  21919. var prevIdx = 0;
  21920. var idx = start;
  21921. for (var k = 0; k < segLen; k++) {
  21922. var p = points[idx];
  21923. if (idx >= allLen || idx < 0) {
  21924. break;
  21925. }
  21926. if (isPointNull(p)) {
  21927. if (connectNulls) {
  21928. idx += dir;
  21929. continue;
  21930. }
  21931. break;
  21932. }
  21933. if (idx === start) {
  21934. ctx[dir > 0 ? 'moveTo' : 'lineTo'](p[0], p[1]);
  21935. v2Copy(cp0, p);
  21936. }
  21937. else {
  21938. if (smooth > 0) {
  21939. var nextIdx = idx + dir;
  21940. var nextP = points[nextIdx];
  21941. if (connectNulls) {
  21942. // Find next point not null
  21943. while (nextP && isPointNull(points[nextIdx])) {
  21944. nextIdx += dir;
  21945. nextP = points[nextIdx];
  21946. }
  21947. }
  21948. var ratioNextSeg = 0.5;
  21949. var prevP = points[prevIdx];
  21950. var nextP = points[nextIdx];
  21951. // Last point
  21952. if (!nextP || isPointNull(nextP)) {
  21953. v2Copy(cp1, p);
  21954. }
  21955. else {
  21956. // If next data is null in not connect case
  21957. if (isPointNull(nextP) && !connectNulls) {
  21958. nextP = p;
  21959. }
  21960. sub(v, nextP, prevP);
  21961. var lenPrevSeg;
  21962. var lenNextSeg;
  21963. if (smoothMonotone === 'x' || smoothMonotone === 'y') {
  21964. var dim = smoothMonotone === 'x' ? 0 : 1;
  21965. lenPrevSeg = Math.abs(p[dim] - prevP[dim]);
  21966. lenNextSeg = Math.abs(p[dim] - nextP[dim]);
  21967. }
  21968. else {
  21969. lenPrevSeg = dist(p, prevP);
  21970. lenNextSeg = dist(p, nextP);
  21971. }
  21972. // Use ratio of seg length
  21973. ratioNextSeg = lenNextSeg / (lenNextSeg + lenPrevSeg);
  21974. scaleAndAdd$1(cp1, p, v, -smooth * (1 - ratioNextSeg));
  21975. }
  21976. // Smooth constraint
  21977. vec2Min(cp0, cp0, smoothMax);
  21978. vec2Max(cp0, cp0, smoothMin);
  21979. vec2Min(cp1, cp1, smoothMax);
  21980. vec2Max(cp1, cp1, smoothMin);
  21981. ctx.bezierCurveTo(
  21982. cp0[0], cp0[1],
  21983. cp1[0], cp1[1],
  21984. p[0], p[1]
  21985. );
  21986. // cp0 of next segment
  21987. scaleAndAdd$1(cp0, p, v, smooth * ratioNextSeg);
  21988. }
  21989. else {
  21990. ctx.lineTo(p[0], p[1]);
  21991. }
  21992. }
  21993. prevIdx = idx;
  21994. idx += dir;
  21995. }
  21996. return k;
  21997. }
  21998. function getBoundingBox(points, smoothConstraint) {
  21999. var ptMin = [Infinity, Infinity];
  22000. var ptMax = [-Infinity, -Infinity];
  22001. if (smoothConstraint) {
  22002. for (var i = 0; i < points.length; i++) {
  22003. var pt = points[i];
  22004. if (pt[0] < ptMin[0]) { ptMin[0] = pt[0]; }
  22005. if (pt[1] < ptMin[1]) { ptMin[1] = pt[1]; }
  22006. if (pt[0] > ptMax[0]) { ptMax[0] = pt[0]; }
  22007. if (pt[1] > ptMax[1]) { ptMax[1] = pt[1]; }
  22008. }
  22009. }
  22010. return {
  22011. min: smoothConstraint ? ptMin : ptMax,
  22012. max: smoothConstraint ? ptMax : ptMin
  22013. };
  22014. }
  22015. var Polyline$1 = Path.extend({
  22016. type: 'ec-polyline',
  22017. shape: {
  22018. points: [],
  22019. smooth: 0,
  22020. smoothConstraint: true,
  22021. smoothMonotone: null,
  22022. connectNulls: false
  22023. },
  22024. style: {
  22025. fill: null,
  22026. stroke: '#000'
  22027. },
  22028. brush: fixClipWithShadow(Path.prototype.brush),
  22029. buildPath: function (ctx, shape) {
  22030. var points = shape.points;
  22031. var i = 0;
  22032. var len$$1 = points.length;
  22033. var result = getBoundingBox(points, shape.smoothConstraint);
  22034. if (shape.connectNulls) {
  22035. // Must remove first and last null values avoid draw error in polygon
  22036. for (; len$$1 > 0; len$$1--) {
  22037. if (!isPointNull(points[len$$1 - 1])) {
  22038. break;
  22039. }
  22040. }
  22041. for (; i < len$$1; i++) {
  22042. if (!isPointNull(points[i])) {
  22043. break;
  22044. }
  22045. }
  22046. }
  22047. while (i < len$$1) {
  22048. i += drawSegment(
  22049. ctx, points, i, len$$1, len$$1,
  22050. 1, result.min, result.max, shape.smooth,
  22051. shape.smoothMonotone, shape.connectNulls
  22052. ) + 1;
  22053. }
  22054. }
  22055. });
  22056. var Polygon$1 = Path.extend({
  22057. type: 'ec-polygon',
  22058. shape: {
  22059. points: [],
  22060. // Offset between stacked base points and points
  22061. stackedOnPoints: [],
  22062. smooth: 0,
  22063. stackedOnSmooth: 0,
  22064. smoothConstraint: true,
  22065. smoothMonotone: null,
  22066. connectNulls: false
  22067. },
  22068. brush: fixClipWithShadow(Path.prototype.brush),
  22069. buildPath: function (ctx, shape) {
  22070. var points = shape.points;
  22071. var stackedOnPoints = shape.stackedOnPoints;
  22072. var i = 0;
  22073. var len$$1 = points.length;
  22074. var smoothMonotone = shape.smoothMonotone;
  22075. var bbox = getBoundingBox(points, shape.smoothConstraint);
  22076. var stackedOnBBox = getBoundingBox(stackedOnPoints, shape.smoothConstraint);
  22077. if (shape.connectNulls) {
  22078. // Must remove first and last null values avoid draw error in polygon
  22079. for (; len$$1 > 0; len$$1--) {
  22080. if (!isPointNull(points[len$$1 - 1])) {
  22081. break;
  22082. }
  22083. }
  22084. for (; i < len$$1; i++) {
  22085. if (!isPointNull(points[i])) {
  22086. break;
  22087. }
  22088. }
  22089. }
  22090. while (i < len$$1) {
  22091. var k = drawSegment(
  22092. ctx, points, i, len$$1, len$$1,
  22093. 1, bbox.min, bbox.max, shape.smooth,
  22094. smoothMonotone, shape.connectNulls
  22095. );
  22096. drawSegment(
  22097. ctx, stackedOnPoints, i + k - 1, k, len$$1,
  22098. -1, stackedOnBBox.min, stackedOnBBox.max, shape.stackedOnSmooth,
  22099. smoothMonotone, shape.connectNulls
  22100. );
  22101. i += k + 1;
  22102. ctx.closePath();
  22103. }
  22104. }
  22105. });
  22106. // FIXME step not support polar
  22107. function isPointsSame(points1, points2) {
  22108. if (points1.length !== points2.length) {
  22109. return;
  22110. }
  22111. for (var i = 0; i < points1.length; i++) {
  22112. var p1 = points1[i];
  22113. var p2 = points2[i];
  22114. if (p1[0] !== p2[0] || p1[1] !== p2[1]) {
  22115. return;
  22116. }
  22117. }
  22118. return true;
  22119. }
  22120. function getSmooth(smooth) {
  22121. return typeof (smooth) === 'number' ? smooth : (smooth ? 0.3 : 0);
  22122. }
  22123. function getAxisExtentWithGap(axis) {
  22124. var extent = axis.getGlobalExtent();
  22125. if (axis.onBand) {
  22126. // Remove extra 1px to avoid line miter in clipped edge
  22127. var halfBandWidth = axis.getBandWidth() / 2 - 1;
  22128. var dir = extent[1] > extent[0] ? 1 : -1;
  22129. extent[0] += dir * halfBandWidth;
  22130. extent[1] -= dir * halfBandWidth;
  22131. }
  22132. return extent;
  22133. }
  22134. function sign(val) {
  22135. return val >= 0 ? 1 : -1;
  22136. }
  22137. /**
  22138. * @param {module:echarts/coord/cartesian/Cartesian2D|module:echarts/coord/polar/Polar} coordSys
  22139. * @param {module:echarts/data/List} data
  22140. * @param {Array.<Array.<number>>} points
  22141. * @private
  22142. */
  22143. function getStackedOnPoints(coordSys, data) {
  22144. var baseAxis = coordSys.getBaseAxis();
  22145. var valueAxis = coordSys.getOtherAxis(baseAxis);
  22146. var valueStart = 0;
  22147. if (!baseAxis.onZero) {
  22148. var extent = valueAxis.scale.getExtent();
  22149. if (extent[0] > 0) {
  22150. // Both positive
  22151. valueStart = extent[0];
  22152. }
  22153. else if (extent[1] < 0) {
  22154. // Both negative
  22155. valueStart = extent[1];
  22156. }
  22157. // If is one positive, and one negative, onZero shall be true
  22158. }
  22159. var valueDim = valueAxis.dim;
  22160. var baseDataOffset = valueDim === 'x' || valueDim === 'radius' ? 1 : 0;
  22161. return data.mapArray([valueDim], function (val, idx) {
  22162. var stackedOnSameSign;
  22163. var stackedOn = data.stackedOn;
  22164. // Find first stacked value with same sign
  22165. while (stackedOn &&
  22166. sign(stackedOn.get(valueDim, idx)) === sign(val)
  22167. ) {
  22168. stackedOnSameSign = stackedOn;
  22169. break;
  22170. }
  22171. var stackedData = [];
  22172. stackedData[baseDataOffset] = data.get(baseAxis.dim, idx);
  22173. stackedData[1 - baseDataOffset] = stackedOnSameSign
  22174. ? stackedOnSameSign.get(valueDim, idx, true) : valueStart;
  22175. return coordSys.dataToPoint(stackedData);
  22176. }, true);
  22177. }
  22178. function createGridClipShape(cartesian, hasAnimation, seriesModel) {
  22179. var xExtent = getAxisExtentWithGap(cartesian.getAxis('x'));
  22180. var yExtent = getAxisExtentWithGap(cartesian.getAxis('y'));
  22181. var isHorizontal = cartesian.getBaseAxis().isHorizontal();
  22182. var x = Math.min(xExtent[0], xExtent[1]);
  22183. var y = Math.min(yExtent[0], yExtent[1]);
  22184. var width = Math.max(xExtent[0], xExtent[1]) - x;
  22185. var height = Math.max(yExtent[0], yExtent[1]) - y;
  22186. var lineWidth = seriesModel.get('lineStyle.normal.width') || 2;
  22187. // Expand clip shape to avoid clipping when line value exceeds axis
  22188. var expandSize = seriesModel.get('clipOverflow') ? lineWidth / 2 : Math.max(width, height);
  22189. if (isHorizontal) {
  22190. y -= expandSize;
  22191. height += expandSize * 2;
  22192. }
  22193. else {
  22194. x -= expandSize;
  22195. width += expandSize * 2;
  22196. }
  22197. var clipPath = new Rect({
  22198. shape: {
  22199. x: x,
  22200. y: y,
  22201. width: width,
  22202. height: height
  22203. }
  22204. });
  22205. if (hasAnimation) {
  22206. clipPath.shape[isHorizontal ? 'width' : 'height'] = 0;
  22207. initProps(clipPath, {
  22208. shape: {
  22209. width: width,
  22210. height: height
  22211. }
  22212. }, seriesModel);
  22213. }
  22214. return clipPath;
  22215. }
  22216. function createPolarClipShape(polar, hasAnimation, seriesModel) {
  22217. var angleAxis = polar.getAngleAxis();
  22218. var radiusAxis = polar.getRadiusAxis();
  22219. var radiusExtent = radiusAxis.getExtent();
  22220. var angleExtent = angleAxis.getExtent();
  22221. var RADIAN = Math.PI / 180;
  22222. var clipPath = new Sector({
  22223. shape: {
  22224. cx: polar.cx,
  22225. cy: polar.cy,
  22226. r0: radiusExtent[0],
  22227. r: radiusExtent[1],
  22228. startAngle: -angleExtent[0] * RADIAN,
  22229. endAngle: -angleExtent[1] * RADIAN,
  22230. clockwise: angleAxis.inverse
  22231. }
  22232. });
  22233. if (hasAnimation) {
  22234. clipPath.shape.endAngle = -angleExtent[0] * RADIAN;
  22235. initProps(clipPath, {
  22236. shape: {
  22237. endAngle: -angleExtent[1] * RADIAN
  22238. }
  22239. }, seriesModel);
  22240. }
  22241. return clipPath;
  22242. }
  22243. function createClipShape(coordSys, hasAnimation, seriesModel) {
  22244. return coordSys.type === 'polar'
  22245. ? createPolarClipShape(coordSys, hasAnimation, seriesModel)
  22246. : createGridClipShape(coordSys, hasAnimation, seriesModel);
  22247. }
  22248. function turnPointsIntoStep(points, coordSys, stepTurnAt) {
  22249. var baseAxis = coordSys.getBaseAxis();
  22250. var baseIndex = baseAxis.dim === 'x' || baseAxis.dim === 'radius' ? 0 : 1;
  22251. var stepPoints = [];
  22252. for (var i = 0; i < points.length - 1; i++) {
  22253. var nextPt = points[i + 1];
  22254. var pt = points[i];
  22255. stepPoints.push(pt);
  22256. var stepPt = [];
  22257. switch (stepTurnAt) {
  22258. case 'end':
  22259. stepPt[baseIndex] = nextPt[baseIndex];
  22260. stepPt[1 - baseIndex] = pt[1 - baseIndex];
  22261. // default is start
  22262. stepPoints.push(stepPt);
  22263. break;
  22264. case 'middle':
  22265. // default is start
  22266. var middle = (pt[baseIndex] + nextPt[baseIndex]) / 2;
  22267. var stepPt2 = [];
  22268. stepPt[baseIndex] = stepPt2[baseIndex] = middle;
  22269. stepPt[1 - baseIndex] = pt[1 - baseIndex];
  22270. stepPt2[1 - baseIndex] = nextPt[1 - baseIndex];
  22271. stepPoints.push(stepPt);
  22272. stepPoints.push(stepPt2);
  22273. break;
  22274. default:
  22275. stepPt[baseIndex] = pt[baseIndex];
  22276. stepPt[1 - baseIndex] = nextPt[1 - baseIndex];
  22277. // default is start
  22278. stepPoints.push(stepPt);
  22279. }
  22280. }
  22281. // Last points
  22282. points[i] && stepPoints.push(points[i]);
  22283. return stepPoints;
  22284. }
  22285. function getVisualGradient(data, coordSys) {
  22286. var visualMetaList = data.getVisual('visualMeta');
  22287. if (!visualMetaList || !visualMetaList.length || !data.count()) {
  22288. // When data.count() is 0, gradient range can not be calculated.
  22289. return;
  22290. }
  22291. var visualMeta;
  22292. for (var i = visualMetaList.length - 1; i >= 0; i--) {
  22293. // Can only be x or y
  22294. if (visualMetaList[i].dimension < 2) {
  22295. visualMeta = visualMetaList[i];
  22296. break;
  22297. }
  22298. }
  22299. if (!visualMeta || coordSys.type !== 'cartesian2d') {
  22300. if (__DEV__) {
  22301. console.warn('Visual map on line style only support x or y dimension.');
  22302. }
  22303. return;
  22304. }
  22305. // If the area to be rendered is bigger than area defined by LinearGradient,
  22306. // the canvas spec prescribes that the color of the first stop and the last
  22307. // stop should be used. But if two stops are added at offset 0, in effect
  22308. // browsers use the color of the second stop to render area outside
  22309. // LinearGradient. So we can only infinitesimally extend area defined in
  22310. // LinearGradient to render `outerColors`.
  22311. var dimension = visualMeta.dimension;
  22312. var dimName = data.dimensions[dimension];
  22313. var axis = coordSys.getAxis(dimName);
  22314. // dataToCoor mapping may not be linear, but must be monotonic.
  22315. var colorStops = map(visualMeta.stops, function (stop) {
  22316. return {
  22317. coord: axis.toGlobalCoord(axis.dataToCoord(stop.value)),
  22318. color: stop.color
  22319. };
  22320. });
  22321. var stopLen = colorStops.length;
  22322. var outerColors = visualMeta.outerColors.slice();
  22323. if (stopLen && colorStops[0].coord > colorStops[stopLen - 1].coord) {
  22324. colorStops.reverse();
  22325. outerColors.reverse();
  22326. }
  22327. var tinyExtent = 10; // Arbitrary value: 10px
  22328. var minCoord = colorStops[0].coord - tinyExtent;
  22329. var maxCoord = colorStops[stopLen - 1].coord + tinyExtent;
  22330. var coordSpan = maxCoord - minCoord;
  22331. if (coordSpan < 1e-3) {
  22332. return 'transparent';
  22333. }
  22334. each$1(colorStops, function (stop) {
  22335. stop.offset = (stop.coord - minCoord) / coordSpan;
  22336. });
  22337. colorStops.push({
  22338. offset: stopLen ? colorStops[stopLen - 1].offset : 0.5,
  22339. color: outerColors[1] || 'transparent'
  22340. });
  22341. colorStops.unshift({ // notice colorStops.length have been changed.
  22342. offset: stopLen ? colorStops[0].offset : 0.5,
  22343. color: outerColors[0] || 'transparent'
  22344. });
  22345. // zrUtil.each(colorStops, function (colorStop) {
  22346. // // Make sure each offset has rounded px to avoid not sharp edge
  22347. // colorStop.offset = (Math.round(colorStop.offset * (end - start) + start) - start) / (end - start);
  22348. // });
  22349. var gradient = new LinearGradient(0, 0, 0, 0, colorStops, true);
  22350. gradient[dimName] = minCoord;
  22351. gradient[dimName + '2'] = maxCoord;
  22352. return gradient;
  22353. }
  22354. Chart.extend({
  22355. type: 'line',
  22356. init: function () {
  22357. var lineGroup = new Group();
  22358. var symbolDraw = new SymbolDraw();
  22359. this.group.add(symbolDraw.group);
  22360. this._symbolDraw = symbolDraw;
  22361. this._lineGroup = lineGroup;
  22362. },
  22363. render: function (seriesModel, ecModel, api) {
  22364. var coordSys = seriesModel.coordinateSystem;
  22365. var group = this.group;
  22366. var data = seriesModel.getData();
  22367. var lineStyleModel = seriesModel.getModel('lineStyle.normal');
  22368. var areaStyleModel = seriesModel.getModel('areaStyle.normal');
  22369. var points = data.mapArray(data.getItemLayout, true);
  22370. var isCoordSysPolar = coordSys.type === 'polar';
  22371. var prevCoordSys = this._coordSys;
  22372. var symbolDraw = this._symbolDraw;
  22373. var polyline = this._polyline;
  22374. var polygon = this._polygon;
  22375. var lineGroup = this._lineGroup;
  22376. var hasAnimation = seriesModel.get('animation');
  22377. var isAreaChart = !areaStyleModel.isEmpty();
  22378. var stackedOnPoints = getStackedOnPoints(coordSys, data);
  22379. var showSymbol = seriesModel.get('showSymbol');
  22380. var isSymbolIgnore = showSymbol && !isCoordSysPolar && !seriesModel.get('showAllSymbol')
  22381. && this._getSymbolIgnoreFunc(data, coordSys);
  22382. // Remove temporary symbols
  22383. var oldData = this._data;
  22384. oldData && oldData.eachItemGraphicEl(function (el, idx) {
  22385. if (el.__temp) {
  22386. group.remove(el);
  22387. oldData.setItemGraphicEl(idx, null);
  22388. }
  22389. });
  22390. // Remove previous created symbols if showSymbol changed to false
  22391. if (!showSymbol) {
  22392. symbolDraw.remove();
  22393. }
  22394. group.add(lineGroup);
  22395. // FIXME step not support polar
  22396. var step = !isCoordSysPolar && seriesModel.get('step');
  22397. // Initialization animation or coordinate system changed
  22398. if (
  22399. !(polyline && prevCoordSys.type === coordSys.type && step === this._step)
  22400. ) {
  22401. showSymbol && symbolDraw.updateData(data, isSymbolIgnore);
  22402. if (step) {
  22403. // TODO If stacked series is not step
  22404. points = turnPointsIntoStep(points, coordSys, step);
  22405. stackedOnPoints = turnPointsIntoStep(stackedOnPoints, coordSys, step);
  22406. }
  22407. polyline = this._newPolyline(points, coordSys, hasAnimation);
  22408. if (isAreaChart) {
  22409. polygon = this._newPolygon(
  22410. points, stackedOnPoints,
  22411. coordSys, hasAnimation
  22412. );
  22413. }
  22414. lineGroup.setClipPath(createClipShape(coordSys, true, seriesModel));
  22415. }
  22416. else {
  22417. if (isAreaChart && !polygon) {
  22418. // If areaStyle is added
  22419. polygon = this._newPolygon(
  22420. points, stackedOnPoints,
  22421. coordSys, hasAnimation
  22422. );
  22423. }
  22424. else if (polygon && !isAreaChart) {
  22425. // If areaStyle is removed
  22426. lineGroup.remove(polygon);
  22427. polygon = this._polygon = null;
  22428. }
  22429. // Update clipPath
  22430. lineGroup.setClipPath(createClipShape(coordSys, false, seriesModel));
  22431. // Always update, or it is wrong in the case turning on legend
  22432. // because points are not changed
  22433. showSymbol && symbolDraw.updateData(data, isSymbolIgnore);
  22434. // Stop symbol animation and sync with line points
  22435. // FIXME performance?
  22436. data.eachItemGraphicEl(function (el) {
  22437. el.stopAnimation(true);
  22438. });
  22439. // In the case data zoom triggerred refreshing frequently
  22440. // Data may not change if line has a category axis. So it should animate nothing
  22441. if (!isPointsSame(this._stackedOnPoints, stackedOnPoints)
  22442. || !isPointsSame(this._points, points)
  22443. ) {
  22444. if (hasAnimation) {
  22445. this._updateAnimation(
  22446. data, stackedOnPoints, coordSys, api, step
  22447. );
  22448. }
  22449. else {
  22450. // Not do it in update with animation
  22451. if (step) {
  22452. // TODO If stacked series is not step
  22453. points = turnPointsIntoStep(points, coordSys, step);
  22454. stackedOnPoints = turnPointsIntoStep(stackedOnPoints, coordSys, step);
  22455. }
  22456. polyline.setShape({
  22457. points: points
  22458. });
  22459. polygon && polygon.setShape({
  22460. points: points,
  22461. stackedOnPoints: stackedOnPoints
  22462. });
  22463. }
  22464. }
  22465. }
  22466. var visualColor = getVisualGradient(data, coordSys) || data.getVisual('color');
  22467. polyline.useStyle(defaults(
  22468. // Use color in lineStyle first
  22469. lineStyleModel.getLineStyle(),
  22470. {
  22471. fill: 'none',
  22472. stroke: visualColor,
  22473. lineJoin: 'bevel'
  22474. }
  22475. ));
  22476. var smooth = seriesModel.get('smooth');
  22477. smooth = getSmooth(seriesModel.get('smooth'));
  22478. polyline.setShape({
  22479. smooth: smooth,
  22480. smoothMonotone: seriesModel.get('smoothMonotone'),
  22481. connectNulls: seriesModel.get('connectNulls')
  22482. });
  22483. if (polygon) {
  22484. var stackedOn = data.stackedOn;
  22485. var stackedOnSmooth = 0;
  22486. polygon.useStyle(defaults(
  22487. areaStyleModel.getAreaStyle(),
  22488. {
  22489. fill: visualColor,
  22490. opacity: 0.7,
  22491. lineJoin: 'bevel'
  22492. }
  22493. ));
  22494. if (stackedOn) {
  22495. var stackedOnSeries = stackedOn.hostModel;
  22496. stackedOnSmooth = getSmooth(stackedOnSeries.get('smooth'));
  22497. }
  22498. polygon.setShape({
  22499. smooth: smooth,
  22500. stackedOnSmooth: stackedOnSmooth,
  22501. smoothMonotone: seriesModel.get('smoothMonotone'),
  22502. connectNulls: seriesModel.get('connectNulls')
  22503. });
  22504. }
  22505. this._data = data;
  22506. // Save the coordinate system for transition animation when data changed
  22507. this._coordSys = coordSys;
  22508. this._stackedOnPoints = stackedOnPoints;
  22509. this._points = points;
  22510. this._step = step;
  22511. },
  22512. dispose: function () {},
  22513. highlight: function (seriesModel, ecModel, api, payload) {
  22514. var data = seriesModel.getData();
  22515. var dataIndex = queryDataIndex(data, payload);
  22516. if (!(dataIndex instanceof Array) && dataIndex != null && dataIndex >= 0) {
  22517. var symbol = data.getItemGraphicEl(dataIndex);
  22518. if (!symbol) {
  22519. // Create a temporary symbol if it is not exists
  22520. var pt = data.getItemLayout(dataIndex);
  22521. if (!pt) {
  22522. // Null data
  22523. return;
  22524. }
  22525. symbol = new SymbolClz(data, dataIndex);
  22526. symbol.position = pt;
  22527. symbol.setZ(
  22528. seriesModel.get('zlevel'),
  22529. seriesModel.get('z')
  22530. );
  22531. symbol.ignore = isNaN(pt[0]) || isNaN(pt[1]);
  22532. symbol.__temp = true;
  22533. data.setItemGraphicEl(dataIndex, symbol);
  22534. // Stop scale animation
  22535. symbol.stopSymbolAnimation(true);
  22536. this.group.add(symbol);
  22537. }
  22538. symbol.highlight();
  22539. }
  22540. else {
  22541. // Highlight whole series
  22542. Chart.prototype.highlight.call(
  22543. this, seriesModel, ecModel, api, payload
  22544. );
  22545. }
  22546. },
  22547. downplay: function (seriesModel, ecModel, api, payload) {
  22548. var data = seriesModel.getData();
  22549. var dataIndex = queryDataIndex(data, payload);
  22550. if (dataIndex != null && dataIndex >= 0) {
  22551. var symbol = data.getItemGraphicEl(dataIndex);
  22552. if (symbol) {
  22553. if (symbol.__temp) {
  22554. data.setItemGraphicEl(dataIndex, null);
  22555. this.group.remove(symbol);
  22556. }
  22557. else {
  22558. symbol.downplay();
  22559. }
  22560. }
  22561. }
  22562. else {
  22563. // FIXME
  22564. // can not downplay completely.
  22565. // Downplay whole series
  22566. Chart.prototype.downplay.call(
  22567. this, seriesModel, ecModel, api, payload
  22568. );
  22569. }
  22570. },
  22571. /**
  22572. * @param {module:zrender/container/Group} group
  22573. * @param {Array.<Array.<number>>} points
  22574. * @private
  22575. */
  22576. _newPolyline: function (points) {
  22577. var polyline = this._polyline;
  22578. // Remove previous created polyline
  22579. if (polyline) {
  22580. this._lineGroup.remove(polyline);
  22581. }
  22582. polyline = new Polyline$1({
  22583. shape: {
  22584. points: points
  22585. },
  22586. silent: true,
  22587. z2: 10
  22588. });
  22589. this._lineGroup.add(polyline);
  22590. this._polyline = polyline;
  22591. return polyline;
  22592. },
  22593. /**
  22594. * @param {module:zrender/container/Group} group
  22595. * @param {Array.<Array.<number>>} stackedOnPoints
  22596. * @param {Array.<Array.<number>>} points
  22597. * @private
  22598. */
  22599. _newPolygon: function (points, stackedOnPoints) {
  22600. var polygon = this._polygon;
  22601. // Remove previous created polygon
  22602. if (polygon) {
  22603. this._lineGroup.remove(polygon);
  22604. }
  22605. polygon = new Polygon$1({
  22606. shape: {
  22607. points: points,
  22608. stackedOnPoints: stackedOnPoints
  22609. },
  22610. silent: true
  22611. });
  22612. this._lineGroup.add(polygon);
  22613. this._polygon = polygon;
  22614. return polygon;
  22615. },
  22616. /**
  22617. * @private
  22618. */
  22619. _getSymbolIgnoreFunc: function (data, coordSys) {
  22620. var categoryAxis = coordSys.getAxesByScale('ordinal')[0];
  22621. // `getLabelInterval` is provided by echarts/component/axis
  22622. if (categoryAxis && categoryAxis.isLabelIgnored) {
  22623. return bind(categoryAxis.isLabelIgnored, categoryAxis);
  22624. }
  22625. },
  22626. /**
  22627. * @private
  22628. */
  22629. // FIXME Two value axis
  22630. _updateAnimation: function (data, stackedOnPoints, coordSys, api, step) {
  22631. var polyline = this._polyline;
  22632. var polygon = this._polygon;
  22633. var seriesModel = data.hostModel;
  22634. var diff = lineAnimationDiff(
  22635. this._data, data,
  22636. this._stackedOnPoints, stackedOnPoints,
  22637. this._coordSys, coordSys
  22638. );
  22639. var current = diff.current;
  22640. var stackedOnCurrent = diff.stackedOnCurrent;
  22641. var next = diff.next;
  22642. var stackedOnNext = diff.stackedOnNext;
  22643. if (step) {
  22644. // TODO If stacked series is not step
  22645. current = turnPointsIntoStep(diff.current, coordSys, step);
  22646. stackedOnCurrent = turnPointsIntoStep(diff.stackedOnCurrent, coordSys, step);
  22647. next = turnPointsIntoStep(diff.next, coordSys, step);
  22648. stackedOnNext = turnPointsIntoStep(diff.stackedOnNext, coordSys, step);
  22649. }
  22650. // `diff.current` is subset of `current` (which should be ensured by
  22651. // turnPointsIntoStep), so points in `__points` can be updated when
  22652. // points in `current` are update during animation.
  22653. polyline.shape.__points = diff.current;
  22654. polyline.shape.points = current;
  22655. updateProps(polyline, {
  22656. shape: {
  22657. points: next
  22658. }
  22659. }, seriesModel);
  22660. if (polygon) {
  22661. polygon.setShape({
  22662. points: current,
  22663. stackedOnPoints: stackedOnCurrent
  22664. });
  22665. updateProps(polygon, {
  22666. shape: {
  22667. points: next,
  22668. stackedOnPoints: stackedOnNext
  22669. }
  22670. }, seriesModel);
  22671. }
  22672. var updatedDataInfo = [];
  22673. var diffStatus = diff.status;
  22674. for (var i = 0; i < diffStatus.length; i++) {
  22675. var cmd = diffStatus[i].cmd;
  22676. if (cmd === '=') {
  22677. var el = data.getItemGraphicEl(diffStatus[i].idx1);
  22678. if (el) {
  22679. updatedDataInfo.push({
  22680. el: el,
  22681. ptIdx: i // Index of points
  22682. });
  22683. }
  22684. }
  22685. }
  22686. if (polyline.animators && polyline.animators.length) {
  22687. polyline.animators[0].during(function () {
  22688. for (var i = 0; i < updatedDataInfo.length; i++) {
  22689. var el = updatedDataInfo[i].el;
  22690. el.attr('position', polyline.shape.__points[updatedDataInfo[i].ptIdx]);
  22691. }
  22692. });
  22693. }
  22694. },
  22695. remove: function (ecModel) {
  22696. var group = this.group;
  22697. var oldData = this._data;
  22698. this._lineGroup.removeAll();
  22699. this._symbolDraw.remove(true);
  22700. // Remove temporary created elements when highlighting
  22701. oldData && oldData.eachItemGraphicEl(function (el, idx) {
  22702. if (el.__temp) {
  22703. group.remove(el);
  22704. oldData.setItemGraphicEl(idx, null);
  22705. }
  22706. });
  22707. this._polyline =
  22708. this._polygon =
  22709. this._coordSys =
  22710. this._points =
  22711. this._stackedOnPoints =
  22712. this._data = null;
  22713. }
  22714. });
  22715. var visualSymbol = function (seriesType, defaultSymbolType, legendSymbol, ecModel, api) {
  22716. // Encoding visual for all series include which is filtered for legend drawing
  22717. ecModel.eachRawSeriesByType(seriesType, function (seriesModel) {
  22718. var data = seriesModel.getData();
  22719. var symbolType = seriesModel.get('symbol') || defaultSymbolType;
  22720. var symbolSize = seriesModel.get('symbolSize');
  22721. data.setVisual({
  22722. legendSymbol: legendSymbol || symbolType,
  22723. symbol: symbolType,
  22724. symbolSize: symbolSize
  22725. });
  22726. // Only visible series has each data be visual encoded
  22727. if (!ecModel.isSeriesFiltered(seriesModel)) {
  22728. if (typeof symbolSize === 'function') {
  22729. data.each(function (idx) {
  22730. var rawValue = seriesModel.getRawValue(idx);
  22731. // FIXME
  22732. var params = seriesModel.getDataParams(idx);
  22733. data.setItemVisual(idx, 'symbolSize', symbolSize(rawValue, params));
  22734. });
  22735. }
  22736. data.each(function (idx) {
  22737. var itemModel = data.getItemModel(idx);
  22738. var itemSymbolType = itemModel.getShallow('symbol', true);
  22739. var itemSymbolSize = itemModel.getShallow('symbolSize', true);
  22740. // If has item symbol
  22741. if (itemSymbolType != null) {
  22742. data.setItemVisual(idx, 'symbol', itemSymbolType);
  22743. }
  22744. if (itemSymbolSize != null) {
  22745. // PENDING Transform symbolSize ?
  22746. data.setItemVisual(idx, 'symbolSize', itemSymbolSize);
  22747. }
  22748. });
  22749. }
  22750. });
  22751. };
  22752. var layoutPoints = function (seriesType, ecModel) {
  22753. ecModel.eachSeriesByType(seriesType, function (seriesModel) {
  22754. var data = seriesModel.getData();
  22755. var coordSys = seriesModel.coordinateSystem;
  22756. if (!coordSys) {
  22757. return;
  22758. }
  22759. var dims = [];
  22760. var coordDims = coordSys.dimensions;
  22761. for (var i = 0; i < coordDims.length; i++) {
  22762. dims.push(seriesModel.coordDimToDataDim(coordSys.dimensions[i])[0]);
  22763. }
  22764. if (dims.length === 1) {
  22765. data.each(dims[0], function (x, idx) {
  22766. // Also {Array.<number>}, not undefined to avoid if...else... statement
  22767. data.setItemLayout(idx, isNaN(x) ? [NaN, NaN] : coordSys.dataToPoint(x));
  22768. });
  22769. }
  22770. else if (dims.length === 2) {
  22771. data.each(dims, function (x, y, idx) {
  22772. // Also {Array.<number>}, not undefined to avoid if...else... statement
  22773. data.setItemLayout(
  22774. idx, (isNaN(x) || isNaN(y)) ? [NaN, NaN] : coordSys.dataToPoint([x, y])
  22775. );
  22776. }, true);
  22777. }
  22778. });
  22779. };
  22780. var samplers = {
  22781. average: function (frame) {
  22782. var sum = 0;
  22783. var count = 0;
  22784. for (var i = 0; i < frame.length; i++) {
  22785. if (!isNaN(frame[i])) {
  22786. sum += frame[i];
  22787. count++;
  22788. }
  22789. }
  22790. // Return NaN if count is 0
  22791. return count === 0 ? NaN : sum / count;
  22792. },
  22793. sum: function (frame) {
  22794. var sum = 0;
  22795. for (var i = 0; i < frame.length; i++) {
  22796. // Ignore NaN
  22797. sum += frame[i] || 0;
  22798. }
  22799. return sum;
  22800. },
  22801. max: function (frame) {
  22802. var max = -Infinity;
  22803. for (var i = 0; i < frame.length; i++) {
  22804. frame[i] > max && (max = frame[i]);
  22805. }
  22806. return max;
  22807. },
  22808. min: function (frame) {
  22809. var min = Infinity;
  22810. for (var i = 0; i < frame.length; i++) {
  22811. frame[i] < min && (min = frame[i]);
  22812. }
  22813. return min;
  22814. },
  22815. // TODO
  22816. // Median
  22817. nearest: function (frame) {
  22818. return frame[0];
  22819. }
  22820. };
  22821. var indexSampler = function (frame, value) {
  22822. return Math.round(frame.length / 2);
  22823. };
  22824. var dataSample = function (seriesType, ecModel, api) {
  22825. ecModel.eachSeriesByType(seriesType, function (seriesModel) {
  22826. var data = seriesModel.getData();
  22827. var sampling = seriesModel.get('sampling');
  22828. var coordSys = seriesModel.coordinateSystem;
  22829. // Only cartesian2d support down sampling
  22830. if (coordSys.type === 'cartesian2d' && sampling) {
  22831. var baseAxis = coordSys.getBaseAxis();
  22832. var valueAxis = coordSys.getOtherAxis(baseAxis);
  22833. var extent = baseAxis.getExtent();
  22834. // Coordinste system has been resized
  22835. var size = extent[1] - extent[0];
  22836. var rate = Math.round(data.count() / size);
  22837. if (rate > 1) {
  22838. var sampler;
  22839. if (typeof sampling === 'string') {
  22840. sampler = samplers[sampling];
  22841. }
  22842. else if (typeof sampling === 'function') {
  22843. sampler = sampling;
  22844. }
  22845. if (sampler) {
  22846. data = data.downSample(
  22847. valueAxis.dim, 1 / rate, sampler, indexSampler
  22848. );
  22849. seriesModel.setData(data);
  22850. }
  22851. }
  22852. }
  22853. }, this);
  22854. };
  22855. /**
  22856. * // Scale class management
  22857. * @module echarts/scale/Scale
  22858. */
  22859. /**
  22860. * @param {Object} [setting]
  22861. */
  22862. function Scale(setting) {
  22863. this._setting = setting || {};
  22864. /**
  22865. * Extent
  22866. * @type {Array.<number>}
  22867. * @protected
  22868. */
  22869. this._extent = [Infinity, -Infinity];
  22870. /**
  22871. * Step is calculated in adjustExtent
  22872. * @type {Array.<number>}
  22873. * @protected
  22874. */
  22875. this._interval = 0;
  22876. this.init && this.init.apply(this, arguments);
  22877. }
  22878. /**
  22879. * Parse input val to valid inner number.
  22880. * @param {*} val
  22881. * @return {number}
  22882. */
  22883. Scale.prototype.parse = function (val) {
  22884. // Notice: This would be a trap here, If the implementation
  22885. // of this method depends on extent, and this method is used
  22886. // before extent set (like in dataZoom), it would be wrong.
  22887. // Nevertheless, parse does not depend on extent generally.
  22888. return val;
  22889. };
  22890. Scale.prototype.getSetting = function (name) {
  22891. return this._setting[name];
  22892. };
  22893. Scale.prototype.contain = function (val) {
  22894. var extent = this._extent;
  22895. return val >= extent[0] && val <= extent[1];
  22896. };
  22897. /**
  22898. * Normalize value to linear [0, 1], return 0.5 if extent span is 0
  22899. * @param {number} val
  22900. * @return {number}
  22901. */
  22902. Scale.prototype.normalize = function (val) {
  22903. var extent = this._extent;
  22904. if (extent[1] === extent[0]) {
  22905. return 0.5;
  22906. }
  22907. return (val - extent[0]) / (extent[1] - extent[0]);
  22908. };
  22909. /**
  22910. * Scale normalized value
  22911. * @param {number} val
  22912. * @return {number}
  22913. */
  22914. Scale.prototype.scale = function (val) {
  22915. var extent = this._extent;
  22916. return val * (extent[1] - extent[0]) + extent[0];
  22917. };
  22918. /**
  22919. * Set extent from data
  22920. * @param {Array.<number>} other
  22921. */
  22922. Scale.prototype.unionExtent = function (other) {
  22923. var extent = this._extent;
  22924. other[0] < extent[0] && (extent[0] = other[0]);
  22925. other[1] > extent[1] && (extent[1] = other[1]);
  22926. // not setExtent because in log axis it may transformed to power
  22927. // this.setExtent(extent[0], extent[1]);
  22928. };
  22929. /**
  22930. * Set extent from data
  22931. * @param {module:echarts/data/List} data
  22932. * @param {string} dim
  22933. */
  22934. Scale.prototype.unionExtentFromData = function (data, dim) {
  22935. this.unionExtent(data.getDataExtent(dim, true));
  22936. };
  22937. /**
  22938. * Get extent
  22939. * @return {Array.<number>}
  22940. */
  22941. Scale.prototype.getExtent = function () {
  22942. return this._extent.slice();
  22943. };
  22944. /**
  22945. * Set extent
  22946. * @param {number} start
  22947. * @param {number} end
  22948. */
  22949. Scale.prototype.setExtent = function (start, end) {
  22950. var thisExtent = this._extent;
  22951. if (!isNaN(start)) {
  22952. thisExtent[0] = start;
  22953. }
  22954. if (!isNaN(end)) {
  22955. thisExtent[1] = end;
  22956. }
  22957. };
  22958. /**
  22959. * @return {Array.<string>}
  22960. */
  22961. Scale.prototype.getTicksLabels = function () {
  22962. var labels = [];
  22963. var ticks = this.getTicks();
  22964. for (var i = 0; i < ticks.length; i++) {
  22965. labels.push(this.getLabel(ticks[i]));
  22966. }
  22967. return labels;
  22968. };
  22969. /**
  22970. * When axis extent depends on data and no data exists,
  22971. * axis ticks should not be drawn, which is named 'blank'.
  22972. */
  22973. Scale.prototype.isBlank = function () {
  22974. return this._isBlank;
  22975. },
  22976. /**
  22977. * When axis extent depends on data and no data exists,
  22978. * axis ticks should not be drawn, which is named 'blank'.
  22979. */
  22980. Scale.prototype.setBlank = function (isBlank) {
  22981. this._isBlank = isBlank;
  22982. };
  22983. enableClassExtend(Scale);
  22984. enableClassManagement(Scale, {
  22985. registerWhenExtend: true
  22986. });
  22987. /**
  22988. * Linear continuous scale
  22989. * @module echarts/coord/scale/Ordinal
  22990. *
  22991. * http://en.wikipedia.org/wiki/Level_of_measurement
  22992. */
  22993. // FIXME only one data
  22994. var scaleProto = Scale.prototype;
  22995. var OrdinalScale = Scale.extend({
  22996. type: 'ordinal',
  22997. init: function (data, extent) {
  22998. this._data = data;
  22999. this._extent = extent || [0, data.length - 1];
  23000. },
  23001. parse: function (val) {
  23002. return typeof val === 'string'
  23003. ? indexOf(this._data, val)
  23004. // val might be float.
  23005. : Math.round(val);
  23006. },
  23007. contain: function (rank) {
  23008. rank = this.parse(rank);
  23009. return scaleProto.contain.call(this, rank)
  23010. && this._data[rank] != null;
  23011. },
  23012. /**
  23013. * Normalize given rank or name to linear [0, 1]
  23014. * @param {number|string} [val]
  23015. * @return {number}
  23016. */
  23017. normalize: function (val) {
  23018. return scaleProto.normalize.call(this, this.parse(val));
  23019. },
  23020. scale: function (val) {
  23021. return Math.round(scaleProto.scale.call(this, val));
  23022. },
  23023. /**
  23024. * @return {Array}
  23025. */
  23026. getTicks: function () {
  23027. var ticks = [];
  23028. var extent = this._extent;
  23029. var rank = extent[0];
  23030. while (rank <= extent[1]) {
  23031. ticks.push(rank);
  23032. rank++;
  23033. }
  23034. return ticks;
  23035. },
  23036. /**
  23037. * Get item on rank n
  23038. * @param {number} n
  23039. * @return {string}
  23040. */
  23041. getLabel: function (n) {
  23042. return this._data[n];
  23043. },
  23044. /**
  23045. * @return {number}
  23046. */
  23047. count: function () {
  23048. return this._extent[1] - this._extent[0] + 1;
  23049. },
  23050. /**
  23051. * @override
  23052. */
  23053. unionExtentFromData: function (data, dim) {
  23054. this.unionExtent(data.getDataExtent(dim, false));
  23055. },
  23056. niceTicks: noop,
  23057. niceExtent: noop
  23058. });
  23059. /**
  23060. * @return {module:echarts/scale/Time}
  23061. */
  23062. OrdinalScale.create = function () {
  23063. return new OrdinalScale();
  23064. };
  23065. /**
  23066. * For testable.
  23067. */
  23068. var roundNumber$1 = round;
  23069. /**
  23070. * @param {Array.<number>} extent Both extent[0] and extent[1] should be valid number.
  23071. * Should be extent[0] < extent[1].
  23072. * @param {number} splitNumber splitNumber should be >= 1.
  23073. * @param {number} [minInterval]
  23074. * @param {number} [maxInterval]
  23075. * @return {Object} {interval, intervalPrecision, niceTickExtent}
  23076. */
  23077. function intervalScaleNiceTicks(extent, splitNumber, minInterval, maxInterval) {
  23078. var result = {};
  23079. var span = extent[1] - extent[0];
  23080. var interval = result.interval = nice(span / splitNumber, true);
  23081. if (minInterval != null && interval < minInterval) {
  23082. interval = result.interval = minInterval;
  23083. }
  23084. if (maxInterval != null && interval > maxInterval) {
  23085. interval = result.interval = maxInterval;
  23086. }
  23087. // Tow more digital for tick.
  23088. var precision = result.intervalPrecision = getIntervalPrecision(interval);
  23089. // Niced extent inside original extent
  23090. var niceTickExtent = result.niceTickExtent = [
  23091. roundNumber$1(Math.ceil(extent[0] / interval) * interval, precision),
  23092. roundNumber$1(Math.floor(extent[1] / interval) * interval, precision)
  23093. ];
  23094. fixExtent(niceTickExtent, extent);
  23095. return result;
  23096. }
  23097. /**
  23098. * @param {number} interval
  23099. * @return {number} interval precision
  23100. */
  23101. function getIntervalPrecision(interval) {
  23102. // Tow more digital for tick.
  23103. return getPrecisionSafe(interval) + 2;
  23104. }
  23105. function clamp(niceTickExtent, idx, extent) {
  23106. niceTickExtent[idx] = Math.max(Math.min(niceTickExtent[idx], extent[1]), extent[0]);
  23107. }
  23108. // In some cases (e.g., splitNumber is 1), niceTickExtent may be out of extent.
  23109. function fixExtent(niceTickExtent, extent) {
  23110. !isFinite(niceTickExtent[0]) && (niceTickExtent[0] = extent[0]);
  23111. !isFinite(niceTickExtent[1]) && (niceTickExtent[1] = extent[1]);
  23112. clamp(niceTickExtent, 0, extent);
  23113. clamp(niceTickExtent, 1, extent);
  23114. if (niceTickExtent[0] > niceTickExtent[1]) {
  23115. niceTickExtent[0] = niceTickExtent[1];
  23116. }
  23117. }
  23118. function intervalScaleGetTicks(interval, extent, niceTickExtent, intervalPrecision) {
  23119. var ticks = [];
  23120. // If interval is 0, return [];
  23121. if (!interval) {
  23122. return ticks;
  23123. }
  23124. // Consider this case: using dataZoom toolbox, zoom and zoom.
  23125. var safeLimit = 10000;
  23126. if (extent[0] < niceTickExtent[0]) {
  23127. ticks.push(extent[0]);
  23128. }
  23129. var tick = niceTickExtent[0];
  23130. while (tick <= niceTickExtent[1]) {
  23131. ticks.push(tick);
  23132. // Avoid rounding error
  23133. tick = roundNumber$1(tick + interval, intervalPrecision);
  23134. if (tick === ticks[ticks.length - 1]) {
  23135. // Consider out of safe float point, e.g.,
  23136. // -3711126.9907707 + 2e-10 === -3711126.9907707
  23137. break;
  23138. }
  23139. if (ticks.length > safeLimit) {
  23140. return [];
  23141. }
  23142. }
  23143. // Consider this case: the last item of ticks is smaller
  23144. // than niceTickExtent[1] and niceTickExtent[1] === extent[1].
  23145. if (extent[1] > (ticks.length ? ticks[ticks.length - 1] : niceTickExtent[1])) {
  23146. ticks.push(extent[1]);
  23147. }
  23148. return ticks;
  23149. }
  23150. /**
  23151. * Interval scale
  23152. * @module echarts/scale/Interval
  23153. */
  23154. var roundNumber = round;
  23155. /**
  23156. * @alias module:echarts/coord/scale/Interval
  23157. * @constructor
  23158. */
  23159. var IntervalScale = Scale.extend({
  23160. type: 'interval',
  23161. _interval: 0,
  23162. _intervalPrecision: 2,
  23163. setExtent: function (start, end) {
  23164. var thisExtent = this._extent;
  23165. //start,end may be a Number like '25',so...
  23166. if (!isNaN(start)) {
  23167. thisExtent[0] = parseFloat(start);
  23168. }
  23169. if (!isNaN(end)) {
  23170. thisExtent[1] = parseFloat(end);
  23171. }
  23172. },
  23173. unionExtent: function (other) {
  23174. var extent = this._extent;
  23175. other[0] < extent[0] && (extent[0] = other[0]);
  23176. other[1] > extent[1] && (extent[1] = other[1]);
  23177. // unionExtent may called by it's sub classes
  23178. IntervalScale.prototype.setExtent.call(this, extent[0], extent[1]);
  23179. },
  23180. /**
  23181. * Get interval
  23182. */
  23183. getInterval: function () {
  23184. return this._interval;
  23185. },
  23186. /**
  23187. * Set interval
  23188. */
  23189. setInterval: function (interval) {
  23190. this._interval = interval;
  23191. // Dropped auto calculated niceExtent and use user setted extent
  23192. // We assume user wan't to set both interval, min, max to get a better result
  23193. this._niceExtent = this._extent.slice();
  23194. this._intervalPrecision = getIntervalPrecision(interval);
  23195. },
  23196. /**
  23197. * @return {Array.<number>}
  23198. */
  23199. getTicks: function () {
  23200. return intervalScaleGetTicks(
  23201. this._interval, this._extent, this._niceExtent, this._intervalPrecision
  23202. );
  23203. },
  23204. /**
  23205. * @return {Array.<string>}
  23206. */
  23207. getTicksLabels: function () {
  23208. var labels = [];
  23209. var ticks = this.getTicks();
  23210. for (var i = 0; i < ticks.length; i++) {
  23211. labels.push(this.getLabel(ticks[i]));
  23212. }
  23213. return labels;
  23214. },
  23215. /**
  23216. * @param {number} data
  23217. * @param {Object} [opt]
  23218. * @param {number|string} [opt.precision] If 'auto', use nice presision.
  23219. * @param {boolean} [opt.pad] returns 1.50 but not 1.5 if precision is 2.
  23220. * @return {string}
  23221. */
  23222. getLabel: function (data, opt) {
  23223. if (data == null) {
  23224. return '';
  23225. }
  23226. var precision = opt && opt.precision;
  23227. if (precision == null) {
  23228. precision = getPrecisionSafe(data) || 0;
  23229. }
  23230. else if (precision === 'auto') {
  23231. // Should be more precise then tick.
  23232. precision = this._intervalPrecision;
  23233. }
  23234. // (1) If `precision` is set, 12.005 should be display as '12.00500'.
  23235. // (2) Use roundNumber (toFixed) to avoid scientific notation like '3.5e-7'.
  23236. data = roundNumber(data, precision, true);
  23237. return addCommas(data);
  23238. },
  23239. /**
  23240. * Update interval and extent of intervals for nice ticks
  23241. *
  23242. * @param {number} [splitNumber = 5] Desired number of ticks
  23243. * @param {number} [minInterval]
  23244. * @param {number} [maxInterval]
  23245. */
  23246. niceTicks: function (splitNumber, minInterval, maxInterval) {
  23247. splitNumber = splitNumber || 5;
  23248. var extent = this._extent;
  23249. var span = extent[1] - extent[0];
  23250. if (!isFinite(span)) {
  23251. return;
  23252. }
  23253. // User may set axis min 0 and data are all negative
  23254. // FIXME If it needs to reverse ?
  23255. if (span < 0) {
  23256. span = -span;
  23257. extent.reverse();
  23258. }
  23259. var result = intervalScaleNiceTicks(
  23260. extent, splitNumber, minInterval, maxInterval
  23261. );
  23262. this._intervalPrecision = result.intervalPrecision;
  23263. this._interval = result.interval;
  23264. this._niceExtent = result.niceTickExtent;
  23265. },
  23266. /**
  23267. * Nice extent.
  23268. * @param {Object} opt
  23269. * @param {number} [opt.splitNumber = 5] Given approx tick number
  23270. * @param {boolean} [opt.fixMin=false]
  23271. * @param {boolean} [opt.fixMax=false]
  23272. * @param {boolean} [opt.minInterval]
  23273. * @param {boolean} [opt.maxInterval]
  23274. */
  23275. niceExtent: function (opt) {
  23276. var extent = this._extent;
  23277. // If extent start and end are same, expand them
  23278. if (extent[0] === extent[1]) {
  23279. if (extent[0] !== 0) {
  23280. // Expand extent
  23281. var expandSize = extent[0];
  23282. // In the fowllowing case
  23283. // Axis has been fixed max 100
  23284. // Plus data are all 100 and axis extent are [100, 100].
  23285. // Extend to the both side will cause expanded max is larger than fixed max.
  23286. // So only expand to the smaller side.
  23287. if (!opt.fixMax) {
  23288. extent[1] += expandSize / 2;
  23289. extent[0] -= expandSize / 2;
  23290. }
  23291. else {
  23292. extent[0] -= expandSize / 2;
  23293. }
  23294. }
  23295. else {
  23296. extent[1] = 1;
  23297. }
  23298. }
  23299. var span = extent[1] - extent[0];
  23300. // If there are no data and extent are [Infinity, -Infinity]
  23301. if (!isFinite(span)) {
  23302. extent[0] = 0;
  23303. extent[1] = 1;
  23304. }
  23305. this.niceTicks(opt.splitNumber, opt.minInterval, opt.maxInterval);
  23306. // var extent = this._extent;
  23307. var interval = this._interval;
  23308. if (!opt.fixMin) {
  23309. extent[0] = roundNumber(Math.floor(extent[0] / interval) * interval);
  23310. }
  23311. if (!opt.fixMax) {
  23312. extent[1] = roundNumber(Math.ceil(extent[1] / interval) * interval);
  23313. }
  23314. }
  23315. });
  23316. /**
  23317. * @return {module:echarts/scale/Time}
  23318. */
  23319. IntervalScale.create = function () {
  23320. return new IntervalScale();
  23321. };
  23322. // [About UTC and local time zone]:
  23323. // In most cases, `number.parseDate` will treat input data string as local time
  23324. // (except time zone is specified in time string). And `format.formateTime` returns
  23325. // local time by default. option.useUTC is false by default. This design have
  23326. // concidered these common case:
  23327. // (1) Time that is persistent in server is in UTC, but it is needed to be diplayed
  23328. // in local time by default.
  23329. // (2) By default, the input data string (e.g., '2011-01-02') should be displayed
  23330. // as its original time, without any time difference.
  23331. var intervalScaleProto = IntervalScale.prototype;
  23332. var mathCeil = Math.ceil;
  23333. var mathFloor = Math.floor;
  23334. var ONE_SECOND = 1000;
  23335. var ONE_MINUTE = ONE_SECOND * 60;
  23336. var ONE_HOUR = ONE_MINUTE * 60;
  23337. var ONE_DAY = ONE_HOUR * 24;
  23338. // FIXME 公用?
  23339. var bisect = function (a, x, lo, hi) {
  23340. while (lo < hi) {
  23341. var mid = lo + hi >>> 1;
  23342. if (a[mid][1] < x) {
  23343. lo = mid + 1;
  23344. }
  23345. else {
  23346. hi = mid;
  23347. }
  23348. }
  23349. return lo;
  23350. };
  23351. /**
  23352. * @alias module:echarts/coord/scale/Time
  23353. * @constructor
  23354. */
  23355. var TimeScale = IntervalScale.extend({
  23356. type: 'time',
  23357. /**
  23358. * @override
  23359. */
  23360. getLabel: function (val) {
  23361. var stepLvl = this._stepLvl;
  23362. var date = new Date(val);
  23363. return formatTime(stepLvl[0], date, this.getSetting('useUTC'));
  23364. },
  23365. /**
  23366. * @override
  23367. */
  23368. niceExtent: function (opt) {
  23369. var extent = this._extent;
  23370. // If extent start and end are same, expand them
  23371. if (extent[0] === extent[1]) {
  23372. // Expand extent
  23373. extent[0] -= ONE_DAY;
  23374. extent[1] += ONE_DAY;
  23375. }
  23376. // If there are no data and extent are [Infinity, -Infinity]
  23377. if (extent[1] === -Infinity && extent[0] === Infinity) {
  23378. var d = new Date();
  23379. extent[1] = +new Date(d.getFullYear(), d.getMonth(), d.getDate());
  23380. extent[0] = extent[1] - ONE_DAY;
  23381. }
  23382. this.niceTicks(opt.splitNumber, opt.minInterval, opt.maxInterval);
  23383. // var extent = this._extent;
  23384. var interval = this._interval;
  23385. if (!opt.fixMin) {
  23386. extent[0] = round(mathFloor(extent[0] / interval) * interval);
  23387. }
  23388. if (!opt.fixMax) {
  23389. extent[1] = round(mathCeil(extent[1] / interval) * interval);
  23390. }
  23391. },
  23392. /**
  23393. * @override
  23394. */
  23395. niceTicks: function (approxTickNum, minInterval, maxInterval) {
  23396. approxTickNum = approxTickNum || 10;
  23397. var extent = this._extent;
  23398. var span = extent[1] - extent[0];
  23399. var approxInterval = span / approxTickNum;
  23400. if (minInterval != null && approxInterval < minInterval) {
  23401. approxInterval = minInterval;
  23402. }
  23403. if (maxInterval != null && approxInterval > maxInterval) {
  23404. approxInterval = maxInterval;
  23405. }
  23406. var scaleLevelsLen = scaleLevels.length;
  23407. var idx = bisect(scaleLevels, approxInterval, 0, scaleLevelsLen);
  23408. var level = scaleLevels[Math.min(idx, scaleLevelsLen - 1)];
  23409. var interval = level[1];
  23410. // Same with interval scale if span is much larger than 1 year
  23411. if (level[0] === 'year') {
  23412. var yearSpan = span / interval;
  23413. // From "Nice Numbers for Graph Labels" of Graphic Gems
  23414. // var niceYearSpan = numberUtil.nice(yearSpan, false);
  23415. var yearStep = nice(yearSpan / approxTickNum, true);
  23416. interval *= yearStep;
  23417. }
  23418. var timezoneOffset = this.getSetting('useUTC')
  23419. ? 0 : (new Date(+extent[0] || +extent[1])).getTimezoneOffset() * 60 * 1000;
  23420. var niceExtent = [
  23421. Math.round(mathCeil((extent[0] - timezoneOffset) / interval) * interval + timezoneOffset),
  23422. Math.round(mathFloor((extent[1] - timezoneOffset) / interval) * interval + timezoneOffset)
  23423. ];
  23424. fixExtent(niceExtent, extent);
  23425. this._stepLvl = level;
  23426. // Interval will be used in getTicks
  23427. this._interval = interval;
  23428. this._niceExtent = niceExtent;
  23429. },
  23430. parse: function (val) {
  23431. // val might be float.
  23432. return +parseDate(val);
  23433. }
  23434. });
  23435. each$1(['contain', 'normalize'], function (methodName) {
  23436. TimeScale.prototype[methodName] = function (val) {
  23437. return intervalScaleProto[methodName].call(this, this.parse(val));
  23438. };
  23439. });
  23440. // Steps from d3
  23441. var scaleLevels = [
  23442. // Format interval
  23443. ['hh:mm:ss', ONE_SECOND], // 1s
  23444. ['hh:mm:ss', ONE_SECOND * 5], // 5s
  23445. ['hh:mm:ss', ONE_SECOND * 10], // 10s
  23446. ['hh:mm:ss', ONE_SECOND * 15], // 15s
  23447. ['hh:mm:ss', ONE_SECOND * 30], // 30s
  23448. ['hh:mm\nMM-dd', ONE_MINUTE], // 1m
  23449. ['hh:mm\nMM-dd', ONE_MINUTE * 5], // 5m
  23450. ['hh:mm\nMM-dd', ONE_MINUTE * 10], // 10m
  23451. ['hh:mm\nMM-dd', ONE_MINUTE * 15], // 15m
  23452. ['hh:mm\nMM-dd', ONE_MINUTE * 30], // 30m
  23453. ['hh:mm\nMM-dd', ONE_HOUR], // 1h
  23454. ['hh:mm\nMM-dd', ONE_HOUR * 2], // 2h
  23455. ['hh:mm\nMM-dd', ONE_HOUR * 6], // 6h
  23456. ['hh:mm\nMM-dd', ONE_HOUR * 12], // 12h
  23457. ['MM-dd\nyyyy', ONE_DAY], // 1d
  23458. ['MM-dd\nyyyy', ONE_DAY * 2], // 2d
  23459. ['MM-dd\nyyyy', ONE_DAY * 3], // 3d
  23460. ['MM-dd\nyyyy', ONE_DAY * 4], // 4d
  23461. ['MM-dd\nyyyy', ONE_DAY * 5], // 5d
  23462. ['MM-dd\nyyyy', ONE_DAY * 6], // 6d
  23463. ['week', ONE_DAY * 7], // 7d
  23464. ['MM-dd\nyyyy', ONE_DAY * 10], // 10d
  23465. ['week', ONE_DAY * 14], // 2w
  23466. ['week', ONE_DAY * 21], // 3w
  23467. ['month', ONE_DAY * 31], // 1M
  23468. ['week', ONE_DAY * 42], // 6w
  23469. ['month', ONE_DAY * 62], // 2M
  23470. ['week', ONE_DAY * 42], // 10w
  23471. ['quarter', ONE_DAY * 380 / 4], // 3M
  23472. ['month', ONE_DAY * 31 * 4], // 4M
  23473. ['month', ONE_DAY * 31 * 5], // 5M
  23474. ['half-year', ONE_DAY * 380 / 2], // 6M
  23475. ['month', ONE_DAY * 31 * 8], // 8M
  23476. ['month', ONE_DAY * 31 * 10], // 10M
  23477. ['year', ONE_DAY * 380] // 1Y
  23478. ];
  23479. /**
  23480. * @param {module:echarts/model/Model}
  23481. * @return {module:echarts/scale/Time}
  23482. */
  23483. TimeScale.create = function (model) {
  23484. return new TimeScale({useUTC: model.ecModel.get('useUTC')});
  23485. };
  23486. /**
  23487. * Log scale
  23488. * @module echarts/scale/Log
  23489. */
  23490. // Use some method of IntervalScale
  23491. var scaleProto$1 = Scale.prototype;
  23492. var intervalScaleProto$1 = IntervalScale.prototype;
  23493. var getPrecisionSafe$1 = getPrecisionSafe;
  23494. var roundingErrorFix = round;
  23495. var mathFloor$1 = Math.floor;
  23496. var mathCeil$1 = Math.ceil;
  23497. var mathPow$1 = Math.pow;
  23498. var mathLog = Math.log;
  23499. var LogScale = Scale.extend({
  23500. type: 'log',
  23501. base: 10,
  23502. $constructor: function () {
  23503. Scale.apply(this, arguments);
  23504. this._originalScale = new IntervalScale();
  23505. },
  23506. /**
  23507. * @return {Array.<number>}
  23508. */
  23509. getTicks: function () {
  23510. var originalScale = this._originalScale;
  23511. var extent = this._extent;
  23512. var originalExtent = originalScale.getExtent();
  23513. return map(intervalScaleProto$1.getTicks.call(this), function (val) {
  23514. var powVal = round(mathPow$1(this.base, val));
  23515. // Fix #4158
  23516. powVal = (val === extent[0] && originalScale.__fixMin)
  23517. ? fixRoundingError(powVal, originalExtent[0])
  23518. : powVal;
  23519. powVal = (val === extent[1] && originalScale.__fixMax)
  23520. ? fixRoundingError(powVal, originalExtent[1])
  23521. : powVal;
  23522. return powVal;
  23523. }, this);
  23524. },
  23525. /**
  23526. * @param {number} val
  23527. * @return {string}
  23528. */
  23529. getLabel: intervalScaleProto$1.getLabel,
  23530. /**
  23531. * @param {number} val
  23532. * @return {number}
  23533. */
  23534. scale: function (val) {
  23535. val = scaleProto$1.scale.call(this, val);
  23536. return mathPow$1(this.base, val);
  23537. },
  23538. /**
  23539. * @param {number} start
  23540. * @param {number} end
  23541. */
  23542. setExtent: function (start, end) {
  23543. var base = this.base;
  23544. start = mathLog(start) / mathLog(base);
  23545. end = mathLog(end) / mathLog(base);
  23546. intervalScaleProto$1.setExtent.call(this, start, end);
  23547. },
  23548. /**
  23549. * @return {number} end
  23550. */
  23551. getExtent: function () {
  23552. var base = this.base;
  23553. var extent = scaleProto$1.getExtent.call(this);
  23554. extent[0] = mathPow$1(base, extent[0]);
  23555. extent[1] = mathPow$1(base, extent[1]);
  23556. // Fix #4158
  23557. var originalScale = this._originalScale;
  23558. var originalExtent = originalScale.getExtent();
  23559. originalScale.__fixMin && (extent[0] = fixRoundingError(extent[0], originalExtent[0]));
  23560. originalScale.__fixMax && (extent[1] = fixRoundingError(extent[1], originalExtent[1]));
  23561. return extent;
  23562. },
  23563. /**
  23564. * @param {Array.<number>} extent
  23565. */
  23566. unionExtent: function (extent) {
  23567. this._originalScale.unionExtent(extent);
  23568. var base = this.base;
  23569. extent[0] = mathLog(extent[0]) / mathLog(base);
  23570. extent[1] = mathLog(extent[1]) / mathLog(base);
  23571. scaleProto$1.unionExtent.call(this, extent);
  23572. },
  23573. /**
  23574. * @override
  23575. */
  23576. unionExtentFromData: function (data, dim) {
  23577. this.unionExtent(data.getDataExtent(dim, true, function (val) {
  23578. return val > 0;
  23579. }));
  23580. },
  23581. /**
  23582. * Update interval and extent of intervals for nice ticks
  23583. * @param {number} [approxTickNum = 10] Given approx tick number
  23584. */
  23585. niceTicks: function (approxTickNum) {
  23586. approxTickNum = approxTickNum || 10;
  23587. var extent = this._extent;
  23588. var span = extent[1] - extent[0];
  23589. if (span === Infinity || span <= 0) {
  23590. return;
  23591. }
  23592. var interval = quantity(span);
  23593. var err = approxTickNum / span * interval;
  23594. // Filter ticks to get closer to the desired count.
  23595. if (err <= 0.5) {
  23596. interval *= 10;
  23597. }
  23598. // Interval should be integer
  23599. while (!isNaN(interval) && Math.abs(interval) < 1 && Math.abs(interval) > 0) {
  23600. interval *= 10;
  23601. }
  23602. var niceExtent = [
  23603. round(mathCeil$1(extent[0] / interval) * interval),
  23604. round(mathFloor$1(extent[1] / interval) * interval)
  23605. ];
  23606. this._interval = interval;
  23607. this._niceExtent = niceExtent;
  23608. },
  23609. /**
  23610. * Nice extent.
  23611. * @override
  23612. */
  23613. niceExtent: function (opt) {
  23614. intervalScaleProto$1.niceExtent.call(this, opt);
  23615. var originalScale = this._originalScale;
  23616. originalScale.__fixMin = opt.fixMin;
  23617. originalScale.__fixMax = opt.fixMax;
  23618. }
  23619. });
  23620. each$1(['contain', 'normalize'], function (methodName) {
  23621. LogScale.prototype[methodName] = function (val) {
  23622. val = mathLog(val) / mathLog(this.base);
  23623. return scaleProto$1[methodName].call(this, val);
  23624. };
  23625. });
  23626. LogScale.create = function () {
  23627. return new LogScale();
  23628. };
  23629. function fixRoundingError(val, originalVal) {
  23630. return roundingErrorFix(val, getPrecisionSafe$1(originalVal));
  23631. }
  23632. /**
  23633. * Get axis scale extent before niced.
  23634. * Item of returned array can only be number (including Infinity and NaN).
  23635. */
  23636. function getScaleExtent(scale, model) {
  23637. var scaleType = scale.type;
  23638. var min = model.getMin();
  23639. var max = model.getMax();
  23640. var fixMin = min != null;
  23641. var fixMax = max != null;
  23642. var originalExtent = scale.getExtent();
  23643. var axisDataLen;
  23644. var boundaryGap;
  23645. var span;
  23646. if (scaleType === 'ordinal') {
  23647. axisDataLen = (model.get('data') || []).length;
  23648. }
  23649. else {
  23650. boundaryGap = model.get('boundaryGap');
  23651. if (!isArray(boundaryGap)) {
  23652. boundaryGap = [boundaryGap || 0, boundaryGap || 0];
  23653. }
  23654. if (typeof boundaryGap[0] === 'boolean') {
  23655. if (__DEV__) {
  23656. console.warn('Boolean type for boundaryGap is only '
  23657. + 'allowed for ordinal axis. Please use string in '
  23658. + 'percentage instead, e.g., "20%". Currently, '
  23659. + 'boundaryGap is set to be 0.');
  23660. }
  23661. boundaryGap = [0, 0];
  23662. }
  23663. boundaryGap[0] = parsePercent$1(boundaryGap[0], 1);
  23664. boundaryGap[1] = parsePercent$1(boundaryGap[1], 1);
  23665. span = (originalExtent[1] - originalExtent[0])
  23666. || Math.abs(originalExtent[0]);
  23667. }
  23668. // Notice: When min/max is not set (that is, when there are null/undefined,
  23669. // which is the most common case), these cases should be ensured:
  23670. // (1) For 'ordinal', show all axis.data.
  23671. // (2) For others:
  23672. // + `boundaryGap` is applied (if min/max set, boundaryGap is
  23673. // disabled).
  23674. // + If `needCrossZero`, min/max should be zero, otherwise, min/max should
  23675. // be the result that originalExtent enlarged by boundaryGap.
  23676. // (3) If no data, it should be ensured that `scale.setBlank` is set.
  23677. // FIXME
  23678. // (1) When min/max is 'dataMin' or 'dataMax', should boundaryGap be able to used?
  23679. // (2) When `needCrossZero` and all data is positive/negative, should it be ensured
  23680. // that the results processed by boundaryGap are positive/negative?
  23681. if (min == null) {
  23682. min = scaleType === 'ordinal'
  23683. ? (axisDataLen ? 0 : NaN)
  23684. : originalExtent[0] - boundaryGap[0] * span;
  23685. }
  23686. if (max == null) {
  23687. max = scaleType === 'ordinal'
  23688. ? (axisDataLen ? axisDataLen - 1 : NaN)
  23689. : originalExtent[1] + boundaryGap[1] * span;
  23690. }
  23691. if (min === 'dataMin') {
  23692. min = originalExtent[0];
  23693. }
  23694. else if (typeof min === 'function') {
  23695. min = min({
  23696. min: originalExtent[0],
  23697. max: originalExtent[1]
  23698. });
  23699. }
  23700. if (max === 'dataMax') {
  23701. max = originalExtent[1];
  23702. }
  23703. else if (typeof max === 'function') {
  23704. max = max({
  23705. min: originalExtent[0],
  23706. max: originalExtent[1]
  23707. });
  23708. }
  23709. (min == null || !isFinite(min)) && (min = NaN);
  23710. (max == null || !isFinite(max)) && (max = NaN);
  23711. scale.setBlank(eqNaN(min) || eqNaN(max));
  23712. // Evaluate if axis needs cross zero
  23713. if (model.getNeedCrossZero()) {
  23714. // Axis is over zero and min is not set
  23715. if (min > 0 && max > 0 && !fixMin) {
  23716. min = 0;
  23717. }
  23718. // Axis is under zero and max is not set
  23719. if (min < 0 && max < 0 && !fixMax) {
  23720. max = 0;
  23721. }
  23722. }
  23723. return [min, max];
  23724. }
  23725. function niceScaleExtent$1(scale, model) {
  23726. var extent = getScaleExtent(scale, model);
  23727. var fixMin = model.getMin() != null;
  23728. var fixMax = model.getMax() != null;
  23729. var splitNumber = model.get('splitNumber');
  23730. if (scale.type === 'log') {
  23731. scale.base = model.get('logBase');
  23732. }
  23733. var scaleType = scale.type;
  23734. scale.setExtent(extent[0], extent[1]);
  23735. scale.niceExtent({
  23736. splitNumber: splitNumber,
  23737. fixMin: fixMin,
  23738. fixMax: fixMax,
  23739. minInterval: (scaleType === 'interval' || scaleType === 'time')
  23740. ? model.get('minInterval') : null,
  23741. maxInterval: (scaleType === 'interval' || scaleType === 'time')
  23742. ? model.get('maxInterval') : null
  23743. });
  23744. // If some one specified the min, max. And the default calculated interval
  23745. // is not good enough. He can specify the interval. It is often appeared
  23746. // in angle axis with angle 0 - 360. Interval calculated in interval scale is hard
  23747. // to be 60.
  23748. // FIXME
  23749. var interval = model.get('interval');
  23750. if (interval != null) {
  23751. scale.setInterval && scale.setInterval(interval);
  23752. }
  23753. }
  23754. /**
  23755. * @param {module:echarts/model/Model} model
  23756. * @param {string} [axisType] Default retrieve from model.type
  23757. * @return {module:echarts/scale/*}
  23758. */
  23759. function createScaleByModel(model, axisType) {
  23760. axisType = axisType || model.get('type');
  23761. if (axisType) {
  23762. switch (axisType) {
  23763. // Buildin scale
  23764. case 'category':
  23765. return new OrdinalScale(
  23766. model.getCategories(), [Infinity, -Infinity]
  23767. );
  23768. case 'value':
  23769. return new IntervalScale();
  23770. // Extended scale, like time and log
  23771. default:
  23772. return (Scale.getClass(axisType) || IntervalScale).create(model);
  23773. }
  23774. }
  23775. }
  23776. /**
  23777. * Check if the axis corss 0
  23778. */
  23779. function ifAxisCrossZero$1(axis) {
  23780. var dataExtent = axis.scale.getExtent();
  23781. var min = dataExtent[0];
  23782. var max = dataExtent[1];
  23783. return !((min > 0 && max > 0) || (min < 0 && max < 0));
  23784. }
  23785. /**
  23786. * @param {Array.<number>} tickCoords In axis self coordinate.
  23787. * @param {Array.<string>} labels
  23788. * @param {string} font
  23789. * @param {number} axisRotate 0: towards right horizontally, clock-wise is negative.
  23790. * @param {number} [labelRotate=0] 0: towards right horizontally, clock-wise is negative.
  23791. * @return {number}
  23792. */
  23793. function getAxisLabelInterval(tickCoords, labels, font, axisRotate, labelRotate) {
  23794. var textSpaceTakenRect;
  23795. var autoLabelInterval = 0;
  23796. var accumulatedLabelInterval = 0;
  23797. var rotation = (axisRotate - labelRotate) / 180 * Math.PI;
  23798. var step = 1;
  23799. if (labels.length > 40) {
  23800. // Simple optimization for large amount of labels
  23801. step = Math.floor(labels.length / 40);
  23802. }
  23803. for (var i = 0; i < tickCoords.length; i += step) {
  23804. var tickCoord = tickCoords[i];
  23805. // Not precise, do not consider align and vertical align
  23806. // and each distance from axis line yet.
  23807. var rect = getBoundingRect(
  23808. labels[i], font, 'center', 'top'
  23809. );
  23810. rect.x += tickCoord * Math.cos(rotation);
  23811. rect.y += tickCoord * Math.sin(rotation);
  23812. // Magic number
  23813. rect.width *= 1.3;
  23814. rect.height *= 1.3;
  23815. if (!textSpaceTakenRect) {
  23816. textSpaceTakenRect = rect.clone();
  23817. }
  23818. // There is no space for current label;
  23819. else if (textSpaceTakenRect.intersect(rect)) {
  23820. accumulatedLabelInterval++;
  23821. autoLabelInterval = Math.max(autoLabelInterval, accumulatedLabelInterval);
  23822. }
  23823. else {
  23824. textSpaceTakenRect.union(rect);
  23825. // Reset
  23826. accumulatedLabelInterval = 0;
  23827. }
  23828. }
  23829. if (autoLabelInterval === 0 && step > 1) {
  23830. return step;
  23831. }
  23832. return (autoLabelInterval + 1) * step - 1;
  23833. }
  23834. /**
  23835. * @param {Object} axis
  23836. * @param {Function} labelFormatter
  23837. * @return {Array.<string>}
  23838. */
  23839. function getFormattedLabels(axis, labelFormatter) {
  23840. var scale = axis.scale;
  23841. var labels = scale.getTicksLabels();
  23842. var ticks = scale.getTicks();
  23843. if (typeof labelFormatter === 'string') {
  23844. labelFormatter = (function (tpl) {
  23845. return function (val) {
  23846. return tpl.replace('{value}', val != null ? val : '');
  23847. };
  23848. })(labelFormatter);
  23849. // Consider empty array
  23850. return map(labels, labelFormatter);
  23851. }
  23852. else if (typeof labelFormatter === 'function') {
  23853. return map(ticks, function (tick, idx) {
  23854. return labelFormatter(
  23855. getAxisRawValue(axis, tick),
  23856. idx
  23857. );
  23858. }, this);
  23859. }
  23860. else {
  23861. return labels;
  23862. }
  23863. }
  23864. function getAxisRawValue(axis, value) {
  23865. // In category axis with data zoom, tick is not the original
  23866. // index of axis.data. So tick should not be exposed to user
  23867. // in category axis.
  23868. return axis.type === 'category' ? axis.scale.getLabel(value) : value;
  23869. }
  23870. /**
  23871. * Cartesian coordinate system
  23872. * @module echarts/coord/Cartesian
  23873. *
  23874. */
  23875. function dimAxisMapper(dim) {
  23876. return this._axes[dim];
  23877. }
  23878. /**
  23879. * @alias module:echarts/coord/Cartesian
  23880. * @constructor
  23881. */
  23882. var Cartesian = function (name) {
  23883. this._axes = {};
  23884. this._dimList = [];
  23885. /**
  23886. * @type {string}
  23887. */
  23888. this.name = name || '';
  23889. };
  23890. Cartesian.prototype = {
  23891. constructor: Cartesian,
  23892. type: 'cartesian',
  23893. /**
  23894. * Get axis
  23895. * @param {number|string} dim
  23896. * @return {module:echarts/coord/Cartesian~Axis}
  23897. */
  23898. getAxis: function (dim) {
  23899. return this._axes[dim];
  23900. },
  23901. /**
  23902. * Get axes list
  23903. * @return {Array.<module:echarts/coord/Cartesian~Axis>}
  23904. */
  23905. getAxes: function () {
  23906. return map(this._dimList, dimAxisMapper, this);
  23907. },
  23908. /**
  23909. * Get axes list by given scale type
  23910. */
  23911. getAxesByScale: function (scaleType) {
  23912. scaleType = scaleType.toLowerCase();
  23913. return filter(
  23914. this.getAxes(),
  23915. function (axis) {
  23916. return axis.scale.type === scaleType;
  23917. }
  23918. );
  23919. },
  23920. /**
  23921. * Add axis
  23922. * @param {module:echarts/coord/Cartesian.Axis}
  23923. */
  23924. addAxis: function (axis) {
  23925. var dim = axis.dim;
  23926. this._axes[dim] = axis;
  23927. this._dimList.push(dim);
  23928. },
  23929. /**
  23930. * Convert data to coord in nd space
  23931. * @param {Array.<number>|Object.<string, number>} val
  23932. * @return {Array.<number>|Object.<string, number>}
  23933. */
  23934. dataToCoord: function (val) {
  23935. return this._dataCoordConvert(val, 'dataToCoord');
  23936. },
  23937. /**
  23938. * Convert coord in nd space to data
  23939. * @param {Array.<number>|Object.<string, number>} val
  23940. * @return {Array.<number>|Object.<string, number>}
  23941. */
  23942. coordToData: function (val) {
  23943. return this._dataCoordConvert(val, 'coordToData');
  23944. },
  23945. _dataCoordConvert: function (input, method) {
  23946. var dimList = this._dimList;
  23947. var output = input instanceof Array ? [] : {};
  23948. for (var i = 0; i < dimList.length; i++) {
  23949. var dim = dimList[i];
  23950. var axis = this._axes[dim];
  23951. output[dim] = axis[method](input[dim]);
  23952. }
  23953. return output;
  23954. }
  23955. };
  23956. function Cartesian2D(name) {
  23957. Cartesian.call(this, name);
  23958. }
  23959. Cartesian2D.prototype = {
  23960. constructor: Cartesian2D,
  23961. type: 'cartesian2d',
  23962. /**
  23963. * @type {Array.<string>}
  23964. * @readOnly
  23965. */
  23966. dimensions: ['x', 'y'],
  23967. /**
  23968. * Base axis will be used on stacking.
  23969. *
  23970. * @return {module:echarts/coord/cartesian/Axis2D}
  23971. */
  23972. getBaseAxis: function () {
  23973. return this.getAxesByScale('ordinal')[0]
  23974. || this.getAxesByScale('time')[0]
  23975. || this.getAxis('x');
  23976. },
  23977. /**
  23978. * If contain point
  23979. * @param {Array.<number>} point
  23980. * @return {boolean}
  23981. */
  23982. containPoint: function (point) {
  23983. var axisX = this.getAxis('x');
  23984. var axisY = this.getAxis('y');
  23985. return axisX.contain(axisX.toLocalCoord(point[0]))
  23986. && axisY.contain(axisY.toLocalCoord(point[1]));
  23987. },
  23988. /**
  23989. * If contain data
  23990. * @param {Array.<number>} data
  23991. * @return {boolean}
  23992. */
  23993. containData: function (data) {
  23994. return this.getAxis('x').containData(data[0])
  23995. && this.getAxis('y').containData(data[1]);
  23996. },
  23997. /**
  23998. * @param {Array.<number>} data
  23999. * @param {boolean} [clamp=false]
  24000. * @return {Array.<number>}
  24001. */
  24002. dataToPoint: function (data, clamp) {
  24003. var xAxis = this.getAxis('x');
  24004. var yAxis = this.getAxis('y');
  24005. return [
  24006. xAxis.toGlobalCoord(xAxis.dataToCoord(data[0], clamp)),
  24007. yAxis.toGlobalCoord(yAxis.dataToCoord(data[1], clamp))
  24008. ];
  24009. },
  24010. /**
  24011. * @param {Array.<number>} point
  24012. * @param {boolean} [clamp=false]
  24013. * @return {Array.<number>}
  24014. */
  24015. pointToData: function (point, clamp) {
  24016. var xAxis = this.getAxis('x');
  24017. var yAxis = this.getAxis('y');
  24018. return [
  24019. xAxis.coordToData(xAxis.toLocalCoord(point[0]), clamp),
  24020. yAxis.coordToData(yAxis.toLocalCoord(point[1]), clamp)
  24021. ];
  24022. },
  24023. /**
  24024. * Get other axis
  24025. * @param {module:echarts/coord/cartesian/Axis2D} axis
  24026. */
  24027. getOtherAxis: function (axis) {
  24028. return this.getAxis(axis.dim === 'x' ? 'y' : 'x');
  24029. }
  24030. };
  24031. inherits(Cartesian2D, Cartesian);
  24032. var linearMap$1 = linearMap;
  24033. function fixExtentWithBands(extent, nTick) {
  24034. var size = extent[1] - extent[0];
  24035. var len = nTick;
  24036. var margin = size / len / 2;
  24037. extent[0] += margin;
  24038. extent[1] -= margin;
  24039. }
  24040. var normalizedExtent = [0, 1];
  24041. /**
  24042. * @name module:echarts/coord/CartesianAxis
  24043. * @constructor
  24044. */
  24045. var Axis = function (dim, scale, extent) {
  24046. /**
  24047. * Axis dimension. Such as 'x', 'y', 'z', 'angle', 'radius'
  24048. * @type {string}
  24049. */
  24050. this.dim = dim;
  24051. /**
  24052. * Axis scale
  24053. * @type {module:echarts/coord/scale/*}
  24054. */
  24055. this.scale = scale;
  24056. /**
  24057. * @type {Array.<number>}
  24058. * @private
  24059. */
  24060. this._extent = extent || [0, 0];
  24061. /**
  24062. * @type {boolean}
  24063. */
  24064. this.inverse = false;
  24065. /**
  24066. * Usually true when axis has a ordinal scale
  24067. * @type {boolean}
  24068. */
  24069. this.onBand = false;
  24070. /**
  24071. * @private
  24072. * @type {number}
  24073. */
  24074. this._labelInterval;
  24075. };
  24076. Axis.prototype = {
  24077. constructor: Axis,
  24078. /**
  24079. * If axis extent contain given coord
  24080. * @param {number} coord
  24081. * @return {boolean}
  24082. */
  24083. contain: function (coord) {
  24084. var extent = this._extent;
  24085. var min = Math.min(extent[0], extent[1]);
  24086. var max = Math.max(extent[0], extent[1]);
  24087. return coord >= min && coord <= max;
  24088. },
  24089. /**
  24090. * If axis extent contain given data
  24091. * @param {number} data
  24092. * @return {boolean}
  24093. */
  24094. containData: function (data) {
  24095. return this.contain(this.dataToCoord(data));
  24096. },
  24097. /**
  24098. * Get coord extent.
  24099. * @return {Array.<number>}
  24100. */
  24101. getExtent: function () {
  24102. return this._extent.slice();
  24103. },
  24104. /**
  24105. * Get precision used for formatting
  24106. * @param {Array.<number>} [dataExtent]
  24107. * @return {number}
  24108. */
  24109. getPixelPrecision: function (dataExtent) {
  24110. return getPixelPrecision(
  24111. dataExtent || this.scale.getExtent(),
  24112. this._extent
  24113. );
  24114. },
  24115. /**
  24116. * Set coord extent
  24117. * @param {number} start
  24118. * @param {number} end
  24119. */
  24120. setExtent: function (start, end) {
  24121. var extent = this._extent;
  24122. extent[0] = start;
  24123. extent[1] = end;
  24124. },
  24125. /**
  24126. * Convert data to coord. Data is the rank if it has a ordinal scale
  24127. * @param {number} data
  24128. * @param {boolean} clamp
  24129. * @return {number}
  24130. */
  24131. dataToCoord: function (data, clamp) {
  24132. var extent = this._extent;
  24133. var scale = this.scale;
  24134. data = scale.normalize(data);
  24135. if (this.onBand && scale.type === 'ordinal') {
  24136. extent = extent.slice();
  24137. fixExtentWithBands(extent, scale.count());
  24138. }
  24139. return linearMap$1(data, normalizedExtent, extent, clamp);
  24140. },
  24141. /**
  24142. * Convert coord to data. Data is the rank if it has a ordinal scale
  24143. * @param {number} coord
  24144. * @param {boolean} clamp
  24145. * @return {number}
  24146. */
  24147. coordToData: function (coord, clamp) {
  24148. var extent = this._extent;
  24149. var scale = this.scale;
  24150. if (this.onBand && scale.type === 'ordinal') {
  24151. extent = extent.slice();
  24152. fixExtentWithBands(extent, scale.count());
  24153. }
  24154. var t = linearMap$1(coord, extent, normalizedExtent, clamp);
  24155. return this.scale.scale(t);
  24156. },
  24157. /**
  24158. * Convert pixel point to data in axis
  24159. * @param {Array.<number>} point
  24160. * @param {boolean} clamp
  24161. * @return {number} data
  24162. */
  24163. pointToData: function (point, clamp) {
  24164. // Should be implemented in derived class if necessary.
  24165. },
  24166. /**
  24167. * @return {Array.<number>}
  24168. */
  24169. getTicksCoords: function (alignWithLabel) {
  24170. if (this.onBand && !alignWithLabel) {
  24171. var bands = this.getBands();
  24172. var coords = [];
  24173. for (var i = 0; i < bands.length; i++) {
  24174. coords.push(bands[i][0]);
  24175. }
  24176. if (bands[i - 1]) {
  24177. coords.push(bands[i - 1][1]);
  24178. }
  24179. return coords;
  24180. }
  24181. else {
  24182. return map(this.scale.getTicks(), this.dataToCoord, this);
  24183. }
  24184. },
  24185. /**
  24186. * Coords of labels are on the ticks or on the middle of bands
  24187. * @return {Array.<number>}
  24188. */
  24189. getLabelsCoords: function () {
  24190. return map(this.scale.getTicks(), this.dataToCoord, this);
  24191. },
  24192. /**
  24193. * Get bands.
  24194. *
  24195. * If axis has labels [1, 2, 3, 4]. Bands on the axis are
  24196. * |---1---|---2---|---3---|---4---|.
  24197. *
  24198. * @return {Array}
  24199. */
  24200. // FIXME Situation when labels is on ticks
  24201. getBands: function () {
  24202. var extent = this.getExtent();
  24203. var bands = [];
  24204. var len = this.scale.count();
  24205. var start = extent[0];
  24206. var end = extent[1];
  24207. var span = end - start;
  24208. for (var i = 0; i < len; i++) {
  24209. bands.push([
  24210. span * i / len + start,
  24211. span * (i + 1) / len + start
  24212. ]);
  24213. }
  24214. return bands;
  24215. },
  24216. /**
  24217. * Get width of band
  24218. * @return {number}
  24219. */
  24220. getBandWidth: function () {
  24221. var axisExtent = this._extent;
  24222. var dataExtent = this.scale.getExtent();
  24223. var len = dataExtent[1] - dataExtent[0] + (this.onBand ? 1 : 0);
  24224. // Fix #2728, avoid NaN when only one data.
  24225. len === 0 && (len = 1);
  24226. var size = Math.abs(axisExtent[1] - axisExtent[0]);
  24227. return Math.abs(size) / len;
  24228. },
  24229. /**
  24230. * @abstract
  24231. * @return {boolean} Is horizontal
  24232. */
  24233. isHorizontal: null,
  24234. /**
  24235. * @abstract
  24236. * @return {number} Get axis rotate, by degree.
  24237. */
  24238. getRotate: null,
  24239. /**
  24240. * Get interval of the axis label.
  24241. * To get precise result, at least one of `getRotate` and `isHorizontal`
  24242. * should be implemented.
  24243. * @return {number}
  24244. */
  24245. getLabelInterval: function () {
  24246. var labelInterval = this._labelInterval;
  24247. if (!labelInterval) {
  24248. var axisModel = this.model;
  24249. var labelModel = axisModel.getModel('axisLabel');
  24250. labelInterval = labelModel.get('interval');
  24251. if (this.type === 'category'
  24252. && (labelInterval == null || labelInterval === 'auto')
  24253. ) {
  24254. labelInterval = getAxisLabelInterval(
  24255. map(this.scale.getTicks(), this.dataToCoord, this),
  24256. axisModel.getFormattedLabels(),
  24257. labelModel.getFont(),
  24258. this.getRotate
  24259. ? this.getRotate()
  24260. : (this.isHorizontal && !this.isHorizontal())
  24261. ? 90
  24262. : 0,
  24263. labelModel.get('rotate')
  24264. );
  24265. }
  24266. this._labelInterval = labelInterval;
  24267. }
  24268. return labelInterval;
  24269. }
  24270. };
  24271. /**
  24272. * Extend axis 2d
  24273. * @constructor module:echarts/coord/cartesian/Axis2D
  24274. * @extends {module:echarts/coord/cartesian/Axis}
  24275. * @param {string} dim
  24276. * @param {*} scale
  24277. * @param {Array.<number>} coordExtent
  24278. * @param {string} axisType
  24279. * @param {string} position
  24280. */
  24281. var Axis2D = function (dim, scale, coordExtent, axisType, position) {
  24282. Axis.call(this, dim, scale, coordExtent);
  24283. /**
  24284. * Axis type
  24285. * - 'category'
  24286. * - 'value'
  24287. * - 'time'
  24288. * - 'log'
  24289. * @type {string}
  24290. */
  24291. this.type = axisType || 'value';
  24292. /**
  24293. * Axis position
  24294. * - 'top'
  24295. * - 'bottom'
  24296. * - 'left'
  24297. * - 'right'
  24298. */
  24299. this.position = position || 'bottom';
  24300. };
  24301. Axis2D.prototype = {
  24302. constructor: Axis2D,
  24303. /**
  24304. * Index of axis, can be used as key
  24305. */
  24306. index: 0,
  24307. /**
  24308. * If axis is on the zero position of the other axis
  24309. * @type {boolean}
  24310. */
  24311. onZero: false,
  24312. /**
  24313. * Axis model
  24314. * @param {module:echarts/coord/cartesian/AxisModel}
  24315. */
  24316. model: null,
  24317. isHorizontal: function () {
  24318. var position = this.position;
  24319. return position === 'top' || position === 'bottom';
  24320. },
  24321. /**
  24322. * Each item cooresponds to this.getExtent(), which
  24323. * means globalExtent[0] may greater than globalExtent[1],
  24324. * unless `asc` is input.
  24325. *
  24326. * @param {boolean} [asc]
  24327. * @return {Array.<number>}
  24328. */
  24329. getGlobalExtent: function (asc) {
  24330. var ret = this.getExtent();
  24331. ret[0] = this.toGlobalCoord(ret[0]);
  24332. ret[1] = this.toGlobalCoord(ret[1]);
  24333. asc && ret[0] > ret[1] && ret.reverse();
  24334. return ret;
  24335. },
  24336. getOtherAxis: function () {
  24337. this.grid.getOtherAxis();
  24338. },
  24339. /**
  24340. * If label is ignored.
  24341. * Automatically used when axis is category and label can not be all shown
  24342. * @param {number} idx
  24343. * @return {boolean}
  24344. */
  24345. isLabelIgnored: function (idx) {
  24346. if (this.type === 'category') {
  24347. var labelInterval = this.getLabelInterval();
  24348. return ((typeof labelInterval === 'function')
  24349. && !labelInterval(idx, this.scale.getLabel(idx)))
  24350. || idx % (labelInterval + 1);
  24351. }
  24352. },
  24353. /**
  24354. * @override
  24355. */
  24356. pointToData: function (point, clamp) {
  24357. return this.coordToData(this.toLocalCoord(point[this.dim === 'x' ? 0 : 1]), clamp);
  24358. },
  24359. /**
  24360. * Transform global coord to local coord,
  24361. * i.e. var localCoord = axis.toLocalCoord(80);
  24362. * designate by module:echarts/coord/cartesian/Grid.
  24363. * @type {Function}
  24364. */
  24365. toLocalCoord: null,
  24366. /**
  24367. * Transform global coord to local coord,
  24368. * i.e. var globalCoord = axis.toLocalCoord(40);
  24369. * designate by module:echarts/coord/cartesian/Grid.
  24370. * @type {Function}
  24371. */
  24372. toGlobalCoord: null
  24373. };
  24374. inherits(Axis2D, Axis);
  24375. var defaultOption = {
  24376. show: true,
  24377. zlevel: 0, // 一级层叠
  24378. z: 0, // 二级层叠
  24379. // 反向坐标轴
  24380. inverse: false,
  24381. // 坐标轴名字,默认为空
  24382. name: '',
  24383. // 坐标轴名字位置,支持'start' | 'middle' | 'end'
  24384. nameLocation: 'end',
  24385. // 坐标轴名字旋转,degree。
  24386. nameRotate: null, // Adapt to axis rotate, when nameLocation is 'middle'.
  24387. nameTruncate: {
  24388. maxWidth: null,
  24389. ellipsis: '...',
  24390. placeholder: '.'
  24391. },
  24392. // 坐标轴文字样式,默认取全局样式
  24393. nameTextStyle: {},
  24394. // 文字与轴线距离
  24395. nameGap: 15,
  24396. silent: false, // Default false to support tooltip.
  24397. triggerEvent: false, // Default false to avoid legacy user event listener fail.
  24398. tooltip: {
  24399. show: false
  24400. },
  24401. axisPointer: {},
  24402. // 坐标轴线
  24403. axisLine: {
  24404. // 默认显示,属性show控制显示与否
  24405. show: true,
  24406. onZero: true,
  24407. onZeroAxisIndex: null,
  24408. // 属性lineStyle控制线条样式
  24409. lineStyle: {
  24410. color: '#333',
  24411. width: 1,
  24412. type: 'solid'
  24413. },
  24414. // 坐标轴两端的箭头
  24415. symbol: ['none', 'none'],
  24416. symbolSize: [10, 15]
  24417. },
  24418. // 坐标轴小标记
  24419. axisTick: {
  24420. // 属性show控制显示与否,默认显示
  24421. show: true,
  24422. // 控制小标记是否在grid里
  24423. inside: false,
  24424. // 属性length控制线长
  24425. length: 5,
  24426. // 属性lineStyle控制线条样式
  24427. lineStyle: {
  24428. width: 1
  24429. }
  24430. },
  24431. // 坐标轴文本标签,详见axis.axisLabel
  24432. axisLabel: {
  24433. show: true,
  24434. // 控制文本标签是否在grid里
  24435. inside: false,
  24436. rotate: 0,
  24437. showMinLabel: null, // true | false | null (auto)
  24438. showMaxLabel: null, // true | false | null (auto)
  24439. margin: 8,
  24440. // formatter: null,
  24441. // 其余属性默认使用全局文本样式,详见TEXTSTYLE
  24442. fontSize: 12
  24443. },
  24444. // 分隔线
  24445. splitLine: {
  24446. // 默认显示,属性show控制显示与否
  24447. show: true,
  24448. // 属性lineStyle(详见lineStyle)控制线条样式
  24449. lineStyle: {
  24450. color: ['#ccc'],
  24451. width: 1,
  24452. type: 'solid'
  24453. }
  24454. },
  24455. // 分隔区域
  24456. splitArea: {
  24457. // 默认不显示,属性show控制显示与否
  24458. show: false,
  24459. // 属性areaStyle(详见areaStyle)控制区域样式
  24460. areaStyle: {
  24461. color: ['rgba(250,250,250,0.3)','rgba(200,200,200,0.3)']
  24462. }
  24463. }
  24464. };
  24465. var axisDefault = {};
  24466. axisDefault.categoryAxis = merge({
  24467. // 类目起始和结束两端空白策略
  24468. boundaryGap: true,
  24469. // splitArea: {
  24470. // show: false
  24471. // },
  24472. splitLine: {
  24473. show: false
  24474. },
  24475. // 坐标轴小标记
  24476. axisTick: {
  24477. // If tick is align with label when boundaryGap is true
  24478. alignWithLabel: false,
  24479. interval: 'auto'
  24480. },
  24481. // 坐标轴文本标签,详见axis.axisLabel
  24482. axisLabel: {
  24483. interval: 'auto'
  24484. }
  24485. }, defaultOption);
  24486. axisDefault.valueAxis = merge({
  24487. // 数值起始和结束两端空白策略
  24488. boundaryGap: [0, 0],
  24489. // 最小值, 设置成 'dataMin' 则从数据中计算最小值
  24490. // min: null,
  24491. // 最大值,设置成 'dataMax' 则从数据中计算最大值
  24492. // max: null,
  24493. // Readonly prop, specifies start value of the range when using data zoom.
  24494. // rangeStart: null
  24495. // Readonly prop, specifies end value of the range when using data zoom.
  24496. // rangeEnd: null
  24497. // 脱离0值比例,放大聚焦到最终_min,_max区间
  24498. // scale: false,
  24499. // 分割段数,默认为5
  24500. splitNumber: 5
  24501. // Minimum interval
  24502. // minInterval: null
  24503. // maxInterval: null
  24504. }, defaultOption);
  24505. // FIXME
  24506. axisDefault.timeAxis = defaults({
  24507. scale: true,
  24508. min: 'dataMin',
  24509. max: 'dataMax'
  24510. }, axisDefault.valueAxis);
  24511. axisDefault.logAxis = defaults({
  24512. scale: true,
  24513. logBase: 10
  24514. }, axisDefault.valueAxis);
  24515. // FIXME axisType is fixed ?
  24516. var AXIS_TYPES = ['value', 'category', 'time', 'log'];
  24517. /**
  24518. * Generate sub axis model class
  24519. * @param {string} axisName 'x' 'y' 'radius' 'angle' 'parallel'
  24520. * @param {module:echarts/model/Component} BaseAxisModelClass
  24521. * @param {Function} axisTypeDefaulter
  24522. * @param {Object} [extraDefaultOption]
  24523. */
  24524. var axisModelCreator = function (axisName, BaseAxisModelClass, axisTypeDefaulter, extraDefaultOption) {
  24525. each$1(AXIS_TYPES, function (axisType) {
  24526. BaseAxisModelClass.extend({
  24527. type: axisName + 'Axis.' + axisType,
  24528. mergeDefaultAndTheme: function (option, ecModel) {
  24529. var layoutMode = this.layoutMode;
  24530. var inputPositionParams = layoutMode
  24531. ? getLayoutParams(option) : {};
  24532. var themeModel = ecModel.getTheme();
  24533. merge(option, themeModel.get(axisType + 'Axis'));
  24534. merge(option, this.getDefaultOption());
  24535. option.type = axisTypeDefaulter(axisName, option);
  24536. if (layoutMode) {
  24537. mergeLayoutParam(option, inputPositionParams, layoutMode);
  24538. }
  24539. },
  24540. defaultOption: mergeAll(
  24541. [
  24542. {},
  24543. axisDefault[axisType + 'Axis'],
  24544. extraDefaultOption
  24545. ],
  24546. true
  24547. )
  24548. });
  24549. });
  24550. ComponentModel.registerSubTypeDefaulter(
  24551. axisName + 'Axis',
  24552. curry(axisTypeDefaulter, axisName)
  24553. );
  24554. };
  24555. function getName(obj) {
  24556. if (isObject(obj) && obj.value != null) {
  24557. return obj.value;
  24558. }
  24559. else {
  24560. return obj + '';
  24561. }
  24562. }
  24563. var axisModelCommonMixin = {
  24564. /**
  24565. * Format labels
  24566. * @return {Array.<string>}
  24567. */
  24568. getFormattedLabels: function () {
  24569. return getFormattedLabels(
  24570. this.axis,
  24571. this.get('axisLabel.formatter')
  24572. );
  24573. },
  24574. /**
  24575. * Get categories
  24576. */
  24577. getCategories: function () {
  24578. return this.get('type') === 'category'
  24579. && map(this.get('data'), getName);
  24580. },
  24581. /**
  24582. * @param {boolean} origin
  24583. * @return {number|string} min value or 'dataMin' or null/undefined (means auto) or NaN
  24584. */
  24585. getMin: function (origin) {
  24586. var option = this.option;
  24587. var min = (!origin && option.rangeStart != null)
  24588. ? option.rangeStart : option.min;
  24589. if (this.axis
  24590. && min != null
  24591. && min !== 'dataMin'
  24592. && typeof min !== 'function'
  24593. && !eqNaN(min)
  24594. ) {
  24595. min = this.axis.scale.parse(min);
  24596. }
  24597. return min;
  24598. },
  24599. /**
  24600. * @param {boolean} origin
  24601. * @return {number|string} max value or 'dataMax' or null/undefined (means auto) or NaN
  24602. */
  24603. getMax: function (origin) {
  24604. var option = this.option;
  24605. var max = (!origin && option.rangeEnd != null)
  24606. ? option.rangeEnd : option.max;
  24607. if (this.axis
  24608. && max != null
  24609. && max !== 'dataMax'
  24610. && typeof max !== 'function'
  24611. && !eqNaN(max)
  24612. ) {
  24613. max = this.axis.scale.parse(max);
  24614. }
  24615. return max;
  24616. },
  24617. /**
  24618. * @return {boolean}
  24619. */
  24620. getNeedCrossZero: function () {
  24621. var option = this.option;
  24622. return (option.rangeStart != null || option.rangeEnd != null)
  24623. ? false : !option.scale;
  24624. },
  24625. /**
  24626. * Should be implemented by each axis model if necessary.
  24627. * @return {module:echarts/model/Component} coordinate system model
  24628. */
  24629. getCoordSysModel: noop,
  24630. /**
  24631. * @param {number} rangeStart Can only be finite number or null/undefined or NaN.
  24632. * @param {number} rangeEnd Can only be finite number or null/undefined or NaN.
  24633. */
  24634. setRange: function (rangeStart, rangeEnd) {
  24635. this.option.rangeStart = rangeStart;
  24636. this.option.rangeEnd = rangeEnd;
  24637. },
  24638. /**
  24639. * Reset range
  24640. */
  24641. resetRange: function () {
  24642. // rangeStart and rangeEnd is readonly.
  24643. this.option.rangeStart = this.option.rangeEnd = null;
  24644. }
  24645. };
  24646. var AxisModel = ComponentModel.extend({
  24647. type: 'cartesian2dAxis',
  24648. /**
  24649. * @type {module:echarts/coord/cartesian/Axis2D}
  24650. */
  24651. axis: null,
  24652. /**
  24653. * @override
  24654. */
  24655. init: function () {
  24656. AxisModel.superApply(this, 'init', arguments);
  24657. this.resetRange();
  24658. },
  24659. /**
  24660. * @override
  24661. */
  24662. mergeOption: function () {
  24663. AxisModel.superApply(this, 'mergeOption', arguments);
  24664. this.resetRange();
  24665. },
  24666. /**
  24667. * @override
  24668. */
  24669. restoreData: function () {
  24670. AxisModel.superApply(this, 'restoreData', arguments);
  24671. this.resetRange();
  24672. },
  24673. /**
  24674. * @override
  24675. * @return {module:echarts/model/Component}
  24676. */
  24677. getCoordSysModel: function () {
  24678. return this.ecModel.queryComponents({
  24679. mainType: 'grid',
  24680. index: this.option.gridIndex,
  24681. id: this.option.gridId
  24682. })[0];
  24683. }
  24684. });
  24685. function getAxisType(axisDim, option) {
  24686. // Default axis with data is category axis
  24687. return option.type || (option.data ? 'category' : 'value');
  24688. }
  24689. merge(AxisModel.prototype, axisModelCommonMixin);
  24690. var extraOption = {
  24691. // gridIndex: 0,
  24692. // gridId: '',
  24693. // Offset is for multiple axis on the same position
  24694. offset: 0
  24695. };
  24696. axisModelCreator('x', AxisModel, getAxisType, extraOption);
  24697. axisModelCreator('y', AxisModel, getAxisType, extraOption);
  24698. // Grid 是在有直角坐标系的时候必须要存在的
  24699. // 所以这里也要被 Cartesian2D 依赖
  24700. ComponentModel.extend({
  24701. type: 'grid',
  24702. dependencies: ['xAxis', 'yAxis'],
  24703. layoutMode: 'box',
  24704. /**
  24705. * @type {module:echarts/coord/cartesian/Grid}
  24706. */
  24707. coordinateSystem: null,
  24708. defaultOption: {
  24709. show: false,
  24710. zlevel: 0,
  24711. z: 0,
  24712. left: '10%',
  24713. top: 60,
  24714. right: '10%',
  24715. bottom: 60,
  24716. // If grid size contain label
  24717. containLabel: false,
  24718. // width: {totalWidth} - left - right,
  24719. // height: {totalHeight} - top - bottom,
  24720. backgroundColor: 'rgba(0,0,0,0)',
  24721. borderWidth: 1,
  24722. borderColor: '#ccc'
  24723. }
  24724. });
  24725. /**
  24726. * Grid is a region which contains at most 4 cartesian systems
  24727. *
  24728. * TODO Default cartesian
  24729. */
  24730. // Depends on GridModel, AxisModel, which performs preprocess.
  24731. var each$8 = each$1;
  24732. var ifAxisCrossZero = ifAxisCrossZero$1;
  24733. var niceScaleExtent = niceScaleExtent$1;
  24734. /**
  24735. * Check if the axis is used in the specified grid
  24736. * @inner
  24737. */
  24738. function isAxisUsedInTheGrid(axisModel, gridModel, ecModel) {
  24739. return axisModel.getCoordSysModel() === gridModel;
  24740. }
  24741. function rotateTextRect(textRect, rotate) {
  24742. var rotateRadians = rotate * Math.PI / 180;
  24743. var boundingBox = textRect.plain();
  24744. var beforeWidth = boundingBox.width;
  24745. var beforeHeight = boundingBox.height;
  24746. var afterWidth = beforeWidth * Math.cos(rotateRadians) + beforeHeight * Math.sin(rotateRadians);
  24747. var afterHeight = beforeWidth * Math.sin(rotateRadians) + beforeHeight * Math.cos(rotateRadians);
  24748. var rotatedRect = new BoundingRect(boundingBox.x, boundingBox.y, afterWidth, afterHeight);
  24749. return rotatedRect;
  24750. }
  24751. function getLabelUnionRect(axis) {
  24752. var axisModel = axis.model;
  24753. var labels = axisModel.getFormattedLabels();
  24754. var axisLabelModel = axisModel.getModel('axisLabel');
  24755. var rect;
  24756. var step = 1;
  24757. var labelCount = labels.length;
  24758. if (labelCount > 40) {
  24759. // Simple optimization for large amount of labels
  24760. step = Math.ceil(labelCount / 40);
  24761. }
  24762. for (var i = 0; i < labelCount; i += step) {
  24763. if (!axis.isLabelIgnored(i)) {
  24764. var unrotatedSingleRect = axisLabelModel.getTextRect(labels[i]);
  24765. var singleRect = rotateTextRect(unrotatedSingleRect, axisLabelModel.get('rotate') || 0);
  24766. rect ? rect.union(singleRect) : (rect = singleRect);
  24767. }
  24768. }
  24769. return rect;
  24770. }
  24771. function Grid(gridModel, ecModel, api) {
  24772. /**
  24773. * @type {Object.<string, module:echarts/coord/cartesian/Cartesian2D>}
  24774. * @private
  24775. */
  24776. this._coordsMap = {};
  24777. /**
  24778. * @type {Array.<module:echarts/coord/cartesian/Cartesian>}
  24779. * @private
  24780. */
  24781. this._coordsList = [];
  24782. /**
  24783. * @type {Object.<string, module:echarts/coord/cartesian/Axis2D>}
  24784. * @private
  24785. */
  24786. this._axesMap = {};
  24787. /**
  24788. * @type {Array.<module:echarts/coord/cartesian/Axis2D>}
  24789. * @private
  24790. */
  24791. this._axesList = [];
  24792. this._initCartesian(gridModel, ecModel, api);
  24793. this.model = gridModel;
  24794. }
  24795. var gridProto = Grid.prototype;
  24796. gridProto.type = 'grid';
  24797. gridProto.axisPointerEnabled = true;
  24798. gridProto.getRect = function () {
  24799. return this._rect;
  24800. };
  24801. gridProto.update = function (ecModel, api) {
  24802. var axesMap = this._axesMap;
  24803. this._updateScale(ecModel, this.model);
  24804. each$8(axesMap.x, function (xAxis) {
  24805. niceScaleExtent(xAxis.scale, xAxis.model);
  24806. });
  24807. each$8(axesMap.y, function (yAxis) {
  24808. niceScaleExtent(yAxis.scale, yAxis.model);
  24809. });
  24810. each$8(axesMap.x, function (xAxis) {
  24811. fixAxisOnZero(axesMap, 'y', xAxis);
  24812. });
  24813. each$8(axesMap.y, function (yAxis) {
  24814. fixAxisOnZero(axesMap, 'x', yAxis);
  24815. });
  24816. // Resize again if containLabel is enabled
  24817. // FIXME It may cause getting wrong grid size in data processing stage
  24818. this.resize(this.model, api);
  24819. };
  24820. function fixAxisOnZero(axesMap, otherAxisDim, axis) {
  24821. // onZero can not be enabled in these two situations:
  24822. // 1. When any other axis is a category axis.
  24823. // 2. When no axis is cross 0 point.
  24824. var axes = axesMap[otherAxisDim];
  24825. if (!axis.onZero) {
  24826. return;
  24827. }
  24828. var onZeroAxisIndex = axis.onZeroAxisIndex;
  24829. // If target axis is specified.
  24830. if (onZeroAxisIndex != null) {
  24831. var otherAxis = axes[onZeroAxisIndex];
  24832. if (otherAxis && canNotOnZeroToAxis(otherAxis)) {
  24833. axis.onZero = false;
  24834. }
  24835. return;
  24836. }
  24837. for (var idx in axes) {
  24838. if (axes.hasOwnProperty(idx)) {
  24839. var otherAxis = axes[idx];
  24840. if (otherAxis && !canNotOnZeroToAxis(otherAxis)) {
  24841. onZeroAxisIndex = +idx;
  24842. break;
  24843. }
  24844. }
  24845. }
  24846. if (onZeroAxisIndex == null) {
  24847. axis.onZero = false;
  24848. }
  24849. axis.onZeroAxisIndex = onZeroAxisIndex;
  24850. }
  24851. function canNotOnZeroToAxis(axis) {
  24852. return axis.type === 'category' || axis.type === 'time' || !ifAxisCrossZero(axis);
  24853. }
  24854. /**
  24855. * Resize the grid
  24856. * @param {module:echarts/coord/cartesian/GridModel} gridModel
  24857. * @param {module:echarts/ExtensionAPI} api
  24858. */
  24859. gridProto.resize = function (gridModel, api, ignoreContainLabel) {
  24860. var gridRect = getLayoutRect(
  24861. gridModel.getBoxLayoutParams(), {
  24862. width: api.getWidth(),
  24863. height: api.getHeight()
  24864. });
  24865. this._rect = gridRect;
  24866. var axesList = this._axesList;
  24867. adjustAxes();
  24868. // Minus label size
  24869. if (!ignoreContainLabel && gridModel.get('containLabel')) {
  24870. each$8(axesList, function (axis) {
  24871. if (!axis.model.get('axisLabel.inside')) {
  24872. var labelUnionRect = getLabelUnionRect(axis);
  24873. if (labelUnionRect) {
  24874. var dim = axis.isHorizontal() ? 'height' : 'width';
  24875. var margin = axis.model.get('axisLabel.margin');
  24876. gridRect[dim] -= labelUnionRect[dim] + margin;
  24877. if (axis.position === 'top') {
  24878. gridRect.y += labelUnionRect.height + margin;
  24879. }
  24880. else if (axis.position === 'left') {
  24881. gridRect.x += labelUnionRect.width + margin;
  24882. }
  24883. }
  24884. }
  24885. });
  24886. adjustAxes();
  24887. }
  24888. function adjustAxes() {
  24889. each$8(axesList, function (axis) {
  24890. var isHorizontal = axis.isHorizontal();
  24891. var extent = isHorizontal ? [0, gridRect.width] : [0, gridRect.height];
  24892. var idx = axis.inverse ? 1 : 0;
  24893. axis.setExtent(extent[idx], extent[1 - idx]);
  24894. updateAxisTransfrom(axis, isHorizontal ? gridRect.x : gridRect.y);
  24895. });
  24896. }
  24897. };
  24898. /**
  24899. * @param {string} axisType
  24900. * @param {number} [axisIndex]
  24901. */
  24902. gridProto.getAxis = function (axisType, axisIndex) {
  24903. var axesMapOnDim = this._axesMap[axisType];
  24904. if (axesMapOnDim != null) {
  24905. if (axisIndex == null) {
  24906. // Find first axis
  24907. for (var name in axesMapOnDim) {
  24908. if (axesMapOnDim.hasOwnProperty(name)) {
  24909. return axesMapOnDim[name];
  24910. }
  24911. }
  24912. }
  24913. return axesMapOnDim[axisIndex];
  24914. }
  24915. };
  24916. /**
  24917. * @return {Array.<module:echarts/coord/Axis>}
  24918. */
  24919. gridProto.getAxes = function () {
  24920. return this._axesList.slice();
  24921. };
  24922. /**
  24923. * Usage:
  24924. * grid.getCartesian(xAxisIndex, yAxisIndex);
  24925. * grid.getCartesian(xAxisIndex);
  24926. * grid.getCartesian(null, yAxisIndex);
  24927. * grid.getCartesian({xAxisIndex: ..., yAxisIndex: ...});
  24928. *
  24929. * @param {number|Object} [xAxisIndex]
  24930. * @param {number} [yAxisIndex]
  24931. */
  24932. gridProto.getCartesian = function (xAxisIndex, yAxisIndex) {
  24933. if (xAxisIndex != null && yAxisIndex != null) {
  24934. var key = 'x' + xAxisIndex + 'y' + yAxisIndex;
  24935. return this._coordsMap[key];
  24936. }
  24937. if (isObject(xAxisIndex)) {
  24938. yAxisIndex = xAxisIndex.yAxisIndex;
  24939. xAxisIndex = xAxisIndex.xAxisIndex;
  24940. }
  24941. // When only xAxisIndex or yAxisIndex given, find its first cartesian.
  24942. for (var i = 0, coordList = this._coordsList; i < coordList.length; i++) {
  24943. if (coordList[i].getAxis('x').index === xAxisIndex
  24944. || coordList[i].getAxis('y').index === yAxisIndex
  24945. ) {
  24946. return coordList[i];
  24947. }
  24948. }
  24949. };
  24950. gridProto.getCartesians = function () {
  24951. return this._coordsList.slice();
  24952. };
  24953. /**
  24954. * @implements
  24955. * see {module:echarts/CoodinateSystem}
  24956. */
  24957. gridProto.convertToPixel = function (ecModel, finder, value) {
  24958. var target = this._findConvertTarget(ecModel, finder);
  24959. return target.cartesian
  24960. ? target.cartesian.dataToPoint(value)
  24961. : target.axis
  24962. ? target.axis.toGlobalCoord(target.axis.dataToCoord(value))
  24963. : null;
  24964. };
  24965. /**
  24966. * @implements
  24967. * see {module:echarts/CoodinateSystem}
  24968. */
  24969. gridProto.convertFromPixel = function (ecModel, finder, value) {
  24970. var target = this._findConvertTarget(ecModel, finder);
  24971. return target.cartesian
  24972. ? target.cartesian.pointToData(value)
  24973. : target.axis
  24974. ? target.axis.coordToData(target.axis.toLocalCoord(value))
  24975. : null;
  24976. };
  24977. /**
  24978. * @inner
  24979. */
  24980. gridProto._findConvertTarget = function (ecModel, finder) {
  24981. var seriesModel = finder.seriesModel;
  24982. var xAxisModel = finder.xAxisModel
  24983. || (seriesModel && seriesModel.getReferringComponents('xAxis')[0]);
  24984. var yAxisModel = finder.yAxisModel
  24985. || (seriesModel && seriesModel.getReferringComponents('yAxis')[0]);
  24986. var gridModel = finder.gridModel;
  24987. var coordsList = this._coordsList;
  24988. var cartesian;
  24989. var axis;
  24990. if (seriesModel) {
  24991. cartesian = seriesModel.coordinateSystem;
  24992. indexOf(coordsList, cartesian) < 0 && (cartesian = null);
  24993. }
  24994. else if (xAxisModel && yAxisModel) {
  24995. cartesian = this.getCartesian(xAxisModel.componentIndex, yAxisModel.componentIndex);
  24996. }
  24997. else if (xAxisModel) {
  24998. axis = this.getAxis('x', xAxisModel.componentIndex);
  24999. }
  25000. else if (yAxisModel) {
  25001. axis = this.getAxis('y', yAxisModel.componentIndex);
  25002. }
  25003. // Lowest priority.
  25004. else if (gridModel) {
  25005. var grid = gridModel.coordinateSystem;
  25006. if (grid === this) {
  25007. cartesian = this._coordsList[0];
  25008. }
  25009. }
  25010. return {cartesian: cartesian, axis: axis};
  25011. };
  25012. /**
  25013. * @implements
  25014. * see {module:echarts/CoodinateSystem}
  25015. */
  25016. gridProto.containPoint = function (point) {
  25017. var coord = this._coordsList[0];
  25018. if (coord) {
  25019. return coord.containPoint(point);
  25020. }
  25021. };
  25022. /**
  25023. * Initialize cartesian coordinate systems
  25024. * @private
  25025. */
  25026. gridProto._initCartesian = function (gridModel, ecModel, api) {
  25027. var axisPositionUsed = {
  25028. left: false,
  25029. right: false,
  25030. top: false,
  25031. bottom: false
  25032. };
  25033. var axesMap = {
  25034. x: {},
  25035. y: {}
  25036. };
  25037. var axesCount = {
  25038. x: 0,
  25039. y: 0
  25040. };
  25041. /// Create axis
  25042. ecModel.eachComponent('xAxis', createAxisCreator('x'), this);
  25043. ecModel.eachComponent('yAxis', createAxisCreator('y'), this);
  25044. if (!axesCount.x || !axesCount.y) {
  25045. // Roll back when there no either x or y axis
  25046. this._axesMap = {};
  25047. this._axesList = [];
  25048. return;
  25049. }
  25050. this._axesMap = axesMap;
  25051. /// Create cartesian2d
  25052. each$8(axesMap.x, function (xAxis, xAxisIndex) {
  25053. each$8(axesMap.y, function (yAxis, yAxisIndex) {
  25054. var key = 'x' + xAxisIndex + 'y' + yAxisIndex;
  25055. var cartesian = new Cartesian2D(key);
  25056. cartesian.grid = this;
  25057. cartesian.model = gridModel;
  25058. this._coordsMap[key] = cartesian;
  25059. this._coordsList.push(cartesian);
  25060. cartesian.addAxis(xAxis);
  25061. cartesian.addAxis(yAxis);
  25062. }, this);
  25063. }, this);
  25064. function createAxisCreator(axisType) {
  25065. return function (axisModel, idx) {
  25066. if (!isAxisUsedInTheGrid(axisModel, gridModel, ecModel)) {
  25067. return;
  25068. }
  25069. var axisPosition = axisModel.get('position');
  25070. if (axisType === 'x') {
  25071. // Fix position
  25072. if (axisPosition !== 'top' && axisPosition !== 'bottom') {
  25073. // Default bottom of X
  25074. axisPosition = 'bottom';
  25075. if (axisPositionUsed[axisPosition]) {
  25076. axisPosition = axisPosition === 'top' ? 'bottom' : 'top';
  25077. }
  25078. }
  25079. }
  25080. else {
  25081. // Fix position
  25082. if (axisPosition !== 'left' && axisPosition !== 'right') {
  25083. // Default left of Y
  25084. axisPosition = 'left';
  25085. if (axisPositionUsed[axisPosition]) {
  25086. axisPosition = axisPosition === 'left' ? 'right' : 'left';
  25087. }
  25088. }
  25089. }
  25090. axisPositionUsed[axisPosition] = true;
  25091. var axis = new Axis2D(
  25092. axisType, createScaleByModel(axisModel),
  25093. [0, 0],
  25094. axisModel.get('type'),
  25095. axisPosition
  25096. );
  25097. var isCategory = axis.type === 'category';
  25098. axis.onBand = isCategory && axisModel.get('boundaryGap');
  25099. axis.inverse = axisModel.get('inverse');
  25100. axis.onZero = axisModel.get('axisLine.onZero');
  25101. axis.onZeroAxisIndex = axisModel.get('axisLine.onZeroAxisIndex');
  25102. // Inject axis into axisModel
  25103. axisModel.axis = axis;
  25104. // Inject axisModel into axis
  25105. axis.model = axisModel;
  25106. // Inject grid info axis
  25107. axis.grid = this;
  25108. // Index of axis, can be used as key
  25109. axis.index = idx;
  25110. this._axesList.push(axis);
  25111. axesMap[axisType][idx] = axis;
  25112. axesCount[axisType]++;
  25113. };
  25114. }
  25115. };
  25116. /**
  25117. * Update cartesian properties from series
  25118. * @param {module:echarts/model/Option} option
  25119. * @private
  25120. */
  25121. gridProto._updateScale = function (ecModel, gridModel) {
  25122. // Reset scale
  25123. each$1(this._axesList, function (axis) {
  25124. axis.scale.setExtent(Infinity, -Infinity);
  25125. });
  25126. ecModel.eachSeries(function (seriesModel) {
  25127. if (isCartesian2D(seriesModel)) {
  25128. var axesModels = findAxesModels(seriesModel, ecModel);
  25129. var xAxisModel = axesModels[0];
  25130. var yAxisModel = axesModels[1];
  25131. if (!isAxisUsedInTheGrid(xAxisModel, gridModel, ecModel)
  25132. || !isAxisUsedInTheGrid(yAxisModel, gridModel, ecModel)
  25133. ) {
  25134. return;
  25135. }
  25136. var cartesian = this.getCartesian(
  25137. xAxisModel.componentIndex, yAxisModel.componentIndex
  25138. );
  25139. var data = seriesModel.getData();
  25140. var xAxis = cartesian.getAxis('x');
  25141. var yAxis = cartesian.getAxis('y');
  25142. if (data.type === 'list') {
  25143. unionExtent(data, xAxis, seriesModel);
  25144. unionExtent(data, yAxis, seriesModel);
  25145. }
  25146. }
  25147. }, this);
  25148. function unionExtent(data, axis, seriesModel) {
  25149. each$8(seriesModel.coordDimToDataDim(axis.dim), function (dim) {
  25150. axis.scale.unionExtentFromData(data, dim);
  25151. });
  25152. }
  25153. };
  25154. /**
  25155. * @param {string} [dim] 'x' or 'y' or 'auto' or null/undefined
  25156. * @return {Object} {baseAxes: [], otherAxes: []}
  25157. */
  25158. gridProto.getTooltipAxes = function (dim) {
  25159. var baseAxes = [];
  25160. var otherAxes = [];
  25161. each$8(this.getCartesians(), function (cartesian) {
  25162. var baseAxis = (dim != null && dim !== 'auto')
  25163. ? cartesian.getAxis(dim) : cartesian.getBaseAxis();
  25164. var otherAxis = cartesian.getOtherAxis(baseAxis);
  25165. indexOf(baseAxes, baseAxis) < 0 && baseAxes.push(baseAxis);
  25166. indexOf(otherAxes, otherAxis) < 0 && otherAxes.push(otherAxis);
  25167. });
  25168. return {baseAxes: baseAxes, otherAxes: otherAxes};
  25169. };
  25170. /**
  25171. * @inner
  25172. */
  25173. function updateAxisTransfrom(axis, coordBase) {
  25174. var axisExtent = axis.getExtent();
  25175. var axisExtentSum = axisExtent[0] + axisExtent[1];
  25176. // Fast transform
  25177. axis.toGlobalCoord = axis.dim === 'x'
  25178. ? function (coord) {
  25179. return coord + coordBase;
  25180. }
  25181. : function (coord) {
  25182. return axisExtentSum - coord + coordBase;
  25183. };
  25184. axis.toLocalCoord = axis.dim === 'x'
  25185. ? function (coord) {
  25186. return coord - coordBase;
  25187. }
  25188. : function (coord) {
  25189. return axisExtentSum - coord + coordBase;
  25190. };
  25191. }
  25192. var axesTypes = ['xAxis', 'yAxis'];
  25193. /**
  25194. * @inner
  25195. */
  25196. function findAxesModels(seriesModel, ecModel) {
  25197. return map(axesTypes, function (axisType) {
  25198. var axisModel = seriesModel.getReferringComponents(axisType)[0];
  25199. if (__DEV__) {
  25200. if (!axisModel) {
  25201. throw new Error(axisType + ' "' + retrieve(
  25202. seriesModel.get(axisType + 'Index'),
  25203. seriesModel.get(axisType + 'Id'),
  25204. 0
  25205. ) + '" not found');
  25206. }
  25207. }
  25208. return axisModel;
  25209. });
  25210. }
  25211. /**
  25212. * @inner
  25213. */
  25214. function isCartesian2D(seriesModel) {
  25215. return seriesModel.get('coordinateSystem') === 'cartesian2d';
  25216. }
  25217. Grid.create = function (ecModel, api) {
  25218. var grids = [];
  25219. ecModel.eachComponent('grid', function (gridModel, idx) {
  25220. var grid = new Grid(gridModel, ecModel, api);
  25221. grid.name = 'grid_' + idx;
  25222. // dataSampling requires axis extent, so resize
  25223. // should be performed in create stage.
  25224. grid.resize(gridModel, api, true);
  25225. gridModel.coordinateSystem = grid;
  25226. grids.push(grid);
  25227. });
  25228. // Inject the coordinateSystems into seriesModel
  25229. ecModel.eachSeries(function (seriesModel) {
  25230. if (!isCartesian2D(seriesModel)) {
  25231. return;
  25232. }
  25233. var axesModels = findAxesModels(seriesModel, ecModel);
  25234. var xAxisModel = axesModels[0];
  25235. var yAxisModel = axesModels[1];
  25236. var gridModel = xAxisModel.getCoordSysModel();
  25237. if (__DEV__) {
  25238. if (!gridModel) {
  25239. throw new Error(
  25240. 'Grid "' + retrieve(
  25241. xAxisModel.get('gridIndex'),
  25242. xAxisModel.get('gridId'),
  25243. 0
  25244. ) + '" not found'
  25245. );
  25246. }
  25247. if (xAxisModel.getCoordSysModel() !== yAxisModel.getCoordSysModel()) {
  25248. throw new Error('xAxis and yAxis must use the same grid');
  25249. }
  25250. }
  25251. var grid = gridModel.coordinateSystem;
  25252. seriesModel.coordinateSystem = grid.getCartesian(
  25253. xAxisModel.componentIndex, yAxisModel.componentIndex
  25254. );
  25255. });
  25256. return grids;
  25257. };
  25258. // For deciding which dimensions to use when creating list data
  25259. Grid.dimensions = Grid.prototype.dimensions = Cartesian2D.prototype.dimensions;
  25260. CoordinateSystemManager.register('cartesian2d', Grid);
  25261. var PI$2 = Math.PI;
  25262. function makeAxisEventDataBase(axisModel) {
  25263. var eventData = {
  25264. componentType: axisModel.mainType
  25265. };
  25266. eventData[axisModel.mainType + 'Index'] = axisModel.componentIndex;
  25267. return eventData;
  25268. }
  25269. /**
  25270. * A final axis is translated and rotated from a "standard axis".
  25271. * So opt.position and opt.rotation is required.
  25272. *
  25273. * A standard axis is and axis from [0, 0] to [0, axisExtent[1]],
  25274. * for example: (0, 0) ------------> (0, 50)
  25275. *
  25276. * nameDirection or tickDirection or labelDirection is 1 means tick
  25277. * or label is below the standard axis, whereas is -1 means above
  25278. * the standard axis. labelOffset means offset between label and axis,
  25279. * which is useful when 'onZero', where axisLabel is in the grid and
  25280. * label in outside grid.
  25281. *
  25282. * Tips: like always,
  25283. * positive rotation represents anticlockwise, and negative rotation
  25284. * represents clockwise.
  25285. * The direction of position coordinate is the same as the direction
  25286. * of screen coordinate.
  25287. *
  25288. * Do not need to consider axis 'inverse', which is auto processed by
  25289. * axis extent.
  25290. *
  25291. * @param {module:zrender/container/Group} group
  25292. * @param {Object} axisModel
  25293. * @param {Object} opt Standard axis parameters.
  25294. * @param {Array.<number>} opt.position [x, y]
  25295. * @param {number} opt.rotation by radian
  25296. * @param {number} [opt.nameDirection=1] 1 or -1 Used when nameLocation is 'middle' or 'center'.
  25297. * @param {number} [opt.tickDirection=1] 1 or -1
  25298. * @param {number} [opt.labelDirection=1] 1 or -1
  25299. * @param {number} [opt.labelOffset=0] Usefull when onZero.
  25300. * @param {string} [opt.axisLabelShow] default get from axisModel.
  25301. * @param {string} [opt.axisName] default get from axisModel.
  25302. * @param {number} [opt.axisNameAvailableWidth]
  25303. * @param {number} [opt.labelRotate] by degree, default get from axisModel.
  25304. * @param {number} [opt.labelInterval] Default label interval when label
  25305. * interval from model is null or 'auto'.
  25306. * @param {number} [opt.strokeContainThreshold] Default label interval when label
  25307. * @param {number} [opt.nameTruncateMaxWidth]
  25308. */
  25309. var AxisBuilder = function (axisModel, opt) {
  25310. /**
  25311. * @readOnly
  25312. */
  25313. this.opt = opt;
  25314. /**
  25315. * @readOnly
  25316. */
  25317. this.axisModel = axisModel;
  25318. // Default value
  25319. defaults(
  25320. opt,
  25321. {
  25322. labelOffset: 0,
  25323. nameDirection: 1,
  25324. tickDirection: 1,
  25325. labelDirection: 1,
  25326. silent: true
  25327. }
  25328. );
  25329. /**
  25330. * @readOnly
  25331. */
  25332. this.group = new Group();
  25333. // FIXME Not use a seperate text group?
  25334. var dumbGroup = new Group({
  25335. position: opt.position.slice(),
  25336. rotation: opt.rotation
  25337. });
  25338. // this.group.add(dumbGroup);
  25339. // this._dumbGroup = dumbGroup;
  25340. dumbGroup.updateTransform();
  25341. this._transform = dumbGroup.transform;
  25342. this._dumbGroup = dumbGroup;
  25343. };
  25344. AxisBuilder.prototype = {
  25345. constructor: AxisBuilder,
  25346. hasBuilder: function (name) {
  25347. return !!builders[name];
  25348. },
  25349. add: function (name) {
  25350. builders[name].call(this);
  25351. },
  25352. getGroup: function () {
  25353. return this.group;
  25354. }
  25355. };
  25356. var builders = {
  25357. /**
  25358. * @private
  25359. */
  25360. axisLine: function () {
  25361. var opt = this.opt;
  25362. var axisModel = this.axisModel;
  25363. if (!axisModel.get('axisLine.show')) {
  25364. return;
  25365. }
  25366. var extent = this.axisModel.axis.getExtent();
  25367. var matrix = this._transform;
  25368. var pt1 = [extent[0], 0];
  25369. var pt2 = [extent[1], 0];
  25370. if (matrix) {
  25371. applyTransform(pt1, pt1, matrix);
  25372. applyTransform(pt2, pt2, matrix);
  25373. }
  25374. var lineStyle = extend(
  25375. {
  25376. lineCap: 'round'
  25377. },
  25378. axisModel.getModel('axisLine.lineStyle').getLineStyle()
  25379. );
  25380. this.group.add(new Line(subPixelOptimizeLine({
  25381. // Id for animation
  25382. anid: 'line',
  25383. shape: {
  25384. x1: pt1[0],
  25385. y1: pt1[1],
  25386. x2: pt2[0],
  25387. y2: pt2[1]
  25388. },
  25389. style: lineStyle,
  25390. strokeContainThreshold: opt.strokeContainThreshold || 5,
  25391. silent: true,
  25392. z2: 1
  25393. })));
  25394. var arrows = axisModel.get('axisLine.symbol');
  25395. var arrowSize = axisModel.get('axisLine.symbolSize');
  25396. if (arrows != null) {
  25397. if (typeof arrows === 'string') {
  25398. // Use the same arrow for start and end point
  25399. arrows = [arrows, arrows];
  25400. }
  25401. if (typeof arrowSize === 'string'
  25402. || typeof arrowSize === 'number'
  25403. ) {
  25404. // Use the same size for width and height
  25405. arrowSize = [arrowSize, arrowSize];
  25406. }
  25407. var symbolWidth = arrowSize[0];
  25408. var symbolHeight = arrowSize[1];
  25409. each$1([
  25410. [opt.rotation + Math.PI / 2, pt1],
  25411. [opt.rotation - Math.PI / 2, pt2]
  25412. ], function (item, index) {
  25413. if (arrows[index] !== 'none' && arrows[index] != null) {
  25414. var symbol = createSymbol(
  25415. arrows[index],
  25416. -symbolWidth / 2,
  25417. -symbolHeight / 2,
  25418. symbolWidth,
  25419. symbolHeight,
  25420. lineStyle.stroke,
  25421. true
  25422. );
  25423. symbol.attr({
  25424. rotation: item[0],
  25425. position: item[1],
  25426. silent: true
  25427. });
  25428. this.group.add(symbol);
  25429. }
  25430. }, this);
  25431. }
  25432. },
  25433. /**
  25434. * @private
  25435. */
  25436. axisTickLabel: function () {
  25437. var axisModel = this.axisModel;
  25438. var opt = this.opt;
  25439. var tickEls = buildAxisTick(this, axisModel, opt);
  25440. var labelEls = buildAxisLabel(this, axisModel, opt);
  25441. fixMinMaxLabelShow(axisModel, labelEls, tickEls);
  25442. },
  25443. /**
  25444. * @private
  25445. */
  25446. axisName: function () {
  25447. var opt = this.opt;
  25448. var axisModel = this.axisModel;
  25449. var name = retrieve(opt.axisName, axisModel.get('name'));
  25450. if (!name) {
  25451. return;
  25452. }
  25453. var nameLocation = axisModel.get('nameLocation');
  25454. var nameDirection = opt.nameDirection;
  25455. var textStyleModel = axisModel.getModel('nameTextStyle');
  25456. var gap = axisModel.get('nameGap') || 0;
  25457. var extent = this.axisModel.axis.getExtent();
  25458. var gapSignal = extent[0] > extent[1] ? -1 : 1;
  25459. var pos = [
  25460. nameLocation === 'start'
  25461. ? extent[0] - gapSignal * gap
  25462. : nameLocation === 'end'
  25463. ? extent[1] + gapSignal * gap
  25464. : (extent[0] + extent[1]) / 2, // 'middle'
  25465. // Reuse labelOffset.
  25466. isNameLocationCenter(nameLocation) ? opt.labelOffset + nameDirection * gap : 0
  25467. ];
  25468. var labelLayout;
  25469. var nameRotation = axisModel.get('nameRotate');
  25470. if (nameRotation != null) {
  25471. nameRotation = nameRotation * PI$2 / 180; // To radian.
  25472. }
  25473. var axisNameAvailableWidth;
  25474. if (isNameLocationCenter(nameLocation)) {
  25475. labelLayout = innerTextLayout(
  25476. opt.rotation,
  25477. nameRotation != null ? nameRotation : opt.rotation, // Adapt to axis.
  25478. nameDirection
  25479. );
  25480. }
  25481. else {
  25482. labelLayout = endTextLayout(
  25483. opt, nameLocation, nameRotation || 0, extent
  25484. );
  25485. axisNameAvailableWidth = opt.axisNameAvailableWidth;
  25486. if (axisNameAvailableWidth != null) {
  25487. axisNameAvailableWidth = Math.abs(
  25488. axisNameAvailableWidth / Math.sin(labelLayout.rotation)
  25489. );
  25490. !isFinite(axisNameAvailableWidth) && (axisNameAvailableWidth = null);
  25491. }
  25492. }
  25493. var textFont = textStyleModel.getFont();
  25494. var truncateOpt = axisModel.get('nameTruncate', true) || {};
  25495. var ellipsis = truncateOpt.ellipsis;
  25496. var maxWidth = retrieve(
  25497. opt.nameTruncateMaxWidth, truncateOpt.maxWidth, axisNameAvailableWidth
  25498. );
  25499. // FIXME
  25500. // truncate rich text? (consider performance)
  25501. var truncatedText = (ellipsis != null && maxWidth != null)
  25502. ? truncateText$1(
  25503. name, maxWidth, textFont, ellipsis,
  25504. {minChar: 2, placeholder: truncateOpt.placeholder}
  25505. )
  25506. : name;
  25507. var tooltipOpt = axisModel.get('tooltip', true);
  25508. var mainType = axisModel.mainType;
  25509. var formatterParams = {
  25510. componentType: mainType,
  25511. name: name,
  25512. $vars: ['name']
  25513. };
  25514. formatterParams[mainType + 'Index'] = axisModel.componentIndex;
  25515. var textEl = new Text({
  25516. // Id for animation
  25517. anid: 'name',
  25518. __fullText: name,
  25519. __truncatedText: truncatedText,
  25520. position: pos,
  25521. rotation: labelLayout.rotation,
  25522. silent: isSilent(axisModel),
  25523. z2: 1,
  25524. tooltip: (tooltipOpt && tooltipOpt.show)
  25525. ? extend({
  25526. content: name,
  25527. formatter: function () {
  25528. return name;
  25529. },
  25530. formatterParams: formatterParams
  25531. }, tooltipOpt)
  25532. : null
  25533. });
  25534. setTextStyle(textEl.style, textStyleModel, {
  25535. text: truncatedText,
  25536. textFont: textFont,
  25537. textFill: textStyleModel.getTextColor()
  25538. || axisModel.get('axisLine.lineStyle.color'),
  25539. textAlign: labelLayout.textAlign,
  25540. textVerticalAlign: labelLayout.textVerticalAlign
  25541. });
  25542. if (axisModel.get('triggerEvent')) {
  25543. textEl.eventData = makeAxisEventDataBase(axisModel);
  25544. textEl.eventData.targetType = 'axisName';
  25545. textEl.eventData.name = name;
  25546. }
  25547. // FIXME
  25548. this._dumbGroup.add(textEl);
  25549. textEl.updateTransform();
  25550. this.group.add(textEl);
  25551. textEl.decomposeTransform();
  25552. }
  25553. };
  25554. /**
  25555. * @public
  25556. * @static
  25557. * @param {Object} opt
  25558. * @param {number} axisRotation in radian
  25559. * @param {number} textRotation in radian
  25560. * @param {number} direction
  25561. * @return {Object} {
  25562. * rotation, // according to axis
  25563. * textAlign,
  25564. * textVerticalAlign
  25565. * }
  25566. */
  25567. var innerTextLayout = AxisBuilder.innerTextLayout = function (axisRotation, textRotation, direction) {
  25568. var rotationDiff = remRadian(textRotation - axisRotation);
  25569. var textAlign;
  25570. var textVerticalAlign;
  25571. if (isRadianAroundZero(rotationDiff)) { // Label is parallel with axis line.
  25572. textVerticalAlign = direction > 0 ? 'top' : 'bottom';
  25573. textAlign = 'center';
  25574. }
  25575. else if (isRadianAroundZero(rotationDiff - PI$2)) { // Label is inverse parallel with axis line.
  25576. textVerticalAlign = direction > 0 ? 'bottom' : 'top';
  25577. textAlign = 'center';
  25578. }
  25579. else {
  25580. textVerticalAlign = 'middle';
  25581. if (rotationDiff > 0 && rotationDiff < PI$2) {
  25582. textAlign = direction > 0 ? 'right' : 'left';
  25583. }
  25584. else {
  25585. textAlign = direction > 0 ? 'left' : 'right';
  25586. }
  25587. }
  25588. return {
  25589. rotation: rotationDiff,
  25590. textAlign: textAlign,
  25591. textVerticalAlign: textVerticalAlign
  25592. };
  25593. };
  25594. function endTextLayout(opt, textPosition, textRotate, extent) {
  25595. var rotationDiff = remRadian(textRotate - opt.rotation);
  25596. var textAlign;
  25597. var textVerticalAlign;
  25598. var inverse = extent[0] > extent[1];
  25599. var onLeft = (textPosition === 'start' && !inverse)
  25600. || (textPosition !== 'start' && inverse);
  25601. if (isRadianAroundZero(rotationDiff - PI$2 / 2)) {
  25602. textVerticalAlign = onLeft ? 'bottom' : 'top';
  25603. textAlign = 'center';
  25604. }
  25605. else if (isRadianAroundZero(rotationDiff - PI$2 * 1.5)) {
  25606. textVerticalAlign = onLeft ? 'top' : 'bottom';
  25607. textAlign = 'center';
  25608. }
  25609. else {
  25610. textVerticalAlign = 'middle';
  25611. if (rotationDiff < PI$2 * 1.5 && rotationDiff > PI$2 / 2) {
  25612. textAlign = onLeft ? 'left' : 'right';
  25613. }
  25614. else {
  25615. textAlign = onLeft ? 'right' : 'left';
  25616. }
  25617. }
  25618. return {
  25619. rotation: rotationDiff,
  25620. textAlign: textAlign,
  25621. textVerticalAlign: textVerticalAlign
  25622. };
  25623. }
  25624. function isSilent(axisModel) {
  25625. var tooltipOpt = axisModel.get('tooltip');
  25626. return axisModel.get('silent')
  25627. // Consider mouse cursor, add these restrictions.
  25628. || !(
  25629. axisModel.get('triggerEvent') || (tooltipOpt && tooltipOpt.show)
  25630. );
  25631. }
  25632. function fixMinMaxLabelShow(axisModel, labelEls, tickEls) {
  25633. // If min or max are user set, we need to check
  25634. // If the tick on min(max) are overlap on their neighbour tick
  25635. // If they are overlapped, we need to hide the min(max) tick label
  25636. var showMinLabel = axisModel.get('axisLabel.showMinLabel');
  25637. var showMaxLabel = axisModel.get('axisLabel.showMaxLabel');
  25638. // FIXME
  25639. // Have not consider onBand yet, where tick els is more than label els.
  25640. labelEls = labelEls || [];
  25641. tickEls = tickEls || [];
  25642. var firstLabel = labelEls[0];
  25643. var nextLabel = labelEls[1];
  25644. var lastLabel = labelEls[labelEls.length - 1];
  25645. var prevLabel = labelEls[labelEls.length - 2];
  25646. var firstTick = tickEls[0];
  25647. var nextTick = tickEls[1];
  25648. var lastTick = tickEls[tickEls.length - 1];
  25649. var prevTick = tickEls[tickEls.length - 2];
  25650. if (showMinLabel === false) {
  25651. ignoreEl(firstLabel);
  25652. ignoreEl(firstTick);
  25653. }
  25654. else if (isTwoLabelOverlapped(firstLabel, nextLabel)) {
  25655. if (showMinLabel) {
  25656. ignoreEl(nextLabel);
  25657. ignoreEl(nextTick);
  25658. }
  25659. else {
  25660. ignoreEl(firstLabel);
  25661. ignoreEl(firstTick);
  25662. }
  25663. }
  25664. if (showMaxLabel === false) {
  25665. ignoreEl(lastLabel);
  25666. ignoreEl(lastTick);
  25667. }
  25668. else if (isTwoLabelOverlapped(prevLabel, lastLabel)) {
  25669. if (showMaxLabel) {
  25670. ignoreEl(prevLabel);
  25671. ignoreEl(prevTick);
  25672. }
  25673. else {
  25674. ignoreEl(lastLabel);
  25675. ignoreEl(lastTick);
  25676. }
  25677. }
  25678. }
  25679. function ignoreEl(el) {
  25680. el && (el.ignore = true);
  25681. }
  25682. function isTwoLabelOverlapped(current, next, labelLayout) {
  25683. // current and next has the same rotation.
  25684. var firstRect = current && current.getBoundingRect().clone();
  25685. var nextRect = next && next.getBoundingRect().clone();
  25686. if (!firstRect || !nextRect) {
  25687. return;
  25688. }
  25689. // When checking intersect of two rotated labels, we use mRotationBack
  25690. // to avoid that boundingRect is enlarge when using `boundingRect.applyTransform`.
  25691. var mRotationBack = identity([]);
  25692. rotate(mRotationBack, mRotationBack, -current.rotation);
  25693. firstRect.applyTransform(mul$1([], mRotationBack, current.getLocalTransform()));
  25694. nextRect.applyTransform(mul$1([], mRotationBack, next.getLocalTransform()));
  25695. return firstRect.intersect(nextRect);
  25696. }
  25697. function isNameLocationCenter(nameLocation) {
  25698. return nameLocation === 'middle' || nameLocation === 'center';
  25699. }
  25700. /**
  25701. * @static
  25702. */
  25703. var ifIgnoreOnTick$1 = AxisBuilder.ifIgnoreOnTick = function (
  25704. axis,
  25705. i,
  25706. interval,
  25707. ticksCnt,
  25708. showMinLabel,
  25709. showMaxLabel
  25710. ) {
  25711. if (i === 0 && showMinLabel || i === ticksCnt - 1 && showMaxLabel) {
  25712. return false;
  25713. }
  25714. // FIXME
  25715. // Have not consider label overlap (if label is too long) yet.
  25716. var rawTick;
  25717. var scale$$1 = axis.scale;
  25718. return scale$$1.type === 'ordinal'
  25719. && (
  25720. typeof interval === 'function'
  25721. ? (
  25722. rawTick = scale$$1.getTicks()[i],
  25723. !interval(rawTick, scale$$1.getLabel(rawTick))
  25724. )
  25725. : i % (interval + 1)
  25726. );
  25727. };
  25728. /**
  25729. * @static
  25730. */
  25731. var getInterval$1 = AxisBuilder.getInterval = function (model, labelInterval) {
  25732. var interval = model.get('interval');
  25733. if (interval == null || interval == 'auto') {
  25734. interval = labelInterval;
  25735. }
  25736. return interval;
  25737. };
  25738. function buildAxisTick(axisBuilder, axisModel, opt) {
  25739. var axis = axisModel.axis;
  25740. if (!axisModel.get('axisTick.show') || axis.scale.isBlank()) {
  25741. return;
  25742. }
  25743. var tickModel = axisModel.getModel('axisTick');
  25744. var lineStyleModel = tickModel.getModel('lineStyle');
  25745. var tickLen = tickModel.get('length');
  25746. var tickInterval = getInterval$1(tickModel, opt.labelInterval);
  25747. var ticksCoords = axis.getTicksCoords(tickModel.get('alignWithLabel'));
  25748. // FIXME
  25749. // Corresponds to ticksCoords ?
  25750. var ticks = axis.scale.getTicks();
  25751. var showMinLabel = axisModel.get('axisLabel.showMinLabel');
  25752. var showMaxLabel = axisModel.get('axisLabel.showMaxLabel');
  25753. var pt1 = [];
  25754. var pt2 = [];
  25755. var matrix = axisBuilder._transform;
  25756. var tickEls = [];
  25757. var ticksCnt = ticksCoords.length;
  25758. for (var i = 0; i < ticksCnt; i++) {
  25759. // Only ordinal scale support tick interval
  25760. if (ifIgnoreOnTick$1(
  25761. axis, i, tickInterval, ticksCnt,
  25762. showMinLabel, showMaxLabel
  25763. )) {
  25764. continue;
  25765. }
  25766. var tickCoord = ticksCoords[i];
  25767. pt1[0] = tickCoord;
  25768. pt1[1] = 0;
  25769. pt2[0] = tickCoord;
  25770. pt2[1] = opt.tickDirection * tickLen;
  25771. if (matrix) {
  25772. applyTransform(pt1, pt1, matrix);
  25773. applyTransform(pt2, pt2, matrix);
  25774. }
  25775. // Tick line, Not use group transform to have better line draw
  25776. var tickEl = new Line(subPixelOptimizeLine({
  25777. // Id for animation
  25778. anid: 'tick_' + ticks[i],
  25779. shape: {
  25780. x1: pt1[0],
  25781. y1: pt1[1],
  25782. x2: pt2[0],
  25783. y2: pt2[1]
  25784. },
  25785. style: defaults(
  25786. lineStyleModel.getLineStyle(),
  25787. {
  25788. stroke: axisModel.get('axisLine.lineStyle.color')
  25789. }
  25790. ),
  25791. z2: 2,
  25792. silent: true
  25793. }));
  25794. axisBuilder.group.add(tickEl);
  25795. tickEls.push(tickEl);
  25796. }
  25797. return tickEls;
  25798. }
  25799. function buildAxisLabel(axisBuilder, axisModel, opt) {
  25800. var axis = axisModel.axis;
  25801. var show = retrieve(opt.axisLabelShow, axisModel.get('axisLabel.show'));
  25802. if (!show || axis.scale.isBlank()) {
  25803. return;
  25804. }
  25805. var labelModel = axisModel.getModel('axisLabel');
  25806. var labelMargin = labelModel.get('margin');
  25807. var ticks = axis.scale.getTicks();
  25808. var labels = axisModel.getFormattedLabels();
  25809. // Special label rotate.
  25810. var labelRotation = (
  25811. retrieve(opt.labelRotate, labelModel.get('rotate')) || 0
  25812. ) * PI$2 / 180;
  25813. var labelLayout = innerTextLayout(opt.rotation, labelRotation, opt.labelDirection);
  25814. var categoryData = axisModel.get('data');
  25815. var labelEls = [];
  25816. var silent = isSilent(axisModel);
  25817. var triggerEvent = axisModel.get('triggerEvent');
  25818. var showMinLabel = axisModel.get('axisLabel.showMinLabel');
  25819. var showMaxLabel = axisModel.get('axisLabel.showMaxLabel');
  25820. each$1(ticks, function (tickVal, index) {
  25821. if (ifIgnoreOnTick$1(
  25822. axis, index, opt.labelInterval, ticks.length,
  25823. showMinLabel, showMaxLabel
  25824. )) {
  25825. return;
  25826. }
  25827. var itemLabelModel = labelModel;
  25828. if (categoryData && categoryData[tickVal] && categoryData[tickVal].textStyle) {
  25829. itemLabelModel = new Model(
  25830. categoryData[tickVal].textStyle, labelModel, axisModel.ecModel
  25831. );
  25832. }
  25833. var textColor = itemLabelModel.getTextColor()
  25834. || axisModel.get('axisLine.lineStyle.color');
  25835. var tickCoord = axis.dataToCoord(tickVal);
  25836. var pos = [
  25837. tickCoord,
  25838. opt.labelOffset + opt.labelDirection * labelMargin
  25839. ];
  25840. var labelStr = axis.scale.getLabel(tickVal);
  25841. var textEl = new Text({
  25842. // Id for animation
  25843. anid: 'label_' + tickVal,
  25844. position: pos,
  25845. rotation: labelLayout.rotation,
  25846. silent: silent,
  25847. z2: 10
  25848. });
  25849. setTextStyle(textEl.style, itemLabelModel, {
  25850. text: labels[index],
  25851. textAlign: itemLabelModel.getShallow('align', true)
  25852. || labelLayout.textAlign,
  25853. textVerticalAlign: itemLabelModel.getShallow('verticalAlign', true)
  25854. || itemLabelModel.getShallow('baseline', true)
  25855. || labelLayout.textVerticalAlign,
  25856. textFill: typeof textColor === 'function'
  25857. ? textColor(
  25858. // (1) In category axis with data zoom, tick is not the original
  25859. // index of axis.data. So tick should not be exposed to user
  25860. // in category axis.
  25861. // (2) Compatible with previous version, which always returns labelStr.
  25862. // But in interval scale labelStr is like '223,445', which maked
  25863. // user repalce ','. So we modify it to return original val but remain
  25864. // it as 'string' to avoid error in replacing.
  25865. axis.type === 'category' ? labelStr : axis.type === 'value' ? tickVal + '' : tickVal,
  25866. index
  25867. )
  25868. : textColor
  25869. });
  25870. // Pack data for mouse event
  25871. if (triggerEvent) {
  25872. textEl.eventData = makeAxisEventDataBase(axisModel);
  25873. textEl.eventData.targetType = 'axisLabel';
  25874. textEl.eventData.value = labelStr;
  25875. }
  25876. // FIXME
  25877. axisBuilder._dumbGroup.add(textEl);
  25878. textEl.updateTransform();
  25879. labelEls.push(textEl);
  25880. axisBuilder.group.add(textEl);
  25881. textEl.decomposeTransform();
  25882. });
  25883. return labelEls;
  25884. }
  25885. // Build axisPointerModel, mergin tooltip.axisPointer model for each axis.
  25886. // allAxesInfo should be updated when setOption performed.
  25887. function fixValue(axisModel) {
  25888. var axisInfo = getAxisInfo(axisModel);
  25889. if (!axisInfo) {
  25890. return;
  25891. }
  25892. var axisPointerModel = axisInfo.axisPointerModel;
  25893. var scale = axisInfo.axis.scale;
  25894. var option = axisPointerModel.option;
  25895. var status = axisPointerModel.get('status');
  25896. var value = axisPointerModel.get('value');
  25897. // Parse init value for category and time axis.
  25898. if (value != null) {
  25899. value = scale.parse(value);
  25900. }
  25901. var useHandle = isHandleTrigger(axisPointerModel);
  25902. // If `handle` used, `axisPointer` will always be displayed, so value
  25903. // and status should be initialized.
  25904. if (status == null) {
  25905. option.status = useHandle ? 'show' : 'hide';
  25906. }
  25907. var extent = scale.getExtent().slice();
  25908. extent[0] > extent[1] && extent.reverse();
  25909. if (// Pick a value on axis when initializing.
  25910. value == null
  25911. // If both `handle` and `dataZoom` are used, value may be out of axis extent,
  25912. // where we should re-pick a value to keep `handle` displaying normally.
  25913. || value > extent[1]
  25914. ) {
  25915. // Make handle displayed on the end of the axis when init, which looks better.
  25916. value = extent[1];
  25917. }
  25918. if (value < extent[0]) {
  25919. value = extent[0];
  25920. }
  25921. option.value = value;
  25922. if (useHandle) {
  25923. option.status = axisInfo.axis.scale.isBlank() ? 'hide' : 'show';
  25924. }
  25925. }
  25926. function getAxisInfo(axisModel) {
  25927. var coordSysAxesInfo = (axisModel.ecModel.getComponent('axisPointer') || {}).coordSysAxesInfo;
  25928. return coordSysAxesInfo && coordSysAxesInfo.axesInfo[makeKey(axisModel)];
  25929. }
  25930. function getAxisPointerModel(axisModel) {
  25931. var axisInfo = getAxisInfo(axisModel);
  25932. return axisInfo && axisInfo.axisPointerModel;
  25933. }
  25934. function isHandleTrigger(axisPointerModel) {
  25935. return !!axisPointerModel.get('handle.show');
  25936. }
  25937. /**
  25938. * @param {module:echarts/model/Model} model
  25939. * @return {string} unique key
  25940. */
  25941. function makeKey(model) {
  25942. return model.type + '||' + model.id;
  25943. }
  25944. /**
  25945. * Base class of AxisView.
  25946. */
  25947. var AxisView = extendComponentView({
  25948. type: 'axis',
  25949. /**
  25950. * @private
  25951. */
  25952. _axisPointer: null,
  25953. /**
  25954. * @protected
  25955. * @type {string}
  25956. */
  25957. axisPointerClass: null,
  25958. /**
  25959. * @override
  25960. */
  25961. render: function (axisModel, ecModel, api, payload) {
  25962. // FIXME
  25963. // This process should proformed after coordinate systems updated
  25964. // (axis scale updated), and should be performed each time update.
  25965. // So put it here temporarily, although it is not appropriate to
  25966. // put a model-writing procedure in `view`.
  25967. this.axisPointerClass && fixValue(axisModel);
  25968. AxisView.superApply(this, 'render', arguments);
  25969. updateAxisPointer(this, axisModel, ecModel, api, payload, true);
  25970. },
  25971. /**
  25972. * Action handler.
  25973. * @public
  25974. * @param {module:echarts/coord/cartesian/AxisModel} axisModel
  25975. * @param {module:echarts/model/Global} ecModel
  25976. * @param {module:echarts/ExtensionAPI} api
  25977. * @param {Object} payload
  25978. */
  25979. updateAxisPointer: function (axisModel, ecModel, api, payload, force) {
  25980. updateAxisPointer(this, axisModel, ecModel, api, payload, false);
  25981. },
  25982. /**
  25983. * @override
  25984. */
  25985. remove: function (ecModel, api) {
  25986. var axisPointer = this._axisPointer;
  25987. axisPointer && axisPointer.remove(api);
  25988. AxisView.superApply(this, 'remove', arguments);
  25989. },
  25990. /**
  25991. * @override
  25992. */
  25993. dispose: function (ecModel, api) {
  25994. disposeAxisPointer(this, api);
  25995. AxisView.superApply(this, 'dispose', arguments);
  25996. }
  25997. });
  25998. function updateAxisPointer(axisView, axisModel, ecModel, api, payload, forceRender) {
  25999. var Clazz = AxisView.getAxisPointerClass(axisView.axisPointerClass);
  26000. if (!Clazz) {
  26001. return;
  26002. }
  26003. var axisPointerModel = getAxisPointerModel(axisModel);
  26004. axisPointerModel
  26005. ? (axisView._axisPointer || (axisView._axisPointer = new Clazz()))
  26006. .render(axisModel, axisPointerModel, api, forceRender)
  26007. : disposeAxisPointer(axisView, api);
  26008. }
  26009. function disposeAxisPointer(axisView, ecModel, api) {
  26010. var axisPointer = axisView._axisPointer;
  26011. axisPointer && axisPointer.dispose(ecModel, api);
  26012. axisView._axisPointer = null;
  26013. }
  26014. var axisPointerClazz = [];
  26015. AxisView.registerAxisPointerClass = function (type, clazz) {
  26016. if (__DEV__) {
  26017. if (axisPointerClazz[type]) {
  26018. throw new Error('axisPointer ' + type + ' exists');
  26019. }
  26020. }
  26021. axisPointerClazz[type] = clazz;
  26022. };
  26023. AxisView.getAxisPointerClass = function (type) {
  26024. return type && axisPointerClazz[type];
  26025. };
  26026. /**
  26027. * @param {Object} opt {labelInside}
  26028. * @return {Object} {
  26029. * position, rotation, labelDirection, labelOffset,
  26030. * tickDirection, labelRotate, labelInterval, z2
  26031. * }
  26032. */
  26033. function layout(gridModel, axisModel, opt) {
  26034. opt = opt || {};
  26035. var grid = gridModel.coordinateSystem;
  26036. var axis = axisModel.axis;
  26037. var layout = {};
  26038. var rawAxisPosition = axis.position;
  26039. var axisPosition = axis.onZero ? 'onZero' : rawAxisPosition;
  26040. var axisDim = axis.dim;
  26041. var rect = grid.getRect();
  26042. var rectBound = [rect.x, rect.x + rect.width, rect.y, rect.y + rect.height];
  26043. var idx = {left: 0, right: 1, top: 0, bottom: 1, onZero: 2};
  26044. var axisOffset = axisModel.get('offset') || 0;
  26045. var posBound = axisDim === 'x'
  26046. ? [rectBound[2] - axisOffset, rectBound[3] + axisOffset]
  26047. : [rectBound[0] - axisOffset, rectBound[1] + axisOffset];
  26048. if (axis.onZero) {
  26049. var otherAxis = grid.getAxis(axisDim === 'x' ? 'y' : 'x', axis.onZeroAxisIndex);
  26050. var onZeroCoord = otherAxis.toGlobalCoord(otherAxis.dataToCoord(0));
  26051. posBound[idx['onZero']] = Math.max(Math.min(onZeroCoord, posBound[1]), posBound[0]);
  26052. }
  26053. // Axis position
  26054. layout.position = [
  26055. axisDim === 'y' ? posBound[idx[axisPosition]] : rectBound[0],
  26056. axisDim === 'x' ? posBound[idx[axisPosition]] : rectBound[3]
  26057. ];
  26058. // Axis rotation
  26059. layout.rotation = Math.PI / 2 * (axisDim === 'x' ? 0 : 1);
  26060. // Tick and label direction, x y is axisDim
  26061. var dirMap = {top: -1, bottom: 1, left: -1, right: 1};
  26062. layout.labelDirection = layout.tickDirection = layout.nameDirection = dirMap[rawAxisPosition];
  26063. layout.labelOffset = axis.onZero ? posBound[idx[rawAxisPosition]] - posBound[idx['onZero']] : 0;
  26064. if (axisModel.get('axisTick.inside')) {
  26065. layout.tickDirection = -layout.tickDirection;
  26066. }
  26067. if (retrieve(opt.labelInside, axisModel.get('axisLabel.inside'))) {
  26068. layout.labelDirection = -layout.labelDirection;
  26069. }
  26070. // Special label rotation
  26071. var labelRotate = axisModel.get('axisLabel.rotate');
  26072. layout.labelRotate = axisPosition === 'top' ? -labelRotate : labelRotate;
  26073. // label interval when auto mode.
  26074. layout.labelInterval = axis.getLabelInterval();
  26075. // Over splitLine and splitArea
  26076. layout.z2 = 1;
  26077. return layout;
  26078. }
  26079. var ifIgnoreOnTick = AxisBuilder.ifIgnoreOnTick;
  26080. var getInterval = AxisBuilder.getInterval;
  26081. var axisBuilderAttrs = [
  26082. 'axisLine', 'axisTickLabel', 'axisName'
  26083. ];
  26084. var selfBuilderAttrs = [
  26085. 'splitArea', 'splitLine'
  26086. ];
  26087. // function getAlignWithLabel(model, axisModel) {
  26088. // var alignWithLabel = model.get('alignWithLabel');
  26089. // if (alignWithLabel === 'auto') {
  26090. // alignWithLabel = axisModel.get('axisTick.alignWithLabel');
  26091. // }
  26092. // return alignWithLabel;
  26093. // }
  26094. var CartesianAxisView = AxisView.extend({
  26095. type: 'cartesianAxis',
  26096. axisPointerClass: 'CartesianAxisPointer',
  26097. /**
  26098. * @override
  26099. */
  26100. render: function (axisModel, ecModel, api, payload) {
  26101. this.group.removeAll();
  26102. var oldAxisGroup = this._axisGroup;
  26103. this._axisGroup = new Group();
  26104. this.group.add(this._axisGroup);
  26105. if (!axisModel.get('show')) {
  26106. return;
  26107. }
  26108. var gridModel = axisModel.getCoordSysModel();
  26109. var layout$$1 = layout(gridModel, axisModel);
  26110. var axisBuilder = new AxisBuilder(axisModel, layout$$1);
  26111. each$1(axisBuilderAttrs, axisBuilder.add, axisBuilder);
  26112. this._axisGroup.add(axisBuilder.getGroup());
  26113. each$1(selfBuilderAttrs, function (name) {
  26114. if (axisModel.get(name + '.show')) {
  26115. this['_' + name](axisModel, gridModel, layout$$1.labelInterval);
  26116. }
  26117. }, this);
  26118. groupTransition(oldAxisGroup, this._axisGroup, axisModel);
  26119. CartesianAxisView.superCall(this, 'render', axisModel, ecModel, api, payload);
  26120. },
  26121. /**
  26122. * @param {module:echarts/coord/cartesian/AxisModel} axisModel
  26123. * @param {module:echarts/coord/cartesian/GridModel} gridModel
  26124. * @param {number|Function} labelInterval
  26125. * @private
  26126. */
  26127. _splitLine: function (axisModel, gridModel, labelInterval) {
  26128. var axis = axisModel.axis;
  26129. if (axis.scale.isBlank()) {
  26130. return;
  26131. }
  26132. var splitLineModel = axisModel.getModel('splitLine');
  26133. var lineStyleModel = splitLineModel.getModel('lineStyle');
  26134. var lineColors = lineStyleModel.get('color');
  26135. var lineInterval = getInterval(splitLineModel, labelInterval);
  26136. lineColors = isArray(lineColors) ? lineColors : [lineColors];
  26137. var gridRect = gridModel.coordinateSystem.getRect();
  26138. var isHorizontal = axis.isHorizontal();
  26139. var lineCount = 0;
  26140. var ticksCoords = axis.getTicksCoords(
  26141. // splitLineModel.get('alignWithLabel')
  26142. );
  26143. var ticks = axis.scale.getTicks();
  26144. var showMinLabel = axisModel.get('axisLabel.showMinLabel');
  26145. var showMaxLabel = axisModel.get('axisLabel.showMaxLabel');
  26146. var p1 = [];
  26147. var p2 = [];
  26148. // Simple optimization
  26149. // Batching the lines if color are the same
  26150. var lineStyle = lineStyleModel.getLineStyle();
  26151. for (var i = 0; i < ticksCoords.length; i++) {
  26152. if (ifIgnoreOnTick(
  26153. axis, i, lineInterval, ticksCoords.length,
  26154. showMinLabel, showMaxLabel
  26155. )) {
  26156. continue;
  26157. }
  26158. var tickCoord = axis.toGlobalCoord(ticksCoords[i]);
  26159. if (isHorizontal) {
  26160. p1[0] = tickCoord;
  26161. p1[1] = gridRect.y;
  26162. p2[0] = tickCoord;
  26163. p2[1] = gridRect.y + gridRect.height;
  26164. }
  26165. else {
  26166. p1[0] = gridRect.x;
  26167. p1[1] = tickCoord;
  26168. p2[0] = gridRect.x + gridRect.width;
  26169. p2[1] = tickCoord;
  26170. }
  26171. var colorIndex = (lineCount++) % lineColors.length;
  26172. this._axisGroup.add(new Line(subPixelOptimizeLine({
  26173. anid: 'line_' + ticks[i],
  26174. shape: {
  26175. x1: p1[0],
  26176. y1: p1[1],
  26177. x2: p2[0],
  26178. y2: p2[1]
  26179. },
  26180. style: defaults({
  26181. stroke: lineColors[colorIndex]
  26182. }, lineStyle),
  26183. silent: true
  26184. })));
  26185. }
  26186. },
  26187. /**
  26188. * @param {module:echarts/coord/cartesian/AxisModel} axisModel
  26189. * @param {module:echarts/coord/cartesian/GridModel} gridModel
  26190. * @param {number|Function} labelInterval
  26191. * @private
  26192. */
  26193. _splitArea: function (axisModel, gridModel, labelInterval) {
  26194. var axis = axisModel.axis;
  26195. if (axis.scale.isBlank()) {
  26196. return;
  26197. }
  26198. var splitAreaModel = axisModel.getModel('splitArea');
  26199. var areaStyleModel = splitAreaModel.getModel('areaStyle');
  26200. var areaColors = areaStyleModel.get('color');
  26201. var gridRect = gridModel.coordinateSystem.getRect();
  26202. var ticksCoords = axis.getTicksCoords(
  26203. // splitAreaModel.get('alignWithLabel')
  26204. );
  26205. var ticks = axis.scale.getTicks();
  26206. var prevX = axis.toGlobalCoord(ticksCoords[0]);
  26207. var prevY = axis.toGlobalCoord(ticksCoords[0]);
  26208. var count = 0;
  26209. var areaInterval = getInterval(splitAreaModel, labelInterval);
  26210. var areaStyle = areaStyleModel.getAreaStyle();
  26211. areaColors = isArray(areaColors) ? areaColors : [areaColors];
  26212. var showMinLabel = axisModel.get('axisLabel.showMinLabel');
  26213. var showMaxLabel = axisModel.get('axisLabel.showMaxLabel');
  26214. for (var i = 1; i < ticksCoords.length; i++) {
  26215. if (ifIgnoreOnTick(
  26216. axis, i, areaInterval, ticksCoords.length,
  26217. showMinLabel, showMaxLabel
  26218. )) {
  26219. continue;
  26220. }
  26221. var tickCoord = axis.toGlobalCoord(ticksCoords[i]);
  26222. var x;
  26223. var y;
  26224. var width;
  26225. var height;
  26226. if (axis.isHorizontal()) {
  26227. x = prevX;
  26228. y = gridRect.y;
  26229. width = tickCoord - x;
  26230. height = gridRect.height;
  26231. }
  26232. else {
  26233. x = gridRect.x;
  26234. y = prevY;
  26235. width = gridRect.width;
  26236. height = tickCoord - y;
  26237. }
  26238. var colorIndex = (count++) % areaColors.length;
  26239. this._axisGroup.add(new Rect({
  26240. anid: 'area_' + ticks[i],
  26241. shape: {
  26242. x: x,
  26243. y: y,
  26244. width: width,
  26245. height: height
  26246. },
  26247. style: defaults({
  26248. fill: areaColors[colorIndex]
  26249. }, areaStyle),
  26250. silent: true
  26251. }));
  26252. prevX = x + width;
  26253. prevY = y + height;
  26254. }
  26255. }
  26256. });
  26257. CartesianAxisView.extend({
  26258. type: 'xAxis'
  26259. });
  26260. CartesianAxisView.extend({
  26261. type: 'yAxis'
  26262. });
  26263. // Grid view
  26264. extendComponentView({
  26265. type: 'grid',
  26266. render: function (gridModel, ecModel) {
  26267. this.group.removeAll();
  26268. if (gridModel.get('show')) {
  26269. this.group.add(new Rect({
  26270. shape: gridModel.coordinateSystem.getRect(),
  26271. style: defaults({
  26272. fill: gridModel.get('backgroundColor')
  26273. }, gridModel.getItemStyle()),
  26274. silent: true,
  26275. z2: -1
  26276. }));
  26277. }
  26278. }
  26279. });
  26280. registerPreprocessor(function (option) {
  26281. // Only create grid when need
  26282. if (option.xAxis && option.yAxis && !option.grid) {
  26283. option.grid = {};
  26284. }
  26285. });
  26286. // In case developer forget to include grid component
  26287. registerVisual(curry(
  26288. visualSymbol, 'line', 'circle', 'line'
  26289. ));
  26290. registerLayout(curry(
  26291. layoutPoints, 'line'
  26292. ));
  26293. // Down sample after filter
  26294. registerProcessor(PRIORITY.PROCESSOR.STATISTIC, curry(
  26295. dataSample, 'line'
  26296. ));
  26297. var STACK_PREFIX = '__ec_stack_';
  26298. function getSeriesStackId(seriesModel) {
  26299. return seriesModel.get('stack') || STACK_PREFIX + seriesModel.seriesIndex;
  26300. }
  26301. function getAxisKey(axis) {
  26302. return axis.dim + axis.index;
  26303. }
  26304. /**
  26305. * @param {Object} opt
  26306. * @param {module:echarts/coord/Axis} opt.axis Only support category axis currently.
  26307. * @param {number} opt.count Positive interger.
  26308. * @param {number} [opt.barWidth]
  26309. * @param {number} [opt.barMaxWidth]
  26310. * @param {number} [opt.barGap]
  26311. * @param {number} [opt.barCategoryGap]
  26312. * @return {Object} {width, offset, offsetCenter} If axis.type is not 'category', return undefined.
  26313. */
  26314. function getLayoutOnAxis(opt, api) {
  26315. var params = [];
  26316. var baseAxis = opt.axis;
  26317. var axisKey = 'axis0';
  26318. if (baseAxis.type !== 'category') {
  26319. return;
  26320. }
  26321. var bandWidth = baseAxis.getBandWidth();
  26322. for (var i = 0; i < opt.count || 0; i++) {
  26323. params.push(defaults({
  26324. bandWidth: bandWidth,
  26325. axisKey: axisKey,
  26326. stackId: STACK_PREFIX + i
  26327. }, opt));
  26328. }
  26329. var widthAndOffsets = doCalBarWidthAndOffset(params, api);
  26330. var result = [];
  26331. for (var i = 0; i < opt.count; i++) {
  26332. var item = widthAndOffsets[axisKey][STACK_PREFIX + i];
  26333. item.offsetCenter = item.offset + item.width / 2;
  26334. result.push(item);
  26335. }
  26336. return result;
  26337. }
  26338. function calBarWidthAndOffset(barSeries, api) {
  26339. var seriesInfoList = map(barSeries, function (seriesModel) {
  26340. var data = seriesModel.getData();
  26341. var cartesian = seriesModel.coordinateSystem;
  26342. var baseAxis = cartesian.getBaseAxis();
  26343. var axisExtent = baseAxis.getExtent();
  26344. var bandWidth = baseAxis.type === 'category'
  26345. ? baseAxis.getBandWidth()
  26346. : (Math.abs(axisExtent[1] - axisExtent[0]) / data.count());
  26347. var barWidth = parsePercent$1(
  26348. seriesModel.get('barWidth'), bandWidth
  26349. );
  26350. var barMaxWidth = parsePercent$1(
  26351. seriesModel.get('barMaxWidth'), bandWidth
  26352. );
  26353. var barGap = seriesModel.get('barGap');
  26354. var barCategoryGap = seriesModel.get('barCategoryGap');
  26355. return {
  26356. bandWidth: bandWidth,
  26357. barWidth: barWidth,
  26358. barMaxWidth: barMaxWidth,
  26359. barGap: barGap,
  26360. barCategoryGap: barCategoryGap,
  26361. axisKey: getAxisKey(baseAxis),
  26362. stackId: getSeriesStackId(seriesModel)
  26363. };
  26364. });
  26365. return doCalBarWidthAndOffset(seriesInfoList, api);
  26366. }
  26367. function doCalBarWidthAndOffset(seriesInfoList, api) {
  26368. // Columns info on each category axis. Key is cartesian name
  26369. var columnsMap = {};
  26370. each$1(seriesInfoList, function (seriesInfo, idx) {
  26371. var axisKey = seriesInfo.axisKey;
  26372. var bandWidth = seriesInfo.bandWidth;
  26373. var columnsOnAxis = columnsMap[axisKey] || {
  26374. bandWidth: bandWidth,
  26375. remainedWidth: bandWidth,
  26376. autoWidthCount: 0,
  26377. categoryGap: '20%',
  26378. gap: '30%',
  26379. stacks: {}
  26380. };
  26381. var stacks = columnsOnAxis.stacks;
  26382. columnsMap[axisKey] = columnsOnAxis;
  26383. var stackId = seriesInfo.stackId;
  26384. if (!stacks[stackId]) {
  26385. columnsOnAxis.autoWidthCount++;
  26386. }
  26387. stacks[stackId] = stacks[stackId] || {
  26388. width: 0,
  26389. maxWidth: 0
  26390. };
  26391. // Caution: In a single coordinate system, these barGrid attributes
  26392. // will be shared by series. Consider that they have default values,
  26393. // only the attributes set on the last series will work.
  26394. // Do not change this fact unless there will be a break change.
  26395. // TODO
  26396. var barWidth = seriesInfo.barWidth;
  26397. if (barWidth && !stacks[stackId].width) {
  26398. // See #6312, do not restrict width.
  26399. stacks[stackId].width = barWidth;
  26400. barWidth = Math.min(columnsOnAxis.remainedWidth, barWidth);
  26401. columnsOnAxis.remainedWidth -= barWidth;
  26402. }
  26403. var barMaxWidth = seriesInfo.barMaxWidth;
  26404. barMaxWidth && (stacks[stackId].maxWidth = barMaxWidth);
  26405. var barGap = seriesInfo.barGap;
  26406. (barGap != null) && (columnsOnAxis.gap = barGap);
  26407. var barCategoryGap = seriesInfo.barCategoryGap;
  26408. (barCategoryGap != null) && (columnsOnAxis.categoryGap = barCategoryGap);
  26409. });
  26410. var result = {};
  26411. each$1(columnsMap, function (columnsOnAxis, coordSysName) {
  26412. result[coordSysName] = {};
  26413. var stacks = columnsOnAxis.stacks;
  26414. var bandWidth = columnsOnAxis.bandWidth;
  26415. var categoryGap = parsePercent$1(columnsOnAxis.categoryGap, bandWidth);
  26416. var barGapPercent = parsePercent$1(columnsOnAxis.gap, 1);
  26417. var remainedWidth = columnsOnAxis.remainedWidth;
  26418. var autoWidthCount = columnsOnAxis.autoWidthCount;
  26419. var autoWidth = (remainedWidth - categoryGap)
  26420. / (autoWidthCount + (autoWidthCount - 1) * barGapPercent);
  26421. autoWidth = Math.max(autoWidth, 0);
  26422. // Find if any auto calculated bar exceeded maxBarWidth
  26423. each$1(stacks, function (column, stack) {
  26424. var maxWidth = column.maxWidth;
  26425. if (maxWidth && maxWidth < autoWidth) {
  26426. maxWidth = Math.min(maxWidth, remainedWidth);
  26427. if (column.width) {
  26428. maxWidth = Math.min(maxWidth, column.width);
  26429. }
  26430. remainedWidth -= maxWidth;
  26431. column.width = maxWidth;
  26432. autoWidthCount--;
  26433. }
  26434. });
  26435. // Recalculate width again
  26436. autoWidth = (remainedWidth - categoryGap)
  26437. / (autoWidthCount + (autoWidthCount - 1) * barGapPercent);
  26438. autoWidth = Math.max(autoWidth, 0);
  26439. var widthSum = 0;
  26440. var lastColumn;
  26441. each$1(stacks, function (column, idx) {
  26442. if (!column.width) {
  26443. column.width = autoWidth;
  26444. }
  26445. lastColumn = column;
  26446. widthSum += column.width * (1 + barGapPercent);
  26447. });
  26448. if (lastColumn) {
  26449. widthSum -= lastColumn.width * barGapPercent;
  26450. }
  26451. var offset = -widthSum / 2;
  26452. each$1(stacks, function (column, stackId) {
  26453. result[coordSysName][stackId] = result[coordSysName][stackId] || {
  26454. offset: offset,
  26455. width: column.width
  26456. };
  26457. offset += column.width * (1 + barGapPercent);
  26458. });
  26459. });
  26460. return result;
  26461. }
  26462. /**
  26463. * @param {string} seriesType
  26464. * @param {module:echarts/model/Global} ecModel
  26465. * @param {module:echarts/ExtensionAPI} api
  26466. */
  26467. function barLayoutGrid(seriesType, ecModel, api) {
  26468. var barWidthAndOffset = calBarWidthAndOffset(
  26469. filter(
  26470. ecModel.getSeriesByType(seriesType),
  26471. function (seriesModel) {
  26472. return !ecModel.isSeriesFiltered(seriesModel)
  26473. && seriesModel.coordinateSystem
  26474. && seriesModel.coordinateSystem.type === 'cartesian2d';
  26475. }
  26476. )
  26477. );
  26478. var lastStackCoords = {};
  26479. var lastStackCoordsOrigin = {};
  26480. ecModel.eachSeriesByType(seriesType, function (seriesModel) {
  26481. // Check series coordinate, do layout for cartesian2d only
  26482. if (seriesModel.coordinateSystem.type !== 'cartesian2d') {
  26483. return;
  26484. }
  26485. var data = seriesModel.getData();
  26486. var cartesian = seriesModel.coordinateSystem;
  26487. var baseAxis = cartesian.getBaseAxis();
  26488. var stackId = getSeriesStackId(seriesModel);
  26489. var columnLayoutInfo = barWidthAndOffset[getAxisKey(baseAxis)][stackId];
  26490. var columnOffset = columnLayoutInfo.offset;
  26491. var columnWidth = columnLayoutInfo.width;
  26492. var valueAxis = cartesian.getOtherAxis(baseAxis);
  26493. var barMinHeight = seriesModel.get('barMinHeight') || 0;
  26494. var valueAxisStart = baseAxis.onZero
  26495. ? valueAxis.toGlobalCoord(valueAxis.dataToCoord(0))
  26496. : valueAxis.getGlobalExtent()[0];
  26497. var coordDims = [
  26498. seriesModel.coordDimToDataDim('x')[0],
  26499. seriesModel.coordDimToDataDim('y')[0]
  26500. ];
  26501. var coords = data.mapArray(coordDims, function (x, y) {
  26502. return cartesian.dataToPoint([x, y]);
  26503. }, true);
  26504. lastStackCoords[stackId] = lastStackCoords[stackId] || [];
  26505. lastStackCoordsOrigin[stackId] = lastStackCoordsOrigin[stackId] || []; // Fix #4243
  26506. data.setLayout({
  26507. offset: columnOffset,
  26508. size: columnWidth
  26509. });
  26510. data.each(seriesModel.coordDimToDataDim(valueAxis.dim)[0], function (value, idx) {
  26511. if (isNaN(value)) {
  26512. return;
  26513. }
  26514. if (!lastStackCoords[stackId][idx]) {
  26515. lastStackCoords[stackId][idx] = {
  26516. p: valueAxisStart, // Positive stack
  26517. n: valueAxisStart // Negative stack
  26518. };
  26519. lastStackCoordsOrigin[stackId][idx] = {
  26520. p: valueAxisStart, // Positive stack
  26521. n: valueAxisStart // Negative stack
  26522. };
  26523. }
  26524. var sign = value >= 0 ? 'p' : 'n';
  26525. var coord = coords[idx];
  26526. var lastCoord = lastStackCoords[stackId][idx][sign];
  26527. var lastCoordOrigin = lastStackCoordsOrigin[stackId][idx][sign];
  26528. var x;
  26529. var y;
  26530. var width;
  26531. var height;
  26532. if (valueAxis.isHorizontal()) {
  26533. x = lastCoord;
  26534. y = coord[1] + columnOffset;
  26535. width = coord[0] - lastCoordOrigin;
  26536. height = columnWidth;
  26537. lastStackCoordsOrigin[stackId][idx][sign] += width;
  26538. if (Math.abs(width) < barMinHeight) {
  26539. width = (width < 0 ? -1 : 1) * barMinHeight;
  26540. }
  26541. lastStackCoords[stackId][idx][sign] += width;
  26542. }
  26543. else {
  26544. x = coord[0] + columnOffset;
  26545. y = lastCoord;
  26546. width = columnWidth;
  26547. height = coord[1] - lastCoordOrigin;
  26548. lastStackCoordsOrigin[stackId][idx][sign] += height;
  26549. if (Math.abs(height) < barMinHeight) {
  26550. // Include zero to has a positive bar
  26551. height = (height <= 0 ? -1 : 1) * barMinHeight;
  26552. }
  26553. lastStackCoords[stackId][idx][sign] += height;
  26554. }
  26555. data.setItemLayout(idx, {
  26556. x: x,
  26557. y: y,
  26558. width: width,
  26559. height: height
  26560. });
  26561. }, true);
  26562. }, this);
  26563. }
  26564. barLayoutGrid.getLayoutOnAxis = getLayoutOnAxis;
  26565. var BaseBarSeries = SeriesModel.extend({
  26566. type: 'series.__base_bar__',
  26567. getInitialData: function (option, ecModel) {
  26568. return createListFromArray(option.data, this, ecModel);
  26569. },
  26570. getMarkerPosition: function (value) {
  26571. var coordSys = this.coordinateSystem;
  26572. if (coordSys) {
  26573. // PENDING if clamp ?
  26574. var pt = coordSys.dataToPoint(value, true);
  26575. var data = this.getData();
  26576. var offset = data.getLayout('offset');
  26577. var size = data.getLayout('size');
  26578. var offsetIndex = coordSys.getBaseAxis().isHorizontal() ? 0 : 1;
  26579. pt[offsetIndex] += offset + size / 2;
  26580. return pt;
  26581. }
  26582. return [NaN, NaN];
  26583. },
  26584. defaultOption: {
  26585. zlevel: 0, // 一级层叠
  26586. z: 2, // 二级层叠
  26587. coordinateSystem: 'cartesian2d',
  26588. legendHoverLink: true,
  26589. // stack: null
  26590. // Cartesian coordinate system
  26591. // xAxisIndex: 0,
  26592. // yAxisIndex: 0,
  26593. // 最小高度改为0
  26594. barMinHeight: 0,
  26595. // 最小角度为0,仅对极坐标系下的柱状图有效
  26596. barMinAngle: 0,
  26597. // cursor: null,
  26598. // barMaxWidth: null,
  26599. // 默认自适应
  26600. // barWidth: null,
  26601. // 柱间距离,默认为柱形宽度的30%,可设固定值
  26602. // barGap: '30%',
  26603. // 类目间柱形距离,默认为类目间距的20%,可设固定值
  26604. // barCategoryGap: '20%',
  26605. // label: {
  26606. // normal: {
  26607. // show: false
  26608. // }
  26609. // },
  26610. itemStyle: {
  26611. // normal: {
  26612. // color: '各异'
  26613. // },
  26614. // emphasis: {}
  26615. }
  26616. }
  26617. });
  26618. BaseBarSeries.extend({
  26619. type: 'series.bar',
  26620. dependencies: ['grid', 'polar'],
  26621. brushSelector: 'rect'
  26622. });
  26623. function setLabel(
  26624. normalStyle, hoverStyle, itemModel, color, seriesModel, dataIndex, labelPositionOutside
  26625. ) {
  26626. var labelModel = itemModel.getModel('label.normal');
  26627. var hoverLabelModel = itemModel.getModel('label.emphasis');
  26628. setLabelStyle(
  26629. normalStyle, hoverStyle, labelModel, hoverLabelModel,
  26630. {
  26631. labelFetcher: seriesModel,
  26632. labelDataIndex: dataIndex,
  26633. defaultText: seriesModel.getRawValue(dataIndex),
  26634. isRectText: true,
  26635. autoColor: color
  26636. }
  26637. );
  26638. fixPosition(normalStyle);
  26639. fixPosition(hoverStyle);
  26640. }
  26641. function fixPosition(style, labelPositionOutside) {
  26642. if (style.textPosition === 'outside') {
  26643. style.textPosition = labelPositionOutside;
  26644. }
  26645. }
  26646. var getBarItemStyle = makeStyleMapper(
  26647. [
  26648. ['fill', 'color'],
  26649. ['stroke', 'borderColor'],
  26650. ['lineWidth', 'borderWidth'],
  26651. // Compatitable with 2
  26652. ['stroke', 'barBorderColor'],
  26653. ['lineWidth', 'barBorderWidth'],
  26654. ['opacity'],
  26655. ['shadowBlur'],
  26656. ['shadowOffsetX'],
  26657. ['shadowOffsetY'],
  26658. ['shadowColor']
  26659. ]
  26660. );
  26661. var barItemStyle = {
  26662. getBarItemStyle: function (excludes) {
  26663. var style = getBarItemStyle(this, excludes);
  26664. if (this.getBorderLineDash) {
  26665. var lineDash = this.getBorderLineDash();
  26666. lineDash && (style.lineDash = lineDash);
  26667. }
  26668. return style;
  26669. }
  26670. };
  26671. var BAR_BORDER_WIDTH_QUERY = ['itemStyle', 'normal', 'barBorderWidth'];
  26672. // FIXME
  26673. // Just for compatible with ec2.
  26674. extend(Model.prototype, barItemStyle);
  26675. extendChartView({
  26676. type: 'bar',
  26677. render: function (seriesModel, ecModel, api) {
  26678. var coordinateSystemType = seriesModel.get('coordinateSystem');
  26679. if (coordinateSystemType === 'cartesian2d'
  26680. || coordinateSystemType === 'polar'
  26681. ) {
  26682. this._render(seriesModel, ecModel, api);
  26683. }
  26684. else if (__DEV__) {
  26685. console.warn('Only cartesian2d and polar supported for bar.');
  26686. }
  26687. return this.group;
  26688. },
  26689. dispose: noop,
  26690. _render: function (seriesModel, ecModel, api) {
  26691. var group = this.group;
  26692. var data = seriesModel.getData();
  26693. var oldData = this._data;
  26694. var coord = seriesModel.coordinateSystem;
  26695. var baseAxis = coord.getBaseAxis();
  26696. var isHorizontalOrRadial;
  26697. if (coord.type === 'cartesian2d') {
  26698. isHorizontalOrRadial = baseAxis.isHorizontal();
  26699. }
  26700. else if (coord.type === 'polar') {
  26701. isHorizontalOrRadial = baseAxis.dim === 'angle';
  26702. }
  26703. var animationModel = seriesModel.isAnimationEnabled() ? seriesModel : null;
  26704. data.diff(oldData)
  26705. .add(function (dataIndex) {
  26706. if (!data.hasValue(dataIndex)) {
  26707. return;
  26708. }
  26709. var itemModel = data.getItemModel(dataIndex);
  26710. var layout = getLayout[coord.type](data, dataIndex, itemModel);
  26711. var el = elementCreator[coord.type](
  26712. data, dataIndex, itemModel, layout, isHorizontalOrRadial, animationModel
  26713. );
  26714. data.setItemGraphicEl(dataIndex, el);
  26715. group.add(el);
  26716. updateStyle(
  26717. el, data, dataIndex, itemModel, layout,
  26718. seriesModel, isHorizontalOrRadial, coord.type === 'polar'
  26719. );
  26720. })
  26721. .update(function (newIndex, oldIndex) {
  26722. var el = oldData.getItemGraphicEl(oldIndex);
  26723. if (!data.hasValue(newIndex)) {
  26724. group.remove(el);
  26725. return;
  26726. }
  26727. var itemModel = data.getItemModel(newIndex);
  26728. var layout = getLayout[coord.type](data, newIndex, itemModel);
  26729. if (el) {
  26730. updateProps(el, {shape: layout}, animationModel, newIndex);
  26731. }
  26732. else {
  26733. el = elementCreator[coord.type](
  26734. data, newIndex, itemModel, layout, isHorizontalOrRadial, animationModel, true
  26735. );
  26736. }
  26737. data.setItemGraphicEl(newIndex, el);
  26738. // Add back
  26739. group.add(el);
  26740. updateStyle(
  26741. el, data, newIndex, itemModel, layout,
  26742. seriesModel, isHorizontalOrRadial, coord.type === 'polar'
  26743. );
  26744. })
  26745. .remove(function (dataIndex) {
  26746. var el = oldData.getItemGraphicEl(dataIndex);
  26747. if (coord.type === 'cartesian2d') {
  26748. el && removeRect(dataIndex, animationModel, el);
  26749. }
  26750. else {
  26751. el && removeSector(dataIndex, animationModel, el);
  26752. }
  26753. })
  26754. .execute();
  26755. this._data = data;
  26756. },
  26757. remove: function (ecModel, api) {
  26758. var group = this.group;
  26759. var data = this._data;
  26760. if (ecModel.get('animation')) {
  26761. if (data) {
  26762. data.eachItemGraphicEl(function (el) {
  26763. if (el.type === 'sector') {
  26764. removeSector(el.dataIndex, ecModel, el);
  26765. }
  26766. else {
  26767. removeRect(el.dataIndex, ecModel, el);
  26768. }
  26769. });
  26770. }
  26771. }
  26772. else {
  26773. group.removeAll();
  26774. }
  26775. }
  26776. });
  26777. var elementCreator = {
  26778. cartesian2d: function (
  26779. data, dataIndex, itemModel, layout, isHorizontal,
  26780. animationModel, isUpdate
  26781. ) {
  26782. var rect = new Rect({shape: extend({}, layout)});
  26783. // Animation
  26784. if (animationModel) {
  26785. var rectShape = rect.shape;
  26786. var animateProperty = isHorizontal ? 'height' : 'width';
  26787. var animateTarget = {};
  26788. rectShape[animateProperty] = 0;
  26789. animateTarget[animateProperty] = layout[animateProperty];
  26790. graphic[isUpdate ? 'updateProps' : 'initProps'](rect, {
  26791. shape: animateTarget
  26792. }, animationModel, dataIndex);
  26793. }
  26794. return rect;
  26795. },
  26796. polar: function (
  26797. data, dataIndex, itemModel, layout, isRadial,
  26798. animationModel, isUpdate
  26799. ) {
  26800. var sector = new Sector({shape: extend({}, layout)});
  26801. // Animation
  26802. if (animationModel) {
  26803. var sectorShape = sector.shape;
  26804. var animateProperty = isRadial ? 'r' : 'endAngle';
  26805. var animateTarget = {};
  26806. sectorShape[animateProperty] = isRadial ? 0 : layout.startAngle;
  26807. animateTarget[animateProperty] = layout[animateProperty];
  26808. graphic[isUpdate ? 'updateProps' : 'initProps'](sector, {
  26809. shape: animateTarget
  26810. }, animationModel, dataIndex);
  26811. }
  26812. return sector;
  26813. }
  26814. };
  26815. function removeRect(dataIndex, animationModel, el) {
  26816. // Not show text when animating
  26817. el.style.text = null;
  26818. updateProps(el, {
  26819. shape: {
  26820. width: 0
  26821. }
  26822. }, animationModel, dataIndex, function () {
  26823. el.parent && el.parent.remove(el);
  26824. });
  26825. }
  26826. function removeSector(dataIndex, animationModel, el) {
  26827. // Not show text when animating
  26828. el.style.text = null;
  26829. updateProps(el, {
  26830. shape: {
  26831. r: el.shape.r0
  26832. }
  26833. }, animationModel, dataIndex, function () {
  26834. el.parent && el.parent.remove(el);
  26835. });
  26836. }
  26837. var getLayout = {
  26838. cartesian2d: function (data, dataIndex, itemModel) {
  26839. var layout = data.getItemLayout(dataIndex);
  26840. var fixedLineWidth = getLineWidth(itemModel, layout);
  26841. // fix layout with lineWidth
  26842. var signX = layout.width > 0 ? 1 : -1;
  26843. var signY = layout.height > 0 ? 1 : -1;
  26844. return {
  26845. x: layout.x + signX * fixedLineWidth / 2,
  26846. y: layout.y + signY * fixedLineWidth / 2,
  26847. width: layout.width - signX * fixedLineWidth,
  26848. height: layout.height - signY * fixedLineWidth
  26849. };
  26850. },
  26851. polar: function (data, dataIndex, itemModel) {
  26852. var layout = data.getItemLayout(dataIndex);
  26853. return {
  26854. cx: layout.cx,
  26855. cy: layout.cy,
  26856. r0: layout.r0,
  26857. r: layout.r,
  26858. startAngle: layout.startAngle,
  26859. endAngle: layout.endAngle
  26860. };
  26861. }
  26862. };
  26863. function updateStyle(
  26864. el, data, dataIndex, itemModel, layout, seriesModel, isHorizontal, isPolar
  26865. ) {
  26866. var color = data.getItemVisual(dataIndex, 'color');
  26867. var opacity = data.getItemVisual(dataIndex, 'opacity');
  26868. var itemStyleModel = itemModel.getModel('itemStyle.normal');
  26869. var hoverStyle = itemModel.getModel('itemStyle.emphasis').getBarItemStyle();
  26870. if (!isPolar) {
  26871. el.setShape('r', itemStyleModel.get('barBorderRadius') || 0);
  26872. }
  26873. el.useStyle(defaults(
  26874. {
  26875. fill: color,
  26876. opacity: opacity
  26877. },
  26878. itemStyleModel.getBarItemStyle()
  26879. ));
  26880. var cursorStyle = itemModel.getShallow('cursor');
  26881. cursorStyle && el.attr('cursor', cursorStyle);
  26882. var labelPositionOutside = isHorizontal
  26883. ? (layout.height > 0 ? 'bottom' : 'top')
  26884. : (layout.width > 0 ? 'left' : 'right');
  26885. if (!isPolar) {
  26886. setLabel(
  26887. el.style, hoverStyle, itemModel, color,
  26888. seriesModel, dataIndex, labelPositionOutside
  26889. );
  26890. }
  26891. setHoverStyle(el, hoverStyle);
  26892. }
  26893. // In case width or height are too small.
  26894. function getLineWidth(itemModel, rawLayout) {
  26895. var lineWidth = itemModel.get(BAR_BORDER_WIDTH_QUERY) || 0;
  26896. return Math.min(lineWidth, Math.abs(rawLayout.width), Math.abs(rawLayout.height));
  26897. }
  26898. // In case developer forget to include grid component
  26899. registerLayout(curry(barLayoutGrid, 'bar'));
  26900. // Visual coding for legend
  26901. registerVisual(function (ecModel) {
  26902. ecModel.eachSeriesByType('bar', function (seriesModel) {
  26903. var data = seriesModel.getData();
  26904. data.setVisual('legendSymbol', 'roundRect');
  26905. });
  26906. });
  26907. /**
  26908. * Data selectable mixin for chart series.
  26909. * To eanble data select, option of series must have `selectedMode`.
  26910. * And each data item will use `selected` to toggle itself selected status
  26911. */
  26912. var dataSelectableMixin = {
  26913. updateSelectedMap: function (targetList) {
  26914. this._targetList = targetList.slice();
  26915. this._selectTargetMap = reduce(targetList || [], function (targetMap, target) {
  26916. targetMap.set(target.name, target);
  26917. return targetMap;
  26918. }, createHashMap());
  26919. },
  26920. /**
  26921. * Either name or id should be passed as input here.
  26922. * If both of them are defined, id is used.
  26923. *
  26924. * @param {string|undefined} name name of data
  26925. * @param {number|undefined} id dataIndex of data
  26926. */
  26927. // PENGING If selectedMode is null ?
  26928. select: function (name, id) {
  26929. var target = id != null
  26930. ? this._targetList[id]
  26931. : this._selectTargetMap.get(name);
  26932. var selectedMode = this.get('selectedMode');
  26933. if (selectedMode === 'single') {
  26934. this._selectTargetMap.each(function (target) {
  26935. target.selected = false;
  26936. });
  26937. }
  26938. target && (target.selected = true);
  26939. },
  26940. /**
  26941. * Either name or id should be passed as input here.
  26942. * If both of them are defined, id is used.
  26943. *
  26944. * @param {string|undefined} name name of data
  26945. * @param {number|undefined} id dataIndex of data
  26946. */
  26947. unSelect: function (name, id) {
  26948. var target = id != null
  26949. ? this._targetList[id]
  26950. : this._selectTargetMap.get(name);
  26951. // var selectedMode = this.get('selectedMode');
  26952. // selectedMode !== 'single' && target && (target.selected = false);
  26953. target && (target.selected = false);
  26954. },
  26955. /**
  26956. * Either name or id should be passed as input here.
  26957. * If both of them are defined, id is used.
  26958. *
  26959. * @param {string|undefined} name name of data
  26960. * @param {number|undefined} id dataIndex of data
  26961. */
  26962. toggleSelected: function (name, id) {
  26963. var target = id != null
  26964. ? this._targetList[id]
  26965. : this._selectTargetMap.get(name);
  26966. if (target != null) {
  26967. this[target.selected ? 'unSelect' : 'select'](name, id);
  26968. return target.selected;
  26969. }
  26970. },
  26971. /**
  26972. * Either name or id should be passed as input here.
  26973. * If both of them are defined, id is used.
  26974. *
  26975. * @param {string|undefined} name name of data
  26976. * @param {number|undefined} id dataIndex of data
  26977. */
  26978. isSelected: function (name, id) {
  26979. var target = id != null
  26980. ? this._targetList[id]
  26981. : this._selectTargetMap.get(name);
  26982. return target && target.selected;
  26983. }
  26984. };
  26985. var PieSeries = extendSeriesModel({
  26986. type: 'series.pie',
  26987. // Overwrite
  26988. init: function (option) {
  26989. PieSeries.superApply(this, 'init', arguments);
  26990. // Enable legend selection for each data item
  26991. // Use a function instead of direct access because data reference may changed
  26992. this.legendDataProvider = function () {
  26993. return this.getRawData();
  26994. };
  26995. this.updateSelectedMap(option.data);
  26996. this._defaultLabelLine(option);
  26997. },
  26998. // Overwrite
  26999. mergeOption: function (newOption) {
  27000. PieSeries.superCall(this, 'mergeOption', newOption);
  27001. this.updateSelectedMap(this.option.data);
  27002. },
  27003. getInitialData: function (option, ecModel) {
  27004. var dimensions = completeDimensions(['value'], option.data);
  27005. var list = new List(dimensions, this);
  27006. list.initData(option.data);
  27007. return list;
  27008. },
  27009. // Overwrite
  27010. getDataParams: function (dataIndex) {
  27011. var data = this.getData();
  27012. var params = PieSeries.superCall(this, 'getDataParams', dataIndex);
  27013. // FIXME toFixed?
  27014. var valueList = [];
  27015. data.each('value', function (value) {
  27016. valueList.push(value);
  27017. });
  27018. params.percent = getPercentWithPrecision(
  27019. valueList,
  27020. dataIndex,
  27021. data.hostModel.get('percentPrecision')
  27022. );
  27023. params.$vars.push('percent');
  27024. return params;
  27025. },
  27026. _defaultLabelLine: function (option) {
  27027. // Extend labelLine emphasis
  27028. defaultEmphasis(option.labelLine, ['show']);
  27029. var labelLineNormalOpt = option.labelLine.normal;
  27030. var labelLineEmphasisOpt = option.labelLine.emphasis;
  27031. // Not show label line if `label.normal.show = false`
  27032. labelLineNormalOpt.show = labelLineNormalOpt.show
  27033. && option.label.normal.show;
  27034. labelLineEmphasisOpt.show = labelLineEmphasisOpt.show
  27035. && option.label.emphasis.show;
  27036. },
  27037. defaultOption: {
  27038. zlevel: 0,
  27039. z: 2,
  27040. legendHoverLink: true,
  27041. hoverAnimation: true,
  27042. // 默认全局居中
  27043. center: ['50%', '50%'],
  27044. radius: [0, '75%'],
  27045. // 默认顺时针
  27046. clockwise: true,
  27047. startAngle: 90,
  27048. // 最小角度改为0
  27049. minAngle: 0,
  27050. // 选中时扇区偏移量
  27051. selectedOffset: 10,
  27052. // 高亮扇区偏移量
  27053. hoverOffset: 10,
  27054. // If use strategy to avoid label overlapping
  27055. avoidLabelOverlap: true,
  27056. // 选择模式,默认关闭,可选single,multiple
  27057. // selectedMode: false,
  27058. // 南丁格尔玫瑰图模式,'radius'(半径) | 'area'(面积)
  27059. // roseType: null,
  27060. percentPrecision: 2,
  27061. // If still show when all data zero.
  27062. stillShowZeroSum: true,
  27063. // cursor: null,
  27064. label: {
  27065. normal: {
  27066. // If rotate around circle
  27067. rotate: false,
  27068. show: true,
  27069. // 'outer', 'inside', 'center'
  27070. position: 'outer'
  27071. // formatter: 标签文本格式器,同Tooltip.formatter,不支持异步回调
  27072. // 默认使用全局文本样式,详见TEXTSTYLE
  27073. // distance: 当position为inner时有效,为label位置到圆心的距离与圆半径(环状图为内外半径和)的比例系数
  27074. },
  27075. emphasis: {}
  27076. },
  27077. // Enabled when label.normal.position is 'outer'
  27078. labelLine: {
  27079. normal: {
  27080. show: true,
  27081. // 引导线两段中的第一段长度
  27082. length: 15,
  27083. // 引导线两段中的第二段长度
  27084. length2: 15,
  27085. smooth: false,
  27086. lineStyle: {
  27087. // color: 各异,
  27088. width: 1,
  27089. type: 'solid'
  27090. }
  27091. }
  27092. },
  27093. itemStyle: {
  27094. normal: {
  27095. borderWidth: 1
  27096. },
  27097. emphasis: {}
  27098. },
  27099. // Animation type canbe expansion, scale
  27100. animationType: 'expansion',
  27101. animationEasing: 'cubicOut',
  27102. data: []
  27103. }
  27104. });
  27105. mixin(PieSeries, dataSelectableMixin);
  27106. /**
  27107. * @param {module:echarts/model/Series} seriesModel
  27108. * @param {boolean} hasAnimation
  27109. * @inner
  27110. */
  27111. function updateDataSelected(uid, seriesModel, hasAnimation, api) {
  27112. var data = seriesModel.getData();
  27113. var dataIndex = this.dataIndex;
  27114. var name = data.getName(dataIndex);
  27115. var selectedOffset = seriesModel.get('selectedOffset');
  27116. api.dispatchAction({
  27117. type: 'pieToggleSelect',
  27118. from: uid,
  27119. name: name,
  27120. seriesId: seriesModel.id
  27121. });
  27122. data.each(function (idx) {
  27123. toggleItemSelected(
  27124. data.getItemGraphicEl(idx),
  27125. data.getItemLayout(idx),
  27126. seriesModel.isSelected(data.getName(idx)),
  27127. selectedOffset,
  27128. hasAnimation
  27129. );
  27130. });
  27131. }
  27132. /**
  27133. * @param {module:zrender/graphic/Sector} el
  27134. * @param {Object} layout
  27135. * @param {boolean} isSelected
  27136. * @param {number} selectedOffset
  27137. * @param {boolean} hasAnimation
  27138. * @inner
  27139. */
  27140. function toggleItemSelected(el, layout, isSelected, selectedOffset, hasAnimation) {
  27141. var midAngle = (layout.startAngle + layout.endAngle) / 2;
  27142. var dx = Math.cos(midAngle);
  27143. var dy = Math.sin(midAngle);
  27144. var offset = isSelected ? selectedOffset : 0;
  27145. var position = [dx * offset, dy * offset];
  27146. hasAnimation
  27147. // animateTo will stop revious animation like update transition
  27148. ? el.animate()
  27149. .when(200, {
  27150. position: position
  27151. })
  27152. .start('bounceOut')
  27153. : el.attr('position', position);
  27154. }
  27155. /**
  27156. * Piece of pie including Sector, Label, LabelLine
  27157. * @constructor
  27158. * @extends {module:zrender/graphic/Group}
  27159. */
  27160. function PiePiece(data, idx) {
  27161. Group.call(this);
  27162. var sector = new Sector({
  27163. z2: 2
  27164. });
  27165. var polyline = new Polyline();
  27166. var text = new Text();
  27167. this.add(sector);
  27168. this.add(polyline);
  27169. this.add(text);
  27170. this.updateData(data, idx, true);
  27171. // Hover to change label and labelLine
  27172. function onEmphasis() {
  27173. polyline.ignore = polyline.hoverIgnore;
  27174. text.ignore = text.hoverIgnore;
  27175. }
  27176. function onNormal() {
  27177. polyline.ignore = polyline.normalIgnore;
  27178. text.ignore = text.normalIgnore;
  27179. }
  27180. this.on('emphasis', onEmphasis)
  27181. .on('normal', onNormal)
  27182. .on('mouseover', onEmphasis)
  27183. .on('mouseout', onNormal);
  27184. }
  27185. var piePieceProto = PiePiece.prototype;
  27186. piePieceProto.updateData = function (data, idx, firstCreate) {
  27187. var sector = this.childAt(0);
  27188. var seriesModel = data.hostModel;
  27189. var itemModel = data.getItemModel(idx);
  27190. var layout = data.getItemLayout(idx);
  27191. var sectorShape = extend({}, layout);
  27192. sectorShape.label = null;
  27193. if (firstCreate) {
  27194. sector.setShape(sectorShape);
  27195. var animationType = seriesModel.getShallow('animationType');
  27196. if (animationType === 'scale') {
  27197. sector.shape.r = layout.r0;
  27198. initProps(sector, {
  27199. shape: {
  27200. r: layout.r
  27201. }
  27202. }, seriesModel, idx);
  27203. }
  27204. // Expansion
  27205. else {
  27206. sector.shape.endAngle = layout.startAngle;
  27207. updateProps(sector, {
  27208. shape: {
  27209. endAngle: layout.endAngle
  27210. }
  27211. }, seriesModel, idx);
  27212. }
  27213. }
  27214. else {
  27215. updateProps(sector, {
  27216. shape: sectorShape
  27217. }, seriesModel, idx);
  27218. }
  27219. // Update common style
  27220. var itemStyleModel = itemModel.getModel('itemStyle');
  27221. var visualColor = data.getItemVisual(idx, 'color');
  27222. sector.useStyle(
  27223. defaults(
  27224. {
  27225. lineJoin: 'bevel',
  27226. fill: visualColor
  27227. },
  27228. itemStyleModel.getModel('normal').getItemStyle()
  27229. )
  27230. );
  27231. sector.hoverStyle = itemStyleModel.getModel('emphasis').getItemStyle();
  27232. var cursorStyle = itemModel.getShallow('cursor');
  27233. cursorStyle && sector.attr('cursor', cursorStyle);
  27234. // Toggle selected
  27235. toggleItemSelected(
  27236. this,
  27237. data.getItemLayout(idx),
  27238. itemModel.get('selected'),
  27239. seriesModel.get('selectedOffset'),
  27240. seriesModel.get('animation')
  27241. );
  27242. function onEmphasis() {
  27243. // Sector may has animation of updating data. Force to move to the last frame
  27244. // Or it may stopped on the wrong shape
  27245. sector.stopAnimation(true);
  27246. sector.animateTo({
  27247. shape: {
  27248. r: layout.r + seriesModel.get('hoverOffset')
  27249. }
  27250. }, 300, 'elasticOut');
  27251. }
  27252. function onNormal() {
  27253. sector.stopAnimation(true);
  27254. sector.animateTo({
  27255. shape: {
  27256. r: layout.r
  27257. }
  27258. }, 300, 'elasticOut');
  27259. }
  27260. sector.off('mouseover').off('mouseout').off('emphasis').off('normal');
  27261. if (itemModel.get('hoverAnimation') && seriesModel.isAnimationEnabled()) {
  27262. sector
  27263. .on('mouseover', onEmphasis)
  27264. .on('mouseout', onNormal)
  27265. .on('emphasis', onEmphasis)
  27266. .on('normal', onNormal);
  27267. }
  27268. this._updateLabel(data, idx);
  27269. setHoverStyle(this);
  27270. };
  27271. piePieceProto._updateLabel = function (data, idx) {
  27272. var labelLine = this.childAt(1);
  27273. var labelText = this.childAt(2);
  27274. var seriesModel = data.hostModel;
  27275. var itemModel = data.getItemModel(idx);
  27276. var layout = data.getItemLayout(idx);
  27277. var labelLayout = layout.label;
  27278. var visualColor = data.getItemVisual(idx, 'color');
  27279. updateProps(labelLine, {
  27280. shape: {
  27281. points: labelLayout.linePoints || [
  27282. [labelLayout.x, labelLayout.y], [labelLayout.x, labelLayout.y], [labelLayout.x, labelLayout.y]
  27283. ]
  27284. }
  27285. }, seriesModel, idx);
  27286. updateProps(labelText, {
  27287. style: {
  27288. x: labelLayout.x,
  27289. y: labelLayout.y
  27290. }
  27291. }, seriesModel, idx);
  27292. labelText.attr({
  27293. rotation: labelLayout.rotation,
  27294. origin: [labelLayout.x, labelLayout.y],
  27295. z2: 10
  27296. });
  27297. var labelModel = itemModel.getModel('label.normal');
  27298. var labelHoverModel = itemModel.getModel('label.emphasis');
  27299. var labelLineModel = itemModel.getModel('labelLine.normal');
  27300. var labelLineHoverModel = itemModel.getModel('labelLine.emphasis');
  27301. var visualColor = data.getItemVisual(idx, 'color');
  27302. setLabelStyle(
  27303. labelText.style, labelText.hoverStyle = {}, labelModel, labelHoverModel,
  27304. {
  27305. labelFetcher: data.hostModel,
  27306. labelDataIndex: idx,
  27307. defaultText: data.getName(idx),
  27308. autoColor: visualColor,
  27309. useInsideStyle: !!labelLayout.inside
  27310. },
  27311. {
  27312. textAlign: labelLayout.textAlign,
  27313. textVerticalAlign: labelLayout.verticalAlign,
  27314. opacity: data.getItemVisual(idx, 'opacity')
  27315. }
  27316. );
  27317. labelText.ignore = labelText.normalIgnore = !labelModel.get('show');
  27318. labelText.hoverIgnore = !labelHoverModel.get('show');
  27319. labelLine.ignore = labelLine.normalIgnore = !labelLineModel.get('show');
  27320. labelLine.hoverIgnore = !labelLineHoverModel.get('show');
  27321. // Default use item visual color
  27322. labelLine.setStyle({
  27323. stroke: visualColor,
  27324. opacity: data.getItemVisual(idx, 'opacity')
  27325. });
  27326. labelLine.setStyle(labelLineModel.getModel('lineStyle').getLineStyle());
  27327. labelLine.hoverStyle = labelLineHoverModel.getModel('lineStyle').getLineStyle();
  27328. var smooth = labelLineModel.get('smooth');
  27329. if (smooth && smooth === true) {
  27330. smooth = 0.4;
  27331. }
  27332. labelLine.setShape({
  27333. smooth: smooth
  27334. });
  27335. };
  27336. inherits(PiePiece, Group);
  27337. // Pie view
  27338. var PieView = Chart.extend({
  27339. type: 'pie',
  27340. init: function () {
  27341. var sectorGroup = new Group();
  27342. this._sectorGroup = sectorGroup;
  27343. },
  27344. render: function (seriesModel, ecModel, api, payload) {
  27345. if (payload && (payload.from === this.uid)) {
  27346. return;
  27347. }
  27348. var data = seriesModel.getData();
  27349. var oldData = this._data;
  27350. var group = this.group;
  27351. var hasAnimation = ecModel.get('animation');
  27352. var isFirstRender = !oldData;
  27353. var animationType = seriesModel.get('animationType');
  27354. var onSectorClick = curry(
  27355. updateDataSelected, this.uid, seriesModel, hasAnimation, api
  27356. );
  27357. var selectedMode = seriesModel.get('selectedMode');
  27358. data.diff(oldData)
  27359. .add(function (idx) {
  27360. var piePiece = new PiePiece(data, idx);
  27361. // Default expansion animation
  27362. if (isFirstRender && animationType !== 'scale') {
  27363. piePiece.eachChild(function (child) {
  27364. child.stopAnimation(true);
  27365. });
  27366. }
  27367. selectedMode && piePiece.on('click', onSectorClick);
  27368. data.setItemGraphicEl(idx, piePiece);
  27369. group.add(piePiece);
  27370. })
  27371. .update(function (newIdx, oldIdx) {
  27372. var piePiece = oldData.getItemGraphicEl(oldIdx);
  27373. piePiece.updateData(data, newIdx);
  27374. piePiece.off('click');
  27375. selectedMode && piePiece.on('click', onSectorClick);
  27376. group.add(piePiece);
  27377. data.setItemGraphicEl(newIdx, piePiece);
  27378. })
  27379. .remove(function (idx) {
  27380. var piePiece = oldData.getItemGraphicEl(idx);
  27381. group.remove(piePiece);
  27382. })
  27383. .execute();
  27384. if (
  27385. hasAnimation && isFirstRender && data.count() > 0
  27386. // Default expansion animation
  27387. && animationType !== 'scale'
  27388. ) {
  27389. var shape = data.getItemLayout(0);
  27390. var r = Math.max(api.getWidth(), api.getHeight()) / 2;
  27391. var removeClipPath = bind(group.removeClipPath, group);
  27392. group.setClipPath(this._createClipPath(
  27393. shape.cx, shape.cy, r, shape.startAngle, shape.clockwise, removeClipPath, seriesModel
  27394. ));
  27395. }
  27396. this._data = data;
  27397. },
  27398. dispose: function () {},
  27399. _createClipPath: function (
  27400. cx, cy, r, startAngle, clockwise, cb, seriesModel
  27401. ) {
  27402. var clipPath = new Sector({
  27403. shape: {
  27404. cx: cx,
  27405. cy: cy,
  27406. r0: 0,
  27407. r: r,
  27408. startAngle: startAngle,
  27409. endAngle: startAngle,
  27410. clockwise: clockwise
  27411. }
  27412. });
  27413. initProps(clipPath, {
  27414. shape: {
  27415. endAngle: startAngle + (clockwise ? 1 : -1) * Math.PI * 2
  27416. }
  27417. }, seriesModel, cb);
  27418. return clipPath;
  27419. },
  27420. /**
  27421. * @implement
  27422. */
  27423. containPoint: function (point, seriesModel) {
  27424. var data = seriesModel.getData();
  27425. var itemLayout = data.getItemLayout(0);
  27426. if (itemLayout) {
  27427. var dx = point[0] - itemLayout.cx;
  27428. var dy = point[1] - itemLayout.cy;
  27429. var radius = Math.sqrt(dx * dx + dy * dy);
  27430. return radius <= itemLayout.r && radius >= itemLayout.r0;
  27431. }
  27432. }
  27433. });
  27434. var createDataSelectAction = function (seriesType, actionInfos) {
  27435. each$1(actionInfos, function (actionInfo) {
  27436. actionInfo.update = 'updateView';
  27437. /**
  27438. * @payload
  27439. * @property {string} seriesName
  27440. * @property {string} name
  27441. */
  27442. registerAction(actionInfo, function (payload, ecModel) {
  27443. var selected = {};
  27444. ecModel.eachComponent(
  27445. {mainType: 'series', subType: seriesType, query: payload},
  27446. function (seriesModel) {
  27447. if (seriesModel[actionInfo.method]) {
  27448. seriesModel[actionInfo.method](
  27449. payload.name,
  27450. payload.dataIndex
  27451. );
  27452. }
  27453. var data = seriesModel.getData();
  27454. // Create selected map
  27455. data.each(function (idx) {
  27456. var name = data.getName(idx);
  27457. selected[name] = seriesModel.isSelected(name)
  27458. || false;
  27459. });
  27460. }
  27461. );
  27462. return {
  27463. name: payload.name,
  27464. selected: selected
  27465. };
  27466. });
  27467. });
  27468. };
  27469. // Pick color from palette for each data item.
  27470. // Applicable for charts that require applying color palette
  27471. // in data level (like pie, funnel, chord).
  27472. var dataColor = function (seriesType, ecModel) {
  27473. // Pie and funnel may use diferrent scope
  27474. var paletteScope = {};
  27475. ecModel.eachRawSeriesByType(seriesType, function (seriesModel) {
  27476. var dataAll = seriesModel.getRawData();
  27477. var idxMap = {};
  27478. if (!ecModel.isSeriesFiltered(seriesModel)) {
  27479. var data = seriesModel.getData();
  27480. data.each(function (idx) {
  27481. var rawIdx = data.getRawIndex(idx);
  27482. idxMap[rawIdx] = idx;
  27483. });
  27484. dataAll.each(function (rawIdx) {
  27485. var filteredIdx = idxMap[rawIdx];
  27486. // If series.itemStyle.normal.color is a function. itemVisual may be encoded
  27487. var singleDataColor = filteredIdx != null
  27488. && data.getItemVisual(filteredIdx, 'color', true);
  27489. if (!singleDataColor) {
  27490. // FIXME Performance
  27491. var itemModel = dataAll.getItemModel(rawIdx);
  27492. var color = itemModel.get('itemStyle.normal.color')
  27493. || seriesModel.getColorFromPalette(dataAll.getName(rawIdx), paletteScope);
  27494. // Legend may use the visual info in data before processed
  27495. dataAll.setItemVisual(rawIdx, 'color', color);
  27496. // Data is not filtered
  27497. if (filteredIdx != null) {
  27498. data.setItemVisual(filteredIdx, 'color', color);
  27499. }
  27500. }
  27501. else {
  27502. // Set data all color for legend
  27503. dataAll.setItemVisual(rawIdx, 'color', singleDataColor);
  27504. }
  27505. });
  27506. }
  27507. });
  27508. };
  27509. // FIXME emphasis label position is not same with normal label position
  27510. function adjustSingleSide(list, cx, cy, r, dir, viewWidth, viewHeight) {
  27511. list.sort(function (a, b) {
  27512. return a.y - b.y;
  27513. });
  27514. // 压
  27515. function shiftDown(start, end, delta, dir) {
  27516. for (var j = start; j < end; j++) {
  27517. list[j].y += delta;
  27518. if (j > start
  27519. && j + 1 < end
  27520. && list[j + 1].y > list[j].y + list[j].height
  27521. ) {
  27522. shiftUp(j, delta / 2);
  27523. return;
  27524. }
  27525. }
  27526. shiftUp(end - 1, delta / 2);
  27527. }
  27528. // 弹
  27529. function shiftUp(end, delta) {
  27530. for (var j = end; j >= 0; j--) {
  27531. list[j].y -= delta;
  27532. if (j > 0
  27533. && list[j].y > list[j - 1].y + list[j - 1].height
  27534. ) {
  27535. break;
  27536. }
  27537. }
  27538. }
  27539. function changeX(list, isDownList, cx, cy, r, dir) {
  27540. var lastDeltaX = dir > 0
  27541. ? isDownList // 右侧
  27542. ? Number.MAX_VALUE // 下
  27543. : 0 // 上
  27544. : isDownList // 左侧
  27545. ? Number.MAX_VALUE // 下
  27546. : 0; // 上
  27547. for (var i = 0, l = list.length; i < l; i++) {
  27548. // Not change x for center label
  27549. if (list[i].position === 'center') {
  27550. continue;
  27551. }
  27552. var deltaY = Math.abs(list[i].y - cy);
  27553. var length = list[i].len;
  27554. var length2 = list[i].len2;
  27555. var deltaX = (deltaY < r + length)
  27556. ? Math.sqrt(
  27557. (r + length + length2) * (r + length + length2)
  27558. - deltaY * deltaY
  27559. )
  27560. : Math.abs(list[i].x - cx);
  27561. if (isDownList && deltaX >= lastDeltaX) {
  27562. // 右下,左下
  27563. deltaX = lastDeltaX - 10;
  27564. }
  27565. if (!isDownList && deltaX <= lastDeltaX) {
  27566. // 右上,左上
  27567. deltaX = lastDeltaX + 10;
  27568. }
  27569. list[i].x = cx + deltaX * dir;
  27570. lastDeltaX = deltaX;
  27571. }
  27572. }
  27573. var lastY = 0;
  27574. var delta;
  27575. var len = list.length;
  27576. var upList = [];
  27577. var downList = [];
  27578. for (var i = 0; i < len; i++) {
  27579. delta = list[i].y - lastY;
  27580. if (delta < 0) {
  27581. shiftDown(i, len, -delta, dir);
  27582. }
  27583. lastY = list[i].y + list[i].height;
  27584. }
  27585. if (viewHeight - lastY < 0) {
  27586. shiftUp(len - 1, lastY - viewHeight);
  27587. }
  27588. for (var i = 0; i < len; i++) {
  27589. if (list[i].y >= cy) {
  27590. downList.push(list[i]);
  27591. }
  27592. else {
  27593. upList.push(list[i]);
  27594. }
  27595. }
  27596. changeX(upList, false, cx, cy, r, dir);
  27597. changeX(downList, true, cx, cy, r, dir);
  27598. }
  27599. function avoidOverlap(labelLayoutList, cx, cy, r, viewWidth, viewHeight) {
  27600. var leftList = [];
  27601. var rightList = [];
  27602. for (var i = 0; i < labelLayoutList.length; i++) {
  27603. if (labelLayoutList[i].x < cx) {
  27604. leftList.push(labelLayoutList[i]);
  27605. }
  27606. else {
  27607. rightList.push(labelLayoutList[i]);
  27608. }
  27609. }
  27610. adjustSingleSide(rightList, cx, cy, r, 1, viewWidth, viewHeight);
  27611. adjustSingleSide(leftList, cx, cy, r, -1, viewWidth, viewHeight);
  27612. for (var i = 0; i < labelLayoutList.length; i++) {
  27613. var linePoints = labelLayoutList[i].linePoints;
  27614. if (linePoints) {
  27615. var dist = linePoints[1][0] - linePoints[2][0];
  27616. if (labelLayoutList[i].x < cx) {
  27617. linePoints[2][0] = labelLayoutList[i].x + 3;
  27618. }
  27619. else {
  27620. linePoints[2][0] = labelLayoutList[i].x - 3;
  27621. }
  27622. linePoints[1][1] = linePoints[2][1] = labelLayoutList[i].y;
  27623. linePoints[1][0] = linePoints[2][0] + dist;
  27624. }
  27625. }
  27626. }
  27627. var labelLayout = function (seriesModel, r, viewWidth, viewHeight) {
  27628. var data = seriesModel.getData();
  27629. var labelLayoutList = [];
  27630. var cx;
  27631. var cy;
  27632. var hasLabelRotate = false;
  27633. data.each(function (idx) {
  27634. var layout = data.getItemLayout(idx);
  27635. var itemModel = data.getItemModel(idx);
  27636. var labelModel = itemModel.getModel('label.normal');
  27637. // Use position in normal or emphasis
  27638. var labelPosition = labelModel.get('position') || itemModel.get('label.emphasis.position');
  27639. var labelLineModel = itemModel.getModel('labelLine.normal');
  27640. var labelLineLen = labelLineModel.get('length');
  27641. var labelLineLen2 = labelLineModel.get('length2');
  27642. var midAngle = (layout.startAngle + layout.endAngle) / 2;
  27643. var dx = Math.cos(midAngle);
  27644. var dy = Math.sin(midAngle);
  27645. var textX;
  27646. var textY;
  27647. var linePoints;
  27648. var textAlign;
  27649. cx = layout.cx;
  27650. cy = layout.cy;
  27651. var isLabelInside = labelPosition === 'inside' || labelPosition === 'inner';
  27652. if (labelPosition === 'center') {
  27653. textX = layout.cx;
  27654. textY = layout.cy;
  27655. textAlign = 'center';
  27656. }
  27657. else {
  27658. var x1 = (isLabelInside ? (layout.r + layout.r0) / 2 * dx : layout.r * dx) + cx;
  27659. var y1 = (isLabelInside ? (layout.r + layout.r0) / 2 * dy : layout.r * dy) + cy;
  27660. textX = x1 + dx * 3;
  27661. textY = y1 + dy * 3;
  27662. if (!isLabelInside) {
  27663. // For roseType
  27664. var x2 = x1 + dx * (labelLineLen + r - layout.r);
  27665. var y2 = y1 + dy * (labelLineLen + r - layout.r);
  27666. var x3 = x2 + ((dx < 0 ? -1 : 1) * labelLineLen2);
  27667. var y3 = y2;
  27668. textX = x3 + (dx < 0 ? -5 : 5);
  27669. textY = y3;
  27670. linePoints = [[x1, y1], [x2, y2], [x3, y3]];
  27671. }
  27672. textAlign = isLabelInside ? 'center' : (dx > 0 ? 'left' : 'right');
  27673. }
  27674. var font = labelModel.getFont();
  27675. var labelRotate = labelModel.get('rotate')
  27676. ? (dx < 0 ? -midAngle + Math.PI : -midAngle) : 0;
  27677. var text = seriesModel.getFormattedLabel(idx, 'normal')
  27678. || data.getName(idx);
  27679. var textRect = getBoundingRect(
  27680. text, font, textAlign, 'top'
  27681. );
  27682. hasLabelRotate = !!labelRotate;
  27683. layout.label = {
  27684. x: textX,
  27685. y: textY,
  27686. position: labelPosition,
  27687. height: textRect.height,
  27688. len: labelLineLen,
  27689. len2: labelLineLen2,
  27690. linePoints: linePoints,
  27691. textAlign: textAlign,
  27692. verticalAlign: 'middle',
  27693. rotation: labelRotate,
  27694. inside: isLabelInside
  27695. };
  27696. // Not layout the inside label
  27697. if (!isLabelInside) {
  27698. labelLayoutList.push(layout.label);
  27699. }
  27700. });
  27701. if (!hasLabelRotate && seriesModel.get('avoidLabelOverlap')) {
  27702. avoidOverlap(labelLayoutList, cx, cy, r, viewWidth, viewHeight);
  27703. }
  27704. };
  27705. var PI2$4 = Math.PI * 2;
  27706. var RADIAN = Math.PI / 180;
  27707. var pieLayout = function (seriesType, ecModel, api, payload) {
  27708. ecModel.eachSeriesByType(seriesType, function (seriesModel) {
  27709. var center = seriesModel.get('center');
  27710. var radius = seriesModel.get('radius');
  27711. if (!isArray(radius)) {
  27712. radius = [0, radius];
  27713. }
  27714. if (!isArray(center)) {
  27715. center = [center, center];
  27716. }
  27717. var width = api.getWidth();
  27718. var height = api.getHeight();
  27719. var size = Math.min(width, height);
  27720. var cx = parsePercent$1(center[0], width);
  27721. var cy = parsePercent$1(center[1], height);
  27722. var r0 = parsePercent$1(radius[0], size / 2);
  27723. var r = parsePercent$1(radius[1], size / 2);
  27724. var data = seriesModel.getData();
  27725. var startAngle = -seriesModel.get('startAngle') * RADIAN;
  27726. var minAngle = seriesModel.get('minAngle') * RADIAN;
  27727. var validDataCount = 0;
  27728. data.each('value', function (value) {
  27729. !isNaN(value) && validDataCount++;
  27730. });
  27731. var sum = data.getSum('value');
  27732. // Sum may be 0
  27733. var unitRadian = Math.PI / (sum || validDataCount) * 2;
  27734. var clockwise = seriesModel.get('clockwise');
  27735. var roseType = seriesModel.get('roseType');
  27736. var stillShowZeroSum = seriesModel.get('stillShowZeroSum');
  27737. // [0...max]
  27738. var extent = data.getDataExtent('value');
  27739. extent[0] = 0;
  27740. // In the case some sector angle is smaller than minAngle
  27741. var restAngle = PI2$4;
  27742. var valueSumLargerThanMinAngle = 0;
  27743. var currentAngle = startAngle;
  27744. var dir = clockwise ? 1 : -1;
  27745. data.each('value', function (value, idx) {
  27746. var angle;
  27747. if (isNaN(value)) {
  27748. data.setItemLayout(idx, {
  27749. angle: NaN,
  27750. startAngle: NaN,
  27751. endAngle: NaN,
  27752. clockwise: clockwise,
  27753. cx: cx,
  27754. cy: cy,
  27755. r0: r0,
  27756. r: roseType
  27757. ? NaN
  27758. : r
  27759. });
  27760. return;
  27761. }
  27762. // FIXME 兼容 2.0 但是 roseType 是 area 的时候才是这样?
  27763. if (roseType !== 'area') {
  27764. angle = (sum === 0 && stillShowZeroSum)
  27765. ? unitRadian : (value * unitRadian);
  27766. }
  27767. else {
  27768. angle = PI2$4 / validDataCount;
  27769. }
  27770. if (angle < minAngle) {
  27771. angle = minAngle;
  27772. restAngle -= minAngle;
  27773. }
  27774. else {
  27775. valueSumLargerThanMinAngle += value;
  27776. }
  27777. var endAngle = currentAngle + dir * angle;
  27778. data.setItemLayout(idx, {
  27779. angle: angle,
  27780. startAngle: currentAngle,
  27781. endAngle: endAngle,
  27782. clockwise: clockwise,
  27783. cx: cx,
  27784. cy: cy,
  27785. r0: r0,
  27786. r: roseType
  27787. ? linearMap(value, extent, [r0, r])
  27788. : r
  27789. });
  27790. currentAngle = endAngle;
  27791. }, true);
  27792. // Some sector is constrained by minAngle
  27793. // Rest sectors needs recalculate angle
  27794. if (restAngle < PI2$4 && validDataCount) {
  27795. // Average the angle if rest angle is not enough after all angles is
  27796. // Constrained by minAngle
  27797. if (restAngle <= 1e-3) {
  27798. var angle = PI2$4 / validDataCount;
  27799. data.each('value', function (value, idx) {
  27800. if (!isNaN(value)) {
  27801. var layout = data.getItemLayout(idx);
  27802. layout.angle = angle;
  27803. layout.startAngle = startAngle + dir * idx * angle;
  27804. layout.endAngle = startAngle + dir * (idx + 1) * angle;
  27805. }
  27806. });
  27807. }
  27808. else {
  27809. unitRadian = restAngle / valueSumLargerThanMinAngle;
  27810. currentAngle = startAngle;
  27811. data.each('value', function (value, idx) {
  27812. if (!isNaN(value)) {
  27813. var layout = data.getItemLayout(idx);
  27814. var angle = layout.angle === minAngle
  27815. ? minAngle : value * unitRadian;
  27816. layout.startAngle = currentAngle;
  27817. layout.endAngle = currentAngle + dir * angle;
  27818. currentAngle += dir * angle;
  27819. }
  27820. });
  27821. }
  27822. }
  27823. labelLayout(seriesModel, r, width, height);
  27824. });
  27825. };
  27826. var dataFilter = function (seriesType, ecModel) {
  27827. var legendModels = ecModel.findComponents({
  27828. mainType: 'legend'
  27829. });
  27830. if (!legendModels || !legendModels.length) {
  27831. return;
  27832. }
  27833. ecModel.eachSeriesByType(seriesType, function (series) {
  27834. var data = series.getData();
  27835. data.filterSelf(function (idx) {
  27836. var name = data.getName(idx);
  27837. // If in any legend component the status is not selected.
  27838. for (var i = 0; i < legendModels.length; i++) {
  27839. if (!legendModels[i].isSelected(name)) {
  27840. return false;
  27841. }
  27842. }
  27843. return true;
  27844. }, this);
  27845. }, this);
  27846. };
  27847. createDataSelectAction('pie', [{
  27848. type: 'pieToggleSelect',
  27849. event: 'pieselectchanged',
  27850. method: 'toggleSelected'
  27851. }, {
  27852. type: 'pieSelect',
  27853. event: 'pieselected',
  27854. method: 'select'
  27855. }, {
  27856. type: 'pieUnSelect',
  27857. event: 'pieunselected',
  27858. method: 'unSelect'
  27859. }]);
  27860. registerVisual(curry(dataColor, 'pie'));
  27861. registerLayout(curry(pieLayout, 'pie'));
  27862. registerProcessor(curry(dataFilter, 'pie'));
  27863. exports.version = version;
  27864. exports.dependencies = dependencies;
  27865. exports.PRIORITY = PRIORITY;
  27866. exports.init = init;
  27867. exports.connect = connect;
  27868. exports.disConnect = disConnect;
  27869. exports.disconnect = disconnect;
  27870. exports.dispose = dispose;
  27871. exports.getInstanceByDom = getInstanceByDom;
  27872. exports.getInstanceById = getInstanceById;
  27873. exports.registerTheme = registerTheme;
  27874. exports.registerPreprocessor = registerPreprocessor;
  27875. exports.registerProcessor = registerProcessor;
  27876. exports.registerPostUpdate = registerPostUpdate;
  27877. exports.registerAction = registerAction;
  27878. exports.registerCoordinateSystem = registerCoordinateSystem;
  27879. exports.getCoordinateSystemDimensions = getCoordinateSystemDimensions;
  27880. exports.registerLayout = registerLayout;
  27881. exports.registerVisual = registerVisual;
  27882. exports.registerLoading = registerLoading;
  27883. exports.extendComponentModel = extendComponentModel;
  27884. exports.extendComponentView = extendComponentView;
  27885. exports.extendSeriesModel = extendSeriesModel;
  27886. exports.extendChartView = extendChartView;
  27887. exports.setCanvasCreator = setCanvasCreator;
  27888. exports.registerMap = registerMap;
  27889. exports.getMap = getMap;
  27890. exports.dataTool = dataTool;
  27891. })));