hqchart
Version:
HQChart - H5, 微信小程序 沪深/港股/数字货币/期货/美股 K线图(kline),走势图,缩放,拖拽,十字光标,画图工具,截图,筹码图. 分析家语法,通达信语法,(麦语法),第3方数据对接
1,581 lines (1,290 loc) • 60 kB
JavaScript
/*
Copyright (c) 2018 jones
http://www.apache.org/licenses/LICENSE-2.0
开源项目 https://github.com/jones2000/HQChart
jones_2000@163.com
封装键盘精灵控件 (页面版 不支持手机)
*/
function JSKeyboardChart(divElement)
{
this.DivElement=divElement;
this.JSChartContainer; //表格控件
this.ResizeListener; //大小变动监听
//h5 canvas
this.CanvasElement=document.createElement("canvas");
this.CanvasElement.className='jskeyboard-drawing';
this.CanvasElement.id=Guid();
this.CanvasElement.setAttribute("tabindex",0);
if (this.CanvasElement.style) this.CanvasElement.style.outline='none';
if(divElement.hasChildNodes())
{
JSConsole.Chart.Log("[JSKeyboardChart::JSRepoJSKeyboardChartrtChart] divElement hasChildNodes", divElement.childNodes);
}
divElement.appendChild(this.CanvasElement);
this.OnSize=function()
{
//画布大小通过div获取
var height=this.DivElement.offsetHeight;
var width=this.DivElement.offsetWidth;
if (this.DivElement.style.height && this.DivElement.style.width)
{
if (this.DivElement.style.height.includes("px"))
height=parseInt(this.DivElement.style.height.replace("px",""));
if (this.DivElement.style.width.includes("px"))
width=parseInt(this.DivElement.style.width.replace("px",""));
}
this.CanvasElement.height=height;
this.CanvasElement.width=width;
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;
JSConsole.Chart.Log(`[JSKeyboardChart::OnSize] devicePixelRatio=${window.devicePixelRatio}, height=${this.CanvasElement.height}, width=${this.CanvasElement.width}`);
if (this.JSChartContainer && this.JSChartContainer.OnSize)
{
this.JSChartContainer.OnSize();
}
}
this.SetOption=function(option)
{
var chart=this.CreateJSKeyboardChartContainer(option);
if (!chart) return false;
if (option.OnCreatedCallback) option.OnCreatedCallback(chart);
this.JSChartContainer=chart;
this.DivElement.JSChart=this; //div中保存一份
//注册事件
if (option.EventCallback)
{
for(var i=0;i<option.EventCallback.length;++i)
{
var item=option.EventCallback[i];
chart.AddEventCallback(item);
}
}
if (option.EnableResize==true) this.CreateResizeListener();
chart.Draw();
}
this.CreateJSKeyboardChartContainer=function(option)
{
var chart=new JSKeyboardChartContainer(this.CanvasElement);
chart.Create(option);
if (option.NetworkFilter) chart.NetworkFilter=option.NetworkFilter;
if (IFrameSplitOperator.IsNonEmptyArray(option.Column)) chart.SetColumn(option.Column);
if (IFrameSplitOperator.IsNumber(option.BorderLine)) chart.Frame.BorderLine=option.BorderLine;
this.SetChartBorder(chart, option);
//注册事件
if (option.EventCallback)
{
for(var i=0;i<option.EventCallback.length;++i)
{
var item=option.EventCallback[i];
chart.AddEventCallback(item);
}
}
return chart;
}
this.SetChartBorder=function(chart, option)
{
if (!option.Border) return;
var item=option.Border;
if (IFrameSplitOperator.IsNumber(option.Border.Left)) chart.Frame.ChartBorder.Left=option.Border.Left;
if (IFrameSplitOperator.IsNumber(option.Border.Right)) chart.Frame.ChartBorder.Right=option.Border.Right;
if (IFrameSplitOperator.IsNumber(option.Border.Top)) chart.Frame.ChartBorder.Top=option.Border.Top;
if (IFrameSplitOperator.IsNumber(option.Border.Bottom)) chart.Frame.ChartBorder.Bottom=option.Border.Bottom;
var pixelTatio = GetDevicePixelRatio(); //获取设备的分辨率
chart.Frame.ChartBorder.Left*=pixelTatio;
chart.Frame.ChartBorder.Right*=pixelTatio;
chart.Frame.ChartBorder.Top*=pixelTatio;
chart.Frame.ChartBorder.Bottom*=pixelTatio;
}
this.CreateResizeListener=function()
{
this.ResizeListener = new ResizeObserver((entries)=>{ this.OnDivResize(entries); });
this.ResizeListener.observe(this.DivElement);
}
this.OnDivResize=function(entries)
{
JSConsole.Chart.Log("[JSKeyboardChart::OnDivResize] entries=", entries);
this.OnSize();
}
/////////////////////////////////////////////////////////////////////////////
//对外接口
this.SetColumn=function(aryColumn, option)
{
if (this.JSChartContainer) this.JSChartContainer.SetColumn(aryColumn,option);
}
//事件回调
this.AddEventCallback=function(obj)
{
if(this.JSChartContainer && typeof(this.JSChartContainer.AddEventCallback)=='function')
{
JSConsole.Chart.Log('[JSKeyboardChart:AddEventCallback] obj=', obj);
this.JSChartContainer.AddEventCallback(obj);
}
}
//重新加载配置
this.ReloadResource=function(option)
{
if(this.JSChartContainer && typeof(this.JSChartContainer.ReloadResource)=='function')
{
JSConsole.Chart.Log('[JSKeyboardChart:ReloadResource] ');
this.JSChartContainer.ReloadResource(option);
}
}
this.ChartDestroy=function()
{
if (this.JSChartContainer && typeof (this.JSChartContainer.ChartDestroy) == 'function')
{
this.JSChartContainer.ChartDestroy();
}
}
this.Draw=function()
{
if(this.JSChartContainer && typeof(this.JSChartContainer.Draw)=='function')
{
JSConsole.Chart.Log('[JSKeyboardChart:Draw] ');
this.JSChartContainer.Draw();
}
}
this.SetSymbolData=function(arySymbol)
{
if(this.JSChartContainer && typeof(this.JSChartContainer.Draw)=='function')
{
JSConsole.Chart.Log('[JSKeyboardChart:SetSymbolData] ', arySymbol);
this.JSChartContainer.SetSymbolData(arySymbol);
}
}
this.Search=function(strText)
{
if(this.JSChartContainer && typeof(this.JSChartContainer.Search)=='function')
{
JSConsole.Chart.Log('[JSKeyboardChart:Search] ', strText);
this.JSChartContainer.Search(strText);
}
}
this.OnKeyDown=function(event)
{
if(this.JSChartContainer && typeof(this.JSChartContainer.OnKeyDown)=='function')
{
JSConsole.Chart.Log('[JSKeyboardChart:OnKeyDown] ', event);
this.JSChartContainer.OnKeyDown(event);
}
}
this.ClearSearch=function(option)
{
if(this.JSChartContainer && typeof(this.JSChartContainer.ClearSearch)=='function')
{
JSConsole.Chart.Log('[JSKeyboardChart:ClearSearch] ', option);
this.JSChartContainer.ClearSearch(option);
}
}
}
JSKeyboardChart.Init=function(divElement)
{
var jsChartControl=new JSKeyboardChart(divElement);
jsChartControl.OnSize();
return jsChartControl;
}
//自定义风格
JSKeyboardChart.SetStyle=function(option)
{
if (option) g_JSChartResource.SetStyle(option);
}
function JSKeyboardChartContainer(uielement)
{
this.ClassName='JSKeyboardChartContainer';
this.Frame; //框架画法
this.ChartPaint=[]; //图形画法
this.Canvas=uielement.getContext("2d"); //画布
this.ShowCanvas=null;
this.NetworkFilter; //数据回调接口
this.Data={ XOffset:0, YOffset:0, Data:[] }; //股票列表
this.MapSymbol=new Map();
this.SourceData={ Data:[] } //码表数据 Data:[ { Symbol:, Spell, Name:, Color: TypeName:, TypeID } ]
this.FunctionKeyData=[]; //功能键 { Priority:, Data:[ { Symbol:, Spell, Name:, Color:, TypeName:, TypeID } ]
//事件回调
this.mapEvent=new Map(); //通知外部调用 key:JSCHART_EVENT_ID value:{Callback:回调,}
this.UIElement=uielement;
this.LastPoint=new Point(); //鼠标位置
//拖拽滚动条
this.DragYScroll=null; //{Start:{x,y}, End:{x, y}}
this.IsDestroy=false; //是否已经销毁了
this.ChartDestroy=function() //销毁
{
this.IsDestroy=true;
}
this.ClearSearch=function(option)
{
this.Data.Data=[];
this.Data.XOffset=0;
this.Data.YOffset=0;
if (option && option.Redraw==true) this.Draw();
}
this.SearchFunctionKeyData=function(strSearch)
{
if (strSearch.length<=0) return null;
if (!IFrameSplitOperator.IsNonEmptyArray(this.FunctionKeyData)) return null;
var aryData=[];
for(var i=0; i<this.FunctionKeyData.length; ++i)
{
var groupData=this.FunctionKeyData[i];
if (!groupData) continue;
if (!IFrameSplitOperator.IsNonEmptyArray(groupData.Data)) continue;
var aryExactQuery=[]; //精确查询
var aryFuzzyQuery=[]; //模糊查询
var aryEqualQuery=[]; //相等
for(var j=0;j<groupData.Data.length;++j)
{
var item=groupData.Data[j];
if (this.SearchSymbol(item, strSearch, aryExactQuery, aryFuzzyQuery,aryEqualQuery)) continue;
}
if (IFrameSplitOperator.IsNonEmptyArray(aryEqualQuery)) aryData.push(...aryEqualQuery);
if (IFrameSplitOperator.IsNonEmptyArray(aryExactQuery)) aryData.push(...aryExactQuery);
if (IFrameSplitOperator.IsNonEmptyArray(aryFuzzyQuery)) aryData.push(...aryFuzzyQuery);
}
if (aryData.length>0) return aryData;
return null;
}
this.Search=function(strText)
{
var aryExactQuery=[]; //精确查询
var aryFuzzyQuery=[]; //模糊查询
var aryEqualQuery=[]; //相等
var aryFuncKeyQuery=null;
this.MapSymbol.clear();
this.Data.Data=[];
this.Data.XOffset=0;
this.Data.YOffset=0;
var strSearch=strText.trim();
if (strSearch.length>0)
{
aryFuncKeyQuery=this.SearchFunctionKeyData(strSearch);
for(var i=0;i<this.SourceData.Data.length;++i)
{
var item=this.SourceData.Data[i];
if (this.SearchSymbol(item, strSearch, aryExactQuery, aryFuzzyQuery, aryEqualQuery)) continue;
else if (this.SearchSpell(item, strSearch, aryExactQuery, aryFuzzyQuery)) continue;
}
}
if (IFrameSplitOperator.IsNonEmptyArray(aryFuncKeyQuery)) this.Data.Data.push(...aryFuncKeyQuery);
if (IFrameSplitOperator.IsNonEmptyArray(aryEqualQuery)) this.Data.Data.push(...aryEqualQuery);
if (IFrameSplitOperator.IsNonEmptyArray(aryExactQuery)) this.Data.Data.push(...aryExactQuery);
if (IFrameSplitOperator.IsNonEmptyArray(aryFuzzyQuery)) this.Data.Data.push(...aryFuzzyQuery);
this.ChartPaint[0].SelectedRow=0;
this.ChartPaint[0].SizeChange=true;
JSConsole.Chart.Log(`[JSKeyboardChart:Search] search=${strSearch}, source=${this.SourceData.Data.length} match=${this.Data.Data.length}`);
this.Draw();
}
this.SearchSymbol=function(item, strText, aryExactQuery, aryFuzzyQuery, aryEqualQuery)
{
var find=item.Symbol.indexOf(strText);
if (find<0) return false;
if (item.Symbol==strText) aryEqualQuery.push(item.Symbol);
else if (find==0) aryExactQuery.push(item.Symbol);
else aryFuzzyQuery.push(item.Symbol);
this.MapSymbol.set(item.Symbol, item);
return true;
}
this.SearchSpell=function(item, strText, aryExactQuery, aryFuzzyQuery)
{
if (!IFrameSplitOperator.IsString(item.Spell)) return false;
var find=item.Spell.indexOf(strText);
if (find!=0) return false;
aryExactQuery.push(item.Symbol);
this.MapSymbol.set(item.Symbol, item);
return true;
}
this.SetSymbolData=function(arySymbol)
{
this.SourceData.Data=[];
for(var i=0;i<arySymbol.length;++i)
{
var item=arySymbol[i];
if (IFrameSplitOperator.IsNumber(item.Priority))
{
if (!this.FunctionKeyData[item.Priority]) this.FunctionKeyData[item.Priority]={ Priority:item.Priority, Data:[] };
this.FunctionKeyData[item.Priority].Data.push(item);
}
else
{
this.SourceData.Data.push(item);
}
}
/*
//测试
this.MapSymbol.clear();
for(var i=0;i<this.SourceData.Data.length && i<3050 ;++i)
{
var item=this.SourceData.Data[i];
this.Data.Data.push(item.Symbol);
this.MapSymbol.set(item.Symbol, item);
}
this.ChartPaint[0].SelectedRow=0;
*/
}
//创建
this.Create=function(option)
{
this.UIElement.JSChartContainer=this;
//创建框架
this.Frame=new JSKeyboardFrame();
this.Frame.ChartBorder=new ChartBorder();
this.Frame.ChartBorder.UIElement=this.UIElement;
this.Frame.ChartBorder.Top=30;
this.Frame.ChartBorder.Left=5;
this.Frame.ChartBorder.Bottom=20;
this.Frame.Canvas=this.Canvas;
//创建表格
var chart=new ChartSymbolList();
chart.Frame=this.Frame;
chart.ChartBorder=this.Frame.ChartBorder;
chart.Canvas=this.Canvas;
chart.UIElement=this.UIElement;
chart.GetEventCallback=(id)=> { return this.GetEventCallback(id); }
chart.GetStockDataCallback=(symbol)=>{ return this.GetStockData(symbol);}
chart.Data=this.Data;
this.ChartPaint[0]=chart;
chart.VScrollbar=new ChartKeyboardVScrollbar();
chart.VScrollbar.Frame=this.Frame;
chart.VScrollbar.Canvas=this.Canvas;
chart.VScrollbar.ChartBorder=this.Frame.ChartBorder;
chart.VScrollbar.Report=chart;
if (option)
{
}
var bRegisterKeydown=true;
var bRegisterWheel=true;
if (option)
{
if (option.KeyDown===false)
{
bRegisterKeydown=false;
JSConsole.Chart.Log('[JSKeyboardChartContainer::Create] not register keydown event.');
}
if (option.Wheel===false)
{
bRegisterWheel=false;
JSConsole.Chart.Log('[JSKeyboardChartContainer::Create] not register wheel event.');
}
}
if (bRegisterKeydown) this.UIElement.addEventListener("keydown", (e)=>{ this.OnKeyDown(e); }, true); //键盘消息
if (bRegisterWheel) this.UIElement.addEventListener("wheel", (e)=>{ this.OnWheel(e); }, true); //上下滚动消息
this.UIElement.ondblclick=(e)=>{ this.UIOnDblClick(e); }
this.UIElement.onmousedown=(e)=> { this.UIOnMouseDown(e); }
this.UIElement.oncontextmenu=(e)=> { this.UIOnContextMenu(e); }
this.UIElement.onmousemove=(e)=>{ this.UIOnMouseMove(e);}
this.UIElement.onmouseout=(e)=>{ this.UIOnMounseOut(e); }
this.UIElement.onmouseleave=(e)=>{ this.UIOnMouseleave(e); }
}
this.Draw=function()
{
if (this.UIElement.width<=0 || this.UIElement.height<=0) return;
this.Canvas.clearRect(0,0,this.UIElement.width,this.UIElement.height);
var pixelTatio = GetDevicePixelRatio(); //获取设备的分辨率
this.Canvas.lineWidth=pixelTatio; //手机端需要根据分辨率比调整线段宽度
this.Frame.Draw();
this.Frame.DrawLogo();
//框架内图形
for(var i=0;i<this.ChartPaint.length;++i)
{
var item=this.ChartPaint[i];
if (item.IsDrawFirst)
item.Draw();
}
for(var i=0; i<this.ChartPaint.length; ++i)
{
var item=this.ChartPaint[i];
if (!item.IsDrawFirst)
item.Draw();
}
}
this.GetStockData=function(symbol)
{
if (!this.MapSymbol.has(symbol)) return null;
return this.MapSymbol.get(symbol);
}
this.ResetStatus=function()
{
this.Data.XOffset=0;
this.Data.YOffset=0;
}
this.ResetSelectStatus=function()
{
var chart=this.GetReportChart();
if (chart)
{
chart.SelectedRow=-1;
chart.SelectedFixedRow=-1;
}
}
//设置事件回调
//{event:事件id, callback:回调函数}
this.AddEventCallback=function(object)
{
if (!object || !object.event || !object.callback) return;
var data={Callback:object.callback, Source:object};
this.mapEvent.set(object.event,data);
}
this.RemoveEventCallback=function(eventid)
{
if (!this.mapEvent.has(eventid)) return;
this.mapEvent.delete(eventid);
}
this.GetEventCallback=function(id) //获取事件回调
{
if (!this.mapEvent.has(id)) return null;
var item=this.mapEvent.get(id);
return item;
}
this.OnSize=function()
{
if (!this.Frame) return;
this.SetSizeChange(true);
this.Draw();
}
this.SetSizeChange=function(bChanged)
{
for(var i=0;i<this.ChartPaint.length;++i)
{
var chart=this.ChartPaint[i];
if (chart) chart.SizeChange=bChanged;
}
}
this.OnWheel=function(e) //滚轴
{
JSConsole.Chart.Log('[JSKeyboardChartContainer::OnWheel]',e);
if (this.ChartSplashPaint && this.ChartSplashPaint.IsEnableSplash == true) return;
if (!this.Data || !IFrameSplitOperator.IsNonEmptyArray(this.Data.Data)) return;
var x = e.clientX-this.UIElement.getBoundingClientRect().left;
var y = e.clientY-this.UIElement.getBoundingClientRect().top;
var isInClient=false;
this.Canvas.beginPath();
this.Canvas.rect(this.Frame.ChartBorder.GetLeft(),this.Frame.ChartBorder.GetTop(),this.Frame.ChartBorder.GetWidth(),this.Frame.ChartBorder.GetHeight());
isInClient=this.Canvas.isPointInPath(x,y);
if (!isInClient) return;
var chart=this.ChartPaint[0];
if (!chart) return;
var wheelValue=e.wheelDelta;
if (!IFrameSplitOperator.IsObjectExist(e.wheelDelta))
wheelValue=e.deltaY* -0.01;
if (wheelValue<0) //下一页
{
if (this.GotoNextPage(this.PageUpDownCycle))
{
this.Draw();
}
}
else if (wheelValue>0) //上一页
{
if (this.GotoPreviousPage(this.PageUpDownCycle))
{
this.Draw();
}
}
if(e.preventDefault) e.preventDefault();
else e.returnValue = false;
}
this.OnKeyDown=function(e)
{
if (this.ChartSplashPaint && this.ChartSplashPaint.IsEnableSplash == true) return;
var reportChart=this.GetReportChart();
if (!reportChart) return;
var keyID = e.keyCode ? e.keyCode :e.which;
switch(keyID)
{
case 33: //page up
if (this.GotoPreviousPage(this.PageUpDownCycle))
{
this.Draw();
}
break;
case 34: //page down
if (this.GotoNextPage(this.PageUpDownCycle))
{
this.Draw();
}
break;
case 38: //up
var result=this.MoveSelectedRow(-1);
if (result)
{
if (result.Redraw) this.Draw();
}
break;
case 40: //down
var result=this.MoveSelectedRow(1)
if (result)
{
if (result.Redraw) this.Draw();
}
break;
case 37: //left
if (this.MoveXOffset(-1)) this.Draw();
break
case 39: //right
if (this.MoveXOffset(1)) this.Draw();
break;
case 13: //Enter
this.OnSelectedSymbol();
break;
}
//不让滚动条滚动
if(e.preventDefault) e.preventDefault();
else e.returnValue = false;
}
this.OnSelectedSymbol=function()
{
if (!this.Data || !IFrameSplitOperator.IsNonEmptyArray(this.Data.Data)) return false;
var chart=this.ChartPaint[0];
if (!chart) return false;
var data=chart.GetSelectedSymbol();
var selItem=this.MapSymbol.get(data.Symbol);
if (!selItem) return false;
var event=this.GetEventCallback(JSCHART_EVENT_ID.ON_KEYBOARD_SELECTED)
if (event && event.Callback)
{
event.Callback(event, { Data:data, RowData:selItem }, this);
}
}
this.UIOnDblClick=function(e)
{
var pixelTatio = GetDevicePixelRatio();
var x = (e.clientX-this.UIElement.getBoundingClientRect().left)*pixelTatio;
var y = (e.clientY-this.UIElement.getBoundingClientRect().top)*pixelTatio;
var chart=this.ChartPaint[0];
if (chart)
{
if (chart.OnDblClick(x,y,e)) this.OnSelectedSymbol();
}
}
this.UIOnMouseDown=function(e)
{
this.DragYScroll=null;
this.DragMove={ Click:{ X:e.clientX, Y:e.clientY }, Move:{X:e.clientX, Y:e.clientY}, PreMove:{X:e.clientX, Y:e.clientY } };
var pixelTatio = GetDevicePixelRatio();
var x = (e.clientX-this.UIElement.getBoundingClientRect().left)*pixelTatio;
var y = (e.clientY-this.UIElement.getBoundingClientRect().top)*pixelTatio;
var chart=this.ChartPaint[0];
if (chart)
{
var clickData=chart.OnMouseDown(x,y,e);
if (!clickData) return;
//if (e.button!=0) return;
if ((clickData.Type==2) && (e.button==0 || e.button==2)) //点击行
{
if (clickData.Redraw==true)
this.Draw();
}
else if (clickData.Type==5 && e.button==0) //右侧滚动条
{
var scroll=clickData.VScrollbar;
if (scroll.Type==1) //顶部按钮
{
if (this.MoveYOffset(-1))
this.Draw();
}
else if (scroll.Type==2) //底部按钮
{
if (this.MoveYOffset(1))
this.Draw();
}
else if (scroll.Type==3) //滚动条
{
this.DragYScroll={ Click:{X:x, Y:y}, LastMove:{X:x, Y:y} };
}
else if (scroll.Type==4) //滚动条内部
{
if (this.SetYOffset(scroll.Pos))
this.Draw();
}
}
}
document.onmousemove=(e)=>{ this.DocOnMouseMove(e); }
document.onmouseup=(e)=> { this.DocOnMouseUp(e); }
}
//去掉右键菜单
this.UIOnContextMenu=function(e)
{
e.preventDefault();
}
this.UIOnMouseMove=function(e)
{
var pixelTatio = GetDevicePixelRatio();
var x = (e.clientX-this.UIElement.getBoundingClientRect().left)*pixelTatio;
var y = (e.clientY-this.UIElement.getBoundingClientRect().top)*pixelTatio;
}
this.UIOnMounseOut=function(e)
{
}
this.UIOnMouseleave=function(e)
{
}
this.DocOnMouseMove=function(e)
{
this.DragMove.PreMove.X=this.DragMove.Move.X;
this.DragMove.PreMove.Y=this.DragMove.Move.Y;
this.DragMove.Move.X=e.clientX;
this.DragMove.Move.Y=e.clientX;
//if (this.DragMove.Move.X!=this.DragMove.PreMove.X || this.DragMove.Move.Y!=this.DragMove.PreMove.Y)
// this.StopAutoDragScrollTimer();
if (this.ChartSplashPaint && this.ChartSplashPaint.IsEnableSplash == true) return;
var pixelTatio = GetDevicePixelRatio();
var x = (e.clientX-this.UIElement.getBoundingClientRect().left)*pixelTatio;
var y = (e.clientY-this.UIElement.getBoundingClientRect().top)*pixelTatio;
if (this.DragYScroll)
{
var chart=this.ChartPaint[0];
if (!chart || !chart.VScrollbar) return;
this.DragYScroll.LastMove.X=x;
this.DragYScroll.LastMove.Y=y;
var pos=chart.VScrollbar.GetScrollPostionByPoint(x,y);
if (this.SetYOffset(pos))
this.Draw();
}
}
this.DocOnMouseUp=function(e)
{
//清空事件
document.onmousemove=null;
document.onmouseup=null;
this.DragYScroll=null;
var event=this.GetEventCallback(JSCHART_EVENT_ID.ON_KEYBOARD_MOUSEUP)
if (event && event.Callback)
{
event.Callback(event, { }, this);
}
}
this.GetMoveAngle=function(pt,pt2) //计算角度
{
var xMove=Math.abs(pt.X-pt2.X);
var yMove=Math.abs(pt.Y-pt2.Y);
var angle=Math.atan(xMove/yMove)*180/Math.PI;
return angle;
}
this.PreventTouchEvent=function(e)
{
if (e.cancelable) e.preventDefault();
e.stopPropagation();
}
this.OnDragYOffset=function(drag, touches, moveUpDown, e)
{
if (moveUpDown<5) return false
var isUp=true;
if (drag.LastMove.Y<touches[0].clientY) isUp=false; //Down
var oneStep=this.YStepPixel;
if (oneStep<=0) oneStep=5;
var step=parseInt(moveUpDown/oneStep);
if (step<=0) return false
if (isUp==false) step*=-1;
if (this.MoveYOffset(step, this.DragPageCycle))
{
drag.IsYMove=true;
this.Draw();
this.DelayUpdateStockData();
}
return true;
}
this.OnDragXOffset=function(drag, touches, moveLeftRight, e)
{
if (moveLeftRight<5) return false;
var isLeft=true;
if (drag.LastMove.X<touches[0].clientX) isLeft=false;//右移数据
var oneStep=this.XStepPixel;
if (oneStep<=0) oneStep=5;
var step=parseInt(moveLeftRight/oneStep); //除以4个像素
if (step<=0) return false;
if (!isLeft) step*=-1;
if (this.MoveXOffset(step))
{
drag.IsXMove=true;
this.Draw();
}
return true;
}
this.GetReportChart=function()
{
var chart=this.ChartPaint[0];
return chart;
}
this.GotoNextPage=function(bCycle) //bCycle 是否循环
{
if (!this.Data || !IFrameSplitOperator.IsNonEmptyArray(this.Data.Data)) return false;
var chart=this.ChartPaint[0];
if (!chart) return false;
var pageSize=chart.GetPageSize();
if (pageSize>this.Data.Data.length) return false;
if (this.Data.YOffset+pageSize>=this.Data.Data.length)
{
if (bCycle===true)
{
this.Data.YOffset=0; //循环到第1页
return true;
}
else
{
return false;
}
}
this.Data.YOffset+=pageSize;
var showDataCount=this.Data.Data.length-this.Data.YOffset;
chart.SelectedRow=this.Data.YOffset;
return true;
}
this.GotoPreviousPage=function(bCycle) //bCycle 是否循环
{
if (!this.Data || !IFrameSplitOperator.IsNonEmptyArray(this.Data.Data)) return false;
var chart=this.ChartPaint[0];
if (!chart) return false;
var pageSize=chart.GetPageSize();
if (pageSize>this.Data.Data.length) return false;
if (this.Data.YOffset<=0)
{
if (bCycle===true)
{
this.Data.YOffset=this.Data.Data.length-pageSize; //循环到最后一页
return true;
}
else
{
return false;
}
}
var offset=this.Data.YOffset;
offset-=pageSize;
if (offset<0) offset=0;
this.Data.YOffset=offset;
chart.SelectedRow=this.Data.YOffset;
return true;
}
this.MoveYOffset=function(setp, bCycle) //bCycle 是否循环
{
if (!this.Data || !IFrameSplitOperator.IsNonEmptyArray(this.Data.Data)) return false;
var chart=this.ChartPaint[0];
if (!chart) return false;
var pageStatus=chart.GetCurrentPageStatus();
if (pageStatus.IsSinglePage) return false;
if (setp>0) //向上
{
var count=this.Data.Data.length;
var pageSize=pageStatus.PageSize;
var offset=this.Data.YOffset;
if (bCycle)
{
for(var i=0;i<setp;++i)
{
++offset;
if (offset+pageSize>count) offset=0;
}
}
else
{
if (offset+pageSize>=count) return false;
for(var i=0;i<setp;++i)
{
if (offset+pageSize+1>count) break;
++offset;
}
}
this.Data.YOffset=offset;
return true;
}
else if (setp<0) //向下
{
setp=Math.abs(setp);
var offset=this.Data.YOffset;
if (bCycle)
{
var pageSize=pageStatus.PageSize;
for(var i=0;i<setp;++i)
{
--offset;
if (offset<0) offset=this.Data.Data.length-pageSize;
}
}
else
{
if (this.Data.YOffset<=0) return false;
for(var i=0;i<setp;++i)
{
if (offset-1<0) break;
--offset;
}
}
this.Data.YOffset=offset;
return true;
}
return false;
}
this.GotoLastPage=function()
{
var chart=this.ChartPaint[0];
if (!chart) return;
//显示最后一屏
var pageSize=chart.GetPageSize(true);
var offset=this.Data.Data.length-pageSize;
if (offset<0) offset=0;
this.Data.DataOffset=offset;
}
this.SetColumn=function(aryColunm, option)
{
var chart=this.ChartPaint[0];
if (!chart) return;
chart.SetColumn(aryColunm);
chart.SizeChange=true;
if (option && option.Redraw) this.Draw();
}
this.ReloadResource=function(option)
{
this.Frame.ReloadResource(option);
for(var i=0;i<this.ChartPaint.length;++i)
{
var item=this.ChartPaint[i];
if (item.ReloadResource) item.ReloadResource(option);
}
if (option && option.Redraw)
{
this.SetSizeChange(true);
this.Draw();
}
}
this.MoveSelectedRow=function(step)
{
var chart=this.ChartPaint[0];
if (!chart) return null;
if (!IFrameSplitOperator.IsNonEmptyArray(this.Data.Data)) return null;
var result={ Redraw:false }; //Redraw=重绘
//可翻页模式
var pageStatus=chart.GetCurrentPageStatus();
var pageSize=pageStatus.PageSize;
var selected=pageStatus.SelectedRow;
if (step>0)
{
if (selected<0 || selected<pageStatus.Start || selected>pageStatus.End)
{
chart.SelectedRow=pageStatus.Start;
result.Redraw=true;
return result;
}
var offset=this.Data.YOffset;
for(var i=0;i<step;++i)
{
++selected;
if (selected>pageStatus.End) ++offset;
if (selected>=this.Data.Data.length)
break;
result.Redraw=true;
chart.SelectedRow=selected;
this.Data.YOffset=offset;
}
return result;
}
else if (step<0)
{
if (selected<0 || selected<pageStatus.Start || selected>pageStatus.End)
{
chart.SelectedRow=pageStatus.End;
result.Redraw=true;
return result;
}
step=Math.abs(step);
var offset=this.Data.YOffset;
for(var i=0;i<step;++i)
{
--selected;
if (selected<pageStatus.Start) --offset;
if (selected<0)
break;
result.Redraw=true;
chart.SelectedRow=selected;
this.Data.YOffset=offset;
}
return result;
}
}
this.SetYOffset=function(pos)
{
if (!IFrameSplitOperator.IsNumber(pos)) return false;
var chart=this.ChartPaint[0];
if (!chart) return false;
var maxOffset=chart.GetYScrollRange();
if (pos<0) pos=0;
if (pos>maxOffset) pos=maxOffset;
this.Data.YOffset=pos;
return true;
}
}
function JSKeyboardFrame()
{
this.ChartBorder;
this.Canvas; //画布
this.BorderLine=null; //1=上 2=下 4=左 8=右
this.BorderColor=g_JSChartResource.Keyboard.BorderColor; //边框线
this.LogoTextColor=g_JSChartResource.FrameLogo.TextColor;
this.LogoTextFont=g_JSChartResource.FrameLogo.Font;
this.ReloadResource=function(option)
{
this.BorderColor=g_JSChartResource.Keyboard.BorderColor; //边框线
this.LogoTextColor=g_JSChartResource.FrameLogo.TextColor;
this.LogoTextFont=g_JSChartResource.FrameLogo.Font;
}
this.Draw=function()
{
var left=ToFixedPoint(this.ChartBorder.GetLeft());
var top=ToFixedPoint(this.ChartBorder.GetTop());
var right=ToFixedPoint(this.ChartBorder.GetRight());
var bottom=ToFixedPoint(this.ChartBorder.GetBottom());
var width=right-left;
var height=bottom-top;
if (!IFrameSplitOperator.IsNumber(this.BorderLine))
{
this.Canvas.strokeStyle=this.BorderColor;
this.Canvas.strokeRect(left,top,width,height);
}
else
{
this.Canvas.strokeStyle=this.BorderColor;
this.Canvas.beginPath();
if ((this.BorderLine&1)>0) //上
{
this.Canvas.moveTo(left,top);
this.Canvas.lineTo(right,top);
}
if ((this.BorderLine&2)>0) //下
{
this.Canvas.moveTo(left,bottom);
this.Canvas.lineTo(right,bottom);
}
if ((this.BorderLine&4)>0) //左
{
this.Canvas.moveTo(left,top);
this.Canvas.lineTo(left,bottom);
}
if ((this.BorderLine&8)>0) //右
{
this.Canvas.moveTo(right,top);
this.Canvas.lineTo(right,bottom);
}
this.Canvas.stroke();
}
}
this.DrawLogo=function()
{
var text=g_JSChartResource.FrameLogo.Text;
if (!IFrameSplitOperator.IsString(text)) return;
this.Canvas.fillStyle=this.LogoTextColor;
this.Canvas.font=this.LogoTextFont;
this.Canvas.textAlign = 'right';
this.Canvas.textBaseline = 'bottom';
var x=this.ChartBorder.GetRight()-30;
var y=this.ChartBorder.GetBottom()-5;
this.Canvas.fillText(text,x,y);
}
}
var KEYBOARD_COLUMN_ID=
{
SHORT_SYMBOL_ID:0, //不带后缀代码
SYMBOL_ID:1,
NAME_ID:2, //简称
TYPE_ID:3, //类型
TYPE_NAME_ID:4, //类型中文名字
}
function ChartSymbolList()
{
this.Canvas; //画布
this.ChartBorder; //边框信息
this.ChartFrame; //框架画法
this.Name; //名称
this.ClassName='ChartSymbolList'; //类名
this.UIElement;
this.IsDrawFirst=false;
this.GetEventCallback; //获取事件
this.Data; //数据 { XOffset:0, YOffset:0, Data:['600000.sh', '000001.sz'] }
this.SizeChange=true;
this.SelectedRow=-1; //选中行ID
this.IsDrawBorder=false; //是否绘制单元格边框
this.ShowSymbol=[]; //显示的股票列表 { Index:序号(排序用), Symbol:股票代码 }
this.VScrollbar; //竖向滚动条
this.BorderColor=g_JSChartResource.Keyboard.BorderColor; //边框线
this.SelectedColor=g_JSChartResource.Keyboard.SelectedColor; //选中行
this.TextColor=g_JSChartResource.Keyboard.TextColor; //文字颜色
//表格内容配置
this.ItemFontConfig={ Size:g_JSChartResource.Keyboard.Item.Font.Size, Name:g_JSChartResource.Keyboard.Item.Font.Name };
this.ItemMergin=
{
Left:g_JSChartResource.Keyboard.Item.Mergin.Left,
Right:g_JSChartResource.Keyboard.Item.Mergin.Right,
Top:g_JSChartResource.Keyboard.Item.Mergin.Top,
Bottom:g_JSChartResource.Keyboard.Item.Mergin.Bottom
};
//缓存
this.ItemFont=15*GetDevicePixelRatio() +"px 微软雅黑";
this.ItemSymbolFont=12*GetDevicePixelRatio() +"px 微软雅黑";
this.ItemNameFont=15*GetDevicePixelRatio() +"px 微软雅黑";
this.ItemNameHeight=0;
this.RowCount=0; //一屏显示行数
this.RowHeight=0; //行高度
this.Column= //{ Type:列id, Title:标题, TextAlign:文字对齐方式, MaxText:文字最大宽度 , TextColor:文字颜色, Sort:0=不支持排序 1=本地排序 0=远程排序 }
[
{ Type:KEYBOARD_COLUMN_ID.SHORT_SYMBOL_ID, Title:"代码", TextAlign:"left", Width:null, MaxText:"888888" },
{ Type:KEYBOARD_COLUMN_ID.NAME_ID, Title:"名称", TextAlign:"left", Width:null, MaxText:"擎擎擎擎擎擎" },
//{ Type:KEYBOARD_COLUMN_ID.TYPE_ID, Title:"类型", TextAlign:"right", Width:null, MaxText:"擎擎擎擎" },
{ Type:KEYBOARD_COLUMN_ID.TYPE_NAME_ID, Title:"类型", TextAlign:"right", Width:null, MaxText:"擎擎擎擎" },
];
this.RectClient={ };
this.ReloadResource=function(resource)
{
this.BorderColor=g_JSChartResource.Keyboard.BorderColor; //边框线
this.SelectedColor=g_JSChartResource.Keyboard.SelectedColor; //选中行
this.TextColor=g_JSChartResource.Keyboard.TextColor; //文字颜色
if (this.VScrollbar) this.VScrollbar.ReloadResource(resource);
}
this.SetColumn=function(aryColumn)
{
if (!IFrameSplitOperator.IsNonEmptyArray(aryColumn)) return;
this.Column=[];
for(var i=0;i<aryColumn.length;++i)
{
var item=aryColumn[i];
var colItem=this.GetDefaultColunm(item.Type);
if (!colItem) continue;
if (item.Title) colItem.Title=item.Title;
if (item.TextAlign) colItem.TextAlign=item.TextAlign;
if (item.MaxText) colItem.MaxText=item.MaxText;
this.Column.push(colItem);
}
}
this.GetDefaultColunm=function(id)
{
var DEFAULT_COLUMN=
[
{ Type:KEYBOARD_COLUMN_ID.SHORT_SYMBOL_ID, Title:"代码", TextAlign:"left", Width:null, MaxText:"888888" },
{ Type:KEYBOARD_COLUMN_ID.NAME_ID, Title:"名称", TextAlign:"left", Width:null, MaxText:"擎擎擎擎擎擎" },
{ Type:KEYBOARD_COLUMN_ID.TYPE_NAME_ID, Title:"类型", TextAlign:"right", Width:null, MaxText:"擎擎擎擎" },
];
for(var i=0;i<DEFAULT_COLUMN.length;++i)
{
var item=DEFAULT_COLUMN[i];
if (item.Type==id) return item;
}
return null;
}
this.Draw=function()
{
this.ShowSymbol=[ ];
if (this.SizeChange) this.CalculateSize();
else this.UpdateCacheData();
var bShowVScrollbar=this.IsShowVScrollbar();
this.Canvas.save();
this.Canvas.beginPath();
this.Canvas.rect(this.RectClient.Left,this.RectClient.Top+1,(this.RectClient.Right-this.RectClient.Left),(this.RectClient.Bottom-(this.RectClient.Top+1)));
//this.Canvas.stroke(); //调试用
this.Canvas.clip();
this.DrawBody();
this.Canvas.restore();
this.DrawBorder();
if (this.VScrollbar && bShowVScrollbar)
{
var bottom=this.ChartBorder.GetBottom();
this.VScrollbar.DrawScrollbar(this.RectClient.Left,this.RectClient.Top+2, this.RectClient.Right+this.VScrollbar.ButtonSize+2, bottom-2);
}
this.SizeChange=false;
}
//更新缓存变量
this.UpdateCacheData=function()
{
this.RectClient.Left=this.ChartBorder.GetLeft();
this.RectClient.Right=this.ChartBorder.GetRight();
this.RectClient.Top=this.ChartBorder.GetTop();
this.RectClient.Bottom=this.ChartBorder.GetBottom();
var bShowVScrollbar=this.IsShowVScrollbar();
if (bShowVScrollbar && this.VScrollbar && this.VScrollbar.Enable)
{
this.RectClient.Right-=(this.VScrollbar.ButtonSize+2);
}
}
this.GetPageSize=function(recalculate) //recalculate 是否重新计算
{
if (recalculate) this.CalculateSize();
var size=this.RowCount;
return size;
}
this.GetCurrentPageStatus=function() //{ Start:起始索引, End:结束索引(数据), PageSize:页面可以显示几条记录, IsEnd:是否是最后一页, IsSinglePage:是否只有一页数据}
{
var result={ Start:this.Data.YOffset, PageSize:this.RowCount, IsEnd:false, SelectedRow:this.SelectedRow, IsSinglePage:false, DataCount:0 };
if (IFrameSplitOperator.IsNonEmptyArray(this.Data.Data))
{
result.End=this.Data.YOffset+this.RowCount-1;
result.IsSinglePage=this.Data.Data.length<=this.RowCount;
result.DataCount=this.Data.Data.length;
if (result.End>=this.Data.Data.length-1) result.IsEnd=true;
if (result.End>=this.Data.Data.length) result.End=this.Data.Data.length-1;
}
else
{
result.Star=0;
result.End=0;
result.IsEnd=true;
result.IsSinglePage=true;
}
return result;
}
this.CalculateSize=function() //计算大小
{
if (this.VScrollbar && this.VScrollbar.Enable) this.VScrollbar.CalculateSize();
this.UpdateCacheData();
var pixelRatio=GetDevicePixelRatio();
this.ItemFont=`${this.ItemFontConfig.Size*pixelRatio}px ${ this.ItemFontConfig.Name}`;
this.RowHeight=this.GetFontHeight(this.ItemFont,"擎")+ this.ItemMergin.Top+ this.ItemMergin.Bottom;
var left=this.RectClient.Left;
var right=this.RectClient.Right;
this.Canvas.font=this.ItemFont;
var itemWidth=0;
for(var i=0;i<this.Column.length;++i)
{
var item=this.Column[i];
itemWidth=this.Canvas.measureText(item.MaxText).width;
item.Width=itemWidth+4+this.ItemMergin.Left+this.ItemMergin.Right;
if (i==this.Column.length-1) //最后一列
{
if (left+item.Width<right) item.Width=right-left;
}
left+=item.Width;
}
this.RowCount=parseInt((this.RectClient.Bottom-this.RectClient.Top)/this.RowHeight);
}
this.DrawText=function(text, textAlign, x, y, textWidth)
{
if (textAlign=='center')
{
x=x+textWidth/2;
this.Canvas.textAlign="center";
}
else if (textAlign=='right')
{
x=x+textWidth;
this.Canvas.textAlign="right";
}
else
{
this.Canvas.textAlign="left";
}
this.Canvas.textBaseline="middle";
this.Canvas.fillText(text,x,y);
}
this.DrawBorder=function()
{
if (!this.IsDrawBorder) return;
var left=this.RectClient.Left;
var right=this.RectClient.Right;
var top=this.RectClient.Top;
var bottom=this.RectClient.Bottom;
this.Canvas.strokeStyle=this.BorderColor;
this.Canvas.beginPath();
this.Canvas.moveTo(left,ToFixedPoint(top));
this.Canvas.lineTo(right,ToFixedPoint(top));
var rowTop=top+this.RowHeight;
var rotBottom=rowTop;
//横线
for(var i=0;i<this.RowCount;++i)
{
var drawTop=ToFixedPoint(rowTop);
this.Canvas.moveTo(left,drawTop);
this.Canvas.lineTo(right,drawTop);
rotBottom=rowTop;
rowTop+=this.RowHeight;
}
//竖线
var columnLeft=left;
for(var i=this.Data.XOffset;i<this.Column.length;++i)
{
var item=this.Column[i];
var drawLeft=ToFixedPoint(columnLeft+item.Width);
this.Canvas.moveTo(drawLeft,top);
this.Canvas.lineTo(drawLeft,rotBottom);
columnLeft+=item.Width;
}
this.Canvas.stroke();
}
this.GetSelectedSymbol=function()
{
if (this.SelectedRow<0 || this.SelectedRow>=this.Data.Data.length) return null;
return { Symbol: this.Data.Data[this.SelectedRow], RowID: this.SelectedRow };
}
this.DrawBody=function()
{
if (!this.Data) return;
if (!IFrameSplitOperator.IsNonEmptyArray(this.Data.Data)) return;
this.Canvas.font=this.ItemFont;
var top=this.RectClient.Top;
var left=this.RectClient.Left;
var rowWidth=this.RectClient.Right-this.RectClient.Left;
var textTop=top;
this.Canvas.font=this.ItemFont;
for(var i=this.Data.YOffset, j=0; i<this.Data.Data.length && j<this.RowCount ;++i, ++j)
{
var symbol=this.Data.Data[i];
var bFillRow=false;
if (i==this.SelectedRow) bFillRow=true; //选中行
if (bFillRow)
{
this.Canvas.fillStyle=this.SelectedColor;
this.Canvas.fillRect(left+1,textTop,rowWidth,this.RowHeight);
}
this.DrawRow(symbol, textTop, i);
this.ShowSymbol.push( { Index:i, Symbol:symbol } );
textTop+=this.RowHeight;
}
}
this.DrawRow=function(symbol, top, dataIndex, rowType) //rowType 0=表格行 1=顶部固定行 2=拖拽行
{
var left=this.RectClient.Left;
var chartRight=this.RectClient.Right;
var data= { Symbol:symbol , Stock:null, Block:null };
if (this.GetStockDataCallback) data.Stock=this.GetStockDataCallback(symbol);
for(var i=this.Data.XOffset;i<this.Column.length;++i)
{
var item=this.Column[i];
this.DrawItem(dataIndex, data, item, left, top, rowType);
left+=item.Width;
if (left>=chartRight) break;
}
}
this.DrawItem=function(index, data, column, left, top, rowType)
{
var itemWidth=column.Width;
var x=left+this.ItemMergin.Left;
var textWidth=column.Width-this.ItemMergin.Left-this.ItemMergin.Right;
var stock=data.Stock;
var drawInfo={ Text:null, TextColor:this.TextColor , TextAlign:column.TextAlign };
if (column.Type==KEYBOARD_COLUMN_ID.SHORT_SYMBOL_ID)
{
if (stock && stock.ShortSymbol) drawInfo.Text=stock.ShortSymbol;
else drawInfo.Text=data.Symbol;
if (stock.Color) drawInfo.TextColor=stock.Color;
}
else if (column.Type==KEYBOARD_COLUMN_ID.NAME_ID)
{
if (stock && stock.Name)
{
drawInfo.Text=this.TextEllipsis(stock.Name, textWidth, column.MaxText);
if (stock.Color) drawInfo.TextColor=stock.Color;
}
}
else if (column.Type==KEYBOARD_COLUMN_ID.TYPE_NAME_ID)
{
if (stock && stock.TypeName)
{
drawInfo.Text=this.TextEllipsis(stock.TypeName, textWidth, column.MaxText);
if (stock.Color) drawInfo.TextColor=stock.Color;
}
}
this.DrawItemText(drawInfo.Text, drawInfo.TextColor, drawInfo.TextAlign, x, top, textWidth);
}
this.DrawItemText=function(text, textColor, textAlign, left, top, width)
{
if (!text) return;
var x=left;
if (textAlign=='center')
{
x=left+width/2;