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'}).
Related
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.
I have to make a tutorial about horse riding using HTML and CSS. Although I am only a beginner, everything has been going more or less smoothly until I had to draw some images.
As you can see from the code, Canvas_1 is the biggest canvas and within its borders I have two smaller canvases, Canvas_2 and myCanvas. I need image preponsko.png to appear on myCanvas and image dresurno.png to appear on Canvas_2. The images have been scaled to fit the canvas.
When I run the complete code, only the second function, for the image dresurno.png, is working and I can see the photo within the canvas borders. The first photo does not appear.
When I delete the second function, first function which draws image preponsko.png works and the image appears. This leads me to conclusion that something in the script part of the code is wrong, but I just can't figure it out. All help is appreciated.
<!DOCTYPE HTML>
<HTML>
<!-- background for the whole page -->
<div id="bg">
<img src="pozadine/pozadina_konji_slajdovi.png" alt="">
</div>
<HEAD>
<link rel="stylesheet" href="css/stil_slajdovi.css">
<link rel="stylesheet" href="css/stil.css">
<meta charset="UTF-8">
</HEAD>
<BODY>
<!-- Canvas_1 - biggest canvas, within its borders are two smaller canvases, Canvas_2 and myCanvas, are positioned -->
<canvas id="Canvas_1" width="1340" height="618" style="position:absolute; left:10px; top:8px; border:solid red;"></canvas>
<canvas id="myCanvas" width="212" height="170" style="position:absolute; left:260px; top:140px; border:3px solid red;"></canvas>
<img id="rsz" width="0" height="0" src="slike/preponsko.png" alt="">
<canvas id="Canvas_2" width="220" height="220" style="position:absolute; left:280px; top:380px; border:3px solid red;"></canvas>
<img id="res_2" width="0" height="0" src="slike/dresurno.png" alt="">
<a id="button_nazad" href="d_saddle_information.html" class="action-button shadow animate blue" style="position:absolute; left:171px; top:589px;">Back</a>
<a id="button_naprijed" href="f_saddle_endurance_western.html" class="action-button shadow animate yellow" style="position:absolute; left:1058px; top:589px;">Forward</a>
<SCRIPT>
<!-- first function that should draw the "slike/preponsko.png" image -->
window.onload = function() {
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
var img = document.getElementById("rsz");
context.drawImage(img, 0,0);
}
<!-- second function that should draw the "slike/dresurno.png" image -->
window.onload = function() {
var canvas = document.getElementById("Canvas_2");
var context = canvas.getContext("2d");
var img = document.getElementById("res_2");
context.drawImage(img, 0,0);
}
</SCRIPT>
</BODY>
Problem is simple to solve.
You have overwritten the window.onload function with a second function.
You have
window.onload = function(){ ...
Then below that you set the same property with a second function
window.onload = function(){ // second function
window.onload can only hold one reference and that will be to the second function.
You can fix it by just defining one function to do everything,
window.onload = function() {
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
var img1 = document.getElementById("rsz");
context.drawImage(img1, 0,0);
canvas = document.getElementById("Canvas_2");
context = canvas.getContext("2d");
var img2 = document.getElementById("res_2");
context.drawImage(img2, 0,0);
}
or better is to create two render functions and call them from the one onload event
// waits for page to load
window.onload = function() {
renderImage("Canvas_1", "rsz"); // draw first image
renderImage("Canvas_2", "res_2"); // draw second image
}
// gets a canvas with id = canvasId and draws the image with id = imageId
function renderImage(canvasId, imageId){
const canvas = document.getElementById(canvasId);
const ctx = canvas.getContext("2d");
const img = document.getElementById(imageId);
ctx.drawimage(img,0,0);
}
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 working on a website where if the browser window is smaller than a certain size, part of the background clashes with the text. Is it possible to change the background after a browser window becomes smaller than a certain amount?
You can hook into the window.onresize event to check the width and height of the window, then based on these values you can set the background color to what you desire.
window.onresize = function(event) {
var height = window.innerHeight;
var width = window.innerWidth;
}
This solution may have compatability issues in other browsers to IE and so the jQuery function is probably more prefered:
$( window ).resize(function() {
var height = $(this).height();
var width = $(this).width();
});
EDIT: (Example provided)
<script type="text/javascript">
window.onresize = function (event) {
var height = window.innereight;
var width = window.innerWidth;
if (width < 500 || height < 500) {
document.getElementById('Div1').style.backgroundColor = '#FF0000';
}
}
//OR WITH JQUERY
$(window).resize(function () {
var height = $(this).height();
var width = $(this).width();
if (width < 500 || height < 500) {
$('#Div1').css('background-color', '#FF0000');
}
});
</script>
<div id="Div1" style="width: 300px; height: 300px;">
test div
</div>
Please not that when using the innerHeight property in Chrome that the value is returned as undefined and so this does not seem to be supported. The jQuery method would be the preferred solution.
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.