chartai 0.0.11 → 0.1.0

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.
@@ -1 +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"}
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,WA0PzB,CAAC"}
@@ -51,13 +51,21 @@ var hoverPlugin = {
51
51
  s.pillAnimRef = requestAnimationFrame(tick);
52
52
  }
53
53
  };
54
- el.addEventListener("mousemove", (e) => {
54
+ const handleHover = (clientX, clientY) => {
55
55
  if (chart.dragging)
56
56
  return;
57
57
  const r = el.getBoundingClientRect();
58
- update(chart.findNearestPoint(e.clientX - r.left, e.clientY - r.top, r.width, r.height));
59
- }, { signal: ac.signal });
60
- ["mouseleave", "pointerdown"].forEach((ev) => el.addEventListener(ev, () => update(null), { signal: ac.signal }));
58
+ update(chart.findNearestPoint(clientX - r.left, clientY - r.top, r.width, r.height));
59
+ };
60
+ el.addEventListener("mousemove", (e) => handleHover(e.clientX, e.clientY), {
61
+ signal: ac.signal
62
+ });
63
+ el.addEventListener("touchmove", (e) => {
64
+ if (e.touches.length === 1) {
65
+ handleHover(e.touches[0].clientX, e.touches[0].clientY);
66
+ }
67
+ }, { signal: ac.signal, passive: true });
68
+ ["mouseleave", "pointerdown", "touchend", "touchcancel"].forEach((ev) => el.addEventListener(ev, () => update(null), { signal: ac.signal }));
61
69
  },
62
70
  afterDraw(ctx, chart) {
63
71
  const s = states.get(chart);
@@ -1 +1 @@
1
- import{a as B}from"../chart-library.js";import"../chunk-cj3zanvs.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};
1
+ import{a as B}from"../chart-library.js";import"../chunk-cj3zanvs.js";var Y=new WeakMap,m=(j,q,I,G,E,O,J,z)=>{j.beginPath(),j.roundRect(q,I,G,E,O),j.fillStyle=J,j.fill(),j.strokeStyle=z,j.lineWidth=1.5,j.stroke()},l={name:"hover",install(j,q){let I=B.getInstance(),G=new AbortController,E={hoverResult:null,pillX:0,pillY:0,pillTargetX:0,pillTargetY:0,pillAnimRef:null,abort:G};Y.set(j,E);let O=(z)=>{if(j.config.onHover)j.config.onHover(z);if(!(j.config.showTooltip??!1))return;if(E.hoverResult=z,I.drawChart(j),z&&!E.pillAnimRef){let U=performance.now(),Q=(F)=>{if(!E.hoverResult)return E.pillAnimRef=null;let N=1-Math.pow(0.5,(F-U)/(j.config.pillDecayMs??60));U=F,E.pillX+=(E.pillTargetX-E.pillX)*N,E.pillY+=(E.pillTargetY-E.pillY)*N,I.drawChart(j),E.pillAnimRef=requestAnimationFrame(Q)};E.pillAnimRef=requestAnimationFrame(Q)}},J=(z,U)=>{if(j.dragging)return;let Q=q.getBoundingClientRect();O(j.findNearestPoint(z-Q.left,U-Q.top,Q.width,Q.height))};q.addEventListener("mousemove",(z)=>J(z.clientX,z.clientY),{signal:G.signal}),q.addEventListener("touchmove",(z)=>{if(z.touches.length===1)J(z.touches[0].clientX,z.touches[0].clientY)},{signal:G.signal,passive:!0}),["mouseleave","pointerdown","touchend","touchcancel"].forEach((z)=>q.addEventListener(z,()=>O(null),{signal:G.signal}))},afterDraw(j,q){let I=Y.get(q);if(!I?.hoverResult||!q.config.showTooltip)return;let{hoverResult:G}=I,E=q.width,O=q.height,J=B.MARGIN,z=B.getInstance().isDark,{formatX:U=String,formatY:Q=String,fontFamily:F=B.DEFAULT_FONT}=q.config,N=(q.bounds.maxX-q.bounds.minX)/q.view.zoomX,g=(q.bounds.maxY-q.bounds.minY)/q.view.zoomY,P=(G.x-(q.bounds.minX+q.view.panX*(q.bounds.maxX-q.bounds.minX)))/N*E,T=O*(1-(G.y-(q.bounds.minY+q.view.panY*(q.bounds.maxY-q.bounds.minY)))/g),u=q.series[G.seriesIndex]||q.series[0],S=`${Math.round(u.color.r*255)},${Math.round(u.color.g*255)},${Math.round(u.color.b*255)}`,H=`rgb(${S})`,k=z?`oklch(from ${H} calc(l + 0.1) c h)`:H;j.save(),j.setLineDash([4,3]),j.strokeStyle=`rgba(${S},0.4)`,j.stroke(new Path2D(`M${P} 0V${O-J.bottom}M${J.left} ${T}H${E}`)),j.restore(),j.beginPath(),j.arc(P,T,4.5,0,Math.PI*2),j.fillStyle=H,j.fill(),j.strokeStyle=z?"rgba(0,0,0,0.6)":"rgba(255,255,255,0.9)",j.stroke();let D=q.series.map((A)=>{let L=0,R=A.rawX.length-1;while(L<=R){let K=L+R>>1;if(Math.abs(A.rawX[K]-G.x)<0.0001)return{label:A.label,val:Q(A.rawY[K]),rawVal:A.rawY[K],col:`rgb(${Math.round(A.color.r*255)},${Math.round(A.color.g*255)},${Math.round(A.color.b*255)})`};A.rawX[K]<G.x?L=K+1:R=K-1}return null}).filter(Boolean);D.sort((A,L)=>Math.abs(L.rawVal)-Math.abs(A.rawVal));let p=D.length,_=D.slice(0,5),C=p-_.length;if(I.pillTargetX=P,I.pillTargetY=T,!I.pillAnimRef)I.pillX=P,I.pillY=T;let X=(A,L,R,K)=>{j.font=`600 10px ${F}`;let b=j.measureText(R).width,Z=b+12,M=18,y=K?A-Z/2:A-Z,o=K?L:L-M/2;j.save();let d=K?Math.atan((I.pillTargetX-I.pillX)/80)*0.2:Math.atan((I.pillTargetY-I.pillY)/80)*0.2;j.translate(A,L),j.rotate(d);let v=K?-Z/2:-Z,w=K?0:-M/2;j.beginPath(),j.roundRect(v,w,Z,M,4),j.fillStyle=z?"rgba(0,0,0,0.75)":"rgba(255,255,255,0.75)",j.fill(),j.fillStyle=`rgba(${S},0.2)`,j.fill(),j.strokeStyle=k,j.lineWidth=1.5,j.stroke(),j.fillStyle=k,j.textAlign="center",j.textBaseline="middle",j.fillText(R,v+Z/2,w+M/2),j.restore()};X(Math.max(J.left,Math.min(E-J.right,I.pillX)),O-J.bottom+4,U(G.x),!0),X(Math.max(J.left,J.left),Math.max(9,Math.min(O-J.bottom-9,I.pillY)),Q(G.y),!1);let f=Math.max(..._.map((A)=>j.measureText(A.label+A.val).width))+40,W=30+_.length*18+(C>0?18:0),V=G.screenX+14,$=G.screenY-W-6;if(V+f>E)V=G.screenX-f-14;if($=Math.max(4,Math.min(O-W-4,G.screenY-W-6)),m(j,V,$,f,W,6,z?"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=z?"#888":"#999",j.fillText(U(G.x),V+10,$+15),_.forEach((A,L)=>{let R=$+35+L*18;j.fillStyle=A.col,j.beginPath(),j.roundRect(V+10,R-4,8,8,2),j.fill(),j.fillStyle=z?"#eee":"#1a1a1a",j.fillText(`${A.label}: ${A.val}`,V+24,R)}),C>0){let A=$+35+_.length*18;j.fillStyle=z?"#666":"#aaa",j.fillText(`+${C} more`,V+10,A)}},uninstall(j){let q=Y.get(j);if(q?.pillAnimRef)cancelAnimationFrame(q.pillAnimRef);q?.abort.abort(),Y.delete(j)}};export{l as hoverPlugin};
@@ -1 +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"}
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,CA2epE"}
@@ -5,11 +5,17 @@ import"../chunk-bgfkgcmg.js";
5
5
 
6
6
  // src/plugins/zoom.ts
7
7
  function zoomPlugin(opts = {}) {
8
- const decay = opts.momentumDecay ?? 0.91;
8
+ const decay = opts.momentumDecay ?? 0.9;
9
9
  const state = new WeakMap;
10
10
  return {
11
11
  name: "zoom",
12
12
  install(chart, el) {
13
+ const originalTouchAction = el.style.touchAction;
14
+ const originalUserSelect = el.style.userSelect;
15
+ const originalWebkitUserSelect = el.style.webkitUserSelect;
16
+ el.style.touchAction = "none";
17
+ el.style.userSelect = "none";
18
+ el.style.webkitUserSelect = "none";
13
19
  const mgr = ChartManager.getInstance();
14
20
  const ac = new AbortController;
15
21
  const s = {
@@ -17,12 +23,35 @@ function zoomPlugin(opts = {}) {
17
23
  lastY: 0,
18
24
  velX: 0,
19
25
  velY: 0,
20
- abort: ac
26
+ abort: ac,
27
+ originalTouchAction,
28
+ originalUserSelect,
29
+ originalWebkitUserSelect,
30
+ el
21
31
  };
22
32
  state.set(chart, s);
23
33
  let pointers = [];
34
+ let gestureState = "none";
35
+ let startX = 0;
36
+ let startY = 0;
37
+ let lastX = 0;
38
+ let lastY = 0;
24
39
  let lastTime = 0;
25
- let pinchDistance = 0, pinchZoomX = 1, pinchZoomY = 1, pinchX = 0.5, pinchY = 0.5;
40
+ const PAN_THRESHOLD = 10;
41
+ const TAP_THRESHOLD = 10;
42
+ const PRESS_TIME = 500;
43
+ let pressTimer = null;
44
+ let pinchStartDist = 0;
45
+ let pinchStartZoomX = 1;
46
+ let pinchStartZoomY = 1;
47
+ let pinchCenterX = 0.5;
48
+ let pinchCenterY = 0.5;
49
+ let velX = 0;
50
+ let velY = 0;
51
+ let lastTapTime = 0;
52
+ let edgeScaleMode = null;
53
+ let edgeScaleStart = 0;
54
+ let edgeScaleInitialZoom = 1;
26
55
  const sendView = () => {
27
56
  mgr.sendViewTransform(chart);
28
57
  mgr.drawChart(chart);
@@ -33,14 +62,14 @@ function zoomPlugin(opts = {}) {
33
62
  if (chart.momentum)
34
63
  cancelAnimationFrame(chart.momentum);
35
64
  const tick = () => {
36
- s.velX *= decay;
37
- s.velY *= decay;
38
- if (Math.abs(s.velX) > 0.00005 || Math.abs(s.velY) > 0.00005) {
65
+ velX *= decay;
66
+ velY *= decay;
67
+ if (Math.abs(velX) > 0.00005 || Math.abs(velY) > 0.00005) {
39
68
  if (chart.zoomMode !== "none" && (chart.zoomMode === "both" || chart.zoomMode === "x-only")) {
40
- chart.view.panX -= s.velX / chart.view.zoomX;
69
+ chart.view.panX -= velX / chart.view.zoomX;
41
70
  }
42
71
  if (chart.zoomMode !== "none" && (chart.zoomMode === "both" || chart.zoomMode === "y-only")) {
43
- chart.view.panY += s.velY / chart.view.zoomY;
72
+ chart.view.panY += velY / chart.view.zoomY;
44
73
  }
45
74
  sendView();
46
75
  chart.momentum = requestAnimationFrame(tick);
@@ -50,12 +79,6 @@ function zoomPlugin(opts = {}) {
50
79
  };
51
80
  chart.momentum = requestAnimationFrame(tick);
52
81
  };
53
- const cancelDrag = () => {
54
- if (chart.dragging) {
55
- chart.dragging = false;
56
- pointers = [];
57
- }
58
- };
59
82
  el.addEventListener("pointerdown", (e) => {
60
83
  if (chart.momentum) {
61
84
  cancelAnimationFrame(chart.momentum);
@@ -64,89 +87,192 @@ function zoomPlugin(opts = {}) {
64
87
  pointers.push(e);
65
88
  el.setPointerCapture(e.pointerId);
66
89
  if (pointers.length === 1) {
67
- chart.dragging = true;
68
- s.lastX = e.clientX;
69
- s.lastY = e.clientY;
70
- s.velX = s.velY = 0;
90
+ gestureState = "detecting";
91
+ startX = e.clientX;
92
+ startY = e.clientY;
93
+ lastX = e.clientX;
94
+ lastY = e.clientY;
95
+ velX = velY = 0;
71
96
  lastTime = performance.now();
97
+ edgeScaleMode = null;
98
+ if (e.pointerType === "touch") {
99
+ pressTimer = window.setTimeout(() => {
100
+ if (gestureState === "detecting") {
101
+ gestureState = "press";
102
+ chart.dragging = false;
103
+ }
104
+ }, PRESS_TIME);
105
+ } else {
106
+ const rect = el.getBoundingClientRect();
107
+ const localX = e.clientX - rect.left;
108
+ const localY = e.clientY - rect.top;
109
+ const margin = ChartManager.MARGIN;
110
+ const overYAxis = localX < margin.left;
111
+ const overXAxis = localY > rect.height - margin.bottom;
112
+ if (overYAxis && !overXAxis) {
113
+ edgeScaleMode = "y";
114
+ edgeScaleStart = e.clientY;
115
+ edgeScaleInitialZoom = chart.view.zoomY;
116
+ chart.dragging = true;
117
+ } else if (overXAxis && !overYAxis) {
118
+ edgeScaleMode = "x";
119
+ edgeScaleStart = e.clientX;
120
+ edgeScaleInitialZoom = chart.view.zoomX;
121
+ chart.dragging = true;
122
+ } else {
123
+ chart.dragging = true;
124
+ }
125
+ }
72
126
  } else if (pointers.length === 2) {
127
+ if (pressTimer) {
128
+ clearTimeout(pressTimer);
129
+ pressTimer = null;
130
+ }
131
+ gestureState = "pinch";
132
+ chart.dragging = false;
133
+ if (e.pointerType === "touch") {
134
+ e.preventDefault();
135
+ }
73
136
  const rect = el.getBoundingClientRect();
74
137
  const dx = pointers[1].clientX - pointers[0].clientX;
75
138
  const dy = pointers[1].clientY - pointers[0].clientY;
76
- pinchDistance = Math.hypot(dx, dy);
77
- pinchZoomX = chart.view.zoomX;
78
- pinchZoomY = chart.view.zoomY;
79
- pinchX = ((pointers[0].clientX + pointers[1].clientX) / 2 - rect.left) / rect.width;
80
- pinchY = 1 - ((pointers[0].clientY + pointers[1].clientY) / 2 - rect.top) / rect.height;
139
+ pinchStartDist = Math.hypot(dx, dy);
140
+ pinchStartZoomX = chart.view.zoomX;
141
+ pinchStartZoomY = chart.view.zoomY;
142
+ pinchCenterX = ((pointers[0].clientX + pointers[1].clientX) / 2 - rect.left) / rect.width;
143
+ pinchCenterY = 1 - ((pointers[0].clientY + pointers[1].clientY) / 2 - rect.top) / rect.height;
81
144
  }
82
- }, { signal: ac.signal });
145
+ }, { passive: false, signal: ac.signal });
83
146
  el.addEventListener("pointermove", (e) => {
84
147
  const idx = pointers.findIndex((p) => p.pointerId === e.pointerId);
85
- if (idx >= 0)
148
+ if (idx >= 0) {
86
149
  pointers[idx] = e;
87
- if (pointers.length === 1 && chart.dragging) {
88
- const rect = el.getBoundingClientRect();
89
- const dx = (e.clientX - s.lastX) / rect.width;
90
- const dy = (e.clientY - s.lastY) / rect.height;
91
- const now = performance.now();
92
- if (now - lastTime < 100) {
93
- s.velX = s.velX * 0.5 + dx * 0.5;
94
- s.velY = s.velY * 0.5 + dy * 0.5;
150
+ }
151
+ if (pointers.length === 1) {
152
+ const totalDist = Math.hypot(e.clientX - startX, e.clientY - startY);
153
+ if (gestureState === "detecting" && totalDist > PAN_THRESHOLD) {
154
+ gestureState = "pan";
155
+ chart.dragging = true;
156
+ if (pressTimer) {
157
+ clearTimeout(pressTimer);
158
+ pressTimer = null;
159
+ }
95
160
  }
96
- lastTime = now;
97
- if (chart.zoomMode !== "none" && (chart.zoomMode === "both" || chart.zoomMode === "x-only")) {
98
- chart.view.panX -= dx / chart.view.zoomX;
161
+ if (gestureState === "press") {
162
+ return;
99
163
  }
100
- if (chart.zoomMode !== "none" && (chart.zoomMode === "both" || chart.zoomMode === "y-only")) {
101
- chart.view.panY += dy / chart.view.zoomY;
164
+ if (edgeScaleMode && e.pointerType !== "touch") {
165
+ if (edgeScaleMode === "x") {
166
+ const pixelDelta = e.clientX - edgeScaleStart;
167
+ const scale = Math.exp(pixelDelta / 200);
168
+ const newZoom = Math.max(ChartManager.MIN_ZOOM, Math.min(ChartManager.MAX_ZOOM, edgeScaleInitialZoom * scale));
169
+ const fx = chart.view.panX + 0.5 / edgeScaleInitialZoom;
170
+ chart.view.zoomX = newZoom;
171
+ chart.view.panX = fx - 0.5 / newZoom;
172
+ sendView();
173
+ return;
174
+ } else if (edgeScaleMode === "y") {
175
+ const pixelDelta = edgeScaleStart - e.clientY;
176
+ const scale = Math.exp(pixelDelta / 200);
177
+ const newZoom = Math.max(ChartManager.MIN_ZOOM, Math.min(ChartManager.MAX_ZOOM, edgeScaleInitialZoom * scale));
178
+ const fy = chart.view.panY + 0.5 / edgeScaleInitialZoom;
179
+ chart.view.zoomY = newZoom;
180
+ chart.view.panY = fy - 0.5 / newZoom;
181
+ sendView();
182
+ return;
183
+ }
102
184
  }
103
- s.lastX = e.clientX;
104
- s.lastY = e.clientY;
105
- sendView();
106
- } else if (pointers.length === 2) {
185
+ if (gestureState === "pan" || chart.dragging && !edgeScaleMode) {
186
+ const rect = el.getBoundingClientRect();
187
+ const dx = (e.clientX - lastX) / rect.width;
188
+ const dy = (e.clientY - lastY) / rect.height;
189
+ const now = performance.now();
190
+ if (now - lastTime < 100) {
191
+ velX = velX * 0.3 + dx * 0.7;
192
+ velY = velY * 0.3 + dy * 0.7;
193
+ }
194
+ lastTime = now;
195
+ if (chart.zoomMode !== "none" && (chart.zoomMode === "both" || chart.zoomMode === "x-only")) {
196
+ chart.view.panX -= dx / chart.view.zoomX;
197
+ }
198
+ if (chart.zoomMode !== "none" && (chart.zoomMode === "both" || chart.zoomMode === "y-only")) {
199
+ chart.view.panY += dy / chart.view.zoomY;
200
+ }
201
+ lastX = e.clientX;
202
+ lastY = e.clientY;
203
+ sendView();
204
+ }
205
+ } else if (pointers.length === 2 && gestureState === "pinch") {
206
+ if (e.pointerType === "touch") {
207
+ e.preventDefault();
208
+ }
209
+ const rect = el.getBoundingClientRect();
107
210
  const dx = pointers[1].clientX - pointers[0].clientX;
108
211
  const dy = pointers[1].clientY - pointers[0].clientY;
109
- const d = Math.hypot(dx, dy);
110
- const scale = d / pinchDistance;
212
+ const dist = Math.hypot(dx, dy);
213
+ const currentPinchCenterX = ((pointers[0].clientX + pointers[1].clientX) / 2 - rect.left) / rect.width;
214
+ const currentPinchCenterY = 1 - ((pointers[0].clientY + pointers[1].clientY) / 2 - rect.top) / rect.height;
215
+ const pixelChange = dist - pinchStartDist;
216
+ const scale = Math.exp(pixelChange / 280);
111
217
  if (chart.zoomMode !== "none" && (chart.zoomMode === "both" || chart.zoomMode === "x-only")) {
112
- const newZoomX = Math.max(ChartManager.MIN_ZOOM, Math.min(ChartManager.MAX_ZOOM, pinchZoomX * scale));
113
- const fx = chart.view.panX + pinchX / pinchZoomX;
218
+ const newZoomX = Math.max(ChartManager.MIN_ZOOM, Math.min(ChartManager.MAX_ZOOM, pinchStartZoomX * scale));
219
+ const fx = chart.view.panX + currentPinchCenterX / chart.view.zoomX;
114
220
  chart.view.zoomX = newZoomX;
115
- chart.view.panX = fx - pinchX / newZoomX;
221
+ chart.view.panX = fx - currentPinchCenterX / newZoomX;
116
222
  }
117
223
  if (chart.zoomMode !== "none" && (chart.zoomMode === "both" || chart.zoomMode === "y-only")) {
118
- const newZoomY = Math.max(ChartManager.MIN_ZOOM, Math.min(ChartManager.MAX_ZOOM, pinchZoomY * scale));
119
- const fy = chart.view.panY + pinchY / pinchZoomY;
224
+ const newZoomY = Math.max(ChartManager.MIN_ZOOM, Math.min(ChartManager.MAX_ZOOM, pinchStartZoomY * scale));
225
+ const fy = chart.view.panY + currentPinchCenterY / chart.view.zoomY;
120
226
  chart.view.zoomY = newZoomY;
121
- chart.view.panY = fy - pinchY / newZoomY;
227
+ chart.view.panY = fy - currentPinchCenterY / newZoomY;
122
228
  }
123
229
  sendView();
124
230
  }
125
- }, { signal: ac.signal });
231
+ }, { passive: false, signal: ac.signal });
126
232
  const endPointer = (e) => {
127
233
  pointers = pointers.filter((p) => p.pointerId !== e.pointerId);
128
234
  el.releasePointerCapture(e.pointerId);
129
- if (pointers.length === 0 && chart.dragging) {
130
- chart.dragging = false;
131
- if (Math.abs(s.velX) > 0.001 || Math.abs(s.velY) > 0.001) {
235
+ if (pressTimer) {
236
+ clearTimeout(pressTimer);
237
+ pressTimer = null;
238
+ }
239
+ if (pointers.length === 0) {
240
+ const totalDist = Math.hypot(e.clientX - startX, e.clientY - startY);
241
+ const isTap = totalDist < TAP_THRESHOLD && gestureState !== "pan";
242
+ if (isTap && e.pointerType === "touch") {
243
+ const now = Date.now();
244
+ if (now - lastTapTime < 300) {
245
+ mgr.resetView(chart.id);
246
+ lastTapTime = 0;
247
+ } else {
248
+ lastTapTime = now;
249
+ }
250
+ }
251
+ if (isTap && e.pointerType !== "touch") {
252
+ const now = Date.now();
253
+ if (now - lastTapTime < 300) {
254
+ mgr.resetView(chart.id);
255
+ lastTapTime = 0;
256
+ } else {
257
+ lastTapTime = now;
258
+ }
259
+ }
260
+ if (gestureState === "pan" && (Math.abs(velX) > 0.001 || Math.abs(velY) > 0.001)) {
132
261
  startMomentum();
133
262
  }
263
+ gestureState = "none";
264
+ chart.dragging = false;
265
+ edgeScaleMode = null;
266
+ } else if (pointers.length === 1) {
267
+ gestureState = "detecting";
268
+ startX = pointers[0].clientX;
269
+ startY = pointers[0].clientY;
270
+ lastX = pointers[0].clientX;
271
+ lastY = pointers[0].clientY;
134
272
  }
135
273
  };
136
274
  el.addEventListener("pointerup", endPointer, { signal: ac.signal });
137
- el.addEventListener("pointercancel", endPointer, {
138
- signal: ac.signal
139
- });
140
- el.addEventListener("pointerleave", cancelDrag, {
141
- signal: ac.signal
142
- });
143
- let lastTap = 0;
144
- el.addEventListener("pointerup", () => {
145
- const now = Date.now();
146
- if (now - lastTap < 300)
147
- mgr.resetView(chart.id);
148
- lastTap = now;
149
- }, { signal: ac.signal });
275
+ el.addEventListener("pointercancel", endPointer, { signal: ac.signal });
150
276
  el.addEventListener("wheel", (e) => {
151
277
  e.preventDefault();
152
278
  if (chart.momentum) {
@@ -195,6 +321,9 @@ function zoomPlugin(opts = {}) {
195
321
  cancelAnimationFrame(chart.momentum);
196
322
  chart.momentum = null;
197
323
  }
324
+ s.el.style.touchAction = s.originalTouchAction;
325
+ s.el.style.userSelect = s.originalUserSelect;
326
+ s.el.style.webkitUserSelect = s.originalWebkitUserSelect;
198
327
  s.abort.abort();
199
328
  state.delete(chart);
200
329
  }
@@ -1 +1 @@
1
- import{a as s}from"../chart-library.js";import"../chunk-cj3zanvs.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};
1
+ import{a as $}from"../chart-library.js";import"../chunk-cj3zanvs.js";function r(S={}){let M=S.momentumDecay??0.9,v=new WeakMap;return{name:"zoom",install(j,F){let g=F.style.touchAction,m=F.style.userSelect,p=F.style.webkitUserSelect;F.style.touchAction="none",F.style.userSelect="none",F.style.webkitUserSelect="none";let E=$.getInstance(),H=new AbortController,n={lastX:0,lastY:0,velX:0,velY:0,abort:H,originalTouchAction:g,originalUserSelect:m,originalWebkitUserSelect:p,el:F};v.set(j,n);let B=[],O="none",A=0,P=0,z=0,y=0,w=0,i=10,l=10,o=500,b=null,T=0,X=1,Y=1,s=0.5,h=0.5,_=0,k=0,V=0,W=null,Z=0,f=1,D=()=>{if(E.sendViewTransform(j),E.drawChart(j),E.syncViews)E.syncAllViews(j)},c=()=>{if(j.momentum)cancelAnimationFrame(j.momentum);let q=()=>{if(_*=M,k*=M,Math.abs(_)>0.00005||Math.abs(k)>0.00005){if(j.zoomMode!=="none"&&(j.zoomMode==="both"||j.zoomMode==="x-only"))j.view.panX-=_/j.view.zoomX;if(j.zoomMode!=="none"&&(j.zoomMode==="both"||j.zoomMode==="y-only"))j.view.panY+=k/j.view.zoomY;D(),j.momentum=requestAnimationFrame(q)}else j.momentum=null};j.momentum=requestAnimationFrame(q)};F.addEventListener("pointerdown",(q)=>{if(j.momentum)cancelAnimationFrame(j.momentum),j.momentum=null;if(B.push(q),F.setPointerCapture(q.pointerId),B.length===1)if(O="detecting",A=q.clientX,P=q.clientY,z=q.clientX,y=q.clientY,_=k=0,w=performance.now(),W=null,q.pointerType==="touch")b=window.setTimeout(()=>{if(O==="detecting")O="press",j.dragging=!1},o);else{let J=F.getBoundingClientRect(),K=q.clientX-J.left,G=q.clientY-J.top,L=$.MARGIN,Q=K<L.left,N=G>J.height-L.bottom;if(Q&&!N)W="y",Z=q.clientY,f=j.view.zoomY,j.dragging=!0;else if(N&&!Q)W="x",Z=q.clientX,f=j.view.zoomX,j.dragging=!0;else j.dragging=!0}else if(B.length===2){if(b)clearTimeout(b),b=null;if(O="pinch",j.dragging=!1,q.pointerType==="touch")q.preventDefault();let J=F.getBoundingClientRect(),K=B[1].clientX-B[0].clientX,G=B[1].clientY-B[0].clientY;T=Math.hypot(K,G),X=j.view.zoomX,Y=j.view.zoomY,s=((B[0].clientX+B[1].clientX)/2-J.left)/J.width,h=1-((B[0].clientY+B[1].clientY)/2-J.top)/J.height}},{passive:!1,signal:H.signal}),F.addEventListener("pointermove",(q)=>{let J=B.findIndex((K)=>K.pointerId===q.pointerId);if(J>=0)B[J]=q;if(B.length===1){let K=Math.hypot(q.clientX-A,q.clientY-P);if(O==="detecting"&&K>i){if(O="pan",j.dragging=!0,b)clearTimeout(b),b=null}if(O==="press")return;if(W&&q.pointerType!=="touch"){if(W==="x"){let G=q.clientX-Z,L=Math.exp(G/200),Q=Math.max($.MIN_ZOOM,Math.min($.MAX_ZOOM,f*L)),N=j.view.panX+0.5/f;j.view.zoomX=Q,j.view.panX=N-0.5/Q,D();return}else if(W==="y"){let G=Z-q.clientY,L=Math.exp(G/200),Q=Math.max($.MIN_ZOOM,Math.min($.MAX_ZOOM,f*L)),N=j.view.panY+0.5/f;j.view.zoomY=Q,j.view.panY=N-0.5/Q,D();return}}if(O==="pan"||j.dragging&&!W){let G=F.getBoundingClientRect(),L=(q.clientX-z)/G.width,Q=(q.clientY-y)/G.height,N=performance.now();if(N-w<100)_=_*0.3+L*0.7,k=k*0.3+Q*0.7;if(w=N,j.zoomMode!=="none"&&(j.zoomMode==="both"||j.zoomMode==="x-only"))j.view.panX-=L/j.view.zoomX;if(j.zoomMode!=="none"&&(j.zoomMode==="both"||j.zoomMode==="y-only"))j.view.panY+=Q/j.view.zoomY;z=q.clientX,y=q.clientY,D()}}else if(B.length===2&&O==="pinch"){if(q.pointerType==="touch")q.preventDefault();let K=F.getBoundingClientRect(),G=B[1].clientX-B[0].clientX,L=B[1].clientY-B[0].clientY,Q=Math.hypot(G,L),N=((B[0].clientX+B[1].clientX)/2-K.left)/K.width,I=1-((B[0].clientY+B[1].clientY)/2-K.top)/K.height,x=Q-T,u=Math.exp(x/280);if(j.zoomMode!=="none"&&(j.zoomMode==="both"||j.zoomMode==="x-only")){let U=Math.max($.MIN_ZOOM,Math.min($.MAX_ZOOM,X*u)),R=j.view.panX+N/j.view.zoomX;j.view.zoomX=U,j.view.panX=R-N/U}if(j.zoomMode!=="none"&&(j.zoomMode==="both"||j.zoomMode==="y-only")){let U=Math.max($.MIN_ZOOM,Math.min($.MAX_ZOOM,Y*u)),R=j.view.panY+I/j.view.zoomY;j.view.zoomY=U,j.view.panY=R-I/U}D()}},{passive:!1,signal:H.signal});let d=(q)=>{if(B=B.filter((J)=>J.pointerId!==q.pointerId),F.releasePointerCapture(q.pointerId),b)clearTimeout(b),b=null;if(B.length===0){let K=Math.hypot(q.clientX-A,q.clientY-P)<l&&O!=="pan";if(K&&q.pointerType==="touch"){let G=Date.now();if(G-V<300)E.resetView(j.id),V=0;else V=G}if(K&&q.pointerType!=="touch"){let G=Date.now();if(G-V<300)E.resetView(j.id),V=0;else V=G}if(O==="pan"&&(Math.abs(_)>0.001||Math.abs(k)>0.001))c();O="none",j.dragging=!1,W=null}else if(B.length===1)O="detecting",A=B[0].clientX,P=B[0].clientY,z=B[0].clientX,y=B[0].clientY};F.addEventListener("pointerup",d,{signal:H.signal}),F.addEventListener("pointercancel",d,{signal:H.signal}),F.addEventListener("wheel",(q)=>{if(q.preventDefault(),j.momentum)cancelAnimationFrame(j.momentum),j.momentum=null;let J=F.getBoundingClientRect(),K=q.clientX-J.left,G=q.clientY-J.top,L=K/J.width,Q=1-G/J.height,N=1-q.deltaY*0.002,I=$.MARGIN,x=K<I.left,u=G>J.height-I.bottom,U,R;if(x)U=!1,R=!0;else if(u)U=!0,R=!1;else U=j.zoomMode==="both"||j.zoomMode==="x-only",R=j.zoomMode==="both"||j.zoomMode==="y-only";if(U){let C=j.view.panX+L/j.view.zoomX;j.view.zoomX=Math.max($.MIN_ZOOM,Math.min($.MAX_ZOOM,j.view.zoomX*N)),j.view.panX=C-L/j.view.zoomX}if(R){let C=j.view.panY+Q/j.view.zoomY;j.view.zoomY=Math.max($.MIN_ZOOM,Math.min($.MAX_ZOOM,j.view.zoomY*N)),j.view.panY=C-Q/j.view.zoomY}if(U||R)D()},{passive:!1,signal:H.signal})},uninstall(j){let F=v.get(j);if(F){if(j.momentum)cancelAnimationFrame(j.momentum),j.momentum=null;F.el.style.touchAction=F.originalTouchAction,F.el.style.userSelect=F.originalUserSelect,F.el.style.webkitUserSelect=F.originalWebkitUserSelect,F.abort.abort(),v.delete(j)}}}}export{r as zoomPlugin};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chartai",
3
- "version": "0.0.11",
3
+ "version": "0.1.0",
4
4
  "description": "tiny gpu powered charts",
5
5
  "type": "module",
6
6
  "main": "./dist/chart-library.js",
@@ -74,5 +74,6 @@
74
74
  },
75
75
  "engines": {
76
76
  "node": ">=18.0.0"
77
- }
77
+ },
78
+ "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
78
79
  }