Fabric.js: 如何将从 URL 加载的 SVG 转换为灰度?
Fabric.js: How to turn SVG loaded from URL into grayscale?
我正在开发一个产品定制工具,用户可以在其中上传他们的 SVG 文件,以便将它们打印在产品上。
现在,我需要能够将这个导入的徽标变成灰度,以向他们展示如果他们选择激光打印类型会是什么样子。
我知道有一个灰度过滤器可以做到这一点,但它似乎不适用于我加载了这段代码的元素(不要介意来自 smarty 的 {$var}):
fabric.loadSVGFromURL(r.filepath, function(objects, options){
var loadedObjects = new fabric.util.groupSVGElements(objects, options);
//loadedObjects.setCoords();
//loadedObjects.setCoords();
var scaleX = canvas_{$image.id_image}.width / loadedObjects.width;
var scaleY = canvas_{$image.id_image}.height / loadedObjects.height;
var scale = (scaleX > scaleY) ? scaleY : scaleX;
canvas_{$image.id_image}.add(loadedObjects).renderAll();
loadedObjects.setCoords();
loadedObjects.set({
top: 0,
left: 0,
scaleX: scale,
scaleY: scale,
selectable: true
});
loadedSVG_{$image.id_image} = loadedObjects;
canvas_{$image.id_image}.renderAll();
});
我想知道实现此结果的最佳方法是什么。我应该把我的元素变成 fabric.Image
吗?这不会通过破坏向量来影响调整大小的能力吗?
还有其他方法吗?
如果您直接使用 Fabric.Image.fromURL()
将 svg 作为图像加载,您的 svg 图像不会降低质量,因为 <img>
标签用于 store svg 图像仍然在向量中。
但是,在应用图像滤镜时,滤镜会在 <canvas>
元素上以 img 标签的默认大小应用一次,这会导致图像光栅化。
一种骇人听闻的方法是首先将 svg 加载到对象或 iframe 或嵌入标记中,将所需的过滤器附加为 SVGFilter,然后将 svg 从 DOM 字符串加载到 Fabric 中.
但这有一些限制:
- 跨源请求被阻止,并且这些标签上没有
crossOrigin
属性。
- 您必须自己进行解析。
- 放大时 FF 中的图像框似乎存在错误...
无论如何,这里是代码和 a plunker example 现场展示。
var canvas = new fabric.Canvas('canvas');
var obj = document.createElement('object');
//make it invisible
obj.style.height = obj.style.width = 0;
// we have to append it so it does load
document.body.appendChild(obj);
obj.onload = function() {
// get the def node of our svgFilter
var defs = document.getElementById('def').cloneNode('true');
// get the doc of our object
var doc = obj.contentDocument || obj.contentWindow.documentElement;
// we can now remove the <object>
document.body.removeChild(obj);
// the target svg
var svg = doc.querySelector('svg');
svg.insertBefore(defs, svg.firstElementChild);
// set the filter on the whole svg
svg.setAttribute('filter', 'url(#desaturate)');
// encode it to a dataURI
var svgStr = new XMLSerializer().serializeToString(svg);
var svgURL = 'data:image/svg+xml; charset=utf8, ' + encodeURIComponent(svgStr);
// load it as an image
fabric.Image.fromURL(svgURL, function(img) {
canvas.add(img).renderAll();
});
// We could have tried with loadSVGFrom... but the filter isn't applied then
/*
fabric.loadSVGFromString(svgStr, function(objects, options) {
var loadedObject = fabric.util.groupSVGElements(objects, options);
canvas.add(loadedObject);
loadedObject.center().setCoords();
canvas.renderAll();
});
fabric.loadSVGFromURL(svgURL, function(objects, options) {
var loadedObject = fabric.util.groupSVGElements(objects, options);
canvas.add(loadedObject);
loadedObject.center().setCoords();
canvas.renderAll();
});
*/
};
obj.data = "yourFile.svg";
<!-- append your svg filter in the main doc -->
<svg id="filters" width="0" height="0">
<defs id="def">
<filter id="desaturate">
<feColorMatrix type="matrix" values="0.3333 0.3333 0.3333 0 0
0.3333 0.3333 0.3333 0 0
0.3333 0.3333 0.3333 0 0
0 0 0 1 0" />
</filter>
</defs>
</svg>
<canvas id="canvas" width="500" height="500"></canvas>
另一种解决方案是先在 up-scaled canvas 上绘制 svg,在其 dataURL 上应用过滤器,然后缩小图像,但这也会产生一些压缩伪影。 ..
var canvas = new fabric.Canvas('canvas');
var scaleSVG = function(url, scale, callback) {
var c = document.createElement('canvas');
var img = new Image();
img.crossOrigin = 'Anonymous';
img.onload = function() {
c.width = this.width * scale;
c.height = this.height * scale;
c.getContext('2d').drawImage(this, 0, 0, c.width, c.height);
callback(c.toDataURL());
};
img.src = url;
};
scaleSVG('https://dl.dropboxusercontent.com/s/b7qcju9ubmdtigj/ball.svg',
10, function(dataURL) {
fabric.Image.fromURL(dataURL, function(img) {
// scale in the other side
var oImg = img.scale(.1);
var filter = new fabric.Image.filters.Grayscale();
oImg.filters.push(filter);
canvas.add(oImg);
oImg.applyFilters(canvas.renderAll.bind(canvas));
}, {
crossOrigin: 'Anonymous'
});
});
<canvas id="canvas" width="500" height="500"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.5.0/fabric.js"></script>
我正在开发一个产品定制工具,用户可以在其中上传他们的 SVG 文件,以便将它们打印在产品上。
现在,我需要能够将这个导入的徽标变成灰度,以向他们展示如果他们选择激光打印类型会是什么样子。
我知道有一个灰度过滤器可以做到这一点,但它似乎不适用于我加载了这段代码的元素(不要介意来自 smarty 的 {$var}):
fabric.loadSVGFromURL(r.filepath, function(objects, options){
var loadedObjects = new fabric.util.groupSVGElements(objects, options);
//loadedObjects.setCoords();
//loadedObjects.setCoords();
var scaleX = canvas_{$image.id_image}.width / loadedObjects.width;
var scaleY = canvas_{$image.id_image}.height / loadedObjects.height;
var scale = (scaleX > scaleY) ? scaleY : scaleX;
canvas_{$image.id_image}.add(loadedObjects).renderAll();
loadedObjects.setCoords();
loadedObjects.set({
top: 0,
left: 0,
scaleX: scale,
scaleY: scale,
selectable: true
});
loadedSVG_{$image.id_image} = loadedObjects;
canvas_{$image.id_image}.renderAll();
});
我想知道实现此结果的最佳方法是什么。我应该把我的元素变成 fabric.Image
吗?这不会通过破坏向量来影响调整大小的能力吗?
还有其他方法吗?
如果您直接使用 Fabric.Image.fromURL()
将 svg 作为图像加载,您的 svg 图像不会降低质量,因为 <img>
标签用于 store svg 图像仍然在向量中。
但是,在应用图像滤镜时,滤镜会在 <canvas>
元素上以 img 标签的默认大小应用一次,这会导致图像光栅化。
一种骇人听闻的方法是首先将 svg 加载到对象或 iframe 或嵌入标记中,将所需的过滤器附加为 SVGFilter,然后将 svg 从 DOM 字符串加载到 Fabric 中.
但这有一些限制:
- 跨源请求被阻止,并且这些标签上没有
crossOrigin
属性。 - 您必须自己进行解析。
- 放大时 FF 中的图像框似乎存在错误...
无论如何,这里是代码和 a plunker example 现场展示。
var canvas = new fabric.Canvas('canvas');
var obj = document.createElement('object');
//make it invisible
obj.style.height = obj.style.width = 0;
// we have to append it so it does load
document.body.appendChild(obj);
obj.onload = function() {
// get the def node of our svgFilter
var defs = document.getElementById('def').cloneNode('true');
// get the doc of our object
var doc = obj.contentDocument || obj.contentWindow.documentElement;
// we can now remove the <object>
document.body.removeChild(obj);
// the target svg
var svg = doc.querySelector('svg');
svg.insertBefore(defs, svg.firstElementChild);
// set the filter on the whole svg
svg.setAttribute('filter', 'url(#desaturate)');
// encode it to a dataURI
var svgStr = new XMLSerializer().serializeToString(svg);
var svgURL = 'data:image/svg+xml; charset=utf8, ' + encodeURIComponent(svgStr);
// load it as an image
fabric.Image.fromURL(svgURL, function(img) {
canvas.add(img).renderAll();
});
// We could have tried with loadSVGFrom... but the filter isn't applied then
/*
fabric.loadSVGFromString(svgStr, function(objects, options) {
var loadedObject = fabric.util.groupSVGElements(objects, options);
canvas.add(loadedObject);
loadedObject.center().setCoords();
canvas.renderAll();
});
fabric.loadSVGFromURL(svgURL, function(objects, options) {
var loadedObject = fabric.util.groupSVGElements(objects, options);
canvas.add(loadedObject);
loadedObject.center().setCoords();
canvas.renderAll();
});
*/
};
obj.data = "yourFile.svg";
<!-- append your svg filter in the main doc -->
<svg id="filters" width="0" height="0">
<defs id="def">
<filter id="desaturate">
<feColorMatrix type="matrix" values="0.3333 0.3333 0.3333 0 0
0.3333 0.3333 0.3333 0 0
0.3333 0.3333 0.3333 0 0
0 0 0 1 0" />
</filter>
</defs>
</svg>
<canvas id="canvas" width="500" height="500"></canvas>
另一种解决方案是先在 up-scaled canvas 上绘制 svg,在其 dataURL 上应用过滤器,然后缩小图像,但这也会产生一些压缩伪影。 ..
var canvas = new fabric.Canvas('canvas');
var scaleSVG = function(url, scale, callback) {
var c = document.createElement('canvas');
var img = new Image();
img.crossOrigin = 'Anonymous';
img.onload = function() {
c.width = this.width * scale;
c.height = this.height * scale;
c.getContext('2d').drawImage(this, 0, 0, c.width, c.height);
callback(c.toDataURL());
};
img.src = url;
};
scaleSVG('https://dl.dropboxusercontent.com/s/b7qcju9ubmdtigj/ball.svg',
10, function(dataURL) {
fabric.Image.fromURL(dataURL, function(img) {
// scale in the other side
var oImg = img.scale(.1);
var filter = new fabric.Image.filters.Grayscale();
oImg.filters.push(filter);
canvas.add(oImg);
oImg.applyFilters(canvas.renderAll.bind(canvas));
}, {
crossOrigin: 'Anonymous'
});
});
<canvas id="canvas" width="500" height="500"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.5.0/fabric.js"></script>