28172 sujets

CSS et mise en forme, CSS3

Bonjour,

J'essaye de styliser la mise en forme de mes vignettes sur mon site internet.

Je veux lui donner une forme de bulle de BD, avec les coins arrondis (ça je sais faire), mais aussi avec un triangle en bas, comme sur l'image jointe (cliquer sur l'image pour bien voir l'effet) :

upload/40365-border-tri.png

Je voudrais faire cela proprement, c'est-à-dire éviter de programmer une image qui se superposerait sur le bas pour laisser apparaître le triangle. Je sais que l'on peut programmer des triangles en css3, mais je ne vois pas comment je pourrais m'y prendre pour qu'il donne cette forme à mon image.

Je suis preneur de toute explication : code, liens vers tutoriaux, etc.

Je vous remercie par avance de votre aide. Smiley smile
Modifié par onirisweb (07 Jun 2014 - 20:24)
Modérateur
Bonjour,

Ci-dessous un code qui permet de clipper un élément HTML (j'ai utilisé un div, mais ça marche en théorie avec n'importe quel élément HTML : voir la ligne en commentaire dans le code HTML juste au dessus du div et qui permet d'afficher une image).

Ce n'est pas encore du css pur bien qu'à l'avenir, cela pourrait le devenir (pour l'instant, il faut mélanger du css et un peu de svg).

Je n'ai pas réussi à faire un masque unique pour firefox et google/safari (firefox semble calculer les coordonnées du masque par rapport au coin supérieur gauche de l'élément, safari/google par rapport au coin supérieur gauche de la fenêtre : à vérifier).

Testé sous safari/google,firefox récent (mac OS). Je n'ai pas testé ie.


<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body {padding:0;margin:10px;}
h1 {font-size:30px;height:50px;margin:0;padding:0;}
.bubble
{
	display:block;
	width:450px;
	height:350px;
	color:white;
	background:red;
	font-size:1.8em;
}

.bubble
{
	-webkit-clip-path:url(#bubbleClip2);
	clip-path:url(#bubbleClip);
}

.innerBubble
{
	padding:0.5em;
}
</style>

</head>
<body>
<h1>Bubble clip</h1>

<!--
<img class="bubble" width="450" height="350" src="im.jpg">
-->

<div class="bubble">
<div class="innerBubble">
Remplacez le div de classe "bubble" par une image genre &lt;img class="bubble" width="450" height="350" src="..."&gt;.<br><br>
Elle sera "clippée" de la même manière.
</div>
</div>


<svg width="0" height="0">
  <defs>
  	<clipPath id="bubbleClip">
  		<rect x="0" y="0" width="450" height="330" rx="20"/>
  		<polygon points="215 330,235 330,225 350"/>
  	</clipPath>
  	<clipPath id="bubbleClip2">
  		<rect x="10" y="60" width="450" height="330" rx="20"/>
  		<polygon points="225 390,245 390,235 410"/>
  	</clipPath>
  </defs>
</svg>

</body>
</html>


EDIT : ajout d'un div à l'intérieur du div faisant la bulle pour afficher le texte de manière plus élégante.
EDIT2 : correction d'un bug qui faisait que l'affichage de la bulle pouvait être incorrect.

Amicalement,
Modifié par parsimonhi (09 Jun 2014 - 12:17)
salut,
on peut faire ça en CSS mais pour le côté dynamique, je ne vois pas trop comment pour l'instant.
Faut savoir que pour faire ça tu ne peux faire autrement qu'en "bricolant" un peu. Déjà il sera plus simple de le faire sans coins arrondies.
Voici un exemple sans coins arrondies et un autre avec coins arrondies.
Modérateur
Bonjour,

Un petit jsfiddle pour la route (solution css via la propriété clip-path, avec un peu de svg en attendant qu'elle soit nativement supportée par les navigateurs) : http://jsfiddle.net/AJtcc/

EDIT : si quelqu'un sait comment faire pour que le clipPath svg ait pour origine le coin supérieur gauche de l'élément HTML auquel il s'applique avec les navigateurs de la famille webkit, je suis preneur !

EDIT : finalement, problème des coordonnées résolues, voir http://jsfiddle.net/AJtcc/1/

Amicalement,
Modifié par parsimonhi (09 Jun 2014 - 15:48)
Modérateur
Bonjour,

J'ai fini par trouver comment résoudre ce problème d'origine des coordonnées et unifier le comportement entre firefox et safari/chrome. Il faut ajouter l'attribut clipPathUnits="objectBoundingBox" à la balise clipPath du svg, et ensuite utiliser des coordonnées sous forme de fraction (1.0 => 100%, 0.5 => 50%, ...). Du coup, la bulle s'adapte automatiquement aux dimensions de l'élément HTML qui y fait référence (via la proprité css -webkit-clip-path:url(#bubbleClip); et clip-path:url(#bubbleClip);).

Pour notre bulle, ça donne ça :


.bubble
{
	-webkit-clip-path:url(#bubbleClip);
	clip-path:url(#bubbleClip);
}


<img class="bubble" width="450" height="350" src="im.png">
<svg width="0" height="0">
	<defs>
		<clipPath id="bubbleClip" clipPathUnits="objectBoundingBox">
			<rect x="0" y="0" width="1.0" height="0.95" rx="0.1"/>
			<polygon points="0.45 0.95,0.55 0.95,0.5 1.0"/>
		</clipPath>
	</defs>
</svg>


EDIT : il semble que si on met plusieurs images dans la même page, safari n'affiche que la première correctement et cache toutes les autres.

Qu'en pensez-vous ?

Amicalement,
Modifié par parsimonhi (09 Jun 2014 - 16:47)
Je viens de me reconnecter et découvre vos réponses. Merci beaucoup, je vais explorer tous ces liens et codes.
Modérateur
Bonjour,

N'ayant toujours pas réussi à faire marcher le "-webkit-clip-path:url(#bubbleClip);" dès qu'il y a plus d'une image concernée dans la page, je pense qu'il est préférable de ne garder dans le css que :


.bubble
{
	clip-path:url(#bubbleClip);
	border-radius:10%;
}


Du coup, la solution ne marche que pour firefox, les autres navigateurs se contentant d'afficher les photos dans un rectangle aux coins éventuellement arrondis pour ceux qui savent le faire.

Une autre solution consisterait à afficher le svg via une balise object. Par exemple :


<object data="test.php?im=im.jpg&w=800&h=600" width="300" height="225" type="image/svg+xml"><img class="noSvgImg" src="im.jpg" width="300" height="225"></object>


Le code css est réduit au minimum :


object, .noSvgImg {border-radius:10%;margin:5px;}


Mais on a alors aussi besoin d'un script php (qui en fait est essentiellement du svg brut) qui va générer ce dont a besoin la balise object :


<?php
header("Content-type: image/svg+xml");
$im=preg_replace("/[()]/","",$_GET["im"]);
$w=preg_replace("/[()]/","",$_GET["w"]);
$h=preg_replace("/[()]/","",$_GET["h"]);
?>
<svg width="100%" height="100%" preserveAspectRatio="none" viewBox="0 0 <?php print $w;?> <?php print $h;?>" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
	<defs>
		<clipPath id="bubbleClip" clipPathUnits="objectBoundingBox">
			<rect x="0" y="0" width="1.0" height="0.95" rx="0.1"></rect>
			<polygon points="0.45,0.95 0.55,0.95 0.5,1.0"></polygon>
		</clipPath>
	</defs>
	<g>
		<image width="100%" height="100%" clip-path="url(#bubbleClip)" xlink:href="<?php print $im;?>"></image>
	</g>
</svg>


La solution est un peu lourde, mais elle affichera une bulle sur quasiment tous les navigateurs supportant le svg. Cette manière de faire est mieux supportée par les navigateurs (et moins boguée) que celle consistant à mettre des balises svg directement dans le HTML. Et avec ce mécanisme, sans rien changer au code HTML, on peut fabriquer des effets les plus divers dans le fichier php générant le svg. Enfin, du fait que object, en cas d'erreur, affiche ce qui se trouve entre sa balise ouvrante et celle fermante, en y mettant une balise img, on s'assure que même les navigateurs ne supportant pas svg pourront afficher l'image quand même.

Amicalement,
Modérateur
Bonjour,

Concernant la solution de Zelalsan pour la bulle aux coins arrondis (bravo au passage), on peut simplifier un peu :


div {display:table;margin:auto;height:300px;}
span:first-child {display:block;overflow:hidden;height:280px;border-radius:10px;}
span:last-child {display:block;height:0;width:0;margin:auto;}
span:last-child {background:url(http://img2.archilovers.com/people/thumb2_76ce02c5-f950-4730-ad49-bd4c159499f5-log1.jpg) no-repeat center bottom;}
span:last-child
{
	border-top:20px solid transparent;
	border-left:20px solid white;
	border-right:20px solid white;
	border-bottom:0;
}


On notera que si on a un fond coloré derrière l'image, il faudra changer les couleurs des border-left et border-right du triangle du bas en conséquence.

EDIT : si on ne veut pas faire référence à l'image dans le fichier css, on peut éventuellement utliser l'attribut style pour une fois. Le code complet devient :


<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<style>

div.bubble {display:table;margin:auto;height:300px;}
div.bubble span:first-child {display:block;overflow:hidden;height:280px;border-radius:10px;}
div.bubble span:last-child {display:block;height:0;width:0;margin:auto;}
div.bubble span:last-child
{
	border-top:20px solid transparent;
	border-left:20px solid white;
	border-right:20px solid white;
	border-bottom:0;
}
</style>

</head>
<body>
<h1>Bubble clip</h1>

<div class="bubble">
		<span>
		<img src="http://img2.archilovers.com/people/thumb2_76ce02c5-f950-4730-ad49-bd4c159499f5-log1.jpg" alt="" />
	</span>
	<span style="background:url(http://img2.archilovers.com/people/thumb2_76ce02c5-f950-4730-ad49-bd4c159499f5-log1.jpg) no-repeat center bottom;">
	</span>
</div>

</body>
</html>


Amicalement,
Modifié par parsimonhi (11 Jun 2014 - 10:46)
Une solution jquery et pseudo, pourrait aussi simplifier le code HTML http://codepen.io/gc-nomade/pen/hjAvp

la base html
<div class="jq uery">
  <img src="http://lorempixel.com/200/200/people/9"alt="people"/>
</div>

Le div en display:table ou inline-table ou inline-block selon l'utilité.
l'image en display:block avec un arrondi.
.jq {
  display:inline-block;
  color:white;/* defaut color border too */
  vertical-align:top;
}
img {
  display:block;
  border-radius:1em;
}


Dans le css on prepare 1 class et une pseudo class
- Une class pour reduire la partie visible de l'image à zéro pour ne montrer qu'un fond de taille presque similaire fond a l'aide de marge interne (attention au box-sizing).
- Un pseudo element qui ne servira plus tard a afficher un bout d'image en heritage de son conteneur.
.jq:after {
  display:block;
  height:0;
  width:0;
  margin:auto;
  border-top:transparent 1em solid;
  border-left: 1em solid;
  border-right:1em solid;  
  content:'';
  background-image:inherit;
  background-position: center bottom;
}
.jq img.jq {
  padding-top: calc(200px - 1em );
  padding-right: 200px;
  height:0;
  width:0;
  background:inherit;
  background-position:0 0;
}


Jquery entre en action pour faire une boucle sur toutes ces boites, y trouver la source de la balise image et appliquée celle-ci en fond à la boite.
$('.jq.uery').each(function(i, obj) {
  var bg = $(this).find('img').attr('src');
  $(this).find('img').addClass('jq');
  $(this).css('background', 'url('+bg+') no-repeat 9999px 9999px' );
});

ensuite nos deux class , via background:inherit; appliquerons en fond l'image en src correspondante.

Pour modifier la taille du triangle, il suffit de modifier le font-size de la boite et obtenir des résultats amusant http://codepen.io/anon/pen/hpuBs

Cdt,
Modifié par gc-nomade (11 Jun 2014 - 15:46)
Modérateur
Bonjour,

On va finir par y arriver avec 1 ligne de HTML, 0 css et 0 script si ça continue ! Smiley cligne

On peut effectivement réduire le HTML a un div entourant un img comme le propose entre autre gc-nomade (beaucoup d'autres trouvailles dans sa solution) et éventuellement se passer de jquery au prix d'un (minuscule) style="background-image:url(im.jpg)". On peut aussi en fin de compte supprimer toutes les tailles qui sont en px, la taille de la boite étant donnée par la taille de l'image en apportant quelques modifications au css :


<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<style>
div.bubble
{
	font-size:1.25em;
	display:inline-block;
	position:relative;
	overflow:visible;
	border-radius:5%;
	margin:0 auto 1em auto;
	background-repeat:no-repeat;
	background-position:center top;
	color:white;
}
div.bubble img
{
	display:block;
	margin-bottom:-1em;
	visibility:hidden;
}
div.bubble:after
{
	content:"";
	display:block;
	position:absolute;
	bottom:-1em;
	left:calc(50% - 1em);
	border-top:1em solid transparent;
	border-left:1em solid;
	border-right:1em solid;
	background-image:inherit;
	background-position:center bottom;
	background-size:inherit;
}
</style>

</head>
<body>
<h1>Bubble clip</h1>
<div class="bubble" style="background-image:url(im.jpg)">
	<img src="im.jpg" alt="" />
</div>

</body>
</html>


Et si l'on ne veut pas de ce (toujours aussi minuscule mais certes irritant) attribut style, on peut le supprimer et adapter le code jquery de gc-nomade (il ne marche pas tel quel avec la solution que je propose, mais il y a peu à modifier), ou simplement inclure en bas de page le petit script javascript ci-dessous :


function magic()
{
	var divs=document.getElementsByClassName("bubble"),k,km=divs.length;
	for (k=0;k<km;k++) divs[k].style.backgroundImage="url("+divs[k].firstElementChild.src+")";
}
window.addEventListener("load",magic,false);


Du coup, on a une solution très dynamique qui s'adapte à toutes les images sans toucher au code css me semble-t-il !

EDIT (13/06/2014) :
1) pour le font-size de div.bubble, il est préférable de choisir une valeur en em ou en une autre unité qui va donner une valeur entière une fois convertie en px ou de mettre directement une valeur en px, sinon on s'expose à des erreurs d'arrondis.
2) si on donne une valeur aux attributs width et height dans la balise img et que ces valeurs sont différentes de la taille de l'image, il faut aussi rajouter un "background-size" avec ces valeurs en px dans l'attribut style du div contenant la bulle (et non dans le css : je n'ai pas encore compris pourquoi pour l'instant).

Amicalement,
Modifié par parsimonhi (13 Jun 2014 - 11:56)