2

I have two canvases. I have made them circular using border-radius. The 2nd is positioned inside the first one (using absolute position).

I have click events on both circles. If you click on inside canvas, the color at the point of the click is loaded in the outside canvas with opacity varying from white to the picked color and finally to black. If you click on outer canvas the exact color value at that point is loaded in the text-box at the bottom

I am unable to click in red zones (as shown in figure below) of the outer canvas when using chrome. I tried z-idex, arcs but nothing is helping me. But In Firefox everything is working fine.

Note: You can drag the picker object in the outer circle. But if you leave it in red zones, you would not be able to click it again in Chrome. Clicking in green zone will get you its control again

Code in this JSFiddle

Edit

I excluded all irrelevant code to make it easy. Now there is only a container having two canvas. Filled simply with two distinct colors. Open following fiddle link in both chrome and firefox. Click on both cirles in different zones and see difference in chrome and firefox. I want them to behave in chrome as they do in firefox

Note I will ultimately draw an image in inner canvas.

Updated Fiddle Link

-im

Sami
  • 8,168
  • 9
  • 66
  • 99

1 Answers1

3

Your problem is because canvases currently are always rectangular, even if they don't look rectangular. Border radius makes the edges except the circle transparent, but it still doesn't stop events in Chrome on the corner areas. This is why you cannot click the bottom circle in those areas

I even tried putting it inside of a container that had a border-radius instead but the click event still goes through

With that being said, you have two options. You could either change your code to only use one canvas with the same type of layout, just drawing the background circle before the other each time. Essentially you'd draw a circle, draw your black to color to white gradient, use the xor operation to combine the two into one circle, then do the same with the rainbox gradient. You must draw the background circle first because canvas paints over the old layers every time

or

You could use javascript to only detect clicks in the circular area which takes just a little bit of math (: This solution is featured in edit below

In the future, CSS Shapes may allow canvases to be non-rectangular elements to be used, I'm actually not sure, but we don't have that capability yet at least

Edit

Alright, so after going through your code a bit it seems there are some things I should cover before I offer a solution

  • Setup all your finite variables outside of the functions that run every time. This means you don't put them (like radiuses, offsets, etc.) in the click function or something that runs often since they don't change
  • Your "radius"es are actually "diameter"s. The format of .rect goes .rect(x, y, width (diameter of circle), height (diameter of circle))
  • Almost always when overlaying canvases like you are you want to make them equal dimensions and starting position to prevent calculation error. In the end it makes it easier, doing all relative positioning with javascript instead of mixing it with CSS. In this case, however, since you're using border-radius instead of arc to make a circle, keep it like it is but position it using javascript ....
  • jQuery isn't needed for something this simple. If you're worried about any load speed I'd recommend doing it in vanilla javascript, essentially just changing the .click() functions into .onclick functions, but I left jQuery for now
  • You can declare multiple variables in a row without declaring var each time by using the following format:

    var name1 = value1,
        name2 = value2;
    
  • Variables with the same value you can declare like so:

    var name1 = name2 = sameValue;
    
  • When children have position:absolute and you want it to be positioned relative to the parent, the parent can have position:relative, position:fixed, or position:absolute. I would think you'd want position:relative in this case
  • When you don't declare var for a variable it becomes global (unlessed chained with a comma like above). For more on that read this question

Now, onto the solution.

After talking with a friend I realized I could sort do the math calculation a lot easier than I originally thought. We can just calculate the center of the circles and use their radiuses and some if statements to make sure the clicks are in the bounds.

Here's the demo

After everything is set up correctly, you can use the following to detect whether or not it's in the bounds of each

function clickHandler(e, r) {
    var ex = e.pageX,
        ey = e.pageY,
        // Distance from click to center
        l = Math.sqrt(Math.pow(cx - ex, 2) + Math.pow(cy - ey, 2));

    if(l > r) { // If the distance is greater than the radius
        if(r === LARGE_RADIUS) { // Outside of the large
            // Do nothing
        } else { // The corner area you were having a problem with
            clickHandler(e, LARGE_RADIUS);
        }
    } else {
        if(r === LARGE_RADIUS) { // Inside the large cirle
            alert('Outer canvas clicked x:' + ex + ',y:' + ey);
        } else { // Inside the small circle
            alert('Inner canvas clicked x:' + ex + ',y:' + ey);
        }
    }
}

// Just call the function with the appropriate radius on click
$(img_canvas).click(function(e) { clickHandler(e, SMALL_RADIUS); });    
$(wheel_canvas).click(function(e) { clickHandler(e, LARGE_RADIUS); });

Hopefully the comments above and code make enough sense, I tried to clean it up as best as I could. If you have any questions don't hesitate to ask!

Community
  • 1
  • 1
Zach Saucier
  • 24,871
  • 12
  • 85
  • 147
  • Seems right but you didnt tell why its working quite fine on `FireFox` – Sami Jun 08 '14 at 20:18
  • Firefox renders it differently. That's about all I can say – Zach Saucier Jun 08 '14 at 20:20
  • Thaks.. Could you plz demonstrate `only use one canvas with the same type of layout (recommended), just drawing the background circle before the other each time` on fiddle if it cud give me same out put in chrome as in firefox – Sami Jun 08 '14 at 20:23
  • I think Zach deserves your bounty, what he says is true. The reason that Firefox does seem to get it right, could be because Firefox makes those calculations for you, because everyone hates this issue, and Firefox is made by the people so people can change it. What Firefox does is usually a bit different from other browsers, sometimes for the better, sometimes not. It shouldn't be taken as 'the standard' – myfunkyside Jun 08 '14 at 20:24
  • @Sami Your javascript is quite convoluted, it'd take me a bit to understand and implement the fix. I added an edit to give more detail – Zach Saucier Jun 08 '14 at 20:35
  • Off course, he is correct and you are right too.. But should it not be good to help the asker little more? So the problem could be solved as well as it is identified. I just asked him to demonstrate his suggestion if he could. I had thought it earlier but havnt been able to do it with one canvas. I dont say he is responsible :) only if he could – Sami Jun 08 '14 at 20:42
  • See my edit. My code is much simpler now. Tell me plz if anything left unclear – Sami Jun 08 '14 at 21:51
  • @Sami I updated with a solution and some helpful hints (: Thanks for simplifying your code to make it easier – Zach Saucier Jun 09 '14 at 02:04
  • Thanks. Pretty convincing. And almost what i needed. Though not what i wanted. But i am clear that its not feasible. – Sami Jun 09 '14 at 08:53