UNPKG

hqchart

Version:

stock chart

1,430 lines (1,177 loc) 73.1 kB
'use strict'; ///////////////////////////////////////////////////////////////////////////////////// // 股票分析思维导图 // 注:include umychart.js ///////////////////////////////////////////////////////////////////////////////////// var JSMIND_NODE_ID = { TABLE_NODE: 1, //表格节点 TEXT_NODE: 2, //文本节点 KLINE_NODE: 3 //K线图 }; function JSMindNode() { this.Name; this.ID = Guid(); this.Title; this.ChartPaint = []; this.Data; //原始的数据 this.Position = {}; //绘制的位置{X:, Y:, Width, Height} this.ScpritOption; //脚本指标设置 {} this.NodeType = 0; this.Scprit; //ScriptIndexConsole this.Children = []; //子节点 this.CreateScprit = function (obj) { var indexOption = { Name: obj.Name, ID: this.ID, Args: obj.Args, Script: obj.Script, IsSectionMode: obj.IsSectionMode, ErrorCallback: obj.ErrorCallback, FinishCallback: obj.FinishCallback }; console.log('[JSMindNode::CreateScprit] indexOption, node ', indexOption, this); this.Scprit = new ScriptIndexConsole(indexOption); }; this.SetSelected = function (selected) { for (var i in this.ChartPaint) { var item = this.ChartPaint[i]; item.IsSelected = selected; } }; this.Move = function (xStep, yStep) { this.Position.X += xStep; this.Position.Y += yStep; }; this.Draw = function () { for (var i in this.ChartPaint) { var item = this.ChartPaint[i]; item.Draw(); } }; this.SetData = function (data, jsExectute) { switch (this.NodeType) { case JSMIND_NODE_ID.TABLE_NODE: return this.SetTableData(data, jsExectute); default: return false; } }; this.SetTableData = function (data, jsExectute) { this.Data = data; var outVar = data.Out; var header = [], body = []; var headerFont = new JSFont(); var colFont = new JSFont(); headerFont.Color = 'rgb(51,51,51)'; colFont.Color = 'rgb(51,51,51)'; for (var i in outVar) { var item = outVar[i]; var headerItem = { Name: item.Name, Font: headerFont }; header.push(headerItem); var colItem = { Text: item.Data.toString(), Value: item.Data }; body.push(colItem); } var chart = this.ChartPaint[0]; chart.Data = { Header: [header], Body: [body] }; return true; }; this.SetSizeChange = function (bChanged) { for (var i in this.ChartPaint) { var item = this.ChartPaint[i]; item.SizeChange = bChanged; } }; } function JSFont() { this.Color = 'rgb(0,0,0)'; //颜色, this.Name = '微软雅黑'; //字体名字, this.Size = 12; // 字体大小 this.GetFont = function () { return this.Size * GetDevicePixelRatio() + 'px ' + this.Name; }; this.GetFontHeight = function () { return this.Size * GetDevicePixelRatio(); }; } function JSMindContainer(uielement) { this.ClassName = 'JSMindContainer'; this.Frame; //框架画法 this.Nodes = []; //JSMindNode this.Canvas = uielement.getContext("2d"); //画布 this.LinesPaint = new NodeLinesPaint(); this.LinesPaint.Canvas = this.Canvas; this.UIElement = uielement; this.MouseDrag; this.DragMode = 1; this.SelectedNode = null; //当前选中节点 this.Symbol; this.Create = function (obj) { this.UIElement.JSMindContainer = this; }; uielement.onmousedown = function (e) { if (!this.JSMindContainer) return; var self = this.JSMindContainer; if (self.DragMode == 0) return; var drag = { "Click": {}, "LastMove": {} //最后移动的位置 }; drag.Click.X = e.clientX; drag.Click.Y = e.clientY; drag.LastMove.X = e.clientX; drag.LastMove.Y = e.clientY; self.MouseDrag = drag; document.JSMindContainer = self; var oldSelectedNode = self.SelectedNode; var pixelTatio = GetDevicePixelRatio(); var pt = {}; pt.X = (e.clientX - this.getBoundingClientRect().left) * pixelTatio; pt.Y = (e.clientY - this.getBoundingClientRect().top) * pixelTatio; var selectedNode = self.GetNodeByPoint(pt); self.SelectedNode = selectedNode; if (selectedNode != oldSelectedNode) { if (oldSelectedNode) oldSelectedNode.SetSelected(false); if (selectedNode) selectedNode.SetSelected(true); self.Draw(); } /* uielement.ondblclick=function(e) { var x = e.clientX-this.getBoundingClientRect().left; var y = e.clientY-this.getBoundingClientRect().top; if(this.JSChartContainer) this.JSChartContainer.OnDoubleClick(x,y,e); } */ document.onmousemove = function (e) { if (!this.JSMindContainer) return; var self = this.JSMindContainer; var drag = self.MouseDrag; if (!drag) return; var moveSetp = Math.abs(drag.LastMove.X - e.clientX); if (self.SelectedNode) { if (Math.abs(drag.LastMove.X - e.clientX) < 5 && Math.abs(drag.LastMove.Y - e.clientY) < 5) return; if (self.MoveNode(e.clientX - drag.LastMove.X, e.clientY - drag.LastMove.Y)) { self.Draw(); } drag.LastMove.X = e.clientX; drag.LastMove.Y = e.clientY; } }; document.onmouseup = function (e) { //清空事件 document.onmousemove = null; document.onmouseup = null; //清空数据 console.log('[JSMindContainer::document.onmouseup]', e); this.JSMindContainer.UIElement.style.cursor = "default"; this.JSMindContainer.MouseDrag = null; this.JSMindContainer = null; }; }; this.Draw = function () { if (this.UIElement.width <= 0 || this.UIElement.height <= 0) return; this.Canvas.clearRect(0, 0, this.UIElement.width, this.UIElement.height); for (var i in this.Nodes) { var item = this.Nodes[i]; item.Draw(); } this.LinesPaint.SetNodes(this.Nodes); this.LinesPaint.Draw(); }; this.GetNodeByPoint = function (pt) { for (var i in this.Nodes) { var node = this.Nodes[i]; for (var j in node.ChartPaint) { var item = node.ChartPaint[j]; if (item.IsPointIn(pt) == 1) return node; } } return null; }; this.MoveNode = function (xStep, yStep) { if (!this.SelectedNode) return false; this.SelectedNode.Move(xStep, yStep); return true; }; //添加一个节点 {Name:, Title:, DataScprit:, NodeType:} this.AddNode = function (obj, nodeType) { if (nodeType == JSMIND_NODE_ID.KLINE_NODE) return this.AddKLineNode(obj); var self = this; var node = new JSMindNode(); node.Name = obj.Name; if (obj.Title) node.Title = obj.Title; if (obj.ID) node.ID = obj.ID; //外部可以指定id,但必须是唯一的 node.NodeType = nodeType; node.Position = obj.Position; if (obj.Index) //创建指标脚本 { var indexInfo = { Name: obj.Index.Name, Args: obj.Index.Args, Script: obj.Index.Script, IsSectionMode: false, ErrorCallback: function ErrorCallback(error) { self.ExecuteError(node, error); }, FinishCallback: function FinishCallback(data, jsExectute) { self.ExecuteFinish(node, data, jsExectute); } }; if (obj.Index.IsSectionMode == true) indexInfo.IsSectionMode = true; node.CreateScprit(indexInfo); } var chart = null; switch (nodeType) { case JSMIND_NODE_ID.TABLE_NODE: chart = new TableNodeChartPaint(); if (obj.ShowMode >= 1) chart.ShowMode = obj.ShowMode; //显示模式 break; case JSMIND_NODE_ID.TEXT_NODE: chart = new TextNodeChartPaint(); if (obj.Content) chart.Data.Content = obj.Content; break; default: return null; } chart.Canvas = this.Canvas; chart.Title = obj.Title; chart.Position = node.Position; node.ChartPaint.push(chart); this.Nodes.push(node); return node; }; this.AddTableNode = function (obj) { return this.AddNode(obj, JSMIND_NODE_ID.TABLE_NODE); }; this.AddTextNode = function (obj) { return this.AddNode(obj, JSMIND_NODE_ID.TEXT_NODE); }; this.AddKLineNode = function (obj) { var self = this; var node = new JSMindNode(); node.Name = obj.Name; if (obj.Title) node.Title = obj.Title; if (obj.ID) node.ID = obj.ID; //外部可以指定id,但必须是唯一的 node.NodeType = JSMIND_NODE_ID.KLINE_NODE; node.Position = obj.Position; chart = new KLineNodeChartPaint(); chart.Canvas = this.Canvas; chart.Title = obj.Title; chart.SetOption(obj); chart.Position = node.Position; node.ChartPaint.push(chart); this.Nodes.push(node); return node; }; this.ExecuteAllScript = function () { var stockObj = { HQDataType: HQ_DATA_TYPE.KLINE_ID, Stock: { Symbol: this.Symbol }, Request: { MaxDataCount: 500, MaxMinuteDayCount: 5 }, Period: 0, Right: 0 }; for (var i in this.Nodes) { var item = this.Nodes[i]; if (item.NodeType == JSMIND_NODE_ID.KLINE_NODE) { item.ChartPaint[0].ChangeSymbol(this.Symbol); } else { if (!item.Scprit) continue; item.Scprit.ExecuteScript(stockObj); } } }; this.ExecuteError = function (node, error) { console.log('[JSMindContainer::ExecuteError] node, error ', node, error); }; this.ExecuteFinish = function (node, data, jsExectute) { console.log('[JSMindContainer::ExecuteFinish] node, data, jsExectute ', node, data, jsExectute); if (node.SetData(data, jsExectute)) { node.SetSizeChange(true); this.Draw(); } }; this.LoadNodeData = function (data) //加载节点树 { for (var i in data.Root) { var item = data.Root[i]; this.LoadRootNode(item); } if (data.Lines) this.LoadNodeLines(data); }; this.LoadRootNode = function (node) //加载主节点 { var jsNode = this.AddNode(node, node.Type); if (node.Children) { for (var j in node.Children) { var childItem = node.Children[j]; this.LoadChildNode(childItem, jsNode); } } }; this.LoadChildNode = function (node, parentJSNode) //加载子节点 { if (!node || !parentJSNode) return; var jsNode = this.AddNode(node, node.Type); if (!jsNode) return; if (node.Children) { for (var i in item.Children) { var childItem = item.Children[i]; this.LoadChildNode(childItem, jsNode); } } parentJSNode.Children.push(jsNode); }; this.LoadNodeLines = function (data) { var lines = data.Lines; var linesData = []; for (var i in lines) { var item = lines[i]; var line = { Start: item.Start, End: item.End, Type: 0 }; if (item.Type >= 1) line.Type = item.Type; linesData.push(line); } this.LinesPaint.Data = linesData; }; } function INodeChartPainting() { this.Canvas; //画布 this.ChartBorder; //边框信息 this.Name; //名称 this.ClassName = 'INodeChartPainting'; //类名 this.SizeChange = true; this.IsSelected = false; //是否被选中 this.SelectedColor = 'rgb(0,40,40)'; //选中点颜色 this.SelectedPointSize = 4 * GetDevicePixelRatio(); this.Title; //标题 this.TitleFont = new JSFont(); this.TittleBG = 'rgb(220,220,220)'; //标题背景颜色 this.PenBorder = "rgb(105,105,105)"; //边框颜色 this.PixelRatio = GetDevicePixelRatio();; this.Data; //数据区 this.Position; this.Draw = function () {}; this.ToInt = function (value) { return Math.floor(value + 0.5); }; this.IsNull = function (value) { if (value === undefined) return true; if (value === null) return true; return false; }; this.IsPointIn = function (pt) // -1=不在上面, 0=表头 { return -1; }; } function TableNodeChartPaint() { this.newMethod = INodeChartPainting; //派生 this.newMethod(); delete this.newMethod; this.ClassName = 'TableNodeChartPaint'; //类名 this.Data = { Header: [], Body: [] }; //this.Data; [ [{ Name:列名1, Font:字体设置, ColFont:列字体(缺省使用Font) }, { Name:列名2, Font:字体设置 },], [{Text:显示内容, Value:数值, Font:}, ] ] /* this.Data={ Header:[ [ { Name:'列名1', Font:new JSFont() }, { Name:'列名2', Font:new JSFont(), ColFont:new JSFont() }, { Name:'列名3', Font:new JSFont(), ColFont:new JSFont() } ] ], Body:[ [ {Text:'显示内容1-1', Value:1 }, {Text:'显示内容1-2', Value:1 }, {Text:'显示内容1-3', Value:1 } ], [ {Text:'显示内容(擎擎擎擎擎擎擎)2-1', Value:1 }, {Text:'显示内容2-2', Value:1 } ,{Text:'显示内容2-3', Value:1 }], [ {Text:'显示内容3-1', Value:1 }, {Text:'显示内容(擎擎擎擎擎)3-2', Value:1 } ,{Text:'显示内容3-3', Value:1 }], [ {Text:'显示内容4-1', Value:1 }, {Text:'显示内容4-2', Value:1 } ,{Text:'显示内容(擎擎)4-3', Value:1 }] ] }; */ this.ShowMode = 1; //1 横向显示 2 竖向显示 this.HeaderCache; //横向 [ {Height:宽度 } ] | 竖向 [{Width:高度}] this.ColumeCache; //[ {Height:, ColFont:}] | 竖向 [ {Width:} ] this.RowHeightCache; //[{Height:,RowFont:}] this.WidthCache; //整个宽度 this.HeightCache; //整个高度 this.TitleHeightCache; this.X = 0; this.Y = 0; this.Width = 0; this.ColMargin = { Left: 5, Top: 5, Right: 5, Bottom: 5 }; this.TitleMargin = { Left: 5, Top: 5, Right: 5, Bottom: 5 }; this.HeaderMargin = { Left: 5, Top: 5, Right: 5, Bottom: 5 }; this.Draw = function () { this.X = this.Position.X; this.Y = this.Position.Y; this.Width = this.Position.Width; this.PixelRatio = GetDevicePixelRatio(); if (this.ShowMode == 2) { if (this.SizeChange == true) this.CacluateSize2(); this.DrawBorder2(); this.DrawTitle(); this.DrawHeader2(); this.DrawBody2(); } else { if (this.SizeChange == true) this.CacluateSize(); this.DrawBorder(); this.DrawTitle(); this.DrawHeader(); this.DrawBody(); } this.DrawSelectPoint(); this.SizeChange = false; }; this.DrawTitle = function () { var pixelRatio = this.PixelRatio; var titleHeight = this.TitleHeightCache; this.Canvas.font = this.TitleFont.GetFont(); this.Canvas.textAlign = 'center'; this.Canvas.textBaseline = 'middle'; this.Canvas.font = this.TextFont; this.Canvas.fillStyle = this.TitleFont.Color; this.Canvas.fillText(this.Title, this.X + this.ToInt(this.WidthCache / 2), this.Y + this.ToInt(titleHeight / 2)); this.Y += this.TitleHeightCache; }; this.DrawHeader = function () { var x = this.X, y = this.Y; var aryHeader = this.Data.Header; var pixelRatio = this.PixelRatio; this.Canvas.textAlign = 'center'; this.Canvas.textBaseline = 'bottom'; for (var i in aryHeader) { var header = aryHeader[i]; var cacheItem = this.HeaderCache[i]; var headerHeight = cacheItem.Height; var left = x, top = y; for (var j in header) { var item = header[j]; var itemWidth = this.ColumeCache[j].Width; this.Canvas.font = item.Font.GetFont(); this.Canvas.fillStyle = item.Font.Color; this.Canvas.fillText(item.Name, left + this.ToInt(itemWidth / 2), top + headerHeight - this.ColMargin.Bottom * pixelRatio); left += itemWidth; } y += headerHeight; } this.Y = y; }; this.DrawBody = function () { var x = this.X, y = this.Y; var body = this.Data.Body; var pixelRatio = this.PixelRatio; this.Canvas.textAlign = 'left'; this.Canvas.textBaseline = 'bottom'; var rowHeight = this.RowHeightCache; for (var i in body) { var row = body[i]; var left = x, top = y; for (var j in row) { var font = this.ColumeCache[j].Font; var colWidth = this.ColumeCache[j].Width; var item = row[j]; this.Canvas.font = font.GetFont(); //计算宽度 this.Canvas.font = font.GetFont(); this.Canvas.fillStyle = font.Color; this.Canvas.fillText(item.Text, left + this.ColMargin.Left * pixelRatio, top + rowHeight - this.ColMargin.Bottom * pixelRatio); left += colWidth; } y += rowHeight; } }; this.DrawBorder = function () { this.Canvas.strokeStyle = this.PenBorder; var left = this.X, top = this.Y; var right = left + this.WidthCache; if (this.TittleBG) //标题背景 { this.Canvas.fillStyle = this.TittleBG; this.Canvas.fillRect(left, top, this.WidthCache, this.TitleHeightCache); } this.Canvas.beginPath(); //标题 this.Canvas.moveTo(left, ToFixedPoint(top + this.TitleHeightCache)); this.Canvas.lineTo(right, ToFixedPoint(top + this.TitleHeightCache)); top += this.TitleHeightCache; var tableTop = top; for (var i in this.HeaderCache) //标题 { var item = this.HeaderCache[i]; this.Canvas.moveTo(left, ToFixedPoint(top + item.Height)); this.Canvas.lineTo(right, ToFixedPoint(top + item.Height)); top += item.Height; } var body = this.Data.Body; for (var i in body) //行 { if (i < body.length - 1) { this.Canvas.moveTo(left, ToFixedPoint(top + this.RowHeightCache)); this.Canvas.lineTo(right, ToFixedPoint(top + this.RowHeightCache)); } top += this.RowHeightCache; } for (var i in this.ColumeCache) { var item = this.ColumeCache[i]; if (i < this.ColumeCache.length - 1) { this.Canvas.moveTo(ToFixedPoint(left + item.Width), ToFixedPoint(tableTop)); this.Canvas.lineTo(ToFixedPoint(left + item.Width), ToFixedPoint(top)); } left += item.Width; } //四周边框 this.Canvas.moveTo(ToFixedPoint(this.X), ToFixedPoint(this.Y)); this.Canvas.lineTo(ToFixedPoint(right), ToFixedPoint(this.Y)); this.Canvas.lineTo(ToFixedPoint(right), ToFixedPoint(top)); this.Canvas.lineTo(ToFixedPoint(this.X), ToFixedPoint(top)); this.Canvas.lineTo(ToFixedPoint(this.X), ToFixedPoint(this.Y)); this.Canvas.stroke(); }; this.CacluateSize = function () { var pixelRatio = this.PixelRatio; this.HeaderCache = []; this.ColumeCache = []; this.RowHeightCache = null; this.WidthCache = null; this.HeightCache = 0; this.TitleHeightCache = null; this.TitleHeightCache = this.TitleFont.GetFontHeight() + (this.TitleMargin.Top + this.TitleMargin.Bottom) * pixelRatio; this.Canvas.font = this.TitleFont.GetFont(); this.WidthCache = this.Canvas.measureText(this.Title).width + (this.TitleMargin.Left + this.TitleMargin.Right) * pixelRatio; this.HeightCache += this.TitleHeightCache; var aryHeader = this.Data.Header; for (var i in aryHeader) { var header = aryHeader[i]; var headerCache = { Height: null }; this.RowHeightCache = null; for (var j in header) { var item = header[j]; var fontHeight = item.Font.GetFontHeight() + (this.HeaderMargin.Top + this.HeaderMargin.Bottom) * pixelRatio; if (headerCache.Height == null || headerCache.Height < fontHeight) headerCache.Height = fontHeight; //表头高度 this.Canvas.font = item.Font.GetFont(); //计算宽度 var textWidth = this.ToInt(this.Canvas.measureText(item.Name).width + (this.HeaderMargin.Left + this.HeaderMargin.Right) * pixelRatio); var colFont = item.ColFont ? item.ColFont : item.Font; //行高 var rowHeight = colFont.GetFontHeight() + (this.ColMargin.Top + this.ColMargin.Bottom) * pixelRatio; if (this.RowHeightCache == null || this.RowHeightCache < rowHeight) this.RowHeightCache = rowHeight; if (this.IsNull(this.ColumeCache[j])) { this.ColumeCache[j] = { Font: colFont, Width: textWidth }; } else { var colItem = this.ColumeCache[j]; colItem.Font = colFont; if (colItem.Width < textWidth) colItem.Width = textWidth; } } this.HeaderCache[i] = headerCache; this.HeightCache += headerCache.Height; } //计算表格每列最大宽度 var body = this.Data.Body; for (var i in body) { var row = body[i]; for (var j in row) { var font = this.ColumeCache[j].Font; var item = row[j]; this.Canvas.font = font.GetFont(); //计算宽度 var textWidth = this.ToInt(this.Canvas.measureText(item.Text).width + (this.ColMargin.Left + this.ColMargin.Right) * pixelRatio); if (this.ColumeCache[j].Width < textWidth) this.ColumeCache[j].Width = textWidth; } this.HeightCache += this.RowHeightCache; } var totalWidth = 0; for (var i in this.ColumeCache) { totalWidth += this.ColumeCache[i].Width; } if (totalWidth > this.WidthCache) { this.WidthCache = totalWidth; } }; //横向 this.CacluateSize2 = function () { var pixelRatio = this.PixelRatio; this.HeaderCache = []; this.ColumeCache = []; this.RowHeightCache = []; this.WidthCache = null; this.HeightCache = 0; this.TitleHeightCache = null; this.TitleHeightCache = this.TitleFont.GetFontHeight() + (this.TitleMargin.Top + this.TitleMargin.Bottom) * pixelRatio; this.Canvas.font = this.TitleFont.GetFont(); this.WidthCache = this.Canvas.measureText(this.Title).width + (this.TitleMargin.Left + this.TitleMargin.Right) * pixelRatio; this.HeightCache += this.TitleHeightCache; var aryHeader = this.Data.Header; for (var i in aryHeader) { var header = aryHeader[i]; var headerCache = null; if (this.IsNull(this.HeaderCache[i])) headerCache = { Width: null };else headerCache = this.HeaderCache[i]; for (var j in header) { var item = header[j]; var fontHeight = item.Font.GetFontHeight() + (this.HeaderMargin.Top + this.HeaderMargin.Bottom) * pixelRatio; this.Canvas.font = item.Font.GetFont(); //计算宽度 var textWidth = this.ToInt(this.Canvas.measureText(item.Name).width + (this.HeaderMargin.Left + this.HeaderMargin.Right) * pixelRatio); if (headerCache.Width == null || headerCache.Width < textWidth) headerCache.Width = textWidth; var colFont = item.ColFont ? item.ColFont : item.Font; //行高 var rowCache = null; if (this.IsNull(this.RowHeightCache[j])) { rowCache = { Height: fontHeight, RowFont: colFont }; this.RowHeightCache[j] = rowCache; } else { rowCache = this.RowHeightCache[j]; if (rowCache.Heigh < fontHeight) rowCache.Heigh = fontHeight; rowCache.RowFont = colFont; } } this.HeaderCache[i] = headerCache; } //计算表格每列最大宽度 var body = this.Data.Body; for (var i in body) { var row = body[i]; var colCache = null; if (this.IsNull(this.ColumeCache[i])) { colCache = { Width: null }; this.ColumeCache[i] = colCache; } else { colCache = this.ColumeCache[j]; } for (var j in row) { var font = this.RowHeightCache[j].RowFont; var item = row[j]; this.Canvas.font = font.GetFont(); //计算宽度 var textWidth = this.ToInt(this.Canvas.measureText(item.Text).width + (this.ColMargin.Left + this.ColMargin.Right) * pixelRatio); if (colCache.Width < textWidth) colCache.Width = textWidth; } } var totalWidth = 0; for (var i in this.HeaderCache) { totalWidth += this.HeaderCache[i].Width; } for (var i in this.ColumeCache) { totalWidth += this.ColumeCache[i].Width; } if (totalWidth > this.WidthCache) { this.WidthCache = totalWidth; } for (var i in this.RowHeightCache) { this.HeightCache += this.RowHeightCache[j].Height; } }; this.DrawHeader2 = function () { var x = this.X, y = this.Y; var aryHeader = this.Data.Header; var pixelRatio = this.PixelRatio; this.Canvas.textAlign = 'center'; this.Canvas.textBaseline = 'bottom'; for (var i in aryHeader) { var header = aryHeader[i]; var cacheItem = this.HeaderCache[i]; var headerWidth = cacheItem.Width; var left = x, top = y; for (var j in header) { var item = header[j]; var itemHeight = this.RowHeightCache[i].Height; this.Canvas.font = item.Font.GetFont(); this.Canvas.fillStyle = item.Font.Color; this.Canvas.fillText(item.Name, left + this.ToInt(headerWidth / 2), top + itemHeight - this.ColMargin.Bottom * pixelRatio); top += itemHeight; } x += headerWidth; } this.X = x; }; this.DrawBody2 = function () { var x = this.X, y = this.Y; var body = this.Data.Body; var pixelRatio = this.PixelRatio; this.Canvas.textAlign = 'left'; this.Canvas.textBaseline = 'bottom'; var left = x, top = y; for (var i in body) { var row = body[i]; var colWidth = this.ColumeCache[i].Width; var top = y; for (var j in row) { var font = this.RowHeightCache[j].RowFont; var colHeight = this.RowHeightCache[j].Height; var item = row[j]; this.Canvas.font = font.GetFont(); this.Canvas.fillStyle = font.Color; this.Canvas.fillText(item.Text, left + this.ColMargin.Left * pixelRatio, top + colHeight - this.ColMargin.Bottom * pixelRatio); top += colHeight; } left += colWidth; } }; this.DrawBorder2 = function () { this.Canvas.strokeStyle = this.PenBorder; var left = this.X, top = this.Y; var right = left + this.WidthCache; if (this.TittleBG) //标题背景 { this.Canvas.fillStyle = this.TittleBG; this.Canvas.fillRect(left, top, this.WidthCache, this.TitleHeightCache); } this.Canvas.beginPath(); //标题 this.Canvas.moveTo(left, ToFixedPoint(top + this.TitleHeightCache)); this.Canvas.lineTo(right, ToFixedPoint(top + this.TitleHeightCache)); top += this.TitleHeightCache; var tableTop = top; for (var i in this.RowHeightCache) { if (i < this.RowHeightCache.length - 1) { var item = this.RowHeightCache[i]; this.Canvas.moveTo(ToFixedPoint(left), ToFixedPoint(top + item.Height)); this.Canvas.lineTo(ToFixedPoint(right), ToFixedPoint(top + item.Height)); } top += item.Height; } left = this.X; for (var i in this.HeaderCache) //标题 { var item = this.HeaderCache[i]; this.Canvas.moveTo(ToFixedPoint(left + item.Width), ToFixedPoint(tableTop)); this.Canvas.lineTo(ToFixedPoint(left + item.Width), ToFixedPoint(top)); left += item.Width; } for (var i in this.ColumeCache) { var item = this.ColumeCache[i]; if (i < this.ColumeCache.length - 1) { this.Canvas.moveTo(ToFixedPoint(left + item.Width), ToFixedPoint(tableTop)); this.Canvas.lineTo(ToFixedPoint(left + item.Width), ToFixedPoint(top)); } left += item.Width; } //四周边框 this.Canvas.moveTo(ToFixedPoint(this.X), ToFixedPoint(this.Y)); this.Canvas.lineTo(ToFixedPoint(right), ToFixedPoint(this.Y)); this.Canvas.lineTo(ToFixedPoint(right), ToFixedPoint(top)); this.Canvas.lineTo(ToFixedPoint(this.X), ToFixedPoint(top)); this.Canvas.lineTo(ToFixedPoint(this.X), ToFixedPoint(this.Y)); this.Canvas.stroke(); }; this.DrawSelectPoint = function () { if (!this.IsSelected) return; var pointSize = this.SelectedPointSize; var left = this.Position.X, top = this.Position.Y; var right = left + this.WidthCache, bottom = top + this.HeightCache; this.Canvas.fillStyle = this.SelectedColor; this.Canvas.fillRect(left, top, pointSize, pointSize); this.Canvas.fillRect(this.ToInt(left + this.WidthCache / 2), top, pointSize, pointSize); this.Canvas.fillRect(right - pointSize, top, pointSize, pointSize); this.Canvas.fillRect(left, this.ToInt(top + this.HeightCache / 2), pointSize, pointSize); this.Canvas.fillRect(right - pointSize, this.ToInt(top + this.HeightCache / 2), pointSize, pointSize); this.Canvas.fillRect(left, bottom - pointSize, pointSize, pointSize); this.Canvas.fillRect(this.ToInt(left + this.WidthCache / 2), bottom - pointSize, pointSize, pointSize); this.Canvas.fillRect(right - pointSize, bottom - pointSize, pointSize, pointSize); }; this.IsPointIn = function (pt) // -1=不在上面, 0=表头 { var left = this.Position.X; var top = this.Position.Y; var right = left + this.WidthCache; var bottom = top + this.TitleHeightCache; if (pt.X > left && pt.X < right && pt.Y > top && pt.Y < bottom) return 1; return -1; }; } function TextNodeChartPaint() { this.newMethod = INodeChartPainting; //派生 this.newMethod(); delete this.newMethod; this.ClassName = 'TextNodeChartPaint'; //类名 this.TitleMargin = { Left: 5, Top: 5, Right: 5, Bottom: 5 }; this.ContentMargin = { Left: 5, Top: 5, Right: 5, Bottom: 5 }; this.ContentFont = new JSFont(); this.Data = {}; //this.Data={ Content:内容 } //临时变量 this.X = 0; this.Y = 0; this.WidthCache; this.HeightCache; this.TitleHeightCache; this.Draw = function () { this.X = this.Position.X; this.Y = this.Position.Y; this.PixelRatio = GetDevicePixelRatio(); if (this.SizeChange) this.CacluateSize(); this.DrawBorder(); this.DrawTitle(); this.DrawContent(); this.DrawSelectPoint(); this.SizeChange = false; }; this.CacluateSize = function () { var pixelRatio = this.PixelRatio; this.WidthCache = 0; this.HeightCache = 0; this.TitleHeightCache = null; this.TitleHeightCache = this.TitleFont.GetFontHeight() + (this.TitleMargin.Top + this.TitleMargin.Bottom) * pixelRatio; this.Canvas.font = this.TitleFont.GetFont(); this.WidthCache = this.Canvas.measureText(this.Title).width + (this.TitleMargin.Left + this.TitleMargin.Right) * pixelRatio; this.HeightCache += this.TitleHeightCache; if (this.Data && this.Data.Content) { this.Canvas.font = this.ContentFont.GetFont(); var contentHeight = this.ContentFont.GetFontHeight() + (this.ContentMargin.Top + this.ContentMargin.Bottom) * pixelRatio; this.HeightCache += contentHeight; var contentWidth = this.Canvas.measureText(this.Data.Content).width + (this.ContentMargin.Left + this.ContentMargin.Right) * pixelRatio; if (contentWidth > this.WidthCache) this.WidthCache = contentWidth; } if (this.WidthCache < this.Position.Width) this.Width = this.Position.Width; }; this.DrawTitle = function () { this.Canvas.textAlign = 'center'; this.Canvas.textBaseline = 'middle'; this.Canvas.font = this.TitleFont.GetFont(); this.Canvas.fillStyle = this.TitleFont.Color; this.Canvas.fillText(this.Title, this.X + this.ToInt(this.WidthCache / 2), this.Y + this.ToInt(this.TitleHeightCache / 2)); this.Y += this.TitleHeightCache; }; this.DrawContent = function () { if (!this.Data || !this.Data.Content) return; var pixelRatio = this.PixelRatio; this.Canvas.textAlign = 'left'; this.Canvas.textBaseline = 'bottom'; this.Canvas.font = this.ContentFont.GetFont(); this.Canvas.fillStyle = this.ContentFont.Color; this.Canvas.fillText(this.Data.Content, this.X + this.ContentMargin.Left * pixelRatio, this.Position.Y + this.HeightCache - this.ContentMargin.Bottom * pixelRatio); }; this.DrawBorder = function () { this.Canvas.strokeStyle = this.PenBorder; var left = this.X, top = this.Y; var right = left + this.WidthCache; var bottom = top + this.HeightCache; if (this.TittleBG) //标题背景 { this.Canvas.fillStyle = this.TittleBG; this.Canvas.fillRect(left, top, this.WidthCache, this.TitleHeightCache); } this.Canvas.beginPath(); //标题 this.Canvas.moveTo(left, ToFixedPoint(top + this.TitleHeightCache)); this.Canvas.lineTo(right, ToFixedPoint(top + this.TitleHeightCache)); top += this.TitleHeightCache; var tableTop = top; //四周边框 this.Canvas.moveTo(ToFixedPoint(this.X), ToFixedPoint(this.Y)); this.Canvas.lineTo(ToFixedPoint(right), ToFixedPoint(this.Y)); this.Canvas.lineTo(ToFixedPoint(right), ToFixedPoint(bottom)); this.Canvas.lineTo(ToFixedPoint(this.X), ToFixedPoint(bottom)); this.Canvas.lineTo(ToFixedPoint(this.X), ToFixedPoint(this.Y)); this.Canvas.stroke(); }; this.IsPointIn = function (pt) // -1=不在上面, 0=表头 { var left = this.Position.X; var top = this.Position.Y; var right = left + this.WidthCache; var bottom = top + this.TitleHeightCache; if (pt.X > left && pt.X < right && pt.Y > top && pt.Y < bottom) return 1; return -1; }; this.DrawSelectPoint = function () { if (!this.IsSelected) return; var pointSize = this.SelectedPointSize; var left = this.Position.X, top = this.Position.Y; var right = left + this.WidthCache, bottom = top + this.HeightCache; this.Canvas.fillStyle = this.SelectedColor; this.Canvas.fillRect(left, top, pointSize, pointSize); this.Canvas.fillRect(this.ToInt(left + this.WidthCache / 2), top, pointSize, pointSize); this.Canvas.fillRect(right - pointSize, top, pointSize, pointSize); this.Canvas.fillRect(left, this.ToInt(top + this.HeightCache / 2), pointSize, pointSize); this.Canvas.fillRect(right - pointSize, this.ToInt(top + this.HeightCache / 2), pointSize, pointSize); this.Canvas.fillRect(left, bottom - pointSize, pointSize, pointSize); this.Canvas.fillRect(this.ToInt(left + this.WidthCache / 2), bottom - pointSize, pointSize, pointSize); this.Canvas.fillRect(right - pointSize, bottom - pointSize, pointSize, pointSize); }; } function NodeLinesPaint() //节点间连线 { this.ClassName = 'NodeLinesPaint'; this.Data = []; //[{ Start:, End: , Type:} this.Nodes = new Map(); //key:id value:node 方便查找使用map this.Canvas; //画布 this.SetNodes = function (nodes) { this.Nodes.clear(); for (var i in nodes) { var item = nodes[i]; this.Nodes.set(item.ID, item); } }; this.Draw = function () { for (var i in this.Data) { var item = this.Data[i]; var startNode = this.Nodes.get(item.Start); var endNode = this.Nodes.get(item.End); if (!startNode || !endNode) continue; var startChart = startNode.ChartPaint[0]; var endChart = endNode.ChartPaint[0]; var line = this.GetLineDirection(startChart, endChart); this.Canvas.strokeStyle = this.PenBorder; this.Canvas.beginPath(); this.Canvas.moveTo(ToFixedPoint(line.Start.X), ToFixedPoint(line.Start.Y)); this.Canvas.lineTo(ToFixedPoint(line.End.X), ToFixedPoint(line.End.Y)); this.Canvas.stroke(); } }; this.GetLineDirection = function (start, end) //获取2个图新的线段位置 1-8 随时针8个方位 { //图形中心点 var left = start.Position.X; var top = start.Position.Y; var width = start.WidthCache; var height = start.HeightCache; var p1 = { X: left + width / 2, Y: top + height / 2 }; //中心点 left2 = end.Position.X; top2 = end.Position.Y; width2 = end.WidthCache; height2 = end.HeightCache; var p2 = { X: left2 + width2 / 2, Y: top2 + height2 / 2 }; //中心点 var angle = this.GetAngle(p1, p2); //角度 var angle2 = this.GetAngle(p2, p1); console.log('[NodeLinesPaint::GetLineDirection] p1, p2, angle', p1, p2, angle, angle2); var linePt; if (angle > 325 || angle <= 35) linePt = { X: left + width / 2, Y: top };else if (angle > 35 && angle <= 55) linePt = { X: left + width, Y: top };else if (angle > 55 && angle <= 125) linePt = { X: left + width, Y: top + height / 2 };else if (angle > 125 && angle <= 145) linePt = { X: left + width, Y: top + height };else if (angle > 145 && angle <= 215) linePt = { X: left + width / 2, Y: top + height };else if (angle > 215 && angle <= 235) linePt = { X: left, Y: top + height };else if (angle > 235 && angle <= 305) linePt = { X: left, Y: top + height / 2 };else if (angle > 305 && angle <= 325) linePt = { X: left, Y: top }; var linePt2; if (angle2 > 325 || angle2 <= 35) linePt2 = { X: left2 + width2 / 2, Y: top2 };else if (angle2 > 35 && angle2 <= 55) linePt2 = { X: left2 + width2, Y: top2 };else if (angle2 > 55 && angle2 <= 125) linePt2 = { X: left2 + width2, Y: top2 + height2 / 2 };else if (angle2 > 125 && angle2 <= 145) linePt2 = { X: left2 + width2, Y: top2 + height2 };else if (angle2 > 145 && angle2 <= 215) linePt2 = { X: left2 + width2 / 2, Y: top2 + height2 };else if (angle2 > 215 && angle2 <= 235) linePt2 = { X: left2, Y: top2 + height2 };else if (angle2 > 235 && angle2 <= 305) linePt2 = { X: left2, Y: top2 + height2 / 2 };else if (angle2 > 305 && angle2 <= 325) linePt2 = { X: left2, Y: top2 }; console.log('[NodeLinesPaint::GetLineDirection] linePt linePt2 ', linePt, linePt2); return { Start: linePt, End: linePt2 }; }; this.GetAngle = function (p1, p2) { var x = Math.abs(p1.X - p2.X); var y = Math.abs(p1.Y - p2.Y); var z = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); var cos = y / z; var radina = Math.acos(cos); //用反三角函数求弧度 var angle = Math.floor(180 / (Math.PI / radina)); //将弧度转换成角度 if (p2.X > p1.X && p2.Y > p1.Y) angle = 180 - angle; //第2个点在第四象限 if (p2.X == p1.X && p2.Y > p1.Y) angle = 180; //第2个点在y轴负方向上 if (p2.X > p1.X && p2.Y == p1.Y) angle = 90; //第2个点在x轴正方向上 if (p2.X < p1.X && p2.Y > p1.Y) angle = 180 + angle; //第2个点在第三象限 if (p2.X < p1.X && p2.Y == p1.Y) angle = 270; //第2个点在x轴负方向 if (p2.X < p1.X && p2.Y < p1.Y) angle = 360 - angle; //第2个点在第二象限 return angle; }; } function JSMind(divElement) { this.DivElement = divElement; this.JSMindContainer; //画图控件 //h5 canvas this.CanvasElement = document.createElement("canvas"); this.CanvasElement.className = 'jsmind-drawing'; this.CanvasElement.id = Guid(); this.CanvasElement.setAttribute("tabindex", 0); if (!divElement.hasChildNodes("canvas")) { divElement.appendChild(this.CanvasElement); } this.OnSize = function () { //画布大小通过div获取 var height = parseInt(this.DivElement.style.height.replace("px", "")); this.CanvasElement.height = height; this.CanvasElement.width = parseInt(this.DivElement.style.width.replace("px", "")); this.CanvasElement.style.width = this.CanvasElement.width + 'px'; this.CanvasElement.style.height = this.CanvasElement.height + 'px'; var pixelTatio = GetDevicePixelRatio(); //获取设备的分辨率 this.CanvasElement.height *= pixelTatio; this.CanvasElement.width *= pixelTatio; console.log('[JSMind::OnSize] devicePixelRatio=' + window.devicePixelRatio + ', height=' + this.CanvasElement.height + ', width=' + this.CanvasElement.width); if (this.JSMindContainer && this.JSMindContainer.Frame) this.JSMindContainer.Frame.SetSizeChange(true); if (this.JSMindContainer) this.JSMindContainer.Draw(); }; this.SetOption = function (option) { var chart = new JSMindContainer(this.CanvasElement); chart.Create({}); if (option.NodeTree) chart.LoadNodeData(option.NodeTree); if (option.Symbol) chart.Symbol = option.Symbol; chart.ExecuteAllScript(); this.JSMindContainer = chart; this.DivElement.JSMind = this; //div中保存一份 this.JSMindContainer.Draw(); }; this.AddTextNode = function (obj) { if (this.JSMindContainer && typeof this.JSMindContainer.AddTextNode == 'function') this.JSMindContainer.AddTextNode(obj); }; this.AddTableNode = function (obj) { if (this.JSMindContainer && typeof this.JSMindContainer.AddTableNode == 'function') this.JSMindContainer.AddTableNode(obj); }; } //初始化 JSMind.Init = function (divElement) { var jsChartControl = new JSMind(divElement); jsChartControl.OnSize(); return jsChartControl; }; //边框信息 function NodeChartBorder() { //四周间距 this.Left = 50; this.Right = 80; this.Top = 0; this.Bottom = 50; this.TitleHeight = 24; //标题高度 this.TopSpace = 0; this.BottomSpace = 0; this.X = 10; this.Y = 10; this.Width = 500; this.Height = 400; this.GetChartWidth = function () { return this.Width; }; this.GetChartHeight = function () { return this.Height; }; this.GetLeft = function () { return this.X + this.Left; }; this.GetRight = function () { return this.X + this.Width - this.Right; }; this.GetTop = function () { return this.Y + this.Top; }; this.GetTopEx = function () //去掉标题,上面间距 { return this.Y + this.Top + this.TitleHeight + this.TopSpace; }; this.GetTopTitle = function () //去掉标题 { return this.Y + this.Top + this.TitleHeight; }; this.GetBottom = function () { return this.Y + this.Height - this.Bottom; }; this.GetBottomEx = function () { return this.Y + this.Height - this.Bottom - this.BottomSpace; }; this.GetWidth = function () { return this.Width - this.Left - this.Right; }; this.GetHeight = function () { return this.Height - this.Top - this.Bottom; }; this.GetHeightEx = function () //去掉标题的高度, 上下间距 { return this.Height - this.Top - this.Bottom - this.TitleHeight - this.TopSpace - this.BottomSpace; }; this.GetRightEx = function () //横屏去掉标题高度的 上面间距 { return this.UIElement.width - this.Right - this.TitleHeight - this.TopSpace; }; this.GetWidthEx = function () //横屏去掉标题宽度 上下间距 { return this.UIElement.width - this.Left - this.Right - this.TitleHeight - this.TopSpace - this.BottomSpace; }; this.GetLeftEx = function () //横屏 { return this.Left + this.BottomSpace; }; this.GetRightTitle = function () //横屏 { return this.UIElement.width - this.Right - this.TitleHeight; }; this.GetTitleHeight = function () { return this.TitleHeight; }; } //K线节点 function KLineNodeChartPaint(canvasElement) { this.newMethod = INodeChartPainting; //派生 this.newMethod(); delete this.newMethod; this.ClassName = 'KLineNodeChartPaint'; //类名 this.TitleMargin = { Left: 5, Top: 5, Right: 5, Bottom: 5 }; this.Period = 0; //周期 0=日线 1=周线 2=月线 3=年线 4=1分钟 5=5分钟 6=15分钟 7=30分钟 8=60分钟 this.Right = 0; //复权 0 不复权 1 前复权 2 后复权 this.SourceData; //原始的历史数据 this.MaxReqeustDataCount = 3000; //数据个数 this.MaxRequestMinuteDayCount = 5; //分钟数据请求的天数 this.PageSize = 200; //每页数据个数 this.Frame; //框架画法 this.ChartPaint = []; //图形画法 this.WindowIndex = []; this.TitlePaint = []; //标题画法 this.KLineApiUrl = g_JSChartResource.Domain + "/API/KLine2"; //历史K线api地址 this.MinuteKLineApiUrl = g_JSChartResource.Domain + '/API/KLine3'; //历史分钟数据 this.CursorIndex = 0; //十字光标X轴索引 this.LastPoint = new Point(); //鼠标位置 this.FrameSplitData = new Map(); this.FrameSplitData.set("double", new SplitData()); this.FrameSplitData.set("price", new PriceSplitData()); this.WidthCache; this.HeightCache; this.Draw = function () { if (this.X != this.Position.X || this.Y != this.Position.Y || !this.WidthCache != this.Position.Width || this.HeightCache != this.Position.Height) { this.CacluateTitleSize(); this.X = this.Position.X; this.Y = this.Position.Y; this.WidthCache = this.Position.Width; this.HeightCache = this.Position.Height; this.Frame.ChartBorder.X = this.X; this.Frame.ChartBorder.Y = this.Y + this.TitleHeightCache; this.Frame.ChartBorder.Width = this.WidthCache; this.Frame.ChartBorder.Height = this.HeightCache - this.TitleHeightCache; this.Frame.SetSizeChage(true); } this.PixelRatio = GetDevicePixelRatio(); this.DrawBorder(); this.DrawTitle(); this.Canvas.save(); this.Canvas.lineWidth = this.PixelRatio; //手机端需要根据分辨率比调整线段宽度 if (this.ChartSplashPaint && this.ChartSplashPaint.IsEnableSplash) { this.Frame.Draw(); this.ChartSplashPaint.Draw(); return; } //框架 this.Frame.Draw(); //框架内图形 for (var i in this.ChartPaint) { var item = this.ChartPaint[i]; if (item.IsDrawFirst) item.Draw(); } for (var i in this.ChartPaint) { var item = this.ChartPaint[i]; if (!item.IsDrawFirst) item.Draw(); } for (var i in this.ChartPaintEx) { var item = this.ChartPaintEx[i]; item.Draw(); } //叠加股票 for (var i in this.OverlayChartPaint) { var item = this.OverlayChartPaint[i]; item.Draw(); } if (this.Frame.DrawOveraly) this.Frame.DrawOveraly(); //画叠加指标 //固定扩展图形 for (var i in this.ExtendChartPaint) { var item = this.ExtendChartPaint[i]; if (!item.IsDynamic && item.IsAnimation == false) item.Draw(); } if (this.Frame.DrawInsideHorizontal) this.Frame.DrawInsideHorizontal(); this.Frame.DrawLock(); this.Frame.Snapshot(); this.Canvas.restore(); this.DrawSelectPoint(); this.SizeChange = false; }; this.CacluateTitleSize = function () { var pixelRatio = this.PixelRatio; this.TitleHeightCache = null; this.TitleHeightCache = this.TitleFont.GetFontHeight() + (this.TitleMargin.Top + this.TitleMargin.Bottom) * pixelRatio; this.Canvas.font = this.TitleFont.GetFont(); this.TitleWidthCache = this.Canvas.measureText(this.Title).width + (this.TitleMargin.Left + this.TitleMargin.Right) * pixelRatio; }; this.DrawTitle = function () { this.Canvas.textAlign = 'center'; this.Canvas.textBaseline = 'middle'; this.Canvas.font = this.TitleFont.GetFont(); this.Canvas.fillStyle = this.TitleFont.Color; this.Canvas.fillText(this.Title, this.X + this.ToInt(this.WidthCache / 2), this.Y + this.ToInt(this.TitleHeightCache / 2)); }; this.DrawBorder = function () { this.Canvas.strokeStyle = this.PenBorder; var left = this.X, top = this.Y;