scroll-arrows 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.
package/dist/index.cjs ADDED
@@ -0,0 +1,572 @@
1
+ 'use strict';
2
+
3
+ var rough = require('roughjs');
4
+
5
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
6
+
7
+ var rough__default = /*#__PURE__*/_interopDefault(rough);
8
+
9
+ // src/scroll-arrow.ts
10
+
11
+ // src/geometry.ts
12
+ var SIDES = [
13
+ "top",
14
+ "bottom",
15
+ "left",
16
+ "right"
17
+ ];
18
+ function docRect(el) {
19
+ const r2 = el.getBoundingClientRect();
20
+ return {
21
+ left: r2.left + window.scrollX,
22
+ top: r2.top + window.scrollY,
23
+ width: r2.width,
24
+ height: r2.height
25
+ };
26
+ }
27
+ function center(r2) {
28
+ return { x: r2.left + r2.width / 2, y: r2.top + r2.height / 2 };
29
+ }
30
+ function socketPoint(r2, side) {
31
+ const c = center(r2);
32
+ switch (side) {
33
+ case "top":
34
+ return { x: c.x, y: r2.top };
35
+ case "bottom":
36
+ return { x: c.x, y: r2.top + r2.height };
37
+ case "left":
38
+ return { x: r2.left, y: c.y };
39
+ case "right":
40
+ return { x: r2.left + r2.width, y: c.y };
41
+ default:
42
+ return c;
43
+ }
44
+ }
45
+ function socketNormal(side) {
46
+ switch (side) {
47
+ case "top":
48
+ return { x: 0, y: -1 };
49
+ case "bottom":
50
+ return { x: 0, y: 1 };
51
+ case "left":
52
+ return { x: -1, y: 0 };
53
+ case "right":
54
+ return { x: 1, y: 0 };
55
+ default:
56
+ return { x: 0, y: 0 };
57
+ }
58
+ }
59
+ function autoSide(self, other) {
60
+ const target = center(other);
61
+ let best = "right";
62
+ let bestDist = Infinity;
63
+ for (const side of SIDES) {
64
+ const p = socketPoint(self, side);
65
+ const d = (p.x - target.x) ** 2 + (p.y - target.y) ** 2;
66
+ if (d < bestDist) {
67
+ bestDist = d;
68
+ best = side;
69
+ }
70
+ }
71
+ return best;
72
+ }
73
+ function resolveEndpoints(startRect, endRect, startSocket, endSocket) {
74
+ const s = startSocket === "auto" ? autoSide(startRect, endRect) : startSocket;
75
+ const e = endSocket === "auto" ? autoSide(endRect, startRect) : endSocket;
76
+ return {
77
+ start: socketPoint(startRect, s),
78
+ end: socketPoint(endRect, e),
79
+ startNormal: socketNormal(s),
80
+ endNormal: socketNormal(e)
81
+ };
82
+ }
83
+ function buildPath(ep, curvature, belly = { x: 0, y: 0 }) {
84
+ const { start, end, startNormal, endNormal } = ep;
85
+ const dx = end.x - start.x;
86
+ const dy = end.y - start.y;
87
+ const dist = Math.hypot(dx, dy) || 1;
88
+ const reach = dist * (0.3 + curvature * 0.4);
89
+ const sn = startNormal.x || startNormal.y ? startNormal : unit(dx, dy);
90
+ const en = endNormal.x || endNormal.y ? endNormal : unit(-dx, -dy);
91
+ const c1 = { x: start.x + sn.x * reach + belly.x, y: start.y + sn.y * reach + belly.y };
92
+ const c2 = { x: end.x + en.x * reach + belly.x, y: end.y + en.y * reach + belly.y };
93
+ return `M ${r(start.x)} ${r(start.y)} C ${r(c1.x)} ${r(c1.y)} ${r(c2.x)} ${r(c2.y)} ${r(end.x)} ${r(end.y)}`;
94
+ }
95
+ function routeOffset(start, end, obstacles, padding = 14) {
96
+ const dx = end.x - start.x;
97
+ const dy = end.y - start.y;
98
+ const len = Math.hypot(dx, dy) || 1;
99
+ const t = { x: dx / len, y: dy / len };
100
+ const n = { x: dy / len, y: -dx / len };
101
+ let best = { x: 0, y: 0 };
102
+ let bestMag = 0;
103
+ for (const b of obstacles) {
104
+ const cx = b.left + b.width / 2;
105
+ const cy = b.top + b.height / 2;
106
+ const relx = cx - start.x;
107
+ const rely = cy - start.y;
108
+ const u = (relx * t.x + rely * t.y) / len;
109
+ if (u < 0 || u > 1) continue;
110
+ const signed = relx * n.x + rely * n.y;
111
+ const radius = Math.abs(b.width / 2 * n.x) + Math.abs(b.height / 2 * n.y);
112
+ const clearance = radius + padding;
113
+ if (Math.abs(signed) >= clearance) continue;
114
+ const sign = signed > 0 ? -1 : 1;
115
+ const offset = signed + sign * clearance;
116
+ if (Math.abs(offset) > bestMag) {
117
+ bestMag = Math.abs(offset);
118
+ best = { x: n.x * offset, y: n.y * offset };
119
+ }
120
+ }
121
+ return best;
122
+ }
123
+ function arrowHeadPath(tip, dir, size) {
124
+ const a = Math.atan2(dir.y, dir.x);
125
+ const spread = Math.PI / 7;
126
+ const p1 = {
127
+ x: tip.x - size * Math.cos(a - spread),
128
+ y: tip.y - size * Math.sin(a - spread)
129
+ };
130
+ const p2 = {
131
+ x: tip.x - size * Math.cos(a + spread),
132
+ y: tip.y - size * Math.sin(a + spread)
133
+ };
134
+ return `M ${r(p1.x)} ${r(p1.y)} L ${r(tip.x)} ${r(tip.y)} L ${r(p2.x)} ${r(p2.y)}`;
135
+ }
136
+ function endTangent(ep) {
137
+ const { end, endNormal } = ep;
138
+ if (endNormal.x || endNormal.y)
139
+ return { x: -endNormal.x + 0, y: -endNormal.y + 0 };
140
+ return unit(end.x - ep.start.x, end.y - ep.start.y);
141
+ }
142
+ function startTangent(ep) {
143
+ const { startNormal } = ep;
144
+ if (startNormal.x || startNormal.y)
145
+ return { x: -startNormal.x + 0, y: -startNormal.y + 0 };
146
+ return unit(ep.start.x - ep.end.x, ep.start.y - ep.end.y);
147
+ }
148
+ function unitNormal(a, b) {
149
+ const dx = b.x - a.x;
150
+ const dy = b.y - a.y;
151
+ const m = Math.hypot(dx, dy);
152
+ if (m === 0) return { x: 0, y: 0 };
153
+ return { x: dy / m + 0, y: -dx / m + 0 };
154
+ }
155
+ function unit(x, y) {
156
+ const m = Math.hypot(x, y) || 1;
157
+ return { x: x / m, y: y / m };
158
+ }
159
+ function r(n) {
160
+ return Math.round(n * 100) / 100;
161
+ }
162
+
163
+ // src/progress.ts
164
+ function easeInOutCubic(t) {
165
+ return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
166
+ }
167
+ function clamp01(t) {
168
+ return t < 0 ? 0 : t > 1 ? 1 : t;
169
+ }
170
+ function scrollProgress(targetRect, range) {
171
+ const vh = window.innerHeight || 1;
172
+ const topFrac = (targetRect.top - window.scrollY) / vh;
173
+ const [enter, leave] = range;
174
+ return clamp01((enter - topFrac) / (enter - leave || 1));
175
+ }
176
+ function midpointRect(a, b) {
177
+ const ra = docRect(a);
178
+ const rb = docRect(b);
179
+ const ca = { x: ra.left + ra.width / 2, y: ra.top + ra.height / 2 };
180
+ const cb = { x: rb.left + rb.width / 2, y: rb.top + rb.height / 2 };
181
+ return {
182
+ left: (ca.x + cb.x) / 2,
183
+ top: (ca.y + cb.y) / 2,
184
+ width: 0,
185
+ height: 0
186
+ };
187
+ }
188
+
189
+ // src/overlay.ts
190
+ var SVG_NS = "http://www.w3.org/2000/svg";
191
+ var overlays = /* @__PURE__ */ new WeakMap();
192
+ function getOverlay(container) {
193
+ var _a;
194
+ let svg = overlays.get(container);
195
+ if (svg && svg.isConnected) return svg;
196
+ svg = document.createElementNS(SVG_NS, "svg");
197
+ svg.setAttribute("data-scroll-arrows", "");
198
+ Object.assign(svg.style, {
199
+ position: "absolute",
200
+ left: "0",
201
+ top: "0",
202
+ width: "100%",
203
+ height: "100%",
204
+ overflow: "visible",
205
+ pointerEvents: "none",
206
+ zIndex: "9999"
207
+ });
208
+ if (container === document.body) {
209
+ (_a = document.body.style).position || (_a.position = "relative");
210
+ } else {
211
+ const pos = getComputedStyle(container).position;
212
+ if (pos === "static") container.style.position = "relative";
213
+ }
214
+ container.appendChild(svg);
215
+ overlays.set(container, svg);
216
+ return svg;
217
+ }
218
+ function overlayOrigin(svg) {
219
+ const r2 = svg.getBoundingClientRect();
220
+ return { x: r2.left + window.scrollX, y: r2.top + window.scrollY };
221
+ }
222
+ function createGroup() {
223
+ return document.createElementNS(SVG_NS, "g");
224
+ }
225
+ function createSvgEl(tag) {
226
+ return document.createElementNS(SVG_NS, tag);
227
+ }
228
+
229
+ // src/roughness.ts
230
+ function mapRoughness(roughness, curvatureOverride, stroke, strokeWidth, seed, anchorEnds = true) {
231
+ const r2 = clamp012(roughness);
232
+ return {
233
+ curvature: curvatureOverride ?? r2 * 0.6,
234
+ rough: {
235
+ roughness: r2 * 3.5,
236
+ bowing: r2 * 3,
237
+ maxRandomnessOffset: r2 * 4,
238
+ // Clean end of the spectrum: a single, near-exact stroke.
239
+ disableMultiStroke: r2 < 0.15,
240
+ // Pin endpoints to the sockets so the arrow always lands on its anchors,
241
+ // even when the middle of the stroke gets scratchy.
242
+ preserveVertices: anchorEnds,
243
+ stroke,
244
+ strokeWidth,
245
+ seed: seed | 0,
246
+ fill: "none"
247
+ }
248
+ };
249
+ }
250
+ function deriveSeed(a, b) {
251
+ let h = 2166136261;
252
+ const s = a + "->" + b;
253
+ for (let i = 0; i < s.length; i++) {
254
+ h ^= s.charCodeAt(i);
255
+ h = Math.imul(h, 16777619);
256
+ }
257
+ return (h >>> 0) % 1e5;
258
+ }
259
+ function clamp012(t) {
260
+ return t < 0 ? 0 : t > 1 ? 1 : t;
261
+ }
262
+
263
+ // src/draw.ts
264
+ function lengths(segs) {
265
+ let lineLen = 0;
266
+ let headLen = 0;
267
+ for (const s of segs) {
268
+ if (s.kind === "line") lineLen = Math.max(lineLen, s.len);
269
+ else headLen += s.len;
270
+ }
271
+ return { lineLen, headLen };
272
+ }
273
+ function dashOffsets(segs, eased) {
274
+ const { lineLen, headLen } = lengths(segs);
275
+ const total = lineLen + headLen || 1;
276
+ const drawn = clamp01(eased) * total;
277
+ const lp = lineLen > 0 ? clamp01(drawn / lineLen) : 1;
278
+ let headDrawn = Math.max(0, drawn - lineLen);
279
+ return segs.map((seg) => {
280
+ if (seg.kind === "line") return seg.len * (1 - lp);
281
+ const show = Math.max(0, Math.min(seg.len, headDrawn));
282
+ headDrawn -= seg.len;
283
+ return seg.len - show;
284
+ });
285
+ }
286
+ function lineProgress(segs, eased) {
287
+ const { lineLen, headLen } = lengths(segs);
288
+ const total = lineLen + headLen || 1;
289
+ const drawn = clamp01(eased) * total;
290
+ return lineLen > 0 ? clamp01(drawn / lineLen) : 1;
291
+ }
292
+ function labelOpacity(lineProg, labelAt, fade = 0.08) {
293
+ return clamp01((lineProg - clamp01(labelAt)) / (fade || 1));
294
+ }
295
+
296
+ // src/scroll-arrow.ts
297
+ var ScrollArrow = class {
298
+ constructor(options) {
299
+ this.group = createGroup();
300
+ /**
301
+ * Drawable segments. Line strokes (rough.js emits 1-2 overlapping ones) share
302
+ * a leading edge so they grow as a single pen tip; heads draw after the line.
303
+ */
304
+ this.segments = [];
305
+ /** Representative line stroke + label nodes, when a label is set. */
306
+ this.lineEl = null;
307
+ this.labelEl = null;
308
+ this.labelBgEl = null;
309
+ this.rafId = 0;
310
+ this.destroyed = false;
311
+ this.onScroll = () => {
312
+ if (this.rafId) return;
313
+ this.rafId = requestAnimationFrame(() => {
314
+ this.rafId = 0;
315
+ this.update();
316
+ });
317
+ };
318
+ this.opts = {
319
+ roughness: 0.5,
320
+ strokeWidth: 2,
321
+ head: "end",
322
+ headSize: 14,
323
+ speed: 1,
324
+ easing: easeInOutCubic,
325
+ progress: 0,
326
+ ...options
327
+ };
328
+ this.container = options.container ?? document.body;
329
+ this.svg = getOverlay(this.container);
330
+ this.rc = rough__default.default.svg(this.svg);
331
+ this.svg.appendChild(this.group);
332
+ this.progress = clamp01(this.opts.progress);
333
+ this.refs = this.resolveRefs();
334
+ this.stroke = options.stroke ?? getComputedStyle(this.container).color ?? "#222";
335
+ this.seed = options.seed ?? deriveSeed(refKey(options.start), refKey(options.end));
336
+ this.render();
337
+ this.bind();
338
+ this.update();
339
+ }
340
+ /** Manually set draw progress (0..1). Only meaningful when scroll is false. */
341
+ setProgress(p) {
342
+ this.progress = clamp01(p);
343
+ this.applyProgress();
344
+ }
345
+ /** Recompute geometry (call after layout changes you control). */
346
+ refresh() {
347
+ this.render();
348
+ this.update();
349
+ }
350
+ destroy() {
351
+ this.destroyed = true;
352
+ this.ro?.disconnect();
353
+ window.removeEventListener("scroll", this.onScroll, true);
354
+ window.removeEventListener("resize", this.onScroll);
355
+ cancelAnimationFrame(this.rafId);
356
+ this.group.remove();
357
+ }
358
+ // --- internals ---------------------------------------------------------
359
+ resolveRefs() {
360
+ const start = resolve(this.opts.start);
361
+ const end = resolve(this.opts.end);
362
+ if (!start || !end) {
363
+ throw new Error("[scroll-arrows] start/end element not found");
364
+ }
365
+ const scroll = this.opts.scroll;
366
+ let target = null;
367
+ if (scroll && scroll !== void 0 && scroll.target) {
368
+ target = resolve(scroll.target);
369
+ }
370
+ return { start, end, target };
371
+ }
372
+ resolveAvoid() {
373
+ const a = this.opts.avoid;
374
+ if (!a) return [];
375
+ const list = Array.isArray(a) ? a : [a];
376
+ return list.map(resolve).filter((el) => el !== null);
377
+ }
378
+ computeEndpoints() {
379
+ const sr = docRect(this.refs.start);
380
+ const er = docRect(this.refs.end);
381
+ const ss = this.opts.startSocket ?? "auto";
382
+ const es = this.opts.endSocket ?? "auto";
383
+ return resolveEndpoints(sr, er, ss, es);
384
+ }
385
+ render() {
386
+ while (this.group.firstChild) this.group.removeChild(this.group.firstChild);
387
+ this.segments = [];
388
+ const ep = this.computeEndpoints();
389
+ const origin = overlayOrigin(this.svg);
390
+ const shift = (e) => ({
391
+ start: { x: e.start.x - origin.x, y: e.start.y - origin.y },
392
+ end: { x: e.end.x - origin.x, y: e.end.y - origin.y },
393
+ startNormal: e.startNormal,
394
+ endNormal: e.endNormal
395
+ });
396
+ const local = shift(ep);
397
+ const { rough: roughOpts, curvature } = mapRoughness(
398
+ this.opts.roughness,
399
+ this.opts.curvature,
400
+ this.stroke,
401
+ this.opts.strokeWidth,
402
+ this.seed,
403
+ this.opts.anchorEnds ?? true
404
+ );
405
+ const obstacles = this.resolveAvoid().map((el) => {
406
+ const dr = docRect(el);
407
+ return {
408
+ left: dr.left - origin.x,
409
+ top: dr.top - origin.y,
410
+ width: dr.width,
411
+ height: dr.height
412
+ };
413
+ });
414
+ const clear = routeOffset(
415
+ local.start,
416
+ local.end,
417
+ obstacles,
418
+ this.opts.avoidPadding ?? 14
419
+ );
420
+ const BOW = 1.6;
421
+ const belly = { x: clear.x * BOW, y: clear.y * BOW };
422
+ const d = buildPath(local, curvature, belly);
423
+ this.appendDrawable(this.rc.path(d, roughOpts), "line");
424
+ const head = this.opts.head;
425
+ const size = this.opts.headSize;
426
+ if (head === "end" || head === "both") {
427
+ const dir = endTangent(local);
428
+ this.appendDrawable(
429
+ this.rc.path(arrowHeadPath(local.end, dir, size), roughOpts),
430
+ "head"
431
+ );
432
+ }
433
+ if (head === "start" || head === "both") {
434
+ const dir = startTangent(local);
435
+ this.appendDrawable(
436
+ this.rc.path(arrowHeadPath(local.start, dir, size), roughOpts),
437
+ "head"
438
+ );
439
+ }
440
+ this.lineEl = null;
441
+ let longest = 0;
442
+ for (const seg of this.segments) {
443
+ seg.len = seg.el.getTotalLength();
444
+ seg.el.style.strokeDasharray = String(seg.len);
445
+ seg.el.style.strokeDashoffset = String(seg.len);
446
+ if (seg.kind === "line" && seg.len >= longest) {
447
+ longest = seg.len;
448
+ this.lineEl = seg.el;
449
+ }
450
+ }
451
+ this.renderLabel();
452
+ this.applyProgress();
453
+ }
454
+ /** Place the label at a point along the line, with a masking background. */
455
+ renderLabel() {
456
+ this.labelEl = null;
457
+ this.labelBgEl = null;
458
+ const text = this.opts.label;
459
+ if (!text || !this.lineEl) return;
460
+ const total = this.lineEl.getTotalLength();
461
+ const at = clampAt(this.opts.labelAt ?? 0.5);
462
+ const pt = this.lineEl.getPointAtLength(at * total);
463
+ const offset = this.opts.labelOffset ?? 0;
464
+ let x = pt.x;
465
+ let y = pt.y;
466
+ if (offset && total > 0) {
467
+ const eps = Math.min(1, total / 2);
468
+ const before = this.lineEl.getPointAtLength(Math.max(0, at * total - eps));
469
+ const after = this.lineEl.getPointAtLength(Math.min(total, at * total + eps));
470
+ const n = unitNormal(before, after);
471
+ x += n.x * offset;
472
+ y += n.y * offset;
473
+ }
474
+ const label = createSvgEl("text");
475
+ label.textContent = text;
476
+ label.setAttribute("x", String(x));
477
+ label.setAttribute("y", String(y));
478
+ label.setAttribute("text-anchor", "middle");
479
+ label.setAttribute("dominant-baseline", "central");
480
+ label.style.font = this.opts.font ?? "600 16px sans-serif";
481
+ label.style.fill = this.opts.labelColor ?? this.stroke;
482
+ this.group.appendChild(label);
483
+ const bgColor = this.opts.labelBackground ?? getComputedStyle(this.container).backgroundColor;
484
+ if (bgColor && bgColor !== "none") {
485
+ const pad = 6;
486
+ const box = label.getBBox();
487
+ const rect = createSvgEl("rect");
488
+ rect.setAttribute("x", String(box.x - pad));
489
+ rect.setAttribute("y", String(box.y - pad / 2));
490
+ rect.setAttribute("width", String(box.width + pad * 2));
491
+ rect.setAttribute("height", String(box.height + pad));
492
+ rect.setAttribute("rx", "4");
493
+ rect.setAttribute("fill", bgColor);
494
+ this.group.insertBefore(rect, label);
495
+ this.labelBgEl = rect;
496
+ }
497
+ this.labelEl = label;
498
+ }
499
+ /** roughjs returns a <g> of one or more <path>; collect them in order. */
500
+ appendDrawable(g, kind) {
501
+ const paths = g.querySelectorAll("path");
502
+ paths.forEach((p) => {
503
+ const el = p;
504
+ el.setAttribute("fill", "none");
505
+ this.group.appendChild(el);
506
+ this.segments.push({ el, len: 0, kind });
507
+ });
508
+ }
509
+ /**
510
+ * Reveal the line as one growing pen tip (all line sub-strokes advance to the
511
+ * same leading fraction together), then draw the arrowhead(s) sequentially
512
+ * once the line is complete.
513
+ */
514
+ applyProgress() {
515
+ const eased = this.opts.easing(clamp01(this.progress));
516
+ const offsets = dashOffsets(this.segments, eased);
517
+ this.segments.forEach((seg, i) => {
518
+ seg.el.style.strokeDashoffset = String(offsets[i]);
519
+ });
520
+ if (this.labelEl) {
521
+ const op = labelOpacity(
522
+ lineProgress(this.segments, eased),
523
+ this.opts.labelAt ?? 0.5
524
+ );
525
+ this.labelEl.style.opacity = String(op);
526
+ if (this.labelBgEl) this.labelBgEl.style.opacity = String(op);
527
+ }
528
+ }
529
+ bind() {
530
+ const targets = [this.refs.start, this.refs.end, ...this.resolveAvoid()];
531
+ if (this.refs.target) targets.push(this.refs.target);
532
+ this.ro = new ResizeObserver(() => this.render());
533
+ targets.forEach((t) => this.ro.observe(t));
534
+ if (this.opts.scroll !== false) {
535
+ window.addEventListener("scroll", this.onScroll, true);
536
+ window.addEventListener("resize", this.onScroll);
537
+ }
538
+ }
539
+ update() {
540
+ if (this.destroyed) return;
541
+ if (this.opts.scroll === false) {
542
+ this.applyProgress();
543
+ return;
544
+ }
545
+ const scroll = this.opts.scroll ?? {};
546
+ const range = scroll.range ?? [0.85, 0.35];
547
+ const targetRect = this.refs.target ? docRect(this.refs.target) : midpointRect(this.refs.start, this.refs.end);
548
+ const raw = scrollProgress(targetRect, range);
549
+ this.progress = clamp01(raw * this.opts.speed);
550
+ this.applyProgress();
551
+ }
552
+ };
553
+ function resolve(ref) {
554
+ return typeof ref === "string" ? document.querySelector(ref) : ref;
555
+ }
556
+ function refKey(ref) {
557
+ return typeof ref === "string" ? ref : ref.tagName + (ref.id ? "#" + ref.id : "");
558
+ }
559
+ function clampAt(t) {
560
+ return t < 0 ? 0 : t > 1 ? 1 : t;
561
+ }
562
+
563
+ // src/index.ts
564
+ function scrollArrow(options) {
565
+ return new ScrollArrow(options);
566
+ }
567
+
568
+ exports.ScrollArrow = ScrollArrow;
569
+ exports.easeInOutCubic = easeInOutCubic;
570
+ exports.scrollArrow = scrollArrow;
571
+ //# sourceMappingURL=index.cjs.map
572
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1,57 @@
1
+ import { S as ScrollArrowOptions } from './types-DehQP2Hx.cjs';
2
+ export { A as ArrowHead, E as ElementRef, P as Point, a as ScrollOptions, b as Socket } from './types-DehQP2Hx.cjs';
3
+
4
+ /** A single hand-drawn arrow that draws itself between two elements on scroll. */
5
+ declare class ScrollArrow {
6
+ private opts;
7
+ private container;
8
+ private svg;
9
+ private rc;
10
+ private group;
11
+ private refs;
12
+ private seed;
13
+ private stroke;
14
+ /**
15
+ * Drawable segments. Line strokes (rough.js emits 1-2 overlapping ones) share
16
+ * a leading edge so they grow as a single pen tip; heads draw after the line.
17
+ */
18
+ private segments;
19
+ /** Representative line stroke + label nodes, when a label is set. */
20
+ private lineEl;
21
+ private labelEl;
22
+ private labelBgEl;
23
+ private progress;
24
+ private ro?;
25
+ private rafId;
26
+ private destroyed;
27
+ constructor(options: ScrollArrowOptions);
28
+ /** Manually set draw progress (0..1). Only meaningful when scroll is false. */
29
+ setProgress(p: number): void;
30
+ /** Recompute geometry (call after layout changes you control). */
31
+ refresh(): void;
32
+ destroy(): void;
33
+ private resolveRefs;
34
+ private resolveAvoid;
35
+ private computeEndpoints;
36
+ private render;
37
+ /** Place the label at a point along the line, with a masking background. */
38
+ private renderLabel;
39
+ /** roughjs returns a <g> of one or more <path>; collect them in order. */
40
+ private appendDrawable;
41
+ /**
42
+ * Reveal the line as one growing pen tip (all line sub-strokes advance to the
43
+ * same leading fraction together), then draw the arrowhead(s) sequentially
44
+ * once the line is complete.
45
+ */
46
+ private applyProgress;
47
+ private bind;
48
+ private onScroll;
49
+ private update;
50
+ }
51
+
52
+ declare function easeInOutCubic(t: number): number;
53
+
54
+ /** Convenience factory. `const a = scrollArrow({ start, end })`. */
55
+ declare function scrollArrow(options: ScrollArrowOptions): ScrollArrow;
56
+
57
+ export { ScrollArrow, ScrollArrowOptions, easeInOutCubic, scrollArrow };
@@ -0,0 +1,57 @@
1
+ import { S as ScrollArrowOptions } from './types-DehQP2Hx.js';
2
+ export { A as ArrowHead, E as ElementRef, P as Point, a as ScrollOptions, b as Socket } from './types-DehQP2Hx.js';
3
+
4
+ /** A single hand-drawn arrow that draws itself between two elements on scroll. */
5
+ declare class ScrollArrow {
6
+ private opts;
7
+ private container;
8
+ private svg;
9
+ private rc;
10
+ private group;
11
+ private refs;
12
+ private seed;
13
+ private stroke;
14
+ /**
15
+ * Drawable segments. Line strokes (rough.js emits 1-2 overlapping ones) share
16
+ * a leading edge so they grow as a single pen tip; heads draw after the line.
17
+ */
18
+ private segments;
19
+ /** Representative line stroke + label nodes, when a label is set. */
20
+ private lineEl;
21
+ private labelEl;
22
+ private labelBgEl;
23
+ private progress;
24
+ private ro?;
25
+ private rafId;
26
+ private destroyed;
27
+ constructor(options: ScrollArrowOptions);
28
+ /** Manually set draw progress (0..1). Only meaningful when scroll is false. */
29
+ setProgress(p: number): void;
30
+ /** Recompute geometry (call after layout changes you control). */
31
+ refresh(): void;
32
+ destroy(): void;
33
+ private resolveRefs;
34
+ private resolveAvoid;
35
+ private computeEndpoints;
36
+ private render;
37
+ /** Place the label at a point along the line, with a masking background. */
38
+ private renderLabel;
39
+ /** roughjs returns a <g> of one or more <path>; collect them in order. */
40
+ private appendDrawable;
41
+ /**
42
+ * Reveal the line as one growing pen tip (all line sub-strokes advance to the
43
+ * same leading fraction together), then draw the arrowhead(s) sequentially
44
+ * once the line is complete.
45
+ */
46
+ private applyProgress;
47
+ private bind;
48
+ private onScroll;
49
+ private update;
50
+ }
51
+
52
+ declare function easeInOutCubic(t: number): number;
53
+
54
+ /** Convenience factory. `const a = scrollArrow({ start, end })`. */
55
+ declare function scrollArrow(options: ScrollArrowOptions): ScrollArrow;
56
+
57
+ export { ScrollArrow, ScrollArrowOptions, easeInOutCubic, scrollArrow };
package/dist/index.js ADDED
@@ -0,0 +1,11 @@
1
+ import { ScrollArrow } from './chunk-HLZXSGP5.js';
2
+ export { ScrollArrow, easeInOutCubic } from './chunk-HLZXSGP5.js';
3
+
4
+ // src/index.ts
5
+ function scrollArrow(options) {
6
+ return new ScrollArrow(options);
7
+ }
8
+
9
+ export { scrollArrow };
10
+ //# sourceMappingURL=index.js.map
11
+ //# sourceMappingURL=index.js.map