Речи пузырь со стрелкой
У меня есть проект, где мне нужно вставить речевые пузыри / окна сообщений. Общая форма, которую я пытаюсь достичь, такова:
.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>
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. Просто сгенерируйте его и используйте с любым блоком.