chartai 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/chart-library.d.ts +170 -0
- package/dist/chart-library.d.ts.map +1 -0
- package/dist/chart-library.js +544 -0
- package/dist/chart-library.min.js +1 -0
- package/dist/gpu-worker.d.ts +2 -0
- package/dist/gpu-worker.d.ts.map +1 -0
- package/dist/gpu-worker.js +853 -0
- package/dist/gpu-worker.min.js +1 -0
- package/dist/hover.d.ts +3 -0
- package/dist/hover.d.ts.map +1 -0
- package/dist/hover.js +181 -0
- package/dist/hover.min.js +1 -0
- package/dist/labels.d.ts +3 -0
- package/dist/labels.d.ts.map +1 -0
- package/dist/labels.js +101 -0
- package/dist/labels.min.js +1 -0
- package/dist/zoom.d.ts +6 -0
- package/dist/zoom.d.ts.map +1 -0
- package/dist/zoom.js +205 -0
- package/dist/zoom.min.js +1 -0
- package/package.json +78 -0
- package/readme.md +121 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var a=256,X="struct Uniforms{width: f32,height: f32,viewMinX: f32,viewMaxX: f32,viewMinY: f32,viewMaxY: f32,pointCount: u32,isDark: f32,bgR: f32,bgG: f32,bgB: f32,pointRadius: f32,dataMinY: f32,dataMaxY: f32,dataMinX: f32,dataMaxX: f32,visibleStart: u32,visibleCount: u32,dispatchXCount: u32,maxSamplesPerPixel: u32,seriesCount: u32,_pad2: u32,_pad3: u32,_pad4: u32,};struct SeriesInfo{color: vec4f,visibleRange: vec2u,pointSize: f32,_pad: f32,};struct SeriesIndex{index: u32,_pad0: u32,_pad1: u32,_pad2: u32,};",r="fn lowerBound(val: f32,count: u32)-> u32{var lo = 0u;var hi = count;while(lo < hi){let mid =(lo + hi)/ 2u;if(dataX[mid] < val){lo = mid + 1u;}else{hi = mid;}}return lo;}";var W="struct Uniforms{width: f32,height: f32,viewMinX: f32,viewMaxX: f32,viewMinY: f32,viewMaxY: f32,pointCount: u32,isDark: f32,bgR: f32,bgG: f32,bgB: f32,pointRadius: f32,dataMinY: f32,dataMaxY: f32,dataMinX: f32,dataMaxX: f32,visibleStart: u32,visibleCount: u32,dispatchXCount: u32,maxSamplesPerPixel: u32,seriesCount: u32,_pad2: u32,_pad3: u32,_pad4: u32,};struct SeriesInfo{color: vec4f,visibleRange: vec2u,pointSize: f32,_pad: f32,};struct SeriesIndex{index: u32,_pad0: u32,_pad1: u32,_pad2: u32,};fn l(c:vec4f)->f32{return dot(c.rgb,vec3f(.299,.587,.114))+c.a*.25;}fn fxaa(u:vec2f,t:texture_2d<f32>,s:sampler)->vec4f{let r=1./vec2f(textureDimensions(t));let rM=textureSampleLevel(t,s,u,0.);let rN=textureSampleLevel(t,s,u+vec2f(0.,-r.y),0.);let rS=textureSampleLevel(t,s,u+vec2f(0.,r.y),0.);let rE=textureSampleLevel(t,s,u+vec2f(r.x,0.),0.);let rW=textureSampleLevel(t,s,u+vec2f(-r.x,0.),0.);let lM=l(rM);let lN=l(rN);let lS=l(rS);let lE=l(rE);let lW=l(rW);let mi=min(lM,min(min(lN,lS),min(lE,lW)));let ma=max(lM,max(max(lN,lS),max(lE,lW)));let ra=ma-mi;if(ra<max(.0833,ma*.166)){return rM;}let lNW=l(textureSampleLevel(t,s,u+vec2f(-r.x,-r.y),0.));let lNE=l(textureSampleLevel(t,s,u+vec2f(r.x,-r.y),0.));let lSW=l(textureSampleLevel(t,s,u+vec2f(-r.x,r.y),0.));let lSE=l(textureSampleLevel(t,s,u+vec2f(r.x,r.y),0.));let sB=min(.35,max(0.,abs((lN+lS+lE+lW)*.25-lM)/ra-.25)*1.33);let iH=abs(lNW+lNE-2.*lN)+abs(lW+lE-2.*lM)*2.+abs(lSW+lSE-2.*lS)>=abs(lNW+lSW-2.*lW)+abs(lN+lS-2.*lM)*2.+abs(lNE+lSE-2.*lE);let l1=select(lW,lN,iH);let l2=select(lE,lS,iH);let pD=abs(l2-lM)>abs(l1-lM);let sL=select(r.x,r.y,iH);let lA=.5*(select(l1,l2,pD)+lM);let gS=max(abs(l1-lM),abs(l2-lM))*.25;var eU=u;if(iH){eU.y+=select(-.5,.5,pD)*sL;}else{eU.x+=select(-.5,.5,pD)*sL;}let eS=select(vec2f(r.x,0.),vec2f(0.,r.y),iH);var uN=eU-eS;var uP=eU+eS;var eN=l(textureSampleLevel(t,s,uN,0.))-lA;var eP=l(textureSampleLevel(t,s,uP,0.))-lA;var dN=abs(eN)>=gS;var dP=abs(eP)>=gS;for(var i=1;i<8;i++){if(!dN){uN-=eS*1.5;eN=l(textureSampleLevel(t,s,uN,0.))-lA;dN=abs(eN)>=gS;}if(!dP){uP+=eS*1.5;eP=l(textureSampleLevel(t,s,uP,0.))-lA;dP=abs(eP)>=gS;}if(dN&&dP){break;}}let dtN=select(u.x-uN.x,u.y-uN.y,iH);let dtP=select(uP.x-u.x,uP.y-u.y,iH);let eB=select(0.,.5-min(dtN,dtP)/(dtN+dtP),(lM-lA<0.)!=(select(eP<0.,eN<0.,dtN<dtP)));var fU=u;let fL=max(eB,sB);if(iH){fU.y+=select(-1.,1.,pD)*fL*sL;}else{fU.x+=select(-1.,1.,pD)*fL*sL;}return textureSampleLevel(t,s,fU,0.);}@group(0)@binding(0)var<uniform> u: Uniforms;@group(0)@binding(1)var inputTex: texture_2d<f32>;@group(0)@binding(2)var samp: sampler;struct VertexOutput{@builtin(position)pos: vec4f,@location(0)uv: vec2f,};@vertex fn vs(@builtin(vertex_index)vi: u32)-> VertexOutput{var positions = array<vec2f,4>(vec2f(-1.0,-1.0),vec2f(1.0,-1.0),vec2f(-1.0,1.0),vec2f(1.0,1.0));var uvs = array<vec2f,4>(vec2f(0.0,1.0),vec2f(1.0,1.0),vec2f(0.0,0.0),vec2f(1.0,0.0));var out: VertexOutput;out.pos = vec4f(positions[vi],0.0,1.0);out.uv = uvs[vi];return out;}@fragment fn fs(in: VertexOutput)-> @location(0)vec4f{return fxaa(in.uv,inputTex,samp);}";var C=`${X}struct LineData{screenX: f32,minScreenY: f32,maxScreenY: f32,valid: f32,};@group(0)@binding(0)var<uniform> u: Uniforms;@group(0)@binding(1)var<storage,read> dataX: array<f32>;@group(0)@binding(2)var<storage,read> dataY: array<f32>;@group(0)@binding(3)var<storage,read_write> lineData: array<LineData>;@group(0)@binding(4)var<storage,read> allSeries: array<SeriesInfo>;${r}@compute @workgroup_size(${a})fn main(@builtin(global_invocation_id)id: vec3u){let outputIdx = id.x;let maxCols = u32(u.width);let count = u.pointCount;if(outputIdx >= maxCols || count == 0u){if(outputIdx < maxCols){lineData[outputIdx] = LineData(-1.0,-1.0,-1.0,0.0);}return;}let viewRangeX = u.viewMaxX - u.viewMinX;let viewRangeY = u.viewMaxY - u.viewMinY;if(viewRangeX < 0.0001 || viewRangeY < 0.0001){lineData[outputIdx] = LineData(-1.0,-1.0,-1.0,0.0);return;}let relPx = f32(outputIdx);let pixelMinX = u.viewMinX +(relPx / u.width)* viewRangeX;let pixelMaxX = u.viewMinX +((relPx + 1.0)/ u.width)* viewRangeX;let startIdx = lowerBound(pixelMinX,count);var endIdx = lowerBound(pixelMaxX,count);endIdx = min(endIdx,count);let centerX =(pixelMinX + pixelMaxX)* 0.5;if(startIdx >= endIdx){var bestIdx = startIdx;if(startIdx > 0u && startIdx < count){let distPrev = abs(dataX[startIdx - 1u] - centerX);let distCurr = abs(dataX[startIdx] - centerX);if(distPrev < distCurr){bestIdx = startIdx - 1u;}}else if(startIdx >= count && count > 0u){bestIdx = count - 1u;}if(bestIdx >= count){lineData[outputIdx] = LineData(-1.0,-1.0,-1.0,0.0);return;}let y = dataY[bestIdx];let normY =(y - u.viewMinY)/ viewRangeY;let screenY = 1.0 - normY;let normX =(dataX[bestIdx] - u.viewMinX)/ viewRangeX;let screenX = normX;lineData[outputIdx] = LineData(screenX,screenY,screenY,1.0);return;}var dataMinY = dataY[startIdx];var dataMaxY = dataY[startIdx];let rangeCount = endIdx - startIdx;let maxSamples = u.maxSamplesPerPixel;if(maxSamples > 1u && rangeCount > maxSamples){let stride = f32(rangeCount - 1u)/ f32(maxSamples - 1u);for(var s = 0u;s < maxSamples;s++){let idx = startIdx + u32(f32(s)* stride);if(idx < endIdx){let y = dataY[idx];dataMinY = min(dataMinY,y);dataMaxY = max(dataMaxY,y);}}let lastY = dataY[endIdx - 1u];dataMinY = min(dataMinY,lastY);dataMaxY = max(dataMaxY,lastY);}else{for(var i = startIdx + 1u;i < endIdx;i++){let y = dataY[i];dataMinY = min(dataMinY,y);dataMaxY = max(dataMaxY,y);}}let normX =(centerX - u.viewMinX)/ viewRangeX;let screenX = normX;let normMaxY =(dataMaxY - u.viewMinY)/ viewRangeY;let normMinY =(dataMinY - u.viewMinY)/ viewRangeY;let minScreenY = 1.0 - normMaxY;let maxScreenY = 1.0 - normMinY;lineData[outputIdx] = LineData(screenX,minScreenY,maxScreenY,1.0);}`,y=`${X}struct LineData{screenX: f32,minScreenY: f32,maxScreenY: f32,valid: f32,};@group(0)@binding(0)var<uniform> u: Uniforms;@group(0)@binding(1)var<storage,read> lineData: array<LineData>;@group(0)@binding(2)var<storage,read> allSeries: array<SeriesInfo>;struct VertexOutput{@builtin(position)pos: vec4f,@location(0)alpha: f32,@location(1)@interpolate(flat)seriesIdx: u32,};@vertex fn vs(@builtin(vertex_index)vi: u32,@builtin(instance_index)series_idx: u32)-> VertexOutput{var out: VertexOutput;out.seriesIdx = series_idx;let maxCols = u32(u.width);let segIdx = vi / 2u;let endpoint = vi % 2u;if(segIdx < maxCols){let d = lineData[segIdx];let y = select(d.maxScreenY,d.minScreenY,endpoint == 0u);out.pos = vec4f(d.screenX * 2.0 - 1.0,1.0 - y * 2.0,0.0,d.valid);out.alpha = d.valid;}else{let connIdx = segIdx - maxCols;if(connIdx + 1u >= maxCols){out.pos = vec4f(0.0,0.0,0.0,0.0);out.alpha = 0.0;return out;}let d0 = lineData[connIdx];let d1 = lineData[connIdx + 1u];let segValid = min(d0.valid,d1.valid);if(endpoint == 0u){let midY =(d0.minScreenY + d0.maxScreenY)* 0.5;out.pos = vec4f(d0.screenX * 2.0 - 1.0,1.0 - midY * 2.0,0.0,segValid);}else{let midY =(d1.minScreenY + d1.maxScreenY)* 0.5;out.pos = vec4f(d1.screenX * 2.0 - 1.0,1.0 - midY * 2.0,0.0,segValid);}out.alpha = segValid;}return out;}@fragment fn fs(in: VertexOutput)-> @location(0)vec4f{if(in.alpha < 0.1){discard;}let series = allSeries[in.seriesIdx];return vec4f(series.color.rgb,1.0);}`;var L=`${X}@group(0)@binding(0)var<uniform> u: Uniforms;@group(0)@binding(1)var<storage,read> dataX: array<f32>;@group(0)@binding(2)var<storage,read> dataY: array<f32>;@group(0)@binding(3)var outputTex: texture_storage_2d<rgba8unorm,write>;@group(0)@binding(4)var<storage,read> allSeries: array<SeriesInfo>;@group(0)@binding(5)var<uniform> seriesIdx: SeriesIndex;@compute @workgroup_size(${a})fn main(@builtin(global_invocation_id)id: vec3u){let series = allSeries[seriesIdx.index];let visStart = series.visibleRange.x;let visCount = series.visibleRange.y;let localIdx = id.y * u.dispatchXCount + id.x;if(localIdx >= visCount){return;}let idx = visStart + localIdx;let count = u.pointCount;if(idx >= count){return;}let x = dataX[idx];let y = dataY[idx];if(y < u.viewMinY || y > u.viewMaxY){return;}let width = u32(u.width);let height = u32(u.height);let rangeX = u.viewMaxX - u.viewMinX;let rangeY = u.viewMaxY - u.viewMinY;if(rangeX < 0.0001 || rangeY < 0.0001){return;}let normX =(x - u.viewMinX)/ rangeX;let normY =(y - u.viewMinY)/ rangeY;let screenX = normX;let screenY = 1.0 - normY;let pixelX = i32(screenX * f32(width));let pixelY = i32(screenY * f32(height));if(idx > visStart){let prevX = dataX[idx - 1u];let prevY = dataY[idx - 1u];let prevNormX =(prevX - u.viewMinX)/ rangeX;let prevNormY =(prevY - u.viewMinY)/ rangeY;let prevPx = i32(prevNormX * f32(width));let prevPy = i32((1.0 - prevNormY)* f32(height));if(pixelX == prevPx && pixelY == prevPy){return;}}let iWidth = i32(width);let iHeight = i32(height);if(pixelX < 0 || pixelX >= iWidth){return;}if(pixelY < 0 || pixelY >= iHeight){return;}let color = series.color;let radius = i32(series.pointSize);for(var dy = -radius;dy <= radius;dy++){for(var dx = -radius;dx <= radius;dx++){if(dx * dx + dy * dy > radius * radius){continue;}let px = pixelX + dx;let py = pixelY + dy;if(px >= 0 && px < iWidth && py >= 0 && py < iHeight){textureStore(outputTex,vec2i(px,py),color);}}}}`;var D=`${X}struct BarData{screenX: f32,minY: f32,maxY: f32,barWidth: f32,};@group(0)@binding(0)var<uniform> u: Uniforms;@group(0)@binding(1)var<storage,read> dataX: array<f32>;@group(0)@binding(2)var<storage,read> dataY: array<f32>;@group(0)@binding(3)var<storage,read_write> barData: array<BarData>;@group(0)@binding(4)var<storage,read> allSeries: array<SeriesInfo>;@group(0)@binding(5)var<uniform> seriesIdx: SeriesIndex;${r}fn barHalfWidth(idx: u32,count: u32)-> f32{if(count <= 1u){return(u.viewMaxX - u.viewMinX)* 0.4;}var spacing: f32;if(idx == 0u){spacing = dataX[1u] - dataX[0u];}else if(idx >= count - 1u){spacing = dataX[count - 1u] - dataX[count - 2u];}else{spacing = min(dataX[idx + 1u] - dataX[idx],dataX[idx] - dataX[idx - 1u]);}let seriesCount = max(1u,u.seriesCount);return(spacing * 0.4)/ f32(seriesCount);}@compute @workgroup_size(${a})fn main(@builtin(global_invocation_id)id: vec3u){let outputIdx = id.x;let maxCols = u32(u.width);let count = u.pointCount;if(outputIdx >= maxCols || count == 0u){if(outputIdx < maxCols){barData[outputIdx] = BarData(0.0,0.0,0.0,0.0);}return;}let viewRangeX = u.viewMaxX - u.viewMinX;let viewRangeY = u.viewMaxY - u.viewMinY;if(viewRangeX < 0.0001 || viewRangeY < 0.0001){barData[outputIdx] = BarData(0.0,0.0,0.0,0.0);return;}let relPx = f32(outputIdx);let pixelMinX = u.viewMinX +(relPx / u.width)* viewRangeX;let pixelMaxX = u.viewMinX +((relPx + 1.0)/ u.width)* viewRangeX;let startIdx = lowerBound(pixelMinX,count);var endIdx = lowerBound(pixelMaxX,count);endIdx = min(endIdx,count);let centerX =(pixelMinX + pixelMaxX)* 0.5;let onePixel = 1.0 / u.width;if(startIdx >= endIdx){var hit = false;var bestX: f32 = 0.0;var bestY: f32 = 0.0;var bestHW: f32 = 0.0;var bestDist: f32 = 1e10;if(startIdx < count){let bx = dataX[startIdx];let hw = barHalfWidth(startIdx,count);if(pixelMinX < bx + hw && pixelMaxX > bx - hw){let d = abs(bx - centerX);bestX = bx;bestY = dataY[startIdx];bestHW = hw;bestDist = d;hit = true;}}if(startIdx > 0u){let prev = startIdx - 1u;let bx = dataX[prev];let hw = barHalfWidth(prev,count);if(pixelMinX < bx + hw && pixelMaxX > bx - hw){let d = abs(bx - centerX);if(d < bestDist){bestX = bx;bestY = dataY[prev];bestHW = hw;bestDist = d;}hit = true;}}if(!hit){barData[outputIdx] = BarData(0.0,0.0,0.0,0.0);return;}let seriesCount = max(1u,u.seriesCount);let barOffset =(f32(seriesIdx.index)- f32(seriesCount - 1u)* 0.5)*(bestHW * 2.0);let offsetX = bestX + barOffset;let normX =(offsetX - u.viewMinX)/ viewRangeX;let fullWidth = bestHW * 2.0 / viewRangeX;let gapSize = max(onePixel,fullWidth * 0.05);let bw = max(fullWidth - gapSize,onePixel);barData[outputIdx] = BarData(normX,bestY,bestY,bw);return;}var dataMinY = dataY[startIdx];var dataMaxY = dataY[startIdx];let rangeCount = endIdx - startIdx;let maxSamples = u.maxSamplesPerPixel;if(maxSamples > 0u && rangeCount > maxSamples){let stride = f32(rangeCount - 1u)/ f32(maxSamples - 1u);for(var s = 0u;s < maxSamples;s++){let idx = startIdx + u32(f32(s)* stride);if(idx < endIdx){let y = dataY[idx];dataMinY = min(dataMinY,y);dataMaxY = max(dataMaxY,y);}}let lastY = dataY[endIdx - 1u];dataMinY = min(dataMinY,lastY);dataMaxY = max(dataMaxY,lastY);}else{for(var i = startIdx + 1u;i < endIdx;i++){let y = dataY[i];dataMinY = min(dataMinY,y);dataMaxY = max(dataMaxY,y);}}let hw = barHalfWidth(startIdx,count);let fullWidth = hw * 2.0 / viewRangeX;let gapSize = max(onePixel,fullWidth * 0.05);let bw = max(fullWidth - gapSize,onePixel);let seriesCount = max(1u,u.seriesCount);let barOffset =(f32(seriesIdx.index)- f32(seriesCount - 1u)* 0.5)*(hw * 2.0);let dataX_centered = dataX[startIdx] + barOffset;let normX =(dataX_centered - u.viewMinX)/ viewRangeX;barData[outputIdx] = BarData(normX,dataMinY,dataMaxY,bw);}`,H=`${X}struct BarData{screenX: f32,minY: f32,maxY: f32,barWidth: f32,};@group(0)@binding(0)var<uniform> u: Uniforms;@group(0)@binding(1)var<storage,read> barData: array<BarData>;@group(0)@binding(2)var<storage,read> allSeries: array<SeriesInfo>;struct VertexOutput{@builtin(position)pos: vec4f,@location(0)normY: f32,@location(1)@interpolate(flat)seriesIdx: u32,};@vertex fn vs(@builtin(vertex_index)vi: u32,@builtin(instance_index)series_idx: u32)-> VertexOutput{var out: VertexOutput;out.seriesIdx = series_idx;let maxCols = u32(u.width);let colIdx = vi / 6u;let vertexType = vi % 6u;if(colIdx >= maxCols){out.pos = vec4f(0.0,0.0,0.0,0.0);out.normY = 0.0;return out;}let bd = barData[colIdx];if(bd.barWidth <= 0.0){out.pos = vec4f(0.0,0.0,0.0,0.0);out.normY = 0.0;return out;}let viewRangeY = u.viewMaxY - u.viewMinY;let safeRangeY = select(viewRangeY,1.0,viewRangeY < 0.0001);let normMinY =(min(bd.minY,0.0)- u.viewMinY)/ safeRangeY;let normMaxY =(max(bd.maxY,0.0)- u.viewMinY)/ safeRangeY;let top = 1.0 - normMaxY;let bottom = 1.0 - normMinY;let halfW = bd.barWidth * 0.5;let left = bd.screenX - halfW;let right = bd.screenX + halfW;var positions = array<vec2f,6>(vec2f(left,bottom),vec2f(right,bottom),vec2f(left,top),vec2f(left,top),vec2f(right,bottom),vec2f(right,top));let screenPos = positions[vertexType];let clipX = screenPos.x * 2.0 - 1.0;let clipY = 1.0 - screenPos.y * 2.0;out.pos = vec4f(clipX,clipY,0.0,1.0);out.normY = normMaxY;return out;}@fragment fn fs(in: VertexOutput)-> @location(0)vec4f{let series = allSeries[in.seriesIdx];return vec4f(series.color.rgb,0.85);}`;var t,_,v=new Map,w=!1,p={},g={},s=0,B=0,N=!1,U=null,R=new ArrayBuffer(112),j=new Float32Array(R),F=new Uint32Array(R),P,q=(e,i)=>{if(!e.seriesStorageBuffer||e.series.length===0)return;let n=new Float32Array(e.series.length*8),l=new Uint32Array(n.buffer);for(let u=0;u<e.series.length;u++){let o=e.series[u],x=u*8;n[x+0]=o.colorR,n[x+1]=o.colorG,n[x+2]=o.colorB,n[x+3]=1,l[x+4]=o.visibleStart,l[x+5]=o.visibleCount,n[x+6]=i?.[u]??e.pointSize,n[x+7]=0}t.queue.writeBuffer(e.seriesStorageBuffer,0,n)},O=(e,i,n,l)=>{let u=j,o=F,x=e.maxX-e.minX,f=e.maxY-e.minY,Y=e.bgColor??(w?[0.11,0.11,0.12]:[0.98,0.98,0.98]);u.set([e.width,e.height,e.minX+e.panX*x,e.minX+e.panX*x+x/e.zoomX,e.minY+e.panY*f,e.minY+e.panY*f+f/e.zoomY],0),o[6]=i.pointCount,u.set([w?1:0,...Y,l??e.pointSize,e.minY,e.maxY,e.minX,e.maxX],7),o[16]=i.visibleStart,o[17]=i.visibleCount,o[18]=0,o[19]=e.maxSamplesPerPixel,o[20]=e.series.length,t.queue.writeBuffer(e.uniformBuffer,0,R)},E=(e,i,n,l,u)=>{let o=t.createShaderModule({code:i}),x=t.createShaderModule({code:n});g[`${e}Compute`]=t.createComputePipeline({layout:t.createPipelineLayout({bindGroupLayouts:[p[`${e}Compute`]]}),compute:{module:o,entryPoint:"main"}}),g[`${e}Render`]=t.createRenderPipeline({layout:t.createPipelineLayout({bindGroupLayouts:[p[`${e}Render`]]}),vertex:{module:x,entryPoint:"vs"},fragment:{module:x,entryPoint:"fs",targets:[{format:"rgba8unorm",blend:u}]},primitive:{topology:l}})};async function J(){if(t)return!0;if(!navigator.gpu)return postMessage({type:"error",message:"WebGPU not supported"}),!1;let e=await navigator.gpu.requestAdapter();if(!e)return postMessage({type:"error",message:"No GPU adapter found"}),!1;return t=await e.requestDevice({requiredLimits:{maxBufferSize:e.limits.maxBufferSize,maxStorageBufferBindingSize:e.limits.maxStorageBufferBindingSize}}),_=navigator.gpu.getPreferredCanvasFormat(),t.lost.then((i)=>{postMessage({type:"error",message:`GPU device lost: ${i.reason} - ${i.message}`})}),K(),postMessage({type:"gpu-ready"}),!0}function K(){p.lineCompute=t.createBindGroupLayout({entries:[{binding:0,visibility:GPUShaderStage.COMPUTE,buffer:{type:"uniform"}},{binding:1,visibility:GPUShaderStage.COMPUTE,buffer:{type:"read-only-storage"}},{binding:2,visibility:GPUShaderStage.COMPUTE,buffer:{type:"read-only-storage"}},{binding:3,visibility:GPUShaderStage.COMPUTE,buffer:{type:"storage"}},{binding:4,visibility:GPUShaderStage.COMPUTE,buffer:{type:"read-only-storage"}}]}),p.lineRender=t.createBindGroupLayout({entries:[{binding:0,visibility:GPUShaderStage.VERTEX|GPUShaderStage.FRAGMENT,buffer:{type:"uniform"}},{binding:1,visibility:GPUShaderStage.VERTEX,buffer:{type:"read-only-storage"}},{binding:2,visibility:GPUShaderStage.FRAGMENT,buffer:{type:"read-only-storage"}}]}),p.scatterCompute=t.createBindGroupLayout({entries:[{binding:0,visibility:GPUShaderStage.COMPUTE,buffer:{type:"uniform"}},{binding:1,visibility:GPUShaderStage.COMPUTE,buffer:{type:"read-only-storage"}},{binding:2,visibility:GPUShaderStage.COMPUTE,buffer:{type:"read-only-storage"}},{binding:3,visibility:GPUShaderStage.COMPUTE,storageTexture:{access:"write-only",format:"rgba8unorm"}},{binding:4,visibility:GPUShaderStage.COMPUTE,buffer:{type:"read-only-storage"}},{binding:5,visibility:GPUShaderStage.COMPUTE,buffer:{type:"uniform"}}]}),p.fxaaRender=t.createBindGroupLayout({entries:[{binding:0,visibility:GPUShaderStage.VERTEX|GPUShaderStage.FRAGMENT,buffer:{type:"uniform"}},{binding:1,visibility:GPUShaderStage.FRAGMENT,texture:{sampleType:"float"}},{binding:2,visibility:GPUShaderStage.FRAGMENT,sampler:{}}]}),p.boxCompute=t.createBindGroupLayout({entries:[{binding:0,visibility:GPUShaderStage.COMPUTE,buffer:{type:"uniform"}},{binding:1,visibility:GPUShaderStage.COMPUTE,buffer:{type:"read-only-storage"}},{binding:2,visibility:GPUShaderStage.COMPUTE,buffer:{type:"read-only-storage"}},{binding:3,visibility:GPUShaderStage.COMPUTE,buffer:{type:"storage"}},{binding:4,visibility:GPUShaderStage.COMPUTE,buffer:{type:"read-only-storage"}},{binding:5,visibility:GPUShaderStage.COMPUTE,buffer:{type:"uniform"}}]}),p.boxRender=p.lineRender,E("line",C,y,"line-list",{color:{srcFactor:"src-alpha",dstFactor:"one-minus-src-alpha"},alpha:{srcFactor:"one",dstFactor:"one-minus-src-alpha"}}),E("box",D,H,"triangle-list",{color:{srcFactor:"src-alpha",dstFactor:"one-minus-src-alpha"},alpha:{srcFactor:"one",dstFactor:"one-minus-src-alpha"}});let e=t.createShaderModule({code:L});g.scatterCompute=t.createComputePipeline({layout:t.createPipelineLayout({bindGroupLayouts:[p.scatterCompute]}),compute:{module:e,entryPoint:"main"}});let i=t.createShaderModule({code:W});g.fxaaRender=t.createRenderPipeline({layout:t.createPipelineLayout({bindGroupLayouts:[p.fxaaRender]}),vertex:{module:i,entryPoint:"vs"},fragment:{module:i,entryPoint:"fs",targets:[{format:_}]},primitive:{topology:"triangle-strip"}}),P=t.createSampler({magFilter:"linear",minFilter:"linear"})}function Q(e,i,n,l=3,u=0,o=null){if(!t){postMessage({type:"error",message:`Cannot create chart ${e}: GPU device not available`});return}let x=i.getContext("webgpu");if(!x){postMessage({type:"error",message:`Cannot create chart ${e}: Failed to get WebGPU context`});return}try{x.configure({device:t,format:_,alphaMode:"premultiplied"})}catch(M){postMessage({type:"error",message:`Cannot create chart ${e}: configure failed - ${M}`});return}let f=i.width||800,Y=i.height||400,d=t.createBuffer({size:112,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),m={id:e,canvas:i,ctx:x,type:n,visible:!0,pointSize:l,maxSamplesPerPixel:u,series:[],uniformBuffer:d,seriesStorageBuffer:null,outputTexture:null,outputTextureView:null,fxaaBindGroup:null,width:f,height:Y,panX:0,panY:0,zoomX:1,zoomY:1,minX:0,maxX:1,minY:0,maxY:1,bgColor:o,dirty:!0};try{A(m)}catch(M){postMessage({type:"error",message:`Cannot create chart ${e}: resource creation failed - ${M}`});return}v.set(e,m),postMessage({type:"chart-registered",id:e})}function A(e){if(e.outputTexture)e.outputTexture.destroy();let i=Math.max(1,e.width),n=Math.max(1,e.height),l=e.type==="scatter"?GPUTextureUsage.STORAGE_BINDING|GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.RENDER_ATTACHMENT:GPUTextureUsage.TEXTURE_BINDING|GPUTextureUsage.RENDER_ATTACHMENT;e.outputTexture=t.createTexture({size:[i,n],format:"rgba8unorm",usage:l}),e.outputTextureView=e.outputTexture.createView(),e.fxaaBindGroup=t.createBindGroup({layout:p.fxaaRender,entries:[{binding:0,resource:{buffer:e.uniformBuffer}},{binding:1,resource:e.outputTextureView},{binding:2,resource:P}]})}function $(e,i,n){if(!e.seriesStorageBuffer)return;if(e.type==="scatter"){if(i.seriesIndexBuffer)i.seriesIndexBuffer.destroy();i.seriesIndexBuffer=t.createBuffer({size:16,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST});let l=new Uint32Array([n,0,0,0]);t.queue.writeBuffer(i.seriesIndexBuffer,0,l),i.computeBindGroup=t.createBindGroup({layout:p.scatterCompute,entries:[{binding:0,resource:{buffer:e.uniformBuffer}},{binding:1,resource:{buffer:i.dataX}},{binding:2,resource:{buffer:i.dataY}},{binding:3,resource:e.outputTextureView},{binding:4,resource:{buffer:e.seriesStorageBuffer}},{binding:5,resource:{buffer:i.seriesIndexBuffer}}]})}else{let l=e.type==="line"?p.lineCompute:p.boxCompute,u=e.type==="line"?p.lineRender:p.boxRender;if(i.lineBuffer)i.lineBuffer.destroy();if(i.lineBuffer=t.createBuffer({size:e.width*16,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.VERTEX}),e.type==="box"){if(i.seriesIndexBuffer)i.seriesIndexBuffer.destroy();i.seriesIndexBuffer=t.createBuffer({size:16,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST});let x=new Uint32Array([n,0,0,0]);t.queue.writeBuffer(i.seriesIndexBuffer,0,x)}let o=[{binding:0,resource:{buffer:e.uniformBuffer}},{binding:1,resource:{buffer:i.dataX}},{binding:2,resource:{buffer:i.dataY}},{binding:3,resource:{buffer:i.lineBuffer}},{binding:4,resource:{buffer:e.seriesStorageBuffer}}];if(e.type==="box"&&i.seriesIndexBuffer)o.push({binding:5,resource:{buffer:i.seriesIndexBuffer}});i.computeBindGroup=t.createBindGroup({layout:l,entries:o}),i.renderBindGroup=t.createBindGroup({layout:u,entries:[{binding:0,resource:{buffer:e.uniformBuffer}},{binding:1,resource:{buffer:i.lineBuffer}},{binding:2,resource:{buffer:e.seriesStorageBuffer}}]})}}function Z(e,i,n){if(i===e.width&&n===e.height)return;if(i<=0||n<=0)return;e.width=i,e.height=n,e.canvas.width=i,e.canvas.height=n;try{A(e);for(let l=0;l<e.series.length;l++)$(e,e.series[l],l)}catch(l){postMessage({type:"error",message:`resize failed for chart ${e.id}: ${l}`})}}function G(e,i,n){let l=v.get(e);if(!l||!t){if(!l)postMessage({type:"error",message:`update-series failed: chart ${e} not found`});return}try{l.minX=n.minX,l.maxX=n.maxX,l.minY=n.minY,l.maxY=n.maxY;for(let u of l.series){if(u.dataX.destroy(),u.dataY.destroy(),u.lineBuffer)u.lineBuffer.destroy();if(u.seriesIndexBuffer)u.seriesIndexBuffer.destroy()}if(l.series=[],l.seriesStorageBuffer)l.seriesStorageBuffer.destroy();if(i.length>0)l.seriesStorageBuffer=t.createBuffer({size:Math.max(32,i.length*32),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST});for(let u of i){let o=t.createBuffer({size:Math.max(16,u.dataX.byteLength),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST}),x=t.createBuffer({size:Math.max(16,u.dataY.byteLength),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST});t.queue.writeBuffer(o,0,u.dataX),t.queue.writeBuffer(x,0,u.dataY);let f={label:u.label,colorR:u.colorR,colorG:u.colorG,colorB:u.colorB,dataX:o,dataY:x,lineBuffer:null,seriesIndexBuffer:null,pointCount:u.dataX.length,visibleStart:0,visibleCount:u.dataX.length,computeBindGroup:null,renderBindGroup:null};l.series.push(f)}for(let u=0;u<l.series.length;u++)$(l,l.series[u],u);postMessage({type:"bounds-update",id:e,...n})}catch(u){postMessage({type:"error",message:`update-series failed for chart ${e}: ${u}`})}}function z(e){if(!e.ctx)return;if(e.width===0||e.height===0)return;if(e.series.length===0)return;let i;try{i=e.ctx.getCurrentTexture().createView()}catch{return}k(e,i)}function k(e,i){let n=t.createCommandEncoder(),l;if(e.type==="scatter"){let f=e.width*e.height*4;l=e.series.map((Y)=>{let d=Math.max(1,Y.visibleCount),m=Math.PI*e.pointSize*e.pointSize;if(d*m>f)return Math.max(1,Math.sqrt(f/(d*Math.PI)));return e.pointSize})}if(q(e,l),e.series.length>0)O(e,e.series[0],0);n.beginRenderPass({colorAttachments:[{view:e.outputTextureView,loadOp:"clear",storeOp:"store",clearValue:{r:0,g:0,b:0,a:0}}]}).end();for(let x=0;x<e.series.length;x++){let f=e.series[x];if(f.pointCount===0)continue;if(e.type==="scatter"){let d=Math.ceil(f.visibleCount/a),m=Math.min(d,65535),M=Math.ceil(d/65535),V=new Uint32Array([m*a]);t.queue.writeBuffer(e.uniformBuffer,72,V);let I=n.beginComputePass();I.setPipeline(g.scatterCompute),I.setBindGroup(0,f.computeBindGroup),I.dispatchWorkgroups(m,M),I.end()}else{O(e,f,x);let Y=e.type==="line"?g.lineCompute:g.boxCompute,d=n.beginComputePass();d.setPipeline(Y),d.setBindGroup(0,f.computeBindGroup),d.dispatchWorkgroups(Math.ceil(e.width/a)),d.end()}}if(e.type!=="scatter"){let x=e.type==="line"?g.lineRender:g.boxRender,f=e.type==="line"?Math.max(0,e.width*4-2):e.width*6,Y=n.beginRenderPass({colorAttachments:[{view:e.outputTextureView,loadOp:"load",storeOp:"store"}]});Y.setPipeline(x);for(let d=0;d<e.series.length;d++){let m=e.series[d];if(m.pointCount===0)continue;Y.setBindGroup(0,m.renderBindGroup),Y.draw(f,1,0,d)}Y.end()}let o=n.beginRenderPass({colorAttachments:[{view:i,loadOp:"clear",storeOp:"store",clearValue:{r:0,g:0,b:0,a:0}}]});if(o.setPipeline(g.fxaaRender),e.fxaaBindGroup)o.setBindGroup(0,e.fxaaBindGroup);o.draw(4),o.end(),t.queue.submit([n.finish()])}function S(){if(!N)N=!0,requestAnimationFrame(ee)}function b(e){if(e.dirty=!0,e.visible)S()}function T(){let e=!1;for(let i of v.values())if(i.dirty=!0,i.visible)e=!0;if(e)S()}function c(){let e=0;for(let i of v.values())if(i.visible&&i.width>0)e++;return e}function h(){if(U!==null)return;U=setInterval(()=>{postMessage({type:"stats",fps:s,renderMs:B,totalCharts:v.size,activeCharts:c()}),s=0},1000)}function ee(){N=!1;let e=performance.now();for(let i of v.values())if(i.visible&&i.dirty&&i.width>0)z(i),i.dirty=!1;B=performance.now()-e,s++}self.onmessage=async(e)=>{let{type:i,...n}=e.data;switch(i){case"init":if(w=n.isDark||!1,await J())h();break;case"theme":w=n.isDark,T();break;case"register-chart":Q(n.id,n.canvas,n.chartType||"scatter",n.pointSize??3,n.maxSamplesPerPixel??100,n.bgColor??null);{let l=v.get(n.id);if(l)b(l)}break;case"unregister-chart":{let l=v.get(n.id);if(l){try{l.ctx.unconfigure()}catch{}if(l.uniformBuffer.destroy(),l.seriesStorageBuffer)l.seriesStorageBuffer.destroy();if(l.outputTexture)l.outputTexture.destroy();for(let u of l.series){if(u.dataX.destroy(),u.dataY.destroy(),u.lineBuffer)u.lineBuffer.destroy();if(u.seriesIndexBuffer)u.seriesIndexBuffer.destroy()}v.delete(n.id)}postMessage({type:"chart-unregistered",id:n.id});break}case"set-point-size":{let l=v.get(n.id);if(l)l.pointSize=Math.max(1,Math.min(8,n.pointSize)),b(l);break}case"set-max-samples":{let l=v.get(n.id);if(l)l.maxSamplesPerPixel=Math.max(0,n.maxSamplesPerPixel|0),b(l);break}case"set-style":{let l=v.get(n.id);if(l){if(n.bgColor!==void 0)l.bgColor=n.bgColor;b(l)}break}case"update-series":{G(n.id,n.series,n.bounds);let l=v.get(n.id);if(l)b(l);break}case"set-visibility":{let l=v.get(n.id);if(l){if(l.visible=n.visible,n.visible&&l.dirty)S()}break}case"view-transform":{let l=v.get(n.id);if(l)l.panX=n.panX,l.panY=n.panY,l.zoomX=Math.max(0.1,Math.min(1e6,n.zoomX)),l.zoomY=Math.max(0.1,Math.min(1e6,n.zoomY)),b(l);break}case"resize":{let l=v.get(n.id);if(l&&n.width>0&&n.height>0)Z(l,n.width,n.height),b(l);break}case"batch-view-transform":{let l=Math.max(0.1,Math.min(1e6,n.zoomX)),u=Math.max(0.1,Math.min(1e6,n.zoomY));for(let o of n.transforms){let x=v.get(o.id);if(x)x.panX=n.panX,x.panY=n.panY,x.zoomX=l,x.zoomY=u,x.dirty=!0}S();break}case"sync-view":for(let l of v.values())l.panX=n.panX,l.panY=n.panY,l.zoomX=n.zoomX,l.zoomY=n.zoomY;T();break}};
|
package/dist/hover.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hover.d.ts","sourceRoot":"","sources":["../../src/plugins/hover.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EAGZ,MAAM,qBAAqB,CAAC;AAkC7B,eAAO,MAAM,WAAW,EAAE,WAgPzB,CAAC"}
|
package/dist/hover.js
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ChartManager
|
|
3
|
+
} from "../chart-library.js";
|
|
4
|
+
|
|
5
|
+
// src/plugins/hover.ts
|
|
6
|
+
var states = new WeakMap;
|
|
7
|
+
var drawBox = (ctx, x, y, w, h, r, fill, stroke) => {
|
|
8
|
+
ctx.beginPath();
|
|
9
|
+
ctx.roundRect(x, y, w, h, r);
|
|
10
|
+
ctx.fillStyle = fill;
|
|
11
|
+
ctx.fill();
|
|
12
|
+
ctx.strokeStyle = stroke;
|
|
13
|
+
ctx.lineWidth = 1.5;
|
|
14
|
+
ctx.stroke();
|
|
15
|
+
};
|
|
16
|
+
var hoverPlugin = {
|
|
17
|
+
name: "hover",
|
|
18
|
+
install(chart, el) {
|
|
19
|
+
const mgr = ChartManager.getInstance();
|
|
20
|
+
const ac = new AbortController;
|
|
21
|
+
const s = {
|
|
22
|
+
hoverResult: null,
|
|
23
|
+
pillX: 0,
|
|
24
|
+
pillY: 0,
|
|
25
|
+
pillTargetX: 0,
|
|
26
|
+
pillTargetY: 0,
|
|
27
|
+
pillAnimRef: null,
|
|
28
|
+
abort: ac
|
|
29
|
+
};
|
|
30
|
+
states.set(chart, s);
|
|
31
|
+
const update = (res) => {
|
|
32
|
+
if (chart.config.onHover)
|
|
33
|
+
chart.config.onHover(res);
|
|
34
|
+
if (!(chart.config.showTooltip ?? false))
|
|
35
|
+
return;
|
|
36
|
+
s.hoverResult = res;
|
|
37
|
+
mgr.drawChart(chart);
|
|
38
|
+
if (res && !s.pillAnimRef) {
|
|
39
|
+
let lastT = performance.now();
|
|
40
|
+
const tick = (now) => {
|
|
41
|
+
if (!s.hoverResult)
|
|
42
|
+
return s.pillAnimRef = null;
|
|
43
|
+
const f = 1 - Math.pow(0.5, (now - lastT) / (chart.config.pillDecayMs ?? 60));
|
|
44
|
+
lastT = now;
|
|
45
|
+
s.pillX += (s.pillTargetX - s.pillX) * f;
|
|
46
|
+
s.pillY += (s.pillTargetY - s.pillY) * f;
|
|
47
|
+
mgr.drawChart(chart);
|
|
48
|
+
s.pillAnimRef = requestAnimationFrame(tick);
|
|
49
|
+
};
|
|
50
|
+
s.pillAnimRef = requestAnimationFrame(tick);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
el.addEventListener("mousemove", (e) => {
|
|
54
|
+
if (chart.dragging)
|
|
55
|
+
return;
|
|
56
|
+
const r = el.getBoundingClientRect();
|
|
57
|
+
update(chart.findNearestPoint(e.clientX - r.left, e.clientY - r.top, r.width, r.height));
|
|
58
|
+
}, { signal: ac.signal });
|
|
59
|
+
["mouseleave", "pointerdown"].forEach((ev) => el.addEventListener(ev, () => update(null), { signal: ac.signal }));
|
|
60
|
+
},
|
|
61
|
+
afterDraw(ctx, chart) {
|
|
62
|
+
const s = states.get(chart);
|
|
63
|
+
if (!s?.hoverResult || !chart.config.showTooltip)
|
|
64
|
+
return;
|
|
65
|
+
const { hoverResult: hvr } = s;
|
|
66
|
+
const w = chart.width;
|
|
67
|
+
const h = chart.height;
|
|
68
|
+
const margin = ChartManager.MARGIN;
|
|
69
|
+
const dark = ChartManager.getInstance().isDark;
|
|
70
|
+
const {
|
|
71
|
+
formatX = String,
|
|
72
|
+
formatY = String,
|
|
73
|
+
fontFamily = ChartManager.DEFAULT_FONT
|
|
74
|
+
} = chart.config;
|
|
75
|
+
const rx = (chart.bounds.maxX - chart.bounds.minX) / chart.view.zoomX;
|
|
76
|
+
const ry = (chart.bounds.maxY - chart.bounds.minY) / chart.view.zoomY;
|
|
77
|
+
const px = (hvr.x - (chart.bounds.minX + chart.view.panX * (chart.bounds.maxX - chart.bounds.minX))) / rx * w;
|
|
78
|
+
const py = h * (1 - (hvr.y - (chart.bounds.minY + chart.view.panY * (chart.bounds.maxY - chart.bounds.minY))) / ry);
|
|
79
|
+
const mainSeries = chart.series[hvr.seriesIndex] || chart.series[0];
|
|
80
|
+
const rgb = `${Math.round(mainSeries.color.r * 255)},${Math.round(mainSeries.color.g * 255)},${Math.round(mainSeries.color.b * 255)}`;
|
|
81
|
+
const col = `rgb(${rgb})`;
|
|
82
|
+
const textCol = dark ? `oklch(from ${col} calc(l + 0.1) c h)` : col;
|
|
83
|
+
ctx.save();
|
|
84
|
+
ctx.setLineDash([4, 3]);
|
|
85
|
+
ctx.strokeStyle = `rgba(${rgb},0.4)`;
|
|
86
|
+
ctx.stroke(new Path2D(`M${px} 0V${h - margin.bottom}M${margin.left} ${py}H${w}`));
|
|
87
|
+
ctx.restore();
|
|
88
|
+
ctx.beginPath();
|
|
89
|
+
ctx.arc(px, py, 4.5, 0, Math.PI * 2);
|
|
90
|
+
ctx.fillStyle = col;
|
|
91
|
+
ctx.fill();
|
|
92
|
+
ctx.strokeStyle = dark ? "rgba(0,0,0,0.6)" : "rgba(255,255,255,0.9)";
|
|
93
|
+
ctx.stroke();
|
|
94
|
+
const seriesData = chart.series.map((ser) => {
|
|
95
|
+
let l = 0, r = ser.rawX.length - 1;
|
|
96
|
+
while (l <= r) {
|
|
97
|
+
const m = l + r >> 1;
|
|
98
|
+
if (Math.abs(ser.rawX[m] - hvr.x) < 0.0001)
|
|
99
|
+
return {
|
|
100
|
+
label: ser.label,
|
|
101
|
+
val: formatY(ser.rawY[m]),
|
|
102
|
+
rawVal: ser.rawY[m],
|
|
103
|
+
col: `rgb(${Math.round(ser.color.r * 255)},${Math.round(ser.color.g * 255)},${Math.round(ser.color.b * 255)})`
|
|
104
|
+
};
|
|
105
|
+
ser.rawX[m] < hvr.x ? l = m + 1 : r = m - 1;
|
|
106
|
+
}
|
|
107
|
+
return null;
|
|
108
|
+
}).filter(Boolean);
|
|
109
|
+
seriesData.sort((a, b) => Math.abs(b.rawVal) - Math.abs(a.rawVal));
|
|
110
|
+
const totalSeries = seriesData.length;
|
|
111
|
+
const displayData = seriesData.slice(0, 5);
|
|
112
|
+
const remainingCount = totalSeries - displayData.length;
|
|
113
|
+
s.pillTargetX = px;
|
|
114
|
+
s.pillTargetY = py;
|
|
115
|
+
if (!s.pillAnimRef) {
|
|
116
|
+
s.pillX = px;
|
|
117
|
+
s.pillY = py;
|
|
118
|
+
}
|
|
119
|
+
const drawPill = (x, y, txt, isX) => {
|
|
120
|
+
ctx.font = `600 10px ${fontFamily}`;
|
|
121
|
+
const tw = ctx.measureText(txt).width, pw = tw + 12, ph = 18;
|
|
122
|
+
const ox = isX ? x - pw / 2 : x - pw, oy = isX ? y : y - ph / 2;
|
|
123
|
+
ctx.save();
|
|
124
|
+
const angle = isX ? Math.atan((s.pillTargetX - s.pillX) / 80) * 0.2 : Math.atan((s.pillTargetY - s.pillY) / 80) * 0.2;
|
|
125
|
+
ctx.translate(x, y);
|
|
126
|
+
ctx.rotate(angle);
|
|
127
|
+
const bx2 = isX ? -pw / 2 : -pw, by2 = isX ? 0 : -ph / 2;
|
|
128
|
+
ctx.beginPath();
|
|
129
|
+
ctx.roundRect(bx2, by2, pw, ph, 4);
|
|
130
|
+
ctx.fillStyle = dark ? "rgba(0,0,0,0.75)" : "rgba(255,255,255,0.75)";
|
|
131
|
+
ctx.fill();
|
|
132
|
+
ctx.fillStyle = `rgba(${rgb},0.2)`;
|
|
133
|
+
ctx.fill();
|
|
134
|
+
ctx.strokeStyle = textCol;
|
|
135
|
+
ctx.lineWidth = 1.5;
|
|
136
|
+
ctx.stroke();
|
|
137
|
+
ctx.fillStyle = textCol;
|
|
138
|
+
ctx.textAlign = "center";
|
|
139
|
+
ctx.textBaseline = "middle";
|
|
140
|
+
ctx.fillText(txt, bx2 + pw / 2, by2 + ph / 2);
|
|
141
|
+
ctx.restore();
|
|
142
|
+
};
|
|
143
|
+
drawPill(Math.max(margin.left, Math.min(w - margin.right, s.pillX)), h - margin.bottom + 4, formatX(hvr.x), true);
|
|
144
|
+
drawPill(Math.max(margin.left, margin.left), Math.max(9, Math.min(h - margin.bottom - 9, s.pillY)), formatY(hvr.y), false);
|
|
145
|
+
const boxW = Math.max(...displayData.map((d) => ctx.measureText(d.label + d.val).width)) + 40;
|
|
146
|
+
const boxH = 30 + displayData.length * 18 + (remainingCount > 0 ? 18 : 0);
|
|
147
|
+
let bx = hvr.screenX + 14, by = hvr.screenY - boxH - 6;
|
|
148
|
+
if (bx + boxW > w)
|
|
149
|
+
bx = hvr.screenX - boxW - 14;
|
|
150
|
+
by = Math.max(4, Math.min(h - boxH - 4, hvr.screenY - boxH - 6));
|
|
151
|
+
drawBox(ctx, bx, by, boxW, boxH, 6, dark ? "rgba(28,28,30,0.95)" : "rgba(255,255,255,0.96)", "rgba(0,0,0,0.08)");
|
|
152
|
+
ctx.textAlign = "left";
|
|
153
|
+
ctx.textBaseline = "middle";
|
|
154
|
+
ctx.fillStyle = dark ? "#888" : "#999";
|
|
155
|
+
ctx.fillText(formatX(hvr.x), bx + 10, by + 15);
|
|
156
|
+
displayData.forEach((sd, i) => {
|
|
157
|
+
const ty = by + 35 + i * 18;
|
|
158
|
+
ctx.fillStyle = sd.col;
|
|
159
|
+
ctx.beginPath();
|
|
160
|
+
ctx.roundRect(bx + 10, ty - 4, 8, 8, 2);
|
|
161
|
+
ctx.fill();
|
|
162
|
+
ctx.fillStyle = dark ? "#eee" : "#1a1a1a";
|
|
163
|
+
ctx.fillText(`${sd.label}: ${sd.val}`, bx + 24, ty);
|
|
164
|
+
});
|
|
165
|
+
if (remainingCount > 0) {
|
|
166
|
+
const ty = by + 35 + displayData.length * 18;
|
|
167
|
+
ctx.fillStyle = dark ? "#666" : "#aaa";
|
|
168
|
+
ctx.fillText(`+${remainingCount} more`, bx + 10, ty);
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
uninstall(chart) {
|
|
172
|
+
const s = states.get(chart);
|
|
173
|
+
if (s?.pillAnimRef)
|
|
174
|
+
cancelAnimationFrame(s.pillAnimRef);
|
|
175
|
+
s?.abort.abort();
|
|
176
|
+
states.delete(chart);
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
export {
|
|
180
|
+
hoverPlugin
|
|
181
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as B}from"../chart-library.js";var W=new WeakMap,m=(j,q,G,I,A,O,E,J)=>{j.beginPath(),j.roundRect(q,G,I,A,O),j.fillStyle=E,j.fill(),j.strokeStyle=J,j.lineWidth=1.5,j.stroke()},l={name:"hover",install(j,q){let G=B.getInstance(),I=new AbortController,A={hoverResult:null,pillX:0,pillY:0,pillTargetX:0,pillTargetY:0,pillAnimRef:null,abort:I};W.set(j,A);let O=(E)=>{if(j.config.onHover)j.config.onHover(E);if(!(j.config.showTooltip??!1))return;if(A.hoverResult=E,G.drawChart(j),E&&!A.pillAnimRef){let J=performance.now(),V=(Z)=>{if(!A.hoverResult)return A.pillAnimRef=null;let F=1-Math.pow(0.5,(Z-J)/(j.config.pillDecayMs??60));J=Z,A.pillX+=(A.pillTargetX-A.pillX)*F,A.pillY+=(A.pillTargetY-A.pillY)*F,G.drawChart(j),A.pillAnimRef=requestAnimationFrame(V)};A.pillAnimRef=requestAnimationFrame(V)}};q.addEventListener("mousemove",(E)=>{if(j.dragging)return;let J=q.getBoundingClientRect();O(j.findNearestPoint(E.clientX-J.left,E.clientY-J.top,J.width,J.height))},{signal:I.signal}),["mouseleave","pointerdown"].forEach((E)=>q.addEventListener(E,()=>O(null),{signal:I.signal}))},afterDraw(j,q){let G=W.get(q);if(!G?.hoverResult||!q.config.showTooltip)return;let{hoverResult:I}=G,A=q.width,O=q.height,E=B.MARGIN,J=B.getInstance().isDark,{formatX:V=String,formatY:Z=String,fontFamily:F=B.DEFAULT_FONT}=q.config,w=(q.bounds.maxX-q.bounds.minX)/q.view.zoomX,g=(q.bounds.maxY-q.bounds.minY)/q.view.zoomY,N=(I.x-(q.bounds.minX+q.view.panX*(q.bounds.maxX-q.bounds.minX)))/w*A,P=O*(1-(I.y-(q.bounds.minY+q.view.panY*(q.bounds.maxY-q.bounds.minY)))/g),H=q.series[I.seriesIndex]||q.series[0],M=`${Math.round(H.color.r*255)},${Math.round(H.color.g*255)},${Math.round(H.color.b*255)}`,u=`rgb(${M})`,f=J?`oklch(from ${u} calc(l + 0.1) c h)`:u;j.save(),j.setLineDash([4,3]),j.strokeStyle=`rgba(${M},0.4)`,j.stroke(new Path2D(`M${N} 0V${O-E.bottom}M${E.left} ${P}H${A}`)),j.restore(),j.beginPath(),j.arc(N,P,4.5,0,Math.PI*2),j.fillStyle=u,j.fill(),j.strokeStyle=J?"rgba(0,0,0,0.6)":"rgba(255,255,255,0.9)",j.stroke();let S=q.series.map((z)=>{let L=0,Q=z.rawX.length-1;while(L<=Q){let K=L+Q>>1;if(Math.abs(z.rawX[K]-I.x)<0.0001)return{label:z.label,val:Z(z.rawY[K]),rawVal:z.rawY[K],col:`rgb(${Math.round(z.color.r*255)},${Math.round(z.color.g*255)},${Math.round(z.color.b*255)})`};z.rawX[K]<I.x?L=K+1:Q=K-1}return null}).filter(Boolean);S.sort((z,L)=>Math.abs(L.rawVal)-Math.abs(z.rawVal));let p=S.length,_=S.slice(0,5),D=p-_.length;if(G.pillTargetX=N,G.pillTargetY=P,!G.pillAnimRef)G.pillX=N,G.pillY=P;let k=(z,L,Q,K)=>{j.font=`600 10px ${F}`;let d=j.measureText(Q).width,U=d+12,Y=18,o=K?z-U/2:z-U,y=K?L:L-Y/2;j.save();let b=K?Math.atan((G.pillTargetX-G.pillX)/80)*0.2:Math.atan((G.pillTargetY-G.pillY)/80)*0.2;j.translate(z,L),j.rotate(b);let X=K?-U/2:-U,v=K?0:-Y/2;j.beginPath(),j.roundRect(X,v,U,Y,4),j.fillStyle=J?"rgba(0,0,0,0.75)":"rgba(255,255,255,0.75)",j.fill(),j.fillStyle=`rgba(${M},0.2)`,j.fill(),j.strokeStyle=f,j.lineWidth=1.5,j.stroke(),j.fillStyle=f,j.textAlign="center",j.textBaseline="middle",j.fillText(Q,X+U/2,v+Y/2),j.restore()};k(Math.max(E.left,Math.min(A-E.right,G.pillX)),O-E.bottom+4,V(I.x),!0),k(Math.max(E.left,E.left),Math.max(9,Math.min(O-E.bottom-9,G.pillY)),Z(I.y),!1);let C=Math.max(..._.map((z)=>j.measureText(z.label+z.val).width))+40,T=30+_.length*18+(D>0?18:0),R=I.screenX+14,$=I.screenY-T-6;if(R+C>A)R=I.screenX-C-14;if($=Math.max(4,Math.min(O-T-4,I.screenY-T-6)),m(j,R,$,C,T,6,J?"rgba(28,28,30,0.95)":"rgba(255,255,255,0.96)","rgba(0,0,0,0.08)"),j.textAlign="left",j.textBaseline="middle",j.fillStyle=J?"#888":"#999",j.fillText(V(I.x),R+10,$+15),_.forEach((z,L)=>{let Q=$+35+L*18;j.fillStyle=z.col,j.beginPath(),j.roundRect(R+10,Q-4,8,8,2),j.fill(),j.fillStyle=J?"#eee":"#1a1a1a",j.fillText(`${z.label}: ${z.val}`,R+24,Q)}),D>0){let z=$+35+_.length*18;j.fillStyle=J?"#666":"#aaa",j.fillText(`+${D} more`,R+10,z)}},uninstall(j){let q=W.get(j);if(q?.pillAnimRef)cancelAnimationFrame(q.pillAnimRef);q?.abort.abort(),W.delete(j)}};export{l as hoverPlugin};
|
package/dist/labels.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"labels.d.ts","sourceRoot":"","sources":["../../src/plugins/labels.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAiB,MAAM,qBAAqB,CAAC;AA8CtE,eAAO,MAAM,YAAY,EAAE,WAmF1B,CAAC"}
|
package/dist/labels.js
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ChartManager
|
|
3
|
+
} from "../chart-library.js";
|
|
4
|
+
|
|
5
|
+
// src/plugins/labels.ts
|
|
6
|
+
var niceTicks = (min, max, count) => {
|
|
7
|
+
const range = max - min;
|
|
8
|
+
if (range <= 0)
|
|
9
|
+
return [min];
|
|
10
|
+
const rough = range / count, mag = 10 ** Math.floor(Math.log10(rough)), res = rough / mag;
|
|
11
|
+
const step = mag * (res <= 1.5 ? 1 : res <= 3 ? 2 : res <= 7 ? 5 : 10);
|
|
12
|
+
const ticks = [];
|
|
13
|
+
for (let v = Math.ceil(min / step) * step;v <= max; v += step)
|
|
14
|
+
ticks.push(v);
|
|
15
|
+
return ticks;
|
|
16
|
+
};
|
|
17
|
+
var getViewState = (chart) => {
|
|
18
|
+
const mgr = ChartManager.getInstance(), w = chart.width, h = chart.height, m = ChartManager.MARGIN;
|
|
19
|
+
const { bounds: b, view: v } = chart, fullX = b.maxX - b.minX, fullY = b.maxY - b.minY;
|
|
20
|
+
const rx = fullX / v.zoomX, ry = fullY / v.zoomY;
|
|
21
|
+
const mx = b.minX + v.panX * fullX, my = b.minY + v.panY * fullY;
|
|
22
|
+
const bgc = chart.bgColor ?? (mgr.isDark ? [0.11, 0.11, 0.12] : [0.98, 0.98, 0.98]);
|
|
23
|
+
return {
|
|
24
|
+
w,
|
|
25
|
+
h,
|
|
26
|
+
m,
|
|
27
|
+
rx,
|
|
28
|
+
ry,
|
|
29
|
+
mx,
|
|
30
|
+
my,
|
|
31
|
+
bg: `${Math.round(bgc[0] * 255)},${Math.round(bgc[1] * 255)},${Math.round(bgc[2] * 255)}`,
|
|
32
|
+
font: chart.fontFamily ?? ChartManager.DEFAULT_FONT,
|
|
33
|
+
text: chart.textColor ?? (mgr.isDark ? "#c0c0c0" : "#333333"),
|
|
34
|
+
grid: chart.gridColor ?? (mgr.isDark ? "rgba(255,255,255,0.06)" : "rgba(0,0,0,0.06)")
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
var labelsPlugin = {
|
|
38
|
+
name: "labels",
|
|
39
|
+
beforeDraw(ctx, chart) {
|
|
40
|
+
const { w, h, m, rx, ry, mx, my, grid } = getViewState(chart);
|
|
41
|
+
ctx.strokeStyle = grid;
|
|
42
|
+
ctx.lineWidth = 1;
|
|
43
|
+
ctx.beginPath();
|
|
44
|
+
niceTicks(my, my + ry, 7).forEach((v) => {
|
|
45
|
+
const y = h * (1 - (v - my) / ry);
|
|
46
|
+
if (y > 5 && y < h - m.bottom - 5) {
|
|
47
|
+
ctx.moveTo(m.left, y);
|
|
48
|
+
ctx.lineTo(w, y);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
niceTicks(mx, mx + rx, 8).forEach((v) => {
|
|
52
|
+
const x = w * ((v - mx) / rx);
|
|
53
|
+
if (x > m.left && x < w) {
|
|
54
|
+
ctx.moveTo(x, 0);
|
|
55
|
+
ctx.lineTo(x, h - m.bottom);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
ctx.stroke();
|
|
59
|
+
},
|
|
60
|
+
afterDraw(ctx, chart) {
|
|
61
|
+
const { w, h, m, rx, ry, mx, my, bg, font, text } = getViewState(chart);
|
|
62
|
+
const {
|
|
63
|
+
formatX = String,
|
|
64
|
+
formatY = String,
|
|
65
|
+
labelSize = ChartManager.DEFAULT_LABEL_SIZE
|
|
66
|
+
} = chart.config;
|
|
67
|
+
const drawFade = (dir, x, y, fw, fh) => {
|
|
68
|
+
const g = dir === "left" ? ctx.createLinearGradient(x, 0, x + fw, 0) : ctx.createLinearGradient(0, y, 0, y + fh);
|
|
69
|
+
const alphas = dir === "left" ? [1, 0.7, 0.2, 0.05, 0] : [0, 0.05, 0.2, 0.7, 1];
|
|
70
|
+
[0, 0.35, 0.55, 0.7, 1].forEach((s, i) => g.addColorStop(s, `rgba(${bg},${alphas[i]})`));
|
|
71
|
+
ctx.fillStyle = g;
|
|
72
|
+
ctx.fillRect(x, y, fw, fh);
|
|
73
|
+
};
|
|
74
|
+
drawFade("left", 0, 0, m.left + 20, h);
|
|
75
|
+
drawFade("bottom", 0, h - m.bottom - 20, w, m.bottom + 20);
|
|
76
|
+
ctx.font = `${labelSize}px ${font}`;
|
|
77
|
+
ctx.fillStyle = text;
|
|
78
|
+
ctx.textAlign = "right";
|
|
79
|
+
ctx.textBaseline = "middle";
|
|
80
|
+
niceTicks(my, my + ry, 7).forEach((v) => {
|
|
81
|
+
const y = h * (1 - (v - my) / ry);
|
|
82
|
+
if (y > 5 && y < h - m.bottom - 5)
|
|
83
|
+
ctx.fillText(formatY(v), m.left - 5, y);
|
|
84
|
+
});
|
|
85
|
+
ctx.textAlign = "right";
|
|
86
|
+
ctx.textBaseline = "top";
|
|
87
|
+
niceTicks(mx, mx + rx, 8).forEach((v) => {
|
|
88
|
+
const x = w * ((v - mx) / rx);
|
|
89
|
+
if (x < m.left - 10 || x > w + 30)
|
|
90
|
+
return;
|
|
91
|
+
ctx.save();
|
|
92
|
+
ctx.translate(x, h - m.bottom + 5);
|
|
93
|
+
ctx.rotate(-Math.PI / 14);
|
|
94
|
+
ctx.fillText(formatX(v), 0, 0);
|
|
95
|
+
ctx.restore();
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
export {
|
|
100
|
+
labelsPlugin
|
|
101
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as d}from"../chart-library.js";var c=(t,m,s)=>{let r=m-t;if(r<=0)return[t];let o=r/s,e=10**Math.floor(Math.log10(o)),n=o/e,a=e*(n<=1.5?1:n<=3?2:n<=7?5:10),l=[];for(let b=Math.ceil(t/a)*a;b<=m;b+=a)l.push(b);return l},S=(t)=>{let m=d.getInstance(),s=t.width,r=t.height,o=d.MARGIN,{bounds:e,view:n}=t,a=e.maxX-e.minX,l=e.maxY-e.minY,b=a/n.zoomX,h=l/n.zoomY,i=e.minX+n.panX*a,p=e.minY+n.panY*l,u=t.bgColor??(m.isDark?[0.11,0.11,0.12]:[0.98,0.98,0.98]);return{w:s,h:r,m:o,rx:b,ry:h,mx:i,my:p,bg:`${Math.round(u[0]*255)},${Math.round(u[1]*255)},${Math.round(u[2]*255)}`,font:t.fontFamily??d.DEFAULT_FONT,text:t.textColor??(m.isDark?"#c0c0c0":"#333333"),grid:t.gridColor??(m.isDark?"rgba(255,255,255,0.06)":"rgba(0,0,0,0.06)")}},$={name:"labels",beforeDraw(t,m){let{w:s,h:r,m:o,rx:e,ry:n,mx:a,my:l,grid:b}=S(m);t.strokeStyle=b,t.lineWidth=1,t.beginPath(),c(l,l+n,7).forEach((h)=>{let i=r*(1-(h-l)/n);if(i>5&&i<r-o.bottom-5)t.moveTo(o.left,i),t.lineTo(s,i)}),c(a,a+e,8).forEach((h)=>{let i=s*((h-a)/e);if(i>o.left&&i<s)t.moveTo(i,0),t.lineTo(i,r-o.bottom)}),t.stroke()},afterDraw(t,m){let{w:s,h:r,m:o,rx:e,ry:n,mx:a,my:l,bg:b,font:h,text:i}=S(m),{formatX:p=String,formatY:u=String,labelSize:w=d.DEFAULT_LABEL_SIZE}=m.config,T=(g,f,y,C,E)=>{let M=g==="left"?t.createLinearGradient(f,0,f+C,0):t.createLinearGradient(0,y,0,y+E),k=g==="left"?[1,0.7,0.2,0.05,0]:[0,0.05,0.2,0.7,1];[0,0.35,0.55,0.7,1].forEach((D,X)=>M.addColorStop(D,`rgba(${b},${k[X]})`)),t.fillStyle=M,t.fillRect(f,y,C,E)};T("left",0,0,o.left+20,r),T("bottom",0,r-o.bottom-20,s,o.bottom+20),t.font=`${w}px ${h}`,t.fillStyle=i,t.textAlign="right",t.textBaseline="middle",c(l,l+n,7).forEach((g)=>{let f=r*(1-(g-l)/n);if(f>5&&f<r-o.bottom-5)t.fillText(u(g),o.left-5,f)}),t.textAlign="right",t.textBaseline="top",c(a,a+e,8).forEach((g)=>{let f=s*((g-a)/e);if(f<o.left-10||f>s+30)return;t.save(),t.translate(f,r-o.bottom+5),t.rotate(-Math.PI/14),t.fillText(p(g),0,0),t.restore()})}};export{$ as labelsPlugin};
|
package/dist/zoom.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zoom.d.ts","sourceRoot":"","sources":["../../src/plugins/zoom.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAiB,MAAM,qBAAqB,CAAC;AAGtE,MAAM,WAAW,iBAAiB;IAChC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,UAAU,CAAC,IAAI,GAAE,iBAAsB,GAAG,WAAW,CAkSpE"}
|
package/dist/zoom.js
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ChartManager
|
|
3
|
+
} from "../chart-library.js";
|
|
4
|
+
|
|
5
|
+
// src/plugins/zoom.ts
|
|
6
|
+
function zoomPlugin(opts = {}) {
|
|
7
|
+
const decay = opts.momentumDecay ?? 0.91;
|
|
8
|
+
const state = new WeakMap;
|
|
9
|
+
return {
|
|
10
|
+
name: "zoom",
|
|
11
|
+
install(chart, el) {
|
|
12
|
+
const mgr = ChartManager.getInstance();
|
|
13
|
+
const ac = new AbortController;
|
|
14
|
+
const s = {
|
|
15
|
+
lastX: 0,
|
|
16
|
+
lastY: 0,
|
|
17
|
+
velX: 0,
|
|
18
|
+
velY: 0,
|
|
19
|
+
abort: ac
|
|
20
|
+
};
|
|
21
|
+
state.set(chart, s);
|
|
22
|
+
let pointers = [];
|
|
23
|
+
let lastTime = 0;
|
|
24
|
+
let pinchDistance = 0, pinchZoomX = 1, pinchZoomY = 1, pinchX = 0.5, pinchY = 0.5;
|
|
25
|
+
const sendView = () => {
|
|
26
|
+
mgr.sendViewTransform(chart);
|
|
27
|
+
mgr.drawChart(chart);
|
|
28
|
+
if (mgr.syncViews)
|
|
29
|
+
mgr.syncAllViews(chart);
|
|
30
|
+
};
|
|
31
|
+
const startMomentum = () => {
|
|
32
|
+
if (chart.momentum)
|
|
33
|
+
cancelAnimationFrame(chart.momentum);
|
|
34
|
+
const tick = () => {
|
|
35
|
+
s.velX *= decay;
|
|
36
|
+
s.velY *= decay;
|
|
37
|
+
if (Math.abs(s.velX) > 0.00005 || Math.abs(s.velY) > 0.00005) {
|
|
38
|
+
if (chart.zoomMode !== "none" && (chart.zoomMode === "both" || chart.zoomMode === "x-only")) {
|
|
39
|
+
chart.view.panX -= s.velX / chart.view.zoomX;
|
|
40
|
+
}
|
|
41
|
+
if (chart.zoomMode !== "none" && (chart.zoomMode === "both" || chart.zoomMode === "y-only")) {
|
|
42
|
+
chart.view.panY += s.velY / chart.view.zoomY;
|
|
43
|
+
}
|
|
44
|
+
sendView();
|
|
45
|
+
chart.momentum = requestAnimationFrame(tick);
|
|
46
|
+
} else {
|
|
47
|
+
chart.momentum = null;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
chart.momentum = requestAnimationFrame(tick);
|
|
51
|
+
};
|
|
52
|
+
const cancelDrag = () => {
|
|
53
|
+
if (chart.dragging) {
|
|
54
|
+
chart.dragging = false;
|
|
55
|
+
pointers = [];
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
el.addEventListener("pointerdown", (e) => {
|
|
59
|
+
if (chart.momentum) {
|
|
60
|
+
cancelAnimationFrame(chart.momentum);
|
|
61
|
+
chart.momentum = null;
|
|
62
|
+
}
|
|
63
|
+
pointers.push(e);
|
|
64
|
+
el.setPointerCapture(e.pointerId);
|
|
65
|
+
if (pointers.length === 1) {
|
|
66
|
+
chart.dragging = true;
|
|
67
|
+
s.lastX = e.clientX;
|
|
68
|
+
s.lastY = e.clientY;
|
|
69
|
+
s.velX = s.velY = 0;
|
|
70
|
+
lastTime = performance.now();
|
|
71
|
+
} else if (pointers.length === 2) {
|
|
72
|
+
const rect = el.getBoundingClientRect();
|
|
73
|
+
const dx = pointers[1].clientX - pointers[0].clientX;
|
|
74
|
+
const dy = pointers[1].clientY - pointers[0].clientY;
|
|
75
|
+
pinchDistance = Math.hypot(dx, dy);
|
|
76
|
+
pinchZoomX = chart.view.zoomX;
|
|
77
|
+
pinchZoomY = chart.view.zoomY;
|
|
78
|
+
pinchX = ((pointers[0].clientX + pointers[1].clientX) / 2 - rect.left) / rect.width;
|
|
79
|
+
pinchY = 1 - ((pointers[0].clientY + pointers[1].clientY) / 2 - rect.top) / rect.height;
|
|
80
|
+
}
|
|
81
|
+
}, { signal: ac.signal });
|
|
82
|
+
el.addEventListener("pointermove", (e) => {
|
|
83
|
+
const idx = pointers.findIndex((p) => p.pointerId === e.pointerId);
|
|
84
|
+
if (idx >= 0)
|
|
85
|
+
pointers[idx] = e;
|
|
86
|
+
if (pointers.length === 1 && chart.dragging) {
|
|
87
|
+
const rect = el.getBoundingClientRect();
|
|
88
|
+
const dx = (e.clientX - s.lastX) / rect.width;
|
|
89
|
+
const dy = (e.clientY - s.lastY) / rect.height;
|
|
90
|
+
const now = performance.now();
|
|
91
|
+
if (now - lastTime < 100) {
|
|
92
|
+
s.velX = s.velX * 0.5 + dx * 0.5;
|
|
93
|
+
s.velY = s.velY * 0.5 + dy * 0.5;
|
|
94
|
+
}
|
|
95
|
+
lastTime = now;
|
|
96
|
+
if (chart.zoomMode !== "none" && (chart.zoomMode === "both" || chart.zoomMode === "x-only")) {
|
|
97
|
+
chart.view.panX -= dx / chart.view.zoomX;
|
|
98
|
+
}
|
|
99
|
+
if (chart.zoomMode !== "none" && (chart.zoomMode === "both" || chart.zoomMode === "y-only")) {
|
|
100
|
+
chart.view.panY += dy / chart.view.zoomY;
|
|
101
|
+
}
|
|
102
|
+
s.lastX = e.clientX;
|
|
103
|
+
s.lastY = e.clientY;
|
|
104
|
+
sendView();
|
|
105
|
+
} else if (pointers.length === 2) {
|
|
106
|
+
const dx = pointers[1].clientX - pointers[0].clientX;
|
|
107
|
+
const dy = pointers[1].clientY - pointers[0].clientY;
|
|
108
|
+
const d = Math.hypot(dx, dy);
|
|
109
|
+
const scale = d / pinchDistance;
|
|
110
|
+
if (chart.zoomMode !== "none" && (chart.zoomMode === "both" || chart.zoomMode === "x-only")) {
|
|
111
|
+
const newZoomX = Math.max(ChartManager.MIN_ZOOM, Math.min(ChartManager.MAX_ZOOM, pinchZoomX * scale));
|
|
112
|
+
const fx = chart.view.panX + pinchX / pinchZoomX;
|
|
113
|
+
chart.view.zoomX = newZoomX;
|
|
114
|
+
chart.view.panX = fx - pinchX / newZoomX;
|
|
115
|
+
}
|
|
116
|
+
if (chart.zoomMode !== "none" && (chart.zoomMode === "both" || chart.zoomMode === "y-only")) {
|
|
117
|
+
const newZoomY = Math.max(ChartManager.MIN_ZOOM, Math.min(ChartManager.MAX_ZOOM, pinchZoomY * scale));
|
|
118
|
+
const fy = chart.view.panY + pinchY / pinchZoomY;
|
|
119
|
+
chart.view.zoomY = newZoomY;
|
|
120
|
+
chart.view.panY = fy - pinchY / newZoomY;
|
|
121
|
+
}
|
|
122
|
+
sendView();
|
|
123
|
+
}
|
|
124
|
+
}, { signal: ac.signal });
|
|
125
|
+
const endPointer = (e) => {
|
|
126
|
+
pointers = pointers.filter((p) => p.pointerId !== e.pointerId);
|
|
127
|
+
el.releasePointerCapture(e.pointerId);
|
|
128
|
+
if (pointers.length === 0 && chart.dragging) {
|
|
129
|
+
chart.dragging = false;
|
|
130
|
+
if (Math.abs(s.velX) > 0.001 || Math.abs(s.velY) > 0.001) {
|
|
131
|
+
startMomentum();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
el.addEventListener("pointerup", endPointer, { signal: ac.signal });
|
|
136
|
+
el.addEventListener("pointercancel", endPointer, {
|
|
137
|
+
signal: ac.signal
|
|
138
|
+
});
|
|
139
|
+
el.addEventListener("pointerleave", cancelDrag, {
|
|
140
|
+
signal: ac.signal
|
|
141
|
+
});
|
|
142
|
+
let lastTap = 0;
|
|
143
|
+
el.addEventListener("pointerup", () => {
|
|
144
|
+
const now = Date.now();
|
|
145
|
+
if (now - lastTap < 300)
|
|
146
|
+
mgr.resetView(chart.id);
|
|
147
|
+
lastTap = now;
|
|
148
|
+
}, { signal: ac.signal });
|
|
149
|
+
el.addEventListener("wheel", (e) => {
|
|
150
|
+
e.preventDefault();
|
|
151
|
+
if (chart.momentum) {
|
|
152
|
+
cancelAnimationFrame(chart.momentum);
|
|
153
|
+
chart.momentum = null;
|
|
154
|
+
}
|
|
155
|
+
const rect = el.getBoundingClientRect();
|
|
156
|
+
const localX = e.clientX - rect.left;
|
|
157
|
+
const localY = e.clientY - rect.top;
|
|
158
|
+
const mx = localX / rect.width;
|
|
159
|
+
const my = 1 - localY / rect.height;
|
|
160
|
+
const scale = 1 - e.deltaY * 0.002;
|
|
161
|
+
const margin = ChartManager.MARGIN;
|
|
162
|
+
const overYAxis = localX < margin.left;
|
|
163
|
+
const overXAxis = localY > rect.height - margin.bottom;
|
|
164
|
+
let zoomX;
|
|
165
|
+
let zoomY;
|
|
166
|
+
if (overYAxis) {
|
|
167
|
+
zoomX = false;
|
|
168
|
+
zoomY = true;
|
|
169
|
+
} else if (overXAxis) {
|
|
170
|
+
zoomX = true;
|
|
171
|
+
zoomY = false;
|
|
172
|
+
} else {
|
|
173
|
+
zoomX = chart.zoomMode === "both" || chart.zoomMode === "x-only";
|
|
174
|
+
zoomY = chart.zoomMode === "both" || chart.zoomMode === "y-only";
|
|
175
|
+
}
|
|
176
|
+
if (zoomX) {
|
|
177
|
+
const fx = chart.view.panX + mx / chart.view.zoomX;
|
|
178
|
+
chart.view.zoomX = Math.max(ChartManager.MIN_ZOOM, Math.min(ChartManager.MAX_ZOOM, chart.view.zoomX * scale));
|
|
179
|
+
chart.view.panX = fx - mx / chart.view.zoomX;
|
|
180
|
+
}
|
|
181
|
+
if (zoomY) {
|
|
182
|
+
const fy = chart.view.panY + my / chart.view.zoomY;
|
|
183
|
+
chart.view.zoomY = Math.max(ChartManager.MIN_ZOOM, Math.min(ChartManager.MAX_ZOOM, chart.view.zoomY * scale));
|
|
184
|
+
chart.view.panY = fy - my / chart.view.zoomY;
|
|
185
|
+
}
|
|
186
|
+
if (zoomX || zoomY)
|
|
187
|
+
sendView();
|
|
188
|
+
}, { passive: false, signal: ac.signal });
|
|
189
|
+
},
|
|
190
|
+
uninstall(chart) {
|
|
191
|
+
const s = state.get(chart);
|
|
192
|
+
if (s) {
|
|
193
|
+
if (chart.momentum) {
|
|
194
|
+
cancelAnimationFrame(chart.momentum);
|
|
195
|
+
chart.momentum = null;
|
|
196
|
+
}
|
|
197
|
+
s.abort.abort();
|
|
198
|
+
state.delete(chart);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
export {
|
|
204
|
+
zoomPlugin
|
|
205
|
+
};
|
package/dist/zoom.min.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as s}from"../chart-library.js";function F(C={}){let O=C.momentumDecay??0.91,Y=new WeakMap;return{name:"zoom",install(e,t){let f=s.getInstance(),d=new AbortController,i={lastX:0,lastY:0,velX:0,velY:0,abort:d};Y.set(e,i);let n=[],z=0,Z=0,b=1,y=1,c=0.5,r=0.5,X=()=>{if(f.sendViewTransform(e),f.drawChart(e),f.syncViews)f.syncAllViews(e)},P=()=>{if(e.momentum)cancelAnimationFrame(e.momentum);let o=()=>{if(i.velX*=O,i.velY*=O,Math.abs(i.velX)>0.00005||Math.abs(i.velY)>0.00005){if(e.zoomMode!=="none"&&(e.zoomMode==="both"||e.zoomMode==="x-only"))e.view.panX-=i.velX/e.view.zoomX;if(e.zoomMode!=="none"&&(e.zoomMode==="both"||e.zoomMode==="y-only"))e.view.panY+=i.velY/e.view.zoomY;X(),e.momentum=requestAnimationFrame(o)}else e.momentum=null};e.momentum=requestAnimationFrame(o)},E=()=>{if(e.dragging)e.dragging=!1,n=[]};t.addEventListener("pointerdown",(o)=>{if(e.momentum)cancelAnimationFrame(e.momentum),e.momentum=null;if(n.push(o),t.setPointerCapture(o.pointerId),n.length===1)e.dragging=!0,i.lastX=o.clientX,i.lastY=o.clientY,i.velX=i.velY=0,z=performance.now();else if(n.length===2){let m=t.getBoundingClientRect(),l=n[1].clientX-n[0].clientX,a=n[1].clientY-n[0].clientY;Z=Math.hypot(l,a),b=e.view.zoomX,y=e.view.zoomY,c=((n[0].clientX+n[1].clientX)/2-m.left)/m.width,r=1-((n[0].clientY+n[1].clientY)/2-m.top)/m.height}},{signal:d.signal}),t.addEventListener("pointermove",(o)=>{let m=n.findIndex((l)=>l.pointerId===o.pointerId);if(m>=0)n[m]=o;if(n.length===1&&e.dragging){let l=t.getBoundingClientRect(),a=(o.clientX-i.lastX)/l.width,M=(o.clientY-i.lastY)/l.height,v=performance.now();if(v-z<100)i.velX=i.velX*0.5+a*0.5,i.velY=i.velY*0.5+M*0.5;if(z=v,e.zoomMode!=="none"&&(e.zoomMode==="both"||e.zoomMode==="x-only"))e.view.panX-=a/e.view.zoomX;if(e.zoomMode!=="none"&&(e.zoomMode==="both"||e.zoomMode==="y-only"))e.view.panY+=M/e.view.zoomY;i.lastX=o.clientX,i.lastY=o.clientY,X()}else if(n.length===2){let l=n[1].clientX-n[0].clientX,a=n[1].clientY-n[0].clientY,v=Math.hypot(l,a)/Z;if(e.zoomMode!=="none"&&(e.zoomMode==="both"||e.zoomMode==="x-only")){let g=Math.max(s.MIN_ZOOM,Math.min(s.MAX_ZOOM,b*v)),p=e.view.panX+c/b;e.view.zoomX=g,e.view.panX=p-c/g}if(e.zoomMode!=="none"&&(e.zoomMode==="both"||e.zoomMode==="y-only")){let g=Math.max(s.MIN_ZOOM,Math.min(s.MAX_ZOOM,y*v)),p=e.view.panY+r/y;e.view.zoomY=g,e.view.panY=p-r/g}X()}},{signal:d.signal});let A=(o)=>{if(n=n.filter((m)=>m.pointerId!==o.pointerId),t.releasePointerCapture(o.pointerId),n.length===0&&e.dragging){if(e.dragging=!1,Math.abs(i.velX)>0.001||Math.abs(i.velY)>0.001)P()}};t.addEventListener("pointerup",A,{signal:d.signal}),t.addEventListener("pointercancel",A,{signal:d.signal}),t.addEventListener("pointerleave",E,{signal:d.signal});let I=0;t.addEventListener("pointerup",()=>{let o=Date.now();if(o-I<300)f.resetView(e.id);I=o},{signal:d.signal}),t.addEventListener("wheel",(o)=>{if(o.preventDefault(),e.momentum)cancelAnimationFrame(e.momentum),e.momentum=null;let m=t.getBoundingClientRect(),l=o.clientX-m.left,a=o.clientY-m.top,M=l/m.width,v=1-a/m.height,g=1-o.deltaY*0.002,p=s.MARGIN,_=l<p.left,L=a>m.height-p.bottom,u,w;if(_)u=!1,w=!0;else if(L)u=!0,w=!1;else u=e.zoomMode==="both"||e.zoomMode==="x-only",w=e.zoomMode==="both"||e.zoomMode==="y-only";if(u){let x=e.view.panX+M/e.view.zoomX;e.view.zoomX=Math.max(s.MIN_ZOOM,Math.min(s.MAX_ZOOM,e.view.zoomX*g)),e.view.panX=x-M/e.view.zoomX}if(w){let x=e.view.panY+v/e.view.zoomY;e.view.zoomY=Math.max(s.MIN_ZOOM,Math.min(s.MAX_ZOOM,e.view.zoomY*g)),e.view.panY=x-v/e.view.zoomY}if(u||w)X()},{passive:!1,signal:d.signal})},uninstall(e){let t=Y.get(e);if(t){if(e.momentum)cancelAnimationFrame(e.momentum),e.momentum=null;t.abort.abort(),Y.delete(e)}}}}export{F as zoomPlugin};
|