In HTML5, I want to make a fillRect() (with a white fill color) and a border (black). I don't want to use strokeRect() unless I can fill that later. I'm making a game where you click on squares and they change color (it's more complex than that but that's what this focuses on).
<canvas id="canvas1" width="400" height="300" style="border:1px solid #000000;"></canvas>
<script>
var c=document.getElementById("canvas1");
var ctx=c.getContext("2d");
ctx.strokeStyle="rgba(0,0,0,1)";
ctx.strokeRect(0,0,100,100);
</script>
The border around the canvas is for reference.
I can use CSS too, but currently everything is in HTML.
you can not fill it later without a library. If you want to change something simply redraw.
You can use something like that:
ctx.fillStyle = 'blue';
ctx.strokeStyle = 'red';
var fillRect = false;
ctx.rect(20, 20, 150, 100);
if (fillRect) {
ctx.fill();
}
ctx.stroke();
it will draw only the border, if you change fillRect to true it will be filled. You can update your canvas on every requestAnimationFrame.
But maybe you want to use a library like paper.js. It makes things like clicking on objects much easier and it abstracts draws on canvas to objects you create once and update later, like what you asked for.
Work out the position you want to draw the square with the width and height. Once you have done that simply draw a bigger square first which has wider by 2 and higher by 2 but with the same center point. So you draw a square which is bigger and then you draw the normal square on top, this then gives you the illusion of the square has a border
HTML
<canvas id="canvas1" width="400" height="300" style="border:1px solid #000000;"></canvas>
CSS
#canvas1{
border: solid 1px black;
}
Javascript
var c=document.getElementById("canvas1");
var ctx=c.getContext("2d");
var rectXPos = 50;
var rectYPos = 50;
var rectWidth = 100;
var rectHeight = 100;
drawBorder(rectXPos, rectYPos, rectWidth, rectHeight)
ctx.fillStyle='#FFF';
ctx.fillRect(rectXPos, rectYPos, rectWidth, rectHeight);
function drawBorder(xPos, yPos, width, height, thickness = 1)
{
ctx.fillStyle='#000';
ctx.fillRect(xPos - (thickness), yPos - (thickness), width + (thickness * 2), height + (thickness * 2));
}
jsfiddle link : https://jsfiddle.net/jxgw19sh/2/
-- Update --
Add an extra parameter to drawBorder called thickness the default value is 1 but you can provide any other number for thickness into the function and it will use value instead of 1.
I tried on my own and it looks like this:
var x = 100;
var y = 100;
var width = 50;
var height = 50;
var borderWidth = 5;
var offset = borderWidth * 2;
c.beginPath();
c.fillStyle = 'black';
c.fillRect( x - borderWidth, y -borderWidth, width + offset, height + offset);
c.fillStyle = 'green';
c.fillRect( x, y, width, height);
Related
i made 2 deference size html canvas to drawing
First canvas = width : 400px,height:200px
Second canvas = width : 200px,height :100px
Now when i drawing in first html canvas i send that coordinates(x1,y1,x2,y2) to second canvas.
When first canvas coordinates send in second canvas it's not drawing in same place as first canvas.
is there way to equal first canvas coordinates to second one without changing canvas width and height .
ctx.beginPath();
ctx.lineWidth = 5;
ctx.lineCap = 'round';
ctx.strokeStyle = "red";
ctx.moveTo(coord.x, coord.y);
ctx.lineTo(ncoord.x , ncoord.y);
ctx.stroke();
//SECOUND CANVAS
ctx2.beginPath();
ctx2.lineWidth = 5;
ctx2.lineCap = 'round';
ctx2.strokeStyle = "red";
ctx2.moveTo(coord.x, coord.y);
ctx2.lineTo(ncoord.x , ncoord.y);
ctx2.stroke();
when user drwaing in canvas 1 i send that coordinates to both canvas. but in second canvas not drawing in same place as canvas 1.
Note : canvas 1 and 2 have deferent width and height.
I need to slove this without changing width height of the both canvas.
I hope I have made the right assumptions to answer your question. I created two different canvases of two different sizes. The coordinates only fit on the first, bigger, canvas.
You can transform the 'big' coordinates to 'small' coordinates by dividing the width or height of the bigger smaller canvases by the bigger canvases.
For example, the height of the big canvas is 200 but the height of the smaller one is 100. If you divide 100 / 200 you get 0.5. The 'small' coordinates should be half as high as the original ones. See for yourself below:
//just for testing purposes
var coord = {
x: 320,
y: 125
};
var ncoord = {
x: 220,
y: 90
};
function drawBig() {
var canvas = document.getElementById("canvas1");
var ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.lineWidth = 5;
ctx.lineCap = 'round';
ctx.strokeStyle = "red";
ctx.moveTo(coord.x, coord.y);
ctx.lineTo(ncoord.x, ncoord.y);
ctx.stroke();
}
function drawSmall() {
let bigCanvas = document.getElementById("canvas1");
let smallCanvas = document.getElementById("canvas2");
//Devide the dimensions of the big and small canvas in order to get the magnification factor:
let widthDimension = smallCanvas.width / bigCanvas.width;
let heightDimension = smallCanvas.height / bigCanvas.height
var ctx2 = smallCanvas.getContext("2d");
ctx2.beginPath();
ctx2.lineWidth = 5;
ctx2.lineCap = 'round';
ctx2.strokeStyle = "red";
//Transform the original coordinates to the right dimensions:
ctx2.moveTo(coord.x * widthDimension, coord.y * heightDimension);
ctx2.lineTo(ncoord.x * widthDimension, ncoord.y * heightDimension);
ctx2.stroke();
}
canvas {
border: 1px solid black;
}
<canvas id="canvas1" width="400" height="200"></canvas>
<hr>
<canvas id="canvas2" width="200" height="100"></canvas>
<button onclick="drawBig()">draw big canvas</button>
<button onclick="drawSmall()">draw small canvas</button>
Hope this helps! If not, please comment
I have made use of a canvas to create a gauge and it is displaying perfectly on the web browser. However, I am planning to display 2 gauges that are side by side of each other; one gauge is to reflect on the speed of the user interaction, the other gauge is to serve as a purpose of the counter. Furthermore, the gauge will be activated upon the initial user interaction on the start button.
Issue:
At this point in time, I have managed to create and correctly display the first gauge. Hence, to create 2 canvas images, I have created 2 <canvas> tags, and when I created the second <script> tag for the second <canvas>, the 2nd canvas image is superimposed on the 1st <canvas> tag. Therefore, I wouldn't be able to see the 1st canvas image.
Hence, I would like to ask for help on how to enable the 2 canvas image to be by the side of each other?
Code:
I have removed the <script> code for the creation of the 2nd canvas, it might not have been correct to begin with, hence i have removed it.
HTML
<canvas id="canvas" width="300" height="300">
</canvas>
<canvas id="Counter" width="300" height="300">
</canvas>
CSS
#canvas {
display: block;
width: 300px;
margin: 100px auto;
}
/*Custom font for numbers*/
#font-face {
font-family: "bebas";
}
JAVASCRIPT
window.onload = function(){
//canvas initialization
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
//dimensions
var W = canvas.width;
var H = canvas.height;
//Variables
var degrees = 0;
var new_degrees = 0;
var difference = 0;
var color = "#ffa500"; //green looks better to me
var bgcolor = "#654321";
var text;
var animation_loop, redraw_loop;
function init()
{
//Clear the canvas everytime a chart is drawn
ctx.clearRect(0, 0, W, H);
//Background 360 degree arc
ctx.beginPath();
ctx.strokeStyle = bgcolor;
ctx.lineWidth = 30;
ctx.arc(W/2, H/2, 100, 0, Math.PI*2, false); //you can see the arc now
ctx.stroke();
//gauge will be a simple arc
//Angle in radians = angle in degrees * PI / 180
var radians = degrees * Math.PI / 180;
ctx.beginPath();
ctx.strokeStyle = color;
ctx.lineWidth = 30;
//The arc starts from the rightmost end. If we deduct 90 degrees from the angles
//the arc will start from the topmost end
ctx.arc(W/2, H/2, 100, 0 - 90*Math.PI/180, radians - 90*Math.PI/180, false);
//you can see the arc now
ctx.stroke();
//Lets add the text
ctx.fillStyle = color;
ctx.font = "50px bebas";
text = Math.floor(degrees/360*100) + "ms";
//Lets center the text deducting half of text width from position x
text_width = ctx.measureText(text).width;
//adding manual value to position y since the height of the text cannot be measured easily. There are hacks but we will keep it manual for now.
ctx.fillText(text, W/2 - text_width/2, H/2 + 15);
}
function draw()
{
//Cancel any movement animation if a new chart is requested
if(typeof animation_loop != undefined) clearInterval(animation_loop);
//random degree from 0 to 360
new_degrees = Math.round(Math.random()*360);
difference = new_degrees - degrees;
animation_loop = setInterval(animate_to, 1000/difference);
}
//function to make the chart move to new degrees
function animate_to()
{
//clear animation loop if degrees reaches to new_degrees
if(degrees == new_degrees)
clearInterval(animation_loop);
if(degrees < new_degrees)degrees++;
else
degrees--;
init();
}
//Lets add some animation for fun
draw();
//Draw a new chart every 2 seconds
redraw_loop = setInterval(draw, 2000);
}
You haven't given your second canvas any styles.
Make them both inline-block, they will be next to eachother.
http://jsbin.com/vabevadoto/edit?html,css,js,output
I added a red outline so you could see the second canvas container is indeed there and next to the other once you make them inline-blocks.
#canvas {
display: inline-block;
width: 300px;
margin: 100px auto;
}
#Counter {
display: inline-block;
width: 300px;
margin: 100px auto;
outline: 1px solid red;
}
This should do the job for you
<canvas id="canvas" width="300" height="300" style="float:left; display:block;">
</canvas>
<canvas id="Counter" width="300" height="300" style="float:left; display:block;">
</canvas>
I am trying to figure out how one can detect if the user's mouse hits a line on an HTML 5 canvas with jQuery.
Here is the code that generates the canvas lines:
<canvas id="myCanvas" width="400" height="400" style="border:1px solid #c3c3c3;">
Your browser does not support the canvas element.
</canvas>
<script type="text/javascript" src="js/jquery-1.4.2.min.js"></script>
<script type="text/javascript">
window.onload = function(){
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.moveTo(40,0);
ctx.lineTo(40,360);
ctx.stroke();
ctx.moveTo(80,400);
ctx.lineTo(80,40);
ctx.stroke();
ctx.moveTo(120,0);
ctx.lineTo(120,360);
ctx.stroke();
ctx.moveTo(160,400);
ctx.lineTo(160,40);
ctx.stroke();
};
</script>
I'm using a modified jQuery script that I actually found in another question on here, but now I can't figure out how to detect the line, mainly the difference in color from white to black, in the canvas. I know that this can be done with images, but I haven't seen anyone with something like this.
I guess my real question is, is there a way to detect color changes on a canvas element with jQuery?
Its possible to do with javascript. In fact you aren't using any jQuery in your example above. An easy way to do it is by grabbing the pixel data from the canvas, and checking the alpha at the specified x and y position. If the alpha isn't set to 0, then you have something drawn on the canvas. Below is a function I put together real quick that does that.
Live Demo
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d"),
width = 400;
height = 400;
canvas.width = canvas.height = 200;
// draw
ctx.moveTo(40, 0);
ctx.lineTo(40, 360);
ctx.stroke();
ctx.moveTo(80, 400);
ctx.lineTo(80, 40);
ctx.stroke();
ctx.moveTo(120, 0);
ctx.lineTo(120, 360);
ctx.stroke();
ctx.moveTo(160, 400);
ctx.lineTo(160, 40);
ctx.stroke();
function detectLine(x, y) {
var imageData = ctx.getImageData(0, 0, width, height),
inputData = imageData.data,
pData = (~~x + (~~y * width)) * 4;
if (inputData[pData + 3]) {
return true;
}
return false;
}
canvas.addEventListener("mousemove", function(e){
var x = e.pageX,
y = e.pageY;
console.log(detectLine(x, y));
});
console.log(detectLine(40, 100));
console.log(detectLine(200, 200));
In this jsfiddle there's a line with a lineWidth of 1.
http://jsfiddle.net/mailrox/9bMPD/350/
e.g:
ctx.lineWidth = 1;
However the line is 2px thick when it's drawn on the canvas, how do you create a 1px thick line.
I could draw a rectangle (with 1px height) however I want the line to also work on diagonals. So how do you get this line to be 1px high?
Thanks!
Canvas calculates from the half of a pixel
ctx.moveTo(50,150.5);
ctx.lineTo(150,150.5);
So starting at a half will fix it
Fixed version: http://jsfiddle.net/9bMPD/357/
This answer explains why it works that way.
You can also translate by half a pixel in the X and Y directions and then use whole values for your coordinates (you may need to round them in some cases):
context.translate(0.5, 0.5)
context.moveTo(5,5);
context.lineTo(55,5);
Keep in mind that if you resize your canvas the translate will be reset - so you'll have to translate again.
You can read about the translate function and how to use it here:
https://www.rgraph.net/canvas/reference/translate.html
This answer explains why it works that way.
Or as this answer states, to get a width of 1, you need to start at a half pixel.
ctx.moveTo(50.5,150.5);
ctx.lineTo(150.5,150.5);
http://jsfiddle.net/9bMPD/355/
For me, only a combination of different 'pixel perfect' techniques helped to archive the results:
Get and scale canvas with the pixel ratio:
pixelRatio = window.devicePixelRatio/ctx.backingStorePixelRatio
Scale the canvas on the resize (avoid canvas default stretch scaling).
multiple the lineWidth with pixelRatio to find proper 'real' pixel line thickness:
context.lineWidth = thickness * pixelRatio;
Check whether the thickness of the line is odd or even. add half of the pixelRatio to the line position for the odd thickness values.
x = x + pixelRatio/2;
The odd line will be placed in the middle of the pixel. The line above is used to move it a little bit.
function getPixelRatio(context) {
dpr = window.devicePixelRatio || 1,
bsr = context.webkitBackingStorePixelRatio ||
context.mozBackingStorePixelRatio ||
context.msBackingStorePixelRatio ||
context.oBackingStorePixelRatio ||
context.backingStorePixelRatio || 1;
return dpr / bsr;
}
var canvas = document.getElementById('canvas');
var context = canvas.getContext("2d");
var pixelRatio = getPixelRatio(context);
var initialWidth = canvas.clientWidth * pixelRatio;
var initialHeight = canvas.clientHeight * pixelRatio;
window.addEventListener('resize', function(args) {
rescale();
redraw();
}, false);
function rescale() {
var width = initialWidth * pixelRatio;
var height = initialHeight * pixelRatio;
if (width != context.canvas.width)
context.canvas.width = width;
if (height != context.canvas.height)
context.canvas.height = height;
context.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
}
function pixelPerfectLine(x) {
context.save();
context.beginPath();
thickness = 1;
// Multiple your stroke thickness by a pixel ratio!
context.lineWidth = thickness * pixelRatio;
context.strokeStyle = "Black";
context.moveTo(getSharpPixel(thickness, x), getSharpPixel(thickness, 0));
context.lineTo(getSharpPixel(thickness, x), getSharpPixel(thickness, 200));
context.stroke();
context.restore();
}
function pixelPerfectRectangle(x, y, w, h, thickness, useDash) {
context.save();
// Pixel perfect rectange:
context.beginPath();
// Multiple your stroke thickness by a pixel ratio!
context.lineWidth = thickness * pixelRatio;
context.strokeStyle = "Red";
if (useDash) {
context.setLineDash([4]);
}
// use sharp x,y and integer w,h!
context.strokeRect(
getSharpPixel(thickness, x),
getSharpPixel(thickness, y),
Math.floor(w),
Math.floor(h));
context.restore();
}
function redraw() {
context.clearRect(0, 0, canvas.width, canvas.height);
pixelPerfectLine(50);
pixelPerfectLine(120);
pixelPerfectLine(122);
pixelPerfectLine(130);
pixelPerfectLine(132);
pixelPerfectRectangle();
pixelPerfectRectangle(10, 11, 200.3, 443.2, 1, false);
pixelPerfectRectangle(41, 42, 150.3, 443.2, 1, true);
pixelPerfectRectangle(102, 100, 150.3, 243.2, 2, true);
}
function getSharpPixel(thickness, pos) {
if (thickness % 2 == 0) {
return pos;
}
return pos + pixelRatio / 2;
}
rescale();
redraw();
canvas {
image-rendering: -moz-crisp-edges;
image-rendering: -webkit-crisp-edges;
image-rendering: pixelated;
image-rendering: crisp-edges;
width: 100vh;
height: 100vh;
}
<canvas id="canvas"></canvas>
Resize event is not fired in the snipped so you can try the file on the github
The Canvas can draw clean straight lines with fillRect().
A rectangle with a 1px height or a 1px width does the job.
It doesn't need half-pixel value:
var ctx = document.getElementById("myCanvas").getContext("2d");
ctx.drawVerticalLine = function(left, top, width, color){
this.fillStyle=color;
this.fillRect(left, top, 1, width);
};
ctx.drawHorizontalLine = function(left, top, width, color){
this.fillStyle=color;
this.fillRect(left, top, width, 1);
}
ctx.drawVerticalLine(150, 0, 300, "green");
ctx.drawHorizontalLine(0, 150, 300, "red");
https://jsfiddle.net/ynur1rab/
Did you see the first hit on google? (search for canvas line width 1px).
Though I have to admit this isn't exactly "clean" or "lean". Ferry Kobus' solution is much better. Then again: it sucks you need to use "half pixels" in the first place...
The fillRect() method can be used to draw thin horizontal or vertical lines in canvas (without having to apply the +0.5 shift on coordinates):
this.fillRect(left, top, 1, height);
this.fillRect(left, top, width, 1);
And you can actually make the lines even thinner by just replacing this code by something like:
this.fillRect(left, top, 0.7, height);
this.fillRect(left, top, width, 0.7);
Lines will be thinner (tending to reach 1 pixel wide) but their color a bit attenuated.
-> working example
To be noted that if we set ctx.lineWidth=0.7 (for the classical beginPath/moveTo/lineTo/stroke sequence), it does not work on Chrome (0.7 and 1 are interpreted the same way). Thus an interest for this fillRect() method.
If none of these answers worked for you, check your browser zoom. Mine was somehow at 125% so every fourth 1px line was drawn 2px wide.
I spent hours trying to figure out why every fiddle on the internet worked and mine didn't (the zoom was only set for my dev tab)
Assuming that I have an <canvas> which contains a line (using lineTo function).
The canvas changes his dimensions (either both height and width, height or width) frequently.
During the canvas dimension change, I would like to keep the same visually width of the lines.
Thanks a lot.
The following snippet represents the current scenario when the dimensions of the canvas effects the visually width of the lines.
var index;
var canvas;
var context;
for (index = 1; index <= 2; index++) {
canvas = document.getElementById("canvas" + index);
context = canvas.getContext('2d');
context.beginPath();
context.moveTo(100, 150);
context.lineTo(450, 50);
context.lineWidth = 16;
context.stroke();
}
<h3>
As you can see, both canvas objects has the same line initialization
in JavaScript code. the only diff between them are the dimensions (in our case, the width).
</h3>
<canvas id="canvas1" style="width:100px"></canvas>
<canvas id="canvas2" style="width:300"></canvas>
So you have a couple things that are going to get weird on you... canvas needs to be sized outside of CSS. By setting size in css you are stretching the canvas vs using it's native sizing.
Notice the difference by switching the sizing on the elements.
The other thing is now that they are sized correctly you're starting the original point outside of the 100px canvas.
Further reading: https://www.w3.org/TR/html5/scripting-1.html#attr-canvas-width
here is a fiddle that shows what is really happening here - https://jsfiddle.net/bopjtwfe/
var index;
var canvas;
var context;
for (index = 1; index <= 2; index++) {
canvas = document.getElementById("canvas" + index);
context = canvas.getContext('2d');
context.beginPath();
context.moveTo(10, 10);
context.lineTo(150, 150);
context.lineWidth = 16;
context.stroke();
}
canvas {
display: block;
margin-bottom: 10px;
background-color: white;
}
<canvas id="canvas1" width='100px' height='100px' ></canvas>
<canvas id="canvas2" width='300px' height='300px'></canvas>