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
https://i.stack.imgur.com/3dFQ5.png
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%; }
That turned out really screwed.
And then I tried with 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?
[srcset]
or the <picture>
element. Better yet, just link to an svg image: <img src="tear.svg" alt="Teardrop"/>
picture
element? No support for IE, no support for early versions of Chrome, no support for safari. Should i continue?
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.
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:
2 quadratic bezier curve commands for the 2 top curves (lines beginning with Q)
1 arc command for the big bottom one (line beginning with A)
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.
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; }
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; }
Your main issue with your CSS code was:
You used a different height than width 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; }
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:
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'); }
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%); }
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; }
Altering the path
values, you would be able to alter the shape of your teardrop design.
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 :
https://i.stack.imgur.com/37Xqa.png
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:
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>
Or if your viewers' font supports it, use the Unicode characters
DROPLET: 💧 (💧)
or
BLACK DROPLET: 🌢 (🌢)
Scale accordingly!
I'd personally use an SVG for this. You can create SVGs in most vector graphics software. I'd recommend:
Inkscape
Sketch
Adobe Illustrator
I have made one below that is a tracing of your shape in Illustrator.
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%); }
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); }
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%); }
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%); }
Note: As mentioned in my answere here, the Path API is not yet supported by IE and Safari.
Further reading:
7 Reasons to Consider SVGs Instead of Canvas
HTML5 Canvas vs. SVG: Choose the Best Tool for the Job
What is the difference between SVG and HTML5 Canvas?
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; }
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; }
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; }
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.
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:
300.000000pt
and metadata that's not really needed
Here are four progressively simpler SVG teardrop shapes:
https://i.stack.imgur.com/A63rB.png
Success story sharing
<svg width="100%" height="100%" viewBox="0 0 8 8" /> <path d="M4 1L3 4A1.2 2 0 1 0 5 4"/> </svg>
... this will fill the area you give it, so it may make "fat" or "skinny" raindrops ... change to fixed height/width if necessaryd="..."
attribute is that you don't have the double curve at the top of the drop anymore.<img />
in the hypertext document.