Well I am trying to make an animation where there are a lot of dots on the canvas and on mouseover they are hidden and after a timeout shown again.
The mouseover effect shouldn't just happen on the exact spot the mouse is but in e.g. a box of 20x20 pixels.
In my prototype I have these loops...
for (var i = -10; i <= 10; i++) {
for (var j = -10; j <= 10; j++) {
var imagedata = c.getImageData(x+i, y+j, 1, 1).data;
if (imagedata[0] == 99) {
fadeInRectangle(c, x+i,y+j);
}
}
}
Is there a quicker way to find dots where the mouse is? It doesn't have to be a square it can be a circle also where the position is checked... I don't care. Thanks in advance
The fiddle: https://jsfiddle.net/vrjw996h/
What I would do is create an array of dot objects, each one storing the dot's x and y position, as well as a visible property (Like {x:10, y: 40, visible: true}). Whenever the mouse moves, loop through each object and check the distance between the mouse and the dot. If a dot is found to be in range, set its property visible to false. After that part, still inside onmousemove, clear the canvas and redraw each dot, skipping those with visible: false. After a few seconds, set visible back to true.
https://jsfiddle.net/Howzieky/vrjw996h/1/
Related
Is there a way to find out if an element is obscured by another element that is not a child of the first one? Because I have a menu that I like to hide when you click anywhere on the page, but not when there is currently a lightbox open that obscures the menu.
function isObscured(element) {
// get element at element's position
var elem = document.elementFromPoint(element.offsetLeft, element.offsetTop);
// return whether obscured element has same parent node (will also return false if
// element === elem)
return elem.parentNode !== element.parentNode;
}
http://jsfiddle.net/pimvdb/tKtEV/
You have to evaluate it yourself.
Take into consideration coordinates and size of the frontmost element, then check for "collision" with the elements behind it you need to control.
It's basically a check for overlapping squares.
If you know which element could be obscuring the "target", you can use the method getBoundingClientRect to find the edges of the obscurer and the "target". Then it's a problem of finding if two rectangles overlap.
It could seem daunting to calculate that, but to separate the X and Y axes is the Egg of Columbus here.
Two rectangles do NOT overlap if:
1) their X axis range doesn't overlap
a. rect1's bottom is above rect2's top
b. rect1's top is below rect2's bottom
2) their Y axis range doesn't overlap
a. rect1's right edge is to the left of rect2's left edge
b. rect1's left edge is to the right of rect2's right edge
Otherwise--that is, if their X axis overlaps and their Y axis also overlaps--they definitely DO overlap.
function overlaps(el1, el2){
var rect1 = el1.getBoundingClientRect();
var rect2 = el2.getBoundingClientRect();
if (rect1.bottom < rect2.top // 1a
|| rect2.bottom < rect1.top // 1b
|| rect1.right < rect2.left // 2a
|| rect2.right < rect1.left) { // 2b
return false;
}
return true;
}
I'm working on a project that requires me to make a text box in which the user will enter a number between 1 and 10. The program will then take this input and create a series of shapes (if the user enters 4, then 4 shapes should be drawn on the canvas). These shapes are also supposed to be different sizes, each shape smaller than the last. The shapes are also supposed to each be a different color.
The problem is I have no idea how to do this, I know how to create a canvas and draw shapes, but the whole, different sizes, colors, and drawing as many shapes as is entered in the text box is throwing me off. Could anyone point me in the right direction?
Firstly to draw the number of boxes that the user inputs we can just have the input be used as a for loop condition:
// "input" is the users entered number, make sure to parseInt() it
for(var i = input; i > 0; i--) {
// Draw shape of size "i" and color "colors[i-1]"
}
Since 10 is the largest number we can have we can make an array of ten colors: colors = ["red","blue","yellow", ... ,"green"]. Now the reason why I went from input to 0 in the for loop is because we have an number that's getting smaller, we can use i as the size of the box in the loop. And you can use i for positioning for the boxes (for example times it by 10 and use it as a varying x position). Or simply make the x and y random.
Now with the concept down a full example might look like so:
var ctx = document.getElementById("canvas").getContext("2d");
var canvasWidth = document.getElementById("canvas").width;
var yPos = 100;
var input = 8;
var colors = ["red","blue","yellow","pink","grey","black","aqua","brown","cyan","green"]
for(var i = input; i > 0; i--) {
// Draw shape of size "i" and color "colors[i]"
ctx.fillStyle = colors[i-1];
ctx.fillRect(canvasWidth - i*25, yPos, i*3, i*3);
}
canvas{ border: 1px solid black; }
<canvas id="canvas" width=300 height=300></canvas>
Edit: Changed sizes to show up left-to-right, per OP's request.
When the user click submit, you should call a javascript function which does the following:
Get the number of shapes to be drawn
Get canvas element by ID
Based on the number of shapes, using conditional statements, draw the shapes (the starting point would be previous object x (or y depending on your preference) + current x (or y))
To get different colors use on border use:
ctx.strokeStyle="green"; & for different fill color use: ctx.fillStyle="red";
For tutorial on how to go about different shapes refer: https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Drawing_shapes
Fiddle:
http://jsfiddle.net/7v85bzam/1/
You may need to experiment a bit with the positioning (x, y co-ordinates)
So I'm trying to solve a graphics problem. Basically there's a container, let's say its 600px wide. If there is only one rectangle in that container(with no overlapping rectangles) it takes up the width of it. However, the issue is when these rectangles overlap, the width has to shrink accordingly. We are given the top left and bottom left y-coordinates of this rectangle. (such as it starts 60px down and ends 120 px down the big container)
So I wrote an overlap algorithm that checks if an overlap exists and counts the number of rectangles that the rectangle overlaps with (including itself). Then I divide the container width by the maximum number of elements overlapped to get the width of the smaller rectangles.
for (i = 0; i < events.length; i++) {
var event = events[i];
var numCollisions = 0;
for (j = 0; j < events.length; j++) {
var eventCmp = events[j];
if (event.start <= eventCmp.start && event.end > eventCmp.start ||
event.start < eventCmp.end && event.end >= eventCmp.end) {
numCollisions++;
}
}
However, I've noticed a huge problem with this. If you look at this picture below the rightmost rectangle has two overlapping rectangles. By my algorithm you would get the container width/3 (including the rectangle itself) which is incorrect. The actual answer is the container width/2.
So the issue is (after this long winded explanation) that I need to find if two rectangles are horizontally aligned. I've been busting my head on this for hours. Any hints on how I can do this?
Well, the easiest answer is divide by 2 IF you have a collision (not caring how many collisions you actually have). If you need something more sophisticated, can you show a more sophisticated case?
a less than optimal, but easy to implement algo would be:
foreach y coord in the main container
rects = find all rects which contain this y coord
foreach rects as rect
rect.maxHorizNeighbors = max(rects.length, rect.maxHorizNeighbors)
theres still some other issues you'll need to address, but this should get you started on your homework assignment. in particular, theres a case where you could possibly end up with horizontal gaps. ill let you find that on your own.
Please have a look at this page
Try to click one of big buttons on the panel and to move your mouse on the y axis. The buttons start rotating as they should. Now, leave your mouse and click them again: why the heck do they insist into getting back in their original position?!
Here's the code snippet related to the button rotation. Please note that the code executed in the draw loop is called every 30ms.
// method
Button.prototype.Rotate = function(yDrag){
this.rotation = - (yDrag - this.rotationYClick) / 80
}
// draw loop
function drawLoop() {
if (buttons[n].roating == true && mousePressed == true)
buttons[n].Rotate(mousePosition_y)
else
buttons[n].roating = false
} // end draw loop
// fired ONLY ONCE when mouse is clicked
function mouseDown() {
buttons[n].roating = true
buttons[n].rotationYClick = mousePosition_y
}
I've intentionally avoided to post most of the code as I'm sure the problem lies in these lines.
Code explanation: when you click on a button, the position of the mouse is stored in the variable rotationYClick of the class Button. While you drag your mouse, the mouse position is constantly compared to the stored value: the distance of your current mouse position from the point which you clicked will determine how much the button rotate. Problem is, as you click it again the rotation is set back to zero. How to solve?!
Here's one of my failed tries by changing the method
// method
Button.prototype.Rotate = function(yDrag){
this.rotation += - (yDrag - this.rotationYClick) / 80
}
Thanks a lot!
Where you are setting this.rotation. Use += instead of =
The problem is that you reevaluate the rotation on each drag without taking into consideration the present rotation angle of the button.
EDIT
Ok, so its not that simple :)
Basically you'll have to keep two variables. One for the original rotation angle, and one that represents the current rotation. So, it becomes something like this:
this.rotation = this._originalRotation - ((yDrag-this.rotationYClick)/80);
and mouseDown becomes:
// fired ONLY ONCE when mouse is clicked
function mouseDown() {
buttons[n].roating = true
buttons[n].rotationYClick = mousePosition_y
buttons[n]._originalRotation = this.rotation;
}
I am using the Raphael library from http://raphaeljs.com/ and work on a chart library. For this library it is useful when the Y-axis are inverted. Now 0,0 is at the top left but I want it to be at the bottom left.
There is a possibility to apply a scale matrix to an element but I want the coordinates to be inverted for whatever I draw. Any clues?
The only way I could figure out to do this was to apply a negative scaling to the svg element using CSS (see this fiddle). (I used jQuery to add the styles).
This is not without problems, though. For example, text is going to be mirrored, unless you do something to un-mirror it (like applying the invert() method I added to elements using Raphael.el):
Raphael.el.invert = function() {
this.transform('s1,-1');
};
Also, if you are going to be interacting with the elements using your mouse, you will have to tweak things. Note that the black circle uses a pretty standard mouseMove function, but it doesn't work - it moves in the wrong direction in y. So you have to do something like I did with the other circles:
function cMove(dx, dy, x,y) {
this.attr('cx', x);
this.attr('cy', paperHeight - y);
};
In short, this is not at all elegant, and no other things I tried were really any better. I know this isn't what you want to hear, but I would recommend getting used to the coordinate system as it is, unless you just plan on displaying static charts.
One small issue with Mike C's resolution is that you have to know if the text is going to be inverted in the end when you create the text. If you want to ensure right-side-up text at the end (after applying other transformations) I found it works well to alter the text element's .transform() to flip the scale of text to right side up at the end.
function InvertText(ObjSet){
// This function resets the inversion of text such that it is always right side up
// ObjSet is a raphael paper.set() object
for (var i=0; i<ObjSet.items.length; i++){
var ThisObj = ObjSet.items[i];
if (ThisObj.type == 'text'){
var tArr = ThisObj.transform();
// Find the scaling factor
for (var j=0; j<tArr.length; j++){
if (tArr[j][0] == 's'){
tArr[0][2] = 1;
break;
}
}
ThisObj.transform(tArr);
}
}
}
You can use like this:
var ObjSet = paper.set().push(
paper.text(0,10,'FirstText'),
paper.path('M,0,0,v,100,h,20,v,-100,h,-20'),
paper.circle(0,0,5)
);
//Flip everything on the y-axis
ObjSet.transform('s,1,-1, T,100,100');
// Make the text right-side-up
InvertText(ObjSet);
Here's how to do it just with RaphaelJS transforms, no CSS transforms.
var SCALE = 2;
var paper = Raphael(0, 0, 400, 700);
// box notched at bottom-center and right-center
var p = paper.path("M0,0 L100,0 L100,40 L90,50 L100,60 L100,100 L60,100 L50,90 L40,100 L0,100 Z");
var bounds = p.getBBox();
p.attr({
stroke: 'none',
fill: [90, '#578A6E', '#34573E'].join("-")
})
.transform("t"+ (-bounds.width/2) +","+ (-bounds.height/2) +
"s"+ SCALE +","+ (-SCALE) +
"t"+ (bounds.width/2) +","+ (-bounds.height/2));
Raphael applies scale transforms from the center of the element's bounding box, rather than its origin. To invert the y-axis, offset before scaling, then offset again after.