UNPKG

hqchart

Version:

HQChart - H5, 微信小程序 沪深/港股/数字货币/期货/美股 K线图(kline),走势图,缩放,拖拽,十字光标,画图工具,截图,筹码图. 分析家语法,通达信语法,(麦语法),第3方数据对接

1,562 lines (1,306 loc) 74.7 kB
///////////////////////////////////////////////////////////////////////////////////// // 股票分析思维导图 // 注: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(error) { self.ExecuteError(node,error); }, FinishCallback:function(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.SizeChang