How can I maintain proper boundaries on CSS triangles when hovering with cursor?
Asked Answered
H

3

12

Is it possible to fix the hovering on http://jsfiddle.net/2AXhR/ so that the correct triangle is activated on hover instead of its sometimes adjacent one? Sometimes the wrong triangle is activated because each triangle element's bounding area is not actually a triangle, but a rectangle, so even though the cursor may appear to be on top of one triangle, it is actually on top of another one that overlaps and has a higher z-index.

The yellow overlay shows the bounds of the top right triangle. It overlaps with its adjacent triangle below, which means when the cursor is in the small area shared by both, the behavior is not consistent.

  <style type="text/css">
  .t {
     position:relative;
     top:55px;
     left:5px;
  }
  .t div {
     position:absolute;
     width: 0px;
     height: 0px;
     border-style: solid;
     border-width: 0 100px 173.2px 100px;
     border-color: transparent transparent #0079c5 transparent;
     transition:all 1s;
     -webkit-transition:all 1s;
     -moz-transition:all 1s;
     cursor:pointer;
     transform-origin:200px 173px;
     -webkit-transform-origin:200px 173px;
     -moz-transform-origin:200px 173px;
     z-index:10;
  }
  .t div:hover {
      z-index:20;
      border-color: transparent transparent #009cff transparent;
  }
  .t div:nth-child(1) {
     transform:rotate(30deg);
     -webkit-transform:rotate(30deg);
     -moz-transform:rotate(30deg);
  }
  .t div:nth-child(1):hover {
     transform:rotate(30deg) translate(-15%, -10%);
     -webkit-transform:rotate(30deg) translate(-15%, -10%);
     -moz-transform:rotate(30deg) translate(-15%, -10%);
  }
  .t div:nth-child(2) {
     transform:rotate(90deg);
     -webkit-transform:rotate(90deg);
     -moz-transform:rotate(90deg);
  }
  .t div:nth-child(2):hover {
     transform:rotate(90deg) translate(-15%, -10%);
     -webkit-transform:rotate(90deg) translate(-15%, -10%);
     -moz-transform:rotate(90deg) translate(-15%, -10%);
  }
  .t div:nth-child(3) {
     transform:rotate(150deg);
     -webkit-transform:rotate(150deg);
     -moz-transform:rotate(150deg);
  }
  .t div:nth-child(3):hover {
     transform:rotate(150deg) translate(-15%, -10%);
     -webkit-transform:rotate(150deg) translate(-15%, -10%);
     -moz-transform:rotate(150deg) translate(-15%, -10%);
  }
  .t div:nth-child(4) {
     transform:rotate(210deg);
     -webkit-transform:rotate(210deg);
     -moz-transform:rotate(210deg);
  }
  .t div:nth-child(4):hover {
     transform:rotate(210deg) translate(-15%, -10%);
     -webkit-transform:rotate(210deg) translate(-15%, -10%);
     -moz-transform:rotate(210deg) translate(-15%, -10%);
  }
  .t div:nth-child(5) {
     transform:rotate(270deg);
     -webkit-transform:rotate(270deg);
     -moz-transform:rotate(270deg);
  }
  .t div:nth-child(5):hover {
     transform:rotate(270deg) translate(-15%, -10%);
     -webkit-transform:rotate(270deg) translate(-15%, -10%);
     -moz-transform:rotate(270deg) translate(-15%, -10%);
  }
  .t div:nth-child(6) {
     transform:rotate(330deg);
     -webkit-transform:rotate(330deg);
     -moz-transform:rotate(330deg);
  }
  </style>

  <div class="t">
     <div></div>
     <div></div>
     <div></div>
     <div></div>
     <div></div>
     <div></div>
  </div>
Herrle answered 16/4, 2014 at 11:7 Comment(7)
Works very good in Firefox 27, can't reproduce what you're describing. Where do you experience these issues or what exactly can one do to reproduce what you're trying to solve? Btw., good work so far. :)Como
Is IE 8 a browser you need to support?Firetrap
I just edited my post to include an image and a new JSFiddle, where you can modify the properties of the hexagon. Try placing your cursor just below the top right triangle. You'll notice that even though the cursor is outside its bounds, it still activates it rather than the adjacent triangle below. I do not need to support IE8.Herrle
You can create a triangle in each triangle, the internal triangle dimensions is exactly the dimensions of the triangular shape which is the rest of the rectangle, so the hover, or mouseOver will work just on the right triangle not all the rectangleBucky
@Firetrap did you read the code? IE8? really?Dottie
@Mujtaba Fathel - I'm not sure how that would solve anything. A new triangle within each triangle will still have the same rectangular bounds as its parent.Herrle
Um... SVG anyone? I can even make a circle with that babyBur
F
12

----- Version 2, cleaner, better (fixes IE and FF issues) -----

Corrected issues :

  1. IE ignored the overflow:hidden; property and the hover events were fired outside the visible triangles.
  2. For some reason there were lines apearing on the triangles in firefox.
  3. the cursor comes back to default if it is between the triangles.

Description :

This aproach uses skewX() to create the triangles. You don't need the "border trick" to create them and you don't need the overflow property either. With this technique, there no overlapping elements at all so hover events can't fire two elements at the same time.

A second div hides half the skewed element to create the triangle and is translated with it on hover using the + CSS selector.


----- DEMO V2 ----- snapshot devtools showing the triangle boundaries




Markup :

<div class="t">
    <div class="wrap">
        <div class="triangle"></div>
        <div class="mask"></div>
    </div>
   <div class="wrap">
        <div class="triangle"></div>
        <div class="mask"></div>
    </div>
    <div class="wrap">
        <div class="triangle"></div>
        <div class="mask"></div>
    </div>
    <div class="wrap">
        <div class="triangle"></div>
        <div class="mask"></div>
    </div>
    <div class="wrap">
        <div class="triangle"></div>
        <div class="mask"></div>
    </div>
    <div class="wrap">
        <div class="triangle"></div>
        <div class="mask"></div>
    </div>
</div>

CSS :

.t div{
    position:absolute;
    top:0; left:0;

    transform-origin:0 0;
    -ms-transform-origin:0 0;
    -webkit-transform-origin:0 0;

    transition:all 1s;
    -webkit-transition:all 1s;
    -moz-transition:all 1s;
}

.t .wrap{
    top:50%; left:50%;

    -ms-transform: skewX(30deg);
    -webkit-transform: skewX(30deg);
    transform: skewX(30deg);
}

.t .wrap .triangle {
    position:relative;
    width: 200px;
    height: 173px;
    background-color: #0079c5;
    cursor:pointer;
    z-index:1;
}
.t .wrap .mask{
    width:100%;
    height:115.5%;
    background-color: #fff;
    left:100%;
    z-index:2;

    -ms-transform: skewX(-30deg) rotate(30deg);
    -webkit-transform: skewX(-30deg) rotate(30deg);
    transform: skewX(-30deg) rotate(30deg);
} 

.t .wrap .triangle:hover{
    background-color: #009cff;

    transform:  translate(10%, 10%);
    -webkit-transform: translate(10%, 10%);
    -moz-transform: translate(10%, 10%);
}

.t .triangle:hover + .mask{
    -ms-transform: skewX(-30deg) rotate(30deg) translate(17.5%, 0);
    -webkit-transform: skewX(-30deg) rotate(30deg) translate(17.5%, 0);
    transform: skewX(-30deg) rotate(30deg) translate(17.5%, 0);
}

.t > div:nth-child(2){
    -ms-transform: rotate(60deg) skewX(30deg);
    -webkit-transform: rotate(60deg) skewX(30deg);
    transform: rotate(60deg) skewX(30deg);
}
.t > div:nth-child(3){
    -ms-transform: rotate(120deg) skewX(30deg);
    -webkit-transform: rotate(120deg) skewX(30deg);
    transform: rotate(120deg) skewX(30deg);
}

.t > div:nth-child(4){
    -ms-transform: rotate(-60deg) skewX(30deg);
    -webkit-transform: rotate(-60deg) skewX(30deg);
    transform: rotate(-60deg) skewX(30deg);
}
.t > div:nth-child(5){
    -ms-transform: rotate(-120deg) skewX(30deg);
    -webkit-transform: rotate(-120deg) skewX(30deg);
    transform: rotate(-120deg) skewX(30deg);
}
.t > div:nth-child(6){
    -ms-transform: rotate(-180deg) skewX(30deg);
    -webkit-transform: rotate(-180deg) skewX(30deg);
    transform: rotate(-180deg) skewX(30deg);
}




Vesrion 1 (original) : fiddle for demo V1

Firetrap answered 20/4, 2014 at 23:34 Comment(9)
Nice approach - I like how the boundary of the outside edge of each triangle is also maintained. I figured that since you are skewing the entire element, then why do we even need to use the border method of creating the triangle? Why not just set the width, height, and background-color? I forked off your fiddle to attempt this, but even with the overflow:hidden on the skewed parent div, the child element still appears as a rectangle. Check it out: jsfiddle.net/zVAa2Herrle
@DaneIracleous I though of that at the bigining but it isn't possible. You would need to move the parent (skewed) element too to maintain the triangle shape. But as you can't select the parent element with CSS it isn't possible. and if you set the hover state on the parent, you loose the boundaries...Firetrap
Ah ok. The one thing I don't like about all our implementations is the whitespace between the triangles. It seems excessive. I wonder if there's a way to improve the anti-aliasing or something. Josh Crozier was able to eliminate all the whitespace, but his can't use animation.Herrle
@DaneIracleous See this one, I added some anti aliasing for chrome with -webkit-backface-visibility: hidden; and reduce the spaces : jsfiddle.net/webtiki/bZxn9 There still is a small one next to top and bottom triangles, I'll look some more for those.Firetrap
That looks great. I actually removed the backface visibility property, and it completely removed the initial whitespace (in Chrome): jsfiddle.net/ZY4DfHerrle
@DaneIracleous very nice :)Firetrap
@DaneIracleous Check out the new updated version, it fixes issues!Firetrap
I think you've perfected this project. I really like how v2 has such precise edges. If you place the cursor in the whitespace between triangles when one is hovered, the cursor goes back to default instead of pointer. That is a great improvement over my implementation.Herrle
@DaneIracleous That is true, I hadn't realized!Firetrap
A
4

Here is a completely different approach. It avoids the boundary issues completely.

It's worth noting that this approach is relatively limited when it comes to achieving the hover effect you had in place. I'm currently looking at alternatives.

EXAMPLE HERE - Works in FF/Chrome it fails in IE11.

HTML

<div class="t">
    <div class="clip">
        <div class="triangle"></div>
    </div>
    <div class="clip">
        <div class="triangle"></div>
    </div>
    <div class="clip">
        <div class="triangle"></div>
    </div>
    <div class="clip">
        <div class="triangle"></div>
    </div>
    <div class="clip">
        <div class="triangle"></div>
    </div>
    <div class="clip">
        <div class="triangle"></div>
    </div>
</div>

CSS

.t {
    width:500px;
    height:500px;
    position:relative;
}
.t > .clip {
    overflow: hidden;
    position: absolute;
    width: 50%;
    height: 50%;
    -webkit-transform-origin: 100% 100%;
}
.t > .clip:first-child {
    -webkit-transform: rotate(60deg) skewY(30deg);
}
.t > .clip:nth-child(2) {
    -webkit-transform: rotate(120deg) skewY(30deg);
}
.t > .clip:nth-child(3) {
    -webkit-transform: rotate(180deg) skewY(30deg);
}
.t > .clip:nth-child(4) {
    -webkit-transform: rotate(240deg) skewY(30deg);
}
.t > .clip:nth-child(5) {
    -webkit-transform: rotate(300deg) skewY(30deg);
}
.t > .clip:nth-child(6) {
    -webkit-transform: rotate(360deg) skewY(30deg);
}
.triangle {
    width: 200%;
    height: 200%;
    -webkit-transform: skewY(-42deg) skewX(-20deg) rotate(-15.5deg);
    background:#0079c5;
}
.triangle:hover {
    background:#009cff;
}
Atombomb answered 20/4, 2014 at 22:24 Comment(1)
That's an interesting approach I didn't consider. I tried editing your fiddle to use the hover animation where the triangle moves outward, but the triangles themselves take up the entire portion of the screen, so there isn't that blank margin that should appear in the empty space. It's just more triangle.Herrle
H
3

I actually solved the problem on my own. Using JavaScript, I set a hover event for each triangle: On hover, I set its own z-index to 20, the next triangle's z-index to 21, and all the rest of the triangles' z-index to 19.

The code looks like this:

  self.e.find(".t div").hover(
  function() {
     $(this).css({
        'z-index': 20,
        'border-color': "transparent transparent "+self.params['colorSelected']+" transparent"
     });
     if($(this).next().length) {
        $(this).next().css("z-index", 21);
     } else {
        self.e.find(".t div").first().css("z-index", 21);
     }
  }, 
  function() {
     self.e.find(".t div").css({
        'z-index': 19,
        'border-color': "transparent transparent "+self.params['color']+" transparent"
     });
  });

The reason why it works is because all the triangles are in order starting from the top left going clockwise. Each triangle incorrectly overlaps its next sibling, so by bringing the next sibling forward in the z plane, it allows the triangles to be defined correctly.

Compare these two JSFiddles, and you'll see the difference in hover behavior:

Unsolved: http://jsfiddle.net/2AXhR/

Solved: http://jsfiddle.net/2AXhR/1/

Herrle answered 20/4, 2014 at 22:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.