You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1190 lines
40 KiB

  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. } from "./umychart.data.wechat.js";
  19. import
  20. {
  21. JSCommonCoordinateData,
  22. MARKET_SUFFIX_NAME
  23. } from "./umychart.coordinatedata.wechat.js";
  24. import
  25. {
  26. g_JSChartResource,
  27. JSCHART_LANGUAGE_ID,
  28. g_JSChartLocalization,
  29. } from './umychart.resource.wechat.js'
  30. import
  31. {
  32. IFrameSplitOperator,
  33. } from './umychart.framesplit.wechat.js'
  34. function IExtendChartPainting()
  35. {
  36. this.Canvas; //画布
  37. this.ChartBorder; //边框信息
  38. this.ChartFrame; //框架画法
  39. this.Name; //名称
  40. this.Data; // = new ChartData(); //数据区
  41. this.ClassName = 'IExtendChartPainting';
  42. this.IsDynamic = false;
  43. this.IsEraseBG = false; //是否每次画的时候需要擦除K线图背景
  44. this.IsAnimation=false;
  45. this.DrawAfterTitle = false; //是否在动态标题画完以后再画,防止动态标题覆盖
  46. this.ID=Guid(),
  47. //上下左右间距
  48. this.Left = 5;
  49. this.Right = 5;
  50. this.Top = 5;
  51. this.Bottom = 5;
  52. this.Draw = function () { } //画图接口
  53. this.SetOption = function (option) { } //设置参数接口
  54. }
  55. //K线Tooltip, 显示在左边或右边
  56. function KLineTooltipPaint()
  57. {
  58. this.newMethod = IExtendChartPainting; //派生
  59. this.newMethod();
  60. delete this.newMethod;
  61. this.IsDynamic = true;
  62. this.IsEraseBG = true;
  63. this.DrawAfterTitle = true;
  64. this.ClassName = 'KLineTooltipPaint';
  65. this.LatestPoint; //手势位置
  66. this.ShowPosition=0; //显示位置 0=左 1=右
  67. this.BorderColor = g_JSChartResource.TooltipPaint.BorderColor; //边框颜色
  68. this.BGColor = g_JSChartResource.TooltipPaint.BGColor; //背景色
  69. this.TitleColor = g_JSChartResource.TooltipPaint.TitleColor; //标题颜色
  70. this.Font = [g_JSChartResource.TooltipPaint.TitleFont];
  71. this.Mergin={ Left:2, Top:3, Bottom:5, Right:5 };
  72. this.ExtendLineWidth=5;
  73. this.Width = 50;
  74. this.Height = 100;
  75. this.LineHeight = 15; //行高
  76. this.LineSpace=2; //行间距
  77. this.Left = 1;
  78. this.Top = 0;
  79. this.HQChart;
  80. this.KLineTitlePaint;
  81. this.IsHScreen = false; //是否横屏
  82. this.LanguageID = JSCHART_LANGUAGE_ID.LANGUAGE_CHINESE_ID;
  83. this.GetLeft = function ()
  84. {
  85. if (this.IsHScreen)
  86. {
  87. return this.ChartBorder.GetRightEx()-this.Height-this.Top;
  88. }
  89. else
  90. {
  91. if (this.ShowPosition==0)
  92. return this.ChartBorder.GetLeft()+this.Left;
  93. else
  94. return this.ChartBorder.GetRight()-this.Width-this.Left;
  95. }
  96. }
  97. this.GetTop = function ()
  98. {
  99. if (this.IsHScreen)
  100. {
  101. if (this.ShowPosition==0)
  102. return this.ChartBorder.GetTop()+this.Left;
  103. else
  104. return this.ChartBorder.GetBottom()-this.Width-this.Left;
  105. }
  106. else
  107. {
  108. return this.ChartBorder.GetTopEx()+this.Top;
  109. }
  110. }
  111. //是否显示
  112. this.IsEnableDraw=function()
  113. {
  114. if (!this.HQChart || !this.HQChart.TitlePaint || !this.HQChart.TitlePaint[0]) return false;
  115. if (this.HQChart.EnableClickModel)
  116. {
  117. if (this.HQChart.ClickModel.IsShowCorssCursor===false) return false;
  118. }
  119. else if (!this.HQChart.IsOnTouch)
  120. {
  121. return false;
  122. }
  123. return true;
  124. }
  125. this.Draw = function ()
  126. {
  127. if (!this.IsEnableDraw()) return;
  128. this.IsHScreen=this.ChartFrame.IsHScreen===true;
  129. this.KLineTitlePaint = this.HQChart.TitlePaint[0];
  130. var klineData = this.KLineTitlePaint.GetCurrentKLineData();
  131. if (!klineData) return;
  132. var titleData=this.GetFormatTitle({Data:klineData});
  133. if (!titleData || !IFrameSplitOperator.IsNonEmptyArray(titleData.AryText)) return;
  134. this.CalculateTooltipSize(titleData);
  135. this.CalculateShowPosition();
  136. this.DrawBG();
  137. this.DrawTooltipData(titleData);
  138. this.DrawBorder();
  139. }
  140. //[{ Text:, Color, Title:, TitleColor, }]
  141. this.GetFormatTitle=function(data)
  142. {
  143. if (!data || !data.Data) return;
  144. var item=data.Data;
  145. var upperSymbol;
  146. if (this.HQChart.Symbol) upperSymbol = this.HQChart.Symbol.toUpperCase();
  147. var defaultfloatPrecision = JSCommonCoordinateData.GetfloatPrecision(this.HQChart.Symbol);//价格小数位数
  148. var aryText=[];
  149. var result={ AryText:aryText };
  150. var text, title, color;
  151. text=IFrameSplitOperator.FormatDateString(item.Date);
  152. aryText.push({ Text:text, Color:this.TitleColor });
  153. var period = this.HQChart.Period;
  154. if (ChartData.IsMinutePeriod(period, true) && IFrameSplitOperator.IsNumber(item.Time))
  155. {
  156. text = this.HQChart.FormatTimeString(item.Time);
  157. aryText.push({ Text:text, Color:this.TitleColor });
  158. }
  159. else if (ChartData.IsSecondPeriod(period) && IFrameSplitOperator.IsNumber(item.Time))
  160. {
  161. text = this.HQChart.FormatTimeString(item.Time,"HH:MM:SS");
  162. aryText.push({ Text:text, Color:this.TitleColor });
  163. }
  164. if (IFrameSplitOperator.IsNumber(item.Open)) //开
  165. {
  166. title = g_JSChartLocalization.GetText('Tooltip-Open', this.LanguageID);
  167. var color = this.KLineTitlePaint.GetColor(item.Open, item.YClose);
  168. text = item.Open.toFixed(defaultfloatPrecision);
  169. aryText.push({Title:title, TitleColor:this.TitleColor, Text:text, Color:color });
  170. }
  171. if (IFrameSplitOperator.IsNumber(item.High)) //高
  172. {
  173. title=g_JSChartLocalization.GetText('Tooltip-High',this.LanguageID);
  174. color=this.KLineTitlePaint.GetColor(item.High,item.YClose);
  175. text=item.High.toFixed(defaultfloatPrecision);
  176. aryText.push({Title:title, TitleColor:this.TitleColor, Text:text, Color:color });
  177. }
  178. if (IFrameSplitOperator.IsNumber(item.Low)) //低
  179. {
  180. title=g_JSChartLocalization.GetText('Tooltip-Low',this.LanguageID);
  181. color=this.KLineTitlePaint.GetColor(item.Low,item.YClose);
  182. text=item.Low.toFixed(defaultfloatPrecision);
  183. aryText.push({Title:title, TitleColor:this.TitleColor, Text:text, Color:color });
  184. }
  185. if (IFrameSplitOperator.IsNumber(item.Close)) //收
  186. {
  187. title=g_JSChartLocalization.GetText('Tooltip-Close',this.LanguageID);
  188. color=this.KLineTitlePaint.GetColor(item.Close,item.YClose);
  189. text=item.Close.toFixed(defaultfloatPrecision);
  190. aryText.push({Title:title, TitleColor:this.TitleColor, Text:text, Color:color });
  191. }
  192. //涨幅
  193. title=g_JSChartLocalization.GetText('Tooltip-Increase',this.LanguageID);
  194. if (item.YClose>0)
  195. {
  196. var value = (item.Close - item.YClose) / item.YClose * 100;
  197. color = this.KLineTitlePaint.GetColor(value, 0);
  198. text = value.toFixed(2) + '%';
  199. }
  200. else
  201. {
  202. text='--.--';
  203. color = this.KLineTitlePaint.GetColor(0, 0);
  204. }
  205. aryText.push({Title:title, TitleColor:this.TitleColor, Text:text, Color:color });
  206. if (IFrameSplitOperator.IsNumber(item.Vol))
  207. {
  208. title = g_JSChartLocalization.GetText('Tooltip-Vol', this.LanguageID);
  209. text = this.HQChart.FormatValueString(item.Vol, 2, this.LanguageID);
  210. aryText.push({Title:title, TitleColor:this.TitleColor, Text:text, Color:this.TitleColor });
  211. }
  212. if (IFrameSplitOperator.IsNumber(item.Amount))
  213. {
  214. title = g_JSChartLocalization.GetText('Tooltip-Amount',this.LanguageID);
  215. text = this.HQChart.FormatValueString(item.Amount, 2, this.LanguageID);
  216. aryText.push({Title:title, TitleColor:this.TitleColor, Text:text, Color:this.TitleColor });
  217. }
  218. //持仓量
  219. if (MARKET_SUFFIX_NAME.IsChinaFutures(upperSymbol) && IFrameSplitOperator.IsNumber(item.Position))
  220. {
  221. title = g_JSChartLocalization.GetText('Tooltip-Position', this.LanguageID);
  222. text = IFrameSplitOperator.FormatValueString(item.Position, 2, this.LanguageID);
  223. aryText.push({Title:title, TitleColor:this.TitleColor, Text:text, Color:this.TitleColor });
  224. }
  225. return result;
  226. }
  227. this.CalculateTooltipSize=function(titleData)
  228. {
  229. this.Canvas.font=this.Font[0];
  230. this.LineHeight=this.Canvas.measureText("擎").width;
  231. var height=0;
  232. var maxTitleWidth=0, maxTextWidth=0, maxLineWidth=0;
  233. for(var i=0; i<titleData.AryText.length; ++i)
  234. {
  235. var item=titleData.AryText[i];
  236. if (height>0) height+=this.LineSpace;
  237. var lineWidth=0;
  238. if (item.Title)
  239. {
  240. var textWidth=this.Canvas.measureText(item.Title).width+2;
  241. if (maxTitleWidth<textWidth) maxTitleWidth=textWidth;
  242. lineWidth+=textWidth;
  243. }
  244. if (item.Text)
  245. {
  246. var textWidth=this.Canvas.measureText(item.Text).width+2;
  247. if (maxTextWidth<textWidth) maxTextWidth=textWidth;
  248. lineWidth+=textWidth;
  249. }
  250. if (maxLineWidth<lineWidth) maxLineWidth=lineWidth;
  251. height+=this.LineHeight;
  252. }
  253. this.Height=height+(this.Mergin.Top+this.Mergin.Bottom);
  254. this.Width=(maxLineWidth)+(this.Mergin.Left+this.Mergin.Right)+this.ExtendLineWidth;
  255. return { Height:this.Height, Width:this.Width, MaxTitleWidth:maxTitleWidth, MaxTextWidth:maxTextWidth };
  256. }
  257. //判断显示位置
  258. this.CalculateShowPosition=function()
  259. {
  260. this.ShowPosition=0;
  261. if (!this.LatestPoint) return;
  262. if(this.IsHScreen)
  263. {
  264. var top=this.ChartBorder.GetTop();
  265. var height=this.ChartBorder.GetHeight();
  266. var yCenter=top+height/2;
  267. if (this.LatestPoint.Y<yCenter) this.ShowPosition=1;
  268. }
  269. else
  270. {
  271. var left=this.ChartBorder.GetLeft();
  272. var width=this.ChartBorder.GetWidth();
  273. var xCenter=left+width/2;
  274. if (this.LatestPoint.X<xCenter) this.ShowPosition=1;
  275. }
  276. }
  277. this.DrawBorder = function ()
  278. {
  279. var isHScreen = (this.ChartFrame.IsHScreen === true);
  280. var left = this.GetLeft();
  281. var top = this.GetTop();
  282. this.Canvas.strokeStyle = this.BorderColor;
  283. if (isHScreen)
  284. {
  285. this.Canvas.strokeRect(this.HQChart.ToFixedPoint(left), this.HQChart.ToFixedPoint(top),
  286. this.HQChart.ToFixedRect(this.Height), this.HQChart.ToFixedRect(this.Width));
  287. }
  288. else
  289. {
  290. this.Canvas.strokeRect(this.HQChart.ToFixedPoint(left), this.HQChart.ToFixedPoint(top),
  291. this.HQChart.ToFixedRect(this.Width), this.HQChart.ToFixedRect(this.Height));
  292. }
  293. }
  294. this.DrawBG = function ()
  295. {
  296. var isHScreen = (this.ChartFrame.IsHScreen === true);
  297. var left = this.GetLeft();
  298. var top = this.GetTop();
  299. this.Canvas.fillStyle = this.BGColor;
  300. if (isHScreen) this.Canvas.fillRect(left, top, this.Height, this.Width);
  301. else this.Canvas.fillRect(left, top, this.Width, this.Height);
  302. }
  303. this.DrawTooltipData = function (titleData)
  304. {
  305. //console.log('[KLineTooltipPaint::DrawKLineData] ', item);
  306. var left = this.GetLeft();
  307. var top = this.GetTop();
  308. if (this.IsHScreen)
  309. {
  310. this.Canvas.save();
  311. var x = this.GetLeft() + this.Height, y = this.GetTop();
  312. this.Canvas.translate(x, y);
  313. this.Canvas.rotate(90 * Math.PI / 180);
  314. //x, y 作为原点
  315. left =0;
  316. top = 0;
  317. }
  318. this.Canvas.textBaseline="top";
  319. var right=left+this.Width-this.Mergin.Right;
  320. left+=this.Mergin.Left;
  321. top+=this.Mergin.Top;
  322. for(var i=0; i<titleData.AryText.length; ++i)
  323. {
  324. var item=titleData.AryText[i];
  325. if (item.Title)
  326. {
  327. this.Canvas.textAlign="left";
  328. this.Canvas.fillStyle=item.TitleColor;
  329. this.Canvas.fillText(item.Title,left,top);
  330. }
  331. if (item.Text)
  332. {
  333. this.Canvas.textAlign="right";
  334. this.Canvas.fillStyle=item.Color;
  335. this.Canvas.fillText(item.Text,right,top);
  336. }
  337. top+=this.LineHeight+this.LineSpace;
  338. }
  339. if (this.IsHScreen) this.Canvas.restore();
  340. }
  341. //设置参数接口
  342. this.SetOption = function (option)
  343. {
  344. if (option.LineHeight > 0) this.LineHeight = option.LineHeight;
  345. if (option.BGColor) this.BGColor = option.BGColor;
  346. if (option.LanguageID > 0) this.LanguageID = option.LanguageID;
  347. }
  348. }
  349. function MinuteTooltipPaint()
  350. {
  351. this.newMethod = KLineTooltipPaint; //派生
  352. this.newMethod();
  353. delete this.newMethod;
  354. this.ClassName = 'MinuteTooltipPaint';
  355. this.IsShowAveragePrice=true;
  356. this.GetTop=function()
  357. {
  358. if (this.IsHScreen)
  359. {
  360. if (this.ShowPosition==0)
  361. return this.ChartBorder.GetTop()+this.Left;
  362. else
  363. return this.ChartBorder.GetBottom()-this.Width-this.Left;
  364. }
  365. else
  366. {
  367. return this.ChartBorder.GetTop()+this.Top;
  368. }
  369. }
  370. this.GetLeft=function()
  371. {
  372. if (this.IsHScreen)
  373. {
  374. return this.ChartBorder.GetRight()-this.Height-this.Top;
  375. }
  376. else
  377. {
  378. if (this.ShowPosition==0)
  379. return this.ChartBorder.GetLeft()+this.Left;
  380. else
  381. return this.ChartBorder.GetRight()-this.Width-this.Left;
  382. }
  383. }
  384. this.GetFormatTitle=function(data)
  385. {
  386. if (!data || !data.Data) return;
  387. var item=data.Data;
  388. var upperSymbol;
  389. if (this.HQChart.Symbol) upperSymbol=this.HQChart.Symbol.toUpperCase();
  390. var defaultfloatPrecision = JSCommonCoordinateData.GetfloatPrecision(this.HQChart.Symbol);//价格小数位数
  391. var isFutures=MARKET_SUFFIX_NAME.IsFutures(upperSymbol); //期货
  392. this.YClose = this.KLineTitlePaint.YClose;
  393. this.YClose=item.YClose;
  394. if (isFutures && IFrameSplitOperator.IsNumber(item.YClearing)) this.YClose=item.YClearing;
  395. var aryText=[];
  396. var result={ AryText:aryText };
  397. var text, title, color, value;
  398. if (IFrameSplitOperator.IsNumber(item.Date))
  399. {
  400. text=IFrameSplitOperator.FormatDateString(item.Date);
  401. aryText.push({ Text:text, Color:this.TitleColor });
  402. }
  403. if (IFrameSplitOperator.IsNumber(item.Time))
  404. {
  405. text=IFrameSplitOperator.FormatTimeString(item.Time);
  406. aryText.push({ Text:text, Color:this.TitleColor });
  407. }
  408. if (IFrameSplitOperator.IsNumber(item.Close)) //最新
  409. {
  410. title = g_JSChartLocalization.GetText('Tooltip-Price', this.LanguageID);
  411. color = this.KLineTitlePaint.GetColor(item.Close, this.YClose);
  412. text = item.Close.toFixed(defaultfloatPrecision);
  413. aryText.push({Title:title, TitleColor:this.TitleColor, Text:text, Color:color });
  414. }
  415. if (IFrameSplitOperator.IsNumber(item.AvPrice) && this.IsShowAveragePrice==true) //均价
  416. {
  417. title = g_JSChartLocalization.GetText('Tooltip-AvPrice', this.LanguageID);
  418. color = this.KLineTitlePaint.GetColor(item.AvPrice, this.YClose);
  419. text = item.AvPrice.toFixed(defaultfloatPrecision);
  420. aryText.push({Title:title, TitleColor:this.TitleColor, Text:text, Color:color });
  421. }
  422. if (IFrameSplitOperator.IsNumber(item.Close) && IFrameSplitOperator.IsNumber(this.YClose)) //涨幅
  423. {
  424. title = g_JSChartLocalization.GetText('Tooltip-Increase', this.LanguageID);
  425. value = (item.Close - this.YClose) / this.YClose * 100;
  426. color = this.KLineTitlePaint.GetColor(value, 0);
  427. text = value.toFixed(2) + '%';
  428. if (this.YClose===0)
  429. {
  430. text="--.--";
  431. color=this.TitleColor;
  432. }
  433. aryText.push({Title:title, TitleColor:this.TitleColor, Text:text, Color:color });
  434. }
  435. if (IFrameSplitOperator.IsNumber(item.Vol))
  436. {
  437. title = g_JSChartLocalization.GetText('Tooltip-Vol', this.LanguageID);
  438. text = this.HQChart.FormatValueString(item.Vol, 2, this.LanguageID);
  439. aryText.push({Title:title, TitleColor:this.TitleColor, Text:text, Color:this.TitleColor });
  440. }
  441. if (IFrameSplitOperator.IsNumber(item.Amount))
  442. {
  443. title = g_JSChartLocalization.GetText('Tooltip-Amount', this.LanguageID);
  444. text = this.HQChart.FormatValueString(item.Amount, 2, this.LanguageID);
  445. aryText.push({Title:title, TitleColor:this.TitleColor, Text:text, Color:this.TitleColor });
  446. }
  447. if (MARKET_SUFFIX_NAME.IsChinaFutures(upperSymbol) && IFrameSplitOperator.IsNumber(item.Position)) //持仓量
  448. {
  449. title = g_JSChartLocalization.GetText('Tooltip-Position', this.LanguageID);
  450. text = IFrameSplitOperator.FormatValueString(item.Position, 2, this.LanguageID);
  451. aryText.push({Title:title, TitleColor:this.TitleColor, Text:text, Color:this.TitleColor });
  452. }
  453. return result;
  454. }
  455. /*
  456. this.DrawTooltipData = function (item)
  457. {
  458. //console.log('[KLineTooltipPaint::DrawKLineData] ', item);
  459. var defaultfloatPrecision = JSCommonCoordinateData.GetfloatPrecision(this.HQChart.Symbol);//价格小数位数
  460. var left = this.GetLeft() + 2;
  461. var top = this.GetTop() + 3;
  462. this.YClose = this.KLineTitlePaint.YClose;
  463. if (this.IsHScreen)
  464. {
  465. this.Canvas.save();
  466. var x = this.GetLeft() + this.Height, y = this.GetTop();
  467. this.Canvas.translate(x, y);
  468. this.Canvas.rotate(90 * Math.PI / 180);
  469. //x, y 作为原点
  470. left = 2;
  471. top = 3;
  472. }
  473. this.Canvas.textBaseline = "top";
  474. this.Canvas.textAlign = "left";
  475. this.Canvas.font = this.Font[0];
  476. var labelWidth = this.Canvas.measureText('擎: ').width;
  477. var aryDateTime = item.DateTime.split(' ');
  478. if (aryDateTime && aryDateTime.length == 2)
  479. {
  480. var text = this.HQChart.FormatDateString(aryDateTime[0]);
  481. this.Canvas.fillStyle = this.TitleColor;
  482. this.Canvas.fillText(text, left, top);
  483. top += this.LineHeight;
  484. text = this.HQChart.FormatTimeString(aryDateTime[1]);
  485. this.Canvas.fillText(text, left, top);
  486. }
  487. top += this.LineHeight;
  488. this.Canvas.fillStyle = this.TitleColor;
  489. text = g_JSChartLocalization.GetText('Tooltip-Price', this.LanguageID);
  490. this.Canvas.fillText(text, left, top);
  491. var color = this.KLineTitlePaint.GetColor(item.Close, this.YClose);
  492. text = item.Close.toFixed(defaultfloatPrecision);
  493. this.Canvas.fillStyle = color;
  494. this.Canvas.fillText(text, left + labelWidth, top);
  495. if (IFrameSplitOperator.IsNumber(item.AvPrice) && this.IsShowAveragePrice==true)
  496. {
  497. top += this.LineHeight;
  498. this.Canvas.fillStyle = this.TitleColor;
  499. text = g_JSChartLocalization.GetText('Tooltip-AvPrice', this.LanguageID);
  500. this.Canvas.fillText(text, left, top);
  501. var color = this.KLineTitlePaint.GetColor(item.AvPrice, this.YClose);
  502. var text = item.AvPrice.toFixed(defaultfloatPrecision);
  503. this.Canvas.fillStyle = color;
  504. this.Canvas.fillText(text, left + labelWidth, top);
  505. }
  506. top += this.LineHeight;
  507. this.Canvas.fillStyle = this.TitleColor;
  508. text = g_JSChartLocalization.GetText('Tooltip-Increase', this.LanguageID);
  509. this.Canvas.fillText(text, left, top);
  510. var value = (item.Close - this.YClose) / this.YClose * 100;
  511. var color = this.KLineTitlePaint.GetColor(value, 0);
  512. var text = value.toFixed(2) + '%';
  513. this.Canvas.fillStyle = color;
  514. this.Canvas.fillText(text, left + labelWidth, top);
  515. if (IFrameSplitOperator.IsNumber(item.Vol))
  516. {
  517. this.Canvas.fillStyle = this.TitleColor;
  518. top += this.LineHeight;
  519. text = g_JSChartLocalization.GetText('Tooltip-Vol', this.LanguageID);
  520. this.Canvas.fillText(text, left, top);
  521. var text = this.HQChart.FormatValueString(item.Vol, 2, this.LanguageID);
  522. this.Canvas.fillText(text, left + labelWidth, top);
  523. }
  524. if (IFrameSplitOperator.IsNumber(item.Amount))
  525. {
  526. top += this.LineHeight;
  527. text = g_JSChartLocalization.GetText('Tooltip-Amount', this.LanguageID);
  528. this.Canvas.fillText(text, left, top);
  529. var text = this.HQChart.FormatValueString(item.Amount, 2, this.LanguageID);
  530. this.Canvas.fillText(text, left + labelWidth, top);
  531. }
  532. //持仓量
  533. var upperSymbol;
  534. if (this.HQChart.Symbol) upperSymbol = this.HQChart.Symbol.toUpperCase();
  535. if (MARKET_SUFFIX_NAME.IsChinaFutures(upperSymbol) && IFrameSplitOperator.IsNumber(item.Position))
  536. {
  537. this.Canvas.fillStyle = this.TitleColor;
  538. top += this.LineHeight;
  539. text = g_JSChartLocalization.GetText('Tooltip-Position', this.LanguageID);
  540. this.Canvas.fillText(text, left, top);
  541. var text = IFrameSplitOperator.FormatValueString(item.Position, 2, this.LanguageID);
  542. this.Canvas.fillText(text, left + labelWidth, top);
  543. }
  544. if (this.IsHScreen) this.Canvas.restore();
  545. }
  546. */
  547. }
  548. //////////////////////////////////////////////////////////////////////////////
  549. // 弹幕
  550. //弹幕数据 { X:X偏移, Y:Y偏移, Text:内容, Color:颜色 }
  551. function BarrageList()
  552. {
  553. this.PlayList = []; //正在播放队列
  554. this.Cache = []; //没有播放的弹幕数据
  555. this.MinLineHeight = 40;
  556. this.Height; //高度
  557. this.Step = 1;
  558. //{Canves:画布, Right:右边坐标, Left:左边坐标, Font:默认字体 }
  559. this.GetPlayList = function (obj)
  560. {
  561. var canves = obj.Canves;
  562. var right = obj.Right;
  563. var left = obj.Left;
  564. var width = right - left;
  565. var isMoveStep = obj.IsMoveStep;
  566. var list = [];
  567. var yOffset = 0;
  568. for (var i = 0; i < this.PlayList.length; ++i)
  569. {
  570. var ary = this.PlayList[i];
  571. var lineHeight = this.MinLineHeight;
  572. if (ary.Height > this.MinLineHeight) lineHeight = ary.Height;
  573. var bAddNewItem = true; //是否需要加入新弹幕
  574. var bRemoveFirst = false; //是否删除第1个数据
  575. for (var j = 0; j < ary.Data.length; ++j)
  576. {
  577. var item = ary.Data[j];
  578. var playItem = { X: item.X, Y: yOffset, Text: item.Text, Color: item.Color, Height: lineHeight, Font: item.Font, Info: item.Info };
  579. list.push(playItem);
  580. if (!isMoveStep) continue;
  581. if (j == ary.Data.length - 1 && this.Cache.length > 0) //最后一个数据了 判断是否需要增加弹幕
  582. {
  583. bAddNewItem = false;
  584. if (!item.TextWidth)
  585. {
  586. if (item.Font && item.Font.Name) canves.font = item.Font.Name;
  587. else canves.font = obj.Font;
  588. item.TextWidth = canves.measureText(playItem.Text + '擎擎').width;
  589. }
  590. if (item.X >= item.TextWidth)
  591. bAddNewItem = true;
  592. }
  593. else if (j == 0)
  594. {
  595. bRemoveFirst = false;
  596. if (!item.TextWidth)
  597. {
  598. if (item.Font && item.Font.Name) canves.font = item.Font.Name;
  599. else canves.font = obj.Font;
  600. item.TextWidth = canves.measureText(playItem.Text + '擎擎').width;
  601. }
  602. if (item.X > width + item.TextWidth) bRemoveFirst = true;
  603. }
  604. item.X += this.Step;
  605. }
  606. if (isMoveStep && bAddNewItem && this.Cache.length > 0) //最后一个数据了 判断是否需要增加弹幕
  607. {
  608. var cacheItem = this.Cache.shift();
  609. var newItem = { X: 0, Text: cacheItem.Text, Color: cacheItem.Color, Font: cacheItem.Font, Info: cacheItem.Info };
  610. ary.Data.push(newItem);
  611. }
  612. if (isMoveStep && bRemoveFirst && ary.Data.length > 0)
  613. {
  614. var removeItem = ary.Data.shift();
  615. this.OnItemPlayEnd(obj.HQChart, removeItem);
  616. }
  617. yOffset += lineHeight;
  618. }
  619. return list;
  620. }
  621. //根据高度计算播放队列个数
  622. this.CacluatePlayLine = function (height)
  623. {
  624. this.Height = height;
  625. var lineCount = parseInt(height / this.MinLineHeight);
  626. if (this.PlayList.length < lineCount)
  627. {
  628. var addCount = lineCount - this.PlayList.length;
  629. for (var i = 0; i < addCount; ++i)
  630. {
  631. this.PlayList.push({ Data: [] });
  632. }
  633. }
  634. else if (this.PlayList.length > lineCount)
  635. {
  636. var removeCount = this.PlayList.length - lineCount;
  637. for (var i = 0; i < removeCount; ++i)
  638. {
  639. var ary = this.PlayList.pop();
  640. for (var j = 0; j < ary.Data.length; ++j)
  641. {
  642. var item = ary.Data[j];
  643. var cacheItem = { Text: item.Text, Color: item.Color, Font: item.Font, Info: item.Info };
  644. this.Cache.unshift(cacheItem);
  645. }
  646. }
  647. }
  648. JSConsole.Chart.Log(`[BarrageList::CacluatePlayLine] LineCount=${this.PlayList.length} Height=${this.Height}`)
  649. }
  650. //添加弹幕
  651. this.AddBarrage = function (barrageData)
  652. {
  653. for (var i in barrageData) {
  654. var item = barrageData[i];
  655. this.Cache.push(item);
  656. }
  657. }
  658. this.OnItemPlayEnd = function (hqChart, item) //单挑弹幕播放完毕
  659. {
  660. //监听事件
  661. var event = hqChart.GetBarrageEvent();
  662. if (!event || !event.Callback) return;
  663. event.Callback(event, item, this);
  664. }
  665. this.Count = function () { return this.Cache.length; } //未播放的弹幕个数
  666. }
  667. //背景图 支持横屏
  668. function BackgroundPaint()
  669. {
  670. this.newMethod = IExtendChartPainting; //派生
  671. this.newMethod();
  672. delete this.newMethod;
  673. this.ClassName = 'BackgroundPaint';
  674. this.IsDynamic = false;
  675. this.IsCallbackDraw = true; //在回调函数里绘制, 不在Draw()中绘制
  676. this.FrameID = 0;
  677. this.Data; //背景数据 { Start:, End:, Color:[] }
  678. this.ID = Guid(); //唯一的ID
  679. /*
  680. this.Data=
  681. [
  682. { Start:{ Date:20181201 }, End:{ Date:20181230 }, Color:'rgb(44,55,44)' } ,
  683. { Start:{ Date:20190308 }, End:{ Date:20190404 }, Color:['rgb(44,55,255)','rgb(200,55,255)'] }
  684. ]
  685. */
  686. this.ChartSubFrame;
  687. this.ChartBorder;
  688. this.KData;
  689. this.Period;
  690. this.XPointCount = 0;
  691. this.SetOption = function (option) //设置
  692. {
  693. if (option.FrameID > 0) this.FrameID = option.FrameID;
  694. if (IFrameSplitOperator.IsObjectExist(option.ID)) this.ID = option.ID;
  695. }
  696. this.Draw = function ()
  697. {
  698. if (!this.Data || !this.HQChart) return;
  699. if (!this.ChartFrame || !this.ChartFrame.SubFrame || this.ChartFrame.SubFrame.length <= this.FrameID) return;
  700. var klineChart = this.HQChart.ChartPaint[0];
  701. if (!klineChart || !klineChart.Data) return;
  702. this.ChartSubFrame = this.ChartFrame.SubFrame[this.FrameID].Frame;
  703. this.ChartBorder = this.ChartSubFrame.ChartBorder;
  704. this.KData = klineChart.Data;
  705. this.Period = this.HQChart.Period;
  706. if (!this.KData || this.KData.Data.length <= 0) return;
  707. var isHScreen = (this.ChartSubFrame.IsHScreen === true);
  708. this.XPointCount = this.ChartSubFrame.XPointCount;
  709. var xPointCount = this.ChartSubFrame.XPointCount;
  710. var firstKItem = this.KData.Data[this.KData.DataOffset];
  711. var endIndex = this.KData.DataOffset + xPointCount - 1;
  712. if (endIndex >= this.KData.Data.length) endIndex = this.KData.Data.length - 1;
  713. var endKItem = this.KData.Data[endIndex];
  714. var showData = this.GetShowData(firstKItem, endKItem);
  715. if (!showData || showData.length <= 0) return;
  716. var kLineMap = this.BuildKLineMap();
  717. var bottom = this.ChartBorder.GetBottomEx();
  718. var top = this.ChartBorder.GetTopEx();
  719. var height = this.ChartBorder.GetHeightEx();
  720. if (isHScreen)
  721. {
  722. top = this.ChartBorder.GetRightEx();
  723. bottom = this.ChartBorder.GetLeftEx();
  724. height = this.ChartBorder.GetWidthEx();
  725. }
  726. for (var i in showData)
  727. {
  728. var item = showData[i];
  729. var rt = this.GetBGCoordinate(item, kLineMap);
  730. if (!rt) continue;
  731. if (Array.isArray(item.Color))
  732. {
  733. var gradient;
  734. if (isHScreen) gradient = this.Canvas.createLinearGradient(bottom, rt.Left, top, rt.Left);
  735. else gradient = this.Canvas.createLinearGradient(rt.Left, top, rt.Left, bottom);
  736. var offset = 1 / item.Color.length;
  737. for (var i in item.Color)
  738. {
  739. gradient.addColorStop(i * offset, item.Color[i]);
  740. }
  741. this.Canvas.fillStyle = gradient;
  742. }
  743. else
  744. {
  745. this.Canvas.fillStyle = item.Color;
  746. }
  747. if (isHScreen) this.Canvas.fillRect(ToFixedRect(bottom), ToFixedRect(rt.Left), ToFixedRect(height), ToFixedRect(rt.Width));
  748. else this.Canvas.fillRect(ToFixedRect(rt.Left), ToFixedRect(top), ToFixedRect(rt.Width), ToFixedRect(height));
  749. }
  750. }
  751. this.GetShowData = function (first, end)
  752. {
  753. var aryData = [];
  754. for (var i in this.Data) {
  755. var item = this.Data[i];
  756. var showItem = {};
  757. if (item.Start.Date >= first.Date && item.Start.Date <= end.Date) showItem.Start = item.Start;
  758. if (item.End.Date >= first.Date && item.End.Date <= end.Date) showItem.End = item.End;
  759. if (showItem.Start || showItem.End)
  760. {
  761. showItem.Color = item.Color;
  762. aryData.push(showItem);
  763. }
  764. }
  765. return aryData;
  766. }
  767. this.BuildKLineMap = function ()
  768. {
  769. var isHScreen = (this.ChartSubFrame.IsHScreen === true);
  770. var dataWidth = this.ChartSubFrame.DataWidth;
  771. var distanceWidth = this.ChartSubFrame.DistanceWidth;
  772. var xOffset = this.ChartBorder.GetLeft() + distanceWidth / 2.0 + g_JSChartResource.FrameLeftMargin;
  773. if (isHScreen) xOffset = this.ChartBorder.GetTop() + distanceWidth / 2.0 + g_JSChartResource.FrameLeftMargin;
  774. var chartright = this.ChartBorder.GetRight();
  775. if (isHScreen) chartright = this.ChartBorder.GetBottom();
  776. var mapKLine = { Data: new Map() }; //Key: date / date time, Value:索引
  777. for (var i = this.KData.DataOffset, j = 0; i < this.KData.Data.length && j < this.XPointCount; ++i, ++j, xOffset += (dataWidth + distanceWidth))
  778. {
  779. var kItem = this.KData.Data[i];
  780. var left = xOffset;
  781. var right = xOffset + dataWidth;
  782. if (right > chartright) break;
  783. var x = left + (right - left) / 2;
  784. if (j == 0) mapKLine.XLeft = left;
  785. mapKLine.XRight = right;
  786. var value = { Index: i, ShowIndex: j, X: x, Right: right, Left: left, Date: kItem.Date };
  787. if (ChartData.IsMinutePeriod(this.Period, true))
  788. {
  789. var key = `Date:${kItem.Date} Time:${kItem.Time}`;
  790. value.Time = kItem.Time;
  791. }
  792. else
  793. {
  794. var key = `Date:${kItem.Date}`;
  795. }
  796. mapKLine.Data.set(key, value);
  797. }
  798. return mapKLine;
  799. }
  800. this.GetBGCoordinate = function (item, kLineMap)
  801. {
  802. var xLeft = null, xRight = null;
  803. if (item.Start)
  804. {
  805. if (ChartData.IsMinutePeriod(this.Period, true))
  806. var key = `Date:${item.Start.Date} Time:${item.Start.Time}`;
  807. else
  808. var key = `Date:${item.Start.Date}`;
  809. if (kLineMap.Data.has(key))
  810. {
  811. var findItem = kLineMap.Data.get(key);
  812. xLeft = findItem.Left;
  813. }
  814. else
  815. {
  816. for (var kItem of kLineMap.Data)
  817. {
  818. var value = kItem[1];
  819. if (value.Date > item.Start.Date)
  820. {
  821. xLeft = value.Left;
  822. break;
  823. }
  824. }
  825. }
  826. }
  827. else
  828. {
  829. xLeft = kLineMap.XLeft;
  830. }
  831. if (item.End)
  832. {
  833. if (ChartData.IsMinutePeriod(this.Period, true))
  834. var key = `Date:${item.End.Date} Time:${item.End.Time}`;
  835. else
  836. var key = `Date:${item.End.Date}`;
  837. if (kLineMap.Data.has(key))
  838. {
  839. var findItem = kLineMap.Data.get(key);
  840. xRight = findItem.Right;
  841. }
  842. else
  843. {
  844. var previousX = null;
  845. for (var kItem of kLineMap.Data)
  846. {
  847. var value = kItem[1];
  848. if (value.Date > item.End.Date)
  849. {
  850. xRight = previousX;
  851. break;
  852. }
  853. previousX = value.Right;
  854. }
  855. }
  856. }
  857. else
  858. {
  859. xRight = kLineMap.XRight;
  860. }
  861. if (xLeft == null || xRight == null) return null;
  862. return { Left: xLeft, Right: xRight, Width: xRight - xLeft };
  863. }
  864. }
  865. //弹幕
  866. function BarragePaint()
  867. {
  868. this.newMethod = IExtendChartPainting; //派生
  869. this.newMethod();
  870. delete this.newMethod;
  871. this.ClassName = 'BarragePaint';
  872. this.IsAnimation = true;
  873. this.IsEraseBG = true;
  874. this.HQChart;
  875. this.Font = g_JSChartResource.Barrage.Font;
  876. this.TextColor = g_JSChartResource.Barrage.Color;
  877. this.FontHeight = g_JSChartResource.Barrage.Height;
  878. this.BarrageList = new BarrageList(); //字幕列表
  879. this.IsMoveStep = false;
  880. this.SetOption = function (option) //设置参数接口
  881. {
  882. if (option)
  883. {
  884. if (option.Step > 0) this.BarrageList.Step = option.Step;
  885. if (option.MinLineHeight) this.Barrage.MinLineHeight = option.MinLineHeight;
  886. }
  887. }
  888. this.DrawHScreen = function ()
  889. {
  890. var height = this.ChartBorder.GetWidth();
  891. var left = this.ChartBorder.GetTop();
  892. var right = this.ChartBorder.GetBottom();
  893. var top = this.ChartBorder.GetRightEx();
  894. var wdith = this.ChartBorder.GetChartWidth();
  895. if (height != this.BarrageList.Height)
  896. this.BarrageList.CacluatePlayLine(height);
  897. this.Canvas.textBaseline = "middle";
  898. this.Canvas.textAlign = "left";
  899. var play = this.BarrageList.GetPlayList({ Canves: this.Canvas, Right: right, Left: left, Font: this.Font, IsMoveStep: this.IsMoveStep, HQChart: this.HQChart });
  900. this.IsMoveStep = false;
  901. if (!play) return;
  902. this.Canvas.save();
  903. this.Canvas.translate(this.ChartBorder.GetChartHeight(), 0);
  904. this.Canvas.rotate(90 * Math.PI / 180);
  905. for (var i = 0; i < play.length; ++i)
  906. {
  907. var item = play[i];
  908. if (item.Color) this.Canvas.fillStyle = item.Color;
  909. else this.Canvas.fillStyle = this.TextColor;
  910. if (item.Font) this.Canvas.font = item.Font.Name;
  911. else this.Canvas.font = this.Font;
  912. var fontHeight = this.FontHeight;
  913. if (item.Font && item.Font.Height > 0) fontHeight = item.Font.Height;
  914. var yOffset = item.Y + parseInt((item.Height - fontHeight) / 2);
  915. this.Canvas.fillText(item.Text, right - item.X, top + yOffset);
  916. }
  917. this.Canvas.restore();
  918. }
  919. this.Draw = function ()
  920. {
  921. if (this.ChartFrame.IsHScreen)
  922. {
  923. this.DrawHScreen();
  924. return;
  925. }
  926. var left = this.ChartBorder.GetLeft();
  927. var right = this.ChartBorder.GetRight();
  928. var top = this.ChartBorder.GetTopEx();
  929. var height = this.ChartBorder.GetHeight();
  930. if (height != this.BarrageList.Height)
  931. this.BarrageList.CacluatePlayLine(height);
  932. this.Canvas.textBaseline = "middle";
  933. this.Canvas.textAlign = "left";
  934. var play = this.BarrageList.GetPlayList({ Canves: this.Canvas, Right: right, Left: left, Font: this.Font, IsMoveStep: this.IsMoveStep, HQChart: this.HQChart });
  935. this.IsMoveStep = false;
  936. if (!play) return;
  937. for (var i = 0; i < play.length; ++i)
  938. {
  939. var item = play[i];
  940. if (item.Color) this.Canvas.fillStyle = item.Color;
  941. else this.Canvas.fillStyle = this.TextColor;
  942. if (item.Font) this.Canvas.font = item.Font.Name;
  943. else this.Canvas.font = this.Font;
  944. var fontHeight = this.FontHeight;
  945. if (item.Font && item.Font.Height > 0) fontHeight = item.Font.Height;
  946. var yOffset = item.Y + parseInt((item.Height - fontHeight) / 2);
  947. this.Canvas.fillText(item.Text, right - item.X, top + yOffset);
  948. }
  949. }
  950. }
  951. /*
  952. 扩展图形
  953. */
  954. function ExtendChartPaintFactory()
  955. {
  956. this.DataMap=new Map(
  957. [
  958. //["FrameSplitPaint", { Create:function() { return new FrameSplitPaint(); } }],
  959. ]
  960. );
  961. this.SetCallbackDraw=new Set();
  962. this.Create=function(name)
  963. {
  964. if (!this.DataMap.has(name)) return null;
  965. var item=this.DataMap.get(name);
  966. return item.Create();
  967. }
  968. this.Add=function(name, option)
  969. {
  970. this.DataMap.set(name, { Create:option.Create } );
  971. }
  972. this.AddCallbackDrawClassName=function(className)
  973. {
  974. if (!className) return;
  975. this.SetCallbackDraw.add(className);
  976. }
  977. this.IsCallbackDraw=function(className)
  978. {
  979. return this.SetCallbackDraw.has(className);
  980. }
  981. }
  982. var g_ExtendChartPaintFactory=new ExtendChartPaintFactory();
  983. //导出统一使用JSCommon命名空间名
  984. export
  985. {
  986. IExtendChartPainting,
  987. KLineTooltipPaint,
  988. BarragePaint,
  989. MinuteTooltipPaint,
  990. BackgroundPaint,
  991. g_ExtendChartPaintFactory,
  992. }
  993. /*
  994. module.exports =
  995. {
  996. JSCommonExtendChartPaint:
  997. {
  998. IExtendChartPainting: IExtendChartPainting,
  999. KLineTooltipPaint: KLineTooltipPaint,
  1000. BarragePaint: BarragePaint,
  1001. MinuteTooltipPaint: MinuteTooltipPaint,
  1002. BackgroundPaint: BackgroundPaint,
  1003. },
  1004. //单个类导出
  1005. JSCommonExtendChartPaint_IExtendChartPainting: IExtendChartPainting,
  1006. JSCommonExtendChartPaint_KLineTooltipPaint: KLineTooltipPaint,
  1007. JSCommonExtendChartPaint_BarragePaint: BarragePaint,
  1008. JSCommonExtendChartPaint_MinuteTooltipPaint: MinuteTooltipPaint,
  1009. JSCommonExtendChartPaint_BackgroundPaint: BackgroundPaint,
  1010. };
  1011. */