UNPKG

hqchart

Version:

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

1,476 lines (1,220 loc) 48.7 kB
/* Copyright (c) 2018 jones http://www.apache.org/licenses/LICENSE-2.0 开源项目 https://github.com/jones2000/HQChart jones_2000@163.com 封装滚动条控件 (H5版本) */ function JSScrollBarChart(divElement) { this.DivElement=divElement; this.JSChartContainer; //表格控件 this.ResizeListener; //h5 canvas this.CanvasElement=document.createElement("canvas"); this.CanvasElement.className='jsscrollbar-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("[JSScrollBarChart::JSScrollBarChart] 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(`[JSScrollBarChart::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.CreateJSScrollBarChartContainer(option); if (!chart) return false; this.JSChartContainer=chart; this.DivElement.JSChart=this; //div中保存一份 if (option.EnableResize==true) this.CreateResizeListener(); if (option.OnCreatedCallback) option.OnCreatedCallback(chart); chart.Draw(); } this.CreateJSScrollBarChartContainer=function(option) { var chart=new JSScrollBarChartContainer(this.CanvasElement); chart.Create(option); if (IFrameSplitOperator.IsNumber(option.DelayDragFrequency)) chart.DelayDragFrequency=option.DelayDragFrequency; 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; if (IFrameSplitOperator.IsBool(item.AutoLeft)) chart.AutoMargin.Left=item.AutoLeft; if (IFrameSplitOperator.IsBool(item.AutoRight)) chart.AutoMargin.Right=item.AutoRight; } this.CreateResizeListener=function() { this.ResizeListener = new ResizeObserver((entries)=>{ this.OnDivResize(entries); }); this.ResizeListener.observe(this.DivElement); } this.OnDivResize=function(entries) { JSConsole.Chart.Log("[JSScrollBarChart::OnDivResize] entries=", entries); this.OnSize( ); } ///////////////////////////////////////////////////////////////////////////// //对外接口 //事件回调 this.AddEventCallback=function(obj) { if(this.JSChartContainer && typeof(this.JSChartContainer.AddEventCallback)=='function') { JSConsole.Chart.Log('[JSScrollBarChart:AddEventCallback] obj=', obj); this.JSChartContainer.AddEventCallback(obj); } } //重新加载配置 this.ReloadResource=function(option) { if(this.JSChartContainer && typeof(this.JSChartContainer.ReloadResource)=='function') { JSConsole.Chart.Log('[JSScrollBarChart: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('[JSScrollBarChart:Draw] '); this.JSChartContainer.Draw(); } } this.RecvData=function(data, option) { if(this.JSChartContainer && typeof(this.JSChartContainer.RecvData)=='function') { JSConsole.Chart.Log('[JSScrollBarChart:RecvData] '); this.JSChartContainer.RecvData(data,option); } } this.UpdateSlider=function(obj) { if(this.JSChartContainer && typeof(this.JSChartContainer.UpdateSlider)=='function') { JSConsole.Chart.Log('[JSScrollBarChart:UpdateSlider] '); this.JSChartContainer.UpdateSlider(obj); } } this.Reset=function(option) { if(this.JSChartContainer && typeof(this.JSChartContainer.Reset)=='function') { JSConsole.Chart.Log('[JSScrollBarChart:Reset] '); this.JSChartContainer.Reset(option); } } //重新加载配置 this.ReloadResource=function(option) { if(this.JSChartContainer && typeof(this.JSChartContainer.ReloadResource)=='function') { JSConsole.Chart.Log('[JSScrollBarChart:ReloadResource] '); this.JSChartContainer.ReloadResource(option); } } } JSScrollBarChart.Init=function(divElement) { var jsChartControl=new JSScrollBarChart(divElement); jsChartControl.OnSize(); return jsChartControl; } //自定义风格 JSScrollBarChart.SetStyle=function(option) { if (option) g_JSChartResource.SetStyle(option); } //获取颜色配置 (设置配必须啊在JSChart.Init()之前) JSScrollBarChart.GetResource=function() { return g_JSChartResource; } ////////////////////////////////////////////////////////////////////////////////////////////////////////// // // // ////////////////////////////////////////////////////////////////////////////////////////////////////////// function JSScrollBarChartContainer(uielement) { this.ClassName='JSScrollBarChartContainer'; this.Frame; //框架画法 this.ChartPaint=[]; //图形画法 this.ChartSplashPaint=null; //等待提示 this.LoadDataSplashTitle="无数据"; //下载数据提示信息 this.Canvas=uielement.getContext("2d"); //画布 this.ShowCanvas=null; this.XOffsetData={ Start:-1, End:-1, Count:0 }; //Count:一共的数据个数 this.SourceData// new ChartData(); //K线数据 this.SliderChart; //滑块 this.DragMove; //={ Click:{ 点击的点}, Move:{最后移动的点}, PreMove:{上一个点的位置} }; this.DragSlider; this.DragTimer; //拖拽延迟定时器 this.DelayDragFrequency=150; this.AutoMargin={ Left:false, Right:false }; //左右2边间距是否跟K线一致 //事件回调 this.mapEvent=new Map(); //通知外部调用 key:JSCHART_EVENT_ID value:{Callback:回调,} this.UIElement=uielement; this.LastPoint=new Point(); //鼠标位置 //this.XStepPixel=10*GetDevicePixelRatio(); this.IsDestroy=false; //是否已经销毁了 this.HQChart=null; this.ChartDestroy=function() //销毁 { this.IsDestroy=true; } this.GetHQChart=function() { return this.HQChart; } //设置事件回调 //{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.Create=function(option) { this.UIElement.JSChartContainer=this; //创建等待提示 this.ChartSplashPaint = new ChartSplashPaint(); this.ChartSplashPaint.Canvas = this.Canvas; this.ChartSplashPaint.SetTitle(this.LoadDataSplashTitle); this.ChartSplashPaint.IsEnableSplash=true; //创建框架 this.Frame=new JSScrollBarFrame(); 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; this.ChartSplashPaint.Frame = this.Frame; //背景 var chart=new ScrollBarBGChart(); chart.ChartFrame=this.Frame; chart.ChartBorder=this.Frame.ChartBorder; chart.Canvas=this.Canvas; chart.GetEventCallback=(id)=> { return this.GetEventCallback(id); } this.ChartPaint.push(chart); //创建滑块 var chart=new SliderChart(); chart.ChartFrame=this.Frame; chart.ChartBorder=this.Frame.ChartBorder; chart.Canvas=this.Canvas; chart.OffsetData=this.XOffsetData; chart.GetEventCallback=(id)=> { return this.GetEventCallback(id); } this.SliderChart=chart; this.ChartPaint.push(chart); //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.UIElement.ontouchstart=(e)=> { this.OnTouchStart(e); } //this.UIElement.ontouchmove=(e)=> {this.OnTouchMove(e); } //this.UIElement.ontouchend=(e)=> {this.OnTouchEnd(e); } } //创建一个图形 this.CreateChartPaint=function(name) { var chart=g_ChartPaintFactory.Create(name); if (!chart) return null; chart.ChartFrame=this.Frame; chart.ChartBorder=this.Frame.ChartBorder; chart.Canvas=this.Canvas; chart.Data=this.Frame.Data; chart.GetEventCallback=(id)=> { return this.GetEventCallback(id); } chart.GetHQChartCallback=()=>{ return this.GetHQChart(); } return chart; } this.GetChartPaintByClassName=function(name) { for(var i=0; i<this.ChartPaint.length; ++i) { var item=this.ChartPaint[i]; if (item.ClassName==name) { return { Chart:item, Index:i }; } } return 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); var pixelTatio = GetDevicePixelRatio(); //获取设备的分辨率 this.Canvas.lineWidth=pixelTatio; //手机端需要根据分辨率比调整线段宽度 if (this.ChartSplashPaint && this.ChartSplashPaint.IsEnableSplash) { this.Frame.Draw( { IsEnableSplash:this.ChartSplashPaint.IsEnableSplash} ); this.ChartSplashPaint.Draw(); return; } 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.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.UpdateFrameMaxMin=function() { var max=null, min=null; for(var i=0;i<this.ChartPaint.length;++i) { var item=this.ChartPaint[i]; if (!item.GetMaxMin) continue; var range=item.GetMaxMin(); if (range==null || range.Max==null || range.Min==null) continue; if (max==null || max<range.Max) max=range.Max; if (min==null || min>range.Min) min=range.Min; } if (IFrameSplitOperator.IsNumber(max) && IFrameSplitOperator.IsNumber(min)) { this.Frame.HorizontalMax=max; this.Frame.HorizontalMin=min; } } //未启动 this.UIOnDblClick=function(e) { } this.CancelDragTimer=function() { if (this.DragTimer) { clearTimeout(this.DragTimer); this.DragTimer=null; } } this.UIOnMouseDown=function(e) { this.CancelDragTimer(); this.DragSlider=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; if (this.SliderChart) { var clickData=this.SliderChart.PtInChart(x,y); if (!clickData) { if (!this.Frame.PtInClient(x,y)) return; //滚动块直接移动到鼠标点击的位置 var index=this.Frame.GetXData(x); index=Math.round(index); var pageRange=this.GetPageRange(); var showCount=pageRange.ShowCount; var start=index-parseInt(showCount/2); if (start<0) start=0; var end=start+showCount; if (end>=this.Frame.XPointCount) { end=this.Frame.XPointCount-1; start=end-showCount; } var drag={ UpdateData:{ StartIndex:start, EndIndex:end, Type:3 } }; this.DragUpdate(drag); return; } this.DragSlider={ Click:{ X:e.clientX, Y:e.clientY }, LastMove:{X:e.clientX, Y:e.clientY}, Data:clickData }; this.DragSlider.DrawCount=0; //重绘次数 } 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; if (this.DragSlider) return; var mouseStatus= mouseStatus={ Cursor:"default" };; //鼠标状态 var item=this.SliderChart.PtInChart(x,y); if (item) { switch(item.Data.Type) { case 0: mouseStatus={ Cursor:"grab", Name:"SliderChart"}; break; case 1: case 2: mouseStatus={ Cursor:"col-resize", Name:"SliderChart"}; break; } } if (mouseStatus) this.UIElement.style.cursor=mouseStatus.Cursor; } 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.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; JSConsole.Chart.Log(`[JSScrollBarChartContainer::DocOnMouseMove] x=${x}, y=${y}`); if (this.DragSlider) { var drag=this.DragSlider; var moveSetp=(e.clientX-drag.LastMove.X)*pixelTatio; if (Math.abs(moveSetp)<1) return; var pageRange=this.GetPageRange(); var left=this.Frame.ChartBorder.GetLeft(); var right=this.Frame.ChartBorder.GetRight(); this.SliderChart.DragMode=true; var type=drag.Data.Data.Type; var xStart=this.SliderChart.XStart+moveSetp; var xEnd=this.SliderChart.XEnd+moveSetp; if (type==0) //整体移动 { if (xStart<left) //第1页 { xStart=pageRange.First.XStart; xEnd=pageRange.First.XEnd; } else if (xEnd>=right) { xStart=pageRange.Last.XStart; xEnd=pageRange.Last.XEnd; } this.SliderChart.XStart=xStart; this.SliderChart.XEnd=xEnd; } else if (type==1) //左移动 { if (xStart<=left) { xStart=pageRange.First.XStart; } else if (xStart>=right) { xStart=pageRange.Last.XEnd; } this.SliderChart.XStart=xStart; } else if (type==2) { if (xEnd>=right) { xEnd=pageRange.Last.XEnd; } else if (xEnd<=left) { xEnd=pageRange.First.XStart; } this.SliderChart.XEnd=xEnd; } drag.UpdateData={ XStart:xStart, XEnd:xEnd, Type:type }; drag.LastMove.X=e.clientX; drag.LastMove.Y=e.clientY; if (drag.DrawCount==0) { this.DragUpdate(drag); } else { this.DragTimer=setTimeout(()=> { this.DragUpdate(this.DragSlider); }, this.DelayDragFrequency); } } } this.DocOnMouseUp=function(e) { //清空事件 document.onmousemove=null; document.onmouseup=null; this.CancelDragTimer(); var dragSlider=this.DragSlider; this.DragMove=null; this.DragSlider=null; this.SliderChart.DragMode=false; this.DragUpdate(dragSlider); } this.DragUpdate=function(dragData) { if (!dragData || !dragData.UpdateData) return; this.UpdateXDataOffset(dragData.UpdateData); this.Draw(); ++dragData.DrawCount; } this.Reset=function(option) { this.SourceData=null; this.XOffsetData.Start=-1; this.XOffsetData.End=-1; this.XOffsetData.Count=0; this.Frame.Data=null; for(var i=0;i<this.ChartPaint.length;++i) { var item=this.ChartPaint[i]; item.Data=null; } if (this.ChartSplashPaint) this.ChartSplashPaint.IsEnableSplash=true; if (option.Draw) this.Draw(); } //外部更新滑块 obj={ Start: , End: } this.UpdateSlider=function(obj) { if (this.SliderChart.DragMode) return; var bSizeChange=false; if ((this.AutoMargin.Left || this.AutoMargin.Right) && obj.Border) { if (this.AutoMargin.Left) { if (this.Frame.ChartBorder.Left!=obj.Border.Left) bSizeChange=true; } if (this.AutoMargin.Right) { if (this.Frame.ChartBorder.Right!=obj.Border.Right) bSizeChange=true; } } var data=obj.Data; if (this.XOffsetData.Start==obj.Start && this.XOffsetData.End==obj.End && this.SourceData==data && !bSizeChange) return; this.SourceData=data; var count=data.Data.length; if (IFrameSplitOperator.IsNumber(obj.RightSpaceCount)) count+=obj.RightSpaceCount; this.Frame.XPointCount=count; this.Frame.Data=data; this.XOffsetData.Count=count; this.XOffsetData.Start=obj.Start; this.XOffsetData.End=obj.End; for(var i=0;i<this.ChartPaint.length;++i) { var item=this.ChartPaint[i]; item.Data=data; } if (this.AutoMargin.Left && obj.Border) { if (IFrameSplitOperator.IsNumber(obj.Border.Left)) this.Frame.ChartBorder.Left=obj.Border.Left; } if (this.AutoMargin.Right && obj.Border) { if (IFrameSplitOperator.IsNumber(obj.Border.Right)) this.Frame.ChartBorder.Right=obj.Border.Right; } this.UpdateFrameMaxMin(); if (this.ChartSplashPaint) this.ChartSplashPaint.IsEnableSplash=false; if (obj.Draw) this.Draw(); } //移动滑块 this.UpdateXDataOffset=function(obj) { if (!obj) return; var type=obj.Type; if (obj.Type==0) { var start=this.Frame.GetXData(obj.XStart); start=parseInt(start+0.5); //四舍五入 var moveSetp=start-this.XOffsetData.Start; this.XOffsetData.Start=start; this.XOffsetData.End+=moveSetp; } else if (obj.Type==1) { var start=this.Frame.GetXData(obj.XStart); start=parseInt(start); this.XOffsetData.Start=start; } else if (obj.Type==2) { var end=this.Frame.GetXData(obj.XEnd); end=parseInt(end); this.XOffsetData.End=end; } else if (obj.Type==3) { this.XOffsetData.End=obj.EndIndex; this.XOffsetData.Start=obj.StartIndex; type=0; } var endItem=this.SourceData.Data[this.XOffsetData.End]; var startItem=this.SourceData.Data[this.XOffsetData.Start]; var sendData={ Type:type, Count:this.XOffsetData.Count }; if (this.XOffsetData.End>this.XOffsetData.Start) { sendData.Start={ Index:this.XOffsetData.Start, Item:startItem}; sendData.End={ Index:this.XOffsetData.End, Item:endItem}; } else { sendData.Start={ Index:this.XOffsetData.End, Item:endItem }; sendData.End={ Index:this.XOffsetData.Start, Item:startItem }; } if (this.HQChart && this.HQChart.JSChartContainer) { var internalChart=this.HQChart.JSChartContainer; if (internalChart.ChartOperator) { var obj={ ID:JSCHART_OPERATOR_ID.OP_SCROOLBAR_SLIDER_CHANGED, Start:sendData.Start, End:sendData.End, Type:sendData.Type }; internalChart.ChartOperator(obj); } } var event=this.GetEventCallback(JSCHART_EVENT_ID.ON_SCROLLBAR_SLIDER_CHANGED); if (event) { event.Callback(event,sendData,this); } } 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.GetPageRange=function() { var result={}; var showCount=Math.abs(this.XOffsetData.Start-this.XOffsetData.End); result.ShowCount=showCount; //第1页 var xStart=this.Frame.GetXFromIndex(0); var xEnd=this.Frame.GetXFromIndex(showCount); result.First={ XStart:xStart, XEnd:xEnd }; //最后一页 var end=this.Frame.XPointCount-1; var xEnd=this.Frame.GetXFromIndex(end); var xStart=this.Frame.GetXFromIndex(end-showCount); result.Last={ XStart:xStart, XEnd:xEnd }; return result; } 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.Draw==true) this.Draw(); //是否立即重绘 } } ///////////////////////////////////////////////////////////////////////////////////////// // 框子 // // ///////////////////////////////////////////////////////////////////////////////////////// function JSScrollBarFrame() { this.ChartBorder; this.Canvas; //画布 this.BorderLine=null; //1=上 2=下 4=左 8=右 this.Data; this.Count=0; this.ClassName='JSScrollBarFrame'; //类名 this.HorizontalMax=10; //Y轴最大值 this.HorizontalMin=5; //Y轴最小值 this.XPointCount=0; this.XSplitTextFont=g_JSChartResource.ScrollBar.XSplitTextFont; this.XSplitTextColor=g_JSChartResource.ScrollBar.XSplitTextColor; this.XSplitLineColor=g_JSChartResource.ScrollBar.XSplitLineColor; this.BorderColor=g_JSChartResource.ScrollBar.BorderColor; //边框线 this.LogoTextColor=g_JSChartResource.FrameLogo.TextColor; this.LogoTextFont=g_JSChartResource.FrameLogo.Font; this.ReloadResource=function(resource) { this.BorderColor=g_JSChartResource.ScrollBar.BorderColor; //边框线 this.LogoTextColor=g_JSChartResource.FrameLogo.TextColor; this.LogoTextFont=g_JSChartResource.FrameLogo.Font; } this.Draw=function() { this.DrawBorder(); this.DrawLogo(); this.DrawVertical(); } 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 = 'left'; this.Canvas.textBaseline = 'top'; var x=this.ChartBorder.GetLeft()+2; var y=this.ChartBorder.GetTop()+2; this.Canvas.fillText(text,x,y); } this.DrawBorder=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; //JSConsole.Chart.Log(`[JSScrollBarFrame.DrawBorder] left=${left} `); 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.GetXFromIndex=function(index) { var count=this.XPointCount; if (count==1) { if (index==0) return this.ChartBorder.GetLeft(); else return this.ChartBorder.GetRight(); } else if (count<=0) { return this.ChartBorder.GetLeft(); } else if (index>=count) { return this.ChartBorder.GetRight(); } else { var offset=this.ChartBorder.GetLeft()+this.ChartBorder.GetWidth()*index/count; return offset; } } this.GetYFromData=function(value) { if(value<=this.HorizontalMin) return this.ChartBorder.GetBottomEx(); if(value>=this.HorizontalMax) return this.ChartBorder.GetTopEx(); var height=this.ChartBorder.GetHeightEx()*(value-this.HorizontalMin)/(this.HorizontalMax-this.HorizontalMin); return this.ChartBorder.GetBottomEx()-height; } //X坐标转x轴数值 this.GetXData=function(x) { if (x<=this.ChartBorder.GetLeft()) return 0; if (x>=this.ChartBorder.GetRight()) return this.XPointCount; return (x-this.ChartBorder.GetLeft())*(this.XPointCount*1.0/this.ChartBorder.GetWidth()); } this.GetPreSetpWidth=function() { return this.XPointCount*1.0/this.ChartBorder.GetWidth(); } this.DrawVertical=function() { if (this.ChartBorder.Bottom<=5) return; if (!this.Data || !IFrameSplitOperator.IsNonEmptyArray(this.Data.Data)) return var item=this.Data.Data[0]; var preYear=parseInt(item.Date/10000); var preDay=item.Date%10000; this.Canvas.font=this.XSplitTextFont; this.Canvas.fillStyle=this.XSplitTextColor; this.Canvas.textBaseline="top"; var yText=this.ChartBorder.GetBottom()+2; var top=this.ChartBorder.GetTop(); var bottom=this.ChartBorder.GetBottom(); var preXText=0; if (ChartData.IsMilliSecondPeriod(this.Data.Period)) { var preHour=null; for(var i=0;i<this.Data.Data.length;++i) { var item=this.Data.Data[i]; var day=item.Date%10000; var time=parseInt(item.Time/1000); var hour=parseInt(time/10000); if (i==0) { var text=IFrameSplitOperator.FormatDateString(item.Date, "MM-DD"); var x=this.ChartBorder.GetLeft(); this.Canvas.textAlign="left"; this.Canvas.fillText(text,x,yText); var textWidth=this.Canvas.measureText(text).width+2; preXText=x+textWidth; preDay=day; preHour=hour; continue; } if (hour!=preHour) { var text=IFrameSplitOperator.FormatTimeString(item.Time, "HH:MM:SS.fff"); var x=this.GetXFromIndex(i); var textWidth=this.Canvas.measureText(text).width+2; if (x-textWidth/2>preXText) { this.Canvas.textAlign="center"; this.Canvas.fillText(text,x,yText); preXText=x+textWidth/2; } x=ToFixedPoint(x); this.Canvas.strokeStyle=this.XSplitLineColor; this.Canvas.beginPath(); this.Canvas.moveTo(x,top); this.Canvas.lineTo(x,bottom); this.Canvas.stroke(); preHour=hour; } } } else if (ChartData.IsMinutePeriod(this.Data.Period,true)) { for(var i=0;i<this.Data.Data.length;++i) { var item=this.Data.Data[i]; var day=item.Date%10000; if (i==0) { var text=IFrameSplitOperator.FormatDateString(item.Date, "MM-DD"); var x=this.ChartBorder.GetLeft(); this.Canvas.textAlign="left"; this.Canvas.fillText(text,x,yText); var textWidth=this.Canvas.measureText(text).width+2; preXText=x+textWidth; preDay=day; continue; } if (day!=preDay) { var text=IFrameSplitOperator.FormatDateString(item.Date, "MM-DD"); var x=this.GetXFromIndex(i); var textWidth=this.Canvas.measureText(text).width+2; if (x-textWidth/2>preXText) { this.Canvas.textAlign="center"; this.Canvas.fillText(text,x,yText); preXText=x+textWidth/2; } x=ToFixedPoint(x); this.Canvas.strokeStyle=this.XSplitLineColor; this.Canvas.beginPath(); this.Canvas.moveTo(x,top); this.Canvas.lineTo(x,bottom); this.Canvas.stroke(); preDay=day; } } } else { for(var i=0;i<this.Data.Data.length;++i) { var item=this.Data.Data[i]; var year=parseInt(item.Date/10000); if (i==0) { var text=`${year}`; var x=this.ChartBorder.GetLeft(); var textWidth=this.Canvas.measureText(text).width+2; this.Canvas.textAlign="left"; this.Canvas.fillText(text,x,yText); preXText=x+textWidth; preYear=year; continue; } if (year!=preYear) { var text=`${year}`; var x=this.GetXFromIndex(i); var textWidth=this.Canvas.measureText(text).width+2; if (x-textWidth/2>preXText) { this.Canvas.textAlign="center"; this.Canvas.fillText(text,x,yText); preXText=x+textWidth/2; } x=ToFixedPoint(x); this.Canvas.strokeStyle=this.XSplitLineColor; this.Canvas.beginPath(); this.Canvas.moveTo(x,top); this.Canvas.lineTo(x,bottom); this.Canvas.stroke(); preYear=year; } } } } this.PtInClient=function(x,y) { var left=ToFixedPoint(this.ChartBorder.GetLeft()); var top=ToFixedPoint(this.ChartBorder.GetTop()); var right=ToFixedPoint(this.ChartBorder.GetRight()); var bottom=ToFixedPoint(this.ChartBorder.GetBottom()); if (x>=left && x<=right && y>=top && y<=bottom) return true; return false; } } ///////////////////////////////////////////////////////////////////////////////////////////////// // 滑块 // ///////////////////////////////////////////////////////////////////////////////////////////////// function SliderChart() { this.Canvas; //画布 this.ChartBorder; //边框信息 this.ChartFrame; //框架画法 this.Name; //名称 this.ClassName='SliderChart'; //类名 this.OffsetData; this.Color=g_JSChartResource.ScrollBar.Slider.BarAreaColor; this.BarColor=g_JSChartResource.ScrollBar.Slider.BarColor; this.BarWidth=g_JSChartResource.ScrollBar.Slider.BarWidth; this.BarPadding=g_JSChartResource.ScrollBar.Slider.BarPadding; //上下留白 this.MinCenterWidth=g_JSChartResource.ScrollBar.Slider.MinCenterWidth; this.DateFont=g_JSChartResource.ScrollBar.Slider.DateFont; this.DateColor=g_JSChartResource.ScrollBar.Slider.DateColor; this.AryRect=[]; //[{ Rect:{Left, Top, Right:, Bottom:, Width, Height:}, Type:0中间 1=左 2=右 }] this.XStart; this.XEnd; this.SizeChange=true; this.DragMode=false; this.ReloadResource=function(resource) { this.Color=g_JSChartResource.ScrollBar.Slider.BarAreaColor; this.BarColor=g_JSChartResource.ScrollBar.Slider.BarColor; this.DateFont=g_JSChartResource.ScrollBar.Slider.DateFont; this.DateColor=g_JSChartResource.ScrollBar.Slider.DateColor; } this.Draw=function() { this.AryRect=[]; if (!this.OffsetData || !IFrameSplitOperator.IsPlusNumber(this.OffsetData.Count)) return; if (!IFrameSplitOperator.IsNumber(this.OffsetData.Start) || !IFrameSplitOperator.IsNumber(this.OffsetData.End)) return; var top=this.ChartBorder.GetTop(); var bottom=this.ChartBorder.GetBottom(); var startData, endData; if (this.DragMode) { var xStart=this.XStart; var xEnd=this.XEnd; var startIndex=this.ChartFrame.GetXData(xStart); var endIndx=this.ChartFrame.GetXData(xEnd); startIndex=parseInt(startIndex); endIndx=parseInt(endIndx); startData={ Data:this.Data.Data[this.OffsetData.Start], X:xStart, Type:startIndex<endIndx?0:1 }; endData={Data:this.Data.Data[this.OffsetData.End], X:xEnd, Type:endIndx>startIndex?1:0 }; } else { var start=this.OffsetData.Start, end=this.OffsetData.End; var xStart=this.ChartFrame.GetXFromIndex(start); var xEnd=this.ChartFrame.GetXFromIndex(end); this.XStart=xStart; this.XEnd=xEnd; startData={ Data:this.Data.Data[this.OffsetData.Start], X:xStart, Type:xStart<xEnd?0:1 }; endData={Data:this.Data.Data[this.OffsetData.End], X:xEnd, Type:xEnd>xStart?1:0 }; } this.Canvas.fillStyle=this.Color; var rtBar={ Left:Math.min(xStart,xEnd), Top:top, Width:Math.abs(xEnd-xStart), Height: bottom-top}; rtBar.Right=rtBar.Left+rtBar.Width; rtBar.Bottom=rtBar.Top+rtBar.Height; if (rtBar.Width<this.MinCenterWidth) { rtBar.Left-=(this.MinCenterWidth-rtBar.Width)/2; rtBar.Width=this.MinCenterWidth; rtBar.Right=rtBar.Left+rtBar.Width; } this.Canvas.fillRect(rtBar.Left, rtBar.Top, rtBar.Width, rtBar.Height); this.AryRect.push({ Rect:rtBar, Type:0}); //左右拖拽块 var pixelRatio=GetDevicePixelRatio(); var barWidth=this.BarWidth*pixelRatio; var barHeight=bottom-top-(this.BarPadding*2)*pixelRatio; var xBar=xStart-this.BarWidth/2; var yBar=top+this.BarPadding*pixelRatio; this.Canvas.fillStyle=this.BarColor; var rtBar={Left:xBar, Top:yBar, Width:barWidth, Height:barHeight}; rtBar.Right=rtBar.Left+rtBar.Width; rtBar.Bottom=rtBar.Top+rtBar.Height; this.Canvas.fillRect(rtBar.Left, rtBar.Top, rtBar.Width, rtBar.Height); this.AryRect.push({ Rect:rtBar, Type:1}); var xBar=xEnd-this.BarWidth/2; var rtBar={Left:xBar, Top:yBar, Width:barWidth, Height:barHeight}; rtBar.Right=rtBar.Left+rtBar.Width; rtBar.Bottom=rtBar.Top+rtBar.Height; this.Canvas.fillRect(rtBar.Left, rtBar.Top, rtBar.Width, rtBar.Height); this.AryRect.push({ Rect:rtBar, Type:2}); //最右边可能是空白区 要处理下 if (endData.Type==1) { var dataIndex=this.OffsetData.End; if (dataIndex>=this.Data.Data.length) endData.Data=this.Data.Data[this.Data.Data.length-1]; } this.DrawDateTime(startData); this.DrawDateTime(endData); } this.DrawDateTime=function(data) { if (!data || !data.Data) return; var text=IFrameSplitOperator.FormatDateString(data.Data.Date); var top=this.ChartBorder.GetTop(); var bottom=this.ChartBorder.GetBottom(); var timeText=null; if (ChartData.IsMilliSecondPeriod(this.Data.Period)) { var time=parseInt(data.Data.Time/1000); text=IFrameSplitOperator.FormatTimeString(time,"HH:MM:SS"); } else if (ChartData.IsSecondPeriod(this.Data.Period)) { text=IFrameSplitOperator.FormatTimeString(time,"HH:MM:SS"); } else if (ChartData.IsMinutePeriod(this.Data.Period, true)) { timeText=IFrameSplitOperator.FormatTimeString(data.Data.Time,"HH:MM"); } if (data.Type==0) { this.Canvas.textAlign="right"; var x=data.X-this.BarWidth/2; } else if (data.Type==1) { this.Canvas.textAlign="left"; var x=data.X+this.BarWidth/2; } this.Canvas.font=this.DateFont; var fontHeight=this.Canvas.measureText("擎").width; this.Canvas.textBaseline="middle"; this.Canvas.fillStyle=this.DateColor; var yText=top+(bottom-top)/2; this.Canvas.fillText(text,x,yText); if (timeText) { yText+=fontHeight; this.Canvas.fillText(timeText,x,yText); } } this.PtInChart=function(x,y) { if (!IFrameSplitOperator.IsNonEmptyArray(this.AryRect)) return null; for(var i=this.AryRect.length-1; i>=0; --i) { var item=this.AryRect[i]; var rt=item.Rect; if (x>=rt.Left && x<=rt.Right && y>=rt.Top && y<=rt.Bottom) { return { Data:item }; } } return null; } } /////////////////////////////////////////////////////////////////////////////////////////////// // 滚动条K线背景色 // // ///////////////////////////////////////////////////////////////////////////////////////////////// function ScrollBarBGChart() { this.Canvas; //画布 this.ChartBorder; //边框信息 this.ChartFrame; //框架画法 this.Name; //名称 this.ClassName='ScrollBarBGChart'; //类名 this.SizeChange=true; this.Data; this.Color=g_JSChartResource.ScrollBar.BGChart.Color; //线段颜色 this.LineWidth=g_JSChartResource.ScrollBar.BGChart.LineWidth; //线段宽度 this.AreaColor=g_JSChartResource.ScrollBar.BGChart.AreaColor; //面积图颜色 this.ReloadResource=function(resource) { this.Color=g_JSChartResource.ScrollBar.BGChart.Color; //线段颜色 this.LineWidth=g_JSChartResource.ScrollBar.BGChart.LineWidth; //线段宽度 this.AreaColor=g_JSChartResource.ScrollBar.BGChart.AreaColor; //面积图颜色 } this.Draw=function() { if (!this.Data || !IFrameSplitOperator.IsNonEmptyArray(this.Data.Data)) return; this.Canvas.save(); if (this.LineWidth>0) this.Canvas.lineWidth=this.LineWidth * GetDevicePixelRatio(); var bottom=this.ChartBorder.GetBottom(); this.Canvas.strokeStyle=this.Color; var bFirstPoint=true; var drawCount=0,x,y; var firstPoint={ }; for(var i=0;i<this.Data.Data.length;++i) { var item=this.Data.Data[i]; var value=item.Close; if (!IFrameSplitOperator.IsNumber(value)) continue; x=this.ChartFrame.GetXFromIndex(i); y=this.ChartFrame.GetYFromData(value); if (bFirstPoint) { this.Canvas.beginPath(); this.Canvas.moveTo(x,y); bFirstPoint=false; firstPoint={ X:x, Y:y }; } else { this.Canvas.lineTo(x,y); } ++drawCount; } if (drawCount>0) { this.Canvas.stroke(); this.Canvas.lineTo(x,bottom); this.Canvas.lineTo(firstPoint.X,bottom); this.Canvas.closePath(); this.Canvas.fillStyle=this.AreaColor; this.Canvas.fill(); } this.Canvas.restore(); } this.GetMaxMin=function() { var range={ Max:null, Min:null }; if (!this.Data || !IFrameSplitOperator.IsNonEmptyArray(this.Data.Data)) return range; for(var i=0;i<this.Data.Data.length;++i) { var item=this.Data.Data[i]; var value=item.Close; if (!IFrameSplitOperator.IsNumber(value)) continue; if (range.Max==null || range.Max<value) range.Max=value; if (range.Min==null || range.Min>value) range.Min=value; } return range; } }