Fabric.js svg with multiple paths cliping to multiple images - javascript

When one svg file has multiple paths(lets say objects) I need to clip those paths to multiple images. I should maintain actual positions of the paths. When adding an image can I clip this image to selected path?
Currently I can only clip svg to one image. But Thats not what I want.
https://jsfiddle.net/hydride/wh35ymev/
var svgEl = document.body.getElementsByTagName('svg')[0];
var serializer = new XMLSerializer();
var svgStr = serializer.serializeToString(svgEl);
var canvas = new fabric.Canvas('c');
canvas.backgroundColor = 'rgb(150,150,150)';
var obj;
var path = fabric.loadSVGFromString(svgStr,function(objects, options) {
obj = fabric.util.groupSVGElements(objects, options);
obj.scaleToHeight(canvas.height);
obj.setFill('transparent');
canvas.add(obj).renderAll();
});
$("#addButton").click(function(){
fabric.Image.fromURL('http://fabricjs.com/assets/pug_small.jpg', function (img) {
img.scaleToHeight(canvas.height).set({
clipTo: function (ctx) {
obj.render(ctx);
}
});
canvas.add(img).setActiveObject(img);
canvas.renderAll();
});
});

Related

a-frame converts my SVG into a pixel-image

I have a < a-sphere> with an SVG wrapped around but it shows pixel. However, it seems like a-frame doesn't support Vectorgraphics.
Is there any solution to this?
I'd try drawing the svg onto a canvas element, and then turning the canvas into a texture using threejs:
html
<a-box svg-load></a-box>
js
AFRAME.registerComponent('svg-load', {
init: function() {
// grab the canvas element
var canvas = document.getElementById('my-canvas')
var ctx = canvas.getContext('2d');
// create an image, which will contain the svg data
var img = new Image();
// this will be triggered when the provided .svg is loaded
img.onload = () => {
// draw the image on the canvas
ctx.drawImage(img, 0, 0, 256, 256);
// create the texture and material
let texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
let material = new THREE.MeshBasicMaterial({ map: texture });
// grab the mesh, replace the material, dispose the old one
let mesh = this.el.getObject3D("mesh")
let tmp = mesh.material
mesh.material = material
tmp.dispose()
}
// provide the .svg file as the image source
img.src = "file.svg";
}
)}
Glitch here
You can also provide the canvas as a material source, but it still looks glitchy to me.
<a-box material="src: my-canvas">

How to resize object without scaling pattern in FabricJS?

I'm wondering if there is an option to resize object without scaling its pattern in FabricJS?
Link to Fiddle
var canvas = window._canvas = new fabric.Canvas('c');
fabric.Image.fromURL('https://assets-cdn.github.com/images/modules/site/integrators/slackhq.png', function(img) {
var patternSourceCanvas = new fabric.StaticCanvas()
patternSourceCanvas.add(img)
patternSourceCanvas.setBackgroundColor('red', patternSourceCanvas.renderAll.bind(patternSourceCanvas))
patternSourceCanvas.setHeight(256);
patternSourceCanvas.setWidth(256);
var pattern = new fabric.Pattern({
source: function() {
return patternSourceCanvas.getElement();
},
repeat: 'no-repeat'
})
// create a rectangle object
var rect = new fabric.Rect({
fill: pattern,
width: 256,
height: 256
})
// "add" rectangle onto canvas
canvas.add(rect)
})
Any idea ?
Add this little code below. This will rescale the background pattern for you while scaling.
canvas.observe("object:scaling", function (e) {
var shape = e.target;
shape.width = shape.scaleX * shape.width;
shape.height = shape.scaleY * shape.height;
shape.scaleX = 1;
shape.scaleY = 1;
});

Fabric.js add images order

I'm trying to add images to canvas strictly, but images are added randomly.
What's the problem?
Here is my simple code.
var canvas = new fabric.Canvas("preview");
var imgs = ['bgBottom','bgTop', 'bgLevel', 'bgCircle'];
for (var i=0; i<imgs.length;i++){
var url = imgs[i]+'.png';
fabric.Image.fromURL(url, function (oImg) {
canvas.add(oImg)
})
}
fabric.Image.fromURL loads the image in the background and runs the anonymous function you pass to it once image load is complete which adds it to the canvas. The order that the browser loads the images will vary and you can't rely on it being in a specific order.
Check out this jsfiddle that shows loading an array of images and making sure they're displayed in a set order. This works by adding the image to the canvas before it's loaded; it just doesn't render until the image is available to be displayed.
The code from the jsfiddle:
var SelfLoadingImage = fabric.util.createClass(fabric.Object, {
initialize: function(src) {
this.image = new Image();
this.image.src = src;
this.image.onload = (function() {
this.width = this.image.width;
this.height = this.image.height;
this.loaded = true;
this.setCoords();
this.fire('image:loaded');
canvas.renderAll();
}).bind(this);
},
_render: function(ctx)
{
if (this.loaded) {
ctx.drawImage(this.image, -this.width / 2, -this.height / 2);
}
}
});
var canvas = new fabric.Canvas("preview");
var imgs = [
'http://icons.iconarchive.com/icons/fasticon/ifunny/128/dog-icon.png', // dog
'http://33.media.tumblr.com/avatar_14ee6ada72a4_128.png', // cat
'http://upload.wikimedia.org/wikipedia/commons/b/bc/Nuvola_devices_mouse.png' // mouse
];
for (var i=0; i<imgs.length;i++){
var url = imgs[i];
var img = new SelfLoadingImage(url);
canvas.add(img);
}

canvg doesn't work with svg already placed in body?

I want to converty my SVG into CANVAS and then save it as image. I have svg already genereated by javascript in my page. I use this code:
$("#menu-save-image").click(function () {
var svg = document.getElementsByTagName('svg');
var canvas = document.getElementById("test");
canvg(canvas, svg);
// or second way
var c = document.getElementById('test');
var ctx = c.getContext('2d');
ctx.drawSvg(svg, 0, 0, 500, 500);
});
Both ways doesn't work. Why?
canvg method needs SVG source string (or url or XMLDocument), so you should convert the svg element to svg source by using XMLSerializer like this.
var svg = document.querySelector('svg');
var serializer = new XMLSerializer();
var svgString = serializer.serializeToString(svg);
var canvas = document.getElementById("test");
canvg(canvas, svgString);
see https://code.google.com/p/canvg/source/browse/trunk/canvg.js

Can't load images on canvas for my js game

I am making a mahjong game in js and I have a problem loading images on canvas
canvas
// Create the canvas
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = 512;
canvas.height = 512;
//draw canvas
document.body.appendChild(canvas);
Arrays
//Array with Tiles & set length to 144 which are the requested tiles
var tiles = new Array(2);//will be 144
//2D map array for showing the images coordinates
var map = [[69,50],[100,150]];//will be 144 coordinates
my function update()
function update(){
for(var i=0;i<tiles.length;i++){
//make the tile object
tiles[i] = new Object();
tiles[i].id = i ;
tiles[i].selected = false;
//set the coordinates from map Array
tiles[i].x=map[i][0];
tiles[i].y=map[i][1];
//tiles[i].ready=false;
//These are for the image location works fine
//convert i to String
var sourceNumber = i.toString();
//add .png to String
var source = sourceNumber.concat(png);
//add /image
var source = dest.concat(source);
tiles[i].img = new Image();
tiles[i].img.onload = function(){
//tiles[i].ready=true;
ctx.drawImage(tiles[i].img,tiles[i].x,tiles[i].y,xdimension,ydimension);
};
tiles[i].img.src = source ;
}
}
I runned it on each of my browser it won't load images , I debugged on chrome and it says on ctx.drawImage(...); -> Uncaught TypeError: Cannot read property 'img' of undefined(repeated many times), So I tried the tiles[I].ready and after load images but still has that error.Any suggestions on how should I implement the loading of the tile images
The first time ctx.drawImage is called, the value for i is 2. The problem is that the for-loop (for(var i=0;i<tiles.length;i++)) has finished executing before any of the images have loaded. Consequently, the value of i at the time the onload function is called is the value at which the loop ceased being run. The easiest way around this is to save the index (i) into the img element itself, so that you can retrieve it in the onload handler.
Here's a simple adaption of your code that seems to work just fine.
The important changes are:
tiles[i].img.iVal = i;
and the body of the onload handler.
I also:
(a) added an array to hold hard-coded image names for convenience, rather than dynamically creating them (I'd have had to name some images into the format that the code computes)
(b) removed the xdimension and ydimension vars from the drawImage call since I dont know what they are.
(c) changed .concat(png) to .concat(".png") since it was easier than declaring a variable called png that holds the string .png
Anyway, here's the sample-code I used:
<!DOCTYPE html>
<html>
<head>
<script>
window.addEventListener('load', onDocLoaded, false);
var canvas, ctx, tiles, map;
function onDocLoaded()
{
canvas = newEl('canvas');
ctx = canvas.getContext('2d');
canvas.width = canvas.height = 512;
document.body.appendChild(canvas);
tiles = new Array(2);
map = [[69,50],[100,150]];
update();
}
var imgFileNames = ["img/girl.png", "img/redbaron.png"];
function update()
{
for(var i=0;i<tiles.length;i++)
{
//make the tile object
tiles[i] = new Object();
tiles[i].id = i ;
tiles[i].selected = false;
//set the coordinates from map Array
tiles[i].x=map[i][0];
tiles[i].y=map[i][1];
//tiles[i].ready=false;
//These are for the image location works fine
//convert i to String
// var sourceNumber = i.toString();
//add .png to String
// var source = sourceNumber.concat(".png");
//add /image
// var source = dest.concat(source);
tiles[i].img = new Image();
tiles[i].img.iVal = i;
tiles[i].img.onload =
function()
{
var curI = this.iVal;
ctx.drawImage(tiles[curI].img,tiles[curI].x,tiles[curI].y);
// ctx.drawImage(this,tiles[curI].x,tiles[curI].y); //equiv to above line
};
tiles[i].img.src = imgFileNames[i];
}
}
</script>
<style>
canvas
{
border: solid 1px red;
}
</style>
</head>
<body>
</body>
</html>

Categories