How to add the vertical parallel lines in the rectangle? - javascript

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.

Related

animating dots in canvas, strange line behaviours with speed of dots > 1 by frame

Trying to animate dots(imageData 1x1) on a javascript canvas to make a starfield.
The strange thing is when those dots move at a speed higher than 1, there is like a flickering or anything else showing not a dot but a line.
here is a fiddle to show the strangeness: http://jsfiddle.net/xp6xd8q1/1/
function clearCanvas() {
ctx.fillStyle = '#000000';
ctx.fillRect(0,0,w,h);
}
function stars() {
this.manyStars = [];
this.addStars = function(nb) {
var i,x,y;
for(i=0;i<nb;i++) {
x = Math.floor(Math.random() * w);
y = Math.floor(Math.random() * h);
this.manyStars.push({x: x,y: y,s: 5}); // dot speed is s
}
}
this.move = function() {
var i,l;
for(i=0,l = this.manyStars.length;i<l;i++) {
this.manyStars[i].x = this.manyStars[i].x - this.manyStars[i].s;
if(this.manyStars[i].x < 0) {
this.manyStars[i].x = w + this.manyStars[i].x;
}
}
}
this.drawStars = function() {
var i,l;
for(i=0,l = this.manyStars.length;i<l;i++) {
ctx.putImageData(dot,this.manyStars[i].x,this.manyStars[i].y);
}
}
}
function run() {
clearCanvas();
s.move();
s.drawStars();
window.requestAnimationFrame(run);
}
//
window.requestAnimationFrame = window.requestAnimationFrame||window.mozRequestAnimationFrame ||window.webkitRequestAnimationFrame||window.msRequestAnimationFrame;
var cv = document.createElement('canvas');
var w = window.innerWidth, h = window.innerHeight;
cv.width = w;
cv.height = h;
var ctx = cv.getContext('2d');
document.body.appendChild(cv);
//
var dot = ctx.createImageData(1,1);
dot.data = [255,255,255,255];
s = new stars();
s.addStars(10);
window.requestAnimationFrame(run);
Any idea on this is very welcomed !
I see it too. The dots appear to be stretched when they move. I screenshotted the canvas at several speeds. The dots really are just 1x1 pixel.
I believe you may be experiencing Display Motion Blur. It's a result of how displays work, and also because the vision cells in your eye take a bit of time to readjust when light changes.
There's not really much you can do about that, except try to hide it. It becomes less and less apparent the larger your moving objects are, and the slower they move.
It also becomes less apparent when the display refresh rate increases. See this example. Since you can't control the users' monitor's refresh rates, this doesn't really help you.

How to interact with shapes in Pixijs?

I am trying to get the id of the shape my mouse is currently hovering over.
my shapes are in a container
// creating the layers
gridLayer = new PIXI.DisplayObjectContainer ();
gridLayer.setInteractive(true);
stage.addChild(gridLayer);
and i am creating each shape like this;
function drawHexagon(x,y, size, gap,scale, color, iterI, iterJ, type) {
var shape = new PIXI.Graphics();
// set a fill and line style
shape.beginFill(color);
shape.lineStyle(1, 0xa0a0a0, 1);
size = size-gap;
for (i = 0; i < 7; i++) {
angle = 2 * Math.PI / 6 * (i + 0.5);
var x_i = x + size * Math.cos(angle);
var y_i = y + size * Math.sin(angle);
if (i === 0) {
shape.moveTo(x_i, scale *y_i)
}
else {
shape.lineTo(x_i, scale * y_i)
}
};
shape.endFill();
// calculate and save the axial coordinates
var cX = iterJ - (iterI - (iterI&1)) / 2;
var cZ = iterI;
var cY = -1*(cX+cZ);
shape.hexId = cX + "x" + cY + "y" + cZ + "z";
shape.hexPosX = x;
shape.hexPosY = y;
shape.setInteractive(true);
shape.mouseover = function(mouseData){
console.log("MOUSE OVER " + shape.hexId);
}
shape.click = function(mouseData){
console.log("MOUSE CLICK " + shape.hexId);
}
gridLayer.addChild(shape);
}
However, clicking on any shape or hovering over it is not showing me anything in the console. what am i doing wrong?
i have tried both
shape.setInteractive(true)
and
shape.interactive = true
but neither seems to work for me.
EDIT: i have added a jsfiddle. it doesnt works (i dont know how to link things in jsfiddle) but you can see my entire code in there.
http://jsfiddle.net/9aqHz/1/
For a PIXI.Graphics object to be interactive you need to set a hitArea shape (it can be a Rectangle, Circle or a Polygon):
shape.hitArea = new PIXI.Polygon([
new PIXI.Point(/* first point */),
new PIXI.Point(/* second point */),
new PIXI.Point(/* third point */),
new PIXI.Point(/* fourth point */),
new PIXI.Point(/* fifth point */)
]);
Another approach would be to generate a texture from the shape and use a Sprite, but the hit area would be the entire rectangular bounds of the hexagon:
var texture = shape.generateTexture();
var sprite = new PIXI.Sprite(texture);
sprite.setInteractive(true);
sprite.anchor.set(0.5, 0.5);
Fiddle with this applied to your example
#imcg I updated your code so it workes with Pixi 3.0.8
- sprite.setInteractive(true);
+ shape.interactive = true;
+ shape.buttonMode = true;
- sprite.setInteractive(true)
+ sprite.interactive = true;
+ sprite.buttonMode = true;
http://jsfiddle.net/LP2j8/56/
I will add a bit of info for anyone who is in the same boat i was in;
When you define a shape as a geom, you have to explicitly state a hitarea.
So adding the following code makes it work;
shape.hitArea = new PIXI.Polygon(vertices);
shape.interactive = true;
shape.click = function(mouseData){
console.log("MOUSE CLICK " + shape.hexId);
}
But, when you define a shape as a sprite/texture, you dont need to do this.
in cases of sprites, just setting shape.interactive = true for the sprite is sufficient. You dont need to set the interactive property for the parent object or the stage.

Canvas WrapText function with fillText

I am creating a multiple choice quiz, however, the questions are too many characters and won't display on 1 line. I understand I have to use the wrapText function in order to paragraph it, I am just unsure how to implement this within my code. The Questions are set as strings in a variable (Questions), with the answers being strings in variable (Options). textpos1, defines the coordinates where I want my question to start, with textpos2 - textpos4 defining the coordinates where the separate answers start. These coordinates are in these positions to align to my background image.
JSFiddle here with full code however it doesn't seem to like my BG image on there... http://jsfiddle.net/danielparry8/6U9Rn/
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
var quizbg = new Image();
var Question = new String;
var Option1 = new String;
var Option2 = new String;
var Option3 = new String;
var mx=0;
var my=0;
var CorrectAnswer = 0;
var qnumber = 0;
var rightanswers=0;
var wronganswers=0;
var QuizFinished = false;
var lock = false;
var textpos1=25;
var textpos2=145;
var textpos3=230;
var textpos4=325;
var Questions = ["Which Manchester United Player won \n the 2008 Golden Boot with 31 Goals?","At which club did Bobby Charlton start his football career?","Which year did Wayne Rooney win the BBC Young Sports Personality of the year award?"];
var Options = [["Cristiano Ronaldo","Wayne Rooney","Ryan Giggs"],["Manchester United","Manchester City","Chelsea"],["2002","2003","2004"]];
The 'setQuestions' function grabs the appropriate question and answers and uses fillText to apply them to the canvas, my only issue being that the Question is displayed on one continuos line.
SetQuestions = function(){
Question=Questions[qnumber];
CorrectAnswer=1+Math.floor(Math.random()*3);
if(CorrectAnswer==1){Option1=Options[qnumber][0];Option2=Options[qnumber][1];Option3=Options[qnumber][2];}
if(CorrectAnswer==2){Option1=Options[qnumber][2];Option2=Options[qnumber][0];Option3=Options[qnumber][1];}
if(CorrectAnswer==3){Option1=Options[qnumber][1];Option2=Options[qnumber][2];Option3=Options[qnumber][0];}
context.textBaseline = "middle";
context.font = "16pt sans-serif,Arial";
context.fillText(Question,20,textpos1);
context.font = "14pt sans-serif,Arial";
context.fillText(Option1,20,textpos2);
context.fillText(Option2,20,textpos3);
context.fillText(Option3,20,textpos4);
}
Below is a wrapText function i have tried to implement into my design with no avail, If anyone can help that is greatly appreciated. Thanks!
function wrapText(context, text, x, y, maxWidth, fontSize, fontFace){
var words = text.split(' ');
var Questions = '';
var lineHeight=fontSize;
context.font=fontSize+" "+fontFace;
for(var n = 0; n < words.length; n++) {
var testLine = Questions + words[n] + ' ';
var metrics = context.measureText(testLine);
var testWidth = metrics.width;
if(testWidth > maxWidth) {
context.fillText(Questions, x, y);
Questions = words[n] + ' ';
y += lineHeight;
}
else {
Questions = testLine;
}
}
context.fillText(Questions, x, y);
return(y);
}
I experimented with your example code. You don't indicate exactly what you expect to be passed in the 'fontSize' argument to your 'wrapText' function, but I assumed it would be something like '16pt'. So you have this code:
var lineHeight=fontSize;
context.font=fontSize+" "+fontFace;
so now...
lineHeight === '16pt'
context.font === '16 pt sans-serif,Arial'
Then later your code says:
y+= lineHeight;
Now you have a problem, because adding '16pt' (lineHeight) to y doesn't make any sense. The value for 'y' is is a simple integer value (representing pixels). Trying to add the string, '16pt' to it results in a strange value and strange results.
I used the code from JSFiddle, copied it to my own sandbox. Then I cut/pasted from above, the code for wrapText.
I substituted for:
context.fillText(Question, 20, textpos1);
with:
wrapText(context,Question,20,textpos1,500,'16pt','sans-serif,Arial');
inside wrapText where
y = lineHeight;
instead use:
y = 16;
You should see your wrapped line. y = 16 is just an arbitrary number I chose. It is representing pixels...it isn't the same thing as 16pt.
Here's an example and a Demo: http://jsfiddle.net/m1erickson/QGz2R/
If you create a "class" then you can reuse that "class" to store and draw as many questions as you need.
First, create a Question & Answer class that holds:
the question text
each answer text
the bounding box of each answer that's drawn on the canvas
the correct answer
Example Code:
// a Question & Answer "class"
function QA(question,options,correctOption){
this.QBB=null; // the bounding box for the wrapped question text
this.Q=question;
this.A=[];
this.correct=correctOption;
for(var i=0;i<options.length;i++){
this.A.push({text:options[i],BB:null});
}
}
// Draw the question and possible answers on the canvas
// At the same time calculate the bounding boxes(BB)
// of each answer so that the BB can be used to test
// if the user clicked in the correct answer
QA.prototype.draw=function(x,y,maxWidth){
var BB=this.QBB=wrapText(this.Q,x,y,maxWidth,14,"verdana");
for(var i=0;i<this.A.length;i++){
y+=BB.height;
BB=this.A[i].BB=wrapText(i+". "+this.A[i].text,x,y,maxWidth,14,"verdana");
ctx.strokeRect(BB.x,BB.y,BB.width,BB.height);
}
return({x:x,y:y});
}
// test if the supplied mouseX/mouseY is inside any answer
QA.prototype.hitAnswer=function(x,y){
for(var i=0;i<this.A.length;i++){
var bb=this.A[i].BB;
if(x>bb.x && x<bb.x+bb.width && y>bb.y && y<bb.y+bb.height){
return(i);
}
}
return(-1);
}
Next,
Listen for mousedown events
Test if the user has clicked in any answer. You do this by testing if the mouse was clicked inside the bounding box of any of the answers.
If the user clicked in an answer alert whether they selected the correct answer or not.
Example code:
// Listen for mousedown and check if the user has
// clicked in an answer to the question
function handleMouseDown(e){
e.preventDefault();
e.stopPropagation();
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
var answer=q.hitAnswer(mouseX,mouseY);
if(answer==q.correct){
$results.text("Correct! #"+q.correct+" is the answer");
}else{
$results.text("Sorry! #"+answer+" is not the answer");
}
}
And here is the slightly modified wrapText code that now returns the bounding box of the text it just wrapped on the canvas:
// wrap the specified text and draw it to the canvas
// and return the bounding box of that wrapped text
function wrapText(text, x, y, maxWidth, fontSize, fontFace){
var startY=y;
var words = text.split(' ');
var line = '';
var lineHeight=fontSize+2;
ctx.font=fontSize+" "+fontFace;
y+=fontSize;
for(var n = 0; n < words.length; n++) {
var testLine = line + words[n] + ' ';
var metrics = ctx.measureText(testLine);
var testWidth = metrics.width;
if(testWidth > maxWidth) {
ctx.fillText(line, x, y);
line = words[n] + ' ';
y += lineHeight;
}
else {
line = testLine;
}
}
ctx.fillText(line, x, y);
return({x:x,y:startY,width:maxWidth,height:y-startY+4});
}

Drawing predefined number of dots inside a canvas

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...

HTML5 Canvas - how to zoom in the pixels?

How would one zoom in a Canvas to see the pixels? Currently when I try to use the scale() function the image is always antialiased.
The property mozImageSmoothingEnabled seems to work, but it's only for Firefox.
As far as I know any rescaling done by the browser will result in a smooth interpolation.
So as long as you want to do this with JS you will have to let JS do all the work. This means either finding a nice library or writing a function yourself. It could look like this. But I hope it's possible to make it faster. As it's one of the simplest scaling algorithms there are probably many people who thought up improvements to do it even faster.
function resize(ctx, sx, sy, sw, sh, tx, ty, tw, th) {
var source = ctx.getImageData(sx, sy, sw, sh);
var sdata = source.data;
var target = ctx.createImageData(tw, th);
var tdata = target.data;
var mapx = [];
var ratiox = sw / tw, px = 0;
for (var i = 0; i < tw; ++i) {
mapx[i] = 4 * Math.floor(px);
px += ratiox;
}
var mapy = [];
var ratioy = sh / th, py = 0;
for (var i = 0; i < th; ++i) {
mapy[i] = 4 * sw * Math.floor(py);
py += ratioy;
}
var tp = 0;
for (py = 0; py < th; ++py) {
for (px = 0; px < tw; ++px) {
var sp = mapx[px] + mapy[py];
tdata[tp++] = sdata[sp++];
tdata[tp++] = sdata[sp++];
tdata[tp++] = sdata[sp++];
tdata[tp++] = sdata[sp++];
}
}
ctx.putImageData(target, tx, ty);
}
This function would take the rectangle of size (sw,sh) at (sx,sy), resize it to (tw,th) and draw it at (tx,ty).
As far as I know the antialiasing behavior is not defined in the spec and will depend on the browser.
One thing you could try is if you set the canvas's width/height with CSS into for example 300x300 and then give the canvas width and height attributes of 150 and 150, it will appear "zoomed in" by 200%. I'm not sure whether this will trigger the antialiasing, but do give it a shot.

Categories