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