scroll-arrows 0.2.0 → 0.3.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/README.md +5 -3
- package/dist/{chunk-LIT577GH.js → chunk-GX722UDR.js} +45 -17
- package/dist/index.cjs +43 -15
- package/dist/index.d.cts +11 -2
- package/dist/index.d.ts +11 -2
- package/dist/index.js +2 -2
- package/dist/react.cjs +43 -15
- package/dist/react.d.cts +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js +1 -1
- package/dist/{types-Cpvz9wtr.d.cts → types-CgHPWybd.d.cts} +19 -5
- package/dist/{types-Cpvz9wtr.d.ts → types-CgHPWybd.d.ts} +19 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -89,9 +89,11 @@ arrow.destroy();
|
|
|
89
89
|
rendering fully drawn and static (no scroll animation) while still tracking
|
|
90
90
|
layout. Opt out with `respectReducedMotion: false` to keep the animation.
|
|
91
91
|
Works the same for `scrollArrowGroup`.
|
|
92
|
-
- **Labels** — `label` rides along the line at `labelAt`
|
|
93
|
-
|
|
94
|
-
|
|
92
|
+
- **Labels** — `label` rides along the line at `labelAt` — a keyword
|
|
93
|
+
(`'start'` / `'middle'` / `'end'`), a `0..1` fraction, or a percentage string
|
|
94
|
+
like `'25%'` (default `'middle'`) — and can sit off the line via `labelOffset`
|
|
95
|
+
(perpendicular px; + = left of the draw direction, − = right). Fades in as the
|
|
96
|
+
pen draws through it.
|
|
95
97
|
`labelBackground` masks a gap in the line behind the text (the excalidraw
|
|
96
98
|
look); style via `labelColor` / `font`.
|
|
97
99
|
- **Staggered groups** — `scrollArrowGroup` owns N arrows and reveals them in
|
|
@@ -301,6 +301,25 @@ function clamp012(t) {
|
|
|
301
301
|
}
|
|
302
302
|
|
|
303
303
|
// src/draw.ts
|
|
304
|
+
var LABEL_KEYWORDS = {
|
|
305
|
+
start: 0,
|
|
306
|
+
middle: 0.5,
|
|
307
|
+
end: 1
|
|
308
|
+
};
|
|
309
|
+
function resolveLabelAt(value, fallback = 0.5) {
|
|
310
|
+
if (value == null) return fallback;
|
|
311
|
+
if (typeof value === "number") {
|
|
312
|
+
return Number.isFinite(value) ? clamp01(value) : fallback;
|
|
313
|
+
}
|
|
314
|
+
const key = value.trim().toLowerCase();
|
|
315
|
+
const keyword = LABEL_KEYWORDS[key];
|
|
316
|
+
if (keyword !== void 0) return keyword;
|
|
317
|
+
if (key.endsWith("%")) {
|
|
318
|
+
const n = Number.parseFloat(key.slice(0, -1));
|
|
319
|
+
return Number.isFinite(n) ? clamp01(n / 100) : fallback;
|
|
320
|
+
}
|
|
321
|
+
return fallback;
|
|
322
|
+
}
|
|
304
323
|
function lengths(segs) {
|
|
305
324
|
let lineLen = 0;
|
|
306
325
|
let headLen = 0;
|
|
@@ -330,7 +349,8 @@ function lineProgress(segs, eased) {
|
|
|
330
349
|
return lineLen > 0 ? clamp01(drawn / lineLen) : 1;
|
|
331
350
|
}
|
|
332
351
|
function labelOpacity(lineProg, labelAt, fade = 0.08) {
|
|
333
|
-
|
|
352
|
+
const start = Math.min(clamp01(labelAt), 1 - fade);
|
|
353
|
+
return clamp01((lineProg - start) / (fade || 1));
|
|
334
354
|
}
|
|
335
355
|
|
|
336
356
|
// src/scroll-arrow.ts
|
|
@@ -344,8 +364,17 @@ var ScrollArrow = class {
|
|
|
344
364
|
this.segments = [];
|
|
345
365
|
/** Representative line stroke + label nodes, when a label is set. */
|
|
346
366
|
this.lineEl = null;
|
|
367
|
+
/**
|
|
368
|
+
* The smooth ideal path `d` (pre-roughjs). Label placement measures against
|
|
369
|
+
* this, not `lineEl`: rough.js bakes its double stroke into one path with two
|
|
370
|
+
* subpaths, so `lineEl.getTotalLength()` is ~2x the visible curve and would
|
|
371
|
+
* put `labelAt` at twice its intended fraction.
|
|
372
|
+
*/
|
|
373
|
+
this.lineD = "";
|
|
347
374
|
this.labelEl = null;
|
|
348
375
|
this.labelBgEl = null;
|
|
376
|
+
/** `opts.labelAt` resolved to a 0..1 fraction; cached by renderLabel for the draw loop. */
|
|
377
|
+
this.resolvedLabelAt = 0.5;
|
|
349
378
|
this.rafId = 0;
|
|
350
379
|
this.destroyed = false;
|
|
351
380
|
this.onScroll = () => {
|
|
@@ -501,6 +530,7 @@ var ScrollArrow = class {
|
|
|
501
530
|
const belly = { x: clear.x * BOW, y: clear.y * BOW };
|
|
502
531
|
d = buildPath(local, curvature, belly);
|
|
503
532
|
}
|
|
533
|
+
this.lineD = d;
|
|
504
534
|
this.appendDrawable(this.rc.path(d, roughOpts), "line");
|
|
505
535
|
const head = this.opts.head;
|
|
506
536
|
const size = this.opts.headSize;
|
|
@@ -537,25 +567,26 @@ var ScrollArrow = class {
|
|
|
537
567
|
this.labelEl = null;
|
|
538
568
|
this.labelBgEl = null;
|
|
539
569
|
const text = this.opts.label;
|
|
540
|
-
if (!text || !this.lineEl) return;
|
|
541
|
-
const
|
|
542
|
-
|
|
543
|
-
const
|
|
570
|
+
if (!text || !this.lineEl || !this.lineD) return;
|
|
571
|
+
const at = resolveLabelAt(this.opts.labelAt);
|
|
572
|
+
this.resolvedLabelAt = at;
|
|
573
|
+
const measure = createSvgEl("path");
|
|
574
|
+
measure.setAttribute("d", this.lineD);
|
|
575
|
+
this.group.appendChild(measure);
|
|
576
|
+
const total = measure.getTotalLength();
|
|
577
|
+
const pt = measure.getPointAtLength(at * total);
|
|
544
578
|
const offset = this.opts.labelOffset ?? 0;
|
|
545
579
|
let x = pt.x;
|
|
546
580
|
let y = pt.y;
|
|
547
581
|
if (offset && total > 0) {
|
|
548
582
|
const eps = Math.min(1, total / 2);
|
|
549
|
-
const before =
|
|
550
|
-
|
|
551
|
-
);
|
|
552
|
-
const after = this.lineEl.getPointAtLength(
|
|
553
|
-
Math.min(total, at * total + eps)
|
|
554
|
-
);
|
|
583
|
+
const before = measure.getPointAtLength(Math.max(0, at * total - eps));
|
|
584
|
+
const after = measure.getPointAtLength(Math.min(total, at * total + eps));
|
|
555
585
|
const n = unitNormal(before, after);
|
|
556
586
|
x += n.x * offset;
|
|
557
587
|
y += n.y * offset;
|
|
558
588
|
}
|
|
589
|
+
this.group.removeChild(measure);
|
|
559
590
|
const label = createSvgEl("text");
|
|
560
591
|
label.textContent = text;
|
|
561
592
|
label.setAttribute("x", String(x));
|
|
@@ -605,7 +636,7 @@ var ScrollArrow = class {
|
|
|
605
636
|
if (this.labelEl) {
|
|
606
637
|
const op = labelOpacity(
|
|
607
638
|
lineProgress(this.segments, eased),
|
|
608
|
-
this.
|
|
639
|
+
this.resolvedLabelAt
|
|
609
640
|
);
|
|
610
641
|
this.labelEl.style.opacity = String(op);
|
|
611
642
|
if (this.labelBgEl) this.labelBgEl.style.opacity = String(op);
|
|
@@ -659,9 +690,6 @@ function resolve(ref) {
|
|
|
659
690
|
function refKey(ref) {
|
|
660
691
|
return typeof ref === "string" ? ref : ref.tagName + (ref.id ? "#" + ref.id : "");
|
|
661
692
|
}
|
|
662
|
-
function clampAt(t) {
|
|
663
|
-
return t < 0 ? 0 : t > 1 ? 1 : t;
|
|
664
|
-
}
|
|
665
693
|
|
|
666
694
|
// src/group.ts
|
|
667
695
|
var ScrollArrowGroup = class {
|
|
@@ -798,5 +826,5 @@ function resolve2(ref) {
|
|
|
798
826
|
}
|
|
799
827
|
|
|
800
828
|
export { ScrollArrow, ScrollArrowGroup, easeInOutCubic };
|
|
801
|
-
//# sourceMappingURL=chunk-
|
|
802
|
-
//# sourceMappingURL=chunk-
|
|
829
|
+
//# sourceMappingURL=chunk-GX722UDR.js.map
|
|
830
|
+
//# sourceMappingURL=chunk-GX722UDR.js.map
|
package/dist/index.cjs
CHANGED
|
@@ -309,6 +309,25 @@ function clamp012(t) {
|
|
|
309
309
|
}
|
|
310
310
|
|
|
311
311
|
// src/draw.ts
|
|
312
|
+
var LABEL_KEYWORDS = {
|
|
313
|
+
start: 0,
|
|
314
|
+
middle: 0.5,
|
|
315
|
+
end: 1
|
|
316
|
+
};
|
|
317
|
+
function resolveLabelAt(value, fallback = 0.5) {
|
|
318
|
+
if (value == null) return fallback;
|
|
319
|
+
if (typeof value === "number") {
|
|
320
|
+
return Number.isFinite(value) ? clamp01(value) : fallback;
|
|
321
|
+
}
|
|
322
|
+
const key = value.trim().toLowerCase();
|
|
323
|
+
const keyword = LABEL_KEYWORDS[key];
|
|
324
|
+
if (keyword !== void 0) return keyword;
|
|
325
|
+
if (key.endsWith("%")) {
|
|
326
|
+
const n = Number.parseFloat(key.slice(0, -1));
|
|
327
|
+
return Number.isFinite(n) ? clamp01(n / 100) : fallback;
|
|
328
|
+
}
|
|
329
|
+
return fallback;
|
|
330
|
+
}
|
|
312
331
|
function lengths(segs) {
|
|
313
332
|
let lineLen = 0;
|
|
314
333
|
let headLen = 0;
|
|
@@ -338,7 +357,8 @@ function lineProgress(segs, eased) {
|
|
|
338
357
|
return lineLen > 0 ? clamp01(drawn / lineLen) : 1;
|
|
339
358
|
}
|
|
340
359
|
function labelOpacity(lineProg, labelAt, fade = 0.08) {
|
|
341
|
-
|
|
360
|
+
const start = Math.min(clamp01(labelAt), 1 - fade);
|
|
361
|
+
return clamp01((lineProg - start) / (fade || 1));
|
|
342
362
|
}
|
|
343
363
|
|
|
344
364
|
// src/scroll-arrow.ts
|
|
@@ -352,8 +372,17 @@ var ScrollArrow = class {
|
|
|
352
372
|
this.segments = [];
|
|
353
373
|
/** Representative line stroke + label nodes, when a label is set. */
|
|
354
374
|
this.lineEl = null;
|
|
375
|
+
/**
|
|
376
|
+
* The smooth ideal path `d` (pre-roughjs). Label placement measures against
|
|
377
|
+
* this, not `lineEl`: rough.js bakes its double stroke into one path with two
|
|
378
|
+
* subpaths, so `lineEl.getTotalLength()` is ~2x the visible curve and would
|
|
379
|
+
* put `labelAt` at twice its intended fraction.
|
|
380
|
+
*/
|
|
381
|
+
this.lineD = "";
|
|
355
382
|
this.labelEl = null;
|
|
356
383
|
this.labelBgEl = null;
|
|
384
|
+
/** `opts.labelAt` resolved to a 0..1 fraction; cached by renderLabel for the draw loop. */
|
|
385
|
+
this.resolvedLabelAt = 0.5;
|
|
357
386
|
this.rafId = 0;
|
|
358
387
|
this.destroyed = false;
|
|
359
388
|
this.onScroll = () => {
|
|
@@ -509,6 +538,7 @@ var ScrollArrow = class {
|
|
|
509
538
|
const belly = { x: clear.x * BOW, y: clear.y * BOW };
|
|
510
539
|
d = buildPath(local, curvature, belly);
|
|
511
540
|
}
|
|
541
|
+
this.lineD = d;
|
|
512
542
|
this.appendDrawable(this.rc.path(d, roughOpts), "line");
|
|
513
543
|
const head = this.opts.head;
|
|
514
544
|
const size = this.opts.headSize;
|
|
@@ -545,25 +575,26 @@ var ScrollArrow = class {
|
|
|
545
575
|
this.labelEl = null;
|
|
546
576
|
this.labelBgEl = null;
|
|
547
577
|
const text = this.opts.label;
|
|
548
|
-
if (!text || !this.lineEl) return;
|
|
549
|
-
const
|
|
550
|
-
|
|
551
|
-
const
|
|
578
|
+
if (!text || !this.lineEl || !this.lineD) return;
|
|
579
|
+
const at = resolveLabelAt(this.opts.labelAt);
|
|
580
|
+
this.resolvedLabelAt = at;
|
|
581
|
+
const measure = createSvgEl("path");
|
|
582
|
+
measure.setAttribute("d", this.lineD);
|
|
583
|
+
this.group.appendChild(measure);
|
|
584
|
+
const total = measure.getTotalLength();
|
|
585
|
+
const pt = measure.getPointAtLength(at * total);
|
|
552
586
|
const offset = this.opts.labelOffset ?? 0;
|
|
553
587
|
let x = pt.x;
|
|
554
588
|
let y = pt.y;
|
|
555
589
|
if (offset && total > 0) {
|
|
556
590
|
const eps = Math.min(1, total / 2);
|
|
557
|
-
const before =
|
|
558
|
-
|
|
559
|
-
);
|
|
560
|
-
const after = this.lineEl.getPointAtLength(
|
|
561
|
-
Math.min(total, at * total + eps)
|
|
562
|
-
);
|
|
591
|
+
const before = measure.getPointAtLength(Math.max(0, at * total - eps));
|
|
592
|
+
const after = measure.getPointAtLength(Math.min(total, at * total + eps));
|
|
563
593
|
const n = unitNormal(before, after);
|
|
564
594
|
x += n.x * offset;
|
|
565
595
|
y += n.y * offset;
|
|
566
596
|
}
|
|
597
|
+
this.group.removeChild(measure);
|
|
567
598
|
const label = createSvgEl("text");
|
|
568
599
|
label.textContent = text;
|
|
569
600
|
label.setAttribute("x", String(x));
|
|
@@ -613,7 +644,7 @@ var ScrollArrow = class {
|
|
|
613
644
|
if (this.labelEl) {
|
|
614
645
|
const op = labelOpacity(
|
|
615
646
|
lineProgress(this.segments, eased),
|
|
616
|
-
this.
|
|
647
|
+
this.resolvedLabelAt
|
|
617
648
|
);
|
|
618
649
|
this.labelEl.style.opacity = String(op);
|
|
619
650
|
if (this.labelBgEl) this.labelBgEl.style.opacity = String(op);
|
|
@@ -667,9 +698,6 @@ function resolve(ref) {
|
|
|
667
698
|
function refKey(ref) {
|
|
668
699
|
return typeof ref === "string" ? ref : ref.tagName + (ref.id ? "#" + ref.id : "");
|
|
669
700
|
}
|
|
670
|
-
function clampAt(t) {
|
|
671
|
-
return t < 0 ? 0 : t > 1 ? 1 : t;
|
|
672
|
-
}
|
|
673
701
|
|
|
674
702
|
// src/group.ts
|
|
675
703
|
var ScrollArrowGroup = class {
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { S as ScrollArrowOptions, a as ScrollArrowGroupOptions } from './types-
|
|
2
|
-
export { A as ArrowHead, E as ElementRef, P as Point, b as ScrollOptions, c as Socket } from './types-
|
|
1
|
+
import { S as ScrollArrowOptions, a as ScrollArrowGroupOptions } from './types-CgHPWybd.cjs';
|
|
2
|
+
export { A as ArrowHead, E as ElementRef, L as LabelPosition, P as Point, b as ScrollOptions, c as Socket } from './types-CgHPWybd.cjs';
|
|
3
3
|
|
|
4
4
|
/** A single hand-drawn arrow that draws itself between two elements on scroll. */
|
|
5
5
|
declare class ScrollArrow {
|
|
@@ -18,8 +18,17 @@ declare class ScrollArrow {
|
|
|
18
18
|
private segments;
|
|
19
19
|
/** Representative line stroke + label nodes, when a label is set. */
|
|
20
20
|
private lineEl;
|
|
21
|
+
/**
|
|
22
|
+
* The smooth ideal path `d` (pre-roughjs). Label placement measures against
|
|
23
|
+
* this, not `lineEl`: rough.js bakes its double stroke into one path with two
|
|
24
|
+
* subpaths, so `lineEl.getTotalLength()` is ~2x the visible curve and would
|
|
25
|
+
* put `labelAt` at twice its intended fraction.
|
|
26
|
+
*/
|
|
27
|
+
private lineD;
|
|
21
28
|
private labelEl;
|
|
22
29
|
private labelBgEl;
|
|
30
|
+
/** `opts.labelAt` resolved to a 0..1 fraction; cached by renderLabel for the draw loop. */
|
|
31
|
+
private resolvedLabelAt;
|
|
23
32
|
private progress;
|
|
24
33
|
private ro?;
|
|
25
34
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { S as ScrollArrowOptions, a as ScrollArrowGroupOptions } from './types-
|
|
2
|
-
export { A as ArrowHead, E as ElementRef, P as Point, b as ScrollOptions, c as Socket } from './types-
|
|
1
|
+
import { S as ScrollArrowOptions, a as ScrollArrowGroupOptions } from './types-CgHPWybd.js';
|
|
2
|
+
export { A as ArrowHead, E as ElementRef, L as LabelPosition, P as Point, b as ScrollOptions, c as Socket } from './types-CgHPWybd.js';
|
|
3
3
|
|
|
4
4
|
/** A single hand-drawn arrow that draws itself between two elements on scroll. */
|
|
5
5
|
declare class ScrollArrow {
|
|
@@ -18,8 +18,17 @@ declare class ScrollArrow {
|
|
|
18
18
|
private segments;
|
|
19
19
|
/** Representative line stroke + label nodes, when a label is set. */
|
|
20
20
|
private lineEl;
|
|
21
|
+
/**
|
|
22
|
+
* The smooth ideal path `d` (pre-roughjs). Label placement measures against
|
|
23
|
+
* this, not `lineEl`: rough.js bakes its double stroke into one path with two
|
|
24
|
+
* subpaths, so `lineEl.getTotalLength()` is ~2x the visible curve and would
|
|
25
|
+
* put `labelAt` at twice its intended fraction.
|
|
26
|
+
*/
|
|
27
|
+
private lineD;
|
|
21
28
|
private labelEl;
|
|
22
29
|
private labelBgEl;
|
|
30
|
+
/** `opts.labelAt` resolved to a 0..1 fraction; cached by renderLabel for the draw loop. */
|
|
31
|
+
private resolvedLabelAt;
|
|
23
32
|
private progress;
|
|
24
33
|
private ro?;
|
|
25
34
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ScrollArrow, ScrollArrowGroup } from './chunk-
|
|
2
|
-
export { ScrollArrow, ScrollArrowGroup, easeInOutCubic } from './chunk-
|
|
1
|
+
import { ScrollArrow, ScrollArrowGroup } from './chunk-GX722UDR.js';
|
|
2
|
+
export { ScrollArrow, ScrollArrowGroup, easeInOutCubic } from './chunk-GX722UDR.js';
|
|
3
3
|
|
|
4
4
|
// src/index.ts
|
|
5
5
|
function scrollArrow(options) {
|
package/dist/react.cjs
CHANGED
|
@@ -310,6 +310,25 @@ function clamp012(t) {
|
|
|
310
310
|
}
|
|
311
311
|
|
|
312
312
|
// src/draw.ts
|
|
313
|
+
var LABEL_KEYWORDS = {
|
|
314
|
+
start: 0,
|
|
315
|
+
middle: 0.5,
|
|
316
|
+
end: 1
|
|
317
|
+
};
|
|
318
|
+
function resolveLabelAt(value, fallback = 0.5) {
|
|
319
|
+
if (value == null) return fallback;
|
|
320
|
+
if (typeof value === "number") {
|
|
321
|
+
return Number.isFinite(value) ? clamp01(value) : fallback;
|
|
322
|
+
}
|
|
323
|
+
const key = value.trim().toLowerCase();
|
|
324
|
+
const keyword = LABEL_KEYWORDS[key];
|
|
325
|
+
if (keyword !== void 0) return keyword;
|
|
326
|
+
if (key.endsWith("%")) {
|
|
327
|
+
const n = Number.parseFloat(key.slice(0, -1));
|
|
328
|
+
return Number.isFinite(n) ? clamp01(n / 100) : fallback;
|
|
329
|
+
}
|
|
330
|
+
return fallback;
|
|
331
|
+
}
|
|
313
332
|
function lengths(segs) {
|
|
314
333
|
let lineLen = 0;
|
|
315
334
|
let headLen = 0;
|
|
@@ -339,7 +358,8 @@ function lineProgress(segs, eased) {
|
|
|
339
358
|
return lineLen > 0 ? clamp01(drawn / lineLen) : 1;
|
|
340
359
|
}
|
|
341
360
|
function labelOpacity(lineProg, labelAt, fade = 0.08) {
|
|
342
|
-
|
|
361
|
+
const start = Math.min(clamp01(labelAt), 1 - fade);
|
|
362
|
+
return clamp01((lineProg - start) / (fade || 1));
|
|
343
363
|
}
|
|
344
364
|
|
|
345
365
|
// src/scroll-arrow.ts
|
|
@@ -353,8 +373,17 @@ var ScrollArrow = class {
|
|
|
353
373
|
this.segments = [];
|
|
354
374
|
/** Representative line stroke + label nodes, when a label is set. */
|
|
355
375
|
this.lineEl = null;
|
|
376
|
+
/**
|
|
377
|
+
* The smooth ideal path `d` (pre-roughjs). Label placement measures against
|
|
378
|
+
* this, not `lineEl`: rough.js bakes its double stroke into one path with two
|
|
379
|
+
* subpaths, so `lineEl.getTotalLength()` is ~2x the visible curve and would
|
|
380
|
+
* put `labelAt` at twice its intended fraction.
|
|
381
|
+
*/
|
|
382
|
+
this.lineD = "";
|
|
356
383
|
this.labelEl = null;
|
|
357
384
|
this.labelBgEl = null;
|
|
385
|
+
/** `opts.labelAt` resolved to a 0..1 fraction; cached by renderLabel for the draw loop. */
|
|
386
|
+
this.resolvedLabelAt = 0.5;
|
|
358
387
|
this.rafId = 0;
|
|
359
388
|
this.destroyed = false;
|
|
360
389
|
this.onScroll = () => {
|
|
@@ -510,6 +539,7 @@ var ScrollArrow = class {
|
|
|
510
539
|
const belly = { x: clear.x * BOW, y: clear.y * BOW };
|
|
511
540
|
d = buildPath(local, curvature, belly);
|
|
512
541
|
}
|
|
542
|
+
this.lineD = d;
|
|
513
543
|
this.appendDrawable(this.rc.path(d, roughOpts), "line");
|
|
514
544
|
const head = this.opts.head;
|
|
515
545
|
const size = this.opts.headSize;
|
|
@@ -546,25 +576,26 @@ var ScrollArrow = class {
|
|
|
546
576
|
this.labelEl = null;
|
|
547
577
|
this.labelBgEl = null;
|
|
548
578
|
const text = this.opts.label;
|
|
549
|
-
if (!text || !this.lineEl) return;
|
|
550
|
-
const
|
|
551
|
-
|
|
552
|
-
const
|
|
579
|
+
if (!text || !this.lineEl || !this.lineD) return;
|
|
580
|
+
const at = resolveLabelAt(this.opts.labelAt);
|
|
581
|
+
this.resolvedLabelAt = at;
|
|
582
|
+
const measure = createSvgEl("path");
|
|
583
|
+
measure.setAttribute("d", this.lineD);
|
|
584
|
+
this.group.appendChild(measure);
|
|
585
|
+
const total = measure.getTotalLength();
|
|
586
|
+
const pt = measure.getPointAtLength(at * total);
|
|
553
587
|
const offset = this.opts.labelOffset ?? 0;
|
|
554
588
|
let x = pt.x;
|
|
555
589
|
let y = pt.y;
|
|
556
590
|
if (offset && total > 0) {
|
|
557
591
|
const eps = Math.min(1, total / 2);
|
|
558
|
-
const before =
|
|
559
|
-
|
|
560
|
-
);
|
|
561
|
-
const after = this.lineEl.getPointAtLength(
|
|
562
|
-
Math.min(total, at * total + eps)
|
|
563
|
-
);
|
|
592
|
+
const before = measure.getPointAtLength(Math.max(0, at * total - eps));
|
|
593
|
+
const after = measure.getPointAtLength(Math.min(total, at * total + eps));
|
|
564
594
|
const n = unitNormal(before, after);
|
|
565
595
|
x += n.x * offset;
|
|
566
596
|
y += n.y * offset;
|
|
567
597
|
}
|
|
598
|
+
this.group.removeChild(measure);
|
|
568
599
|
const label = createSvgEl("text");
|
|
569
600
|
label.textContent = text;
|
|
570
601
|
label.setAttribute("x", String(x));
|
|
@@ -614,7 +645,7 @@ var ScrollArrow = class {
|
|
|
614
645
|
if (this.labelEl) {
|
|
615
646
|
const op = labelOpacity(
|
|
616
647
|
lineProgress(this.segments, eased),
|
|
617
|
-
this.
|
|
648
|
+
this.resolvedLabelAt
|
|
618
649
|
);
|
|
619
650
|
this.labelEl.style.opacity = String(op);
|
|
620
651
|
if (this.labelBgEl) this.labelBgEl.style.opacity = String(op);
|
|
@@ -668,9 +699,6 @@ function resolve(ref) {
|
|
|
668
699
|
function refKey(ref) {
|
|
669
700
|
return typeof ref === "string" ? ref : ref.tagName + (ref.id ? "#" + ref.id : "");
|
|
670
701
|
}
|
|
671
|
-
function clampAt(t) {
|
|
672
|
-
return t < 0 ? 0 : t > 1 ? 1 : t;
|
|
673
|
-
}
|
|
674
702
|
|
|
675
703
|
// src/group.ts
|
|
676
704
|
var ScrollArrowGroup = class {
|
package/dist/react.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RefObject } from 'react';
|
|
2
|
-
import { S as ScrollArrowOptions, a as ScrollArrowGroupOptions } from './types-
|
|
2
|
+
import { S as ScrollArrowOptions, a as ScrollArrowGroupOptions } from './types-CgHPWybd.cjs';
|
|
3
3
|
|
|
4
4
|
type Anchor = RefObject<Element | null> | Element | string;
|
|
5
5
|
interface UseScrollArrowOptions extends Omit<ScrollArrowOptions, 'start' | 'end'> {
|
package/dist/react.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RefObject } from 'react';
|
|
2
|
-
import { S as ScrollArrowOptions, a as ScrollArrowGroupOptions } from './types-
|
|
2
|
+
import { S as ScrollArrowOptions, a as ScrollArrowGroupOptions } from './types-CgHPWybd.js';
|
|
3
3
|
|
|
4
4
|
type Anchor = RefObject<Element | null> | Element | string;
|
|
5
5
|
interface UseScrollArrowOptions extends Omit<ScrollArrowOptions, 'start' | 'end'> {
|
package/dist/react.js
CHANGED
|
@@ -13,6 +13,13 @@ type ArrowHead = 'start' | 'end' | 'both' | 'none';
|
|
|
13
13
|
type Route = 'curved' | 'elbow';
|
|
14
14
|
/** Anything we can resolve to a live DOM element. */
|
|
15
15
|
type ElementRef = Element | string;
|
|
16
|
+
/**
|
|
17
|
+
* Where a label sits along the line: a keyword, a `0..1` fraction, or a
|
|
18
|
+
* percentage string like `'25%'`. A dynamically built percentage string is
|
|
19
|
+
* typed as `string`, so cast it (`pct as LabelPosition`) — any unrecognized
|
|
20
|
+
* value falls back to the default at runtime.
|
|
21
|
+
*/
|
|
22
|
+
type LabelPosition = 'start' | 'middle' | 'end' | number | `${number}%`;
|
|
16
23
|
interface ScrollOptions {
|
|
17
24
|
/**
|
|
18
25
|
* The element whose travel through the viewport drives the draw.
|
|
@@ -91,11 +98,18 @@ interface ScrollArrowOptions {
|
|
|
91
98
|
headSize?: number;
|
|
92
99
|
/** Text to place along the line. Omit for no label. */
|
|
93
100
|
label?: string;
|
|
94
|
-
/** Position of the label along the line, 0 (start) .. 1 (end). Default 0.5. */
|
|
95
|
-
labelAt?: number;
|
|
96
101
|
/**
|
|
97
|
-
*
|
|
98
|
-
*
|
|
102
|
+
* Where the label sits *along* the line. Accepts:
|
|
103
|
+
* - a keyword: `'start'`, `'middle'`, or `'end'`;
|
|
104
|
+
* - a `0..1` fraction (`0` = start, `1` = end); or
|
|
105
|
+
* - a percentage string like `'25%'`.
|
|
106
|
+
* Default `'middle'`. For sideways placement see `labelOffset`.
|
|
107
|
+
*/
|
|
108
|
+
labelAt?: LabelPosition;
|
|
109
|
+
/**
|
|
110
|
+
* How far the label sits *perpendicular* to the line, in px (sideways, not
|
|
111
|
+
* along it). Positive sits to the left of the draw direction, negative to the
|
|
112
|
+
* right; 0 sits on the line. Default 0.
|
|
99
113
|
*/
|
|
100
114
|
labelOffset?: number;
|
|
101
115
|
/** Label text color. Default: stroke color. */
|
|
@@ -179,4 +193,4 @@ interface ScrollArrowGroupOptions {
|
|
|
179
193
|
enabled?: boolean;
|
|
180
194
|
}
|
|
181
195
|
|
|
182
|
-
export type { ArrowHead as A, ElementRef as E, Point as P, ScrollArrowOptions as S, ScrollArrowGroupOptions as a, ScrollOptions as b, Socket as c };
|
|
196
|
+
export type { ArrowHead as A, ElementRef as E, LabelPosition as L, Point as P, ScrollArrowOptions as S, ScrollArrowGroupOptions as a, ScrollOptions as b, Socket as c };
|
|
@@ -13,6 +13,13 @@ type ArrowHead = 'start' | 'end' | 'both' | 'none';
|
|
|
13
13
|
type Route = 'curved' | 'elbow';
|
|
14
14
|
/** Anything we can resolve to a live DOM element. */
|
|
15
15
|
type ElementRef = Element | string;
|
|
16
|
+
/**
|
|
17
|
+
* Where a label sits along the line: a keyword, a `0..1` fraction, or a
|
|
18
|
+
* percentage string like `'25%'`. A dynamically built percentage string is
|
|
19
|
+
* typed as `string`, so cast it (`pct as LabelPosition`) — any unrecognized
|
|
20
|
+
* value falls back to the default at runtime.
|
|
21
|
+
*/
|
|
22
|
+
type LabelPosition = 'start' | 'middle' | 'end' | number | `${number}%`;
|
|
16
23
|
interface ScrollOptions {
|
|
17
24
|
/**
|
|
18
25
|
* The element whose travel through the viewport drives the draw.
|
|
@@ -91,11 +98,18 @@ interface ScrollArrowOptions {
|
|
|
91
98
|
headSize?: number;
|
|
92
99
|
/** Text to place along the line. Omit for no label. */
|
|
93
100
|
label?: string;
|
|
94
|
-
/** Position of the label along the line, 0 (start) .. 1 (end). Default 0.5. */
|
|
95
|
-
labelAt?: number;
|
|
96
101
|
/**
|
|
97
|
-
*
|
|
98
|
-
*
|
|
102
|
+
* Where the label sits *along* the line. Accepts:
|
|
103
|
+
* - a keyword: `'start'`, `'middle'`, or `'end'`;
|
|
104
|
+
* - a `0..1` fraction (`0` = start, `1` = end); or
|
|
105
|
+
* - a percentage string like `'25%'`.
|
|
106
|
+
* Default `'middle'`. For sideways placement see `labelOffset`.
|
|
107
|
+
*/
|
|
108
|
+
labelAt?: LabelPosition;
|
|
109
|
+
/**
|
|
110
|
+
* How far the label sits *perpendicular* to the line, in px (sideways, not
|
|
111
|
+
* along it). Positive sits to the left of the draw direction, negative to the
|
|
112
|
+
* right; 0 sits on the line. Default 0.
|
|
99
113
|
*/
|
|
100
114
|
labelOffset?: number;
|
|
101
115
|
/** Label text color. Default: stroke color. */
|
|
@@ -179,4 +193,4 @@ interface ScrollArrowGroupOptions {
|
|
|
179
193
|
enabled?: boolean;
|
|
180
194
|
}
|
|
181
195
|
|
|
182
|
-
export type { ArrowHead as A, ElementRef as E, Point as P, ScrollArrowOptions as S, ScrollArrowGroupOptions as a, ScrollOptions as b, Socket as c };
|
|
196
|
+
export type { ArrowHead as A, ElementRef as E, LabelPosition as L, Point as P, ScrollArrowOptions as S, ScrollArrowGroupOptions as a, ScrollOptions as b, Socket as c };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "scroll-arrows",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Hand-drawn arrows that draw themselves between two elements as you scroll. Roughness goes from clean straight lines to scratchy and curvy.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|