CSS Apply Border to a Cloud Shape

CSS apply border to a cloud shape?

You can do it without any additional elements. Just use the ::before and ::after pseudo-elements with the same size and round shape as the top cloud bubbles. z-index keeps everything in the right layer.

Demo: jsFiddle

Output:

output

CSS:

body{
background-color: #4ca3ff;
}

#cloud {
height: 230px;
margin: 40px;
position: relative;
width: 400px;
}

#cloud div {
border: solid 5px black;
}

#bottom_c {
background-color: #fff;
border-radius: 100px;
height: 150px;
position: absolute;
top: 100px;
width: 350px;
z-index: 0;
}

#right_c{
background-color: #fff;
border-radius: 100%;
height: 150px;
left: 140px;
position: absolute;
top: 40px;
width: 150px;
z-index: -1;
}

#left_c{
background-color: #fff;
border-radius: 100%;
height: 100px;
left: 50px;
position: absolute;
top: 70px;
width: 100px;
z-index: -1;
}

#cloud::before {
background-color: white;
border-radius: 50%;
content: '';
height: 100px;
left: 55px;
position: absolute;
top: 75px;
width: 100px;
z-index: 1;
}

#cloud::after {
position: absolute; top: 45px; left: 145px;
background-color: white;
border-radius: 50%;
content: '';
width: 150px;
height: 150px;
z-index: 1;
}

HTML:

<div id="cloud">
<div id="bottom_c"></div>
<div id="right_c"></div>
<div id="left_c"></div>
</div>

How to create a cloud by combining shapes into one using CSS?

You can consider multiple background to create your cloud with one element. You can easily add any number of circle/ellipse with any size and position. One value of radius will give a circle and 2 values will give an ellipse

.cloud {  width:200px;  height:150px;  border-radius: 0 0 50px 50px;  background:  /*                radius                            position  / 2xradius*/  radial-gradient(35px 30px,blue 98%,transparent 100%) 20% 30%  /70px    60px,  radial-gradient(50px 45px,blue 98%,transparent 100%) 50% 50%  /100px   90px,  radial-gradient(50px     ,blue 98%,transparent 100%) 100% 100%/100px   100px,  radial-gradient(40px     ,blue 98%,transparent 100%) 0 100%   /80px    80px,  /* base of the cloud */  linear-gradient(blue,blue) bottom/100% 40px;;  background-repeat:no-repeat;}
<div class="cloud"></div>

Creating a responsive cloud shape

The cloud shape can be created using SVG with a single path element in SVG. SVGs by nature are scalable without causing any distortions to the shape. The browser support for SVG is very good and fallback for IE8 and lower (if needed) can be provided using VML.

Shape Creation

The commands used in drawing the shape and their meaning are as follows:

  • M 25,60 - This command moves the pen to a point which is 25px ahead of the origin (0,0) on X-axis and 60px ahead of the origin on Y-axis. (Note: The command is written in uppercase which indicates that it is absolute movement and not relative movement).
  • a 20,20 1 0,0 0,40 - This command creates an arc whose X and Y radii are 20px. The arcs starting point is at (25,60) and the end point is (25,100) (that is, 0px away in X-axis and 40px away in Y-axis).
  • h 50 - This command draws a horizontal line that is 50px ahead in relation to the starting point. Since it is relative, the end point would be at (75,100).
  • a 20,20 1 0,0 0,-40 - Similar to the second command, this creates another arc whose radii are 20px on either axis and its end point is 40px before in relation to the previous point. So in essence this would create an arc from (75,100) to (75,60). This and the second command together form the arcs on the two sides of the cloud.
  • a 10,10 1 0,0 -15,-10 - Another arc command to create one portion of the curved top of the cloud. The radii are 10px and the arc would be from (75,60) to (60,50).
  • a 15,15 1 0,0 -35,10 - The final arc to complete the cloud. Radii are 15px and the arc would be from (60,50) to (25,60). (25,60) was the original starting point and thus completes the shape.
  • z - Closes the path.

svg {  height: 50%;  width: 50%;}path {  fill: white;  stroke: black;  stroke-width: 2;  stroke-linejoin: round;}path:hover {  fill: aliceblue;  stroke: lightskyblue;}
<svg viewBox='0 0 105 105'>  <path d='M 25,60            a 20,20 1 0,0 0,40            h 50            a 20,20 1 0,0 0,-40            a 10,10 1 0,0 -15,-10            a 15,15 1 0,0 -35,10             z' /></svg>

Create a complex CSS shape (speaking bubble)

I had this thing that it could be done with just one element - and it can be done, I just don't think it's exactly the best solution to do it like this.

DEMO

HTML:

<div class='speech-bubble'>Hello!</div>

CSS:

.speech-bubble {
position: relative;
margin: .5em auto;
padding: 1em;
width: 10em; height: 4em;
border-radius: .25em;
transform: rotate(-4deg) rotateY(15deg);
background: #629bdd;
font: 2em/4 Century Gothic, Verdana, sans-serif;
text-align: center;
}
.speech-bubble:before, .speech-bubble:after {
position: absolute;
z-index: -1;
content: '';
}
.speech-bubble:after {
top: 0; right: 0; bottom: 0; left: 0;
border-radius: inherit;
transform: rotate(2deg) translate(.35em, -.15em) scale(1.02);
background: #f4fbfe;
}
.speech-bubble:before {
border: solid 0 transparent;
border-right: solid 3.5em #f4fbfe;
border-bottom: solid .25em #629bdd;
bottom: .25em; left: 1.25em;
width: 0; height: 1em;
transform: rotate(45deg) skewX(75deg);
}

Applying border to image shape

Simple option is to draw the image twice, first with a small scale applied to grow the image a little. Masking if the images aren't transparent (but are black&white).

CSS Clouds - How to adjust to inner content?

First remove all absolute positioning. (as I mentioned in the comments)

Next place the whitesmoke background on the title element instead of it's parent element (which has padding) and you get a pretty good result.

.title {
position: relative;
text-align: center;
background: whitesmoke; /* here */
}

You can experiment with adding pseudo elements around the title to add circles around the top-left and bottom right areas of the text.

.title:before {
content: '';
display:inline-block;
width: 100%;
height: 50%;
position: absolute;
left: -40px;
z-index: -1;
box-shadow: inset 0px 0px 16px -4px white, -1px 0 4px -2px black;
border-radius: 100%;
background: whitesmoke;
top:0;
}
.title:after {
content: '';
display:inline-block;
width: 100%;
height: 50%;
position: absolute;
right: -40px;
z-index: -1;
box-shadow: inset 0px 0px 16px -4px white, 1px 0 4px -2px black;
border-radius: 100%;
background: whitesmoke;
bottom:0;
}

Updated FIDDLE (I updated the fiddle you posted in the comments)

I can't frame this image inside an SVG

Use mask not mask-border and apply the same properties as the background to have a perfect matching:

.wrapper {
background-image:url('https://i.ibb.co/KjmqTs2/cloud.png');
background-repeat:no-repeat;
background-size:200px;
background-position:50% 50%;
-webkit-mask: url('https://i.ibb.co/KjmqTs2/cloud.png');
-webkit-mask-repeat:no-repeat;
-webkit-mask-size:200px;
-webkit-mask-position:50% 50%;
width:200px;
}
img {
max-width:100%;
}
<div class="wrapper">
<img src="https://i.stack.imgur.com/2GKtb.png" alt="Sample Image" class="svg-border-mask" />
</div>

Place objects on a defined shape

To illustration your solution, let's assume you want to drop a raindrop in your cloud and be sure all raindrop pixels are fully inside the cloud...

Sample ImageSample Image

Then the answer to your question requires asking this question:

Are all opaque raindrop pixels fully inside the cloud?

To answer this question, you must compare every pixel on the raindrop with every pixel underneath.

  • If the raindrop pixel is transparent, then ignore this pixel because this part of the raindrop is transparent anyway.
  • If the raindrop pixel is opaque and the pixel underneath is transparent, then this raindrop pixel is not contained in the cloud.
  • If both the raindrop pixel & underneath pixel are opaque, then this raindrop pixel is contained in the cloud.

You can get the required transparency information about the raindrop and cloud by drawing their images on a canvas and then requesting getImageData. 'getImageData' returns the red, green, blue & alpha information about every pixel on the canvas. To answer the question, we just need the alpha information.

Here's annotated code and a Demo:

// canvas related variablesvar canvas=document.getElementById("canvas");var ctx=canvas.getContext("2d");var cw=canvas.width;var ch=canvas.height;var offsetX,offsetY;
// load the cloud and raindrop imagesvar cloudmap,rainmap;var rain=new Image();rain.crossOrigin='anonymous';rain.onload=start;rain.src='https://dl.dropboxusercontent.com/u/139992952/raindrop1.png';var cloud=new Image();cloud.crossOrigin='anonymous';cloud.onload=start;cloud.src="https://dl.dropboxusercontent.com/u/139992952/cloud.png";var cloud1=new Image();cloud1.crossOrigin='anonymous';cloud1.onload=start;cloud1.src="https://dl.dropboxusercontent.com/u/139992952/multple/cloud1.png";var imageCount=3;
function start(){ if(--imageCount>0){return;}
// resize the canvas to the size of the cloud // and draw the cloud on the canvas cw=canvas.width=cloud.width; ch=canvas.height=cloud.height; draw();
// create a transparency map of the cloud cloudmap={ width:cloud.width, height:cloud.height, map:transparencyMap(cloud), };
// create a transparency map of the raindrop rainmap={ width:rain.width, height:rain.height, map:transparencyMap(rain), }
// listen for mousemove events $("#canvas").mousemove(function(e){handleMouseMove(e);});
// listen for window scroll events calcCanvasOffset(); $(window).scroll(function(){ calcCanvasOffset(); });
}

function transparencyMap(img){ // create a temp canvas sized to the img size var c=document.createElement('canvas'); var cctx=c.getContext('2d'); c.width=img.width; c.height=img.height; // draw the img on the canvas cctx.drawImage(img,0,0); // get the pixel data for every pixel on the canvas var data=cctx.getImageData(0,0,c.width,c.height).data; // create an array that reports the status // of every pixel on the canvas // (status: true if opaque, false if transparent) var map=[]; for(var i=0;i<data.length;i+=4){ map.push(data[i+3]>250); } return(map);}

function draw(mouseX,mouseY,isContained){ // draw the cloud ctx.clearRect(0,0,cw,ch); if(isContained){ // draw the blue cloud indicating the raindrop is not fully contained ctx.drawImage(cloud,0,0); }else{ // draw the yellow cloud indicating the raindrop is fully contained ctx.drawImage(cloud1,0,0); } // if the mouse position was supplied if(mouseX){ ctx.drawImage(rain,mouseX-rain.width/2,mouseY-rain.height/2); }}

function AcontainsB(ax,ay,amap,bx,by,bmap){ // set a flag indicating of the raindrop is fully contained in the cloud var isContained=true;
// calc the relative position of the raindrop vs cloud in the canvas var deltaX=bx-ax; var deltaY=by-ay;
// test every pixel of B against A // if B is opaque and a is not opaque then B is not contained by A var y=0; while(isContained && y<bmap.height){ var x=0; while(isContained && x<bmap.width){ // calc the map array indexes for the cloud(A) & raindrop(B) var mapIndexA=(y+deltaY)*amap.width+(x+deltaX); var mapIndexB=y*bmap.width+x; // if the raindrop is opaque at this pixel if(bmap.map[mapIndexB]){
// ...and if this pixel is off canvas if(mapIndexA<0 || mapIndexA>=amap.map.length){ // ...then the raindrop is not in the cloud at this pixel isContained=false; // ...or if the pixel under the raindrop is transparent }else if(!amap.map[mapIndexA]){ // ...then the raindrop is not in the cloud at this pixel isContained=false; } } x++; } y++; } return(isContained);}

function handleMouseMove(e){ // tell the browser we're handling this event e.preventDefault(); e.stopPropagation();
// get the current mouse position mouseX=parseInt(e.clientX-offsetX); mouseY=parseInt(e.clientY-offsetY);
// calc the top-left corner of the raindrop image var rainX=parseInt(mouseX-rain.width/2); var rainY=parseInt(mouseY-rain.height/2);
// ask if the raindrop is fully contained in the cloud var isContained=AcontainsB(0,0,cloudmap,rainX,rainY,rainmap);

// redraw the cloud & raindrop draw(mouseX,mouseY,isContained);
}

// recalc the canvas offsetX & offsetYfunction calcCanvasOffset(){ var BB=canvas.getBoundingClientRect(); offsetX=BB.left; offsetY=BB.top; }
body{ background-color: ivory; }canvas{border:1px solid red;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script><h4>Use the mouse to drag the raindrop over the canvas<br>The cloud turns blue if the rain is fully inside the cloud</h4><canvas id="canvas" width=600 height=500></canvas>


Related Topics



Leave a reply



Submit