I'm using a canvas in ReactJs and when I want to draw a rectangle that takes up the whole canvas, it doesn't fill up completely, it leaves a little space along the length and width of the canvas.
I emphasize that I am changing the width and heigth of the canvas through css so I use the canvas.client property
How can I make it completely full?
My code:
const PDFDocumentWrapper = styled.div`
canvas {
width: 100% !important;
height: auto !important;
}
`;
<PDFDocumentWrapper>
<canvas ref={canvasRef}
onMouseDown={
e => {
let nativeEvent = e.nativeEvent;
const canvas = canvasRef.current
const ctx = canvas.getContext('2d')
handleMouseDown(nativeEvent, ctx);
}}/>
</PDFDocumentWrapper>
my function:
function handleMouseDown(event, ctx) {
ctx.fillStyle="#f00";
ctx.fillRect(0,0,ctx.canvas.clientWidth,ctx.canvas.clientHeight);
}
Note:
If I use ctx.width it returns undefined
Have you tried this:
function handleMouseDown(event, ctx) {
ctx.fillStyle="#f00";
ctx.fillRect(0,0,ctx.canvas.width,ctx.canvas.height);
}
I believe that there may be a difference between clientHeight/clientWidth and height/width due to scaling in your browser.
Related
I am using react and typescript to make a website. I have a custom background gradient that I want to stretch till the end of an element that is in the foreground. The background should come just below the 'some text' section.
Page Design
function LandingPage() {
return (
<div>
<Background/>
<Navbar />
<SomeText />
<Footer />
</div>
);
}
What I tried doing was to calculate the height of the 'some-text' section, and then set the height of the background to this value.
function Background() {
useEffect(() => {
const canvasElement = document.getElementById("gradient-canvas");
const gradient: any = new Gradient();
if (canvasElement) {
gradient.initGradient("#gradient-canvas");
} else {
gradient.pause();
}
}, []);
const bgHeight = document
?.getElementById("some-text-section")
?.clientHeight?.toString();
const divStyle = {
height: bgHeight?.toString() + "px",
};
return (
<canvas
id="gradient-canvas"
className="bg-gradient-to-r from-red -z-10 absolute"
style={divStyle}
/>
);
}
This is not working and the background occupies the entire screen instead of the specified style height. The 'Gradient' is imported from a javascript file.
Thanks!
I'm trying to create a basic example of offscreen rendering canvas but I'm error in js "cannot read property of context". actually my idea is to create a demo like I saw in https://yalantis.com/ I want to create my name initial. If there is any better idea to achieve this then please enlighten me.
Thanks here is my basic attempt before the actual implementation :)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Off Screen Canvas</title>
<script>
function createOffscreenCanvas() {
var offScreenCanvas= document.createElement('canvas');
offScreenCanvas.width= '1360px';
offScreenCanvas.height= '400px';
var context= offScreenCanvas.getContext("2d");
context.fillRect(10,10,200,200);
}
function copyToOnScreen(offScreenCanvas) {
var onScreenContext=document.getElementById('onScreen').getContext('2d');
var offScreenContext=offScreenCanvas.getContext('2d');
var image=offScreenCanvas.getImageData(10,10,200,200);
onScreenContext.putImageData(image,0,0);
}
function main() {
copyToOnScreen(createOffscreenCanvas());
}
</script>
<style>
#onScreen {
width:1360px;
height: 400px;
}
</style>
</head>
<body onload="main()">
<canvas id="onScreen"></canvas>
</body>
</html>
You could achieve this in the following way ...
function createOffscreenCanvas() {
var offScreenCanvas = document.createElement('canvas');
offScreenCanvas.width = '1360';
offScreenCanvas.height = '400';
var context = offScreenCanvas.getContext("2d");
context.fillStyle = 'orange'; //set fill color
context.fillRect(10, 10, 200, 200);
return offScreenCanvas; //return canvas element
}
function copyToOnScreen(offScreenCanvas) {
var onScreenContext = document.getElementById('onScreen').getContext('2d');
onScreenContext.drawImage(offScreenCanvas, 0, 0);
}
function main() {
copyToOnScreen(createOffscreenCanvas());
}
canvas {
border: 1px solid black;
}
<body onload="main()">
<canvas id="onScreen" width="1360" height="400"></canvas>
note : never set canvas's width and height using css. instead use the native width and height property of the canvas.
You can try to do that with experimental OffScreenCanvas API. I'm leaving the link here.
So you don't actually need a canvas element attached to DOM with this experimental API, furthermore you can perform your drawing in webworkers, so it will not block the browser's main thread if you're drawing thousands of objects.
const offscreen = new OffscreenCanvas(256, 256);
offscreen.getContext("2d") // or webgl, etc.
Please beware that it's experimental. You can try to check like "OffscreenCanvas" in window and use that, if it's not available, you can fallback to document.createElement("canvas").
Return offScreenCanvas in your function createOffscreenCanvas
function createOffscreenCanvas() {
var offScreenCanvas= document.createElement('canvas');
offScreenCanvas.width= '1360px';
offScreenCanvas.height= '400px';
var context= offScreenCanvas.getContext("2d");
context.fillRect(10,10,200,200);
return offScreenCanvas;
}
Edit
You were getting image date from canvas not context.
function copyToOnScreen(offScreenCanvas) {
var onScreenContext=document.getElementById('onScreen').getContext('2d');
var offScreenContext = offScreenCanvas.getContext('2d');
var image=offScreenContext.getImageData(10,10,200,200);
onScreenContext.putImageData(image,0,0);
}
I'm using HTML2Canvas to convert my DIV to a PNG. It is going well, but when my DIV is larger than the screen, only the visible part of the DIV in the screen is rendered in the image.
Is there any workaround so I can render the whole DIV into the image?
Here is my code:
html2canvas(divDrop, {
onrendered: function (canvas) {
imgMap.style.display = "";
imgMap.src = canvas.toDataURL("image/png");
},
width: divDrop.clientWidth,
height: divDrop.clientHeight
});
As described in the comments, I'm posting the working link below:
JSFiddle example
$(function() {
$("#btnSave").click(function() {
html2canvas($("#widget"), {
onrendered: function(canvas) {
theCanvas = canvas;
document.body.appendChild(canvas);
// Convert and download as image
Canvas2Image.saveAsPNG(canvas);
$("#img-out").append(canvas);
// Clean up
//document.body.removeChild(canvas);
}
});
});
});
I'm having problems with resizing cells and built-in mxGraph layouts.
If I put a cell on canvas, and I try to resize it, even for a pixel, it grows huge, something like 50000px x 30000px, so it streches my whole canvas, and of course it is unusable.
If I load a graph from an xml file from the database, I can resize cells without any problems.
Similar thing happens with the built in layouts. I'd like to use compact tree layout (the reason I like it beacuse it aligns my whole horizontal).
When I draw a graph and try to use that layout, my graph goes wild, also streching to 50000px x 30000 px (example dimensions, but the scroll is so tiny I can barely aim it with the mouse).
If I load a graph from xml from a database, compact tree layout works perfect. But as soon as I add another cell in it, and try to use compact tree layout again, it goes wild, again.
I use absolute positioning for div which holds the canvas, as same as on the example here (http://jgraph.github.io/mxgraph/javascript/examples/editors/workfloweditor.html)
This is my css and html :
<head>
<style type="text/css">
#graphContainer {
background: url('../../resources/jgraph/src/images/grid.gif');
left: 20px;
right: 20px;
top: 65px;
bottom: 20px;
position: absolute;
border: 1px solid #F2F2F2;
white-space: nowrap;
font-family: Arial;
font-size: 8pt;
display: block;
}
</style>
</head>
<body>
<div id="graphContainer"></div>
<script>
$(document).ready(function(){
mc.init(document.getElementById('graphContainer'));
});
</script>
</body>
</html>
And this is my javascript for graph initialization (along with the couple of events, beacuse I'm not sure if they are the problem):
mxConnectionHandler.prototype.connectImage = new mxImage('../../resources/jgraph/src/images/connector.gif', 14, 14);
if (!mxClient.isBrowserSupported()) {
mxUtils.error('Browser is not supported!', 200, false);
} else {
var root = new mxCell();
root.insert(new mxCell());
var model = new mxGraphModel(root);
if (mxClient.IS_QUIRKS)
{
document.body.style.overflow = 'hidden';
new mxDivResizer(graphContainer);
}
var editor = new mxEditor();
editor.setGraphContainer(graphContainer);
editor.readGraphModel(model);
var graph = editor.graph;
graph.setConnectable(true);
new mxRubberband(graph);
/* CODE FOR ADDING THE TOOLBAR, excluded from example */
//code for writing out the node name
graph.convertValueToString = function(cell)
{
if (mxUtils.isNode(cell.value))
{
var outValue = cell.value.getAttribute('nodeName');
if (outValue != null && outValue.length > 0)
{
return outValue;
}
return '';
}
return '';
};
//defining on select event
graph.getSelectionModel().addListener(mxEvent.CHANGE, function(sender, evt)
{
events.cellSelectionChanged(graph, graph.getSelectionCell());
});
//triggering the on select event
events.cellSelectionChanged(graph);
//cells added event
graph.addListener(mxEvent.CELLS_ADDED, function(sender, evt) {
var vertex = evt.getProperties().cells[0];
if(vertex.isVertex()){
var decoder = new mxCodec();
var nodeModel = decoder.decode(vertex.value);
if(nodeModel.type=='node' || nodeModel.type=='branch'){
utils.changeCellAttribute(vertex, 'nodeName', 'Node_' + vertex.id);
}else if(nodeModel.type=='start'){
utils.changeCellAttribute(vertex, 'nodeName', 'START');
}else if(nodeModel.type=='end'){
utils.changeCellAttribute(vertex, 'nodeName', 'END');
}else if(nodeModel.type=='form'){
utils.changeCellAttribute(vertex, 'nodeName', 'Form');
}
}
});
//on connect event
graph.connectionHandler.addListener(mxEvent.CONNECT, function(sender, evt){
var model = graph.getModel();
var edge = evt.getProperty('cell');
var source = model.getTerminal(edge, true);
var target = model.getTerminal(edge, false);
});
}
Any thoughts what the problem might be?
Solution:
Complete graph and cell configuration is loaded from the database (in this example), including the width and height for the cells.
The problem was adding toolbar items for certain cell types, more precise, dropped cell default width and height. As I said we are loading the configuration from the database, it is completely string-ified, so were the width and height.
They both had to be cast to JavaScript Number object for resize and layout to behave properly.
I am having a problem getting the desired layout of canvas elements on a page. I am using a table to do the layout. The desired layout is to have one canvas to the left that is full height and two other canvases to the right, one on top of the other, with a combined height of the left canvas. The two right canvases are of fixed width and height. When the browser window is resized I want the left canvas to resize to take up all the width available (up to the width of the right canvases). I am using window.onresize event to catch the resize events and to resize the left canvas.
The problem I see is that the left cavas will resize correctly when the browser window width gets bigger, but fails to shrink when the browser window width gets smaller! The code is below. What gives? Is there something inherent in canvas that doesn't allow flexible resizing?
I have searched for a answer to this issue with no luck. Hopefully someone here has conquered this and can give me a hand.
Here is sample code:
<!DOCTYPE html>
<html>
<head>
<title>SO Example</title>
<script type="text/javascript">
window.onload = function ()
{
var canvas = document.getElementById("wf1");
canvas.width = canvas.parentNode.clientWidth;
canvas.height = canvas.parentNode.clientHeight;
}
window.onresize = function ()
{
var canvas = document.getElementById("wf1");
canvas.width = canvas.parentNode.clientWidth;
canvas.height = canvas.parentNode.clientHeight;
}
</script>
<style type="text/css">
table
{
border-collapse: collapse;
background-color: #ccc;
}
tr, td
{
padding: 0;
line-height: 0;
width: 100%;
}
</style>
</head>
<body>
<table>
<tr class="slot">
<td>
<canvas id="wf1" class="wfblock" style="background-color:red;"></canvas>
</td>
<td>
<canvas id="pm1" style="background-color: green; width: 200px; height: 84px;"></canvas>
<canvas id="pm2" style="background-color: blue; width: 200px; height: 84px;"></canvas>
</td>
</tr>
</table>
</body>
</html>
Your canvas can be adjusted in both directions : growing and shrinking.
I do that frequently but I found that the only thing that always works (especially when tables are involved) is giving 100% dimensions for both width and height and adjust the container (be it a td or a div) in a classical way.
To avoid problem with viewport dimensions being different from the ones of the canvas, I always add a listener for the resize event.
Using jquery, I usually end up with this kind of class, where an instance of Grapher is created for each canvas :
function Grapher(options) {
this.graphId = options.canvasId;
this.dimChanged = true; // you may remove that if you want (see above)
};
Grapher.prototype.draw = function() {
if (!this._ensureInit()) return;
// makes all the drawing, depending on the state of the application's model
// uses dimChanged to know if the positions and dimensions of drawed objects have
// to be recomputed due to a change in canvas dimensions
}
Grapher.prototype._ensureInit = function() {
if (this.canvas) return true;
var canvas = document.getElementById(this.graphId);
if (!canvas) {
return false;
}
if (!$('#'+this.graphId).is(':visible')) return false;
this.canvas = canvas;
this.context = this.canvas.getContext("2d");
var _this = this;
var setDim = function() {
_this.w = _this.canvas.clientWidth;
_this.h = _this.canvas.clientHeight;
_this.canvas.width = _this.w;
_this.canvas.height = _this.h;
_this.dimChanged = true;
_this.draw(); // calls the function that draws the content
};
setDim();
$(window).resize(setDim);
// other inits (mouse hover, mouse click, etc.)
return true;
};
In your case I would create for example new Grapher({canvasId:'#wf1'}).