How to retain object positions on resizing window in fabric.js - javascript

I wrote an app where I am trying to hide right eye of the dog using fabric.js rectangle.
But if I resize the window the rectangle is moved.
Here's the fiddle and code :
`
var canvas = this.__canvas = new fabric.Canvas('c');
canvas.setWidth(window.innerWidth*0.98);
canvas.setHeight(window.innerHeight*0.98);
canvas.backgroundColor = 'pink';
canvas.selection = false;
var x1=150, y1=150, x2=180, y2=180, img1;
var rect = new fabric.Rect({
stroke: 'blue',
opacity: 1,
strokeWidth: 2,
selectable: true,
fill : 'green',
left : x1,
top : y1,
width : x2,
height : y2
});
fabric.Image.fromURL('http://fabricjs.com/assets/pug_small.jpg', function(myImg) {
//i create an extra var for to change some image properties
img1 = myImg.set({ left: 0, top: 0 ,width:canvas.width,height:canvas.height});
canvas.add(img1);
canvas.add(rect);
});
window.addEventListener("resize", function() {
console.log('resizing');
var dims = {
w : canvas.width,
h : canvas.height
};
canvas.setWidth(window.innerWidth*0.98);
canvas.setHeight(window.innerHeight*0.98);
img1.width = window.innerWidth*0.98;
img1.height = window.innerHeight*0.98;
rect.left *= dims.w/canvas.width;
rect.top *= dims.h/canvas.height;
rect.width *= dims.w/canvas.width;
rect.height *= dims.h/canvas.height;
});`
Am I missing something?

Related

Draw array of images in separate canvas

Im working with canvas
I want to load images in different canvas but the problem is all the images are drawn in the same canvas
I want the images to be loaded separately in each canvas dynamically
I have shared the js fiddle below along with the code
https://jsfiddle.net/ewhom2db/
HTML
<canvas id="canvas"></canvas>
JS
let sampleData = [
{"imageWidth":3000,"imageHeight":2000,"imagePrefix":"https://swall.teahub.io/photos/small/","thumbPath":"291-2915301_crculos-gradiente-desvanecidos-degradado-3000-x-2000.jpg","x1":200,"x2":150,"y1":150,"y2":200},
{"imageWidth":2000,"imageHeight":2000,"imagePrefix":"https://swall.teahub.io/photos/small/","thumbPath":"281-2819581_2000-x-2000-wallpapers-25238i6-anime-photos-2000.jpg","x1":200,"x2":150,"y1":150,"y2":200}
];
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
// ctx.clearRect(0, 0, canvas.width, canvas.height);
for (let i = 0; i < sampleData.length; i++) {
let base_image = new Image();
base_image.src = sampleData[i].imagePrefix + sampleData[i].thumbPath;
let x1 = sampleData[i].x1;
let x2 = sampleData[i].x2;
let y1 = sampleData[i].y1;
let y2 = sampleData[i].y2;
let imageWidth = sampleData[i].imageWidth;
let imageHeight = sampleData[i].imageHeight;
canvas.width = imageWidth;
canvas.height = imageHeight;
base_image.onload = function () {
// base_image.src = this.sampleData[i].imagePrefix + this.sampleData[i].thumbPath;
ctx.drawImage(base_image, 0, 0);
ctx.strokeRect(x1, y1, x2 - x1, y2 - y1);
// base_image.src.push(this.sampleData[i].imagePrefix + this.sampleData[i].thumbPath);
};
}
Help will be appreciated thanks
You are drawing over the same canvas context every time you draw an image. If you want to draw each image to a separate canvas then you need to have separate canvases to draw them to.
You could create an array of canvases and then draw to the context of each canvas.
let sampleData = [
{
imageWidth: 3000,
imageHeight: 2000,
imagePrefix: "https://swall.teahub.io/photos/small/",
thumbPath:
"291-2915301_crculos-gradiente-desvanecidos-degradado-3000-x-2000.jpg",
x1: 200,
x2: 150,
y1: 150,
y2: 200
},
{
imageWidth: 2000,
imageHeight: 2000,
imagePrefix: "https://swall.teahub.io/photos/small/",
thumbPath:
"281-2819581_2000-x-2000-wallpapers-25238i6-anime-photos-2000.jpg",
x1: 200,
x2: 150,
y1: 150,
y2: 200
}
];
let canvas = [];
let ctx = [];
for (let i = 0; i < sampleData.length; i++) {
canvas.push(document.createElement("canvas"))
ctx.push(canvas[i].getContext('2d'));
document.body.appendChild(canvas[i]);
let base_image = new Image();
base_image.src = sampleData[i].imagePrefix + sampleData[i].thumbPath;
let x1 = sampleData[i].x1;
let x2 = sampleData[i].x2;
let y1 = sampleData[i].y1;
let y2 = sampleData[i].y2;
let imageWidth = sampleData[i].imageWidth;
let imageHeight = sampleData[i].imageHeight;
canvas[i].width = imageWidth;
canvas[i].height = imageHeight;
base_image.onload = function () {
// base_image.src = this.sampleData[i].imagePrefix + this.sampleData[i].thumbPath;
ctx[i].drawImage(base_image, 0, 0);
ctx[i].strokeRect(x1, y1, x2 - x1, y2 - y1);
// base_image.src.push(this.sampleData[i].imagePrefix + this.sampleData[i].thumbPath);
};
}
Your image appear to be so far apart because your canvases are huge but the image doesn't fill the canvas.

Restrict drawing area in fabric JS

I want to restrict drawing area of a canvas by a button click.
I am drawing a rect by fabric js. I want to enable drawing mode in that rect only.
If mouse moves out of rectangle, drawing stops.
Here is my code of drawing rect:
var canvas = new fabric.Canvas('canvas');
var rect = new fabric.Rect({
top : 100,
left : 100,
width : 60,
height : 70,
fill : 'red'
});
canvas.add(rect);
canvas.on('mouse:down', function (option) {
console.log(option);
if (typeof option.target != "undefined") {
return;
} else {
var startY = option.e.offsetY,
startX = option.e.offsetX;
console.log(startX, startY);
var rect2 = new fabric.Rect({
top : startY,
left : startX,
width : 0,
height : 0,
fill : 'transparent',
stroke: 'red',
strokewidth: 4
});
canvas.add(rect2);
console.log("added");
canvas.on('mouse:move', function (option) {
var e = option.e;
rect2.set('width', e.offsetX - startX);
rect2.set('height', e.offsetY - startY);
rect2.setCoords();
});
}
});
canvas.on('mouse:up', function () {
canvas.off('mouse:move');
});

Drawing consistent freehand dotted line in HTML5 Canvas

I am trying to create a whiteboarding web app using HTML5 and Canvas.
I have implement a simple pen and paintbrush shaped pen with help from this brilliant article:
http://perfectionkills.com/exploring-canvas-drawing-techniques/
My issue is withe dotted line pen and highlighter pen.
The dotted line looks like a simple unbroken line if the mouse moves slowly, and with large gaps if moved quickly. What I want is a consistently spaced dotted line.
I tried setting the context.setLineDash but this has no effect on the result.
I then tried to calculate a minimum distance between the last point and current point and draw if over the dot gap lenth but this also does not seemeingly affect the result.
Here is my code:
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var lastPoint;
var isDrawing = false;
context.lineWidth = 4;
context.lineJoin = context.lineCap = 'round';
canvas.onmousedown = function(e) {
isDrawing = true;
lastPoint = {
x: e.clientX,
y: e.clientY
};
lastPoint = {
x: e.offsetX,
y: e.offsetY
};
};
canvas.onmousemove = function(e) {
if (!isDrawing) return;
context.beginPath();
context.strokeStyle = 'red';
context.fillStyle = 'red';
mx = e.clientX; // mouse pointer and stroke path is off if use this
my = e.clientY;
mx = e.offsetX; // mouse pointer and stroke path match using this
my = e.offsetY;
context.setLineDash([5, 25]);
xlen = Math.abs(mx - lastPoint.x) + context.lineWidth;
ylen = Math.abs(my - lastPoint.y) + context.lineWidth;
gap = Math.sqrt((ylen * ylen) + (xlen * xlen));
if (gap >= 5) {
context.moveTo(lastPoint.x, lastPoint.y);
context.lineTo(mx, my);
context.stroke();
lastPoint = {
x: mx,
y: my
};
}
};
canvas.onmouseup = function() {
isDrawing = false;
};
html,body,canvas
{
width: 100%;
height: 100%;
margin: 0;
}
<canvas id="canvas" ></canvas>
The result is this:
With the highlighter, I get overlapping points which give dark spots on the path. The code for this is:
context.globalAlpha = 0.3;
context.moveTo(lastPoint.x, lastPoint.y);
context.lineTo(mx, my);
context.stroke();
lastPoint = { x: mx, y: my };
The result:

How to scale an image for a canvas pattern?

I want to fill a Canvas with an Image and scale it to a certain width beforehand.
I am trying to achieve an effect where an image in the foreground of the canvas can be erased with the mouse to view an image in the background. This is why I need to use a pattern to fill my canvas instead of just using drawImage(). Everything works apart from the scaling of the foreground image. Here is my code for generating the pattern:
var blueprint_background = new Image();
blueprint_background.src = "myfunurl";
blueprint_background.width = window.innerWidth;
blueprint_background.onload = function(){
var pattern = context.createPattern(this, "no-repeat");
context.fillStyle = pattern;
context.fillRect(0, 0, window.innerWidth, 768);
context.fill();
};
This does exactly what it should do, except that the image keeps its original size.
As you see, I want the image to scale to window.innerWidth (which has the value 1920 when logging it).
If needed, I can provide the rest of the code, but since the error is most likely in this snippet, I decided not to post the rest.
EDIT: Here is my full code with the suggested changes. The front ground image now displays over the full width, however the erasing does not work anymore.
JavaScript (Note that I use jQuery instead of $):
jQuery(document).ready(function() {
var cwidth = window.innerWidth;
var cheight = 768;
function createCanvas(parent, width, height) {
var canvas = {};
canvas.node = document.createElement('canvas');
canvas.context = canvas.node.getContext('2d');
canvas.node.width = width || 100;
canvas.node.height = height || 100;
parent.appendChild(canvas.node);
return canvas;
}
function init(canvas, fillColor) {
var ctx = canvas.context;
canvas.isDrawing = true;
jQuery('#canvas').children().css('position:absolute; top: ' + jQuery('#Top_bar').height() + 'px');
// define a custom fillCircle method
ctx.fillCircle = function(x, y, radius, fillColor) {
this.fillStyle = fillColor;
this.beginPath();
this.moveTo(x, y);
this.arc(x, y, radius, 0, Math.PI * 2, false);
this.fill();
};
// bind mouse events
canvas.onmousemove = function(e) {
if (!canvas.isDrawing) {
return;
}
var x = e.pageX - this.offsetLeft;
var y = e.pageY - jQuery('#Top_bar').outerHeight();
var radius = 30;
var fillColor = '#ff0000';
ctx.globalCompositeOperation = 'destination-out';
ctx.fillCircle(x, y, radius, fillColor);
};
}
var container = document.getElementById('canvas');
jQuery('#canvas').css('position:absolute; top: ' + jQuery('#Top_bar').height() + 'px');
var canvas = createCanvas(container, cwidth, cheight);
init(canvas, '#ddd');
var fgimg = document.getElementById("fgimg");
fgimg.width = cwidth;
var context = canvas.node.getContext("2d");
let canvasP = document.getElementById("pattern");
canvasP.width = window.innerWidth;
canvasP.height = 768;
let ctxP = canvasP.getContext("2d");
ctxP.drawImage( fgimg, 0, 0,window.innerWidth,768 );
context.fillStyle = context.createPattern(canvasP,"no-repeat");
context.fillRect(0,0, canvas.width, canvas.height);
});
CSS:
#canvas {
background:url(http://ulmke-web.de/wp-content/uploads/2019/01/Header-6.jpg);
background-repeat: no-repeat;
background-size: cover;
background-position: center center;
width: 100%;
height: 768px;
}
HTML:
<div id="canvas">
<canvas id="pattern">
</div>
<div style="display:none">
<img id="fgimg" src=" http://ulmke-web.de/wp-content/uploads/2019/01/Header-5.jpg">
</div>
I would use two canvases. On the first one you draw your image and you use this canvas as an image to create the pattern. In order to scale the image you scale the size of the first canvas #pattern in my example.
For example you can do this for a 10/10 image:
canvasP.width = 10;
canvasP.height = 10;
ctxP.drawImage( redpoint, 2.5, 2.5 );
or you can do this for a 20/20 image:
canvasP.width = 20;
canvasP.height = 20;
ctxP.drawImage( redpoint, 5, 5,10,10 );
Furthermore, in my example I'm adding a little margin around the image.
let canvasP = document.getElementById("pattern");
if (canvasP && canvasP.getContext) {
let ctxP = canvasP.getContext("2d");
/*canvasP.width = 10;
canvasP.height = 10;
ctxP.drawImage( redpoint, 2.5, 2.5 ); */
canvasP.width = 20;
canvasP.height = 20;
ctxP.drawImage( redpoint, 5, 5,10,10 );
}
let canvas1 = document.getElementById("canvas");
if (canvas1 && canvas1.getContext) {
let ctx1 = canvas1.getContext("2d");
if (ctx1) {
ctx1.fillStyle = ctx1.createPattern(canvasP,"repeat");
ctx1.fillRect(0,0, canvas1.width, canvas1.height);
}
}
canvas{border:1px solid}
<img id="redpoint" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO 9TXL0Y4OHwAAAABJRU5ErkJggg==">
<canvas id="pattern"></canvas>
<canvas id="canvas"></canvas>
I hope it helps.

Opacity to canvas without affecting inner image

The problem I have is on the image slider. The blue triangule is created using a multiply canvas effect seen here:
http://albertogasparin.it/articles/2011/05/html5-multiply-filter-canvas/
I want the blue triangle to have it's opacity to 0.5, but if I put that on the css (canvas element) it also affects the opacity of the image that's below. Any ideas how to achieve this?
This is my code:
function to_canvas(im,w,h){
var isIE8 = $.browser.msie && +$.browser.version === 8;
var canvas;
var imageBottom;
var im_w = w;
var im_h = h;
var imgData;
var pix;
var pixcount = 0;
var paintrow = 0;
var multiplyColor = [70, 116, 145];
var x_offset = Math.floor(($('#'+im).attr('width') - im_w)/2);
var y_offset = Math.floor(($('#'+im).attr('height') - im_h)/2);
if ( isIE8 ) {
$('<div />' , {
'id' : 'div-'+im,
'class' : 'pseudo_canvas'
}).css({
'width' : im_w,
'height' : im_h
}).insertBefore('#'+im);
$('#'+im).appendTo('#div-'+im).fadeIn();
$('<img>' , {
'src' : '/img/blueborder.png',
'class' : 'blueborder'
}).css({
}).insertBefore('#'+im);
$('#'+im).appendTo('#div-'+im).fadeIn();
}else{
imageBottom = document.getElementById(im);
canvas = document.createElement('canvas');
canvas.width = im_w;
canvas.height = im_h;
imageBottom.parentNode.insertBefore(canvas, imageBottom);
ctx = canvas.getContext('2d');
ctx.drawImage(imageBottom, -x_offset , -y_offset);
imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
pix = imgData.data;
for (var i = 0 ; i < pix.length; i += 4) {
if(pixcount > im_w - (im_h - paintrow) ){
pix[i ] = multiply(multiplyColor[0], pix[i ]);
pix[i+1] = multiply(multiplyColor[1], pix[i+1]);
pix[i+2] = multiply(multiplyColor[2], pix[i+2]);
}
if(pixcount < im_w-1){
pixcount++;
}else{
paintrow++;
pixcount = 0;
}
}
ctx.putImageData(imgData, 0, 0);
/* $('#'+im).remove(); */
}
}
function multiply(topValue, bottomValue){
return topValue * bottomValue / 255;
}
This is how I want the triangle color/opacity (obviously without the opacity of the image on the back):
if u have canvas-
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
context.globalCompositeOperation = "destination-over";
context.fillStyle = "rgba(0, 0, 200, 0.5)";//Change the color combination here
context.fillRect(0, 0, 1000, 1000);
one way would be to use a semi-transparent background instead (e.g. png file)

Categories