@tsdraw/core 0.9.0 → 0.9.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/dist/index.cjs +95 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +95 -30
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -561,7 +561,11 @@ function getLineDash(dash, width) {
|
|
|
561
561
|
var DEFAULT_SPACING = 20;
|
|
562
562
|
var DEFAULT_LINE_WIDTH = 0.5;
|
|
563
563
|
var DEFAULT_DOT_RADIUS = 1;
|
|
564
|
-
var DEFAULT_OPACITY =
|
|
564
|
+
var DEFAULT_OPACITY = 1;
|
|
565
|
+
var LINE_GAP_FADE_IN = 3;
|
|
566
|
+
var LINE_GAP_FADE_FULL = 8;
|
|
567
|
+
var DOT_GAP_FADE_IN = 2;
|
|
568
|
+
var DOT_GAP_FADE_FULL = 16;
|
|
565
569
|
function resolvePresetPatternColor(colorLight, colorDark, theme) {
|
|
566
570
|
if (theme === "dark") return colorDark ?? colorLight ?? "#888888";
|
|
567
571
|
return colorLight ?? "#c0c0c0";
|
|
@@ -574,56 +578,116 @@ function visiblePageRect(viewport, canvasWidth, canvasHeight) {
|
|
|
574
578
|
maxY: (canvasHeight - viewport.y) / viewport.zoom
|
|
575
579
|
};
|
|
576
580
|
}
|
|
577
|
-
function
|
|
581
|
+
function isOnGrid(value, gridSpacing) {
|
|
582
|
+
const remainder = (value % gridSpacing + gridSpacing) % gridSpacing;
|
|
583
|
+
return remainder < 0.5 || remainder > gridSpacing - 0.5;
|
|
584
|
+
}
|
|
585
|
+
function fadeForScreenGap(screenGap, fadeIn, fadeFull) {
|
|
586
|
+
return Math.min(1, Math.max(0, (screenGap - fadeIn) / (fadeFull - fadeIn)));
|
|
587
|
+
}
|
|
588
|
+
function buildLevels(baseSpacing, zoom, fadeIn, fadeFull) {
|
|
589
|
+
let topSpacing = baseSpacing;
|
|
590
|
+
while (topSpacing * zoom < fadeFull) {
|
|
591
|
+
topSpacing *= 2;
|
|
592
|
+
}
|
|
593
|
+
const levels = [];
|
|
594
|
+
for (let s = topSpacing; s >= baseSpacing; s /= 2) {
|
|
595
|
+
const fade = fadeForScreenGap(s * zoom, fadeIn, fadeFull);
|
|
596
|
+
if (fade < 0.01) break;
|
|
597
|
+
levels.push({ spacing: s, fade });
|
|
598
|
+
}
|
|
599
|
+
return levels;
|
|
600
|
+
}
|
|
601
|
+
function drawDotTile(ctx, physicalWidth, physicalHeight, panXPx, panYPx, exactTileSize, radiusPx, color, alpha) {
|
|
602
|
+
if (alpha < 5e-3 || exactTileSize < 2) return;
|
|
603
|
+
const tilePixels = Math.ceil(exactTileSize);
|
|
604
|
+
const tileScale = exactTileSize / tilePixels;
|
|
605
|
+
const center = tilePixels / 2;
|
|
606
|
+
const tile = new OffscreenCanvas(tilePixels, tilePixels);
|
|
607
|
+
const tctx = tile.getContext("2d");
|
|
608
|
+
tctx.fillStyle = color;
|
|
609
|
+
tctx.beginPath();
|
|
610
|
+
tctx.arc(center, center, radiusPx / tileScale, 0, Math.PI * 2);
|
|
611
|
+
tctx.fill();
|
|
612
|
+
const pattern = ctx.createPattern(tile, "repeat");
|
|
613
|
+
if (!pattern) return;
|
|
614
|
+
pattern.setTransform(
|
|
615
|
+
new DOMMatrix().translateSelf(panXPx - exactTileSize / 2, panYPx - exactTileSize / 2).scaleSelf(tileScale, tileScale)
|
|
616
|
+
);
|
|
617
|
+
ctx.save();
|
|
618
|
+
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
619
|
+
ctx.globalAlpha = alpha;
|
|
620
|
+
ctx.fillStyle = pattern;
|
|
621
|
+
ctx.fillRect(0, 0, physicalWidth, physicalHeight);
|
|
622
|
+
ctx.restore();
|
|
623
|
+
}
|
|
624
|
+
function drawMergingDots(ctx, viewport, canvasWidth, canvasHeight, baseSpacing, dotRadius, color, opacity) {
|
|
625
|
+
const dpr = ctx.getTransform().a;
|
|
626
|
+
const levels = buildLevels(baseSpacing, viewport.zoom, DOT_GAP_FADE_IN, DOT_GAP_FADE_FULL);
|
|
627
|
+
if (levels.length === 0) return;
|
|
628
|
+
const physicalWidth = canvasWidth * dpr;
|
|
629
|
+
const physicalHeight = canvasHeight * dpr;
|
|
630
|
+
const panXPx = viewport.x * dpr;
|
|
631
|
+
const panYPx = viewport.y * dpr;
|
|
632
|
+
const radiusPx = dotRadius * dpr;
|
|
633
|
+
for (const level of levels) {
|
|
634
|
+
const tileSize = level.spacing * viewport.zoom * dpr;
|
|
635
|
+
drawDotTile(ctx, physicalWidth, physicalHeight, panXPx, panYPx, tileSize, radiusPx, color, opacity * level.fade);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
function drawHorizontalLinesForLevel(ctx, visible, spacing, skipSpacing, lineWidth, color, alpha) {
|
|
578
639
|
const startY = Math.floor(visible.minY / spacing) * spacing;
|
|
579
640
|
ctx.save();
|
|
580
641
|
ctx.strokeStyle = color;
|
|
581
|
-
ctx.lineWidth = lineWidth
|
|
582
|
-
ctx.globalAlpha =
|
|
642
|
+
ctx.lineWidth = lineWidth;
|
|
643
|
+
ctx.globalAlpha = alpha;
|
|
583
644
|
ctx.beginPath();
|
|
584
645
|
for (let y = startY; y <= visible.maxY; y += spacing) {
|
|
646
|
+
if (skipSpacing > 0 && isOnGrid(y, skipSpacing)) continue;
|
|
585
647
|
ctx.moveTo(visible.minX, y);
|
|
586
648
|
ctx.lineTo(visible.maxX, y);
|
|
587
649
|
}
|
|
588
650
|
ctx.stroke();
|
|
589
651
|
ctx.restore();
|
|
590
652
|
}
|
|
591
|
-
function
|
|
653
|
+
function drawMergingLines(ctx, visible, baseSpacing, lineWidth, color, opacity, zoom) {
|
|
654
|
+
const compensatedWidth = lineWidth / ctx.getTransform().a;
|
|
655
|
+
const levels = buildLevels(baseSpacing, zoom, LINE_GAP_FADE_IN, LINE_GAP_FADE_FULL);
|
|
656
|
+
for (let i = 0; i < levels.length; i++) {
|
|
657
|
+
const level = levels[i];
|
|
658
|
+
const coarserSpacing = i > 0 ? levels[i - 1].spacing : 0;
|
|
659
|
+
drawHorizontalLinesForLevel(ctx, visible, level.spacing, coarserSpacing, compensatedWidth, color, opacity * level.fade);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
function drawGridForLevel(ctx, visible, spacing, skipSpacing, lineWidth, color, alpha) {
|
|
592
663
|
const startX = Math.floor(visible.minX / spacing) * spacing;
|
|
593
664
|
const startY = Math.floor(visible.minY / spacing) * spacing;
|
|
594
|
-
const compensatedWidth = lineWidth / ctx.getTransform().a;
|
|
595
665
|
ctx.save();
|
|
596
666
|
ctx.strokeStyle = color;
|
|
597
|
-
ctx.lineWidth =
|
|
598
|
-
ctx.globalAlpha =
|
|
667
|
+
ctx.lineWidth = lineWidth;
|
|
668
|
+
ctx.globalAlpha = alpha;
|
|
599
669
|
ctx.beginPath();
|
|
600
670
|
for (let x = startX; x <= visible.maxX; x += spacing) {
|
|
671
|
+
if (skipSpacing > 0 && isOnGrid(x, skipSpacing)) continue;
|
|
601
672
|
ctx.moveTo(x, visible.minY);
|
|
602
673
|
ctx.lineTo(x, visible.maxY);
|
|
603
674
|
}
|
|
604
675
|
for (let y = startY; y <= visible.maxY; y += spacing) {
|
|
676
|
+
if (skipSpacing > 0 && isOnGrid(y, skipSpacing)) continue;
|
|
605
677
|
ctx.moveTo(visible.minX, y);
|
|
606
678
|
ctx.lineTo(visible.maxX, y);
|
|
607
679
|
}
|
|
608
680
|
ctx.stroke();
|
|
609
681
|
ctx.restore();
|
|
610
682
|
}
|
|
611
|
-
function
|
|
612
|
-
const
|
|
613
|
-
const
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
ctx.beginPath();
|
|
619
|
-
for (let x = startX; x <= visible.maxX; x += spacing) {
|
|
620
|
-
for (let y = startY; y <= visible.maxY; y += spacing) {
|
|
621
|
-
ctx.moveTo(x + compensatedRadius, y);
|
|
622
|
-
ctx.arc(x, y, compensatedRadius, 0, Math.PI * 2);
|
|
623
|
-
}
|
|
683
|
+
function drawMergingGrid(ctx, visible, baseSpacing, lineWidth, color, opacity, zoom) {
|
|
684
|
+
const compensatedWidth = lineWidth / ctx.getTransform().a;
|
|
685
|
+
const levels = buildLevels(baseSpacing, zoom, LINE_GAP_FADE_IN, LINE_GAP_FADE_FULL);
|
|
686
|
+
for (let i = 0; i < levels.length; i++) {
|
|
687
|
+
const level = levels[i];
|
|
688
|
+
const coarserSpacing = i > 0 ? levels[i - 1].spacing : 0;
|
|
689
|
+
drawGridForLevel(ctx, visible, level.spacing, coarserSpacing, compensatedWidth, color, opacity * level.fade);
|
|
624
690
|
}
|
|
625
|
-
ctx.fill();
|
|
626
|
-
ctx.restore();
|
|
627
691
|
}
|
|
628
692
|
function renderCanvasBackground(ctx, viewport, canvasWidth, canvasHeight, options, theme) {
|
|
629
693
|
if (!options || options.type === "blank") return;
|
|
@@ -631,23 +695,24 @@ function renderCanvasBackground(ctx, viewport, canvasWidth, canvasHeight, option
|
|
|
631
695
|
options.render(ctx, viewport, canvasWidth, canvasHeight);
|
|
632
696
|
return;
|
|
633
697
|
}
|
|
634
|
-
const
|
|
635
|
-
if (
|
|
698
|
+
const baseSpacing = options.spacing ?? DEFAULT_SPACING;
|
|
699
|
+
if (baseSpacing <= 0) return;
|
|
636
700
|
const color = resolvePresetPatternColor(options.color, options.colorDark, theme);
|
|
637
701
|
const opacity = options.opacity ?? DEFAULT_OPACITY;
|
|
702
|
+
if (options.type === "dots") {
|
|
703
|
+
drawMergingDots(ctx, viewport, canvasWidth, canvasHeight, baseSpacing, options.size ?? DEFAULT_DOT_RADIUS, color, opacity);
|
|
704
|
+
return;
|
|
705
|
+
}
|
|
638
706
|
const visible = visiblePageRect(viewport, canvasWidth, canvasHeight);
|
|
639
707
|
ctx.save();
|
|
640
708
|
ctx.translate(viewport.x, viewport.y);
|
|
641
709
|
ctx.scale(viewport.zoom, viewport.zoom);
|
|
642
710
|
switch (options.type) {
|
|
643
711
|
case "lines":
|
|
644
|
-
|
|
712
|
+
drawMergingLines(ctx, visible, baseSpacing, options.size ?? DEFAULT_LINE_WIDTH, color, opacity, viewport.zoom);
|
|
645
713
|
break;
|
|
646
714
|
case "grid":
|
|
647
|
-
|
|
648
|
-
break;
|
|
649
|
-
case "dots":
|
|
650
|
-
drawDotPattern(ctx, visible, spacing, options.size ?? DEFAULT_DOT_RADIUS, color, opacity);
|
|
715
|
+
drawMergingGrid(ctx, visible, baseSpacing, options.size ?? DEFAULT_LINE_WIDTH, color, opacity, viewport.zoom);
|
|
651
716
|
break;
|
|
652
717
|
}
|
|
653
718
|
ctx.restore();
|