it-swarm.com.ru

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

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

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>

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

Цели

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

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

Вопросы

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

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

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

31
jbutler483

Чтобы достичь этого, вы должны рассмотреть возможность изменения разметки, чтобы сделать ваш 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>

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

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

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>

43
jbutler483

SVG

Это не проходит хит-тест, так как прозрачная граница также кликабельна 

Это можно сделать с помощью событий указателя в SVG.
pointer-events:visibleFill; Выберет только ту часть, где находится Paint. 

Этот пример использует 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

4
Persijn

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

http://www.cssarrowplease.com/

0
Oleg Abrazhaev