Can't draw a line on the canvas - javascript

I am having trouble drawing a line on the HTML canvas with JavaScript. For the record, I don't want to use any pre-written line-drawing methods, I need to do it using pixel manipulation. I tried drawing a line on a 500x500 pixel canvas that I already gave data with this
function drawBackgtoundAndLine()
{
var cnvs = document.getElementById("cnvs");
var cont = cnvs.getContext("2d")
var imdt = cont.getImageData(0,0,500,500)
//Fill canvas with a color
for ( var i = 0 ; i < imdt.data.length ; i = i + 4)
{
imdt.data[i] = 200;
imdt.data[i+1] = 100;
imdt.data[i+2] = 0;
imdt.data[i+3] = 255;
}
//Draw a horizontal line
var index = 0;
for ( var c = 0 ; c < 500 ; c++)
{
index = (4*c)+488000;
imdt.data[index] = 0;
imdt.data[index+1] = 0;
imdt.data[index+2] = 0;
imdt.data[index+3] = 255;
}
cont.putImageData( imdt , 0 , 0 )
}
You can see it in action in this fiddle. My math, by the way, that gave me the second for loop to draw a line is:
I want to color the whole 245th row. So, to pass over the first 244 rows, I multiply 2000(the number of data points in each row) times 244 rows to get 488000. Then I cycle through the loop 500 times to hit each pixel in the row, and add the 488000 to get to the right row. I'd really appreciate an explanation/fix for the 245th row not turning black.

You did not set the canvas size.
Note that the CSS size is only about display, not the number of pixels in the canvas.
You need to set the real canvas size for example with:
cnvs.width = 500;
cnvs.height = 500;
Remember that when you set the height/width the canvas is cleared (even if the value is the same as the current size), also remember that to get pixel-perfect graphics you need to keep the canvas size the same size as the element on the page (i.e. cnvs.offsetWidth and cnvs.offsetHeight).

Related

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.

how to change the color of a pixel in javascript

i'm currently trying to figure out how to change the color of a black pixel into yellow. i understand that yellow has 255 green and 255 red, with 0 blue(i think).
So i'm trying to set the Green and Red to 255 and the blue to 0 but the image continues to remain black. This is my code:
var img = new SimpleImage(200,200);
print(img);
for (var pixel of img.values()) {
var newG = 0 - pixel.getGreen();
var newR = 255 - pixel.getRed();
var newB = 255 - pixel.getBlue();
var Black = newG + newR + newB;
pixel.setGreen(newG);
pixel.setRed(newR);
pixel.setBlue(newB);
}
by the way, the class "SimpleImage" is an image of a black screen, which I am trying to turn yellow. Let me know if more info is needed, Thanks!
I'm sure there's more than one way to do this. And many people here are more experienced than me with the graphics APIs.
I would do it by placing the image in a canvas, reading the canvas by iterating through each pixel, make your color change (yes, it's R 255 G 255 B 000) and write to the canvas by iterating through it.
Loading the canvas with the image is simple and explained in many places here and also on w3Schools if you prefer.
function colorFilter()
{
//Access the canvas
c = document.getElementById("canvas1");
context = c.getContext("2d");
//Declare variables
var imgData = context.getImageData(0,0,canvas1.width,canvas1.height);
var data = imgData.data;
var red = new Array();
var green = new Array();
var blue = new Array();
var alpha = new Array();
//Read image and make changes on the fly as it's read
for (i = 0; i < data.length; i += 4)
{
red[i] = imgData.data[i];
if (red[i] == 0) red[i] = 255;
green[i] = imgData.data[i+1];
if (green[i] == 0) green[i] = 255;
blue[i] = imgData.data[i+2]; // no change, blue == 0 for black and for yellow
alpha[i] = imgData.data[i+3]; // Again, no change
}
// Write the image back to the canvas
for (i = 0; i < data.length; i += 4)
{
imgData.data[i] = red[i];
imgData.data[i+1] = green[i];
imgData.data[i+2] = blue[i];
imgData.data[i+3] = alpha[i];
}
context.putImageData(imgData, 0, 0);
}
This is my first answer here, it may not be perfect, but I use this regularly, so it does work.
If you're using a 3rd party library, I would think that it's not needed, since the code I used is pure JavaScript.
Hope that helps as much as others here have helped me.
It's pretty simple. You just need to assign the respective RGB values
var img = new SimpleImage(200,200);
print(img);
for (var pixel of img.values()) {
pixel.setRed(255);
pixel.setGreen(255);
pixel.setBlue(0);
}
print(img); //Print result
Don't forget to print the changed image to see the result.
Actually blacks Rob value is 0,0,0. So u made a mistake in the values. For green it should be 255-pixel.getGreen(),for red also 255-pixel.getRed. Then u have to also put print(img) after the for loop for it to work.
var img = new SimpleImage(200,200);
for (var pixel of img.values()) {
pixel.setGreen(255);
pixel.setRed(255);
}
print(img);

Drawing squares with createJS creates rectangles

Apart from the title not being entirely correct I'm having a problem drawing squares with createJs. I'm drawing rectangles with equally big sides which in general generates a square, but not for me, I'm getting this:
The code I'm using is as follows (very much simplified):
function getRandomNumber(max)
{
return Math.floor(Math.random() * max);
}
var colors = ["Red", "Green", "Blue"];
function createTileArea()
{
var stage = new createjs.Stage("tileArea");
stage.name = "stage";
var size = 50;
for (row = 0; row < 10; row++) {
for (col = 0; col < 10; col++) {
var id = row + "_" + col;
var color = colors[getRandomNumber(3)];
var tile = new createjs.Shape();
tile.graphics.beginFill(color);
tile.graphics.drawRect(0, 0, size, size);
tile.graphics.endFill();
tile.x = col * size;
tile.y = row * size;
tile.height = size;
tile.width = size;
tile.name = id;
stage.addChild(tile);
}
}
stage.update();
}
createTileArea();
I have a fiddle here: http://jsfiddle.net/QWP3Z/2/
My question is: I have a canvas that has 500px width and height and I'm generating 10 rectangles that are 50px high and wide, so why am I getting 6 horizontal squares and three vertical squares that are all rectangles?
Is this some sort of scaling problem?
do not use the css style to resize the canvas, but rather change its width and height directly, either in html or in code.
Otherwise createJs will set the width and height, which seems to default to 300X150, and the css will act as a zoom to put it back to -in your example- 500X500.

Drawing zoomable audio waveform timeline in Javascript

I have raw 44,1 kHz audio data from a song as Javascript array and I'd like to create a zoomable timeline out of it.
Example timeline from Audacity:
Since there are millions of timepoints normal Javascript graphics libraries probably don't cut it: I think, not sure, that normal graph libraries will die on this many timepoints. But does there exist already libraries for this sort of visualization for JS? Canvas, webGL, SVG all are acceptable solutions.
A solution preferably with zoom and pan.
Note that this happens strictly on client side and server-side solutions are not accetable.
I've looked into this same problem pretty extensively. To the best of my knowledge, the only existing project that does close to what you want is wavesurfer.js. I haven't used it, but the screenshots and the description sound promising.
See also this question.
Best of luck.
You cannot simply take the the waveform data and render all data points, this is terribly inefficient.
Variable explanation:
width: Draw area width in pixels, max is screen width
height: Same as width but then height of draw area
spp: Samples per pixel, this is your zoom level
resolution: Number of samples to take per pixel sample range, tweak for performance vs accuracy.
scroll: You will need virtual scrolling for performance, this is the scroll position in px
data: The raw audio data array, probably several million samples long
drawData: The reduced audio data used to draw
You are going to have to only take the samples that are in the viewport from the audio data and reduce those. Commenly this results in a data set that is 2 * width, you use this data set to render the image.
To zoom out increase spp, to zoom in decrease it. Changing scroll value pans it.
The following code has O(RN) complexity where N is width and R is resolution. Maximum accuracy is at spp <= resolution.
The code will look something like this, this gets the peak values, you could do rms or average as well.
let reduceAudioPeak = function(data, spp, scroll, width, resolution) {
let drawData = new Array(width);
let startSample = scroll * spp;
let skip = Math.ceil(spp / resolution);
// For each pixel in draw area
for (let i = 0; i < width; i++) {
let min = 0; // minimum value in sample range
let max = 0; // maximum value in sample range
let pixelStartSample = startSample + (i * spp);
// Iterate over the sample range for this pixel (spp)
// and find the min and max values.
for(let j = 0; j < spp; j += skip) {
const index = pixelStartSample + j;
if(index < data.length) {
let val = data[index];
if (val > max) {
max = val;
} else if (val < min) {
min = val;
}
}
}
drawData[i] = [min, max];
}
return drawData;
}
With this data you can draw it like this, you could use lines, svg etc:
let drawWaveform = function(canvas, drawData, width, height) {
let ctx = canvas.getContext('2d');
let drawHeight = height / 2;
// clear canvas incase there is already something drawn
ctx.clearRect(0, 0, width, height);
for(let i = 0; i < width; i++) {
// transform data points to pixel height and move to centre
let minPixel = drawData[i][0] * drawHeigth + drawHeight;
let maxPixel = drawData[i][1] * drawHeight + drawHeight;
let pixelHeight = maxPixel - minPixel;
ctx.fillRect(i, minPixel, 1, pixelHeight);
}
}
I have used RaphaelJS for SVG rendering in the browser at it has performed very well. It is what I would go for. Hopefully SVG will be up to the task.

HTML 5 canvas animation - objects blinking

I am learning ways of manipulating HTML 5 Canvas, and decided to write a simple game, scroller arcade, for better comprehension. It is still at very beginning of development, and rendering a background (a moving star field), I encountered little, yet annoying issue - some of the stars are blinking, while moving. Here's the code I used:
var c = document.getElementById('canv');
var width = c.width;
var height = c.height;
var ctx = c.getContext('2d');//context
var bgObjx = new Array;
var bgObjy = new Array;
var bgspeed = new Array;
function init(){
for (var i = 1; i < 50; i++){
bgObjx.push(Math.floor(Math.random()*height));
bgObjy.push(Math.floor(Math.random()*width));
bgspeed.push(Math.floor(Math.random()*4)+1);
}
setInterval('draw_bg();',50);
}
function draw_bg(){
var distance; //distace to star is displayed by color
ctx.fillStyle = "rgb(0,0,0)";
ctx.fillRect(0,0,width,height);
for (var i = 0; i < bgObjx.length; i++){
distance = Math.random() * 240;
if (distance < 100) distance = 100;//Don't let it be too dark
ctx.fillStyle = "rgb("+distance+","+distance+","+distance+")";
ctx.fillRect(bgObjx[i], bgObjy[i],1,1);
bgObjx[i] -=bgspeed[i];
if (bgObjx[i] < 0){//if star has passed the border of screen, redraw it as new
bgObjx[i] += width;
bgObjy[i] = Math.floor(Math.random() * height);
bgspeed[i] = Math.floor (Math.random() * 4) + 1;
}
}
}
As you can see, there are 3 arrays, one for stars (objects) x coordinate, one for y, and one for speed variable. Color of a star changes every frame, to make it flicker. I suspected that color change is the issue, and binded object's color to speed:
for (var i = 0; i < bgObjx.length; i++){
distance = bgspeed[i]*30;
Actually, that solved the issue, but I still don't get how. Would any graphics rendering guru bother to explain this, please?
Thank you in advance.
P.S. Just in case: yes, I've drawn some solutions from existing Canvas game, including the color bind to speed. I just want to figure out the reason behind it.
In this case, the 'Blinking' of the stars is caused by a logic error in determining the stars' distance (color) value.
distance = Math.random() * 240; // This is not guaranteed to return an integer
distance = (Math.random() * 240)>>0; // This rounds down the result to nearest integer
Double buffering is usually unnecessary for canvas, as browsers will not display the drawn canvas until the drawing functions have all been completed.
Used to see a similar effect when programming direct2d games. Found a double-buffer would fix the flickering.
Not sure how you would accomplish a double(or triple?)-buffer with the canvas tag, but thats the first thing I would look into.

Categories