hqchart
Version:
HQChart - H5, 微信小程序 沪深/港股/数字货币/期货/美股 K线图(kline),走势图,缩放,拖拽,十字光标,画图工具,截图,筹码图. 分析家语法,通达信语法,(麦语法),第3方数据对接
1,562 lines (1,306 loc) • 74.7 kB
JavaScript
/////////////////////////////////////////////////////////////////////////////////////
// 股票分析思维导图
// 注: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