229

How do I create a shape like this to display on a webpage?

I don't want to use images since they would get blurry on scaling

Teardrop shape I need to make with HTML, CSS or SVG

I tried with CSS:

.tear {
  display: inline-block;
  transform: rotate(-30deg);
  border: 5px solid green;
  width: 50px;
  height: 100px;
  border-top-left-radius: 50%;
  border-bottom-left-radius: 50%;
  border-bottom-right-radius: 50%;
}
<div class="tear">
</div>

That turned out really screwed.

And then I tried with SVG:

<svg viewBox="0 100 100">
  <polygon points="50,0 100,70 50,100 0,70"/>
</svg>

It did get the shape, but the bottom part wasn't curved.

Is there a way to create this shape so it can be used in an HTML page?

Persijn
  • 14,624
  • 3
  • 43
  • 72
  • You can create the shape you want here (and see the code by clicking one of the top buttons) : http://svg-edit.googlecode.com/svn/trunk/editor/svg-editor.html#group – Seblor Jun 08 '15 at 14:09
  • 12
    "I don't want to use images since they would get blurry on scaling", why are you scaling the image? They don't get blurry if you use `[srcset]` or the `` element. Better yet, just link to an svg image: `Teardrop` – zzzzBov Jun 08 '15 at 15:59
  • 33
    @zzzzBov: Are you really recommending the [`picture`](http://caniuse.com/#search=picture) element? **No support** for IE, **no support** for early versions of Chrome, **no support** for safari. Should i continue? – jbutler483 Jun 08 '15 at 16:03
  • 2
    @jbutler483, I recommend SVG for this case, but after that, [yes i'm recommending the `` element, along with Picturefill and an appropriate noscript fallback](http://scottjehl.github.io/picturefill/). – zzzzBov Jun 08 '15 at 16:51
  • 9
    @zzzzBov. `why are you scaling the image?` Well, the image can appear blurry without the dev scaling it. All you need for that is a browser zooming in. In my case, I have a HighDPI screen and plenty of blurry images. So, yeah, if you can avoid images using SVG, go for it. – Nolonar Jun 08 '15 at 21:58
  • 64
    Unicode solves it all... U+1F4A7 – Thomas Weller Jun 09 '15 at 14:03
  • 21
    @Thomas I see a square :/ http://i.stack.imgur.com/8fXMf.png – user000001 Jun 09 '15 at 16:52
  • I think "droplet" is more appropriate to use for the title of this question? – Netorica Jun 10 '15 at 03:04
  • 3
    @jbutler483 Safari and IE maybe, but who cares about early versions of Chrome? Especially since Chrome all but forces you to auto-update. – user253751 Jun 10 '15 at 11:24
  • 1
    Semantically and practically, I think you should use an image for this. But, of course, you should *not* use a raster graphics image (not PNG, and definitely not JPG, God no), but a **vector graphics** image (that is, in practice, SVG). – Andreas Rejbrand Jun 10 '15 at 12:28
  • 3
    There are benefits to both using a **css** and **svg** solution to achieve this shape. – Persijn Jun 10 '15 at 12:32
  • @immibis older mobile phones may not get updates for browsers – Volvox Jun 12 '15 at 07:13
  • Why not change the title of question to "drop of water"? – Salman A Jun 13 '15 at 13:58
  • 4
    @SalmanA ... because a teardrop is the best description for this shape. An actual drop of water (not on a surface) looks more like a jelly fish without tentacles... though most people do recognize a teardrop as the symbol for rain, this is a combination of tricks of the eye in fast motion of a rain drop interpolated to more observable slower flow of a teardrop – technosaurus Jun 14 '15 at 02:32

11 Answers11

332

SVG approach:

You can achieve the double curve easily with an inline SVG and the <path/> element instead of the <polygon/> element which doesn't allow curved shapes.

The following example uses the <path/> element with:

<svg width="30%" viewbox="0 0 30 42">
  <path fill="transparent" stroke="#000" stroke-width="1.5"
        d="M15 3
           Q16.5 6.8 25 18
           A12.8 12.8 0 1 1 5 18
           Q13.5 6.8 15 3z" />
</svg>

SVG is a great tool to make this kind of shapes with double curves. You can check this post about double curves with an SVG/CSS comparison. Some of the advantages of using SVG in this case are:

  • Curve control
  • Fill control (opacity, color)
  • Stroke control (width, opacity, color)
  • Amount of code
  • Time to build and maintain the shape
  • Scalable
  • No HTTP request (if used inline like in the example)

Browser support for inline SVG goes back to Internet Explorer 9. See canIuse for more information.

Community
  • 1
  • 1
web-tiki
  • 99,765
  • 32
  • 217
  • 249
  • it can reduce to: ` ` ... this will fill the area you give it, so it may make "fat" or "skinny" raindrops ... change to fixed height/width if necessary – technosaurus Jun 09 '15 at 07:32
  • 1
    @technosaurus the problem with reducing the number of commands in the `d="..."` attribute is that you don't have the double curve at the top of the drop anymore. – web-tiki Jun 09 '15 at 07:40
  • 10
    +1 because you should use SVG for this, not CSS. The CSS features you'd need to achieve it have about the same browser support as SVG so there's no advantage for CSS on that score. CSS can do shapes, but that's not what it's designed for; don't try to hammer in a nail with a screwdriver just for the sake of being clever when you can do it just as well with a tool designed for the job. – Simba Jun 10 '15 at 11:51
  • 6
    Even better: create a SVG file and use `` in the hypertext document. – Andreas Rejbrand Jun 10 '15 at 12:31
  • @AndreasRejbrand that can be a good idea depending on the project but it adds a HTTP request which OP seems to want to avoid. – web-tiki Jun 13 '15 at 11:56
144

Basic Border-Radius

You can do this within CSS relatively easily using border-radius' and transforms. Your CSS was just a little bit out.

.tear {
  width: 50px;
  height: 50px;
  border-radius: 0 50% 50% 50%;
  border: 3px solid black;
  transform: rotate(45deg);
  margin-top: 20px;
}
<div class="tear"></div>

Advanced Border-Radius

This will be very similar to above but gives it a bit more shape.

.tear {
  width: 50px;
  height: 50px;
  border-radius: 80% 0 55% 50% / 55% 0 80% 50%;
  border: 3px solid black;
  transform: rotate(-45deg);
  margin-top: 20px;
}
<div class="tear"></div>
jbutler483
  • 24,074
  • 9
  • 92
  • 145
Stewartside
  • 20,378
  • 12
  • 60
  • 81
  • 5
    @zzzzBov I don't fully understand how it is the wrong tool, OP asked for a CSS or SVG solution and i came up with a CSS solution which fits the description as close as possible. Its lightweight and easy to modify. – Stewartside Jun 08 '15 at 15:52
  • 1
    Nowhere in the question does it state that CSS or SVG is a requirement. OP just posted his own attempts and asked "Is there anyway to create this shape so it can be used in a html page?" This answer suggests using CSS, which is not an appropriate way of generating what is semantically an image. – zzzzBov Jun 08 '15 at 15:55
  • 12
    @zzzzBov Using CSS for images and sprites is common use. It is a very minimal piece of code to generate a generic "Image". I believe it is appropriate because it is usable within the questions spec of being usable on a web page. – Stewartside Jun 08 '15 at 15:59
  • 13
    @zzzzBov: CSS is perfectly fine for shapes. Who's to say what such shape is used for? *semantically an image* - OP has clearly stated they do not wish to use an image, otherwise why else would such a question be asked? – jbutler483 Jun 08 '15 at 16:00
  • 48
    @jbutler483, "CSS is perfectly fine for shapes" no, it's awful. Just because you *can* doesn't mean you *should*. It introduces all sorts of garbage in markup, and is a tedious mess to maintain. Images are much simpler to maintain as they are easily separated into their own individual self-contained file. – zzzzBov Jun 08 '15 at 16:53
  • 4
    @zzzzBov: I'm guessing you're just one of those people who don't like shapes. That's fine. But I suppose you'd also use an image for the likes of a circle just because it's more 'symantic' to do so. Whilst yes, for some items an image would be a smaller size than that of a css file, but in most cases, why add an extra http call when you can (quite happily) reduce this by using a short piece of css. – jbutler483 Jun 09 '15 at 08:08
  • 21
    'Garbage in the markup' occurs when you use bootstrap, or font awesome icons. Seriously, I think you're going a bit over the top for the likes of this when (quite obviously) this can be achieved using a border radius declaration. But hay, who's to tell you not to use images? – jbutler483 Jun 09 '15 at 08:08
  • 1
    @zzzzBov: If it's decoration, it requires no markup at all; a `:before` or `:after` will do. If it's content ("semantically an image"), you can use an appropriate small image and alt text for fallback (CSS load failure, user settings, accessibility) and still use CSS to get something that scales well. SVG works as well, but it should only take two lines and an arc in a single stroked path to get the job done. – Stan Rogers Jun 09 '15 at 15:27
  • 4
    CSS can be a completely valid solution. @zzzzBov if a client demanded a non-standard li bullet i.e. checkmark, triangle, whatever, would you use an image? Or would you use :before? If you would use something else please leave a comment, I like learning new methods. – sareed Jun 09 '15 at 21:38
  • 1
    For CSS, it's amazing - but I prefer the SVG approach – Jeremy Dicaire Jun 10 '15 at 17:55
89

Your main issue with your CSS code was:

  1. You used a different height than width
  2. You haven't rotated the correct angle size

So, by 'fixing' these issues, you would generate:

.tear {
  display: inline-block;
  transform: rotate(-45deg);
  border: 5px solid green;
  width: 100px;
  height: 100px;
  border-top-left-radius: 50%;
  border-bottom-left-radius: 50%;
  border-bottom-right-radius: 50%;
}
/***for demo only***/

.tear {
  margin: 50px;
}
<div class="tear">
</div>

Please also note to save on CSS length, you could re-write your border-radius properties to:

border-radius: 50% 0 50% 50%;

this could be enhanced with pseudo elements as shown in this fiddle

Alternatives

I found this by Vinay Challuru on codepen.

Please note that with the logic here, I was able to create the SVG to nearly any possible build shape/etc. For example, a quick output was:

<svg viewBox='0 0 400 400'>
  <path fill="none" stroke="#333" stroke-width="5" d="M200,40 C200,115 280,180 280,240 A80,80,0 0,1,120,240 C120,180 200,115 200,40" stroke-linejoin='miter'></path>
</svg>

It's using an SVG and allows you to alter the shape in multiple ways, having the ability to alter its shape to the desired result:

var SVG = function() {
  this.element = document.getElementsByTagName("svg")[0];
  this.namespace = "http://www.w3.org/2000/svg";
  this.width = 400;
  this.height = 400;
}

/****Let's initialise our SVG ready to draw our shape****/
var svg = new SVG();

/****This sets up the user interface - we've included the script for this as an external library for the codepen****/
var gui = new dat.GUI();

/****Here's where the code to create the shape begins!****/
var Teardrop = function() {
  this.x = svg.width * 0.5;
  this.y = svg.height * 0.1;
  this.width = svg.width * 0.4;
  this.triangleHeight = svg.height * 0.5;
  this.yCP1 = svg.height * 0.2;
  this.yCP2 = svg.height * 0.45;
  this.element = null;
  this.ctrlPoints = [];
  this.anchors = [];
  this.fill = "none";
  this.stroke = "#333";
  this.strokeWidth = 2;
  this.showCtrlPoints = true;
  this.init();
}

Teardrop.prototype.init = function() {
  this.element = document.createElementNS(svg.namespace, "path");
  svg.element.appendChild(this.element);
  this.element.setAttribute("fill", this.fill);
  this.element.setAttribute("stroke", this.stroke);
  this.element.setAttribute("stroke-width", this.strokeWidth);

  for (var i = 0; i < 3; i++) {
    this.ctrlPoints.push(document.createElementNS(svg.namespace, "circle"));
    svg.element.appendChild(this.ctrlPoints[i]);

    this.ctrlPoints[i].setAttribute("fill", this.fill);
    this.ctrlPoints[i].setAttribute("stroke", 'red');
    this.ctrlPoints[i].setAttribute("stroke-width", 1);


    this.anchors.push(document.createElementNS(svg.namespace, "line"));
    svg.element.appendChild(this.anchors[i]);

    this.anchors[i].setAttribute("stroke-width", 1);
    this.anchors[i].setAttribute("stroke", this.stroke);
    this.anchors[i].setAttribute("stroke-dasharray", "3,2");
  }

  this.draw();
}

Teardrop.prototype.draw = function() {
  this.radius = this.width / 2;
  path = [
    "M", this.x, ",", this.y,
    "C", this.x, ",", this.yCP1, " ", this.x + this.width / 2, ",", this.yCP2, " ", this.x + this.width / 2, ",", this.y + this.triangleHeight,
    "A", this.radius, ",", this.radius, ",", "0 0,1,", this.x - this.width / 2, ",", this.y + this.triangleHeight,
    "C", this.x - this.width / 2, ",", this.yCP2, " ", this.x, ",", this.yCP1, " ", this.x, ",", this.y
  ];
  this.element.setAttribute("d", path.join(""));

  cpCoords = [];
  cpCoords[0] = [this.x, this.yCP1];
  cpCoords[1] = [this.x - this.width / 2, this.yCP2];
  cpCoords[2] = [this.x + this.width / 2, this.yCP2];

  anchorCoords = [];
  anchorCoords[0] = [this.x, this.y];
  anchorCoords[1] = [this.x - this.width / 2, this.y + this.triangleHeight];
  anchorCoords[2] = [this.x + this.width / 2, this.y + this.triangleHeight];

  for (var i = 0; i < 3; i++) {
    this.ctrlPoints[i].setAttribute("cx", cpCoords[i][0]);
    this.ctrlPoints[i].setAttribute("cy", cpCoords[i][1]);

    this.anchors[i].setAttribute("x1", cpCoords[i][0]);
    this.anchors[i].setAttribute("x2", anchorCoords[i][0]);
    this.anchors[i].setAttribute("y1", cpCoords[i][1]);
    this.anchors[i].setAttribute("y2", anchorCoords[i][1]);

    if (this.showCtrlPoints) {
      this.ctrlPoints[i].setAttribute("r", 2);
      this.anchors[i].setAttribute("stroke-width", 1);
    } else {
      this.ctrlPoints[i].setAttribute("r", 0);
      this.anchors[i].setAttribute("stroke-width", 0);
    }
  }
}

var teardrop = new Teardrop();

gui.add(teardrop, 'triangleHeight', 0, svg.height * 0.75);
gui.add(teardrop, 'width', 0, 200);
gui.add(teardrop, 'yCP1', 0, svg.height);
gui.add(teardrop, 'yCP2', 0, svg.height);
gui.add(teardrop, 'showCtrlPoints', 0, svg.height);

for (var i in gui.__controllers) {
  gui.__controllers[i].onChange(function() {
    teardrop.draw();
  });
}
html,
body {
  height: 100%;
}
svg {
  display: block;
  margin: 0 auto;
  background: url('http://unitedshapes.com/images/graph-paper/graph-paper.png');
}
<script src="//cdnjs.cloudflare.com/ajax/libs/dat-gui/0.5/dat.gui.min.js"></script>
<svg width='400px' height='400px'></svg>

Disclaimer I did not write the above pen, only sourced it.


CSS Version

Although this is far from complete, you may also be able to generate this shape using CSS.

.tear{
    height:200px;
    width:200px;
    background: linear-gradient(to bottom, rgba(0,0,0,0) 0%,rgba(0,0,0,0) 29%,rgba(0,0,0,1) 30%,rgba(0,0,0,1) 100%);
    border-radius:50%;
    margin:120px;
    position:relative;
}
.tear:before{
    content:"";
    position:absolute;
    top:-70%;left:0%;
    height:100%;width:50%;
    background: radial-gradient(ellipse at -50% -50%, rgba(0,0,0,0) 0%,rgba(0,0,0,0) 75%,rgba(0,0,0,1) 76%,rgba(0,0,0,1) 100%);
}
.tear:after{
    content:"";
    position:absolute;
    top:-70%;left:50%;
    height:100%;width:50%;
    background: radial-gradient(ellipse at 150% -50%, rgba(0,0,0,0) 0%,rgba(0,0,0,0) 75%,rgba(0,0,0,1) 76%,rgba(0,0,0,1) 100%);
}
<div class="tear"></div>

SVG Version

I should know that SVG should be at the top of this answer, however, I like a challenge and so here is an attempt with SVG.

svg {
  height: 300px;
}
svg path {
  fill: tomato;
}
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" viewBox="0 0 100 100">

  <path d="M49.015,0.803
    c-0.133-1.071-1.896-1.071-2.029,0
    C42.57,36.344,20,43.666,20,68.367   
    C20,83.627,32.816,96,48,96
    s28-12.373,28-27.633
    C76,43.666,53.43,36.344,49.015,0.803z 
    M44.751,40.09   
    c-0.297,1.095-0.615,2.223-0.942,3.386
    c-2.007,7.123-4.281,15.195-4.281,24.537
    c0,5.055-2.988,6.854-5.784,6.854   
    c-3.189,0-5.782-2.616-5.782-5.831
    c0-11.034,5.315-18.243,10.005-24.604
    c1.469-1.991,2.855-3.873,3.983-5.749   
    c0.516-0.856,1.903-0.82,2.533,0.029
    C44.781,39.116,44.879,39.619,44.751,40.09z"/>


</svg>

Altering the path values, you would be able to alter the shape of your teardrop design.

jbutler483
  • 24,074
  • 9
  • 92
  • 145
  • 10
    These all seem very... verbose. The js solution is particularly absurd. – egid Jun 09 '15 at 04:36
  • 4
    @egid: I did state in my answer that I did not create the js version. Also note the js version is allowing you to alter the shape at run time – jbutler483 Jun 11 '15 at 16:10
59

IMO this shape requires smooth curve-to beziers to ensure continuity of the curve.

The Drop in question :

For the drop in question,

  • smooth curves can't be used, as control points wont be of same length. But we still need to make the control points lie exactly opposite (180 deg) to the previous control points, to ensure full continuity of curve The picture given below illustrates this point :

enter image description here
Note: Red and blue curves are two different quadratic curves.

  • stroke-linejoin="miter", for the pointed top part.

  • AS this shape only uses successive c commands, we can omit it.

Here's the final snippet:

<svg height="300px" width="300px" viewBox="0 0 12 16">
  <path fill="#FFF" stroke="black" stroke-width="0.5" stroke-linejoin="miter" 
        d="M 6 1 c -2 3 -5 5 -5 9
           0 7 10 7 10 0 
           0 -4 -3 -6 -5 -9z" />
</svg>

TBH though, accepted answer's curves are not quite continuous.


For IE 5-8 (VML)

Only works in IE 5-8. VML uses different commands than SVG. Eg. it uses v for relative cubic beziers.

Note: This snippet won't run in IE 5-8 too. You need to create an html file and run it directly in the browser.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html xmlns:v="urn:schemas-microsoft-com:vml">
<head>
    <style> v\:* { behavior: url(#default#VML); }

    </style >
</head>
<body>
    <div style="width:240; height:320;">
        <v:shape coordorigin="0 0" coordsize="12 16" fillcolor="white" strokecolor="black" strokewidth="1"
            strokeweight="5" style="width:240; height:320" 
            path="M 6 1 v -2 3 -5 5 -5 9
           0 7 10 7 10 0 
           0 -4 -3 -6 -5 -9 x e">
        </v:shape>
    </div>
</body>
</html>
Max Payne
  • 2,423
  • 17
  • 32
  • "smooth curves can't be used, as control points wont be of same length." Doesn't "smooth" just mean that the tangent handles (control points) sit on a straight line? Why do they have to be of the same length? – Niccolo M. Jun 14 '15 at 13:26
  • 2
    @NiccoloM. In svg, smooth curve (T and S commands) means that the handle is symmetrically opposite, as well as **equal to previous handle's length**. In normal language, smooth curve might mean different handle lengths, but in svg, the length also is equal to previous curves handle :) – Max Payne Jun 14 '15 at 14:07
42

Or if your viewers' font supports it, use the Unicode characters

DROPLET: (&#128167;)

or

BLACK DROPLET: (&#127778;)

Scale accordingly!

Gnubie
  • 2,587
  • 4
  • 25
  • 38
  • You could use this with @font-face, but then you have a font file to keep in the right place and such. – user1934286 Jun 10 '15 at 05:55
  • 3
    SVG and a dedicated Unicode symbol like this is good. CSS for this is bad. PNG is okay, but not perfect since it is raster graphics. JPG is extremely bad, so bad that I would get nightmares if I saw it. – Andreas Rejbrand Jun 10 '15 at 12:34
  • @AndreasRejbrand a 20x20 PNG would scale worst than a 200x200 JPG. And, with the same size, an uncompressed JPG is equivalent to a PNG. They are both raster and they suffer from the same issues. – nico Jun 11 '15 at 17:18
  • @nico: In theory, yes, you can use JPG with zero compression. But the Internet is full of disasters like this one: https://en.wikipedia.org/wiki/Atomic_number#/media/File:Atomic_number_depiction.jpg – Andreas Rejbrand Jun 11 '15 at 23:39
28

I'd personally use an SVG for this. You can create SVGs in most vector graphics software. I'd recommend:

I have made one below that is a tracing of your shape in Illustrator.

<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="223.14px" height="319.008px" viewBox="0 0 223.14 319.008" enable-background="new 0 0 223.14 319.008" xml:space="preserve">
  <path fill="none" stroke="#000000" stroke-width="12" stroke-miterlimit="10" d="M111.57,13.291c0,0,57.179,86.984,72.719,108.819
     c30.359,42.66,41.005,114.694,1.626,154.074c-20.464,20.463-47.533,30.293-74.344,29.488h-0.002
     c-26.811,0.805-53.88-9.025-74.344-29.488C-2.154,236.804,8.492,164.77,38.851,122.11C54.391,100.275,111.57,13.291,111.57,13.291z" />
</svg>
Djave
  • 8,595
  • 8
  • 70
  • 124
28

HTML Canvas

This is an option uncovered in this thread so far. The commands used for Canvas drawings are very similar to SVG (and web-tiki deserves the credits for the base idea used in this answer).

The shape in question can be created either using canvas' own curve commands (Quadratic or Bezier) or the Path API. The answer contains examples for all three methods.

The browser support for Canvas is quite good.


Using Quadratic Curves

window.onload = function() {
  var canvas = document.getElementById('canvas');
  if (canvas.getContext) {
    var ctx = canvas.getContext('2d');

    ctx.beginPath();
    ctx.lineJoin = 'miter';
    ctx.moveTo(120, 20);
    ctx.quadraticCurveTo(117.5, 30, 148, 68);
    ctx.arc(120, 88, 34.5, 5.75, 3.66, false);
    ctx.quadraticCurveTo(117.5, 35, 120, 20);
    ctx.closePath();
    ctx.strokeStyle = '#000';
    ctx.lineWidth = 2;
    ctx.fillStyle = '#77CCEE'
    ctx.stroke();
    ctx.fill();
  }
}
canvas {
  margin: 50px;
  height: 100px;
  width: 200px;
  transform: scale(1.5);
}

body{
  background-image: radial-gradient(circle, #3F9CBA 0%, #153346 100%);
}
<canvas id='canvas'></canvas>

Below is an advanced version with gradient fill and shadows. I have also included a hover effect on the shape to illustrate one drawback of Canvas when compared to SVG. Canvas is raster (pixel) based and hence would look blurred/pixelated when scaled beyond a certain point. The only solution to that would be to repaint the shape on every browser resize which is an overhead.

window.onload = function() {
  var canvas = document.getElementById('canvas');
  if (canvas.getContext) {
    var ctx = canvas.getContext('2d');
    var lineargradient = ctx.createRadialGradient(135, 95, 1, 135, 95, 10);
    lineargradient.addColorStop(0, 'white');
    lineargradient.addColorStop(1, '#77CCEE');      
    ctx.beginPath();
    ctx.lineJoin = 'miter';
    ctx.moveTo(120, 20);
    ctx.quadraticCurveTo(117.5, 30, 148, 68);
    ctx.arc(120, 88, 34.5, 5.75, 3.66, false);
    ctx.quadraticCurveTo(117.5, 35, 120, 20);
    ctx.closePath();
    ctx.strokeStyle = '#333';
    ctx.lineWidth = 3;
    ctx.fillStyle = lineargradient;
    ctx.shadowOffsetX = 2;
    ctx.shadowOffsetY = 2;
    ctx.shadowBlur = 2;
    ctx.shadowColor = "rgba(50, 50, 50, 0.5)";      
    ctx.stroke();
    ctx.fill();
  }
}
canvas {
  margin: 50px;
  height: 100px;
  width: 200px;
  transform: scale(1.5);
}


/* Just for demo */

body{
  background-image: radial-gradient(circle, #3F9CBA 0%, #153346 100%);
}

canvas{
  transition: all 1s;
}

canvas:hover{
  transform: scale(2);
}
<canvas id='canvas'></canvas>

Using Bezier Curves

window.onload = function() {
  var canvas = document.getElementById('canvas');
  if (canvas.getContext) {
    var ctx = canvas.getContext('2d');
    var lineargradient = ctx.createRadialGradient(135, 95, 1, 135, 95, 10);
    lineargradient.addColorStop(0, 'white');
    lineargradient.addColorStop(1, '#77CCEE');
    ctx.beginPath();
    ctx.lineJoin = 'miter';
    ctx.arc(120, 88, 35, 5.74, 3.66, false);
    ctx.bezierCurveTo(100, 55, 122, 27.5, 120, 20);
    ctx.bezierCurveTo(122, 27.5, 121, 31.5, 150, 70);
    ctx.closePath();
    ctx.strokeStyle = 'rgba(109,195,250,0.2)';
    ctx.lineWidth = 1;
    ctx.fillStyle = lineargradient;
    ctx.shadowOffsetX = 2;
    ctx.shadowOffsetY = 2;
    ctx.shadowBlur = 2;
    ctx.shadowColor = "rgba(50, 50, 50, 0.5)";
    ctx.stroke();
    ctx.fill();
  }
}
canvas {
  margin: 75px;
  height: 300px;
  width: 300px;
  transform: scale(1.5);
}
body {
  background-image: radial-gradient(circle, #3F9CBA 0%, #153346 100%);
}
<canvas id='canvas' height='300' width='300'></canvas>

Using Path API

window.onload = function() {
  var canvas = document.getElementById('canvas');
  if (canvas.getContext) {
    var ctx = canvas.getContext('2d');

    ctx.lineJoin = 'miter';
    var p = new Path2D("M120 20 Q117.5 30 146 68 A34 34 0 1 1 92 68 Q117.5 35 120 20z");
    ctx.strokeStyle = '#000';
    ctx.lineWidth = 2;
    ctx.fillStyle = '#77CCEE'
    ctx.stroke(p);
    ctx.fill(p);
  }
}
canvas {
  margin: 50px;
  height: 100px;
  width: 200px;
  transform: scale(1.5);
}

body {
  background-image: radial-gradient(circle, #3F9CBA 0%, #153346 100%);
}
<canvas id='canvas'></canvas>

Note: As mentioned in my answere here, the Path API is not yet supported by IE and Safari.


Further reading:

Community
  • 1
  • 1
Harry
  • 87,580
  • 25
  • 202
  • 214
  • I dont see how svg is better option always. it certainly is better option in many cases like this one. But canvas has its own pros. Nice answer. from your answer i can make out that, it certainly is a lot easier to use SVG! – Max Payne Jun 11 '15 at 06:39
  • Yeah @TimKrul, its easier to write/use SVG. Canvas has its own pros but from whatever I read it is not too advantageous when using for simple logo like shapes primarily because it is raster based unlike SVG. – Harry Jun 11 '15 at 06:46
25

I also found this on Codepen made by user Ana Tudor using CSS and the box-shadow style and parametric equations. Very simple, very little code. And many browsers support the CSS3 Box-shadow style:

body {
  background-color: black;
}
.tear {
  position: absolute;
  top: 50%;
  left: 50%;
  margin: -0.125em;
  width: 0.25em;
  height: 0.25em;
  border-radius: 50%;
  box-shadow: 0em -5em red, 0.00118em -4.97592em #ff1800, 0.00937em -4.90393em #ff3000, 0.03125em -4.7847em #ff4800, 0.07283em -4.6194em #ff6000, 0.13915em -4.40961em #ff7800, 0.23408em -4.15735em #ff8f00, 0.36em -3.86505em #ffa700, 0.51777em -3.53553em #ffbf00, 0.70654em -3.17197em gold, 0.92382em -2.77785em #ffef00, 1.16547em -2.35698em #f7ff00, 1.42582em -1.91342em #dfff00, 1.69789em -1.45142em #c7ff00, 1.97361em -0.97545em #afff00, 2.2441em -0.49009em #97ff00, 2.5em 0.0em #80ff00, 2.73182em 0.49009em #68ff00, 2.93032em 0.97545em #50ff00, 3.08681em 1.45142em #38ff00, 3.19358em 1.91342em #20ff00, 3.24414em 2.35698em #08ff00, 3.23352em 2.77785em #00ff10, 3.15851em 3.17197em #00ff28, 3.01777em 3.53553em #00ff40, 2.81196em 3.86505em #00ff58, 2.54377em 4.15735em #00ff70, 2.21783em 4.40961em #00ff87, 1.84059em 4.6194em #00ff9f, 1.42017em 4.7847em #00ffb7, 0.96608em 4.90393em #00ffcf, 0.48891em 4.97592em #00ffe7, 0.0em 5em cyan, -0.48891em 4.97592em #00e7ff, -0.96608em 4.90393em #00cfff, -1.42017em 4.7847em #00b7ff, -1.84059em 4.6194em #009fff, -2.21783em 4.40961em #0087ff, -2.54377em 4.15735em #0070ff, -2.81196em 3.86505em #0058ff, -3.01777em 3.53553em #0040ff, -3.15851em 3.17197em #0028ff, -3.23352em 2.77785em #0010ff, -3.24414em 2.35698em #0800ff, -3.19358em 1.91342em #2000ff, -3.08681em 1.45142em #3800ff, -2.93032em 0.97545em #5000ff, -2.73182em 0.49009em #6800ff, -2.5em 0.0em #7f00ff, -2.2441em -0.49009em #9700ff, -1.97361em -0.97545em #af00ff, -1.69789em -1.45142em #c700ff, -1.42582em -1.91342em #df00ff, -1.16547em -2.35698em #f700ff, -0.92382em -2.77785em #ff00ef, -0.70654em -3.17197em #ff00d7, -0.51777em -3.53553em #ff00bf, -0.36em -3.86505em #ff00a7, -0.23408em -4.15735em #ff008f, -0.13915em -4.40961em #ff0078, -0.07283em -4.6194em #ff0060, -0.03125em -4.7847em #ff0048, -0.00937em -4.90393em #ff0030, -0.00118em -4.97592em #ff0018;
}
<div class="tear"></div>
programking
  • 1,376
  • 1
  • 17
  • 32
20

CSS Version

As there are a fair few answers here I thought why not add to it with another method. This is using both HTML and CSS to create the teardrop.

This will allow you to change the colour of the border and background of the teardrop and also re-size the top part of it.

Using a single div we can create a circle with border and border-radius. Then using pseudo elements (:before & :after) we create a CSS triangle more here, this will act as the tip of the teardrop. Using the :before as the border we place :after on top with a smaller size and the desired background colour.

div {
  width: 100px;
  height: 100px;
  border-radius: 50%;
  border: 4px solid;
  margin: 80px auto;
  position: relative;
}
div:before,
div:after {
  content: "";
  display: block;
  position: absolute;
  width: 0;
  height: 0;
}
div:before {
  border-left: 50px solid transparent;
  border-right: 50px solid transparent;
  border-bottom: 104px solid black;
  top: -75px;
}
div:after {
  border-left: 46px solid transparent;
  border-right: 46px solid transparent;
  border-bottom: 96px solid #fff;
  top: -66px;
  left: 0;
  right: 0;
  margin: auto;
  z-index: 1;
}
<div></div>

Here is a demo of the teardrop with a background colour

div {
  width: 100px;
  height: 100px;
  border-radius: 50%;
  border: 4px solid;
  background: red;
  margin: 80px;
  position: relative;
}
div:before,
div:after {
  content: "";
  display: block;
  position: absolute;
  width: 0;
  height: 0;
}
div:before {
  border-left: 50px solid transparent;
  border-right: 50px solid transparent;
  border-bottom: 100px solid black;
  top: -70px;
}
div:after {
  border-left: 46px solid transparent;
  border-right: 46px solid transparent;
  border-bottom: 96px solid red;
  top: -66px;
  left: 0;
  right: 0;
  margin: auto;
  z-index: 1;
}
<div></div>

It is as simple as putting a background colour onto the div and changing :after bottom-border colour to the same. To change the border you will need to change div border colour and :before background colour too.

Ruddy
  • 9,795
  • 5
  • 45
  • 66
  • Ruddy the pen is missing a background color value. – Persijn Jun 10 '15 at 08:09
  • 2
    @Persijn Harry said something in chat and I was looking at it on that pen forgetting it auto saves every now and then. Haha, I will put it back the way it was. Done. – Ruddy Jun 10 '15 at 08:10
17

It is quite easy to do this with SVG by just using an image conversion resource such as http://image.online-convert.com/convert-to-svg, which was used to create the following:

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
 "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
 width="213.000000pt" height="300.000000pt" viewBox="0 0 213.000000 300.000000"
 preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.12, written by Peter Selinger 2001-2015
</metadata>
<g transform="translate(0.000000,300.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M1035 2944 c-143 -250 -231 -380 -508 -752 -347 -465 -432 -616 -493
-882 -91 -394 10 -753 285 -1013 508 -479 1334 -361 1677 240 126 221 165 494
105 726 -66 254 -178 452 -609 1076 -96 140 -226 335 -288 435 -155 249 -135
229 -169 170z m85 -212 c40 -69 192 -298 543 -818 268 -396 354 -593 364 -835
12 -281 -82 -509 -296 -714 -103 -99 -236 -173 -396 -221 -82 -25 -105 -27
-260 -28 -148 -1 -181 2 -255 22 -348 96 -611 357 -691 689 -41 167 -25 392
41 587 62 185 154 334 444 716 177 235 320 444 402 592 27 49 51 88 54 88 3 0
25 -35 50 -78z"/>
</g>
</svg>
Anonymous
  • 11,748
  • 6
  • 35
  • 57
  • @Persijn Yes, read the top: "which was used to create the following" – Anonymous Jun 08 '15 at 14:15
  • 9
    @Persijn why do you keep asking copied from some editor? Svg is svg , you can use whichever tool you want to create one. – Abhinav Gauniyal Jun 08 '15 at 15:52
  • @AbhinavGauniyal this is a coding site, so I prefer solutions made by hand/coded. – Persijn Jun 08 '15 at 15:56
  • 2
    @AbhinavGauniyal: 'editors' as you like to call them, add extra 'fluff' to declarations. For example, `300.000000pt` and metadata that's not *really* needed – jbutler483 Jun 08 '15 at 16:06
  • 12
    @Persijn what do you mean by hand/coded. Someone just made/hand/coded illustrator to just ease their coding tasks, and it still produces the same thing. Then again when you're using svg in browser, why don't you yourself hand/code it using assembly language? and why stop at assembly , hand/code it using wires or just draw it yourself. This is not a reason that I was expecting. – Abhinav Gauniyal Jun 08 '15 at 16:36
  • 2
    @jbutler483 yes and they can be trimmed out. There are many tools that do that for you , here's one for you https://github.com/svg/svgo – Abhinav Gauniyal Jun 08 '15 at 16:38
  • @AbhinavGauniyal Join me in chat: http://chat.stackoverflow.com/rooms/69317/css-shapes-and-designs – Persijn Jun 08 '15 at 18:20
  • 6
    @persijn this answer provides the svg paths needed. i really have no clue what your objection is. – user428517 Jun 08 '15 at 19:40
9

Here are four progressively simpler SVG teardrop shapes:

enter image description here

<svg viewbox="-20 -20 180 180">
  <g stroke="black" fill="none">
    
    <path transform="translate(0)"
     d="M   0  0
        C   0 10  10 17  10 27
        C  10 40 -10 40 -10 27
        C -10 17   0 10   0  0
        Z"/>

    <path transform="translate(40)"
     d="M   0  0
        C   0 16  15 25   5 34
        Q   0 38         -5 34
        C -15 25   0 16   0  0
        Z"/>
    
    <path transform="translate(80)"
     d="M   0  0
        C   0 10  18 36   0 36
        S          0 10   0  0
        Z"/>

    <path transform="translate(120)"
     d="M   0  0
        Q  18 36   0 36
        T          0  0
        Z"/>
    
    
    
    
    <g stroke-width="0.25" stroke="red">
      <g transform="translate(0)">
        <ellipse rx="1" ry="1" cx="0"   cy="0" />
        <ellipse rx="1" ry="1" cx="0"   cy="10"/>
        <ellipse rx="1" ry="1" cx="10"  cy="17"/>
        <ellipse rx="1" ry="1" cx="10"  cy="27"/>
        <ellipse rx="1" ry="1" cx="10"  cy="40"/>
        <ellipse rx="1" ry="1" cx="-10" cy="40"/>
        <ellipse rx="1" ry="1" cx="-10" cy="27"/>
        <ellipse rx="1" ry="1" cx="-10" cy="17"/>
        <line x1="0"   y1="0"  x2="0"   y2="10"/>
        <line x1="10"  y1="17" x2="10"  y2="40"/>
        <line x1="-10" y1="40" x2="-10" y2="17"/>
      </g>
      <g transform="translate(40)">
        <ellipse rx="1" ry="1" cx="0"   cy="0" />
        <ellipse rx="1" ry="1" cx="0"   cy="16"/>
        <ellipse rx="1" ry="1" cx="15"  cy="25"/>
        <ellipse rx="1" ry="1" cx="5"   cy="34"/>
        <ellipse rx="1" ry="1" cx="0"   cy="38"/>
        <ellipse rx="1" ry="1" cx="-5"  cy="34"/>
        <ellipse rx="1" ry="1" cx="-15" cy="25"/>
        <line x1="0"  y1="0"  x2="0"   y2="16"/>
        <line x1="15" y1="25" x2="0"   y2="38"/>
        <line x1="0"  y1="38" x2="-15" y2="25"/>
      </g>
      <g transform="translate(80)">
        <ellipse rx="1" ry="1" cx="0"   cy="0" />
        <ellipse rx="1" ry="1" cx="0"   cy="10"/>
        <ellipse rx="1" ry="1" cx="18"  cy="36"/>
        <ellipse rx="1" ry="1" cx="0"   cy="36"/>
        <ellipse rx="1" ry="1" cx="-18" cy="36" stroke="gray"/>
        <line x1="0"  y1="0"  x2="0"   y2="10"/>
        <line x1="18" y1="36" x2="0"   y2="36"/>
        <line x1="0"  y1="36" x2="-18" y2="36" stroke="gray" stroke-dasharray="0.5"/>
      </g>
      <g transform="translate(120)">
        <ellipse rx="1" ry="1" cx="0"   cy="0" />
        <ellipse rx="1" ry="1" cx="18"  cy="36"/>
        <ellipse rx="1" ry="1" cx="0"   cy="36"/>
        <ellipse rx="1" ry="1" cx="-18" cy="36" stroke="gray"/>
        <line x1="18" y1="36" x2="0"   y2="36"/>
        <line x1="0"  y1="36" x2="-18" y2="36" stroke="gray" stroke-dasharray="0.5"/>
      </g>
    </g>    
  </g>
  <g font-size="6" transform="translate(-1.5,-4)">
    <text x="-10" y="-8"># of unique points:</text>
    <text transform="translate(  0)">8</text>
    <text transform="translate( 40)">7</text>
    <text transform="translate( 80)">4</text>
    <text transform="translate(120)">3</text>
  </g>
</svg>
Andrew Willems
  • 11,880
  • 10
  • 53
  • 70