web-dev-qa-db-fra.com

Bulle de dialogue avec flèche

J'ai un projet dans lequel j'ai besoin d'insérer bulles de dialogue/boîtes de message . La forme générale que j'essaie d'obtenir est la suivante:

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>

Cela ne passe actuellement pas un test de réussite car la bordure transparente est également cliquable.

Objectifs

  • La zone de texte (zones cliquables/survolables) doit coller aux limites de la forme (les limites transparentes ici sont également survolables, ce qui l'invalide).

  • J'ai besoin d'afficher la forme sur différents contenus (images, dégradés, texte ...), 

Problèmes

Les principaux problèmes que je rencontre lors de la manipulation de cette forme sont les suivants:

  • Avoir la capacité de déplacer le triangle autour de la bulle de dialogue en fonction de la position de l'élément auquel elle fait référence (côtés haut/gauche/droite/bas)
  • ajouter une bordure ou une ombre autour d'elle lorsque l'accent est nécessaire

Est-il possible de résoudre ces problèmes?

31
jbutler483

Pour ce faire, vous devriez envisager de modifier votre balisage afin de rendre votre code html plus efficace. Ceci peut être réalisé en utilisant un pseudo-élément. Je vais aborder chaque point individuellement et les regrouper à la fin de ma réponse.

Tout d'abord, 

Utilisez des pseudo-éléments pour éviter les éléments supplémentaires

Vous pouvez utiliser un pseudo-élément pour supprimer le supplément .triangle div. Cela réduit non seulement vos nombres div, mais aide également au positionnement car vous pouvez utiliser les propriétés top:left:right: et bottom: css afin de vous positionner en fonction de votre élément principal. Cela peut être vu ci-dessous:

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


Test de frappe

Pour créer votre "test de frappe", vous souhaiterez peut-être utiliser un élément pivoté au lieu d'un hack de bordure.

Quelque chose comme:

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>

ou utilisez un pseudo-élément asymétrique:

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>

qui affichera le pointeur uniquement lorsque le carré ou l'élément principal est survolé . Mais accrochez-vous, cela gâche le positionnement? comment pouvez-vous gérer cela?

Il y a quelques solutions à cela. L'une d'entre elles consiste à utiliser la propriété 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>

Ajout d'une bordure

Vous pouvez maintenant ajouter une bordure assez facilement, simplement en ajoutant une déclaration de bordure à l'élément principal et en définissant les border-bottom et border-right du pseudo-élément sur inherit.

Frontière

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>

Boîte ombre:

Afin d’avoir une ombre de boîte, j’ai utilisé le pseudo-élément :after afin de masquer l’ombre de la boîte sur l’autre pseudo, faisant ainsi apparaître l’élément comme un élément unique.

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>

Mettre tous ensemble

Vous pouvez également ajouter un rayon de bordure à votre boîte de message ou à votre bulle en utilisant à nouveau la propriété 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>

Cela vous permet même de créer non seulement un triangle, mais aussi un cercle.

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>

Si vous rencontrez des problèmes de contenu débordant et que vous êtes "caché" derrière ce pseudo-élément et que vous n'êtes pas inquiet au sujet de la présence d'une bordure, vous pouvez utiliser un index z négatif qui résoudra ce problème.

Vous n'aimez pas utiliser des «nombres magiques»?

Si vous n'aimez pas l'idée d'utiliser une valeur de calcul, dans laquelle le positionnement dans ma réponse utilise actuellement (pendant que vous travaillez), vous pouvez utiliser transform:translate(50%) 

Ce serait une bien meilleure approche, puisque:

  • Vous n'avez pas besoin de connaître la taille de la bordure, ni la moitié de la largeur
  • Vous allez rendre votre boîte de dialogue/bulle beaucoup plus dynamique dans son positionnement et prendre en charge des tailles supplémentaires.

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>

Voulez-vous le déplacer? Vous pouvez!

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>

Voulez-vous un le droit?

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>

Voulez-vous que ce soit une forme de triangle différente?

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

Cela ne passe pas un test de réussite car la bordure transparente est également cliquable 

Cela peut être fait en utilisant les événements de pointeur dans svg.
pointer-events:visibleFill; Ne sélectionnera que la partie où il y a Paint. 

Cet exemple utilise filter_box-shadow et n'est pas pris en charge par IE.
Utilise également deux formes.

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

Cet exemple utilise un chemin
Devrait être entièrement pris en charge par 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

J'ai trouvé ce service formidable pour créer des flèches CSS personnalisables . Générez-le et utilisez-le avec n'importe quel bloc.

http://www.cssarrowplease.com/

0
Oleg Abrazhaev