umychart.extendchart.wechat.js 74 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165
  1. /*
  2. copyright (c) 2018 jones
  3. http://www.apache.org/licenses/LICENSE-2.0
  4. 开源项目 https://github.com/jones2000/HQChart
  5. jones_2000@163.com
  6. 图形扩展画法
  7. */
  8. //日志
  9. import { JSConsole } from "./umychart.console.wechat.js"
  10. //行情数据结构体 及涉及到的行情算法(复权,周期等)
  11. import
  12. {
  13. ChartData, HistoryData,
  14. SingleData, MinuteData,
  15. Guid,
  16. ToFixedPoint,
  17. ToFixedRect,
  18. JSCHART_EVENT_ID,
  19. JSCHART_BUTTON_ID,
  20. CloneData,
  21. } from "./umychart.data.wechat.js";
  22. import
  23. {
  24. JSCommonCoordinateData,
  25. MARKET_SUFFIX_NAME
  26. } from "./umychart.coordinatedata.wechat.js";
  27. import
  28. {
  29. g_JSChartResource,
  30. JSCHART_LANGUAGE_ID,
  31. g_JSChartLocalization,
  32. JSChartResource,
  33. } from './umychart.resource.wechat.js'
  34. import
  35. {
  36. IFrameSplitOperator,
  37. } from './umychart.framesplit.wechat.js'
  38. import
  39. {
  40. IChartDrawPicture,
  41. } from "./umychart.ChartDrawPicture.wechart.js"
  42. function IExtendChartPainting()
  43. {
  44. this.Canvas; //画布
  45. this.ChartBorder; //边框信息
  46. this.ChartFrame; //框架画法
  47. this.Name; //名称
  48. this.Data; // = new ChartData(); //数据区
  49. this.ClassName = 'IExtendChartPainting';
  50. this.IsDynamic = false;
  51. this.IsEraseBG = false; //是否每次画的时候需要擦除K线图背景
  52. this.IsAnimation=false;
  53. this.DrawAfterTitle = false; //是否在动态标题画完以后再画,防止动态标题覆盖
  54. this.ID=Guid(),
  55. //上下左右间距
  56. this.Left = 5;
  57. this.Right = 5;
  58. this.Top = 5;
  59. this.Bottom = 5;
  60. this.Draw = function () { } //画图接口
  61. this.SetOption = function (option) { } //设置参数接口
  62. this.SetFillStyle=function(color, x0, y0, x1, y1)
  63. {
  64. if (Array.isArray(color))
  65. {
  66. let gradient = this.Canvas.createLinearGradient(x0, y0, x1, y1);
  67. var offset=1/(color.length-1);
  68. for(var i=0; i<color.length; ++i)
  69. {
  70. var value=i*offset;
  71. gradient.addColorStop(value, color[i]);
  72. }
  73. this.Canvas.fillStyle=gradient;
  74. }
  75. else
  76. {
  77. this.Canvas.fillStyle=color;
  78. }
  79. }
  80. }
  81. //K线Tooltip, 显示在左边或右边
  82. function KLineTooltipPaint()
  83. {
  84. this.newMethod = IExtendChartPainting; //派生
  85. this.newMethod();
  86. delete this.newMethod;
  87. this.IsDynamic = true;
  88. this.IsEraseBG = true;
  89. this.DrawAfterTitle = true;
  90. this.ClassName = 'KLineTooltipPaint';
  91. this.LatestPoint; //手势位置
  92. this.ShowPosition=0; //显示位置 0=左 1=右
  93. this.BorderColor = g_JSChartResource.TooltipPaint.BorderColor; //边框颜色
  94. this.BGColor = g_JSChartResource.TooltipPaint.BGColor; //背景色
  95. this.TitleColor = g_JSChartResource.TooltipPaint.TitleColor; //标题颜色
  96. this.Font = [g_JSChartResource.TooltipPaint.TitleFont];
  97. this.Mergin={ Left:2, Top:3, Bottom:5, Right:5 };
  98. this.ExtendLineWidth=5;
  99. this.Width = 50;
  100. this.Height = 100;
  101. this.LineHeight = 15; //行高
  102. this.LineSpace=2; //行间距
  103. this.Left = 1;
  104. this.Top = 0;
  105. this.HQChart;
  106. this.KLineTitlePaint;
  107. this.IsHScreen = false; //是否横屏
  108. this.LanguageID = JSCHART_LANGUAGE_ID.LANGUAGE_CHINESE_ID;
  109. this.GetVolUnit=function()
  110. {
  111. var upperSymbol;
  112. if (this.HQChart.Symbol) upperSymbol=this.HQChart.Symbol.toUpperCase();
  113. var unit=MARKET_SUFFIX_NAME.GetVolUnit(upperSymbol);
  114. return unit;
  115. }
  116. this.GetLeft = function ()
  117. {
  118. if (this.IsHScreen)
  119. {
  120. return this.ChartBorder.GetRightEx()-this.Height-this.Top;
  121. }
  122. else
  123. {
  124. if (this.ShowPosition==0)
  125. return this.ChartBorder.GetLeft()+this.Left;
  126. else
  127. return this.ChartBorder.GetRight()-this.Width-this.Left;
  128. }
  129. }
  130. this.GetTop = function ()
  131. {
  132. if (this.IsHScreen)
  133. {
  134. if (this.ShowPosition==0)
  135. return this.ChartBorder.GetTop()+this.Left;
  136. else
  137. return this.ChartBorder.GetBottom()-this.Width-this.Left;
  138. }
  139. else
  140. {
  141. return this.ChartBorder.GetTopEx()+this.Top;
  142. }
  143. }
  144. //是否显示
  145. this.IsEnableDraw=function()
  146. {
  147. if (!this.HQChart || !this.HQChart.TitlePaint || !this.HQChart.TitlePaint[0]) return false;
  148. if (this.HQChart.EnableClickModel)
  149. {
  150. if (this.HQChart.ClickModel.IsShowCorssCursor===false) return false;
  151. }
  152. else if (!this.HQChart.IsOnTouch)
  153. {
  154. return false;
  155. }
  156. return true;
  157. }
  158. this.Draw = function ()
  159. {
  160. if (!this.IsEnableDraw()) return;
  161. this.IsHScreen=this.ChartFrame.IsHScreen===true;
  162. this.KLineTitlePaint = this.HQChart.TitlePaint[0];
  163. var klineData = this.KLineTitlePaint.GetCurrentKLineData();
  164. if (!klineData) return;
  165. var titleData=this.GetFormatTitle({Data:klineData});
  166. if (!titleData || !IFrameSplitOperator.IsNonEmptyArray(titleData.AryText)) return;
  167. this.CalculateTooltipSize(titleData);
  168. this.CalculateShowPosition();
  169. this.DrawBG();
  170. this.DrawTooltipData(titleData);
  171. this.DrawBorder();
  172. }
  173. //[{ Text:, Color, Title:, TitleColor, }]
  174. this.GetFormatTitle=function(data)
  175. {
  176. if (!data || !data.Data) return;
  177. var item=data.Data;
  178. var upperSymbol;
  179. if (this.HQChart.Symbol) upperSymbol = this.HQChart.Symbol.toUpperCase();
  180. var defaultfloatPrecision = JSCommonCoordinateData.GetfloatPrecision(this.HQChart.Symbol);//价格小数位数
  181. var unit=this.GetVolUnit();
  182. var aryText=[];
  183. var result={ AryText:aryText };
  184. var text, title, color;
  185. text=IFrameSplitOperator.FormatDateString(item.Date);
  186. aryText.push({ Text:text, Color:this.TitleColor });
  187. var period = this.HQChart.Period;
  188. if (ChartData.IsMinutePeriod(period, true) && IFrameSplitOperator.IsNumber(item.Time))
  189. {
  190. text = this.HQChart.FormatTimeString(item.Time);
  191. aryText.push({ Text:text, Color:this.TitleColor });
  192. }
  193. else if (ChartData.IsSecondPeriod(period) && IFrameSplitOperator.IsNumber(item.Time))
  194. {
  195. text = this.HQChart.FormatTimeString(item.Time,"HH:MM:SS");
  196. aryText.push({ Text:text, Color:this.TitleColor });
  197. }
  198. if (IFrameSplitOperator.IsNumber(item.Open)) //开
  199. {
  200. title = g_JSChartLocalization.GetText('Tooltip-Open', this.LanguageID);
  201. var color = this.KLineTitlePaint.GetColor(item.Open, item.YClose);
  202. text = item.Open.toFixed(defaultfloatPrecision);
  203. aryText.push({Title:title, TitleColor:this.TitleColor, Text:text, Color:color });
  204. }
  205. if (IFrameSplitOperator.IsNumber(item.High)) //高
  206. {
  207. title=g_JSChartLocalization.GetText('Tooltip-High',this.LanguageID);
  208. color=this.KLineTitlePaint.GetColor(item.High,item.YClose);
  209. text=item.High.toFixed(defaultfloatPrecision);
  210. aryText.push({Title:title, TitleColor:this.TitleColor, Text:text, Color:color });
  211. }
  212. if (IFrameSplitOperator.IsNumber(item.Low)) //低
  213. {
  214. title=g_JSChartLocalization.GetText('Tooltip-Low',this.LanguageID);
  215. color=this.KLineTitlePaint.GetColor(item.Low,item.YClose);
  216. text=item.Low.toFixed(defaultfloatPrecision);
  217. aryText.push({Title:title, TitleColor:this.TitleColor, Text:text, Color:color });
  218. }
  219. if (IFrameSplitOperator.IsNumber(item.Close)) //收
  220. {
  221. title=g_JSChartLocalization.GetText('Tooltip-Close',this.LanguageID);
  222. color=this.KLineTitlePaint.GetColor(item.Close,item.YClose);
  223. text=item.Close.toFixed(defaultfloatPrecision);
  224. aryText.push({Title:title, TitleColor:this.TitleColor, Text:text, Color:color });
  225. }
  226. //涨幅
  227. title=g_JSChartLocalization.GetText('Tooltip-Increase',this.LanguageID);
  228. if (item.YClose>0)
  229. {
  230. var value = (item.Close - item.YClose) / item.YClose * 100;
  231. color = this.KLineTitlePaint.GetColor(value, 0);
  232. text = value.toFixed(2) + '%';
  233. }
  234. else
  235. {
  236. text='--.--';
  237. color = this.KLineTitlePaint.GetColor(0, 0);
  238. }
  239. aryText.push({Title:title, TitleColor:this.TitleColor, Text:text, Color:color });
  240. if (IFrameSplitOperator.IsNumber(item.Vol))
  241. {
  242. title = g_JSChartLocalization.GetText('Tooltip-Vol', this.LanguageID);
  243. text = this.HQChart.FormatValueString(item.Vol/unit, 2, this.LanguageID);
  244. aryText.push({Title:title, TitleColor:this.TitleColor, Text:text, Color:this.TitleColor });
  245. }
  246. if (IFrameSplitOperator.IsNumber(item.Amount))
  247. {
  248. title = g_JSChartLocalization.GetText('Tooltip-Amount',this.LanguageID);
  249. text = this.HQChart.FormatValueString(item.Amount, 2, this.LanguageID);
  250. aryText.push({Title:title, TitleColor:this.TitleColor, Text:text, Color:this.TitleColor });
  251. }
  252. //换手率
  253. if (IFrameSplitOperator.IsNumber(item.FlowCapital) )
  254. {
  255. var text="--.--%";
  256. title=g_JSChartLocalization.GetText('Tooltip-Exchange',this.LanguageID);
  257. if (item.FlowCapital!=0)
  258. {
  259. var value=item.Vol/item.FlowCapital*100;
  260. text=value.toFixed(2)+'%';
  261. }
  262. aryText.push({Title:title, TitleColor:this.TitleColor, Text:text, Color:this.TitleColor });
  263. }
  264. //持仓量
  265. if (MARKET_SUFFIX_NAME.IsChinaFutures(upperSymbol) && IFrameSplitOperator.IsNumber(item.Position))
  266. {
  267. title = g_JSChartLocalization.GetText('Tooltip-Position', this.LanguageID);
  268. text = IFrameSplitOperator.FormatValueString(item.Position, 2, this.LanguageID);
  269. aryText.push({Title:title, TitleColor:this.TitleColor, Text:text, Color:this.TitleColor });
  270. }
  271. return result;
  272. }
  273. this.CalculateTooltipSize=function(titleData)
  274. {
  275. this.Canvas.font=this.Font[0];
  276. this.LineHeight=this.Canvas.measureText("擎").width;
  277. var height=0;
  278. var maxTitleWidth=0, maxTextWidth=0, maxLineWidth=0;
  279. for(var i=0; i<titleData.AryText.length; ++i)
  280. {
  281. var item=titleData.AryText[i];
  282. if (height>0) height+=this.LineSpace;
  283. var lineWidth=0;
  284. if (item.Title)
  285. {
  286. var textWidth=this.Canvas.measureText(item.Title).width+2;
  287. if (maxTitleWidth<textWidth) maxTitleWidth=textWidth;
  288. lineWidth+=textWidth;
  289. }
  290. if (item.Text)
  291. {
  292. var textWidth=this.Canvas.measureText(item.Text).width+2;
  293. if (maxTextWidth<textWidth) maxTextWidth=textWidth;
  294. lineWidth+=textWidth;
  295. }
  296. if (IFrameSplitOperator.IsNumber(item.Space)) lineWidth+=item.Space;
  297. if (maxLineWidth<lineWidth) maxLineWidth=lineWidth;
  298. height+=this.LineHeight;
  299. }
  300. this.Height=height+(this.Mergin.Top+this.Mergin.Bottom);
  301. this.Width=(maxLineWidth)+(this.Mergin.Left+this.Mergin.Right)+this.ExtendLineWidth;
  302. return { Height:this.Height, Width:this.Width, MaxTitleWidth:maxTitleWidth, MaxTextWidth:maxTextWidth };
  303. }
  304. //判断显示位置
  305. this.CalculateShowPosition=function()
  306. {
  307. this.ShowPosition=0;
  308. if (!this.LatestPoint) return;
  309. if(this.IsHScreen)
  310. {
  311. var top=this.ChartBorder.GetTop();
  312. var height=this.ChartBorder.GetHeight();
  313. var yCenter=top+height/2;
  314. if (this.LatestPoint.Y<yCenter) this.ShowPosition=1;
  315. }
  316. else
  317. {
  318. var left=this.ChartBorder.GetLeft();
  319. var width=this.ChartBorder.GetWidth();
  320. var xCenter=left+width/2;
  321. if (this.LatestPoint.X<xCenter) this.ShowPosition=1;
  322. }
  323. }
  324. this.DrawBorder = function ()
  325. {
  326. var isHScreen = (this.ChartFrame.IsHScreen === true);
  327. var left = this.GetLeft();
  328. var top = this.GetTop();
  329. this.Canvas.strokeStyle = this.BorderColor;
  330. if (isHScreen)
  331. {
  332. this.Canvas.strokeRect(this.HQChart.ToFixedPoint(left), this.HQChart.ToFixedPoint(top),
  333. this.HQChart.ToFixedRect(this.Height), this.HQChart.ToFixedRect(this.Width));
  334. }
  335. else
  336. {
  337. this.Canvas.strokeRect(this.HQChart.ToFixedPoint(left), this.HQChart.ToFixedPoint(top),
  338. this.HQChart.ToFixedRect(this.Width), this.HQChart.ToFixedRect(this.Height));
  339. }
  340. }
  341. this.DrawBG = function ()
  342. {
  343. var isHScreen = (this.ChartFrame.IsHScreen === true);
  344. var left = this.GetLeft();
  345. var top = this.GetTop();
  346. this.Canvas.fillStyle = this.BGColor;
  347. if (isHScreen) this.Canvas.fillRect(left, top, this.Height, this.Width);
  348. else this.Canvas.fillRect(left, top, this.Width, this.Height);
  349. }
  350. this.DrawTooltipData = function (titleData)
  351. {
  352. //console.log('[KLineTooltipPaint::DrawKLineData] ', item);
  353. var left = this.GetLeft();
  354. var top = this.GetTop();
  355. if (this.IsHScreen)
  356. {
  357. this.Canvas.save();
  358. var x = this.GetLeft() + this.Height, y = this.GetTop();
  359. this.Canvas.translate(x, y);
  360. this.Canvas.rotate(90 * Math.PI / 180);
  361. //x, y 作为原点
  362. left =0;
  363. top = 0;
  364. }
  365. this.Canvas.textBaseline="top";
  366. var right=left+this.Width-this.Mergin.Right;
  367. left+=this.Mergin.Left;
  368. top+=this.Mergin.Top;
  369. for(var i=0; i<titleData.AryText.length; ++i)
  370. {
  371. var item=titleData.AryText[i];
  372. var titleWidth=0;
  373. if (item.Title)
  374. {
  375. this.Canvas.textAlign="left";
  376. this.Canvas.fillStyle=item.TitleColor;
  377. this.Canvas.fillText(item.Title,left,top);
  378. var titleWidth=this.Canvas.measureText(item.Title).width+2;
  379. }
  380. if (item.Text)
  381. {
  382. if (item.TextAlign==1) //1=左对齐 0=右对齐
  383. {
  384. var yText=left+titleWidth+2;
  385. if (IFrameSplitOperator.IsNumber(item.Space)) yText+=item.Space; //标题和数据内容间距
  386. this.Canvas.textAlign="left";
  387. this.Canvas.fillStyle=item.Color;
  388. this.Canvas.fillText(item.Text,yText,top);
  389. }
  390. else
  391. {
  392. this.Canvas.textAlign="right";
  393. this.Canvas.fillStyle=item.Color;
  394. this.Canvas.fillText(item.Text,right,top);
  395. }
  396. }
  397. top+=this.LineHeight+this.LineSpace;
  398. }
  399. if (this.IsHScreen) this.Canvas.restore();
  400. }
  401. //设置参数接口
  402. this.SetOption = function (option)
  403. {
  404. if (!option) return;
  405. if (option.LineHeight > 0) this.LineHeight = option.LineHeight;
  406. if (option.BGColor) this.BGColor = option.BGColor;
  407. if (option.LanguageID > 0) this.LanguageID = option.LanguageID;
  408. if (IFrameSplitOperator.IsNumber(option.LineSpace)) this.LineSpace=option.LineSpace;
  409. if (IFrameSplitOperator.IsNumber(option.ExtendLineWidth)) this.ExtendLineWidth=option.ExtendLineWidth;
  410. JSChartResource.CopyMargin(this.Mergin, option.Mergin);
  411. }
  412. }
  413. function MinuteTooltipPaint()
  414. {
  415. this.newMethod = KLineTooltipPaint; //派生
  416. this.newMethod();
  417. delete this.newMethod;
  418. this.ClassName = 'MinuteTooltipPaint';
  419. this.IsShowAveragePrice=true;
  420. this.GetTop=function()
  421. {
  422. if (this.IsHScreen)
  423. {
  424. if (this.ShowPosition==0)
  425. return this.ChartBorder.GetTop()+this.Left;
  426. else
  427. return this.ChartBorder.GetBottom()-this.Width-this.Left;
  428. }
  429. else
  430. {
  431. return this.ChartBorder.GetTop()+this.Top;
  432. }
  433. }
  434. this.GetLeft=function()
  435. {
  436. if (this.IsHScreen)
  437. {
  438. return this.ChartBorder.GetRight()-this.Height-this.Top;
  439. }
  440. else
  441. {
  442. if (this.ShowPosition==0)
  443. return this.ChartBorder.GetLeft()+this.Left;
  444. else
  445. return this.ChartBorder.GetRight()-this.Width-this.Left;
  446. }
  447. }
  448. this.GetFormatTitle=function(data)
  449. {
  450. if (!data || !data.Data) return;
  451. var item=data.Data;
  452. var upperSymbol;
  453. if (this.HQChart.Symbol) upperSymbol=this.HQChart.Symbol.toUpperCase();
  454. var defaultfloatPrecision = JSCommonCoordinateData.GetfloatPrecision(this.HQChart.Symbol);//价格小数位数
  455. var isFutures=MARKET_SUFFIX_NAME.IsFutures(upperSymbol); //期货
  456. var unit=this.GetVolUnit();
  457. this.YClose = this.KLineTitlePaint.YClose;
  458. this.YClose=item.YClose;
  459. if (isFutures && IFrameSplitOperator.IsNumber(item.YClearing)) this.YClose=item.YClearing;
  460. var aryText=[];
  461. var result={ AryText:aryText };
  462. var text, title, color, value;
  463. if (IFrameSplitOperator.IsNumber(item.Date))
  464. {
  465. text=IFrameSplitOperator.FormatDateString(item.Date);
  466. aryText.push({ Text:text, Color:this.TitleColor });
  467. }
  468. if (IFrameSplitOperator.IsNumber(item.Time))
  469. {
  470. text=IFrameSplitOperator.FormatTimeString(item.Time);
  471. aryText.push({ Text:text, Color:this.TitleColor });
  472. }
  473. if (IFrameSplitOperator.IsNumber(item.Close)) //最新
  474. {
  475. title = g_JSChartLocalization.GetText('Tooltip-Price', this.LanguageID);
  476. color = this.KLineTitlePaint.GetColor(item.Close, this.YClose);
  477. text = item.Close.toFixed(defaultfloatPrecision);
  478. aryText.push({Title:title, TitleColor:this.TitleColor, Text:text, Color:color });
  479. }
  480. if (IFrameSplitOperator.IsNumber(item.AvPrice) && this.IsShowAveragePrice==true) //均价
  481. {
  482. title = g_JSChartLocalization.GetText('Tooltip-AvPrice', this.LanguageID);
  483. color = this.KLineTitlePaint.GetColor(item.AvPrice, this.YClose);
  484. text = item.AvPrice.toFixed(defaultfloatPrecision);
  485. aryText.push({Title:title, TitleColor:this.TitleColor, Text:text, Color:color });
  486. }
  487. if (IFrameSplitOperator.IsNumber(item.Close) && IFrameSplitOperator.IsNumber(this.YClose)) //涨幅
  488. {
  489. title = g_JSChartLocalization.GetText('Tooltip-Increase', this.LanguageID);
  490. value = (item.Close - this.YClose) / this.YClose * 100;
  491. color = this.KLineTitlePaint.GetColor(value, 0);
  492. text = value.toFixed(2) + '%';
  493. if (this.YClose===0)
  494. {
  495. text="--.--";
  496. color=this.TitleColor;
  497. }
  498. aryText.push({Title:title, TitleColor:this.TitleColor, Text:text, Color:color });
  499. }
  500. if (IFrameSplitOperator.IsNumber(item.Vol))
  501. {
  502. title = g_JSChartLocalization.GetText('Tooltip-Vol', this.LanguageID);
  503. text = this.HQChart.FormatValueString(item.Vol/unit, 2, this.LanguageID);
  504. aryText.push({Title:title, TitleColor:this.TitleColor, Text:text, Color:this.TitleColor });
  505. }
  506. if (IFrameSplitOperator.IsNumber(item.Amount))
  507. {
  508. title = g_JSChartLocalization.GetText('Tooltip-Amount', this.LanguageID);
  509. text = this.HQChart.FormatValueString(item.Amount, 2, this.LanguageID);
  510. aryText.push({Title:title, TitleColor:this.TitleColor, Text:text, Color:this.TitleColor });
  511. }
  512. if (MARKET_SUFFIX_NAME.IsChinaFutures(upperSymbol) && IFrameSplitOperator.IsNumber(item.Position)) //持仓量
  513. {
  514. title = g_JSChartLocalization.GetText('Tooltip-Position', this.LanguageID);
  515. text = IFrameSplitOperator.FormatValueString(item.Position, 2, this.LanguageID);
  516. aryText.push({Title:title, TitleColor:this.TitleColor, Text:text, Color:this.TitleColor });
  517. }
  518. return result;
  519. }
  520. /*
  521. this.DrawTooltipData = function (item)
  522. {
  523. //console.log('[KLineTooltipPaint::DrawKLineData] ', item);
  524. var defaultfloatPrecision = JSCommonCoordinateData.GetfloatPrecision(this.HQChart.Symbol);//价格小数位数
  525. var left = this.GetLeft() + 2;
  526. var top = this.GetTop() + 3;
  527. this.YClose = this.KLineTitlePaint.YClose;
  528. if (this.IsHScreen)
  529. {
  530. this.Canvas.save();
  531. var x = this.GetLeft() + this.Height, y = this.GetTop();
  532. this.Canvas.translate(x, y);
  533. this.Canvas.rotate(90 * Math.PI / 180);
  534. //x, y 作为原点
  535. left = 2;
  536. top = 3;
  537. }
  538. this.Canvas.textBaseline = "top";
  539. this.Canvas.textAlign = "left";
  540. this.Canvas.font = this.Font[0];
  541. var labelWidth = this.Canvas.measureText('擎: ').width;
  542. var aryDateTime = item.DateTime.split(' ');
  543. if (aryDateTime && aryDateTime.length == 2)
  544. {
  545. var text = this.HQChart.FormatDateString(aryDateTime[0]);
  546. this.Canvas.fillStyle = this.TitleColor;
  547. this.Canvas.fillText(text, left, top);
  548. top += this.LineHeight;
  549. text = this.HQChart.FormatTimeString(aryDateTime[1]);
  550. this.Canvas.fillText(text, left, top);
  551. }
  552. top += this.LineHeight;
  553. this.Canvas.fillStyle = this.TitleColor;
  554. text = g_JSChartLocalization.GetText('Tooltip-Price', this.LanguageID);
  555. this.Canvas.fillText(text, left, top);
  556. var color = this.KLineTitlePaint.GetColor(item.Close, this.YClose);
  557. text = item.Close.toFixed(defaultfloatPrecision);
  558. this.Canvas.fillStyle = color;
  559. this.Canvas.fillText(text, left + labelWidth, top);
  560. if (IFrameSplitOperator.IsNumber(item.AvPrice) && this.IsShowAveragePrice==true)
  561. {
  562. top += this.LineHeight;
  563. this.Canvas.fillStyle = this.TitleColor;
  564. text = g_JSChartLocalization.GetText('Tooltip-AvPrice', this.LanguageID);
  565. this.Canvas.fillText(text, left, top);
  566. var color = this.KLineTitlePaint.GetColor(item.AvPrice, this.YClose);
  567. var text = item.AvPrice.toFixed(defaultfloatPrecision);
  568. this.Canvas.fillStyle = color;
  569. this.Canvas.fillText(text, left + labelWidth, top);
  570. }
  571. top += this.LineHeight;
  572. this.Canvas.fillStyle = this.TitleColor;
  573. text = g_JSChartLocalization.GetText('Tooltip-Increase', this.LanguageID);
  574. this.Canvas.fillText(text, left, top);
  575. var value = (item.Close - this.YClose) / this.YClose * 100;
  576. var color = this.KLineTitlePaint.GetColor(value, 0);
  577. var text = value.toFixed(2) + '%';
  578. this.Canvas.fillStyle = color;
  579. this.Canvas.fillText(text, left + labelWidth, top);
  580. if (IFrameSplitOperator.IsNumber(item.Vol))
  581. {
  582. this.Canvas.fillStyle = this.TitleColor;
  583. top += this.LineHeight;
  584. text = g_JSChartLocalization.GetText('Tooltip-Vol', this.LanguageID);
  585. this.Canvas.fillText(text, left, top);
  586. var text = this.HQChart.FormatValueString(item.Vol, 2, this.LanguageID);
  587. this.Canvas.fillText(text, left + labelWidth, top);
  588. }
  589. if (IFrameSplitOperator.IsNumber(item.Amount))
  590. {
  591. top += this.LineHeight;
  592. text = g_JSChartLocalization.GetText('Tooltip-Amount', this.LanguageID);
  593. this.Canvas.fillText(text, left, top);
  594. var text = this.HQChart.FormatValueString(item.Amount, 2, this.LanguageID);
  595. this.Canvas.fillText(text, left + labelWidth, top);
  596. }
  597. //持仓量
  598. var upperSymbol;
  599. if (this.HQChart.Symbol) upperSymbol = this.HQChart.Symbol.toUpperCase();
  600. if (MARKET_SUFFIX_NAME.IsChinaFutures(upperSymbol) && IFrameSplitOperator.IsNumber(item.Position))
  601. {
  602. this.Canvas.fillStyle = this.TitleColor;
  603. top += this.LineHeight;
  604. text = g_JSChartLocalization.GetText('Tooltip-Position', this.LanguageID);
  605. this.Canvas.fillText(text, left, top);
  606. var text = IFrameSplitOperator.FormatValueString(item.Position, 2, this.LanguageID);
  607. this.Canvas.fillText(text, left + labelWidth, top);
  608. }
  609. if (this.IsHScreen) this.Canvas.restore();
  610. }
  611. */
  612. }
  613. //////////////////////////////////////////////////////////////////////////////
  614. // 弹幕
  615. //弹幕数据 { X:X偏移, Y:Y偏移, Text:内容, Color:颜色 }
  616. function BarrageList()
  617. {
  618. this.PlayList = []; //正在播放队列
  619. this.Cache = []; //没有播放的弹幕数据
  620. this.MinLineHeight = 40;
  621. this.Height; //高度
  622. this.Step = 1;
  623. //{Canves:画布, Right:右边坐标, Left:左边坐标, Font:默认字体 }
  624. this.GetPlayList = function (obj)
  625. {
  626. var canves = obj.Canves;
  627. var right = obj.Right;
  628. var left = obj.Left;
  629. var width = right - left;
  630. var isMoveStep = obj.IsMoveStep;
  631. var list = [];
  632. var yOffset = 0;
  633. for (var i = 0; i < this.PlayList.length; ++i)
  634. {
  635. var ary = this.PlayList[i];
  636. var lineHeight = this.MinLineHeight;
  637. if (ary.Height > this.MinLineHeight) lineHeight = ary.Height;
  638. var bAddNewItem = true; //是否需要加入新弹幕
  639. var bRemoveFirst = false; //是否删除第1个数据
  640. for (var j = 0; j < ary.Data.length; ++j)
  641. {
  642. var item = ary.Data[j];
  643. var playItem = { X: item.X, Y: yOffset, Text: item.Text, Color: item.Color, Height: lineHeight, Font: item.Font, Info: item.Info };
  644. list.push(playItem);
  645. if (!isMoveStep) continue;
  646. if (j == ary.Data.length - 1 && this.Cache.length > 0) //最后一个数据了 判断是否需要增加弹幕
  647. {
  648. bAddNewItem = false;
  649. if (!item.TextWidth)
  650. {
  651. if (item.Font && item.Font.Name) canves.font = item.Font.Name;
  652. else canves.font = obj.Font;
  653. item.TextWidth = canves.measureText(playItem.Text + '擎擎').width;
  654. }
  655. if (item.X >= item.TextWidth)
  656. bAddNewItem = true;
  657. }
  658. else if (j == 0)
  659. {
  660. bRemoveFirst = false;
  661. if (!item.TextWidth)
  662. {
  663. if (item.Font && item.Font.Name) canves.font = item.Font.Name;
  664. else canves.font = obj.Font;
  665. item.TextWidth = canves.measureText(playItem.Text + '擎擎').width;
  666. }
  667. if (item.X > width + item.TextWidth) bRemoveFirst = true;
  668. }
  669. item.X += this.Step;
  670. }
  671. if (isMoveStep && bAddNewItem && this.Cache.length > 0) //最后一个数据了 判断是否需要增加弹幕
  672. {
  673. var cacheItem = this.Cache.shift();
  674. var newItem = { X: 0, Text: cacheItem.Text, Color: cacheItem.Color, Font: cacheItem.Font, Info: cacheItem.Info };
  675. ary.Data.push(newItem);
  676. }
  677. if (isMoveStep && bRemoveFirst && ary.Data.length > 0)
  678. {
  679. var removeItem = ary.Data.shift();
  680. this.OnItemPlayEnd(obj.HQChart, removeItem);
  681. }
  682. yOffset += lineHeight;
  683. }
  684. return list;
  685. }
  686. //根据高度计算播放队列个数
  687. this.CacluatePlayLine = function (height)
  688. {
  689. this.Height = height;
  690. var lineCount = parseInt(height / this.MinLineHeight);
  691. if (this.PlayList.length < lineCount)
  692. {
  693. var addCount = lineCount - this.PlayList.length;
  694. for (var i = 0; i < addCount; ++i)
  695. {
  696. this.PlayList.push({ Data: [] });
  697. }
  698. }
  699. else if (this.PlayList.length > lineCount)
  700. {
  701. var removeCount = this.PlayList.length - lineCount;
  702. for (var i = 0; i < removeCount; ++i)
  703. {
  704. var ary = this.PlayList.pop();
  705. for (var j = 0; j < ary.Data.length; ++j)
  706. {
  707. var item = ary.Data[j];
  708. var cacheItem = { Text: item.Text, Color: item.Color, Font: item.Font, Info: item.Info };
  709. this.Cache.unshift(cacheItem);
  710. }
  711. }
  712. }
  713. JSConsole.Chart.Log(`[BarrageList::CacluatePlayLine] LineCount=${this.PlayList.length} Height=${this.Height}`)
  714. }
  715. //添加弹幕
  716. this.AddBarrage = function (barrageData)
  717. {
  718. for (var i in barrageData) {
  719. var item = barrageData[i];
  720. this.Cache.push(item);
  721. }
  722. }
  723. this.OnItemPlayEnd = function (hqChart, item) //单挑弹幕播放完毕
  724. {
  725. //监听事件
  726. var event = hqChart.GetBarrageEvent();
  727. if (!event || !event.Callback) return;
  728. event.Callback(event, item, this);
  729. }
  730. this.Count = function () { return this.Cache.length; } //未播放的弹幕个数
  731. }
  732. //背景图 支持横屏
  733. function BackgroundPaint()
  734. {
  735. this.newMethod = IExtendChartPainting; //派生
  736. this.newMethod();
  737. delete this.newMethod;
  738. this.ClassName = 'BackgroundPaint';
  739. this.IsDynamic = false;
  740. this.IsCallbackDraw = true; //在回调函数里绘制, 不在Draw()中绘制
  741. this.IsDrawAllFrame=false; //全部的指标框都画
  742. this.SetDrawFrameID=new Set([0]); //指定几个指标框画
  743. this.FrameID = 0;
  744. this.Data; //背景数据[ { Start:{ Date:, Time: }, End:{ Date:, Time }, Color:['rgb(44,55,255)','rgb(200,55,255)'] }]
  745. this.IsShow=true;
  746. this.SubFrame=null;
  747. this.ID = Guid(); //唯一的ID
  748. this.HQChart;
  749. this.SetOption = function (option) //设置
  750. {
  751. if (!option) return;
  752. if (IFrameSplitOperator.IsNumber(option.FrameID)) this.FrameID=option.FrameID;
  753. if (IFrameSplitOperator.IsObjectExist(option.ID)) this.ID=option.ID;
  754. if (IFrameSplitOperator.IsBool(option.IsDrawAllFrame)) this.IsDrawAllFrame=option.IsDrawAllFrame;
  755. if (IFrameSplitOperator.IsNonEmptyArray(option.AryFrameID)) this.SetDrawFrameID=new Set(option.AryFrameID);
  756. }
  757. this.Draw=function()
  758. {
  759. this.SubFrame=null;
  760. if (this.FrameID<0) return;
  761. if (!this.HQChart) return;
  762. if (!this.ChartFrame || !IFrameSplitOperator.IsNonEmptyArray(this.ChartFrame.SubFrame)) return;
  763. if (!this.IsShow) return;
  764. if (!IFrameSplitOperator.IsNonEmptyArray(this.Data)) return;
  765. if (!this.ChartFrame.SubFrame[this.FrameID]) return;
  766. this.SubFrame=this.ChartFrame.SubFrame[this.FrameID].Frame;
  767. if (!this.SubFrame) return;
  768. var kData=this.HQChart.GetKData();
  769. if (!kData || !IFrameSplitOperator.IsNonEmptyArray(kData.Data)) return;
  770. var bHScreen=(this.SubFrame.IsHScreen===true);
  771. this.IsHScreen=bHScreen;
  772. var isMinute=this.SubFrame.IsMinuteFrame();
  773. var dataWidth=this.SubFrame.DataWidth;
  774. var distanceWidth=this.SubFrame.DistanceWidth;
  775. var xPointCount=this.SubFrame.XPointCount;
  776. var border=this.SubFrame.GetBorder();
  777. if (bHScreen)
  778. {
  779. var chartright=border.BottomEx;
  780. var xOffset=border.TopEx+distanceWidth/2.0+g_JSChartResource.FrameLeftMargin;
  781. }
  782. else
  783. {
  784. var xOffset=border.LeftEx+distanceWidth/2.0+g_JSChartResource.FrameLeftMargin;
  785. var chartright=border.RightEx;
  786. }
  787. var mapBG=new Map(); //key= index, Value={ Start:{Left, Center, Right, Item:}, End:{ Left, Center, Right, Item:} }
  788. var startIndex=kData.DataOffset;
  789. for(var i=startIndex,j=0;i<kData.Data.length && j<xPointCount;++i,++j,xOffset+=(dataWidth+distanceWidth))
  790. {
  791. var kItem=kData.Data[i];
  792. var aryFind=this.FindMatchData(kItem);
  793. if (!aryFind) continue;
  794. if (isMinute)
  795. {
  796. var x=this.ChartFrame.GetXFromIndex(j);
  797. var left=x;
  798. var right=x;
  799. }
  800. else
  801. {
  802. var left=xOffset;
  803. var right=xOffset+dataWidth;
  804. var x=left+(right-left)/2;
  805. }
  806. for(var k=0;k<aryFind.length;++k)
  807. {
  808. var findItem=aryFind[k];
  809. if (mapBG.has(findItem.Index))
  810. {
  811. var bgItem=mapBG.get(findItem.Index);
  812. bgItem.End.Left=left;
  813. bgItem.End.Right=right;
  814. bgItem.End.Item=findItem.Item;
  815. }
  816. else
  817. {
  818. mapBG.set(findItem.Index, { Item:findItem.Item, Start:{ Left:left, Right:right, Item:findItem.Item }, End:{ Left:left, Right:right, Item:findItem.Item }})
  819. }
  820. }
  821. }
  822. if (mapBG.size>0)
  823. {
  824. this.Canvas.save();
  825. //this.ClipClient(bHScreen);
  826. this.DrawBG(mapBG);
  827. this.Canvas.restore();
  828. }
  829. }
  830. this.FindMatchData=function(kItem)
  831. {
  832. var aryFind=[];
  833. for(var i=0;i<this.Data.length;++i)
  834. {
  835. var item=this.Data[i];
  836. var start=item.Start;
  837. var end=item.End;
  838. var bMatch=false;
  839. if (IFrameSplitOperator.IsNumber(kItem.Date) && IFrameSplitOperator.IsNumber(kItem.Time))
  840. {
  841. if (kItem.Date>start.Date && kItem.Date<end.Date)
  842. {
  843. bMatch=true;
  844. }
  845. else if (kItem.Date==start.Date && kItem.Date==end.Date)
  846. {
  847. if (kItem.Time>=start.Time && kItem.Time<=end.Time)bMatch=true;
  848. }
  849. else if (kItem.Date==start.Date)
  850. {
  851. if (kItem.Time>=start.Time) bMatch=true;
  852. }
  853. else if (kItem.Date==end.Date)
  854. {
  855. if ((kItem.Time<=end.Time)) bMatch=true;
  856. }
  857. }
  858. else if (IFrameSplitOperator.IsNumber(kItem.Date) && !IFrameSplitOperator.IsNumber(kItem.Time))
  859. {
  860. if (kItem.Date>=start.Date && kItem.Date<=end.Date) bMatch=true;
  861. }
  862. if (bMatch) aryFind.push({ Index:i, Item:item });
  863. }
  864. if (aryFind.Length<=0) return null;
  865. return aryFind;
  866. }
  867. this.DrawBG=function(mapBG)
  868. {
  869. for(var mapItem of mapBG)
  870. {
  871. var bgItem=mapItem[1];
  872. //this.DrawBGItem(this.SubFrame, bgItem);
  873. for(var i=0;i<this.ChartFrame.SubFrame.length;++i)
  874. {
  875. var subFrame=this.ChartFrame.SubFrame[i].Frame;
  876. if (this.IsDrawAllFrame || this.SetDrawFrameID.has(i))
  877. {
  878. this.DrawBGItem(subFrame, bgItem);
  879. }
  880. }
  881. }
  882. }
  883. this.DrawBGItem=function(frame, bgItem)
  884. {
  885. var border=frame.GetBorder();
  886. if (this.IsHScreen)
  887. {
  888. var top=border.RightEx;
  889. var bottom=border.LeftEx;
  890. var left=border.Top;
  891. }
  892. else
  893. {
  894. var top=border.TopEx;
  895. var bottom=border.BottomEx;
  896. var left=border.Left;
  897. }
  898. if (this.IsHScreen)
  899. {
  900. this.SetFillStyle(bgItem.Item.Color,top,left,bottom,left);
  901. var start=bgItem.Start;
  902. var end=bgItem.End;
  903. var rtBG={ Left:bottom, Right:top, Top:start.Left, Bottom:end.Right };
  904. rtBG.Width=rtBG.Right-rtBG.Left;
  905. rtBG.Height=rtBG.Bottom-rtBG.Top;
  906. this.Canvas.fillRect(ToFixedRect(rtBG.Left),ToFixedRect(rtBG.Top),ToFixedRect(rtBG.Width),ToFixedRect(rtBG.Height));
  907. }
  908. else
  909. {
  910. this.SetFillStyle(bgItem.Item.Color, left,top, left,bottom);
  911. var start=bgItem.Start;
  912. var end=bgItem.End;
  913. var rtBG={ Left:start.Left, Right:end.Right, Top:top, Bottom:bottom };
  914. rtBG.Width=rtBG.Right-rtBG.Left;
  915. rtBG.Height=rtBG.Bottom-rtBG.Top;
  916. this.Canvas.fillRect(ToFixedRect(rtBG.Left),ToFixedRect(rtBG.Top),ToFixedRect(rtBG.Width),ToFixedRect(rtBG.Height));
  917. }
  918. }
  919. }
  920. //弹幕
  921. function BarragePaint()
  922. {
  923. this.newMethod = IExtendChartPainting; //派生
  924. this.newMethod();
  925. delete this.newMethod;
  926. this.ClassName = 'BarragePaint';
  927. this.IsAnimation = true;
  928. this.IsEraseBG = true;
  929. this.HQChart;
  930. this.Font = g_JSChartResource.Barrage.Font;
  931. this.TextColor = g_JSChartResource.Barrage.Color;
  932. this.FontHeight = g_JSChartResource.Barrage.Height;
  933. this.BarrageList = new BarrageList(); //字幕列表
  934. this.IsMoveStep = false;
  935. this.SetOption = function (option) //设置参数接口
  936. {
  937. if (option)
  938. {
  939. if (option.Step > 0) this.BarrageList.Step = option.Step;
  940. if (option.MinLineHeight) this.Barrage.MinLineHeight = option.MinLineHeight;
  941. }
  942. }
  943. this.DrawHScreen = function ()
  944. {
  945. var height = this.ChartBorder.GetWidth();
  946. var left = this.ChartBorder.GetTop();
  947. var right = this.ChartBorder.GetBottom();
  948. var top = this.ChartBorder.GetRightEx();
  949. var wdith = this.ChartBorder.GetChartWidth();
  950. if (height != this.BarrageList.Height)
  951. this.BarrageList.CacluatePlayLine(height);
  952. this.Canvas.textBaseline = "middle";
  953. this.Canvas.textAlign = "left";
  954. var play = this.BarrageList.GetPlayList({ Canves: this.Canvas, Right: right, Left: left, Font: this.Font, IsMoveStep: this.IsMoveStep, HQChart: this.HQChart });
  955. this.IsMoveStep = false;
  956. if (!play) return;
  957. this.Canvas.save();
  958. this.Canvas.translate(this.ChartBorder.GetChartHeight(), 0);
  959. this.Canvas.rotate(90 * Math.PI / 180);
  960. for (var i = 0; i < play.length; ++i)
  961. {
  962. var item = play[i];
  963. if (item.Color) this.Canvas.fillStyle = item.Color;
  964. else this.Canvas.fillStyle = this.TextColor;
  965. if (item.Font) this.Canvas.font = item.Font.Name;
  966. else this.Canvas.font = this.Font;
  967. var fontHeight = this.FontHeight;
  968. if (item.Font && item.Font.Height > 0) fontHeight = item.Font.Height;
  969. var yOffset = item.Y + parseInt((item.Height - fontHeight) / 2);
  970. this.Canvas.fillText(item.Text, right - item.X, top + yOffset);
  971. }
  972. this.Canvas.restore();
  973. }
  974. this.Draw = function ()
  975. {
  976. if (this.ChartFrame.IsHScreen)
  977. {
  978. this.DrawHScreen();
  979. return;
  980. }
  981. var left = this.ChartBorder.GetLeft();
  982. var right = this.ChartBorder.GetRight();
  983. var top = this.ChartBorder.GetTopEx();
  984. var height = this.ChartBorder.GetHeight();
  985. if (height != this.BarrageList.Height)
  986. this.BarrageList.CacluatePlayLine(height);
  987. this.Canvas.textBaseline = "middle";
  988. this.Canvas.textAlign = "left";
  989. var play = this.BarrageList.GetPlayList({ Canves: this.Canvas, Right: right, Left: left, Font: this.Font, IsMoveStep: this.IsMoveStep, HQChart: this.HQChart });
  990. this.IsMoveStep = false;
  991. if (!play) return;
  992. for (var i = 0; i < play.length; ++i)
  993. {
  994. var item = play[i];
  995. if (item.Color) this.Canvas.fillStyle = item.Color;
  996. else this.Canvas.fillStyle = this.TextColor;
  997. if (item.Font) this.Canvas.font = item.Font.Name;
  998. else this.Canvas.font = this.Font;
  999. var fontHeight = this.FontHeight;
  1000. if (item.Font && item.Font.Height > 0) fontHeight = item.Font.Height;
  1001. var yOffset = item.Y + parseInt((item.Height - fontHeight) / 2);
  1002. this.Canvas.fillText(item.Text, right - item.X, top + yOffset);
  1003. }
  1004. }
  1005. }
  1006. //////////////////////////////////////////////////////////////////////////////////
  1007. // 筹码图
  1008. function StockChipPhone()
  1009. {
  1010. this.newMethod=IExtendChartPainting; //派生
  1011. this.newMethod();
  1012. delete this.newMethod;
  1013. this.Name='筹码分布手机版';
  1014. this.ClassName='StockChipPhone';
  1015. this.HQChart;
  1016. this.PenBorder=g_JSChartResource.FrameBorderPen; //边框
  1017. this.ColorProfit='rgb(255,0,0)'; //盈利的线段
  1018. this.ColorNoProfit='rgb(90,141,248)'; //非盈利
  1019. this.ColorAveragePrice='rgb(0,139,0)'; //平均价线
  1020. this.ColorBG='rgb(190,190,190)'; //筹码背景线段颜色
  1021. this.Font=g_JSChartResource.StockChip.Font;
  1022. this.InfoColor=g_JSChartResource.StockChip.InfoColor;
  1023. this.CloseButtonConfig=CloneData(g_JSChartResource.StockChip.PhoneCloseButton);
  1024. this.DayInfoColor=g_JSChartResource.StockChip.DayInfoColor;
  1025. this.PeriodTextTemplate="999周期内成本99.9%";
  1026. this.ShowType=0; //0=所有筹码
  1027. this.PixelRatio=1;
  1028. this.LineHeight=16;
  1029. this.Left=50; //左边间距
  1030. this.Width=150; //筹码图宽度
  1031. this.CalculateType=0; //0=平均分布 1=三角分布
  1032. this.PriceZoom=100; //价格放大倍数
  1033. this.Buttons=[];
  1034. this.DAY_COLOR=
  1035. [
  1036. ['rgb(255,0,0)','rgb(255,128,128)','rgb(255,0,128)','rgb(255,100,0)','rgb(192,128,0)','rgb(255,192,0)'],
  1037. ['rgb(120,80,225)','rgb(160,160,225)','rgb(80,80,255)','rgb(120,120,255)','rgb(32,64,192)','rgb(0,64,128)'],
  1038. ];
  1039. this.SetOption=function(option)
  1040. {
  1041. if (!option) return;
  1042. if (IFrameSplitOperator.IsNumber(option.ShowType)) this.ShowType=option.ShowType;
  1043. if (option.Width>100) this.Width=option.Width;
  1044. if (option.CalculateType>0) this.CalculateType=option.CalculateType;
  1045. if (IFrameSplitOperator.IsNumber(option.PriceZoom)) this.PriceZoom=option.PriceZoom;
  1046. }
  1047. this.ReloadResource=function(resource)
  1048. {
  1049. this.PenBorder=g_JSChartResource.FrameBorderPen;
  1050. this.Font=g_JSChartResource.StockChip.Font;
  1051. this.InfoColor=g_JSChartResource.StockChip.InfoColor;
  1052. this.CloseButtonConfig=CloneData(g_JSChartResource.StockChip.PhoneCloseButton);
  1053. this.DayInfoColor=g_JSChartResource.StockChip.DayInfoColor;
  1054. }
  1055. this.Draw=function()
  1056. {
  1057. this.Buttons=[];
  1058. this.IsHScreen=this.ChartFrame.IsHScreen==true;
  1059. if (this.IsHScreen)
  1060. {
  1061. var border=this.ChartBorder.GetHScreenBorder();
  1062. var left=ToFixedPoint(border.Left);
  1063. var right=ToFixedPoint(border.Right);
  1064. var top=ToFixedPoint(border.Bottom+this.Left);
  1065. var bottom=ToFixedPoint(border.ChartHeight-2*this.PixelRatio);
  1066. this.ClientRect={Left:left,Top:top, Right:right, Bottom:bottom};
  1067. this.ClientRect.Width=this.ClientRect.Right-this.ClientRect.Left;
  1068. this.ClientRect.Height=this.ClientRect.Bottom-this.ClientRect.Top;
  1069. }
  1070. else
  1071. {
  1072. var left=ToFixedPoint(this.ChartBorder.GetRight()+this.Left);
  1073. var top=ToFixedPoint(this.ChartBorder.GetTop());
  1074. var right=ToFixedPoint(left+this.Width-1*this.PixelRatio);
  1075. var bottom=ToFixedPoint(this.ChartBorder.GetBottom());
  1076. this.ClientRect={Left:left,Top:top,Right:right, Bottom:bottom};
  1077. this.ClientRect.Width=this.ClientRect.Right-this.ClientRect.Left;
  1078. this.ClientRect.Height=this.ClientRect.Bottom-this.ClientRect.Top;
  1079. }
  1080. if (ChartData.IsTickPeriod(this.HQChart.Period))
  1081. {
  1082. }
  1083. else
  1084. {
  1085. if (this.CalculateChip())
  1086. {
  1087. this.DrawAllChip();
  1088. if (this.ShowType==1|| this.ShowType==2) this.DrawDayChip();
  1089. this.CalculateCast(); //计算成本
  1090. if (this.IsHScreen) this.DrawHScreenChipInfo();
  1091. else this.DrawChipInfo();
  1092. }
  1093. }
  1094. this.DrawBorder();
  1095. this.DrawCloseButton();
  1096. this.SizeChange=false;
  1097. }
  1098. this.DrawBorder=function()
  1099. {
  1100. this.Canvas.strokeStyle=this.PenBorder;
  1101. this.Canvas.strokeRect(this.ClientRect.Left,this.ClientRect.Top,this.ClientRect.Width,this.ClientRect.Height);
  1102. }
  1103. this.DrawAllChip=function()
  1104. {
  1105. var KLineFrame=this.HQChart.Frame.SubFrame[0].Frame;
  1106. var selectPrice=this.Data.SelectData.Close;
  1107. var aryProfitPoint=[];
  1108. var aryNoProfitPoint=[];
  1109. var totalVol=0,totalAmount=0,totalProfitVol=0, totalYProfitVol=0; //总的成交量, 总的成交金额, 总的盈利的成交量
  1110. var yPrice=this.Data.YPrice;
  1111. var maxPrice=KLineFrame.HorizontalMax;
  1112. var minPrice=KLineFrame.HorizontalMin;
  1113. var MaxVol=1;
  1114. for(var i=0;i<this.Data.AllChip.length;++i)
  1115. {
  1116. var vol=this.Data.AllChip[i];
  1117. if(!vol) continue;
  1118. var price=(i+this.Data.MinPrice)/this.PriceZoom;
  1119. totalVol+=vol;
  1120. totalAmount+=price*vol;
  1121. if (price<yPrice) totalYProfitVol+=vol; //获利的成交量
  1122. if (price<selectPrice) totalProfitVol+=vol; //鼠标当前位置 获利的成交量
  1123. if (price<=maxPrice && price>=minPrice)
  1124. {
  1125. if (MaxVol<vol) MaxVol=vol;
  1126. }
  1127. }
  1128. this.Data.MaxVol=MaxVol; //把成交量最大值替换成 当前屏成交量最大值
  1129. for(var i=0;i<this.Data.AllChip.length;++i)
  1130. {
  1131. var vol=this.Data.AllChip[i];
  1132. if(!vol) continue;
  1133. var price=(i+this.Data.MinPrice)/this.PriceZoom;
  1134. if (price>maxPrice || price<minPrice) continue;
  1135. if (this.IsHScreen)
  1136. {
  1137. var y=KLineFrame.GetYFromData(price,false);
  1138. var x=(vol/this.Data.MaxVol)*this.ClientRect.Height+this.ClientRect.Top;
  1139. }
  1140. else
  1141. {
  1142. var y=KLineFrame.GetYFromData(price,false);
  1143. var x=(vol/this.Data.MaxVol)*this.ClientRect.Width+this.ClientRect.Left;
  1144. }
  1145. if (price<selectPrice) aryProfitPoint.push({X:x,Y:y});
  1146. else aryNoProfitPoint.push({X:x,Y:y});
  1147. }
  1148. this.Data.ChipInfo=
  1149. {
  1150. Vol:totalVol, AveragePrice:totalAmount/totalVol, ProfitVol:totalProfitVol,
  1151. ProfitRate:totalVol>0?totalProfitVol/totalVol*100:0,
  1152. YProfitRate:totalVol>0?totalYProfitVol/totalVol*100:0
  1153. };
  1154. if (this.ShowType==0)
  1155. {
  1156. this.DrawLines(aryProfitPoint,this.ColorProfit);
  1157. this.DrawLines(aryNoProfitPoint,this.ColorNoProfit);
  1158. var averagePrice=this.Data.ChipInfo.AveragePrice;
  1159. if (averagePrice>0 && averagePrice<=maxPrice && averagePrice>=minPrice)
  1160. {
  1161. averagePrice=averagePrice.toFixed(2);
  1162. this.DrawAveragePriceLine(aryProfitPoint,aryNoProfitPoint,KLineFrame.GetYFromData(averagePrice),this.ColorAveragePrice);
  1163. }
  1164. }
  1165. else //在火焰山模式下, 筹码用一个颜色
  1166. {
  1167. this.DrawLines(aryProfitPoint,this.ColorBG);
  1168. this.DrawLines(aryNoProfitPoint,this.ColorBG);
  1169. }
  1170. }
  1171. this.DrawLines=function(aryPoint,color)
  1172. {
  1173. if (!IFrameSplitOperator.IsNonEmptyArray(aryPoint)) return;
  1174. this.Canvas.strokeStyle=color;
  1175. this.Canvas.beginPath();
  1176. for(var i =0; i<aryPoint.length; ++i)
  1177. {
  1178. var item=aryPoint[i];
  1179. if (this.IsHScreen)
  1180. {
  1181. this.Canvas.moveTo(item.Y,this.ClientRect.Top);
  1182. this.Canvas.lineTo(item.Y,item.X);
  1183. }
  1184. else
  1185. {
  1186. this.Canvas.moveTo(this.ClientRect.Left,item.Y);
  1187. this.Canvas.lineTo(item.X,item.Y);
  1188. }
  1189. }
  1190. this.Canvas.stroke();
  1191. }
  1192. this.DrawAveragePriceLine=function(aryProfitPoint,aryNoProfitPoint,y,color)
  1193. {
  1194. for(var i=0; i<aryProfitPoint.length; ++i)
  1195. {
  1196. var item=aryProfitPoint[i];
  1197. if (item.Y==y)
  1198. {
  1199. this.Canvas.strokeStyle=color;
  1200. this.Canvas.beginPath();
  1201. if (this.IsHScreen)
  1202. {
  1203. this.Canvas.moveTo(item.Y,this.ClientRect.Top);
  1204. this.Canvas.lineTo(item.Y,item.X);
  1205. }
  1206. else
  1207. {
  1208. this.Canvas.moveTo(this.ClientRect.Left,item.Y);
  1209. this.Canvas.lineTo(item.X,item.Y);
  1210. }
  1211. this.Canvas.stroke();
  1212. return;
  1213. }
  1214. }
  1215. for(var i=0; i<aryNoProfitPoint.length; ++i)
  1216. {
  1217. var item=aryNoProfitPoint[i];
  1218. if (item.Y==y)
  1219. {
  1220. this.Canvas.strokeStyle=color;
  1221. this.Canvas.beginPath();
  1222. if (this.IsHScreen)
  1223. {
  1224. this.Canvas.moveTo(item.Y,this.ClientRect.Top);
  1225. this.Canvas.lineTo(item.Y,item.X);
  1226. }
  1227. else
  1228. {
  1229. this.Canvas.moveTo(this.ClientRect.Left,item.Y);
  1230. this.Canvas.lineTo(item.X,item.Y);
  1231. }
  1232. this.Canvas.stroke();
  1233. return;
  1234. }
  1235. }
  1236. }
  1237. this.GetChipInfoTitle=function()
  1238. {
  1239. var aryText=[]; //从底部往上
  1240. if (ChartData.IsDayPeriod(this.HQChart.Period, true))
  1241. {
  1242. var item={ Title:"日期:", Text:IFrameSplitOperator.FormatDateString(this.Data.SelectData.Date) };
  1243. aryText.push(item);
  1244. }
  1245. else if (ChartData.IsMinutePeriod(this.HQChart.Period, true))
  1246. {
  1247. var item={ Title:`${IFrameSplitOperator.FormatDateString(this.Data.SelectData.Date)} ${IFrameSplitOperator.FormatTimeString(this.Data.SelectData.Time, "HH:MM")}` };
  1248. aryText.push(item);
  1249. }
  1250. var item={ Title:"集中度:", Text:"--.--%", };
  1251. if (IFrameSplitOperator.IsNonEmptyArray(this.Data.Cast)) item.Text=`${this.Data.Cast[0].Rate.toFixed(2)}%`;
  1252. aryText.push(item);
  1253. var item={ Title:"90%成本:", Text:"--.--", };
  1254. if (IFrameSplitOperator.IsNonEmptyArray(this.Data.Cast)) item.Text=`${this.Data.Cast[0].MinPrice.toFixed(2)}-${this.Data.Cast[0].MaxPrice.toFixed(2)}`;
  1255. aryText.push(item);
  1256. var item={ Title:"平均成本::", Text:"--.--", };
  1257. if (this.Data.ChipInfo && IFrameSplitOperator.IsNumber(this.Data.ChipInfo.AveragePrice)) item.Text=`${this.Data.ChipInfo.AveragePrice.toFixed(2)}`;
  1258. if (IFrameSplitOperator.IsNumber(this.Data.YPrice) && IFrameSplitOperator.IsNumber(this.Data.ChipInfo.YProfitRate))
  1259. {
  1260. var item={ Title:`${this.Data.YPrice.toFixed(2)}获利:`, Text:`${this.Data.ChipInfo.YProfitRate.toFixed(2)}%` };
  1261. aryText.push(item);
  1262. }
  1263. if (this.IsHScreen) //横屏直接就显示数值 画进度条太麻烦了
  1264. {
  1265. var item={ Title:"获利比例:", Text: `${this.Data.ChipInfo.ProfitRate.toFixed(2)}%`};
  1266. aryText.push(item);
  1267. }
  1268. else
  1269. {
  1270. var item={ Title:"获利比例:", Type:1 };
  1271. aryText.push(item);
  1272. }
  1273. return aryText;
  1274. }
  1275. this.DrawHScreenChipInfo=function()
  1276. {
  1277. var aryText=this.GetChipInfoTitle();
  1278. if (!IFrameSplitOperator.IsNonEmptyArray(aryText)) return;
  1279. this.Canvas.font=this.Font;
  1280. this.Canvas.fillStyle=this.InfoColor;
  1281. this.Canvas.textBaseline='bottom';
  1282. this.Canvas.textAlign='left';
  1283. var top=this.ClientRect.Top+2*this.PixelRatio;
  1284. var left=this.ClientRect.Left+2*this.PixelRatio;
  1285. var bottom=this.ClientRect.Bottom;
  1286. var lineHeight=this.LineHeight;
  1287. this.Canvas.save();
  1288. this.Canvas.translate(left, top);
  1289. this.Canvas.rotate(90 * Math.PI / 180);
  1290. var yText=0, xText=0;
  1291. for(var i=0;i<aryText.length;++i)
  1292. {
  1293. var item=aryText[i];
  1294. yText=0;
  1295. var textColor=item.TitleColor;
  1296. if (item.TitleColor) textColor=item.TitleColor;
  1297. this.Canvas.fillStyle=textColor;
  1298. this.Canvas.fillText(item.Title,yText,xText);
  1299. var textWidth=this.Canvas.measureText(item.Title).width+4*this.PixelRatio;
  1300. var yText=textWidth;
  1301. if (item.Text)
  1302. {
  1303. var textColor=item.TitleColor;
  1304. if (item.TextColor) textColor=item.TextColor;
  1305. this.Canvas.fillStyle=textColor;
  1306. this.Canvas.fillText(item.Text,yText,xText);
  1307. }
  1308. xText-=lineHeight;
  1309. }
  1310. this.Canvas.restore();
  1311. }
  1312. this.DrawChipInfo=function()
  1313. {
  1314. var aryText=this.GetChipInfoTitle();
  1315. if (!IFrameSplitOperator.IsNonEmptyArray(aryText)) return;
  1316. var bottom=this.ClientRect.Top+this.ClientRect.Height-1;
  1317. var left=this.ClientRect.Left+2;
  1318. var right=this.ClientRect.Left+this.ClientRect.Width;
  1319. this.Canvas.font=this.Font;
  1320. this.Canvas.fillStyle=this.InfoColor;
  1321. this.Canvas.textBaseline='bottom';
  1322. this.Canvas.textAlign='left';
  1323. var lineHeight=this.LineHeight;
  1324. var yText=bottom;
  1325. for(var i=0;i<aryText.length;++i)
  1326. {
  1327. var item=aryText[i];
  1328. var textColor=item.TitleColor;
  1329. if (item.TitleColor) textColor=item.TitleColor;
  1330. this.Canvas.fillStyle=textColor;
  1331. this.Canvas.fillText(item.Title,left,yText);
  1332. var textWidth=this.Canvas.measureText(item.Title).width+4;
  1333. var xText=left+textWidth;
  1334. if (item.Text)
  1335. {
  1336. var textColor=item.TitleColor;
  1337. if (item.TextColor) textColor=item.TextColor;
  1338. this.Canvas.fillStyle=textColor;
  1339. this.Canvas.fillText(item.Text,xText,yText);
  1340. }
  1341. if (item.Type==1)
  1342. {
  1343. var barLeft=left+textWidth+2;
  1344. var barWidth=(right-5-barLeft);
  1345. this.Canvas.strokeStyle=this.ColorNoProfit;
  1346. this.Canvas.strokeRect(barLeft,yText-lineHeight,barWidth,lineHeight);
  1347. this.Canvas.strokeStyle=this.ColorProfit;
  1348. this.Canvas.strokeRect(barLeft,yText-lineHeight,barWidth*(this.Data.ChipInfo.ProfitRate/100),lineHeight);
  1349. var text=this.Data.ChipInfo.ProfitRate.toFixed(2)+'%';
  1350. this.Canvas.textAlign='center';
  1351. this.Canvas.fillText(text,barLeft+barWidth/2,yText);
  1352. }
  1353. yText-=lineHeight;
  1354. }
  1355. this.DrawChipPeriodInfo(yText);
  1356. }
  1357. this.DrawChipPeriodInfo=function(bottom)
  1358. {
  1359. if (this.ShowType!=1 && this.ShowType!=2) return;
  1360. var lineHeight=this.LineHeight;
  1361. var right=this.ClientRect.Left+this.ClientRect.Width-1;
  1362. this.Canvas.textAlign='right';
  1363. var maxTextWidth=this.Canvas.measureText(this.PeriodTextTemplate).width+2;
  1364. this.PeriodTextTemplate
  1365. this.Data.DayChip.sort(function(a,b){return b.Day-a.Day;})
  1366. for(var i=0;i<this.Data.DayChip.length;++i)
  1367. {
  1368. var item=this.Data.DayChip[i];
  1369. var rate=0;
  1370. if (this.Data.ChipInfo && this.Data.ChipInfo.Vol>0) rate=item.Vol/this.Data.ChipInfo.Vol*100;
  1371. var text=item.Day+'周期'+(this.ShowType==1?'前':'内')+'成本'+(IFrameSplitOperator.IsNumber(rate)? (rate.toFixed(1)+'%'):"--.-%");
  1372. this.Canvas.fillStyle=item.Color;
  1373. this.Canvas.fillRect(right-maxTextWidth,bottom-lineHeight,maxTextWidth,lineHeight);
  1374. this.Canvas.fillStyle=this.DayInfoColor;
  1375. this.Canvas.fillText(text,right-1,bottom);
  1376. bottom-=lineHeight;
  1377. }
  1378. }
  1379. this.DrawDayChip=function()
  1380. {
  1381. if (!IFrameSplitOperator.IsNonEmptyArray(this.Data.DayChip)) return;
  1382. var KLineFrame=this.HQChart.Frame.SubFrame[0].Frame;
  1383. for(var i=0;i<this.Data.DayChip.length;++i)
  1384. {
  1385. var aryPoint=[];
  1386. var chipData=this.Data.DayChip[i].Chip;
  1387. if (!chipData) continue;
  1388. var totalVol=0;
  1389. for(var j=0;j<chipData.length;++j)
  1390. {
  1391. var vol=chipData[j];
  1392. if(!vol) continue;
  1393. totalVol+=vol;
  1394. var price=(j+this.Data.MinPrice)/100;
  1395. if (this.IsHScreen)
  1396. {
  1397. var y=KLineFrame.GetYFromData(price);
  1398. var x=(vol/this.Data.MaxVol)*this.ClientRect.Height+this.ClientRect.Top;
  1399. }
  1400. else
  1401. {
  1402. var y=KLineFrame.GetYFromData(price);
  1403. var x=(vol/this.Data.MaxVol)*this.ClientRect.Width+this.ClientRect.Left;
  1404. }
  1405. aryPoint.push({X:x,Y:y});
  1406. }
  1407. this.Data.DayChip[i].Vol=totalVol;
  1408. this.DrawArea(aryPoint,this.Data.DayChip[i].Color);
  1409. }
  1410. }
  1411. this.DrawArea=function(aryPoint,color)
  1412. {
  1413. if (!IFrameSplitOperator.IsNonEmptyArray(aryPoint)) return;
  1414. this.Canvas.fillStyle=color;
  1415. this.Canvas.beginPath();
  1416. if (this.IsHScreen)
  1417. {
  1418. this.Canvas.moveTo(aryPoint[0].Y,this.ClientRect.Top);
  1419. for(var i=0; i<aryPoint.length; ++i)
  1420. {
  1421. var item=aryPoint[i];
  1422. this.Canvas.lineTo(item.Y,item.X);
  1423. }
  1424. this.Canvas.lineTo(aryPoint[aryPoint.length-1].Y,this.ClientRect.Top);
  1425. }
  1426. else
  1427. {
  1428. this.Canvas.moveTo(this.ClientRect.Left,aryPoint[0].Y);
  1429. for(var i=0; i<aryPoint.length; ++i)
  1430. {
  1431. var item=aryPoint[i];
  1432. this.Canvas.lineTo(item.X,item.Y);
  1433. }
  1434. this.Canvas.lineTo(this.ClientRect.Left,aryPoint[aryPoint.length-1].Y);
  1435. }
  1436. this.Canvas.fill();
  1437. }
  1438. //关闭按钮
  1439. this.DrawCloseButton=function()
  1440. {
  1441. var config=this.CloseButtonConfig;
  1442. var rtButton=null;
  1443. if (this.IsHScreen)
  1444. {
  1445. var border=this.ChartBorder.GetHScreenBorder();
  1446. var right=border.Left-2;
  1447. var bottom=border.ChartHeight-2;
  1448. var left=right-config.Size;
  1449. var top=bottom-config.Size;
  1450. var rtButton={ Left:left, Top:top, Bottom:bottom, Right:right };
  1451. rtButton.Width=rtButton.Right-rtButton.Left;
  1452. rtButton.Height=rtButton.Bottom-rtButton.Top;
  1453. }
  1454. else
  1455. {
  1456. var border=this.ChartBorder.GetBorder();
  1457. var top=border.Bottom+2;
  1458. var bottom=top+config.Size;
  1459. var right=border.ChartWidth-2;
  1460. var left=right-config.Size;
  1461. var rtButton={ Left:left, Top:top, Bottom:bottom, Right:right };
  1462. rtButton.Width=rtButton.Right-rtButton.Left;
  1463. rtButton.Height=rtButton.Bottom-rtButton.Top;
  1464. }
  1465. if (!rtButton) return;
  1466. if (config.Border)
  1467. {
  1468. if (config.Border.BGColor)
  1469. {
  1470. this.Canvas.fillStyle=config.Border.BGColor;
  1471. this.Canvas.fillRect(rtButton.Left,rtButton.Top,rtButton.Width,rtButton.Height);
  1472. }
  1473. }
  1474. var rtIcon={ Left:rtButton.Left+2, Right:rtButton.Right-2, Top:rtButton.Top+2, Bottom:rtButton.Bottom-2 };
  1475. this.Canvas.strokeStyle=config.Color;
  1476. this.Canvas.beginPath();
  1477. this.Canvas.moveTo(rtIcon.Left,rtIcon.Top);
  1478. this.Canvas.lineTo(rtIcon.Right,rtIcon.Bottom);
  1479. this.Canvas.moveTo(rtIcon.Right,rtIcon.Top);
  1480. this.Canvas.lineTo(rtIcon.Left,rtIcon.Bottom);
  1481. this.Canvas.stroke();
  1482. var btnItem={ ID:JSCHART_BUTTON_ID.CHIP_CLOSE, Rect:rtButton };
  1483. this.Buttons.push(btnItem);
  1484. }
  1485. this.PtInButtons=function(x,y) //坐标是否在按钮上
  1486. {
  1487. for(var i=0;i<this.Buttons.length;++i)
  1488. {
  1489. var item=this.Buttons[i];
  1490. if (!item.Rect) continue;
  1491. var rect=item.Rect;
  1492. if (x>=rect.Left && x<=rect.Right && y>=rect.Top && y<=rect.Bottom)
  1493. {
  1494. return { ID:item.ID, Rect:rect };
  1495. }
  1496. }
  1497. return null;
  1498. }
  1499. /////////////////////////////////////////////////////////////////////////
  1500. //计算
  1501. this.CalculateChip=function() //计算筹码
  1502. {
  1503. if (!this.HQChart) return false;
  1504. if (!this.HQChart.FlowCapitalReady) return false;
  1505. var symbol=this.HQChart.Symbol;
  1506. if (!symbol) return false;
  1507. var recvData=this.SendCalculateChipEvent();
  1508. if (recvData && recvData.PreventDefault)
  1509. return recvData.Result;
  1510. if (MARKET_SUFFIX_NAME.IsSHSZIndex(symbol)) return false; //指数暂时不支持移动筹码
  1511. var bindData=this.HQChart.ChartPaint[0].Data;
  1512. //if (bindData.Period>=4) return false; //分钟K线不支持, 没时间做,以后再做吧
  1513. var count=bindData.DataOffset+parseInt(this.HQChart.CursorIndex);
  1514. if (count>=bindData.Data.length) count=bindData.Data.length-1;
  1515. var selData=bindData.Data[count];
  1516. var yPrice=selData.Close;
  1517. var mouseY=this.HQChart.LastPoint.Y;
  1518. if (mouseY) yPrice=this.HQChart.Frame.SubFrame[0].Frame.GetYData(mouseY);
  1519. //JSConsole.Chart.Log("[StockChip::CalculateChip]",count,this.HQChart.CursorIndex,selData);
  1520. const rate=1;
  1521. var aryVol=[];
  1522. var seed=1,vol,maxPrice,minPrice;
  1523. for(let i=count;i>=0;--i)
  1524. {
  1525. var item=bindData.Data[i];
  1526. var changeRate=1; //换手率
  1527. if (item.FlowCapital>0) changeRate=item.Vol/item.FlowCapital;
  1528. if (i==count) vol=item.Vol*changeRate;
  1529. else vol=item.Vol*seed;
  1530. var dataItem={Vol:vol,High:item.High,Low:item.Low};
  1531. aryVol.push(dataItem);
  1532. seed*=(1-changeRate*rate);
  1533. if (!maxPrice || maxPrice<item.High) maxPrice=item.High;
  1534. if (!minPrice || minPrice>item.Low) minPrice=item.Low;
  1535. }
  1536. //JSConsole.Chart.Log("[StockChip::CalculateChip]",maxPrice,minPrice);
  1537. if (!maxPrice || !minPrice) return true;
  1538. var priceZoom=this.PriceZoom;
  1539. maxPrice=parseInt(maxPrice*priceZoom);
  1540. minPrice=parseInt(minPrice*priceZoom);
  1541. var dataCount=maxPrice-minPrice;
  1542. var aryChip=new Array()
  1543. for(let i=0;i<=dataCount;++i)
  1544. {
  1545. aryChip.push(0);
  1546. }
  1547. var dayChip=[];
  1548. var distributeData;
  1549. if (this.ShowType==2)
  1550. {
  1551. var dayChip=
  1552. [
  1553. {Day:100, Color:this.DAY_COLOR[1][5]}, {Day:60, Color:this.DAY_COLOR[1][4]}, {Day:30, Color:this.DAY_COLOR[1][3]},
  1554. {Day:20, Color:this.DAY_COLOR[1][2]}, {Day:10, Color:this.DAY_COLOR[1][1]}, {Day:5, Color:this.DAY_COLOR[1][0]}
  1555. ];
  1556. for(let i in aryVol)
  1557. {
  1558. var item=aryVol[i];
  1559. var high=parseInt(item.High*priceZoom);
  1560. var low=parseInt(item.Low*priceZoom);
  1561. var averageVol=item.Vol;
  1562. if (high-low>0) averageVol=item.Vol/(high-low);
  1563. if (averageVol<=0.000000001) continue;
  1564. for(var k=0;k<dayChip.length;++k)
  1565. {
  1566. if (i==dayChip[k].Day)
  1567. {
  1568. dayChip[k].Chip=aryChip.slice(0);
  1569. break;
  1570. }
  1571. }
  1572. distributeData={Low:low, High:high, Vol:item.Vol, MaxPrice:maxPrice, MinPrice:minPrice};
  1573. this.CalculateDistribute(aryChip, distributeData );
  1574. }
  1575. }
  1576. else if (this.ShowType==1)
  1577. {
  1578. var dayChip=
  1579. [
  1580. {Day:5, Color:this.DAY_COLOR[0][0]},{Day:10, Color:this.DAY_COLOR[0][1]},{Day:20, Color:this.DAY_COLOR[0][2]},
  1581. {Day:30, Color:this.DAY_COLOR[0][3]},{Day:60, Color:this.DAY_COLOR[0][4]},{Day:100, Color:this.DAY_COLOR[0][5]}
  1582. ];
  1583. for(let i=aryVol.length-1;i>=0;--i)
  1584. {
  1585. var item=aryVol[i];
  1586. var high=parseInt(item.High*priceZoom);
  1587. var low=parseInt(item.Low*priceZoom);
  1588. var averageVol=item.Vol;
  1589. if (high-low>0) averageVol=item.Vol/(high-low);
  1590. if (averageVol<=0.000000001) continue;
  1591. for(var k=0;k<dayChip.length;++k)
  1592. {
  1593. if (i==dayChip[k].Day)
  1594. {
  1595. dayChip[k].Chip=aryChip.slice(0);
  1596. break;
  1597. }
  1598. }
  1599. distributeData={Low:low, High:high, Vol:item.Vol, MaxPrice:maxPrice, MinPrice:minPrice};
  1600. this.CalculateDistribute(aryChip, distributeData);
  1601. }
  1602. }
  1603. else
  1604. {
  1605. for(let i in aryVol)
  1606. {
  1607. var item=aryVol[i];
  1608. var high=parseInt(item.High*priceZoom);
  1609. var low=parseInt(item.Low*priceZoom);
  1610. var averageVol=item.Vol;
  1611. if (high-low>0) averageVol=item.Vol/(high-low);
  1612. if (averageVol<=0.000000001) continue;
  1613. distributeData={Low:low, High:high, Vol:item.Vol, MaxPrice:maxPrice, MinPrice:minPrice};
  1614. this.CalculateDistribute(aryChip, distributeData);
  1615. }
  1616. }
  1617. if (!distributeData) return false;
  1618. this.Data={AllChip:aryChip, MaxVol:distributeData.MaxVol, MaxPrice:maxPrice, MinPrice:minPrice,SelectData:selData, DayChip:dayChip, YPrice:yPrice};
  1619. return true;
  1620. }
  1621. this.SendCalculateChipEvent=function()
  1622. {
  1623. var event=this.HQChart.GetEventCallback(JSCHART_EVENT_ID.ON_CALCULATE_CHIP_DATA);
  1624. if (!event) return null;
  1625. var bindData=this.HQChart.ChartPaint[0].Data;
  1626. var curIndex=bindData.DataOffset+parseInt(this.HQChart.CursorIndex);
  1627. var sendData=
  1628. {
  1629. Symbol:this.HQChart.Symbol, Period:this.HQChart.Period, KData:bindData,
  1630. CurrentIndex:curIndex, Y:this.HQChart.LastPoint.Y, HQChart:this.HQChart,
  1631. ShowType:this.ShowType, CalculateType:this.CalculateType,
  1632. PreventDefault:false,
  1633. Result:null, //true/false
  1634. };
  1635. event.Callback(event, sendData, this);
  1636. return sendData;
  1637. }
  1638. this.EvenlyDistribute=function(aryChip, data) //平均分布 data={Low, High, Vol, MaxVol, MaxPrice, MinPrice }
  1639. {
  1640. var low=data.Low, high=data.High, maxPrice=data.MaxPrice, minPrice=data.MinPrice, maxVol=1;
  1641. if ( (high-low)== 0) return;
  1642. var averageVol=data.Vol/(high-low);
  1643. for(var j=low;j<=high && j<=maxPrice;++j)
  1644. {
  1645. var index=j-minPrice;
  1646. aryChip[index]+=averageVol;
  1647. if (maxVol<aryChip[index]) maxVol=aryChip[index];
  1648. }
  1649. data.MaxVol=maxVol;
  1650. }
  1651. this.TriangleDistribute=function(aryChip, data) //三角分布
  1652. {
  1653. var low=data.Low, high=data.High, maxPrice=data.MaxPrice, minPrice=data.MinPrice, maxVol=1;
  1654. var ANGLE = 45, PI=3.1415926535;
  1655. var middlePrice = (high - low) / 2.0 + low;
  1656. var totalValue=0;
  1657. var aryVol=[];
  1658. for(var i=low+1, j=1 ;i<=middlePrice;++i,++j)
  1659. {
  1660. var y = Math.tan(ANGLE* PI / 180)*j;
  1661. totalValue+=y;
  1662. aryVol.push({Index:i-minPrice, Value:y});
  1663. }
  1664. for(var i=high-1, j=1 ;i>middlePrice;--i,++j)
  1665. {
  1666. var y = Math.tan(ANGLE* PI / 180)*j;
  1667. totalValue+=y
  1668. aryVol.push({Index:i-minPrice, Value:y});
  1669. }
  1670. if (totalValue>0)
  1671. {
  1672. for(var i=0;i<aryVol.length;++i)
  1673. {
  1674. var item=aryVol[i];
  1675. aryChip[item.Index]+=item.Value*data.Vol/totalValue;
  1676. if (maxVol<aryChip[item.Index]) maxVol=aryChip[item.Index];
  1677. }
  1678. data.MaxVol=maxVol;
  1679. }
  1680. }
  1681. this.CalculateDistribute=function(aryChip, data)
  1682. {
  1683. if (this.CalculateType==1) this.TriangleDistribute(aryChip, data);
  1684. else this.EvenlyDistribute(aryChip, data);
  1685. }
  1686. this.CalculateCast=function() //计算 90% 70%的成本价
  1687. {
  1688. if (!this.Data.ChipInfo || !this.Data.ChipInfo.Vol) return;
  1689. var aryCast=
  1690. [
  1691. {Start:0.05,End:0.95, MaxPrice:0, MinPrice:0, Rate:0},
  1692. {Start:0.15,End:0.85, MaxPrice:0, MinPrice:0, Rate:0}
  1693. ];
  1694. var averagePrice=this.Data.ChipInfo.AveragePrice;
  1695. var totalProfitVol=this.Data.ChipInfo.ProfitVol;
  1696. var tempVol=0;
  1697. for(var i=0, castCount=0;i<this.Data.AllChip.length;++i)
  1698. {
  1699. if (castCount==4) break;
  1700. var vol=this.Data.AllChip[i];
  1701. if (vol<=0) continue;
  1702. var price=(i+this.Data.MinPrice)/this.PriceZoom;
  1703. tempVol+=vol;
  1704. var rate=tempVol/totalProfitVol;
  1705. for(var j=0; j<aryCast.length; ++j)
  1706. {
  1707. var itemCast=aryCast[j];
  1708. if (itemCast.MinPrice<=0 && rate>itemCast.Start)
  1709. {
  1710. itemCast.MinPrice=price;
  1711. ++castCount;
  1712. }
  1713. if (itemCast.MaxPrice<=0 && rate>itemCast.End)
  1714. {
  1715. itemCast.MaxPrice=price;
  1716. ++castCount;
  1717. }
  1718. }
  1719. }
  1720. for(var i=0; i<aryCast.length; ++i)
  1721. {
  1722. var item=aryCast[i];
  1723. var addPrice=item.MaxPrice+item.MinPrice;
  1724. if (addPrice) item.Rate=Math.abs(item.MaxPrice-item.MinPrice)/addPrice*100;
  1725. }
  1726. this.Data.Cast=aryCast;
  1727. }
  1728. }
  1729. //最新数据点闪动
  1730. function LatestPointFlashPaint()
  1731. {
  1732. this.newMethod=IExtendChartPainting; //派生
  1733. this.newMethod();
  1734. delete this.newMethod;
  1735. this.ClassName='LatestPointFlashPaint';
  1736. this.HQChart;
  1737. this.FlashType=1; //0=数据更新了闪烁, 1=一直显示
  1738. this.PointColor=g_JSChartResource.LatestPointFlash.PointColor;
  1739. this.PointRadius=g_JSChartResource.LatestPointFlash.PointRadius;
  1740. this.BGColor=g_JSChartResource.LatestPointFlash.BGColor;
  1741. this.BGRadius=g_JSChartResource.LatestPointFlash.BGRadius;
  1742. this.Style=1; //0=默认配置 1=图形保持一致
  1743. this.SetOption=function(option)
  1744. {
  1745. }
  1746. this.ReloadResource=function(resource)
  1747. {
  1748. this.PointColor=g_JSChartResource.LatestPointFlash.PointColor;
  1749. this.PointRadius=g_JSChartResource.LatestPointFlash.PointRadius;
  1750. this.BGColor=g_JSChartResource.LatestPointFlash.BGColor;
  1751. this.BGRadius=g_JSChartResource.LatestPointFlash.BGRadius;
  1752. }
  1753. this.GetChartColor=function()
  1754. {
  1755. var chart=this.HQChart.ChartPaint[0];
  1756. if (!chart) return;
  1757. if (chart.ClassName=="ChartKLine")
  1758. {
  1759. this.PointColor=chart.CloseLineColor;
  1760. this.BGColor=IChartDrawPicture.ColorToRGBA(chart.CloseLineColor, 0.6);
  1761. }
  1762. else if (chart.ClassName=="ChartMinutePriceLine")
  1763. {
  1764. this.PointColor=chart.Color;
  1765. this.BGColor=IChartDrawPicture.ColorToRGBA(chart.Color, 0.6);
  1766. }
  1767. }
  1768. this.Draw=function()
  1769. {
  1770. if (!this.HQChart.GlobalOption || !this.HQChart.GlobalOption.LatestPoint) return;
  1771. var point=this.HQChart.GlobalOption.LatestPoint;
  1772. if (!IFrameSplitOperator.IsNumber(point.X) || !IFrameSplitOperator.IsNumber(point.Y)) return;
  1773. this.DrawPoint(point);
  1774. }
  1775. this.DrawPoint=function(point)
  1776. {
  1777. if (this.Style==1) this.GetChartColor();
  1778. this.Canvas.fillStyle=this.BGColor;
  1779. this.Canvas.beginPath();
  1780. this.Canvas.arc(point.X,point.Y,this.BGRadius,0,360,false);
  1781. this.Canvas.fill();
  1782. this.Canvas.closePath();
  1783. //画实心圆
  1784. this.Canvas.fillStyle=this.PointColor;
  1785. this.Canvas.beginPath();
  1786. this.Canvas.arc(point.X,point.Y,this.PointRadius,0,360,false);
  1787. this.Canvas.fill();
  1788. this.Canvas.closePath();
  1789. }
  1790. }
  1791. /*
  1792. 扩展图形
  1793. */
  1794. function ExtendChartPaintFactory()
  1795. {
  1796. this.DataMap=new Map(
  1797. [
  1798. //["FrameSplitPaint", { Create:function() { return new FrameSplitPaint(); } }],
  1799. ["LatestPointFlashPaint", {Create:function() { return new LatestPointFlashPaint(); }}],
  1800. ]
  1801. );
  1802. this.SetCallbackDraw=new Set();
  1803. this.Create=function(name)
  1804. {
  1805. if (!this.DataMap.has(name)) return null;
  1806. var item=this.DataMap.get(name);
  1807. return item.Create();
  1808. }
  1809. this.Add=function(name, option)
  1810. {
  1811. this.DataMap.set(name, { Create:option.Create } );
  1812. }
  1813. this.AddCallbackDrawClassName=function(className)
  1814. {
  1815. if (!className) return;
  1816. this.SetCallbackDraw.add(className);
  1817. }
  1818. this.IsCallbackDraw=function(className)
  1819. {
  1820. return this.SetCallbackDraw.has(className);
  1821. }
  1822. }
  1823. var g_ExtendChartPaintFactory=new ExtendChartPaintFactory();
  1824. //导出统一使用JSCommon命名空间名
  1825. export
  1826. {
  1827. IExtendChartPainting,
  1828. KLineTooltipPaint,
  1829. BarragePaint,
  1830. MinuteTooltipPaint,
  1831. BackgroundPaint,
  1832. g_ExtendChartPaintFactory,
  1833. StockChipPhone,
  1834. }
  1835. /*
  1836. module.exports =
  1837. {
  1838. JSCommonExtendChartPaint:
  1839. {
  1840. IExtendChartPainting: IExtendChartPainting,
  1841. KLineTooltipPaint: KLineTooltipPaint,
  1842. BarragePaint: BarragePaint,
  1843. MinuteTooltipPaint: MinuteTooltipPaint,
  1844. BackgroundPaint: BackgroundPaint,
  1845. },
  1846. //单个类导出
  1847. JSCommonExtendChartPaint_IExtendChartPainting: IExtendChartPainting,
  1848. JSCommonExtendChartPaint_KLineTooltipPaint: KLineTooltipPaint,
  1849. JSCommonExtendChartPaint_BarragePaint: BarragePaint,
  1850. JSCommonExtendChartPaint_MinuteTooltipPaint: MinuteTooltipPaint,
  1851. JSCommonExtendChartPaint_BackgroundPaint: BackgroundPaint,
  1852. };
  1853. */