无论位置如何,如何获得一致的笔划宽度?
How to get Consistent Stroke Widths Regardless of Location?
问题的实质是,当我四处移动 SVG 元素时,线条的宽度会发生变化。
我在浏览器环境中工作,我想要清晰的线条(无抗锯齿),因此形状渲染设置为 crispEdges。例如,您可能
"d M 0 0 L 10 0 L 10 10 L 0 10 z"
定义一个正方形,样式设置为
'fill: none; stroke: black; stroke-width: 1px; shape-rendering: crispEdges'
然后使用translate() 用鼠标拖动它。它可以工作,但是随着方块的移动,线条的粗细会跳跃一个像素,我希望粗细保持不变。
我想我知道为什么它会发生,但我找不到任何方法来阻止它。它的发生(我认为)是由于相对于 viewBox 的坐标映射到相对于物理监视器像素的坐标的方式。当一个像素宽(在 SVG 术语中)的线被映射到显示器时,它可能跨越也可能不跨越几个像素,因此它最终可能会变粗或变细一个(屏幕)像素。
我试过弄乱 viewBox 大小与绘图区域大小的比率,将平移量四舍五入为整数或采用整数 + 0.50 的形式,但似乎没有这样的解决方案工作,因为它归结为浏览器中的缩放级别。 vector-effect: non-scaling-stroke 也没有解决它。
有没有办法告诉 SVG 将所有路径视为一维几何形状,并为所有路径使用特定的笔?换句话说,我希望用笔来绘制所有内容 "scale with the zoom",而不是在绘制线条后按比例缩放。
拜托,仅限香草 JS。
您可以通过 运行 下面的代码来测试我的意思。单击正方形并将其拖动(无触摸设备)。请注意,在某些级别的浏览器缩放下,它可能表现得很好,而不是我所描述的那样。
发生了一些奇怪的事情。 svg 元素(正方形)开始时看起来是统一的,所有边的宽度都相同。它仅响应鼠标移动而移动,并且该事件(大概)发生在鼠标移动整个(绝不是小数)屏幕像素时。因此,如果正方形以这种方式开始,人们会期望它移动整个屏幕像素并保持与屏幕像素对齐。
跟进...不是屏幕CTM的问题。我测试了逆确实是逆;也就是说,CTM x CTM^{-1} 是我尝试过的每个值的标识。 matrixTransform(getScreenCTM().inverse()) 提供的值在输入中是线性的(或在浮点数允许的范围内呈线性)。
<html>
<body>
<svg xmlns="http://www.w3.org/2000/svg" id="svgtest"
style = "border: 1px solid; display: block; margin-left: auto; margin-right: auto; shape-rendering: crispEdges;"
width = "400" height = "400" viewBox = "0 0 100 100" >
</svg>
<script>
var theSquare = {
x : 10,
y : 10
};
var initialSquare = {
x : 10,
y : 10
};
var SideLength = 10;
var initialX = 0;
var initialY = 0;
var draggingSquare = 0;
function pointInRect(p, r) {
return p.x > r.xLeft && p.x < r.xRight && p.y > r.yTop && p.y < r.yBottom;
}
function mouseToLocal(theEvent) {
var screenPt = svgArea.createSVGPoint();
screenPt.x = theEvent.clientX;
screenPt.y = theEvent.clientY;
var svgPt = screenPt.matrixTransform(svgArea.getScreenCTM().inverse());
return {
x : svgPt.x,
y : svgPt.y
};
}
function doMouseDown(event) {
var localCoord = mouseToLocal(event);
initialX = localCoord.x;
initialY = localCoord.y;
var pt = {x: initialX, y: initialY};
var rect = {xLeft: theSquare.x, xRight: theSquare.x + SideLength,
yTop: theSquare.y, yBottom: theSquare.y + SideLength};
if (pointInRect(pt,rect))
{
draggingSquare = 1;
initialSquare.x = theSquare.x;
initialSquare.y = theSquare.y;
}
else
draggingSquare = 0;
}
function doMouseUp(event) {
draggingSquare = 0;
}
function doMouseMove(event) {
if (draggingSquare == 0)
return;
var localCoord = mouseToLocal(event);
zeroTranslate.setTranslate(initialSquare.x + localCoord.x - initialX,
+initialSquare.y + localCoord.y - initialY);
theSquare.x = initialSquare.x + localCoord.x - initialX;
theSquare.y = initialSquare.y + localCoord.y - initialY;
}
var svgArea = document.getElementById('svgtest');
var theSVGSquare = document.createElementNS("http://www.w3.org/2000/svg", 'path' );
theSVGSquare.setAttributeNS(null, "d", "M 0 0" +
" L " + SideLength + " 0" +
" L " + SideLength + " " + SideLength +
" L 0 " +SideLength +
" z");
theSVGSquare.setAttributeNS(null, 'style', 'fill: none; stroke: black; stroke-width: 0.5px; shape-rendering: crispEdges' );
var tforms = theSVGSquare.transform;
var zeroTranslate = svgArea.createSVGTransform();
zeroTranslate.setTranslate(initialSquare.x,initialSquare.y);
theSVGSquare.transform.baseVal.insertItemBefore(zeroTranslate,0);
svgArea.appendChild(theSVGSquare);
svgArea.addEventListener("mousedown",doMouseDown,false);
svgArea.addEventListener("mouseup",doMouseUp,false);
svgArea.addEventListener("mousemove",doMouseMove,false);
</script>
</body>
</html>
我发布此 "answer" 作为正式结束问题的方式,并感谢 Robert Longson 的关注。
简而言之,我想做的事情不能在浏览器中完成,这很奇怪,因为我只想画一条线。由于抗锯齿,使用 HTML canvas 标签会产生一些模糊的东西,而 SVG 会产生抖动的线条。归根结底是浏览器没有提供设备像素级精度所需的信息。
我在我的个人网站上发布了关于这个问题的讨论,并附有示例:
问题的实质是,当我四处移动 SVG 元素时,线条的宽度会发生变化。
我在浏览器环境中工作,我想要清晰的线条(无抗锯齿),因此形状渲染设置为 crispEdges。例如,您可能
"d M 0 0 L 10 0 L 10 10 L 0 10 z"
定义一个正方形,样式设置为
'fill: none; stroke: black; stroke-width: 1px; shape-rendering: crispEdges'
然后使用translate() 用鼠标拖动它。它可以工作,但是随着方块的移动,线条的粗细会跳跃一个像素,我希望粗细保持不变。
我想我知道为什么它会发生,但我找不到任何方法来阻止它。它的发生(我认为)是由于相对于 viewBox 的坐标映射到相对于物理监视器像素的坐标的方式。当一个像素宽(在 SVG 术语中)的线被映射到显示器时,它可能跨越也可能不跨越几个像素,因此它最终可能会变粗或变细一个(屏幕)像素。
我试过弄乱 viewBox 大小与绘图区域大小的比率,将平移量四舍五入为整数或采用整数 + 0.50 的形式,但似乎没有这样的解决方案工作,因为它归结为浏览器中的缩放级别。 vector-effect: non-scaling-stroke 也没有解决它。
有没有办法告诉 SVG 将所有路径视为一维几何形状,并为所有路径使用特定的笔?换句话说,我希望用笔来绘制所有内容 "scale with the zoom",而不是在绘制线条后按比例缩放。
拜托,仅限香草 JS。
您可以通过 运行 下面的代码来测试我的意思。单击正方形并将其拖动(无触摸设备)。请注意,在某些级别的浏览器缩放下,它可能表现得很好,而不是我所描述的那样。
发生了一些奇怪的事情。 svg 元素(正方形)开始时看起来是统一的,所有边的宽度都相同。它仅响应鼠标移动而移动,并且该事件(大概)发生在鼠标移动整个(绝不是小数)屏幕像素时。因此,如果正方形以这种方式开始,人们会期望它移动整个屏幕像素并保持与屏幕像素对齐。
跟进...不是屏幕CTM的问题。我测试了逆确实是逆;也就是说,CTM x CTM^{-1} 是我尝试过的每个值的标识。 matrixTransform(getScreenCTM().inverse()) 提供的值在输入中是线性的(或在浮点数允许的范围内呈线性)。
<html>
<body>
<svg xmlns="http://www.w3.org/2000/svg" id="svgtest"
style = "border: 1px solid; display: block; margin-left: auto; margin-right: auto; shape-rendering: crispEdges;"
width = "400" height = "400" viewBox = "0 0 100 100" >
</svg>
<script>
var theSquare = {
x : 10,
y : 10
};
var initialSquare = {
x : 10,
y : 10
};
var SideLength = 10;
var initialX = 0;
var initialY = 0;
var draggingSquare = 0;
function pointInRect(p, r) {
return p.x > r.xLeft && p.x < r.xRight && p.y > r.yTop && p.y < r.yBottom;
}
function mouseToLocal(theEvent) {
var screenPt = svgArea.createSVGPoint();
screenPt.x = theEvent.clientX;
screenPt.y = theEvent.clientY;
var svgPt = screenPt.matrixTransform(svgArea.getScreenCTM().inverse());
return {
x : svgPt.x,
y : svgPt.y
};
}
function doMouseDown(event) {
var localCoord = mouseToLocal(event);
initialX = localCoord.x;
initialY = localCoord.y;
var pt = {x: initialX, y: initialY};
var rect = {xLeft: theSquare.x, xRight: theSquare.x + SideLength,
yTop: theSquare.y, yBottom: theSquare.y + SideLength};
if (pointInRect(pt,rect))
{
draggingSquare = 1;
initialSquare.x = theSquare.x;
initialSquare.y = theSquare.y;
}
else
draggingSquare = 0;
}
function doMouseUp(event) {
draggingSquare = 0;
}
function doMouseMove(event) {
if (draggingSquare == 0)
return;
var localCoord = mouseToLocal(event);
zeroTranslate.setTranslate(initialSquare.x + localCoord.x - initialX,
+initialSquare.y + localCoord.y - initialY);
theSquare.x = initialSquare.x + localCoord.x - initialX;
theSquare.y = initialSquare.y + localCoord.y - initialY;
}
var svgArea = document.getElementById('svgtest');
var theSVGSquare = document.createElementNS("http://www.w3.org/2000/svg", 'path' );
theSVGSquare.setAttributeNS(null, "d", "M 0 0" +
" L " + SideLength + " 0" +
" L " + SideLength + " " + SideLength +
" L 0 " +SideLength +
" z");
theSVGSquare.setAttributeNS(null, 'style', 'fill: none; stroke: black; stroke-width: 0.5px; shape-rendering: crispEdges' );
var tforms = theSVGSquare.transform;
var zeroTranslate = svgArea.createSVGTransform();
zeroTranslate.setTranslate(initialSquare.x,initialSquare.y);
theSVGSquare.transform.baseVal.insertItemBefore(zeroTranslate,0);
svgArea.appendChild(theSVGSquare);
svgArea.addEventListener("mousedown",doMouseDown,false);
svgArea.addEventListener("mouseup",doMouseUp,false);
svgArea.addEventListener("mousemove",doMouseMove,false);
</script>
</body>
</html>
我发布此 "answer" 作为正式结束问题的方式,并感谢 Robert Longson 的关注。
简而言之,我想做的事情不能在浏览器中完成,这很奇怪,因为我只想画一条线。由于抗锯齿,使用 HTML canvas 标签会产生一些模糊的东西,而 SVG 会产生抖动的线条。归根结底是浏览器没有提供设备像素级精度所需的信息。
我在我的个人网站上发布了关于这个问题的讨论,并附有示例: