带箭头的语音气泡

时间:2015-05-18 09:08:11

标签: css css3 css-shapes

我有一个项目需要插入语音气泡/消息框。我想要达到的一般形状就是这个:

CSS speech bubble with arrow or triangle



.bubble {
  height: 100px;
  width: 200px;
  border: 3px solid gray;
  background: lightgray;
  position: relative;
  cursor:pointer;
}
.triangle {
  width: 0;
  border-top: 20px solid black;
  border-left: 20px solid transparent;
  border-right: 20px solid transparent;
  cursor:pointer;
}

<div class="bubble">Speech bubble
</div>
<div class="triangle">
</div>
&#13;
&#13;
&#13;

目前这不会通过命中测试,因为透明边框也是可点击的。

目标

  • 点击框(可点击/可挖掘区域)需要坚持形状的边界(此处的透明边框也可以悬空,使其无效)。

  • 我需要在各种内容(图片,等级,文字......)上显示形状,

问题

操作此形状时遇到的主要问题是:

  • 能够根据所引用元素的位置(上/左/右/下)在讲话气泡周围移动三角形
  • 在需要强调时在其周围添加边框或框阴影

无论如何解决这些问题?

4 个答案:

答案 0 :(得分:45)

为了实现这一点,您应该考虑更改标记,以使您的HTML更有效。这可以使用伪元素来实现。我将逐一解决每个问题,并在答案结束时将它们全部放在一起。

首先,

使用伪元素来避免额外元素

您可以使用伪元素删除额外的.triangle div。这不仅可以减少您的div数量,还有助于定位,因为您可以使用top: left: right:bottom: css属性来根据您的主要元素进行定位。这可以在下面看到:

&#13;
&#13;
.oneAndOnlyDiv {
  height: 100px;
  width: 200px;
  border: 3px solid gray;
  background: lightgray;
  position: relative;
}
.oneAndOnlyDiv:before {
  content: "";
  position: absolute;
  top: 100%;
  left: 20px;
  width: 0;
  border-top: 20px solid black;
  border-left: 20px solid transparent;
  border-right: 20px solid transparent;
}
&#13;
<div class="oneAndOnlyDiv">Main div</div>
&#13;
&#13;
&#13;

命中测试

为了创建&#34;命中测试&#34;,您可能希望使用旋转元素而不是边框​​黑客。

类似的东西:

&#13;
&#13;
div {
  height: 100px;
  width: 200px;
  background: gray;
  position: relative;
  cursor:pointer;
}
div:before {
  content: "";
  position: absolute;
  top: 100%;
  left: 20px;
  height: 20px;
  width: 20px;
  background: black;
  transform: rotate(45deg);
  transform-origin:top right;
}
&#13;
<div>Only element</div>
&#13;
&#13;
&#13;

或使用偏斜的伪元素:

&#13;
&#13;
div {
  height: 100px;
  width: 200px;
  background: gray;
  position: relative;
  cursor:pointer;
}
div:before {
  content: "";
  position: absolute;
  top: 90%;
  left: 20px;
  height: 30%;
  width: 20px;
  background: black;
  transform: skewY(-45deg);
  transform-origin:bottom left;
  z-index:-1;
}
&#13;
<div>Only element</div>
&#13;
&#13;
&#13;

仅在方形或主要元素悬停时显示指针。 但是,坚持下去会弄乱定位?你怎么能处理这个?

有一些解决方案。其中一个是使用calc CSS属性。

&#13;
&#13;
div {
  height: 100px;
  width: 200px;
  background: gray;
  position: relative;
  cursor:pointer;
}
div:before {
  content: "";
  position: absolute;
  top: -webkit-calc(100% - 10px); /*may require prefix for old browser support*/
  top: calc(100% - 10px); /*i.e. half the height*/
  left: 20px;
  height: 20px;
  width: 20px;
  background: gray;
  transform: rotate(45deg);
}
&#13;
<div>Only element</div>
&#13;
&#13;
&#13;

添加边框

您现在可以非常轻松地添加边框,只需向主元素添加边框声明,并将伪元素的border-bottomborder-right设置为inherit

<强>边界

&#13;
&#13;
div {
  height: 100px;
  width: 200px;
  background: gray;
  position: relative;
  cursor:pointer;
  border:3px double black;
}
div:before {
  content: "";
  position: absolute;
  top: -webkit-calc(100% - 10px); /*may require prefix for old browser support*/
  top: calc(100% - 10px); /*i.e. half the height*/
  left: 20px;
  height: 20px;
  width: 20px;
  background: gray;
  transform: rotate(45deg);
  border-bottom:inherit;
  border-right:inherit;
  box-shadow:inherit;
}
&#13;
<div>Only element</div>
&#13;
&#13;
&#13;

Box Shadow:

为了有一个盒子阴影,我使用了:after伪元素来隐藏其他伪的盒子阴影,使元素看起来像一个单独的元素。

&#13;
&#13;
div {
  height: 100px;
  width: 200px;
  background: gray;
  position: relative;
  cursor:pointer;
  box-shadow: 5px 5px 10px 2px black;
}
div:before,div:after {
  content: "";
  position: absolute;
  top: -webkit-calc(100% - 10px); /*may require prefix for old browser support*/
  top: calc(100% - 10px); /*i.e. half the height*/
  left: 20px;
  height: 20px;
  width: 20px;
  background: gray;
  transform: rotate(45deg);
z-index:-1;
  box-shadow:inherit;
}
div:after{
  box-shadow:none;
  z-index:8;
  }
&#13;
<div>Only element</div>
&#13;
&#13;
&#13;

全部放在一起

您还可以使用border-radius属性再次为消息框或语音气泡添加边框半径:

&#13;
&#13;
div {
  height: 100px;
  width: 200px;
  background: gray;
  position: relative;
  cursor:pointer;
  border:3px double black;
  border-radius:10px;
}
div:before {
  content: "";
  position: absolute;
  top: -webkit-calc(100% - 10px); /*may require prefix for old browser support*/
  top: calc(100% - 10px); /*i.e. half the height*/
  left: 20px;
  height: 20px;
  width: 20px;
  background: gray;
  transform: rotate(45deg);
  border-bottom:inherit;
  border-right:inherit;
  box-shadow:inherit;
}
&#13;
<div>Only element</div>
&#13;
&#13;
&#13;

这甚至可以让你不仅创建一个三角形,而是创建一个圆圈呢?

&#13;
&#13;
div {
  height: 100px;
  width: 200px;
  background: gray;
  position: relative;
  cursor:pointer;
  border:3px double black;
  border-radius:10px;
}
div:before {
  content: "";
  position: absolute;
  top: -webkit-calc(100% - 13px); /*may require prefix for old browser support*/
  top: calc(100% - 13px); /*i.e. half the height + border*/
  left: 20px;
  height: 20px;
  width: 20px;
  background: gray;
  transform: rotate(45deg);
  border:3px double transparent;
  border-bottom:inherit;
  border-right:inherit;
  box-shadow:inherit;
  border-radius:50%;
}
&#13;
<div>Only element</div>
&#13;
&#13;
&#13;

如果您遇到内容溢出且被隐藏的问题。在这个伪元素的背后,你并不担心有边框,你可以使用负z-index来解决这个问题。

不喜欢使用魔法数字&#39;?

如果您不喜欢使用钙值,我的答案中的定位当前正在使用(工作时),您可能希望使用transform:translate(50%)

这是一个更好的方法,因为:

  • 您不需要知道边框的大小,也不需要知道宽度的一半
  • 您的消息框/气泡的定位会更加动态,并且会支持进一步的搜索。

&#13;
&#13;
div {
  height: 100px;
  width: 200px;
  background: gray;
  position: relative;
  cursor: pointer;
  border: 3px double black;
  border-radius: 10px;
}
div:before {
  content: "";
  position: absolute;
  top: 100%;
  left: 30px;
  height: 20px;
  width: 20px;
  background: gray;
  box-sizing:border-box;
  transform: rotate(45deg) translate(-50%);
  border-bottom: inherit;
  border-right: inherit;
  box-shadow: inherit;
}
&#13;
<div>Only element</div>
&#13;
&#13;
&#13;

想要搬家吗?你可以!

&#13;
&#13;
div {
  height: 100px;
  width: 200px;
  background: gray;
  position: relative;
  cursor: pointer;
  border: 3px double black;
  border-radius: 10px;
}
div:before {
  content: "";
  position: absolute;
  top: 100%;
  left: 10%;
  height: 20px;
  width: 20px;
  background: gray;
  box-sizing: border-box;
  transform: rotate(45deg) translate(-50%);
  border-bottom: inherit;
  border-right: inherit;
  box-shadow: inherit;
  transition: all 0.8s;
}
div:hover:before {
  left: 90%;
}
&#13;
<div>Only element</div>
&#13;
&#13;
&#13;

想要一个正确吗?

&#13;
&#13;
div {
  height: 100px;
  width: 200px;
  background: gray;
  position: relative;
  cursor: pointer;
  border: 3px double black;
  border-radius: 10px;
}
div:before {
  content: "";
  position: absolute;
  top: 15%;
  left: 100%;
  height: 20px;
  width: 20px;
  background: gray;
  box-sizing:border-box;
  transform: rotate(45deg) translate(-50%);
  border-top: inherit;
  border-right: inherit;
  box-shadow: inherit;
  transition:all 0.8s;
}
div:hover:before{
  top:80%;
  }
&#13;
<div>Only Element</div>
&#13;
&#13;
&#13;

想要它是一个不同形状的三角形吗?

&#13;
&#13;
div {
  height: 100px;
  width: 200px;
  background: gray;
  position: relative;
  cursor: pointer;
  border-radius: 10px;
}
div:before {
  content: "";
  position: absolute;
  top: 70%;
  left: 100%;
  height: 20px;
  width: 20px;
  background: gray;
  box-sizing:border-box;
  transform:  translate(-50%) skewX(45deg);
  box-shadow: inherit;
  transition:all 0.8s;
  z-index:-1;
}
div:hover:before{
  transform:  translate(-50%);
  border-radius:50%;
  top:20%;
  }
&#13;
<div>Only Element</div>
&#13;
&#13;
&#13;

答案 1 :(得分:5)

我们可以依靠 clip-path 和阴影过滤器轻松实现这一点:

.box {
  margin: 50px;
  width: 200px;
  height: 100px;
  border-radius: 15px;
  background: red;
  position: relative;
  filter:  /* the more shadow you add the thicker the border will be */
    drop-shadow(0px 0px 1px green) 
    drop-shadow(0px 0px 1px green) 
    drop-shadow(0px 0px 1px green) 
    drop-shadow(0px 0px 1px green) 
    drop-shadow(0px 0px 1px green) 
    drop-shadow(0px 0px 1px green);
}

.box::before {
  content: "";
  position: absolute;
  top: 100%;
  left: 20%;
  height: 30px;
  width: 50px;
  background: inherit;
  clip-path: polygon(0 0, 100% 0, 50% 100%);
}

.box:hover {
  background:blue;
}

body {
  background:linear-gradient(to right, pink,grey);
}
<div class="box"></div>

我们可以扩展这个基本示例以考虑任何类型的位置和三角形:

.box {
  margin: 30px;
  width: 150px;
  height: 80px;
  display:inline-block;
  border-radius: 15px;
  background: red;
  position: relative;
  filter:  /* the more shadow you add the thicker the border will be */
    drop-shadow(0px 0px 1px green) 
    drop-shadow(0px 0px 1px green) 
    drop-shadow(0px 0px 1px green) 
    drop-shadow(0px 0px 1px green) 
    drop-shadow(0px 0px 1px green) 
    drop-shadow(0px 0px 1px green);
}

.box::before {
  content: "";
  position: absolute;
  height: var(--h,20px);
  width: var(--w,30px);
  background: inherit;
  transform:scale(var(--x,1),var(--y,1));
}

.box.p-bottom::before{
  top: 100%;
  clip-path: polygon(0 0, 100% 0, 50% 100%);
}
.box.p-bottom.alt::before{
  clip-path: polygon(0 0, 100% 0, 100% 100%);
}
.box.p-top::before{
  bottom: 100%;
  clip-path: polygon(0 100%, 100% 100%, 50% 0);
}
.box.p-top.alt::before{
  clip-path: polygon(0 100%, 100% 100%, 100% 0);
}
.box.p-left::before{
  right: 100%;
  clip-path: polygon(100% 0, 100% 100%,0% 50%);
}
.box.p-left.alt::before{
  clip-path: polygon(100% 0, 100% 100%,0% 100%);
}
.box.p-right::before{
  left: 100%;
  clip-path: polygon(0% 0, 0% 100%,100% 50%);
}
.box.p-right.alt::before{
  clip-path: polygon(0% 0, 0% 100%,100% 100%);
}

.box.right::before{
  right:var(--p,20px);
}
.box.left::before {
  left:var(--p,20px);
}
.box.top::before{
  top:var(--p,20px);
}
.box.bottom::before {
  bottom:var(--p,20px);
}


.box:hover {
  background:blue;
}

body {
  background:linear-gradient(to right, pink,grey);
}
<div class="box p-bottom right"></div>
<div class="box p-bottom right alt"></div>
<div class="box p-bottom right alt" style="--x:-1"></div>
<div class="box p-top left"></div>
<div class="box p-top right" style="--p:40%"></div>
<div class="box p-top right alt" style="--p:40%"></div>
<div class="box p-left top"></div>
<div class="box p-left top alt"></div>
<div class="box p-right bottom" style="--w:20px;"></div>
<div class="box p-right bottom" style="--p:30px;--w:20px;--h:30px"></div>
<div class="box p-right bottom alt" style="--p:30px;--w:20px;--h:30px"></div>
<div class="box p-right bottom alt" style="--p:30px;--w:20px;--h:30px;--y:-1"></div>

speech bubble with arrow CSS

我们还可以考虑整个形状的任何类型的背景。该技巧适用于固定的宽度/高度。我们的想法是为主元素和伪元素创建一个具有相同大小的背景,然后我们只需调整伪元素内部的位置以匹配父元素的位置(以获得完美的重叠)

.box {
  --h:20px;
  --w:30px;
  --p:20px;

  margin: 30px;
  width: 150px;
  height: 80px;
  display:inline-block;
  border-radius: 15px;
  background: 
    var(--back,linear-gradient(45deg,red,purple)) 
    center/
    calc(150px + 2*var(--w)) calc(80px + 2*var(--h));
  position: relative;
  filter:  /* the more shadow you add the thicker the border will be */
    drop-shadow(0px 0px 1px green) 
    drop-shadow(0px 0px 1px green) 
    drop-shadow(0px 0px 1px green) 
    drop-shadow(0px 0px 1px green) 
    drop-shadow(0px 0px 1px green) 
    drop-shadow(0px 0px 1px green);
}

.box::before {
  content: "";
  position: absolute;
  height: var(--h);
  width: var(--w);
  background: inherit;
  transform:scale(var(--x,1),var(--y,1));
  background-position:var(--b1) 0 var(--b2);
}

.box.p-bottom::before{
  top: 100%;
  clip-path: polygon(0 0, 100% 0, 50% 100%);
  --b1:bottom;
}
.box.p-bottom.alt::before{
  clip-path: polygon(0 0, 100% 0, 100% 100%);
}
.box.p-top::before{
  bottom: 100%;
  clip-path: polygon(0 100%, 100% 100%, 50% 0);
  --b1:top;
}
.box.p-top.alt::before{
  clip-path: polygon(0 100%, 100% 100%, 100% 0);
}
.box.p-left::before{
  right: 100%;
  clip-path: polygon(100% 0, 100% 100%,0% 50%);
  --b1:left;
}
.box.p-left.alt::before{
  clip-path: polygon(100% 0, 100% 100%,0% 100%);
}
.box.p-right::before{
  left: 100%;
  clip-path: polygon(0% 0, 0% 100%,100% 50%);
  --b1:right;
}
.box.p-right.alt::before{
  clip-path: polygon(0% 0, 0% 100%,100% 100%);
}

.box.right::before{
  right:var(--p);
  --b2:right calc(-1*var(--p) - var(--w)); 
}
.box.left::before {
  left:var(--p);
  --b2:left  calc(-1*var(--p) - var(--w));
}
.box.top::before{
  top:var(--p);
  --b2:top  calc(-1*var(--p) - var(--h));
}
.box.bottom::before {
  bottom:var(--p);
  --b2:bottom  calc(-1*var(--p) - var(--h));
}

body {
  background:linear-gradient(to right, pink,grey);
}
<div class="box p-bottom right"></div>
<div class="box p-bottom right alt" style="--back:url(https://picsum.photos/id/15/400/300)"></div>
<div class="box p-bottom right alt" style="--x:-1;--back:red"></div>
<div class="box p-top left" style="--back:url(https://picsum.photos/id/18/400/300)"></div>
<div class="box p-top right" style="--p:40px;--back:url(https://picsum.photos/id/1018/400/300)"></div>
<div class="box p-top right alt" style="--p:60px;--back:radial-gradient(red,pink,yellow)"></div>
<div class="box p-left top" style="--back:black"></div>
<div class="box p-left top alt" style="--back:repeating-linear-gradient(45deg,#fff 0 10px,orange 0 20px)"></div>
<div class="box p-right bottom" style="--w:20px;--back:linear-gradient(red,pink,yellow)"></div>
<div class="box p-right bottom" style="--p:30px;--w:20px;--h:30px;--back:repeating-radial-gradient(#fff 0 10px,orange 0 20px)"></div>
<div class="box p-right bottom alt" style="--p:30px;--w:20px;--h:30px;--back:conic-gradient(red,pink,yellow,red)"></div>
<div class="box p-right bottom alt" style="--p:30px;--w:20px;--h:30px;--y:-1;"></div>

CSS speech bubble with random background

答案 2 :(得分:4)

SVG

  

这不会通过命中测试,因为透明边框也是可点击的

这可以使用svg中的指针事件来完成 pointer-events:visibleFill;只会选择有油漆的部分。

此示例使用filter_box-shadow,IE不支持 也使用两种形状。

html,
body {
  margin: 0;
  padding: 0;
}
.bubble {
  width: 150px;
  height: 150px;
  -webkit-filter: drop-shadow(5px 5px 0px #aaa);
  filter: drop-shadow(5px 5px 0px #aaa);
}
.bubble-shape {
  fill: #1e1;
}
.shape-text {
  color: black;
}
<svg class="bubble" viewBox="0 0 110 110" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none">
  <g class="bubble-shape" style="cursor:pointer; pointer-events:visibleFill;">
    <rect x="10" y="10" width="90" height="90" rx="15" ry="15" />
    <polygon points="20,94 40,94 30,105" />
  </g>
</svg>

enter image description here

此示例使用一个路径
IE应该完全支持。

html,
body {
  margin: 0;
  padding: 0;
}
.bubble {
  width: 150px;
  height: 150px;
}
.bubble-shape {
  stroke-width: 15;
  stroke: #ddd;
  fill: #1e1;
}
.shape-text {
  color: black;
}
<svg class="bubble" viewBox="-70 -10 390 370" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none">
  <g style="cursor:pointer; pointer-events:visible;">
    <path class="bubble-shape" d="m 0,0 250,0 c 25,0 50,20 50,50 l 0,225 c 0,25 -25,50 -50,50 l -175,0 -25,20 -20,-20 -40,0 c -25,0 -50,-25 -50,-50 l 0,-225 C -50,25 -50,0 0,0 Z" />
  </g>
</svg>

enter image description here

答案 3 :(得分:0)

我发现这项出色的服务可用于制作可自定义的CSS箭头。 只需生成它并与任何块一起使用即可。

http://www.cssarrowplease.com/