In my very simple code example below, I am trying to draw a box, 10 pixels in from the margin all the way around the outside of a canvas. Despite getting what seem like legit values from the screen extends of the browser window, I am only getting an L-shaped line, which seems really odd. My code seems very straight forward. So 2 questions:
Why is it so small (eg not going across the whole browser window?
Why isn't it a box like the code suggests it should be?
<html>
<head></head>
<body>
<p id="X">X:</p>
<p id="Y">Y:</p>
<!--<form>
Timeline Item: <input type="text" name="item"><br>
Timeline Date: <input type="date" name="date"><br>
</form>-->
<canvas id="DemoCanvas" width=100% height=100%></canvas>
<script>
var canvas = document.getElementById('DemoCanvas');
var xoutput = document.getElementById('X');
var youtput = document.getElementById('Y');
// Always check for properties and methods, to make sure your code doesn't break in other browsers.
if (canvas.getContext)
{
var margin = 10;
var startx = margin;
var starty = margin;
var endx = (window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth) - margin;
var endy = (window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight) - margin;
xoutput.innerHTML = endx;
youtput.innerHTML = endy-margin;
var context = canvas.getContext('2d');
context.beginPath();
context.moveTo(startx,starty);
context.lineTo(endx,starty);
context.lineTo(endx,endy);
context.lineTo(startx,endy);
context.lineTo(startx,starty);
context.stroke();
}
</script>
</body>
</html>
Here's the result which you can clearly see is both way too small and not a box at all:
Your problem stems from the common misunderstanding of the canvas' height and width properties. You're giving the values as percents, but that wont work. From MDN:
HTMLCanvasElement.height Is a positive integer reflecting the height
HTML attribute of the element interpreted in CSS pixels. When
the attribute is not specified, or if it is set to an invalid value,
like a negative, the default value of 150 is used.
So you should use plain integers instead. If you need a dynamic size, you can use javascript to update the values.
<html>
<head></head>
<body>
<p id="X">X:</p>
<p id="Y">Y:</p>
<!--<form>
Timeline Item: <input type="text" name="item"><br>
Timeline Date: <input type="date" name="date"><br>
</form>-->
<canvas id="DemoCanvas" width="628" height="200"></canvas>
<script>
var canvas = document.getElementById('DemoCanvas');
var xoutput = document.getElementById('X');
var youtput = document.getElementById('Y');
// Always check for properties and methods, to make sure your code doesn't break in other browsers.
if (canvas.getContext)
{
var margin = 10;
var startx = margin;
var starty = margin;
var endx = (window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth) - margin;
var endy = (window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight) - margin;
xoutput.innerHTML = endx;
youtput.innerHTML = endy-margin;
var context = canvas.getContext('2d');
context.beginPath();
context.moveTo(startx,starty);
context.lineTo(endx,starty);
context.lineTo(endx,endy);
context.lineTo(startx,endy);
context.lineTo(startx,starty);
context.stroke();
}
</script>
</body>
</html>
Because you are using window / document width & height,
e.g. the entire webpage, so the lines goes outside of the canvas.
you should instead use the canvas element's width & height.
var canvas = document.getElementById('DemoCanvas');
var xoutput = document.getElementById('X');
var youtput = document.getElementById('Y');
// Always check for properties and methods, to make sure your code doesn't break in other browsers.
if (canvas.getContext)
{
var margin = 10;
var startx = margin;
var starty = margin;
var endx = canvas.clientWidth - margin;
var endy = canvas.clientHeight - margin;
xoutput.innerHTML = endx;
youtput.innerHTML = endy-margin;
var context = canvas.getContext('2d');
context.beginPath();
context.moveTo(startx,starty);
context.lineTo(endx,starty);
context.lineTo(endx,endy);
context.lineTo(startx,endy);
context.lineTo(startx,starty);
context.stroke();
}
<p id="X">X:</p>
<p id="Y">Y:</p>
<!--<form>
Timeline Item: <input type="text" name="item"><br>
Timeline Date: <input type="date" name="date"><br>
</form>-->
<canvas id="DemoCanvas" width=100 height=100></canvas>
To achieve "fullscreen", something you can do is to use a parent element or the body tag and then "copy" it's width & height to the canvas afterwards.
Note that i use vh & vw instead of % to avoid any stretching issues, etc.
var canvas = document.getElementById('DemoCanvas');
var xoutput = document.getElementById('X');
var youtput = document.getElementById('Y');
updateCanvasSize();
function updateCanvasSize(){
canvas.width = bodySim.clientWidth;
canvas.height = bodySim.clientHeight;
draw();
}
function draw(){
if (canvas.getContext)
{
var margin = 10;
var startx = margin;
var starty = margin;
var endx = canvas.clientWidth - margin;
var endy = canvas.clientHeight - margin;
xoutput.innerHTML = endx;
youtput.innerHTML = endy-margin;
var context = canvas.getContext('2d');
context.beginPath();
context.moveTo(startx,starty);
context.lineTo(endx,starty);
context.lineTo(endx,endy);
context.lineTo(startx,endy);
context.lineTo(startx,starty);
context.stroke();
}
} //end of draw
#bodySim {
width: 100vw;
height: 100vh;
}
<p id="X">X:</p>
<p id="Y">Y:</p>
<!--<form>
Timeline Item: <input type="text" name="item"><br>
Timeline Date: <input type="date" name="date"><br>
</form>-->
<div id="bodySim">
<canvas id="DemoCanvas" ></canvas>
</div>
Related
I'm trying to implement a behaviour by which I can add an initial image to a canvas (placeholder) and then subsequently I can then choose a different image to replace that image - that new image should then scale itself to "fit" within the bounds of the old image. For example if I choose a portrait image as the "placeholder" and then choose a landscape image to replace it, Id expect the landscape image to first scale its width and maintain aspect ratio, and then vertically align itself within the boundaries of the placeholder dimensions.
Here is a jsfiddle of what I have working so far: https://jsfiddle.net/cgallagher/co8dg527/1/
and here is the code:
var canvas = new fabric.Canvas('stage');
var cw = canvas.getWidth()
var ch = canvas.getHeight()
var _currentImage;
function setProductImage(url){
var oldWidth;
var oldHeight;
var oldLeft;
var oldTop;
fabric.Image.fromURL(url, function(img) {
if (_currentImage) {
oldWidth = _currentImage.getScaledWidth()
oldHeight = _currentImage.getScaledHeight()
oldLeft = _currentImage.left
oldTop = _currentImage.top
canvas.remove(_currentImage);
}
_currentImage = img;
img.set({ 'left': oldLeft || 0 })
img.set({ 'top': oldTop || 0 })
if (oldHeight && oldHeight > img.getScaledHeight()){
img.scaleToWidth(oldWidth);
img.set({'height': oldHeight })
} else if (oldWidth > img.getScaledWidth()){
img.scaleToHeight(oldHeight);
img.set({'width': oldWidth })
}
img.selectable = true
canvas.add(img).setActiveObject(img);
});
}
function addImage(url){
setProductImage(url)
canvas.renderAll()
}
You can see that the image does scale the way I'd like but it doesn't then align itself.
I'm toying with the idea of dropping a bounding box around the image too and possibly trying to align and scale within that but I'm not sure this is even possible with fabric?
This is what I had done.
When you run scaleToWidth and scaleToHeight the scaleX andscaleY is changing and you need to adjust the oldHeight and oldWidth to the new img.scaleX/img.scalaY
I also rewrite _renderFill method from fabric.Image.prototype to create that offset effect.
Check here:https://jsfiddle.net/mariusturcu93/co8dg527/37/
Code
<html>
<head>
<title>Fabric kicking</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.4.0/fabric.js"></script>
<style>
#stage {
border: solid 1px #333;
}
</style>
</head>
<body>
<button onclick="addImage('https://s.hs-data.com/bilder/spieler/gross/208995.jpg')">Add image</button>
<button onclick="addImage('https://cdn.images.express.co.uk/img/dynamic/67/590x/Mateusz-Klich-883903.jpg')">add another image</button>
<canvas id="stage" width="600" height="600"></canvas>
<script>
var canvas = new fabric.Canvas('stage');
var cw = canvas.getWidth()
var ch = canvas.getHeight()
var _currentImage;
function setProductImage(url){
var oldWidth;
var oldHeight;
var oldLeft;
var oldTop;
fabric.Image.fromURL(url, function(img) {
if (_currentImage) {
oldWidth = _currentImage.getScaledWidth()
oldHeight = _currentImage.getScaledHeight()
oldLeft = _currentImage.left
oldTop = _currentImage.top
canvas.remove(_currentImage);
}
_currentImage = img;
img.set({ 'left': oldLeft || 0 })
img.set({ 'top': oldTop || 0 })
if (oldHeight && oldHeight > img.getScaledHeight()){
img.scaleToWidth(oldWidth);
img.set({'height': oldHeight/img.scaleX})
} else if (oldWidth > img.getScaledWidth()){
img.scaleToHeight(oldHeight);
img.set({'width': oldWidth/img.scaleY })
}
img.selectable = true
canvas.add(img).setActiveObject(img);
});
}
function addImage(url){
setProductImage(url)
canvas.renderAll()
}
</script>
<img src="https://s.hs-data.com/bilder/spieler/gross/208995.jpg" />
<img src="https://cdn.images.express.co.uk/img/dynamic/67/590x/Mateusz-Klich-883903.jpg" />
</body>
</html>
fabric.Image.prototype._renderFill rewrite
fabric.Image.prototype._renderFill= (function(renderFill){
return function(ctx) {
var w = this.width, h = this.height, sW = w * this._filterScalingX, sH = h * this._filterScalingY,
x = -w / 2, y = -h / 2, elementToDraw = this._element;
y = y + Math.abs((this._element.height-h)/2);
x = x + Math.abs((this._element.width-w)/2);
elementToDraw && ctx.drawImage(elementToDraw,
this.cropX * this._filterScalingX,
this.cropY * this._filterScalingY,
sW,
sH,
x, y, w, h);
}
})()
I'm using createJS to drawn inside the canvas. I have my canvas set to occupy the browser window maintaining aspect ratio using the resize() function.
This is the code:
mytext = new createjs.Text("Lorem ipsum dolor sit amet 2","19px Calibri","#073949");
mytext.x = 450
mytext.y = 300;
stage.addChild(mytext);
resize = function() {
var canvas = document.getElementById('canvas');
var canvasRatio = canvas.height / canvas.width;
var windowRatio = window.innerHeight / window.innerWidth;
var width;
var height;
if (windowRatio < canvasRatio) {
height = window.innerHeight - 35;
width = height / canvasRatio;
} else {
width = window.innerWidth;
height = width * canvasRatio;
}
canvas.style.width = width + 'px';
canvas.style.height = height + 'px';
}()
What happens is that the text gets blurry (decrease of quality) when the canvas resizes.
http://i.imgur.com/RQOSajs.png
vs
http://i.imgur.com/Xwhf5c5.png
How can I solve this issue?
Since you are using CreateJS, you can simply resize the canvas, and scale the entire stage to redraw everything at the new size:
// just showing width to simplify the example:
var newWidth = 800;
var scale = newWidth/myCanvas.width;
myCanvas.width = newWidth;
myStage.scaleX = myStage.scaleY = scale;
myStage.update(); // draw at the new size.
#Adam's answer is correct as far as scaling the canvas goes. You do NOT want to scale with CSS, as it will stretch your canvas instead of changing its pixel dimensions. Set the width and height of the canvas using JavaScript instead.
stage.canvas.width = window.innerWidth;
stage.canvas.height = window.innerHeight;
As you stated in your comment, this will only change the canvas size, and not reposition or scale your content. You will have to do this manually. This is fairly simple. Generally, I recommend putting your "resize" listener in the JavaScript in your HTML file, rather than on a frame script.
First, determine the scale, based on the size of the window and the size of your content. You can use the exportRoot.nominalBounds.width and exportRoot.nominalBounds.height which is the bounds of the first frame. If you want to scale something else, use its nominalBounds instead.
Note that nominalBounds is appended to all MovieClips exported from Flash/Animate. If you enable multi-frame bounds, and want to use those, you will have to modify your approach.
The main idea is to use the original, unscaled size of your contents.
var bounds = exportRoot.nominalBounds;
// Uses the larger of the width or height. This will "fill" the viewport.
// Change to Math.min to "fit" instead.
var scale = Math.max(window.innerWidth / bounds.width, window.innerHeight / bounds.height);
exportRoot.scaleX = exportRoot.scaleY = scale;
You can then center it if you want.
exportRoot.x = *window.innerWidth - bounds.width*scale)/2;
exportRoot.y = *window.innerHeight - bounds.height*scale)/2;
Here is a quick sample of a responsive canvas using a simple shape as the scaling contents:
http://jsfiddle.net/lannymcnie/4yy08pax/
Doing this with Flash/Animate CC export has come up a few times, so it is on my list of future EaselJS demos to include on createjs.com, and in the EaselJS GitHub.
I hope this helps.
Take a look at my jsfiddle : https://jsfiddle.net/CanvasCode/ecr7o551/1/
Basically you just store the original canvas size and then use that to work out new positions and sizes
html
<canvas id="canvas" width="400" height="400">
Canvas was unable to start up.
</canvas>
<button onclick="resize()">Click me</button>
javascript
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var originalWidth = canvas.width;
var originalHeight = canvas.height;
render = function()
{
context.fillStyle = "#DDD";
context.fillRect(0,0, originalWidth * (canvas.width / originalWidth), originalHeight * (canvas.height / originalHeight));
context.fillStyle = "#000";
var fontSize = 48 * (canvas.width / originalWidth);
context.font = fontSize+"px serif";
context.fillText("Hello world", 100 * (canvas.width / originalWidth), 200 * (canvas.height / originalHeight));
}
resize = function()
{
canvas.width = 800;
canvas.height = 600;
render();
}
render();
The HTML5 canvas element works with two different sizes
Visual size on screen, controlled via CSS, like you're setting with canvas.style.width/height
Size of pixel buffer for the drawing, controlled via numeric width and height pixel attributes on the canvas element.
The browser will stretch the buffer to fit the size on screen, so if the two values are not 1:1 ratio text will look blurry.
Try adding the following lines to your code
canvas.width = width;
canvas.height = height;
I created a function to resize all the elements on the screen after resizing the canvas. It saves the initial coordinates and scales for the elements with the original width of 900 px and then it changes them according to the current width ratio relative to the original width ratio. The text isn't blurry/bad quality anymore.
resize = function() {
var canvas = document.getElementById('canvas');
var canvasRatio = canvas.height / canvas.width;
var windowRatio = window.innerHeight / window.innerWidth;
var width;
var height;
if (windowRatio < canvasRatio) {
height = window.innerHeight;
width = height / canvasRatio;
} else {
width = window.innerWidth;
height = width * canvasRatio;
}
canvas.width = width;
canvas.height = height;
reziseElements();
};
reziseElements = function()
{
var canvrat = canvas.width / 900;
//Simplified
stage.scaleX = stage.scaleY = canvrat;
//Old Version
/*for (i=0; i<stage.numChildren ;i++)
{
currentChild = stage.getChildAt(i);
if (typeof currentChild.oscaleX == 'undefined')
{
currentChild.oscaleX = currentChild.scaleX;
currentChild.ox = currentChild.x;
currentChild.oy = currentChild.y;
}
}
for (i=0; i<stage.numChildren ;i++)
{
currentChild = stage.getChildAt(i);
currentChild.scaleX = currentChild.scaleY = currentChild.oscaleX * canvrat
currentChild.x = currentChild.ox * canvrat
currentChild.y = currentChild.oy * canvrat
} */
}
I asked a similar question and it got labelled as a dupe despite the supposed dupe not answering my question.
Edit: The dupe was [stackoverflow.com/questions/17211920/make-canvas-height-auto][1]
I am using a canvas to print a thermal receipt and as you might imagine the receipt can be varying heights depending on how many items are on it.
I need the canvas therefore to adjust to the content.
I know of canvas.height = window.innerHeight and similar but i don't see how this can help me.
html
<body onload='onDrawReceipt(shipment, returns, company, customer, img)'>
<form onsubmit='return false;'>
<table id= "thermal-receipt-table" style='width:100%;'>
<tbody>
<tr >
<td colspan='2' class='left'>
<div id='canvasFrame'>
<canvas id='canvasPaper' width='576' height= '640' >
</canvas>
</div>
</td>
</tr>
</tbody>
</table>
</form>
js
function onDrawReceipt(order, returns, company, customer, img) {
var canvas = document.getElementById('canvasPaper');
if (canvas.getContext) {
var context = canvas.getContext('2d');
context.clearRect(0, 0, canvas.width, canvas.height);
context.textBaseline = 'top';
lineSpace = 20;
leftPosition = 0;
//centerPosition = canvas.width / 2;
centerPosition = (canvas.width + 26) / 2;
//rightPosition = canvas.width;
rightPosition = (canvas.width - 0);
// cursor = 0;
cursor = 80;
//rest of code taking content to more than 640px in height.
}
You can rely on contentContainerNode.offsetHeight
var canvas = document.getElementById('canvasPaper')
var contentContainerNode = document.getElementById('myContentContainer')
canvas.height = contentContainerNode.offsetHeight
How can I make the windows scrollbar's Y coordinate be the same as the mouse Y coordinate, how
can I make the scrollbar follow the mouse?
The most compute efficient using way of doing this utilizing DOM-L3:
//HTML BLOCK
<canvas onmousemove="TrackMouse(event)"></canvas>
//JS BLOCK
function TrackMouse(e){
window.scrollBy(e.webkitMovementX, e.webkitMovementY);
}
Here is a jQuery-free solution:
var height = document.documentElement.scrollHeight;
function scroll(event){
var y = event.clientY;
var yPercentage = y/screen.height;
window.scrollTo(0,yPercentage*height);
}
window.onmousemove = scroll;
In action: http://jsfiddle.net/xF2vs/3/
On a real page, if your mouse is at the bottom of the screen, the page will be scrolled entirely to the bottom. For some reason in jsfiddle it doesn't quite make it.
What it is doing is finding how far down your mouse is on the screen and scrolling the document down that same percent.
Sharing my modified version Nile's reply to get a version that scrolls both X and Y.
var height = document.documentElement.scrollHeight;
var width = document.documentElement.scrollWidth;
function scroll(event) {
var y = event.clientY;
var x = event.clientX;
var yPercentage = y / screen.height;
var xPercentage = x / screen.width;
window.scrollTo(xPercentage * width, yPercentage * height);
}
window.onmousemove = scroll;
To scroll just the width, but not the height (as Murplyx requested a year ago) use the following:
var width = document.documentElement.scrollWidth;
function scroll(event) {
var x = event.clientX;
var xPercentage = x / screen.width;
window.scrollTo(xPercentage * width, 0);
}
window.onmousemove = scroll;
Thank you Nile for the start!
I'm trying to draw an image to the html5 canvas, but can't quite remember exactly how it's done.
I have the following html document:
<html>
<head>
<title>Understanding Business</title>
<section hidden>
<img id = "startButton" src = "startButton.png" alt = "Start Button" height = "40" width = "50" href = "javascript:drawFirstGameElements();">
</section>
</head>
<body onLoad = "drawStartButton()">
<h1>Understanding Business</h1>
<canvas id ="gameCanvas" width = "1000" height= "500" style = "border:1px solid #000000;">Your browser does not support the HTML5 canvas.</canvas>
<script type = "text/javascript">
window.onload = function(){
var gameCanvas = document.getElementById("gameCanvas");
var context = gameCanvas.getContext("2d");
//gameCanvas.addEventListener('onclick', clickReporter, false);
gameCanvas.addEventListener('mouseMove', function(event){
var mousePosition = getMouseCoords(gameCanvas, event);
});
/* Add some global variables */
var image = new Image();
var imageSource;
var imageX;
var imageY;
function drawStartButton(){
/*image.onload = function(){
context.drawImage(image, 500, 250);
};
image.src = "startButton.png"; */
var startButtonImg = document.getElementById("startButton");
context.drawImage(startButtonImg, 500, 250);
}
function drawFirstGameElements(){
drawDescriptionBox1;
}
function drawDescriptionBox1(){
context.moveTo(100, 400);
context.lineTo(150, 400);
context.lineTo(150, 450);
context.lineTo(100, 450);
context.lineTo(100, 400);
context.moveTo(110, 425);
context.strokeText("Asset");
}
//Function to get mouse coordinates on canvas
function getMouseCoords(gameCanvas, event){
var rect = gameCanvas.getBoundingClientRect(), root = document.documentElement;
//return mouse position
var mouseX = event.clientX - rect.top - root.scrollTop;
var mouseY = event.clientY - rect.left - root.scrollLeft;
return{
x: mouseX,
y: mouseY
};
}
}
For some reason, when I view the page in the browser, although the canvas is displayed, the "start button image" is not displayed on the canvas. It's been a while since I last used the HTML5 canvas, and I can't quite figure out what it is I'm doing wrong... Can someone point me in the right direction?
Cheers,
Edit
Code updated to reflect changes as suggested, but I'm still having the same problem. Any other suggestions?
Typo in your code :
var gameCanvas = document.getElementById("gameCanvas");
var context = gameCanvas.getContext("2d");
//gameCanvas.addEventListener('onclick', clickReporter, false);
gameCanvas.addEventListener('mouseMove', function(event){
var mousePosition = getMouseCoords(gameCanvas, event);
}); // HERE ADD );
/* Add some global variables */
var image = new Image();
var imageSource;
var imageX;
var imageY;
//HERE DELETE THE }
jsfiddle : http://jsfiddle.net/RqbPn/