I am building an app in Rails, and I am using a gem called easy_as_pie which enables me to use this Jquery plugin called 'Easy Pie Chart' (http://rendro.github.io/easy-pie-chart/)
I have the pie chart working no problem, using the following code:
$(document).ready(function() {
$('.chart').easyPieChart({
size: 300,
animate: 1400,
lineWidth: 150,
lineCap: "butt",
scaleColor: false,
trackColor: "black",
barColor: "white"
});
});
The question I have, is whether not it would be possible to have the chart load a background image instead of a solid colour. In the documentation it allows you to use a gradient with a function, using the following code:
new EasyPieChart(element, {
barColor: function(percent) {
var ctx = this.renderer.ctx();
var canvas = this.renderer.canvas();
var gradient = ctx.createLinearGradient(0,0,canvas.width,0);
gradient.addColorStop(0, "#ffe57e");
gradient.addColorStop(1, "#de5900");
return gradient;
}
});
I was hoping to get the gradient working, then work on trying to manipulate the function to load in an image. But I have been unsuccessful in even getting the gradient to work.
Provided your gradient example works like it looks, you should be able to draw just about anything you want into the canvas object they've provided. With that in mind, you might be able to draw images like so:
new EasyPieChart(element, {
barColor: function(percent) {
var ctx = this.renderer.ctx();
var canvas = this.renderer.canvas();
var yourImage = new Image();
yourImage.src = "path-to-your-img.jpg";
return ctx.drawImage(yourImage);
}
});
However, Image objects, like <img> tags, require a further GET request to the server to actually load them, so the above probably won't actually work (sorry for the tease). Instead, you'll have to wait for that image to load before calling it, for example:
// Instantiate the image, it's blank here.
var yourImage = new Image();
// Add a callback that uses the fully-loaded img
yourImage.onload = function() {
new EasyPieChart(element, {
barColor: function(percent) {
var ctx = this.renderer.ctx();
var canvas = this.renderer.canvas();
return ctx.drawImage(yourImage);
}
});
};
// Set the src, that queues it for the actual GET request.
yourImage.src = "path-to-your-img.jpg";
Related
I want to add draw shapes on a Image, so decided to use Fabric JS. I am able to get all shapes and objects working well. Now How do I background set exactly to Image. The Image is dynamic, I want to draw on top of that image, and basically store the annotations of objects for backend processing.
Existing solution using Canvas
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d");
canvas.width = 903;
canvas.height = 657;
var background = new Image();
background.src = "http://www.samskirrow.com/background.png";
// Make sure the image is loaded first otherwise nothing will draw.
background.onload = function(){
ctx.drawImage(background,0,0);
}
Reference - https://stackoverflow.com/a/14013068/2094670
I like this solution.
Live Demo
My Current Fabric JS Code
// Initialize a simple canvas
var canvas = new fabric.Canvas("c", {
hoverCursor: 'pointer',
selection: true,
selectionBorderColor: 'green',
backgroundColor: null
});
// Define the URL where your background image is located
var imageUrl = "../dog.jpg";
// Define
canvas.setBackgroundImage(imageUrl, canvas.renderAll.bind(canvas), {
// Optionally add an opacity lvl to the image
backgroundImageOpacity: 0.5,
// should the image be resized to fit the container?
backgroundImageStretch: false
});
Html
<canvas id="c"></canvas>
Problem.
The image of dog is full hd image, however I see only portion of it. How to do render the background image to its actual height and width using Fabric Canvas ? Since Images are comming dynamically, I might not know exact height and width to hardcode it.
Here's the way to set canvas size base on image size via fabric.js.
It's done initiating the Fabric canvas inside the onload callback, that way we are able to get the image width and height, then set them into the setDimesions method.
img.onload = function () {
...
canvas.setDimensions({ width: img.width, height: img.height });
};
var imageUrl = "http://i.imgur.com/yf6d9SX.jpg";
var background = new Image();
background.src = imageUrl;
// wait for the image to load, then set Fabric Canvas on the callback that runs after image finishes loading
background.onload = function () {
var canvas = new fabric.Canvas("c", {
hoverCursor: "pointer",
selection: true,
selectionBorderColor: "green",
backgroundColor: null,
});
// set canvas width and height based on image size
canvas.setDimensions({ width: background.width, height: background.height });
canvas.setBackgroundImage(imageUrl, canvas.renderAll.bind(canvas), {
// Optionally add an opacity lvl to the image
backgroundImageOpacity: 0.5,
// should the image be resized to fit the container?
backgroundImageStretch: false,
});
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/4.1.0/fabric.min.js"></script>
<canvas id="c"></canvas>
I'm using Konva.js to do some canvas animations. I have circle shapes, with an image fill, and would like to apply a color overlay/filter to the shape (RGBA).
This is how I'm creating the Shape object:
var konvaObject = new Konva.Circle({
x: 100,
y: 100,
radius: 300,
stroke: this.color,
strokeWidth: 6,
fillPatternRepeat: 'no-repeat',
});
// load the image into the shape:
var imageObj = new Image();
imageObj.onload = function () {
konvaObject.fillPatternImage(imageObj);
konvaObject.draw();
}
imageObj.src = 'www.demo.com/anImageName.png';
demo: http://jsbin.com/winugimeme/edit?js,output
The Docs outline an RGBA filter, however as far as I can tell it can only be applied to Konva.Image items.
Is there a way to re-work my above code so that I can apply filters to the shape object/fill image?
According to filter documentation, you have to cache shape before applying filters http://konvajs.github.io/api/Konva.Filters.html#RGBA
node.cache();
node.filters([Konva.Filters.RGBA]);
node.blue(120);
node.green(200);
node.alpha(0.3);
Note: jsbin demo will not work with this example as fill image should be CORS enabled (e.g. hosted on same domain).
I'm attempting to add a loaded image into a fabric Group object. Everything looks ok, but the selection controls aren't selectable and I can't drag the object around. The top left control works though and after clicking it everything is fine.
Here is a jsfiddle that demonstrates the behavior.
var canvas = new fabric.Canvas('canvas', {
width: 200,
height: 200
});
var group = new fabric.Group();
canvas.add(group);
fabric.Image.fromURL('https://placehold.it/100x100', function(img) {
group.addWithUpdate(img);
canvas.setActiveObject(group);
canvas.renderAll();
});
Is this a bug or am I doing something wrong?
For some performance reason, fabricjs does not call setCoords automatically after adding objects to a group ( in case of many object added you can call setCoords just once ).
So after doing addWithUpdate, just call group.setCoords();
var canvas = new fabric.Canvas('canvas', {
width: 200,
height: 200
});
var group = new fabric.Group();
canvas.add(group);
fabric.Image.fromURL('https://placehold.it/100x100', function(img) {
group.addWithUpdate(img);
group.setCoords();
canvas.setActiveObject(group);
canvas.renderAll();
});
I came across this post because I was having trouble getting the positioning of images to work properly. I ended up finding that it's easier to just create create an image element using document.createElement and set the src, then feed that into fabric.Image with all the options you need (instead of using fabric.Image.fromURL which was too much of a headache to use), before adding it to the group.
var oImg = document.createElement("img");
oImg.setAttribute('src', 'https://upload.wikimedia.org/wikipedia/commons/thumb/9/92/Cog_font_awesome.svg/512px-Cog_font_awesome.svg.png');
var i = new fabric.Image(oImg, {
originX: 'center',
originY: 'center',
left: left+35,
top: top-30,
scaleX:.05,
scaleY:.05,
});
g = new fabric.Group([r, t, i]); // where r and t are other fabric objects
I am creating an application for T-shirt customization in which I have put the canvas on image using CSS, but the problem is saving that image as canvas. toDataURL just gives part of the canvas area, but I want the whole image. There are other solutions on Stack Overflow but they do not solve this problem.
Hello,
you have to create an image object (tshirt) with a text object that holds the message.
to do that , load the image with fabric.Image.fromURL() function and inside the function , also create a text object that is going to show the tshirt message.
so, your image and text belong to a group object.
every time you want to load new text , you call the loadText function and you change the text object.
i also added 4 buttons in order to manupulate up/down/left/right the text .
you can export the canvas + image+ text into the function saveImg(),
but on the jsfiddle you will get a security message for Tained canvases.
that happens because on the example i load the image from another domain and the code runs on another domain, you can run that code on your web application with no problem at all.
that is the code :
var canvas = new fabric.Canvas('c');
var scaleFactor=0.4
canvas.backgroundColor = 'yellow';
canvas.renderAll();
var myImg = 'http://izy.urweb.eu/files/tshirt.jpg';
fabric.Image.fromURL(myImg, function(myImg) {
var img1 = myImg.scale(scaleFactor).set({ left: 0, top: 0 });
var text = new fabric.Text('the_text_sample\nand more', {
fontFamily: 'Arial',
fontSize:20,
});
text.set("top",myImg.height*scaleFactor-myImg.height*scaleFactor+150);
text.set("left",myImg.width*scaleFactor/2-text.width/2);
var group = new fabric.Group([ img1,text ], { left: 10, top: 10 });
canvas.add(group);
});
$('#loadText').on('click',loadText);
$('#saveImg').on('click',saveImg);
function loadText(){
console.log($('#logo').val());
canvas._objects[0]._objects[1].text = $('#logo').val();
canvas.renderAll();
}
function saveImg(){
console.log('export image');
if (!fabric.Canvas.supports('toDataURL')) {
alert('This browser doesn\'t provide means to serialize canvas to an image');
}
else {
window.open(canvas.toDataURL('png'));
}
}
$('#left').on('click',function(){
canvas._objects[0]._objects[1].set('left',canvas._objects[0]._objects[1].left-1);
canvas.renderAll();
})
$('#right').on('click',function(){
canvas._objects[0]._objects[1].set('left',canvas._objects[0]._objects[1].left+1);
canvas.renderAll();
})
$('#top').on('click',function(){
canvas._objects[0]._objects[1].set('top',canvas._objects[0]._objects[1].top-1);
canvas.renderAll();
})
$('#bottom').on('click',function(){
canvas._objects[0]._objects[1].set('top',canvas._objects[0]._objects[1].top+1);
canvas.renderAll();
})
that is the jsfiddle example: http://jsfiddle.net/tornado1979/zrazuhcq/1/
hope helps, good luck.
My problem started with the version 5 of KineticJS, before that it was not a problem. Native KineticJS shapes such as squares and circles can be saved to an image file using the stage.toDataURL function. But it doesn't work for non-Kinetic shapes drawn with normal canvas methods such as beginPath(); and canvas.fill(); (version 4 did this fine). The following code draws two rectangles, one red and one blue. The red is custom, the blue is a native kinetic rectangle.
<body>
<div id="container"></div>
<button id="save">
Save as image
</button>
<script src="http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v5.0.2.min.js"> </script>
<script>
var stage = new Kinetic.Stage({
container: 'container',
width: 578,
height: 200
});
var layer = new Kinetic.Layer();
var box = new Kinetic.Rect({
x: 400,
y: 80,
width: 100,
height: 50,
fill: '#00D2FF',
stroke: 'black',
strokeWidth: 4,
draggable: true
});
layer.add(box);
stage.add(layer);
var canvas = layer.getCanvas().getContext('2d');
canvas.beginPath();
canvas.setAttr('strokeStyle', 'black');
canvas.setAttr('fillStyle', '#FF2222');
canvas.setAttr('lineWidth', 8);
canvas.rect(50,80,100,50);
canvas.stroke();
canvas.fill();
document.getElementById('save').addEventListener('click', function() {
stage.toDataURL({
callback: function(dataUrl) {
window.location.href = dataUrl;
}
});
}, false);
</script>
</body>
Both shapes appear, but only the blue rectangle appears in the image generated by the toDataURL function. The way they are drawn has changed in KineticJS 5, where you set attributes for fillStyle etc. so I'm thinking that may have something to do with it, or maybe the fact that the custom shape is added after the layer is added to the stage...
You are correct, between recent versions much has changed, and this has probably broken something in your drawing function.
You should consult the official docs on each item, but basically a custom shape has slightly updated properties... first of all "StrokeStyle" is no longer a valid property. Just use 'stroke'. Same thing with FillStyle.
Also -- 'dashArray' is no longer valid, now it's just 'dash' -- so I'm sure there are more things that changed that I'm not recalling... right, such as 'lineWidth' is now 'strokeWidth'...
Also -- the way you show or don't show strokes and fills has changed... yep, pretty much most of the way you used to do it has been changed slightly. 'drawFunc' is now 'sceneFunc' also...
var ctx = layer.getContext();
var customShape01 = new Kinetic.Shape({
sceneFunc: function(ctx) {
ctx.beginPath();
ctx.moveTo(162.1, 213.8);
ctx.bezierCurveTo(162.1, 213.8, 180.7, 215.3, 193.5, 214.5);
ctx.bezierCurveTo(205.8, 213.7, 221.8, 212.3, 222.8, 221.4);
ctx.bezierCurveTo(222.9, 221.7, 222.9, 222.0, 222.9, 222.3);
ctx.bezierCurveTo(222.9, 232.4, 204.6, 232.7, 192.0, 227.1);
ctx.bezierCurveTo(179.4, 221.5, 163.1, 213.8, 162.1, 213.8);
ctx.closePath();
ctx.fillStrokeShape(this);
},
id: 'customShape01',
fill: 'rgb(255, 0, 255)',
stroke: 'black',
strokeWidth: 2,
lineJoin: 'round',
dash: [5,5],
dashEnabled: 'true',
strokeEnabled: 'true'
});
check out a full working sample (you'll have to allow popups).
http://jsfiddle.net/axVXN/1/