0

So say I have the following HTML structure:

<canvas></canvas>
<div class="overLay">
    <button>Click me!</button>
</div>

The canvas is absolutely positioned with a negative z index, so the overlay is positioned over it (I have that much working). The issue is that I want the overlay div to have a white background, but the button to have a transparent background and show through to the canvas/body background color.

background-color:rgba(0,0,0,0); 

doesn't work because it just shows the white background behind it. Any ideas on how to accomplish this effect?

The question this may have been marked as a potential duplicate of fails to account for the fact that buttons can have properties such as border-radius and offers no suitable solutions.

Alex
  • 724
  • 1
  • 9
  • 24

4 Answers4

0

There's no way to create a hole inside an HTML element. A Transparent background can only be applied to an element that does not descend from a parent that has a non-transparent background.

What you could do, thought it's a little bit more complicate, is to create divs around your button, at it's same level, as siblings. Give those divs a white background, and then your transparent button should work.

I've created a sample using a table layout, where the button remains in the middle row, in the center cell. See that the button reveals the canvas background color:

canvas {
    width: 200px;
    height: 200px;
    background: purple;
    position: absolute;
    z-index: -1;
}

.overLay {
    width: 200px;
    height: 200px;
    display: table;
}

.overLay button {
    background-color: Transparent;
    white-space: nowrap;
}

.overLay > div {
    display: table-row;
}

.overLay > .middle {
    height: 1px;
}

.overLay > div > div {
    display: table-cell;
    text-align: center;
}

.overLay > .middle > div:nth-child(2) {
    width: 1px;
}

/* Now, set the background on the divs around the button */
.top, .left, .right, .bottom {
    background: white;
}
<canvas></canvas>
<div class="overLay">
    <div class="top">
        <div></div>
        <div></div>
        <div></div>
    </div>
    <div class="middle">
        <div class="left"></div>
        <div>
            <button>Click me!</button>
        </div>
        <div class="right"></div>
    </div>
    <div class="bottom">
        <div></div>
        <div></div>
        <div></div>
    </div>
</div>
LcSalazar
  • 16,524
  • 3
  • 37
  • 69
0

Since in my original answer, you pointed the intention of using a border radius. So there's another approach to make this possible.

Based on this asnwer

Make the overlay with a gradient radius background, that creates the gradient color center in the position where you want the button to be, at the button's size. Make the outer color as white, and the inner color as Transparent;.

Then, set your button's position. (I did this by centering it in a table layout):

canvas {
    width: 200px;
    height: 200px;
    background: purple;
    position: absolute;
    z-index: -1;
}

.overLay {
    width: 200px;
    height: 200px;
    display: table;
    background-color: white;
    background: -webkit-radial-gradient(50% 50%, circle, transparent 25px, white 0px);
    background: radial-gradient(50% 50%, circle, transparent 25px, white 0px);
}

.overLay > div {
    display: table-cell;
    text-align: center;
    vertical-align: middle;
}

button {
    background-color: transparent;
    border-radius: 50%;
    width: 50px;
    height: 50px;
    position: relative;
}
<canvas></canvas>
<div class="overLay">
    <div>
        <button>Click me!</button>
    </div>
</div>
Community
  • 1
  • 1
LcSalazar
  • 16,524
  • 3
  • 37
  • 69
  • I'm away from my computer at the moment, but will this work with a non circular button (just rounded corners, straight sides)? I've not used radial gradients before. If it is, don't worry about another example, I can find out how to do it, just wondering. This does look to be what I'm looking for though. – Alex May 14 '15 at 18:39
  • I'm sorry, but this approach would work only for a circle.... More than that, your best shot is to create a white, transparent holed, png background for the overlay, and place the button exactly over the spot... – LcSalazar May 14 '15 at 18:49
  • A similar, but better technique, is to use box-shadow. There is an answer in the original question (the one marked as dup) using this technique, and it can handle any border-radius setting – vals May 15 '15 at 05:21
0

Mmm..., have you thought about using an SVG element with a mask. That should do the trick. Take a look at the snippet.

body {
  padding: 0;
  margin: 0;
}
.your-canvas {
  position: absolute;
  top: 0;
  left: 0;
  z-index: -1;
}
button {
  width: 150px;
  height: 100px;
  background-color: transparent;
  color: white;
  cursor: pointer;
}
<div class="your-canvas">
  <img width="512" alt="Weingarten Kuppel 8" src="//upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Weingarten_Kuppel_8.jpg/512px-Weingarten_Kuppel_8.jpg"/>
</div>
<svg height="324" width="512">
  <defs>
    <mask id="mask">
      <rect width="512" height="324" fill="white"/>
      <rect x="181" y="112" width="150" height="100" fill="black"/>
    </mask>
  </defs>
  <rect width="512" height="324" style="fill:rgba(255,0,0,.6);" mask="url(#mask)"/>
  <foreignObject class="node" x="181" y="112" width="150" height="100">
    <body xmlns="http://www.w3.org/1999/xhtml">
      <button>Click Me</button>
    </body>
  </foreignObject>
</svg>

As far as i know it is even possible to use masking in pure css, but haven't had the time to look it up. Here is some information on using masks with svg Clipping and masking.

Have fun.

DavidDomain
  • 14,976
  • 4
  • 42
  • 50
0

You could...

  • Reduce a canvas element to button size (== a button-canvas!),
  • Use CSS to position the button-canvas as desired over the div,
  • Add a click event listener on the canvas,
  • Draw whatever design you need on the button-canvas.

This way you have complete flexibility using the amazing drawing tools available with the canvas element.

Here's example code and a (fanciful) Demo.

While this demo is just for fun, you can easily restyle this example & turn it into a reusable widget:

var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;

var width=80;
var height=40;
var borderwidth=4;
var x=0;
var y=0;
x+=borderwidth;
y+=borderwidth/2;
var w=width-borderwidth-borderwidth;
var h=height-borderwidth;
var depth=5;
//
var stopCount=8;
var angle=0;
var angleDelta=360;
//
var labelColor='black';

canvas.width=width;
canvas.height=height;

requestAnimationFrame(animate);

$("#canvas").mousedown(function(e){handleMouseDown(e);});
$("#canvas").mouseup(function(e){handleMouseUp(e);});
$("#canvas").mouseout(function(e){handleMouseUp(e);});


function makeGradient(){
  var g=ctx.createLinearGradient(0,0,cw,0);
  for(var i=0;i<stopCount;i++){
    var stop=i/stopCount;
    var hslDegrees=(angle+angleDelta*stop)%360;
    var hsl="hsl(" + hslDegrees + ",100%, 50%)"
    g.addColorStop(stop,hsl);
  }
  return(g);
}

function animate(){
  ctx.clearRect(0,0,cw,ch);
  gradientBorder();
  ctx.font='12px verdana';
  ctx.fillStyle=labelColor;
  ctx.fillText('Click Me',x+10,y+25);
  angle++;
  requestAnimationFrame(animate);
}

//
function gradientBorder(){
  var lw=ctx.lineWidth
  var ss=ctx.strokeStyle;
  ctx.lineWidth=borderwidth;
  ctx.strokeStyle=makeGradient();
  //
  ctx.beginPath();
  ctx.moveTo(x+w-depth,y);
  ctx.lineTo(x+depth,y);
  ctx.bezierCurveTo(  x-depth/2,y+h*1/6,  x+depth*2,y+h*2/6, x,y+h/2);
  ctx.bezierCurveTo(  x+depth*2,y+h*4/6,  x-depth/2,y+h*5/6, x+depth,y+h);
  ctx.lineTo(x+w-depth,y+h);
  ctx.bezierCurveTo(  x+w+depth/2,y+h*5/6, x+w-depth*2,y+h*4/6, x+w,y+h/2);
  ctx.bezierCurveTo(  x+w-depth*2,y+h*2/6, x+w+depth/2,y+h*1/6, x+w-depth,y);
  ctx.closePath();
  ctx.stroke();
  //
  var b2=borderwidth/2;
  ctx.beginPath();
  ctx.moveTo(x+w-depth-1,y+b2);
  ctx.lineTo(x+depth+2,y+b2);
  ctx.bezierCurveTo(  x-depth/2+b2+2,y+h*1/6+1,  x+depth*2+b2,y+h*2/6, x+b2+1,y+h/2);
  ctx.bezierCurveTo(  x+depth*2+b2,y+h*4/6+1,  x-depth/2+b2+1,y+h*5/6, x+depth+b2-2,y+h-b2);
  ctx.strokeStyle='#666';
  ctx.lineWidth=0.50;

  ctx.moveTo(x+depth+b2-2,y+h-b2);
  ctx.lineTo(x+w-depth-2,y+h-b2);
  ctx.bezierCurveTo(  x+w+depth/2-b2-2,y+h*5/6, x+w-depth*2-b2+1,y+h*4/6, x+w-b2-2,y+h/2);
  ctx.bezierCurveTo(  x+w-depth*2-b2+1,y+h*2/6, x+w+depth/2-b2-2,y+h*1/6, x+w-depth-b2+3,y+b2);
  ctx.strokeStyle='#666';
  ctx.lineWidth=0.50;
  ctx.stroke();
  //
  ctx.strokeStyle=ss;
  ctx.lineWidth=lw;

  ctx.fillStyle='gainsboro';
  ctx.fill();
}

function handleMouseDown(e){ labelColor='red'; }

function handleMouseUp(e){ labelColor='black'; }
body{ background-color:white; }
.demo{
  width:200px;
  height:100px;
  border:1px solid red;
  position:relative;
}
#canvas{
  position:absolute;
  left:10px;
  top:10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div class='demo'>
  <canvas id="canvas"></canvas>
</div>
markE
  • 102,905
  • 11
  • 164
  • 176