How to navigate to a specific object location programatically in fabricjs - javascript

When I have a bunch of objects on canvas at different locations and my web page has scrollbars for canvas area, I have navigation buttons for next/prev which makes the respective object as active by calling canvas.setActiveObject() but I need to auto scroll to that specific location.
I haven't found anything on the html canvas context to achieve that.
canvas.getContext().moveTo(x, y); moves the pointer logically but I need to move physically.
function gonext(idx) {
var objs = fcanvas.getObjects();
fcanvas.setActiveObject(objs[idx]);
}
var fcanvas = new fabric.Canvas("mycanvas");
fcanvas.setWidth(500);
fcanvas.setHeight(1200);
var rect = new fabric.Rect({
left: 10,
top: 10,
width: 50,
height: 50,
fill: '#FF454F'
});
fcanvas.add(rect);
fcanvas.setActiveObject(rect);
var rect = new fabric.Rect({
left: 10,
top: 1100,
width: 50,
height: 50,
fill: '#FF454F'
});
fcanvas.add(rect);
fcanvas.renderAll();
<script src="https://rawgit.com/kangax/fabric.js/master/dist/fabric.js"></script>
<button onclick="gonext(1)">Next</button>
<div >
<canvas id="mycanvas" style="border:1px solid black"></canvas>
</div>
Here is the jsfiddle in action, clicking on NEXT will make the next object active but I also need to scroll down to that object
Any direction on this one?

Maybe you have to adjust offset in your page, but a simple window.scrollTo doens't help you?
Please try the executable snippet below:
function gonext(idx) {
var objs = fcanvas.getObjects();
var obj = objs[idx];
fcanvas.setActiveObject(obj);
window.scrollTo(0, obj.top);
}
var fcanvas = new fabric.Canvas("mycanvas");
fcanvas.setWidth(500);
fcanvas.setHeight(1200);
var rect = new fabric.Rect({
left: 10,
top: 10,
width: 50,
height: 50,
fill: '#FF454F'
});
fcanvas.add(rect);
fcanvas.setActiveObject(rect);
var rect = new fabric.Rect({
left: 10,
top: 1100,
width: 50,
height: 50,
fill: '#FF454F'
});
fcanvas.add(rect);
fcanvas.renderAll();
<script src="https://rawgit.com/kangax/fabric.js/master/dist/fabric.js"></script>
<button onclick="gonext(1)">Next</button>
<div >
<canvas id="mycanvas" style="border:1px solid black"></canvas>
</div>

The issue I had was, canvas was inside a nested DIVs, I had do use.scrollTop on my root div to fix the issue

Related

Matching Wrapper+Canvas+Div to an Image's Size

I'm using heatmap.js in Google Apps Script and I am trying to resize the heatmap canvas (where I can actually plot points) to fit an image. How can I change the javascript or styling to make this work?
CODE: https://jsfiddle.net/nateomardavis/51h2guL9/
The wrapper has a black border.
The heatmap div has a blue border.
The canvas has a red border.
How it Should Work: I should be able to mark the heatmap anywhere on the image. The wrapper, heatmap div and canvas should all be sized based on the size of the image.
How it Currently Works: I can only mark the heatmap inside the blue/black border. My javascript resizes the canvas to fit the image, but not the bounds of the heatmap.
What I've Tried: You can see in the fiddle how I've tried to change what the javascript is resizing (lines 88-90).I also thought about using the using the image as the background-image of the heatmap div but the sizing wasn't right and I wanted to be able to save the heatmap and image as one layer (for printing/saving). I also tried changing the size of the image (line 101) but anything above 55% would cut the image off.
The screenshot below is just for reference, but the full working code is in the fiddle. I have it all under HTML because that's how it works in Google Apps Script.
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script type="text/javascript" src="https://www.patrick-wied.at/static/heatmapjs/assets/js/heatmap.min.js"></script>
<style>
html, body {
width: 100%;
height: 100%;
}
html {
color: #333;
font-family: sans-serif;
font-size: 16pt;
font-weight: 100;
line-height: 1.5em;
}
.wrapper {
width: 100%;
height:100%;
background:rgba(0,0,0,.03);
border:3px solid black;
}
.heatmap {
width:100%;
height:100%;
background-position: top;
background-repeat: no-repeat;
background-size: contain;
position: relative;
border:3px solid blue;
}
.canvas {
border:3px solid red;
}
</style>
</head>
<body>
<button class="clear">Clear</button>
<button onclick="printPage()" class="print-btn no-print" style="">Print</button>
<p id="coords"></p>
<div id="wrapper" class="wrapper"> <!-- black box on web app-->
<div id="heatmap" class = "heatmap" style="position: relative;"> <!-- blue box on web app-->
<canvas id="canvas" class="canvas"></canvas> <!-- red box on web app-->
</div>
</div>
<script>
//CREATE HEATMAP
window.onload = function() {
var heatmapInstance = h337.create({
container: document.querySelector('.heatmap'),
gradient: {
'.25': 'black',
'.5': 'red',
'.75': 'white'
},
radius: 20
});
document.querySelector('.wrapper').onclick = function(ev) {
heatmapInstance.addData({
x: ev.layerX,
y: ev.layerY,
value: 1
});
};
document.querySelector('.clear').onclick = function() {
//heatmapInstance._renderer._clear();
heatmapInstance.setData({data:[]});
};
//RESIVE CANVAS TO FIT IMAGE
//http://jsfiddle.net/6ZsCz/1135/
var canvas = document.getElementById('canvas'); //v1
//var canvas = document.getElementById('heatmap'); //v2
//var canvas = document.getElementById('wrapper'); //v3
var ctx = canvas.getContext('2d');
ctx.canvas.width = window.innerWidth;
ctx.canvas.height = window.innerHeight;
var imageObj = new Image();
imageObj.onload = function() {
canvas.width = imageObj.width; // version 2
canvas.height = imageObj.height; // version 2
ctx.drawImage(imageObj,0,0); // version 2
//ctx.drawImage(imageObj, 0, 0, imageObj.width * 0.5, imageObj.height * 0.5); //version 1
};
imageObj.src = 'https://upload.wikimedia.org/wikipedia/commons/thumb/8/82/Soccer_Field_Transparant.svg/1200px-Soccer_Field_Transparant.svg.png';
};
//MOUSE COORDINATES
//works inside heatmap
function getCursorPosition(canvas, event) {
const rect = canvas.getBoundingClientRect()
const x = event.clientX - rect.left
const y = event.clientY - rect.top
var coor = "X coords: " + x + ", Y coords: " + y;
document.getElementById("coords").innerHTML = coor;
}
const canvas = document.querySelector('.heatmap')
canvas.addEventListener('mousedown', function(e) {
getCursorPosition(canvas, e)
})
function printPage() {
window.print();
}
</script>
</body>
</html>
After lots of tinkering, I was able to figure it out: https://jsfiddle.net/nateomardavis/p1kwrhmv
I used img and gave it a background-image with CSS.
I then set the heatmap layer to relative and the image layer to absolute using the solution HERE.
Last, I then set the image layer css using the method outlined HERE.
.heatmap {
background-size: contain;
background-repeat: no-repeat;
width: 100%;
height: 300%; /*if the image is bigger than screen, this is needed */
position: relative;
}
.backgroundIMG {
background-image: url('https://upload.wikimedia.org/wikipedia/commons/thumb/8/82/Soccer_Field_Transparant.svg/1200px-Soccer_Field_Transparant.svg.png');
background-size: contain;
background-repeat: no-repeat;
position: absolute;
width: 100%;
height: 0;
padding-top: 151.33%;
}

Saving a Canvas as Image Works as Expected but Image is Very Pixelated

I have been tinkering with what follows, and after scouring the web I'm turning up short for how to make the captured image clearer; in brief, the problem I have is that the downloaded image is very choppy.
var canvas = [],
image;
var mainCanvas;
mainCanvas = new fabric.Canvas('c0');
for (i = 1; i <= 3; i++) {
canvas[i] = new fabric.StaticCanvas('sc' + i);
}
function addText() {
var text = new fabric.IText('Type here...', {
fontSize: 27,
top: 10,
left: 10,
});
mainCanvas.add(text);
}
var rect = new fabric.Rect({
fill: '#ff0000',
width: 100,
height: 100,
id: 1
});
var circle = new fabric.Circle({
fill: '#ffff00',
radius: 50,
left: 150,
top: 150,
originX: 'center',
originY: 'center',
id: 2
});
mainCanvas.on('object:added', onModified);
mainCanvas.on('object:modified', onModified);
mainCanvas.on('object:scaling', onModified);
mainCanvas.on('object:moving', onModified);
mainCanvas.add(rect, circle);
function onModified(option) {
var ob = option.target;
var index = mainCanvas.getObjects().indexOf(ob);
ob.clone(function(obj) {
for (i = 1; i <= 3; i++) {
canvas[i].insertAt(obj, index, true);
}
});
};
$('#update').click(function() {
updateCanvas();
});
function updateCanvas() {
var json = JSON.stringify(mainCanvas);
for (i = 1; i <= 3; i++) {
canvas[i].loadFromJSON(json);
}
}
// Toggling Images
function replaceImage(imgUrl) {
if (!isImageLoaded) return; //return if initial image not loaded
image.setSrc(imgUrl, function() {
mainCanvas.renderAll();
updateCanvas();
},{ crossOrigin: 'anonymous' } );
}
// Default (Blank)
fabric.Image.fromURL('https://i.imgur.com/SamdNdX.png', function(img) {
isImageLoaded = true;
image = img.set({
selectable: false,
evented: false,
});
mainCanvas.add(image);
mainCanvas.sendToBack(image);
updateCanvas();
},{ crossOrigin: 'anonymous' });
$('#save').click(function() {
html2canvas($('#imagesave'), {
onrendered: function(canvas) {
var a = document.createElement('a');
// toDataURL defaults to png, so we need to request a jpeg, then convert for file download.
a.href = canvas.toDataURL("image/jpeg").replace("image/jpeg", "image/octet-stream");
a.download = 'myfile.jpg';
a.click();
}
})
});
html * {
margin: 0px;
padding: 0px;
}
body {
margin: 0px;
padding: 0px;
}
canvas {
margin: 0px;
display: block;
padding: 0;
}
td,
tr {
margin: 0;
padding: 0;
border: 0;
outline: 0;
vertical-align: baseline;
}
#imagesave {
background-color: white;
height: 637.5px;
width: 825px;
padding-left: 75px;
border: 1px solid black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/html2canvas#1.0.0-rc.5/dist/html2canvas.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.22/fabric.js"></script>
<button onclick="addText();" class="dropdown-item">Add Text</button><button id="save">Save</button>
<button onclick="replaceImage('https://i.imgur.com/SamdNdX.png')">Blank</button>
<button onclick="replaceImage('https://i.imgur.com/TIINd6E.png')">Hands Pic</button>
<div id="imagesave">
<table>
<tr>
<td>
<canvas id="c0" width="187.5" height="636"></canvas>
</td>
<td>
<canvas id="sc1" width="187.5" height="636"></canvas>
</td>
<td>
<canvas id="sc2" width="187.5" height="636"></canvas>
</td>
<td>
<canvas id="sc3" width="187.5" height="636"></canvas>
</td>
</tr>
</table>
</div>
I have tried updating my various libraries without much luck. What can I do to sharpen the end result (downloaded image)? Or maybe another library that I should try using?
Here is one of the ways to achieve it. It is a simple canvas with a sample text, circle, rectangle, and an image and which you can export to a .png file, feel free to change the format to .jpeg, it will work as well.
var mainCanvas = new fabric.Canvas('canvas');
// Add a rectange
var rect = new fabric.Rect({
fill: '#ff0000',
width: 100,
height: 100,
id: 1
});
mainCanvas.add(rect);
// Add a circle
var circle = new fabric.Circle({
fill: '#ffff00',
radius: 50,
left: 150,
top: 150,
originX: 'center',
originY: 'center',
id: 2
});
mainCanvas.add(circle);
// Add a text
var text = new fabric.IText('Type here...', {
fontSize: 27,
top: 10,
left: 10,
});
mainCanvas.add(text);
// Add an image
fabric.Image.fromURL('https://placekitten.com/200/303', function(img) {
var image = img.set({
selectable: false,
evented: false,
left: 230
});
mainCanvas.add(image);
mainCanvas.sendToBack(image);
mainCanvas.renderAll()
}, { crossOrigin: 'anonymous' });
// Handle click of the save button
var saveButton = document.getElementById('save')
saveButton.addEventListener('click', function (e) {
this.href = mainCanvas.toDataURL({
format: 'png'
});
this.download = 'canvas.png'
}, false)
body {
font-family: sans-serif;
}
canvas {
border: 2px solid #333;
}
#save {
border: 1px solid #333;
text-decoration: none;
color: #fff;
font-weight: bold;
background-color: #333;
padding: 10px;
margin-top: 15px;
display: inline-block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.2/fabric.min.js"></script>
<div id="app">
<canvas id="canvas" width="500" height="350"></canvas>
Save canvas as image
</div>
You can also check it in this Code Sandbox https://codesandbox.io/s/stackoverflow-60879984-fabricjs-362-export-canvas-as-image-k69er

drawImage() is not working

When I open the html file in chrome, I only see the background, I open the consol and I see "hi" ever frame but I cant see the Image that I "drew" using the drawImage() `
var ctx = document.querySelector('Canvas').getContext('2d')
function draw() {
ctx.drawImage(document.getElementById('Mario'), 50, 61, 0, 0)
window.requestAnimationFrame(draw)
console.log('hi')
}
draw()
#Canvas {
width: 100%;
height: 100%;
background: url(Pictures/Background.jpg);
margin: -8px;
}
<canvas id='Canvas'></canvas>
<div style='display:none;'>
<img id='Mario' src='Pictures/Mario.png'>
</div>
Here's your problem:
ctx.drawImage(document.getElementById('Mario'), 50, 61, 0, 0);
You've got your image width and height set to 0. Switch it to this instead:
ctx.drawImage(document.getElementById('Mario'), 0, 0, 50, 61);

Scale rectangle object from middle – fabricjs

I have a rectangle object on fabricjs canvas. Now, when I try to scale the rectangle it always scales from a fixed corner and only expands to the direction where the mouse is. What I want is, when I scale it should scale from center and expand either side. I tried setting originX and originY but that didn’t work. Can anyone help me on this? Thanks!
var rect = new fabric.Rect({
top: 150,
left: 150,
width: 100,
height: 100,
fill: 'black',
originX: 'center',
originY: 'center',
});
You need to set centeredScaling property to true for the rectangle object to make its' scale origin to center ...
var canvas = new fabric.Canvas('c');
var rect = new fabric.Rect({
top: 100,
left: 100,
width: 50,
height: 50,
fill: 'black',
originX: 'center',
originY: 'center',
centeredScaling: true
});
canvas.add(rect).renderAll();
canvas{border: 1px solid #ccc}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.11/fabric.min.js"></script>
<canvas id="c" width="200" height="200"></canvas>

How to create fabricjs object with fix height and width?

I have created fabricJS object (fabric.Text) successfully. But now I want that object with fixed height and width. Currently when I edits text of the object, it(object) is scaled. But now I want object with fixed height and width. And when I edits text, text should be condense (reduce width of text and decrease size of spacing) and height and width of object should be fixed. Here is reference link (in which center text is what I have to develop). Here is my code.
$(function () {
var fontfamily = 'Arial';
var fontsize = 30;
var fontspacing = $('#fontspacing').val();
var canvas = new fabric.Canvas('c');
var text = new fabric.Text($('#text').val(), {
top: 250,
left: 250,
centeredScaling: true,
hasControls: true,
hasBorders: false,
textAlign: 'center',
fontSize: fontsize,
fontFamily: fontfamily,
originX: 'center'
});
canvas.add(text).renderAll();
$('#text').on('keyup', function () {
canvas.setActiveObject(canvas.item(canvas.getObjects().length - 1));
var obj = canvas.getActiveObject();
if (obj) {
obj.setText($('#text').val());
canvas.renderAll();
}
});
});
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.5.0/fabric.js"></script>
<script src="https://rawgit.com/EffEPi/fabric.curvedText/master/fabric.curvedText.js"></script>
</head>
<body>
<div class="row">
<div class="row canvas-container" style="width: 50%; float: left;">
<canvas id="c" width="500" height="500" style="border:1px dotted #000; border-radius: 50%;"></canvas><br>
</div>
<div class="row" style="float: left; width: 50%; height: 150px; background-color: violet;">
Text :
<input type="text" id="text" /><br>
</div>
</div>
</body>
</html>

Categories

Resources