Речи пузырь со стрелкой

У меня есть проект, где мне нужно вставить речевые пузыри / окна сообщений. Общая форма, которую я пытаюсь достичь, такова:

CSS речевой пузырь со стрелкой или треугольником

.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>

В настоящее время этот тест не проходит, так как прозрачная граница также активна.

Цели

  • Хит-бокс (кликабельные / перемещаемые области) должен придерживаться границ фигуры (прозрачные границы также можно перемещать, что делает это недействительным).

  • Мне нужно отобразить форму поверх различного контента (изображения, градиенты, текст...),

вопросы

Основные проблемы, с которыми я сталкиваюсь при манипулировании этой формой:

  • Иметь возможность перемещать треугольник вокруг речевого пузыря в соответствии с положением элемента, к которому он относится (верхняя / левая / правая / нижняя стороны)
  • добавление рамки или тени вокруг него, когда необходимо выделить

Есть ли способ решить эти проблемы?

4 ответа

Решение

Чтобы добиться этого, вы должны рассмотреть возможность изменения разметки, чтобы сделать ваш HTML более эффективным. Это может быть достигнуто с помощью псевдоэлемента. Я рассмотрю каждый пункт индивидуально и соберу все вместе в конце моего ответа.

Прежде всего,

Используйте псевдоэлементы, чтобы избежать лишних элементов

Вы можете использовать псевдоэлемент, чтобы удалить лишние .triangle дела. Это не только уменьшает ваши числа div, но также помогает с позиционированием, так как вы можете использовать top:left:right: а также bottom: CSS свойства для того, чтобы позиционировать в соответствии с вашим основным элементом. Это можно увидеть ниже:

.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;
}
<div class="oneAndOnlyDiv">Main div</div>


Хит-тестирование

Чтобы создать свой "тест на попадание", вы можете использовать повернутый элемент вместо взлома границы.

Что-то вроде:

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;
}
<div>Only element</div>

или используйте наклонный псевдоэлемент:

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;
}
<div>Only element</div>

который покажет указатель только тогда, когда наведен квадрат или основной элемент. Но подождите, это портит расположение? как вы можете справиться с этим?

Есть несколько решений для этого. Одним из которых является использование calc CSS свойство

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);
}
<div>Only element</div>

Добавление границы

Теперь вы можете легко добавить границу, просто добавив объявление границы в основной элемент и установив border-bottom а также border-right псевдоэлемента в inherit

бордюр

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;
}
<div>Only element</div>

Box Shadow:

Чтобы получить тень от коробки, я использовал :after псевдоэлемент, чтобы скрыть тень блока над другим псевдоэлементом, чтобы элемент выглядел как единый элемент.

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;
  }
<div>Only element</div>

Собираем все вместе

Вы также можете снова добавить радиус границы к вашему окну сообщения или речевому пузырю, используя свойство border-radius:

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;
}
<div>Only element</div>

Это даже позволяет вам создать не только треугольник, но как насчет круга?

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%;
}
<div>Only element</div>

Если у вас есть проблемы с переполнением контента и его "скрытием" за этим псевдоэлементом, и вы не беспокоитесь о наличии границы, вы можете использовать отрицательный z-индекс, который решит эту проблему.

Не любите использовать "магические числа"?

Если вам не нравится идея использования значения calc, в котором в настоящее время используется позиционирование в моем ответе (пока работает), вы можете использовать transform:translate(50%)

Это был бы намного лучший подход, так как:

  • Вам не нужно знать ни размер границы, ни половину ширины
  • Вы будете делать ваше окно сообщения / пузыря намного более динамичным в своем позиционировании и будете поддерживать дальнейшие размеры.

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;
}
<div>Only element</div>

Хотите переместить это? Вы можете!

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%;
}
<div>Only element</div>

Хотите это право?

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%;
  }
<div>Only Element</div>

Хотите, чтобы это была другая форма треугольника?

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%;
  }
<div>Only Element</div>

Мы можем положиться на clip-path и фильтр падающей тени, чтобы легко этого добиться:

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>

введите описание изображения здесь

В этом примере используется один путь
Должно быть полностью поддержано 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>

введите описание изображения здесь

Я нашел этот замечательный сервис для создания настраиваемых стрелок CSS. Просто сгенерируйте его и используйте с любым блоком.

http://www.cssarrowplease.com/

Другие вопросы по тегам