Reputation: 333
I am using fabric.js with Meteor.
I have the following SVG image which I am using as a pattern on a path within a path group.
As you can see there is some transparency to it.
What I wish to do is to fill a path with this pattern, and then use a color picker to be able to manipulate it's colour. I have been able to do this so far by adding the SVG as a pattern, and then duplicating the path and filling that with a colour with 50% opacity, but of course this changes the whole area colour, not just the patterns colour. Basically since the pattern is already a 'fill' of a path, how would I fill the pattern itself?
Here's a diagram to better explain...
Here's the code I am using to add the pattern when a particular pattern is selected from a dropdown list (using Meteor event)...
'change .shapeTopPattern': function(e, template){ e.preventDefault();
var textureID = e.target.value;
if(textureID != '') { //If this shape has a texture selected.
var texture = Textures.findOne(textureID);
//Get the image
var textureIMG = new Image;
textureIMG.crossOrigin = "anonymous";
textureIMG.src = texture.image;
var patternURL = textureIMG.src;
fabric.Image.fromURL(patternURL, function(img) {
var obj = canvas.getActiveObject();
var paths = obj.paths;
paths.forEach(function(p) {
if (p.pathName == "shapeTopTexture") {
img.width = p.width;
img.height = p.height
var patternSourceCanvas = new fabric.StaticCanvas();
patternSourceCanvas.add(img);
patternSourceCanvas.setDimensions({
width: p.width,
height: p.height
});
var texture = patternSourceCanvas.getElement();
var pattern = new fabric.Pattern({
source: texture,
repeat: 'no-repeat',
offsetX: p.width/2,
offsetY: p.height/2
});
obj.setFill();
if (obj instanceof fabric.PathGroup) {
p.fill = pattern;
obj.perPixelTargetFind = false;
} else {
obj.setFill(pattern);
}
canvas.renderAll();
}
});
});
}
},
This is the function I am using on a change event of the color picker. "shapeTopPatternColour" is the duplicated path (top of the cylinder) from the cylinder path-group. I know that this is obviously why the colour is going over the entire area instead of only the pattern, but it's the closest I got to what I wanted.
changeTopPatternColour = function(o, color, opacity) {
/*
* o = object
* color = rgbcolor data
* opacity = .5
*/
var paths = o.paths;
paths.forEach(function(p) {
if (p.pathName == "shapeTopPatternColour") {
p.setFill(color);
p.opacity = opacity;
}
});
canvas.renderAll();
};
So how can I...
Thanks in advance.
Upvotes: 2
Views: 4100
Reputation: 10211
Unfortunately I could not find a way to use as a pattern an svg file without having necessarily to rasterize it, I am sorry. In the example below you can see for yourself that I always entirely rewrite the surfaces. Instead of loading the svg like an image, I load the svg normally and then I add it to the canvas using a pathgroup. For this snippet I am using your files as strings to avoid annoying CORS issues.
Here you can find the bitbucket repository
All the best.
$(function() {
var canvas = new fabric.Canvas('c');
canvas.setWidth(600);
canvas.setHeight(370);
var tint = function(color) {
canvas.clear();
var svgObjectString = '<?xml version="1.0" encoding="utf-8"?>' +
'<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"' +
' viewBox="0 0 596 366" style="enable-background:new 0 0 596 366;" xml:space="preserve">' +
'<style type="text/css">' +
'.st0{fill:#E0E0E0;}' +
' .st1{fill:url(#SVGID_1_);}' +
'</style>' +
'<path class="st0" d="M596,103.5c0,32-41.7,60.5-107.2,79.5c-51.7,15-118.2,24-190.8,24s-139.1-9-190.8-24C41.7,164,0,135.4,0,103.5' +
'c0-0.2,0-0.3,0-0.5C0.8,46.1,133.9,0,298,0s297.2,46.1,298,103C596,103.2,596,103.3,596,103.5z"/>' +
'<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="0" y1="133.25" x2="596" y2="133.25" gradientTransform="matrix(1 0 0 -1 0 368)">' +
'<stop offset="0" style="stop-color:#E8E8E8"/>' +
'<stop offset="0.23" style="stop-color:#878787"/>' +
'<stop offset="0.6" style="stop-color:#D6D6D6"/>' +
'<stop offset="1" style="stop-color:#ADADAD"/>' +
'</linearGradient>' +
'<path class="st1" d="M596,103.5v159c0,0.2,0,0.3,0,0.5c-0.8,56.9-133.9,103-298,103S0.8,319.9,0,263c0-0.2,0-0.3,0-0.5v-159' +
'c0,32,41.7,60.5,107.2,79.5c51.7,15,118.2,24,190.8,24s139.1-9,190.8-24C554.3,164,596,135.4,596,103.5z"/>' +
'</svg>';
fabric.loadSVGFromString(svgObjectString, function(svgobject) {
//fabric.loadSVGFromURL('../../Content/object.svg', function (svgobject) {
var objGroup = new fabric.PathGroup(svgobject, {
width: 596,
height: 500
});
canvas.add(objGroup);
var svgPatternString = '<?xml version="1.0" encoding="utf-8"?>' +
'<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">' +
'<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"' +
' width="576px" height="576px" viewBox="0 0 576 576" enable-background="new 0 0 576 576" xml:space="preserve">' +
'<polygon fill="#848484" points="290.664,286.625 244.214,729.625 338.422,729.625 "/>' +
'<polygon fill="#848484" points="290.711,286.625 337.16,-156.375 242.953,-156.375 "/>' +
'<polygon fill="#848484" points="290.688,286.602 -152.312,240.152 -152.312,334.359 "/>' +
'<polygon fill="#848484" points="290.688,286.648 733.688,333.098 733.688,238.89 "/>' +
'<polygon fill="#848484" points="290.664,286.625 244.214,729.625 338.422,729.625 "/>' +
'<polygon fill="#848484" points="290.711,286.625 337.16,-156.375 242.953,-156.375 "/>' +
'<polygon fill="#848484" points="290.688,286.602 -152.312,240.152 -152.312,334.359 "/>' +
'<polygon fill="#848484" points="290.688,286.648 733.688,333.098 733.688,238.89 "/>' +
'<polygon fill="#848484" points="290.666,286.616 75.532,676.646 162.318,713.297 "/>' +
'<polygon fill="#848484" points="290.709,286.634 505.843,-103.396 419.057,-140.047 "/>' +
'<polygon fill="#848484" points="290.696,286.604 -99.334,71.469 -135.984,158.255 "/>' +
'<polygon fill="#848484" points="290.679,286.646 680.709,501.78 717.359,414.994 "/>' +
'<polygon fill="#848484" points="290.671,286.608 -59.646,561.716 5.948,629.336 "/>' +
'<polygon fill="#848484" points="290.704,286.642 641.021,11.534 575.427,-56.086 "/>' +
'<polygon fill="#848484" points="290.704,286.609 15.597,-63.709 -52.024,1.885 "/>' +
'<polygon fill="#848484" points="290.671,286.641 565.778,636.958 633.398,571.364 "/>' +
'<polygon fill="#848484" points="290.679,286.603 -137.942,407.811 -102.993,495.296 "/>' +
'<polygon fill="#848484" points="290.696,286.647 719.315,165.439 684.367,77.954 "/>' +
'<polygon fill="#848484" points="290.709,286.617 169.502,-142.004 82.016,-107.055 "/>' +
'<polygon fill="#848484" points="290.666,286.634 411.873,715.253 499.358,680.305 "/>' +
'</svg>';
fabric.loadSVGFromString(svgPatternString, function(svgpattern) {
//fabric.loadSVGFromURL('../../Content/pattern.svg',
// function (svgpattern) {
svgpattern.map(function(item) {
item.fill = color;
});
var ptnGroup = new fabric.PathGroup(svgpattern, {
top: -190,
width: 596,
height: 500
});
var paths = objGroup.paths;
var path = paths[0];
svgpattern.width = path.width;
svgpattern.height = path.height;
var patternSourceCanvas = new fabric.StaticCanvas();
patternSourceCanvas.add(ptnGroup);
patternSourceCanvas.setDimensions({
width: path.width,
height: path.height
});
var texture = patternSourceCanvas.getElement();
var pattern = new fabric.Pattern({
source: texture,
repeat: 'no-repeat',
offsetX: path.width / 2,
offsetY: path.height / 2
});
objGroup.setFill();
objGroup.globalCompositeOperation = "source-over";
path.setFill("#ff0000");
path.fill = pattern;
objGroup.perPixelTargetFind = false;
canvas.renderAll();
});
});
}
$('button')
.on('click',
function(e, args) {
tint(e.target.value);
});
});
canvas {
border: 1px solid #ccc;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.2/fabric.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<div class="row">
<div class="col-md-7">
<canvas id="c" style="border: solid 1px #ccc;"></canvas>
</div>
<div class="col-md-5">
<button class="btn-danger" value="red">red</button>
<button class="btn-success" value="green">green</button>
<button class="btn-primary" value="blue">blue</button>
</div>
</div>
</div>
Edit: to retrieve the dimensions of the top of your cylinder, I am using the first path of svgobject
var paths = objGroup.paths;
var path = paths[0];
svgpattern.width = path.width;
svgpattern.height = path.height;
var patternSourceCanvas = new fabric.StaticCanvas();
patternSourceCanvas.add(ptnGroup);
patternSourceCanvas.setDimensions({
width: path.width,
height: path.height
});
Upvotes: 3