Drawing predefined number of dots inside a canvas - javascript

I am trying achieve the following:
-Given two variables numberColumns/numberRows I want to draw a grid of rectangles or dots in a set width canvas for example 800x400
I have tried several things, but I fail at getting the rectangles/dots the right size with the right spacing
This is an example I tried to draw one row. I am trying to get to work on any given number of rows/columns
function draw(){
var width = 800;
var height = 400;
var nrow = 32;
var ncol = 48;
var canvas = document.getElementById('tutorial');
if (canvas.getContext){
var ctx = canvas.getContext('2d');
//Have a border so drawing starts at 20,20
var spacew = width - 40;
var x = Math.floor(spacew/ncol);
var currCol = 20;
for(i = 1; i<ncol; i++){
ctx.beginPath();
ctx.arc(currCol, 20, x, 0, Math.PI*2, true);
ctx.closePath();
ctx.fill();
currCol = currCol + x*2;
}
}
}
Any idea on how I would go about this, maybe an example?
Thanks

I have created an example here http://jsfiddle.net/J9MLq/7/. Each circle has a diameter that is a 2*radius. i have put it when calculating the radius dynamically depending on the width of canvas in var x = width/ncol/2; (You don't need any Math.floor/Math.ceil, otherwise you will have gaps between circles and also borders). Also, now canvas element is resizable, your function accepts parameters draw(width, height). Now you can play with the rows by yourself. You can extend the function to accept rows and columns amount as well. Just try it there...

Related

Canvas, diagonal lines are thicker

i have following problem. Im trying to draw a border with notches on my buttons and slider. thats working fine. but i noticed that my diagonal lines are twice as thick as the normal ones.
I saw that i have to work on the width/height settings in the canvas/tag itself. but i cant get it working!
Maybe i dont understand realy to make it work. pls help :)
button_canvas_border
slider_canvas_border
thats my html
<canvas width="1290" height="738" class="slider_canvas" id="slider_canvas" ></canvas>
or
<canvas class="slider_canvas" id="slider_canvas" ></canvas>
both dont work
and my js
jQuery(document).ready(function($) {
$('.slider_canvas').each(function(index, canvas) {
var wrapper_height = $('.nsaw_slider_front_wrapper').height() + 30;
var wrapper_width = $('.nsaw_slider_front_wrapper').width() + 30;
/* doesnt matter what i do on the below, its just not working */
canvas.width = wrapper_width;
canvas.height = wrapper_height;
canvas.style.width = wrapper_width;
canvas.style.height = wrapper_height;
canvas.setAttribute('width', wrapper_width);
canvas.setAttribute('height', wrapper_height);
/* doesnt matter what i do on the above, its just not working */
var point_01_cord = wrapper_width * 0.85;
var point_02_cord = wrapper_height * 0.25423;
var point_03_cord = wrapper_width * 0.15;
var point_04_cord = wrapper_height * 0.74577;
var ctx = canvas.getContext('2d');
var gradient = ctx.createLinearGradient(0, 0, 170, 0);
gradient.addColorStop("0", "#0033ff");
gradient.addColorStop("1" ,"#ffff00");
ctx.strokeStyle = gradient;
ctx.lineWidth = 5;
ctx.beginPath();
ctx.translate(0.5,0.5);
ctx.moveTo(0,0);
ctx.lineTo(point_01_cord,0);
ctx.lineTo(wrapper_width,point_02_cord);
ctx.lineTo(wrapper_width,wrapper_height);
ctx.lineTo(point_03_cord,wrapper_height);
ctx.lineTo(0,point_04_cord);
ctx.lineTo(0,0);
ctx.stroke();
});
});
maybe someone can help :)
Strokes do overlap from both sides of the coordinates, that's by the way why you saw some say you should translate by 0.5, so that lineWidth = 1 covers a full pixel instead of two halves. (More on that here).
Here you are drawing a 5px wide line so you really don't want that offset (5 pixels can be rendered perfectly fine), even though it's not your actual problem here.
Your drawing is at the edges of the canvas boundaries. This means that only half of your line is visible.
To workaround that, offset all your lines coordinates to take into account its lineWidth. For instance the top line instead of having its y values set to 0 should have it set to 2.5 without the initial translate or to 2 with if you really want to keep it.

How can I get the visible height of a font? [duplicate]

I know how to get this height of a font:
By placing the text in a div and getting offset height of the div.
But I would like to get this actual height (Which will depend on font family):
Is that in any way possible using web based programming?
Is there a simple solution? I think the answer is no.
If you're ok with a more involved (and processor-intensive) solution, you could try this:
Render the text to a canvas, then use canvasCtx.getImageData(..) to retrieve pixel information. Next you would do something similar to what this pseudo code describes:
first_y : null
last_y : null
for each y:
for each x:
if imageData[x][y] is black:
if first_y is null:
first_y = y
last_y = y
height = last_y - first_y
This basically looks for the top (lowest y-index) of the lettering (black pixels) and the bottom (highest y-index) then subtracts to retrieve the height.
I was writing the code while Jason answered, but I decided to post it anyway:
http://jsfiddle.net/adtn8/2/
If you follow the comments you should get the idea what's going on and why. It works pretty fast and it's not so complicated as it may sound. Checked with GIMP and it is accurate.
(code to be sure it wont be lost):
// setup variables
var c = document.createElement('canvas'),
div = document.getElementsByTagName('div')[0],
out = document.getElementsByTagName('output')[0];
// set canvas's size to be equal with div
c.width = div.offsetWidth;
c.height = div.offsetHeight;
var ctx = c.getContext('2d');
// get div's font from computed style and apply it to context
ctx.font = window.getComputedStyle(div).font;
// use color other than black because all pixels are 0 when black and transparent
ctx.fillStyle = '#bbb';
// draw the text near the bottom of the canvas
ctx.fillText(div.innerText, 0, div.offsetHeight);
// loop trough the canvas' data to find first colored pixel
var data = ctx.getImageData(0, 0, c.width, c.height).data,
minY = 0, len = data.length;
for (var i = 0; i < len; i += 4) {
// when you found it
if (data[i] != 0) {
// get pixel's y position
minY = Math.floor(i / 4 / c.width);
break;
}
}
// and print out the results
out.innerText = c.height - minY + 'px';
EDIT:
I even made jQuery plugin for this: https://github.com/maciek134/jquery-textHeight
Enjoy.

JS canvas white lines when scaling

Using JavaScript I am displaying an array on an html 5 canvas. The program uses c.fillRect() for each value in the array. Everything looks normal until I scale it using c.scale(). After being scaled white lines are visible between the squares. I do know their white because that is the color of the background (When the background changes their color changes too).
Since the squares are 5 units apart I tried setting their width to 5.5 instead of 5; this only remove the white lines when zoom in far enough, but when zooming out the white lines were still there.
This is my code (unnecessary parts removed):
function loop()
{
c.resetTransform();
c.fillStyle = "white";
c.fillRect(0, 0, c.canvas.width, c.canvas.height);
c.scale(scale, scale);
c.translate(xViewportOffset, yViewportOffset);
...
for(var x = 0; x < array.length; x++)
{
for(var y = 0; y < array[x].length; y++)
{
...
c.fillStyle = 'rgb(' + r + ',' + g + ',' + b + ')';
c.fillRect(0 + x * 5, 200 + y * 5, 5, 5);
}
}
...
}
No scaling:
Zoomed in:
Zoomed out:
(the pattern changes depending on the amount of zoom)
Thanks for any help and if any other information is needed please let me know.
Update:
I am using Google Chrome
Version 71.0.3578.98 (Official Build) (64-bit)
This is probably because you are using non-integer values to set the context's scale and/or translate.
Doing so, your rects are not on pixel boundaries anymore but on floating values.
Let's make a simple example:
Two pixels, one at coords (x,y) (11,10) the other at coords (12,10).
At default scale, both pixels should be neighbors.
Now, if we apply a scale of 1.3, the real pixel-coords of the first square will be at (14.3,13) and the ones of the second one at (15.6,13).
None of these coords can hold a single pixel, so browsers will apply antialiasing, which consist in smoothing your color with the background color to give the impression of smaller pixels. This is what makes your grids.
const ctx = small.getContext('2d');
ctx.scale(1.3, 1.3);
ctx.fillRect(2,10,10,10);
ctx.fillRect(12,10,10,10);
const mag = magnifier.getContext('2d');
mag.scale(10,10);
mag.imageSmoothingEnabled = false;
mag.drawImage(small, 0,-10);
/* it is actually transparent, not just more white */
body:hover{background:yellow}
<canvas id="small" width="50" height="50"></canvas><br>
<canvas id="magnifier" width="300" height="300"></canvas>
To avoid this, several solutions, all dependent on what you are doing exactly.
In your case, it seems you'd win a lot by working on an ImageData which would allow you to replace all these fillRect calls to simpler and faster pixel manipulation.
By using a small ImageData, the size of your matrix, you can replace each rect to a single pixel. Then you just need to put this matrix on your canvas and redraw the canvas over itself at the correct scale after disabling the imageSmootingEnabled flag, which allows us to disable antialiasing for drawImage and CanvasPatterns only.
// the original matrix will be 20x20 squares
const width = 20;
const height = 20;
const ctx = canvas.getContext('2d');
// create an ImageData the size of our matrix
const img = ctx.createImageData(width, height);
// wrap it inside an Uint32Array so that we can work on it faster
const pixels = new Uint32Array(img.data.buffer);
// we could have worked directly with the Uint8 version
// but our loop would have needed to iterate 4 pixels every time
// just to draw a radial-gradient
const rad = width / 2;
// iterate over every pixels
for(let x=0; x<width; x++) {
for(let y=0; y<height; y++) {
// make a radial-gradient
const dist = Math.min(Math.hypot(rad - x, rad - y), rad);
const color = 0xFF * ((rad - dist) / rad) + 0xFF000000;
pixels[(y * width) + x] = color;
}
}
// here we are still at 50x50 pixels
ctx.putImageData(img, 0, 0);
// in case we had transparency, this composite mode will ensure
// that only what we draw after is kept on the canvas
ctx.globalCompositeOperation = "copy";
// remove anti-aliasing for drawImage
ctx.imageSmoothingEnabled = false;
// make it bigger
ctx.scale(30,30);
// draw the canvas over itself
ctx.drawImage(canvas, 0,0);
// In case we draw again, reset all to defaults
ctx.setTransform(1,0,0,1,0,0);
ctx.globalCompositeOperation = "source-over";
body:hover{background:yellow}
<canvas id="canvas" width="600" height="600"></canvas>

HTML5 canvas rectangles using easeljs

I am creating a multiple choice quiz on a canvas.
Naturally for the options I used squares as checkBoxes. Now I have put an event Listener on clicking a checkbox, it should get colored. However the listener doesnt work on entire square only the border. May I know how can the problem be resolved and also I wanted to know how to know if a square is filled with a color.
Function for loading options with oIndex from 0,1, till numberOfOptions
pHeight is a a global variable, optionChecks- the checkbox and optionsClick- the invisible rectangle for clicking on
function loadOption(oIndex){
optionChecks[oIndex] = new createjs.Shape();
optionChecks[oIndex].graphics.beginStroke("blue");
optionChecks[oIndex].graphics.setStrokeStyle(2);
optionChecks[oIndex].graphics.drawRect( canvas2.width*0.05 ,pHeight+20 ,20 ,20);
stage.addChild(optionChecks[oIndex] );
stage.update();
var y1 = pHeight+15;
var ht = y1;
var maxWidth = canvas2.width *0.8;
var text = problemOptions[oIndex];
var lineHeight = 25;
var x = (canvas2.width - maxWidth) / 2;
var y = y1;//pHeight+15;
//function for wrapping text
wrapText(ctx2, text, x, y, maxWidth, lineHeight);
ht = wHeight +10;
stage.update();
optionClicks[oIndex] = new createjs.Shape();
optionClicks[oIndex].graphics.beginStroke("black");
optionClicks[oIndex].graphics.setStrokeStyle(2);
optionClicks[oIndex].graphics.drawRect( canvas2.width*0.05,y1,maxWidth-500 ,pHeight-y1+10);
optionClicks[oIndex].addEventListener("click", handleOption);
stage.addChild(optionClicks[oIndex] );
}
Thanks in advance.
You could draw another opaque rect and use the button's hitArea - http://createjs.com/Docs/EaselJS/classes/DisplayObject.html#property_hitArea

How to add the vertical parallel lines in the rectangle?

I want to add the vertical lines when I draw rectangle. The no of lines is dependent on the user and can be read from the text box.
I know the logic but somehow I am not able to get the answer.
I am calculating the width of the rectangle and then diving the width on the basis of no of vertical lines.
Click the checkbox near rectangle and draw using mouse down events
Please let me know where I am going wrong.
function PlotPitch()
{
var iPatches = document.getElementById('txtPatchCount').value;
var iTop = mySel.y;
var iBottom = mySel.y + mySel.h;
var iLeft = mySel.x;
var iX = iLeft;
canvas = document.getElementById('canvas2');
context = canvas.getContext('2d');
for (var iPatch=1; iPatch<iPatches; ++iPatch) {
iX = iLeft + iPatch*mySel.w/iPatches;
context.moveTo(iX, iTop);
context.lineTo(iX, iBottom);
}
context.lineWidth=0.25;
context.stroke();
}
http://jsfiddle.net/K5wcs/4/
If I am adding this the code is breaking and I am not able to draw anything.
What you should do if you have 'strange' behaviour is to separate concerns, so in this case that could be by creating a function that you test separately, which draws the lines, then once it's tested ok, plug it in code by just calling the function. You should find quickly.
So begin by testing this :
function drawLines(Context, mySel, iPatches) {
var iTop = mySel.y;
var iBottom = mySel.y + mySel.h;
var iLeft = mySel.x;
var iX = iLeft;
var colWidth = mySel.w/iPatches ;
for (var iPatch=1; iPatch<iPatches; ++iPatch) {
iX += colWidth;
Context.moveTo(iX, iTop);
Context.lineTo(iX, iBottom);
}
Context.lineWidth=0.25;
Context.stroke();
}
Good luck.

Categories