@scratch/scratch-svg-renderer 13.6.10 → 13.7.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scratch/scratch-svg-renderer",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.7.0",
|
|
4
4
|
"description": "SVG renderer for Scratch",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"homepage": "https://github.com/scratchfoundation/scratch-svg-renderer#readme",
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"babel-loader": "9.2.1",
|
|
65
65
|
"copy-webpack-plugin": "6.4.1",
|
|
66
66
|
"eslint": "9.39.4",
|
|
67
|
-
"eslint-config-scratch": "14.1.
|
|
67
|
+
"eslint-config-scratch": "14.1.10",
|
|
68
68
|
"globals": "16.5.0",
|
|
69
69
|
"jsdom": "13.2.0",
|
|
70
70
|
"mkdirp": "2.1.6",
|
|
@@ -73,8 +73,8 @@
|
|
|
73
73
|
"scratch-semantic-release-config": "4.0.1",
|
|
74
74
|
"scratch-webpack-configuration": "3.1.2",
|
|
75
75
|
"semantic-release": "25.0.3",
|
|
76
|
-
"tap": "21.
|
|
77
|
-
"webpack": "5.106.
|
|
76
|
+
"tap": "21.7.0",
|
|
77
|
+
"webpack": "5.106.2",
|
|
78
78
|
"webpack-cli": "5.1.4",
|
|
79
79
|
"webpack-dev-server": "5.2.3"
|
|
80
80
|
},
|
package/src/transform-applier.js
CHANGED
|
@@ -487,6 +487,56 @@ const _parseUrl = (value, windowRef) => {
|
|
|
487
487
|
return res;
|
|
488
488
|
};
|
|
489
489
|
|
|
490
|
+
// Fixes an issue where clip paths didn’t follow transforms.
|
|
491
|
+
//
|
|
492
|
+
// In SVG, elements can have transforms (scale, rotate, translate, etc.)
|
|
493
|
+
// applied via a transform matrix. In this file, we apply those transforms
|
|
494
|
+
// into the actual geometry (e.g. path data) instead of keeping them as
|
|
495
|
+
// separate transform attributes.
|
|
496
|
+
//
|
|
497
|
+
// However, clip paths are defined separately and are referenced by elements.
|
|
498
|
+
// When we applied transforms to the element’s geometry, the clip path itself
|
|
499
|
+
// remained unchanged, so it no longer lined up with the transformed shape.
|
|
500
|
+
//
|
|
501
|
+
// This function clones the original clip path and applies the same transform
|
|
502
|
+
// matrix to it, ensuring the clipping region stays correctly aligned with
|
|
503
|
+
// the transformed element.
|
|
504
|
+
const _createClipPath = function (clipPathId, svgTag, matrix) {
|
|
505
|
+
const oldClipPath = svgTag.getElementById(clipPathId);
|
|
506
|
+
if (!oldClipPath) return null;
|
|
507
|
+
|
|
508
|
+
// Build unique ID from matrix, same pattern as _createGradient
|
|
509
|
+
let matrixString = Matrix.toString(matrix);
|
|
510
|
+
matrixString = matrixString.substring(8, matrixString.length - 1);
|
|
511
|
+
const newClipPathId = `${clipPathId}-${matrixString}`;
|
|
512
|
+
|
|
513
|
+
// Already transformed, reuse it
|
|
514
|
+
if (svgTag.getElementById(newClipPathId)) {
|
|
515
|
+
return `url(#${newClipPathId})`;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
let defs = svgTag.getElementsByTagName('defs');
|
|
519
|
+
if (defs.length === 0) {
|
|
520
|
+
defs = SvgElement.create('defs');
|
|
521
|
+
svgTag.appendChild(defs);
|
|
522
|
+
} else {
|
|
523
|
+
defs = defs[0];
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// Clone and give new ID
|
|
527
|
+
const newClipPath = oldClipPath.cloneNode(true);
|
|
528
|
+
newClipPath.setAttribute('id', newClipPathId);
|
|
529
|
+
|
|
530
|
+
// Compose with any existing transform on the clipPath rather than replacing it
|
|
531
|
+
const existingMatrix = _parseTransform(oldClipPath);
|
|
532
|
+
const composedMatrix = Matrix.compose(matrix, existingMatrix);
|
|
533
|
+
newClipPath.setAttribute('transform', Matrix.toString(composedMatrix));
|
|
534
|
+
|
|
535
|
+
defs.appendChild(newClipPath);
|
|
536
|
+
|
|
537
|
+
return `url(#${newClipPathId})`;
|
|
538
|
+
};
|
|
539
|
+
|
|
490
540
|
/**
|
|
491
541
|
* Scratch 2.0 displays stroke widths in a "normalized" way, that is,
|
|
492
542
|
* if a shape with a stroke width has a transform applied, it will be
|
|
@@ -512,6 +562,18 @@ const _parseUrl = (value, windowRef) => {
|
|
|
512
562
|
const transformStrokeWidths = function (svgTag, windowRef, bboxForTesting) {
|
|
513
563
|
const inherited = Matrix.identity();
|
|
514
564
|
|
|
565
|
+
const _applyTransformToClipPath = function (element, matrix) {
|
|
566
|
+
const clipPathAttr = element.attributes && element.attributes['clip-path'];
|
|
567
|
+
if (!clipPathAttr) return;
|
|
568
|
+
const clipPathId = _parseUrl(clipPathAttr.value, windowRef);
|
|
569
|
+
if (!clipPathId) return;
|
|
570
|
+
|
|
571
|
+
const newClipPathRef = _createClipPath(clipPathId, svgTag, matrix);
|
|
572
|
+
if (newClipPathRef) {
|
|
573
|
+
element.setAttribute('clip-path', newClipPathRef);
|
|
574
|
+
}
|
|
575
|
+
};
|
|
576
|
+
|
|
515
577
|
const applyTransforms = (element, matrix, strokeWidth, fill, stroke) => {
|
|
516
578
|
if (_isContainerElement(element)) {
|
|
517
579
|
// Push fills and stroke width down to leaves
|
|
@@ -523,12 +585,15 @@ const transformStrokeWidths = function (svgTag, windowRef, bboxForTesting) {
|
|
|
523
585
|
if (element.attributes.stroke) stroke = element.attributes.stroke.value;
|
|
524
586
|
}
|
|
525
587
|
|
|
588
|
+
const currentMatrix = Matrix.compose(matrix, _parseTransform(element));
|
|
589
|
+
_applyTransformToClipPath(element, currentMatrix);
|
|
590
|
+
|
|
526
591
|
// If any child nodes don't take attributes, leave the attributes
|
|
527
592
|
// at the parent level.
|
|
528
593
|
for (let i = 0; i < element.childNodes.length; i++) {
|
|
529
594
|
applyTransforms(
|
|
530
595
|
element.childNodes[i],
|
|
531
|
-
|
|
596
|
+
currentMatrix,
|
|
532
597
|
strokeWidth,
|
|
533
598
|
fill,
|
|
534
599
|
stroke
|