【发布时间】:2020-05-18 18:04:03
【问题描述】:
问题的本质是当我移动一个 SVG 元素时,线条的宽度会发生变化。
我在浏览器环境中工作,我想要清晰的线条(无抗锯齿),因此形状渲染设置为清晰边缘。例如,您可能有
"d M 0 0 L 10 0 L 10 10 L 0 10 z"
定义一个正方形,样式设置为
'填充:无;中风:黑色;笔画宽度:1px;形状渲染:crispEdges'
然后使用 translate() 用鼠标拖动它。它可以工作,但是随着正方形的移动,线条的粗细会跳动一个像素,我希望粗细保持不变。
我想我知道为什么会发生这种情况,但我找不到任何方法来阻止它。它发生(我认为)是由于相对于 viewBox 的坐标映射到相对于物理监视器像素的坐标的方式。当一个像素宽(在 SVG 术语中)的线被映射到显示器时,它可能会或可能不会跨越多个像素,因此它最终可能会变得更厚或更薄(屏幕)像素。
我尝试弄乱 viewBox 大小与绘图区域大小的比率,将平移量四舍五入为整数或采用整数 + 0.50 的形式,但似乎没有这样的解决方案可行因为它归结为浏览器的缩放级别。矢量效果:非缩放笔画也不能修复它。
有没有办法告诉 SVG 将所有路径视为 1d 几何形状,并为所有路径使用特定的笔?换句话说,我希望用于绘制所有内容的笔“随缩放而缩放”,而不是在绘制线条后缩放。
拜托,仅限原版 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>
【问题讨论】:
-
我正在 Windows/FireFox 上进行测试。
-
我刚刚在 Chrome 中尝试过。相同的行为(u)r
-
SideLength = 10.5 能帮你解决问题吗?
-
不,我刚试过 SideLength = 10.5。我认为这一定是关于如何将屏幕外位图(假设有)复制到监视器。
-
没有离屏位图。
标签: javascript svg zooming