Implementing ClipTo function on fabricjs canvas - javascript

I am able to clip my canvas to a shape if I hardcode the SVG when the canvas initially loads. Now I am trying to do this with a click function. The challenge is that since everything has loaded, when I click on it and load the clipping function, it clears my canvas and leaves just the shape and background. I'm looking for ideas on how to implement this. I only know how to load the opts in a new fabric.canvas function. I suspect I will have to get the current canvas data and then apply the opts parameter to it, but I am not sure the best way to do it. Here is my code:
var canvas = new fabric.Canvas('imageCanvas');
$('#shape').on('click', function(){
var clipPath = new fabric.Path("M161.469,0.007 C161.469,0.007 214.694,96.481 214.694,96.481 C214.694,96.481 322.948,117.266 322.948,117.266 C322.948,117.266 247.591,197.675 247.591,197.675 C247.591,197.675 261.269,306.993 261.269,306.993 C261.269,306.993 161.469,260.209 161.469,260.209 C161.469,260.209 61.667,306.993 61.667,306.993 C61.667,306.993 75.346,197.675 75.346,197.675 C75.346,197.675 -0.010,117.266 -0.010,117.266 C-0.010,117.266 108.242,96.481 108.242,96.481 C108.242,96.481 161.469,0.007 161.469,0.007", ),
opts = {
controlsAboveOverlay: true,
backgroundColor: 'rgb(255,255,255)',
clipTo: function (ctx) {
if (typeof backgroundColor !== 'undefined') {
ctx.fillStyle = backgroundColor;
ctx.fillRect(0, 0, 900, 900);
}
clipPath.render(ctx);
}
}
//obviously this is not going to work
var reloadShape = JSON.stringify(canvas);
canvas.loadFromJSON(reloadShape);
new fabric.Canvas('imageCanvas', opts);
});

You should initialize canvas just once in your application, otherwise you will loose content of it.
Later when you choose your clipping path create and assign your clipTo function.
if not needed any other processing you could also do just
canvas.clipTo = clipPath._render;
without creating the new function.
//do this once on your application:
var opts = {
controlsAboveOverlay: true,
backgroundColor: 'rgb(255,255,255)',
},
canvas = new fabric.Canvas('imageCanvas', opts);
$('#shape').on('click', function(){
var clipPath = new fabric.Path("M161.469,0.007 C161.469,0.007 214.694,96.481 214.694,96.481 C214.694,96.481 322.948,117.266 322.948,117.266 C322.948,117.266 247.591,197.675 247.591,197.675 C247.591,197.675 261.269,306.993 261.269,306.993 C261.269,306.993 161.469,260.209 161.469,260.209 C161.469,260.209 61.667,306.993 61.667,306.993 C61.667,306.993 75.346,197.675 75.346,197.675 C75.346,197.675 -0.010,117.266 -0.010,117.266 C-0.010,117.266 108.242,96.481 108.242,96.481 C108.242,96.481 161.469,0.007 161.469,0.007", );
canvas.clipTo = function (ctx) {
if (typeof backgroundColor !== 'undefined') {
ctx.fillStyle = backgroundColor;
ctx.fillRect(0, 0, 900, 900);
//for clipping _render would be enough.
// but .render() will allow you to position the path where you want with top and left
clipPath.render(ctx);
}
// display new canvas clipped.
canvas.renderAll();
});

Related

HTML canvas saving on mysql database

I'm stuck with my code.
Problem: I have canvas and inside it I draw the lines. And after I finished I want that lines to stay in the right place where i left that(before reload website). So I need to send that canvas to mysql data base. But here I stuck. Did I first need to create .png image and then try to send that image information to database? or somehow I can send it right off from code to database by using AJAX? I read a lot of information and I am confused right now.
If I will use method HTMLgetImageData() and HTMLputImageData() then I need to create some real image in my server? or I can take straight from the canvas? and send to mysql databse? :)
so now I have Canvas in html and some script for drawing the lines:
$(".widget_body").on("mousedown", "canvas", function() {
var id = $(this).attr("id");
var canvas = document.getElementById(id);
var canvas,
context,
dragging = false,
dragStartLocation,
snapshot;
fitToContainer(canvas);
function fitToContainer(canvas){
// Make it visually fill the positioned parent
canvas.style.width ='100%';
canvas.style.height='100%';
// ...then set the internal size to match
canvas.width = canvas.offsetWidth;
canvas.height = canvas.offsetHeight;
}
function getCanvasCoordinates(event) {
var x = event.clientX - canvas.getBoundingClientRect().left,
y = event.clientY - canvas.getBoundingClientRect().top;
return {x: x, y: y};
}
function takeSnapshot() {
snapshot = context.getImageData(0, 0, canvas.width, canvas.height);
}
function restoreSnapshot() {
context.putImageData(snapshot, 0, 0);
}
function drawLine(position) {
context.beginPath();
context.moveTo(dragStartLocation.x, dragStartLocation.y);
context.lineTo(position.x, position.y);
context.stroke();
}
function dragStart(event) {
dragging = true;
dragStartLocation = getCanvasCoordinates(event);
takeSnapshot();
}
function drag(event) {
var position;
if (dragging === true) {
restoreSnapshot();
position = getCanvasCoordinates(event);
drawLine(position);
}
}
function dragStop(event) {
dragging = false;
restoreSnapshot();
var position = getCanvasCoordinates(event);
drawLine(position);
}
function clearCanvas(event) {
context.clearRect(0, 0, canvas.width, canvas.height);
}
context = canvas.getContext('2d');
context.strokeStyle = 'purple';
context.lineWidth = 4;
context.lineCap = 'round';
canvas.addEventListener('mousedown', dragStart, false);
canvas.addEventListener('mousemove', drag, false);
canvas.addEventListener('mouseup', dragStop, false);
canvas.addEventListener('dblclick', clearCanvas, false);
});
Maybe somebody can suggest something to me? Maybe something about next steps?What should I have to do from this moment?
Well, it depends on whether you're saving the Canvas as a single image or if you're saving each component of it (such as lines, squares, etc).
If you're saving it as a single image, it will be easier to just save the Data URL to your database. Otherwise, create JavaScript objects containing the properties and values of each shape, e.g.:
var line =
{
Name: "Line",
Color: "#3D4AEE",
Shadow: "NULL"
Length: "",
Point: "130, 120"
}
Then convert the object into a JSON String:
var JSONLine = JSON.stringify(line);
Now you have something you can insert into the database.
Now, when you need to retrieve this from the database, so you can redraw it in the browser, all you need to do is lookup the "design", get all the bits that make up that design and redraw them to the Canvas, using the properties of the shapes that you saved.
I'll leave it up to you to figure out how to structure your database to accommodate the different types of shapes, and their relationships to "designs" that are created.
1. You could save the coordinates in a database without reloading the page using AJAX and then fetch the coordinates via AJAX and set them dynamicly in the Javascript. If you want to use a JS Library that makes AJAX-requests easier to use, I recommend jQuery http://api.jquery.com/jquery.ajax/
2. You could convert the canvas to an image using something like
function convertCanvasToImage(canvas) {
var image = new Image();
image.src = canvas.toDataURL("image/png");
return image;
}
And then save the image in a database. However, you won't be able to change the canvas this way, it will be an image. The first way allows you to save the canvas as it is with it's information. Kind of like Photoshop and a .PSD file.
Firstly, you should use Canvas.toDataURL export the data. After that, you can send the data with a FormData via Fetch API.
var fd = new FormData();
fd.append('field', canvas.toDataURL('image/jpg'), 'sketch.jpg');
fetch('/saveSketch', {
method: 'POST',
body: fd,
});
On server side, you need to parse this FormData to retrieve the file. At this time, your files are already available for being saved into database or filesystem.

HTML5 Frame by frame animation (multiple images)

I've been losing my mind over this. I spent 3 hours trying different methods and finding a solution online, and I still haven't fixed it.
I have two separate images(not a spritesheet) and they need to be displayed one after the other, as an animation, infinitely. Here's is my latest code:
var canvas, context, imageOne, imageTwo, animation;
function init(){
canvas = document.getElementById("canvas");
context = canvas.getContext("2d");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
imageOne = new Image();
imageTwo = new Image();
imageOne.src = "catone.png";
imageTwo.src = "cattwo.png";
// Just to make sure both images are loaded
setTimeout(function() { requestAnimationFrame(main);}, 3000);
}
function main(){
animation = {
clearCanvas: function(){
context.clearRect(0, 0, canvas.width, canvas.height);
},
renderImageOne: function(){
context.drawImage(imageOne, 100, 100);
},
renderImageTwo: function(){
context.drawImage(imageTwo, 100, 100);
}
};
animation.renderImageOne();
// I also tried calling animation.clearCanvas();
context.clearRect(0, 0, canvas.width, canvas.height);
animation.renderImageTwo();
// I put this here to confirm that the browser has entered the function, and that it hasn't stopped after animation.renderImageTwo();
console.log("cats");
requestAnimationFrame(main);
}
init();
But the problem is that the only one image is displayed, and it's not moving. I can't see any errors or warnings in the console. I'm also sure HTML and JavaScript are connected properly and the images are in the right path. So in any case, only the image in the first function is displayed. Example: animation.renderImageOne(); displays catone, but if I replace it with animation.renderImageTwo(); it displays cattwo.
The problem is here:
animation.renderImageOne();
// I also tried calling animation.clearCanvas();
context.clearRect(0, 0, canvas.width, canvas.height);
animation.renderImageTwo();
Is it's drawing the first image, clearing the canvas, then drawing the second image, then after all that it draws to the screen. Leaving you with only seeing the second image. You will need a variable that alternates values, and use that to determine which picture you should draw:
var canvas, context, imageOne, imageTwo, animation;
var imageToDraw = "one";
And then:
function main() {
...
if(imageToDraw == "one") {
animation.renderImageOne();
imageToDraw = "two";
}
else if(imageToDraw == "two") {
animation.renderImageTwo();
imageToDraw = "one";
}
...
}
Note: You don't need to define animation inside main(), you can move it into global scope. That way you don't redefine it each time you call main().

replacing an image in canvs - javascript

I'm attempting to make a very simple meme generator, whereby you select the image you want to add text onto using a drop down menu. I can successfully select the image I want and display it within the canvas, however when I change my selection, instead of changing the image it adds it over the first image.
I'd like to be able to chose the image, and if i change my mind it simply replaces the current image. Any suggestions you might have would be very welcome!
Here's my code:
window.onload = function init(){
var selector = document.getElementById('selector');
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var imageObj = new Image();
var imagesObj = {
"fry" : ['images/fry.jpg'],
"badluck" : ['images/badluck.jpg'],
"success" : ['images/success.jpg']
};
selector.addEventListener('change', function(){
imagesObj[selector.value].forEach(function(item){
imageObj.onload = function() {
context.drawImage(imageObj, 0, 0);
};
imageObj.src = item;
});
});
}
It looks like this did the trick:
context.clearRect(0, 0, 298, 350);

Custom image angles in collage

I am working on a collage in HTML5 canvas. However, I am finding difficulty in arranging the images in different angles. I want to arrange first pic at angle of PI/4 and the other one at angle -PI/70. Here is the jsFiddle with the problem.
var pic1 = new Image();
pic1.src = "http://www.fantom-xp.com/wallpapers/23/Windows_7_-_Swan.jpg";
context.translate(170,170);
context.rotate(Math.PI/8);
pic1.onload = function(){
context.drawImage(pic1, 20, 20, 200, 200);
}
var pic2 = new Image();
pic2.src = "http://www.redorbit.com/media/uploads/2004/10/38_ec8164eb3e4bddf76ef1b8eb564b9514.jpg";
context.translate(100,10);
context.rotate(-Math.PI/70);
pic2.onload = function(){
context.drawImage(pic2, 0, 0, 200, 200);
}
What am I missing?
Assuming you want to rotate your images around their center points you need to use this drawImage:
context.drawImage(image, -image.width/2, -image.height/2).
That's because the translate point becomes the rotation point.
Here's a generic image rotation function:
function tiltedPicture(centerX,centerY,degreeAngle,image){
ctx.save();
ctx.translate(centerX,centerY);
ctx.rotate(degreeAngle*Math.PI/180);
ctx.drawImage(image,-image.width/2,-image.height/2);
ctx.restore();
}
There are some issues in your code.
First of all, your first image couldn't be loaded. You will see it when you add:
pic1.onerror = function() {
console.log('Error loading');
}
Next you should use save and restore methods. Read here.
The problem is when you call twice context.translate(170,170); you will get th final translation at x: 340, y: 340. If you will combine more complex transformaions you could get result which is hard to predict. Fortunatelly there are methods save and restore. Save - saves current transformation state, and Restore - restores last saved state.
Usage in your case (for the first pic):
pic1.onload = function(){
context.save();
context.translate(170,170);
context.rotate(Math.PI/2);
context.drawImage(pic1, 20, 20, 200, 200);
context.restore();
}
pic1.onerror = function() {
console.log('error loading');
}
See demo.

Removing an image from a canvas in HTML5

there's an example, which loads 2 images:
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
var img1 = new Image();
img.src = "/path/to/image/img1.png";
img.onload = function() {
ctx.drawImage(img, 0, 0);
};
var img2 = new Image();
img2.src = "/path/to/image/img2.png";
img2.onload = function() {
ctx.drawImage(img2, 100, 100);
};
I need to remove(replace) img2 from canvas. What is the best way to do it?
I think maybe you misunderstand what a Canvas is.
A canvas is essentially a 2 dimensional grid of pixels along an 'X' axis and a 'Y' axis. You use the API to draw pixels onto that canvas, so when you draw an image you're basically drawing the pixels that make up that image onto your canvas. The reason there is NO method that lets you just remove an image, is because the Canvas doesn't know there's an image there in the first place, it just see pixels.
This is unlike the HTML DOM (Document Object Model) where everything is a HTML element, or an actual 'thing' you can interact with, hook-up script events to etc. this isn't the case with stuff you draw onto a Canvas. When draw a 'thing' onto a Canvas, that thing doesn't become something you can target or hook into, it's just pixels. To get a 'thing' you need to represent your 'thing' in some way such as a JavaScript object, and maintain a collection of these JS objects somewhere. This how how Canvas games work. This lack of a DOM-like structure for Canvas makes rendering very fast, but can be a pain for implementing UI elements that you can easily hook into and interact with, remove etc. For that you might want to try SVG.
To answer your question, simply paint a rectangle onto your Canvas that covers up your image by using the same X/Y coords and dimensions you used for your original image, or try Pointy's solution. 'Cover-up' is probably the wrong terminology, since you're actually replacing the pixels (there are no layers in Canvas).
It's not clear what you want the canvas to show when the image is gone. If you want it to be transparent, you could get the image data and fill it with transparent pixels:
var img = ctx.createImageData(w, h);
for (var i = img.data.length; --i >= 0; )
img.data[i] = 0;
ctx.putImageData(img, 100, 100);
where "w" and "h" would be the width and height of your original image.
edit — if you just want another image there, why not just put one there? It will overwrite whatever pixels are there on the canvas.
You can use clearRect() function to clear the image area.Rather then clearing whole context you can clear only the image area using this:
ctx.clearRect(xcoordinate_of_img1,ycoordinate_of_img1,xcoordinate_of_img1 + img1.width ,ycoord_of_img1 +img1.height );
If what "Sunday Ironfoot" said is right, then the best way to remove an image is by drawing the images once again from scratch. For this, you need to have an array of images and draw only the ones you use. For example,
function EmptyClass{};
var img=new Array();
img[0]=new EmptyClass;
img[0].i=new Image();
img[0].src="yourfile1.jpg";
img[0].enabled=true;
img[1]=new EmptyClass;
img[1].i=new Image();
img[1].src="yourfile2.jpg";
img[1].enabled=false;// <-------- not enabled, should not be drawn equivalent to removing
img[2]=new EmptyClass;
img[2].i=new Image();
img[2].src="yourfile3.jpg";
img[2].enabled=true;
for(var i=0;i<3;i++){
if(img[i].enabled)ctx.drawImage(img[i], 100, 100);
}
P.S. I am creating an engine for javascript canvas. Will post it within a week
Peace
You can erase an image by drawing the same image again, using a different globalCompositeOperation
ctx.globalCompositeOperation ="xor"
ctx.drawImage(img2, 100, 100);
See https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation
Unlike drawing things yourself, if you 'replace' THE image on a canvas, the old one is still there.
Canvas c2;
...
if (null != Image2) {
var ctx = c2.getContext("2d");
ctx.clearRect(0, 0, c2.width, c2.height);
}
Can you overlay canvas objects (I guess I should try before asking, you can -1 one me for being lazy). I guess I'd be interested in have one canvas element as a background, and then another for a layer objects that pop in and out of view. Might be a little more efficient then having to redraw every image if one gets deleted or moved. I'll play around and see what I can find.
There is ``ctx.clearRect(x, y, w, h)'' but this is not a good way to remove the shape, because it will remove any full or partial shapes in the same area of the removed shape. This shouldn't happen, and may remove one or more shapes, I've found it's best to save all your shapes in a list that usually comes from the database using backend language or ajax request, and add for it's shape object an identifier, when you need to remove a shape just remove that shape from the list using the id or the index, then Redraw the canvas with this new array of shapes without a deleted shape, the next time the page loads, this shape will not be added to this list, because it should be deleted from database.
const projectStamps = [{image_id: 'scream', x: 100, y: 100, id: 1}, {image_id: 'scream', x: 100, y: 100, id: 2}, {image_id: 'scream', x: 50, y: 0, id: 3}, {image_id: 'scream', x: 150, y: 0, id: 4}];
let currentShapes = [];
const canvas = document.getElementById("myCanvas");
const ctx = canvas.getContext("2d");
function validStampObj(stamp){
if (typeof(stamp.x) !== 'number' || typeof(stamp.y) !== 'number' || typeof(stamp.image_id) === 'undefined' || !document.getElementById(stamp.image_id)){
return false;
} else {
return true;
}
}
function addStamp(stamp){
if (!validStampObj(stamp)){
console.log("can not add stamp, invalid object");
return false;
}
const image = document.getElementById(stamp.image_id);
stamp['w'] = image.getBoundingClientRect().width;
stamp['h'] = image.getBoundingClientRect().height;
ctx.drawImage(image, stamp.x, stamp.y, stamp.w, stamp.h);
currentShapes.push(stamp);
return stamp;
}
let id = 1;
window.onload = function() {
drawProject();
};
function clearCanvas(){
currentShapes = [];
ctx.clearRect(0, 0, canvas.width, canvas.height);
return true;
}
const projectImage = document.getElementById("project_image");
function drawProject(){
if (!projectImage){console.log('missing project image element');return false;}
clearCanvas();
ctx.drawImage(projectImage,0,0);
projectStamps.forEach( (stamp)=>{
addStamp(stamp);
});
}
function removeStamp(targetId){
let targetI = false;
for (let i=0; i<projectStamps.length; i++){
if (projectStamps[i].id == targetId){
targetI = i;
break;
}
}
if (targetI !== false){
/* remove the stamp from drawing stamps list and redraw the data */
projectStamps.splice(targetI,1);
drawProject();
}
}
setTimeout( ()=>{
removeStamp(3);
console.log("removed icon with id 3");
}, 2500 );
<p>Image to use:</p>
<img id="scream" width="35" height="35"
src="https://i.ibb.co/wYyc259/iconimage.png" alt="The Scream">
<img id="project_image" width="450" height="300"
src="https://i.ibb.co/sK5HtQy/bulding-image.png" style="position:absolute;left:-15455px;">
<p>Canvas:</p>
<button onclick="drawProject()">Redraw things</button>
<canvas id="myCanvas" width="450" height="300"
style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.
</canvas>
notes if you used clearRect in this example it will remove the part of main image of the canvas not just the icon with id 3 like this code does hope it helps.

Categories