使用 svg 防止子像素渲染
Prevent subpixel rendering with svg
我目前正在使用 SVG,但走到了死胡同。
SVG 有线条,应该与缩放一起缩放(以便它们保持平衡:例如 100% 宽度 10px --> 10% 宽度 1px)
我使用以下代码缩放所有 stroke-widths
:
var svgPath = this._svgContainer.find('svg [class*="style"]');
for (var i = 0; i < svgPath.length; ++i) {
var newStrokeWidth = this._oldStrokeWidth[i] * (1 / (width / imgData.w));
$(svgPath[i]).css(
'stroke-width', newStrokeWidth
);
}
其中 width
是缩放后的新宽度,imgData.w
是原始未缩放的宽度。
问题是,如果我放大到很远。笔划变小并导致亚像素渲染。据说黑线会变成灰色。
我的想法是在某个点剪切值以防止它。
但据我所知,由于不同的屏幕(台式机、移动设备、4K),我也必须考虑设备像素比
如果有人可以帮助我解决我的问题,那就太好了
var newStrokeWidth = this._oldStrokeWidth[i] * (1 / (width / imgData.w));
newStrokeWidth = (newStrokeWidth < 1) ? 1 : newStrokeWidth;
newStrokeWidth 将始终为 1 或更大
我们终于找到了解决方案,以防有人遇到同样的问题:
1) 由于 this._$svgElement
的平移和 vpx
的计算在完全不同的代码部分中,元素为 'between' 像素。 (100.88945px for example)。这会导致线条模糊。
我用一个简单的 Math.round().
修复了这部分
this._hammerCanvas.on('panmove', (event: any) => {
const translate3d = 'translate3d(' + Math.round(this._oldDeltaX + ((vpx === imgData.x) ? 0 : vpx) + event.deltaX) + 'px, ' + Math.round(this._oldDeltaY + ((vpy === imgData.y) ? 0 : vpy) + event.deltaY) + 'px, 0)';
this._$svgElement.css({
transform: translate3d
});
}
2) 为了解决 SVG 视口和线条强度之间的问题,我必须实施一种方法来计算 strokewidth 等于 1 'real' 像素关于 svgs 维度。
更新后的代码如下所示:(这是从服务器加载 SVG 后的初始代码。在缩放内部,上面的旧代码仍然相同)
const pixelRatio = devicePixelRatio || 1;
const widthRatio = this._initSVGWidth / svgContainerWidth;
const heightRatio = this._initSVGHeight / svgContainerHeight;
this._svgZoomFactor = Math.max(widthRatio, heightRatio);
const strokeWidth1px = this.computeStrokeWidth1px(widthRatio, heightRatio);
for (let i = 0; i < svgPaths.length; ++i) {
this._initalStrokeWidth[i] = parseFloat($(svgPaths[i]).css('stroke-width'));
const newStrokeWidth = Math.max(strokeWidth1px / pixelRatio, this._svgZoomFactor * this._initalStrokeWidth[i]);
$(svgPaths[i])[0].setAttribute('style', 'stroke-width:' + newStrokeWidth);
this._oldStrokeWidth[i] = newStrokeWidth;
}
和计算:
protected computeStrokeWidth1px (widthRatio: number, heightRatio: number): number {
const viewBox = this._$svgElement[0].getAttribute('viewBox').split(' ');
const viewBoxWidthRatio = parseFloat(viewBox[2]) / this._$svgElement.width();
const viewBoxHeightRatio = parseFloat(viewBox[3]) / this._$svgElement.height();
return widthRatio > heightRatio ? viewBoxWidthRatio : viewBoxHeightRatio;
}
我目前正在使用 SVG,但走到了死胡同。
SVG 有线条,应该与缩放一起缩放(以便它们保持平衡:例如 100% 宽度 10px --> 10% 宽度 1px)
我使用以下代码缩放所有 stroke-widths
:
var svgPath = this._svgContainer.find('svg [class*="style"]');
for (var i = 0; i < svgPath.length; ++i) {
var newStrokeWidth = this._oldStrokeWidth[i] * (1 / (width / imgData.w));
$(svgPath[i]).css(
'stroke-width', newStrokeWidth
);
}
其中 width
是缩放后的新宽度,imgData.w
是原始未缩放的宽度。
问题是,如果我放大到很远。笔划变小并导致亚像素渲染。据说黑线会变成灰色。
我的想法是在某个点剪切值以防止它。 但据我所知,由于不同的屏幕(台式机、移动设备、4K),我也必须考虑设备像素比
如果有人可以帮助我解决我的问题,那就太好了
var newStrokeWidth = this._oldStrokeWidth[i] * (1 / (width / imgData.w));
newStrokeWidth = (newStrokeWidth < 1) ? 1 : newStrokeWidth;
newStrokeWidth 将始终为 1 或更大
我们终于找到了解决方案,以防有人遇到同样的问题:
1) 由于 this._$svgElement
的平移和 vpx
的计算在完全不同的代码部分中,元素为 'between' 像素。 (100.88945px for example)。这会导致线条模糊。
我用一个简单的 Math.round().
this._hammerCanvas.on('panmove', (event: any) => {
const translate3d = 'translate3d(' + Math.round(this._oldDeltaX + ((vpx === imgData.x) ? 0 : vpx) + event.deltaX) + 'px, ' + Math.round(this._oldDeltaY + ((vpy === imgData.y) ? 0 : vpy) + event.deltaY) + 'px, 0)';
this._$svgElement.css({
transform: translate3d
});
}
2) 为了解决 SVG 视口和线条强度之间的问题,我必须实施一种方法来计算 strokewidth 等于 1 'real' 像素关于 svgs 维度。
更新后的代码如下所示:(这是从服务器加载 SVG 后的初始代码。在缩放内部,上面的旧代码仍然相同)
const pixelRatio = devicePixelRatio || 1;
const widthRatio = this._initSVGWidth / svgContainerWidth;
const heightRatio = this._initSVGHeight / svgContainerHeight;
this._svgZoomFactor = Math.max(widthRatio, heightRatio);
const strokeWidth1px = this.computeStrokeWidth1px(widthRatio, heightRatio);
for (let i = 0; i < svgPaths.length; ++i) {
this._initalStrokeWidth[i] = parseFloat($(svgPaths[i]).css('stroke-width'));
const newStrokeWidth = Math.max(strokeWidth1px / pixelRatio, this._svgZoomFactor * this._initalStrokeWidth[i]);
$(svgPaths[i])[0].setAttribute('style', 'stroke-width:' + newStrokeWidth);
this._oldStrokeWidth[i] = newStrokeWidth;
}
和计算:
protected computeStrokeWidth1px (widthRatio: number, heightRatio: number): number {
const viewBox = this._$svgElement[0].getAttribute('viewBox').split(' ');
const viewBoxWidthRatio = parseFloat(viewBox[2]) / this._$svgElement.width();
const viewBoxHeightRatio = parseFloat(viewBox[3]) / this._$svgElement.height();
return widthRatio > heightRatio ? viewBoxWidthRatio : viewBoxHeightRatio;
}