CSS - how to create circle pie canvas like this? - html

I really like this element,
but how to create it? I am not sure what's the correct designation of the element...
Thank you very much.

This effect can be achieved by layering a couple arc()s:
// bright blue full circle
d.beginPath();
d.arc(50, 50, 50, 0, 2 * Math.PI, false);
d.fillStyle = "#aaeeff";
d.fill();
// dark blue percentage circle
d.beginPath();
d.moveTo(50, 50);
d.arc(50, 50, 50, -0.5 * Math.PI, 0.78 * 2 * Math.PI - 0.5 * Math.PI, false);
d.fillStyle = "#00aaff";
d.fill();
// white inner filler
d.beginPath();
d.moveTo(50, 50);
d.arc(50, 50, 25, 0, 2 * Math.PI, false);
d.fillStyle = "#ffffff";
d.fill();
and finally rendering the text:
d.moveTo(50, 50);
d.fillStyle = "#606060";
d.font = "12pt sans-serif";
d.fillText("78%", 36, 56);
Fiddle: http://jsfiddle.net/j6NVg/

Instead of using the <canvas> element, I have chosen to construct the pie chart relying on CSS and JS entirely. The HTML markup is as follow:
<div class="pie" data-percent="78">
<div class="left">
<span></span>
</div>
<div class="right">
<span></span>
</div>
</div>
The CSS is as follow. The trick is to split the circle into two halves (the nested .left and .right elements). The halves will have their overflowing content hidden, and contain nested <span> that we will manipulate with JS for rotation later. Add vendor prefixes when appropriate :)
.pie {
background-color: #eee;
border-radius: 50%;
width: 200px;
height: 200px;
overflow: hidden;
position: relative;
}
.pie > div {
float: left;
width: 50%;
height: 100%;
position: relative;
overflow: hidden;
}
.pie span {
background-color: #4a7298;
display: block;
width: 100%;
height: 100%;
}
.pie .left span {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
-webkit-transform-origin: 100% 50%;
transform-origin: 100% 50%;
}
.pie .right span {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
-webkit-transform-origin: 0% 50%;
transform-origin: 0% 50%;
}
.pie:before,
.pie:after {
border-radius: 50%;
display: block;
position: absolute;
top: 50%;
left: 50%;
-webkit-transform: translateX(-50%) translateY(-50%);
transform: translateX(-50%) translateY(-50%);
}
.pie:before {
background-color: #fff;
content: "";
width: 75%;
height: 75%;
z-index: 100;
}
.pie:after {
content: attr(data-percent) "%";
z-index: 200;
text-align: center;
}
I have used the following with jQuery:
$(function() {
$(".pie").each(function() {
var percent = $(this).data("percent").slice(0,-1), // Removes '%'
$left = $(this).find(".left span"),
$right = $(this).find(".right span"),
deg;
if(percent<=50) {
// Hide left
$left.hide();
// Adjust right
deg = 180 - (percent/100*360)
$right.css({
"transform": "rotateZ(-"+deg+"deg)"
});
} else {
// Adjust left
deg = 180 - ((percent-50)/100*360)
$left.css({
"transform": "rotateZ(-"+deg+"deg)"
});
}
});
});
Here is the fiddle: http://jsfiddle.net/Aw5Rf/7/

Check the below links for more info (not an exact one.But you can get some idea).
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<title>Canvas Test</title>
</head>
<body>
<section>
<div>
<canvas id="canvas" width="400" height="300">
This text is displayed if your browser does not support HTML5 Canvas.
</canvas>
</div>
<script type="text/javascript">
var myColor = ["#ECD078","#D95B43","#C02942","#542437","#53777A"];
var myData = [10,30,20,60,40];
function getTotal(){
var myTotal = 0;
for (var j = 0; j < myData.length; j++) {
myTotal += (typeof myData[j] == 'number') ? myData[j] : 0;
}
return myTotal;
}
function plotData() {
var canvas;
var ctx;
var lastend = 0;
var myTotal = getTotal();
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < myData.length; i++) {
ctx.fillStyle = myColor[i];
ctx.beginPath();
ctx.moveTo(200,150);
ctx.arc(200,150,150,lastend,lastend+
(Math.PI*2*(myData[i]/myTotal)),false);
ctx.lineTo(200,150);
ctx.fill();
lastend += Math.PI*2*(myData[i]/myTotal);
}
}
plotData();
</script>
</section>
</body>
</html>
For more info :Graphing Data in the HTML5 Canvas Element Simple Pie Charts
Another Link : Pure CSS3 Pie Charts effect
This is an online demo: http://jsbin.com/uFaSOwO/1/

First of all what you need can be done exactly using jQuery knob plugin. Still interested in a CSS Solution, than here's what I have done
<div class="load_me"></div>
.load_me {
margin: 100px;
height: 50px;
width: 50px;
border: 5px solid #f00;
border-radius: 50%;
border-top-color: transparent;
}
Demo
Animating the Knob Credits
If you want to prevent the mouse alteration, you can simply add readOnly
$this.knob({
readOnly: true
});
Demo

FIDDLE with ANIMATION
Here's my approach:
var ctx = canvas.getContext('2d');
/*
* in canvas, 0 degrees angle is on the right edge of a circle,
* while we want to start at the top edge of the circle.
* We'll use this variable to compensate the difference.
*/
var relativeAngle = 270;
function drawCanvas() {
ctx.clearRect(0, 0, 90, 90);
//light blue circle
ctx.lineWidth = 20;
ctx.strokeStyle = '#D8E8F7';
ctx.beginPath();
ctx.arc(45, 45, 35, 0, 2*Math.PI);
ctx.stroke();
//dark blue circle
ctx.strokeStyle = '#66ADF4';
ctx.beginPath();
//notice the angle conversion from degrees to radians in the 5th argument
ctx.arc(45, 45, 35, 1.5*Math.PI, ((angle + relativeAngle) / 180) * Math.PI);
ctx.stroke();
//text
ctx.textBaseline = 'middle';
ctx.textAlign = 'center';
ctx.fillStyle = '#666';
ctx.font = 'bold 14px serif';
// angle conversion to percentage value
ctx.fillText(parseInt(100 * angle / 360).toString() + '%', 45, 45);
}
var angle;
function timeout() {
angle = parseInt(360 * percent / 100);
drawCanvas();
if (angle > 360) {
document.getElementById('run').disabled = false;
percent = 0;
return;
}
percent++;
setTimeout(timeout, 10);
};
var percent = 0;
/* START the ANIMATION */
timeout();
At the bottom of the code you'll find a self evaluating function timeout which calls the drawCanvas function every 10 miliseconds and increments the blue circle angle. I hope everything is clear here. If not, feel free to ask!
Enjoy it!

Related

How to use WebGL to render a button background?

Let's say that I have a GL shader I'd want to use as an HTML button background (it's a pretty animated gradient). How can I achieve that?
I can think of setting the background to a texture and rendering the shader to this texture. Is that fundamentally possible?
Using a canvas inside a button is the most logical way to go, note that this is just an example and not a shader, but we use the canvas to render WebGL so it can be slightly different for your case.
var c = document.getElementsByTagName("canvas")[0];
var ctx = c.getContext("2d");
// Create gradient
var grd = ctx.createLinearGradient(0, 0, 200, 0);
grd.addColorStop(0, "red");
grd.addColorStop(1, "green");
// Fill with gradient
ctx.fillStyle = grd;
ctx.fillRect(0, 0, 400, 200);
button {
position: relative;
background-color: transparent;
border: none;
outline: none;
padding: 8px;
}
button > div {
position: relative;
z-index: 1;
}
canvas {
position: absolute;
top: 0;
right: 0;
width: 100%;
height: 100%;
z-index: -1;
pointer-events: none;
}
<button>
<canvas></canvas>
<div>Fancy Button</div>
</button>

Spinning a wheel of fortune with a predetermined rotation value

My goal is to (upon clicking a button) spin a wheel multiple times, then end on a specific rotation value (0...360) while easing out the animation. I can currently 'smoothly' spin to a specific point on the wheel without any full revolutions. My problem is trying to spin the wheel multiple times and then landing on a specific point to make it look more realistic. Is this achievable with the CSS animaton property or anything else?
Here is my code...
const wheelEl = document.getElementById("wheel");
const sliceSize = 360 / 3;
function spinWheel(index) {
// Reset animation
wheelEl.style.transition = "none";
wheelEl.style.transform = "rotate(0deg)";
// Play animation on the next frame
setTimeout(() => {
wheelEl.style.transition = "all ease 1s";
// Target rotation margin
let rotMin = (sliceSize * (index))+(sliceSize/15);
let rotMax = (sliceSize * (index + 1))-(sliceSize/15);
// Target rotation
let rot = Math.floor(Math.random() * (rotMax - rotMin + 1) + rotMin);
wheelEl.style.transform = `rotate(-${rot}deg)`;
}, 0);
}
#container {
position: absolute;
text-align: center;
}
#arrow {
position: absolute;
top: -5px;
left: 50%;
transform: translateX(-50%);
border-top: 10px solid black;
border-left: 8px solid transparent;
border-right: 8px solid transparent;
z-index: 1;
}
#wheel {
width: 100px;
height: 100px;
border-radius: 50%;
background-image: conic-gradient(lightpink 0 120deg, lightblue 0 240deg, lightsalmon 0 360deg);
}
<div id="container">
<div id="arrow"></div>
<div id="wheel"></div>
<br />
<button onclick="spinWheel(2)">Spin wheel</button>
</div>
You can stick to using transform.
This snippet does two things: adds a random (within bounds) number of 360 degrees to the rotation value and sets the easing function to ease-out so that the rotation slows down towards the end only.
So that the effect can be seen, the time of the full rotation is set to 5 seconds but of course alter this to whatever you want. You could for example make that random within some bounds too if desired.
You could also play with the Beziere function that represents ease-out if say you wanted a longer stretch of it seeming to slow down.
const wheelEl = document.getElementById("wheel");
const sliceSize = 360 / 3;
function spinWheel(index) {
// Reset animation
wheelEl.style.transition = "none";
wheelEl.style.transform = "rotate(0deg)";
// Play animation on the next frame
setTimeout(() => {
wheelEl.style.transition = "all ease-out 5s";
// Target rotation margin
const rotMin = (sliceSize * (index)) + (sliceSize / 15);
const rotMax = (sliceSize * (index + 1)) - (sliceSize / 15);
// Target rotation
const fullRots = Math.floor(Math.random() * 5) + 5; // minimum 5 rotations max 9
const rot = (fullRots * 360) + Math.floor(Math.random() * (rotMax - rotMin + 1) + rotMin);
wheelEl.style.transform = `rotate(-${rot}deg)`;
}, 0);
}
#container {
position: absolute;
text-align: center;
}
#arrow {
position: absolute;
top: -5px;
left: 50%;
transform: translateX(-50%);
border-top: 10px solid black;
border-left: 8px solid transparent;
border-right: 8px solid transparent;
z-index: 1;
}
#wheel {
width: 100px;
height: 100px;
border-radius: 50%;
background-image: conic-gradient(lightpink 0 120deg, lightblue 0 240deg, lightsalmon 0 360deg);
}
<div id="container">
<div id="arrow"></div>
<div id="wheel"></div>
<br />
<button onclick="spinWheel(2)">Spin wheel</button>
</div>

CANVAS - line with 100% of width

I would like to draw a line using CANVAS with 100% width acrossing through the entire screen similar to a css in the 'image-background' of the body like this example below:
Ex:
Made with CSS
body {
background-image: url(horizontal-line.png);
background-repeat: repeat-x;
background-position: 0px 10%;
}
made with CANVAS
???
How can I solve it? Thank you
Updated demo:
https://jsfiddle.net/mulperi/xnob50yd/1/
A full window sized canvas and 100% stroke in the middle of the screen
Here are the css and js parts:
<style>
canvas {
padding: 0;
margin: auto;
width: 100%;
height: 100%;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
</style>
<canvas id="canvas"></canvas>
<script>
const c = document.getElementById("canvas");
const ctx = c.getContext("2d");
c.width = window.innerWidth;
c.height = window.innerHeight;
ctx.fillStyle = "red";
ctx.fillRect(0, window.innerHeight / 2, window.innerWidth, 10);
ctx.fill();
window.onresize = function() {
ctx.width = window.innerWidth;
ctx.height = window.innerHeight;
}
</script>
With window.onresize you make sure that canvas changes size dynamically with the browser window.
Canvas &lthr&gt line
Used javascript to find the width of the screen. document.documentElement.clientWidth
Then used that to set the size of the canvas element. canvas.width = screenWidth;
Then draw a rect width size 2px height and its width equal to screenWidth.
ctx.fillRect(0, 10, screenWidth, 2)
What is the 10 for? In this example i set the canvas to 20 height. So the middle of the canvas is 10.
document.addEventListener("DOMContentLoaded", function() {
draw();
});
function draw() {
var canvas = document.getElementById('demo');
//Set the canvas to viewport size
var screenWidth = document.documentElement.clientWidth;
canvas.width = screenWidth;
if (canvas.getContext) {
var ctx = canvas.getContext('2d');
ctx.fillStyle = 'rgb(255, 100, 0)';
ctx.fillRect(0, 10, screenWidth, 2);
} else {
convas.addChild("No canvas here");
}
}
body {
margin: 0;
}
/*to make the canvas visible*/
#demo {
border-top: 1px solid black;
border-bottom: 1px solid black;
}
<p>Canvas</p>
<canvas id="demo" width="" height="20"></canvas>

Creating a Snowflake shape that contains text in shape

I'm trying to create a snowflake on my webpage for the winter season.
The first thing I tried was creating it with SVG:
<h3>Koch Snowflake Frac</h3>
<svg viewBox="-5 -5 110 110" xmlns="http://www.w3.org/2000/svg">
<polyline stroke="cornflowerblue" stroke-width="2" fill="rgba(255,255,255,0.5)" points="55 5,
60 10,
65 10,
65 15,
70 20,
75 20,
80 15,
85 20,
90 20,
85 25,
90 30,
80 30,
75 35,
80 40,
90 40,
85 45,
90 50,
85 50,
80 55,
75 50,
70 50,
65 55,
65 60,
60 60,
55 65,
50 60,
45 60,
45 55,
40 50,
35 50,
30 55,
25 50,
20 50,
25 45,
20 40,
30 40,
35 35,
30 30,
20 30,
25 25,
20 20,
25 20,
30 15,
35 20,
40 20,
45 15,
45 10,
50 10,
55 5" />
<foreignObject x="0" y="0" requiredExtensions="http://www.w3.org/1999/xhtml">
<body xmlns="http://www.w3.org/1999/xhtml">
<p>Here is a paragraph that requires word wrap</p>
</body>
</foreignObject>
</svg>
I could not get the <foreignObject> to work and even if I did it's not supported in IE browsers.
There is no need to support old IE browsers, but I would like support in at least one of them.
Also minor detail at the top, the shape is not closed.
Then I tried creating a snowflake in it with CSS:
.snowflake {
position: absolute;
width: 200px;
display: inline-block;
border-bottom: 10px solid cornflowerblue;
top: 200px;
left: 100px;
background-color: white;
}
.snowflake:before {
position: absolute;
content: "";
display: inline-block;
width: 50px;
border-bottom: 10px solid cornflowerblue;
transform: rotate(45deg);
top: -20px;
}
.snowflake:after {
position: absolute;
content: "";
display: inline-block;
width: 50px;
border-bottom: 10px solid cornflowerblue;
transform: rotate(-45deg);
top: 20px;
}
.smallbranch {
position: absolute;
right: 0px;
display: inline-block;
width: 50px;
border-bottom: 10px solid cornflowerblue;
transform: rotate(45deg);
top: 17px;
box-shadow: -130px -5px 0px 0px cornflowerblue;
}
.smallbranch:before {
position: absolute;
content: "";
display: inline-block;
width: 50px;
border-bottom: 10px solid cornflowerblue;
transform: rotate(90deg);
top: -22px;
left: -22px;
box-shadow: 130px -5px 0px 0px cornflowerblue;
}
.circle {
position: absolute;
left: 50%;
width: 50px;
height: 50px;
border-radius: 50%;
border: 5px solid cornflowerblue;
background-color: white;
transform: translate(-50%, -50%);
}
.circle:before {
position: absolute;
content: "";
display: inline-block;
width: 50px;
border-bottom: 10px solid cornflowerblue;
transform: rotate(90deg);
top: -52px;
left: 20px;
transform: rotate(-45deg);
}
.circle:after {
position: absolute;
content: "";
display: inline-block;
width: 50px;
border-bottom: 10px solid cornflowerblue;
transform: rotate(90deg);
top: 102px;
left: 20px;
transform: rotate(45deg);
}
.branch {
position: absolute;
display: inline-block;
height: 200px;
border-right: 10px solid cornflowerblue;
left: 50%;
top: -100px;
}
<div class="snowflake">
<div class="branch"></div>
<div class="smallbranch"></div>
<div class="circle">Text in here</div>
</div>
This was my best attempt with CSS.
Now here the text is displayed but its not on one line. My idea is to use this in a logo or for a button on the webpage. So I don't think I will need line wrap functionality on the shape, but it would be a plus if it had.
The shape I would like created:
TL;DR
What I would like is a snowflake with text in the middle of the shape.
I'm asking for a solution where the text could be any length and still be inside the shape.
You don't have to create a shape that's exactly the same as what I have tried as long as the shape is a snowflake with text in the center it's ok. I don't know how long the text will be so the shape has to contain the text.
Play with this demo
This is actually a quite interesting question, and coming up with an answer was not easy.
The question asks to make a shape(in this case a snowflake), that would scale to fit the text inside of it. My first advice is to use an image, not try and create the shape with CSS. Images are much easier to make scale, and can have more detail then a CSS shape.
So, lets show how we can accomplish this.
First of all, since you want the element to scale to fit the font, we need to make the element display:inline-block. This will make it only be as wide as it's content, unlike block which would make it as wide as it's parent, and still be able to set the height(which you cannot do with inline).
Next, we need to make the element with a height the same as the width. Luckily, there is a trick in CSS that allows you to do just that. The padding of an element is calculated based on it's width, so if you set the padding-bottom(or padding-top) to 100%, it will have the same width as height.(See this excellent SO answer for further info).
After this, it is just a matter of centering the text inside the snowflake, which may take a little playing with the values to fit your font-family.
If you want the jsfiddle with code:
JSFiddle Demo
Full-Screen JSFiddle Demo
Tested in Chrome, FireFox, IE, and Safari. Minor adjustments may be needed for certain font-family's
.snowflake{
display:inline-block;
background:url("http://i.imgur.com/4M9MH1Q.png") scroll no-repeat center/contain;
}
/*This is for setting the height the same as the width, a 1:1 ratio. more info http://www.mademyday.de/css-height-equals-width-with-pure-css.html#outer_wrap */
.snowflake:after{
content: "";
display: block;
padding-top: 100%;
}
.snowflake span{
display:inline-block;
-webkit-transform: translateY(110%);
-ms-transform: translateY(110%);
transform: translateY(110%);
width:100%;
text-align:center;
padding-top:20%;
}
/*This part is ugly, but it is required to work in chrome or IE, you may have to change the char for different font types*/
.snowflake span:before, .snowflake span:after{
content:"aaa";
visibility:hidden;
opacity:0;
}
Font-size 12pt:
<div class="snowflake" style="font-size:12pt;">
<span>It's Snowing!</span>
</div>
Font-size 24pt:
<div class="snowflake" style="font-size:24pt;">
<span>It's Snowing!</span>
</div>
Font-size 48pt:
<div class="snowflake" style="font-size:48pt;">
<span>It's Snowing!</span>
</div>
EDIT: This solution is prettier, but doesn't work in Chrome or IE
.snowflake{
display:inline-block;
background:url("http://i.imgur.com/4M9MH1Q.png") scroll no-repeat center/contain;
}
/*This is for setting the height the same as the width, a 1:1 ratio. more info http://www.mademyday.de/css-height-equals-width-with-pure-css.html#outer_wrap */
.snowflake:after{
content: "";
display: block;
padding-top: 100%;
}
.snowflake span{
display:inline-block;
transform: translateY(90%);
padding:20%;
}
Font-size 12pt:
<div class="snowflake" style="font-size:12pt;">
<span>It's Snowing!</span>
</div>
Font-size 24pt:
<div class="snowflake" style="font-size:24pt;">
<span>It's Snowing!</span>
</div>
Font-size 48pt:
<div class="snowflake" style="font-size:48pt;">
<span>It's Snowing!</span>
</div>
The main condition for this to work is:
.snowflake must be display:inline-block;
Full-Screen JSFiddle Demo
My solution uses part SVG, part HTML/CSS to create the effect required.
I have used the calc() CSS3 function along with viewport based width/height units which then give it the responsiveness required.
It looks much better when the element is above 200px wide.
calc() Browser Support
Viewport Units Browser Support
The Code
.container {
position: relative;
overflow: auto;
display: block;
width: 50vw;
height: 50vw;
}
.container svg {
width: 100%;
height: 100%;
position: absolute;
}
p {
position: absolute;
z-index: 100;
background: white;
left: 17.5vw;
top: 17.5vw;
margin: 0;
padding: 10px 0;
width: 10vw;
text-align: center;
font-size: 2vw;
border: 20px solid black;
}
#media screen and (min-width: 920px) {
p {
font-size: 3vw;
width: 12.5vw;
}
}
<div class="container">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve" width="50%">
<polygon id="christmas-snowflake-icon" points="441.535,346.644 373.955,307.624 438.697,290.354 431.342,262.782 338.967,287.424
284.535,255.999 339.49,224.271 431.299,249.242 438.787,221.705 374.311,204.168 441.535,165.356 427.266,140.644 359.686,179.66
377.1,114.956 349.545,107.541 324.697,199.861 270.27,231.285 270.27,167.831 337.797,100.809 317.695,80.554 270.27,127.624
270.27,50 241.732,50 241.732,128.036 194.404,80.604 174.203,100.76 241.732,168.438 241.732,231.286 186.779,199.558
162.498,107.565 134.906,114.847 151.957,179.455 84.732,140.644 70.465,165.356 138.045,204.373 73.303,221.645 80.66,249.218
173.035,224.574 227.465,255.999 172.51,287.727 80.701,262.758 73.211,290.293 137.688,307.832 70.465,346.644 84.732,371.356
152.312,332.337 134.898,397.042 162.457,404.459 187.303,312.137 241.732,280.711 241.732,344.169 174.203,411.191
194.307,431.446 241.732,384.376 241.732,462 270.27,462 270.27,383.964 317.598,431.396 337.797,411.24 270.27,343.562
270.27,280.712 325.223,312.439 349.502,404.435 377.094,397.15 360.043,332.545 427.268,371.356 "></polygon>
</svg>
<p>This is some text Now</p>
</div>
CodePen
Using svg i have managed to make an almost responsive solution
.box{
display:inline-block;
position:relative;
width:300px;
height:300px;
}
.box:after{
position:absolute;
content:"Some text";
width:20%;
height:20%;
padding:5%;
font-size:100%;
left:calc(50% - 15%);
top:calc(50% - 19%);
}
<div class="box">
<svg viewBox="0 0 99.999997 100" height="100%" width="100%">
<g transform="translate(0,-952.36216)" id="layer1">
<path id="path4701" d="m 23,959 5,-3 6,11 6,-11 6,0 -9,17 4,7 8,-14 7,13 4,-7 -7,-13 3,-5 7,13 6,-11 4,2 -6,11 12,0 3,5 -18,0 -4,8 16,0 -7,13 8,0.12 7,-13 6,0 -7,13 12,0 -0.12,6 -12,-0 5,11 -3,5 -9,-17 -8,0.12 8,14 -16,0 4,8 18,0 -3,5 -12,-0.06 6,11 -4,3 -6,-11 -7,13 -3,-5 7,-13 -4,-7 -7,14 -8,-14 -4,7 9,17 -6,0.12 -6,-11 -6,11 -4,-3 6,-11 -14,-0.12 3,-6 15,0.12 4,-8 -15,-0.063 7,-14 -8,0 -9,17 -3,-5 6,-11 -12,0 0,-6 12,-0.06 -7,-13 6,-0.12 7,13 8,0.06 -7,-13 15,0.06 -4,-8 -15,-0.06 -3,-5 14,-0.06 z" style="fill:#2ad4ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"/>
</g>
</svg>
</div>
Try re sizing in the following snippet,it stays almost responsive if width and height are increased equally
.box{
display:inline-block;
position:relative;
width:300px;
height:300px;
resize:both;
border:2px solid;
overflow:auto;
}
.box:after{
position:absolute;
content:"Some text";
width:20%;
height:20%;
padding:5%;
font-size:100%;
left:calc(50% - 15%);
top:calc(50% - 19%);
}
<div class="box">
<svg viewBox="0 0 99.999997 100" height="100%" width="100%">
<g transform="translate(0,-952.36216)" id="layer1">
<path id="path4701" d="m 23,959 5,-3 6,11 6,-11 6,0 -9,17 4,7 8,-14 7,13 4,-7 -7,-13 3,-5 7,13 6,-11 4,2 -6,11 12,0 3,5 -18,0 -4,8 16,0 -7,13 8,0.12 7,-13 6,0 -7,13 12,0 -0.12,6 -12,-0 5,11 -3,5 -9,-17 -8,0.12 8,14 -16,0 4,8 18,0 -3,5 -12,-0.06 6,11 -4,3 -6,-11 -7,13 -3,-5 7,-13 -4,-7 -7,14 -8,-14 -4,7 9,17 -6,0.12 -6,-11 -6,11 -4,-3 6,-11 -14,-0.12 3,-6 15,0.12 4,-8 -15,-0.063 7,-14 -8,0 -9,17 -3,-5 6,-11 -12,0 0,-6 12,-0.06 -7,-13 6,-0.12 7,13 8,0.06 -7,-13 15,0.06 -4,-8 -15,-0.06 -3,-5 14,-0.06 z" style="fill:#2ad4ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"/>
</g>
</svg>
</div>
Using Canvas:
The snowflake can also be created programmatically using HTML5 Canvas. Doing it programmatically allows for better control over the text and keeps it within the container at all times. I am certain that this method can be converted into its equivalent SVG version but am more comfortable with Canvas.
One disadvantage of using Canvas is that the output is not responsive. It becomes blurred (pixelated) when scaled. The only way to avoid it is to redraw the shape once again whenever there is a change to the text (or) dimensions etc.
Construction
The shape is actually comprised of three parts and they are as follows:
Inner star with 6 spikes (the green colored star)
The lines (red colored)
Outer star with 6 spikes (the blue colored star)
The stars are drawn using the same approach as described in my answer here. Basically we assign an inner and an outer radius to each of them, find points on the inner and outer circle and then connect them alternately to produce the star.
The lines are also drawn using a similar approach except that it has only one radius and the lines are drawn from the points on the (imaginary) inner circle of the green star to specified points on the circle.
Calculation of dimensions: Since the text has to be fit within the shape and the shape must expand to fit the text, the first step that needs to be done is to calculate the width that the text requires. This is done using measureText().width. The value that is obtained is set as the radius of the green star's inner circle (because the text has to remain within it).
Finally, since the outer blue star should look as though it is kind of clipped (not appear in full), height and width lesser than that of the blue star's outer radius is set and a border-radius is assigned to the canvas element.
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d"),
spikes = 6,
step = Math.PI / spikes,
rot; /* no. of spikes for the stars */
function drawSnowFlake(content, font) {
ctx.font = font; /* font of the text */
/* get width required to fit text and set radius of each star */
var text = ctx.measureText(content);
innerRadius = (text.width / 2) * 1.15; /* extra factor is to leave gap between text and shape */
outerRadius = text.width;
/* set center point for the individual parts of the shape */
x = leftOffset = outerRadius * 1.5;
y = topOffset = outerRadius * 1.5;
/* canvas height and width should be set same as the largest star for clip */
canvas.height = outerRadius * 3;
canvas.width = outerRadius * 3;
/* default settings */
rot = Math.PI / 2 * 3;
ctx.beginPath();
ctx.lineWidth = 15; /* thickness of the lines */
ctx.strokeStyle = 'blue'; /* color of the lines */
ctx.font = font; /* font of the text */
/* create the inner star */
ctx.moveTo(leftOffset, topOffset - outerRadius)
for (i = 0; i < spikes; i++) {
x = leftOffset + Math.cos(rot) * outerRadius;
y = topOffset + Math.sin(rot) * outerRadius;
ctx.lineTo(x, y);
rot += step;
x = leftOffset + Math.cos(rot) * innerRadius;
y = topOffset + Math.sin(rot) * innerRadius;
ctx.lineTo(x, y);
rot += step;
}
ctx.lineTo(leftOffset, topOffset - outerRadius);
ctx.closePath();
ctx.stroke();
ctx.restore();
/* draws the outer star */
ctx.beginPath();
ctx.moveTo(leftOffset, topOffset - (outerRadius * 1.725));
for (i = 0; i < spikes; i++) {
x = leftOffset + Math.cos(rot) * (outerRadius * 1.725);
y = topOffset + Math.sin(rot) * (outerRadius * 1.725);
ctx.lineTo(x, y)
rot += step
x = leftOffset + Math.cos(rot) * (outerRadius * 1.15);
y = topOffset + Math.sin(rot) * (outerRadius * 1.15);
ctx.lineTo(x, y);
rot += step;
}
ctx.lineTo(leftOffset, topOffset - (outerRadius * 1.725));
ctx.closePath();
ctx.stroke();
ctx.restore();
/* draws the lines from the stars */
rot = Math.PI / 3;
ctx.beginPath();
for (i = 0; i < spikes; i++) {
x = leftOffset + Math.cos(rot) * innerRadius;
y = topOffset + Math.sin(rot) * innerRadius;
x2 = leftOffset + Math.cos(rot) * (outerRadius * 1.5);
y2 = topOffset + Math.sin(rot) * (outerRadius * 1.5);
ctx.moveTo(x, y);
ctx.lineTo(x2, y2);
rot += 2 * Math.PI / spikes;
}
ctx.closePath();
ctx.stroke();
/* add text and position it */
ctx.textBaseline = "middle";
ctx.textAlign = "center";
ctx.fillText(content, leftOffset, topOffset);
}
document.getElementById('draw').addEventListener('click', function() {
var input = document.getElementById('content').value;
var fontsize = document.getElementById('fontsize').value;
var fontname = document.getElementById('fontname').value;
drawSnowFlake(input, fontsize + "px " + fontname);
});
drawSnowFlake("TEXT HERE", "16px Arial");
div {
margin-bottom: 10px;
}
canvas {
border-radius: 50%;
}
<div class='controls'>
<input id='content' type='textbox' value='TEXT HERE' />
<select id='fontsize'>
<option value='10'>10px</option>
<option value='12'>12px</option>
<option value='14'>14px</option>
<option value='16' selected>16px</option>
</select>
<select id='fontname'>
<option value='Arial' selected>Arial</option>
<option value='Verdana'>Verdana</option>
<option value='Courier'>Courier</option>
</select>
<button id='draw'>Draw</button>
</div>
<canvas id='canvas'></canvas>
With Text Wrap and Bevelled Edges:
Here is a more complex demo which also supports wrapping of the text into multiple lines if content exceeds a certain width. The below is what this snippet will do:
If the size required for the text is less than max-width, shrink the shape
If the size required for the text (height or width) is more than max-width, split the text into multiple lines, expand the shape to fit the text in center
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d"),
spikes = 6,
/* no. of spikes for the stars */
step = Math.PI / spikes,
rot, maxWidth = 100,
lineHeight = 20;
function drawSnowFlake(content, font) {
ctx.font = font; /* font of the text */
/* get width & height required to fit text and set radius of each star */
var text = ctx.measureText(content);
var width = text.width;
var height = splitText(ctx, content, maxWidth, lineHeight);
/* decide which among height & width should be used for radius */
if (width < maxWidth && height < maxWidth) {
innerRadius = (width / 2) * 1.15; /* extra factor is to leave gap between text and shape */
outerRadius = width;
} else if (width > maxWidth && height < maxWidth) {
innerRadius = (maxWidth / 2) * 1.15; /* extra factor is to leave gap between text and shape */
outerRadius = maxWidth;
} else {
innerRadius = (height / 2) * 1.15; /* extra factor is to leave gap between text and shape */
outerRadius = height;
}
/* set center point for the individual parts of the shape */
x = leftOffset = outerRadius * 1.5;
y = topOffset = outerRadius * 1.5;
/* canvas height and width should be set same as the largest star for clip */
canvas.height = outerRadius * 3;
canvas.width = outerRadius * 3;
/* default settings */
rot = Math.PI / 2 * 3;
ctx.beginPath();
ctx.lineWidth = 15; /* thickness of the lines */
ctx.lineJoin = 'bevel';
ctx.strokeStyle = 'blue'; /* color of the lines */
ctx.font = font; /* font of the text */
/* create the inner star */
ctx.moveTo(leftOffset, topOffset - outerRadius)
for (i = 0; i < spikes; i++) {
x = leftOffset + Math.cos(rot) * outerRadius;
y = topOffset + Math.sin(rot) * outerRadius;
ctx.lineTo(x, y);
rot += step;
x = leftOffset + Math.cos(rot) * innerRadius;
y = topOffset + Math.sin(rot) * innerRadius;
ctx.lineTo(x, y);
rot += step;
}
ctx.lineTo(leftOffset, topOffset - outerRadius);
ctx.closePath();
ctx.stroke();
ctx.restore();
/* draws the outer star */
ctx.beginPath();
ctx.moveTo(leftOffset, topOffset - (outerRadius * 1.725));
for (i = 0; i < spikes; i++) {
x = leftOffset + Math.cos(rot) * (outerRadius * 1.725);
y = topOffset + Math.sin(rot) * (outerRadius * 1.725);
ctx.lineTo(x, y)
rot += step
x = leftOffset + Math.cos(rot) * (outerRadius * 1.15);
y = topOffset + Math.sin(rot) * (outerRadius * 1.15);
ctx.lineTo(x, y);
rot += step;
}
ctx.lineTo(leftOffset, topOffset - (outerRadius * 1.725));
ctx.closePath();
ctx.stroke();
ctx.restore();
/* draws the lines from the stars */
rot = Math.PI / 3;
ctx.beginPath();
for (i = 0; i < spikes; i++) {
x = leftOffset + Math.cos(rot) * innerRadius;
y = topOffset + Math.sin(rot) * innerRadius;
x2 = leftOffset + Math.cos(rot) * (outerRadius * 1.5);
y2 = topOffset + Math.sin(rot) * (outerRadius * 1.5);
ctx.moveTo(x, y);
ctx.lineTo(x2, y2);
rot += 2 * Math.PI / spikes;
}
ctx.closePath();
ctx.stroke();
/* add text and position it */
wrapText(ctx, content, leftOffset, (topOffset - ((height - lineHeight) / 2)), maxWidth, lineHeight);
}
document.getElementById('draw').addEventListener('click', function() {
var input = document.getElementById('content').value;
var fontsize = document.getElementById('fontsize').value;
var fontname = document.getElementById('fontname').value;
drawSnowFlake(input, fontsize + "px " + fontname);
});
/* split text into lines based on width and calculate required height */
function splitText(context, text, maxWidth, lineHeight) {
var words = text.split(' ');
var line = '',
height = lineHeight;
for (var n = 0; n < words.length; n++) {
var testLine = line + words[n] + ' ';
var metrics = context.measureText(testLine);
var testWidth = metrics.width;
if (testWidth > maxWidth && n > 0) {
line = words[n] + ' ';
height += lineHeight;
} else {
line = testLine;
}
}
return height;
}
/* source from http://www.html5canvastutorials.com/tutorials/html5-canvas-wrap-text-tutorial/ */
function wrapText(context, text, x, y, maxWidth, lineHeight) {
var words = text.split(' ');
var line = '';
context.textBaseline = "middle";
context.textAlign = "center";
var topOffset = x;
for (var n = 0; n < words.length; n++) {
var testLine = line + words[n] + ' ';
var metrics = context.measureText(testLine);
var testWidth = metrics.width;
if (testWidth > maxWidth && n > 0) {
context.fillText(line, x, y);
line = words[n] + ' ';
y += lineHeight;
} else {
line = testLine;
}
}
context.fillText(line, x, y);
}
drawSnowFlake('CSS SHAPES, SVG, CANVAS DESIGNS ROCK', "16px Arial");
div {
margin-bottom: 10px;
}
canvas {
border-radius: 50%;
}
<div class='controls'>
<input id='content' type='textbox' value='CSS SHAPES, SVG, CANVAS DESIGNS ROCK' />
<select id='fontsize'>
<option value='10'>10px</option>
<option value='12'>12px</option>
<option value='14'>14px</option>
<option value='16' selected>16px</option>
</select>
<select id='fontname'>
<option value='Arial' selected>Arial</option>
<option value='Verdana'>Verdana</option>
<option value='Courier'>Courier</option>
</select>
<button id='draw'>Draw</button>
</div>
<canvas id='canvas'></canvas>
The OP wanted the snowflake to scale to fit the size of the text. Most of the other answers are doing it the other way around.
Here's the simplest solution I could come up width. It is mostly just HTML and SVG with a tiny snippet of JS. This is required because I don't believe it is possible to have an SVG scale to fit a parent that doesn't have an explicit size.
Change the text content or the CSS style of the inner <div> to your heart's content and the SVG snowflake will scale to match.
You could almost certainly do away with the JS if you were happy to use a bitmap background image instead of an SVG.
$().ready(function() {
divWidth = $(".snowflake div").outerWidth();
$(".snowflake svg").css("width", divWidth + "px");
});
.snowflake
{
display: inline-block;
position: relative;
}
.snowflake svg
{
position: absolute;
fill: #eef;
}
.snowflake DIV
{
position: absolute;
padding: 100px;
font: 30pt sans-serif;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="snowflake">
<svg viewBox="2 0 32 36">
<path d="M33.212,26.16l-3.054-1.764l1.84-1.062c0.238-0.139,0.32-0.441,0.184-0.684c-0.14-0.238-0.445-0.322-0.684-0.183 L29.16,23.82l-2.32-1.34l4.729-2.73c0.239-0.139,0.321-0.441,0.184-0.684c-0.139-0.238-0.445-0.322-0.684-0.183l-5.23,3.019 l-3.619-2.09l4.352-1.918l-4.354-1.919l3.619-2.091l5.231,3.021c0.079,0.047,0.165,0.067,0.25,0.067 c0.174,0,0.342-0.091,0.435-0.25c0.139-0.239,0.057-0.546-0.184-0.683l-4.731-2.732l2.32-1.34L31.5,13.32 c0.079,0.046,0.165,0.066,0.25,0.066c0.173,0,0.341-0.09,0.435-0.25c0.138-0.238,0.056-0.545-0.184-0.682L30.16,11.39l3.052-1.762 c0.239-0.139,0.32-0.443,0.184-0.684c-0.14-0.238-0.446-0.322-0.684-0.184l-3.051,1.763L29.66,8.401c0-0.275-0.225-0.5-0.5-0.5 c-0.276,0-0.5,0.225-0.5,0.5l0.001,2.699l-2.32,1.34l0.001-5.46c0-0.276-0.224-0.5-0.5-0.5c-0.275,0-0.5,0.224-0.5,0.5 l-0.001,6.037l-3.619,2.09l0.515-4.728l-3.838,2.81V9.008l5.229-3.021c0.238-0.138,0.32-0.443,0.184-0.684 c-0.14-0.238-0.445-0.321-0.684-0.182l-4.729,2.73V5.173l2.339-1.352c0.239-0.138,0.321-0.443,0.184-0.684 c-0.14-0.238-0.445-0.322-0.684-0.182L18.399,4.02V0.5c0-0.276-0.224-0.5-0.5-0.5s-0.5,0.224-0.5,0.5v3.523L15.56,2.961 c-0.24-0.141-0.545-0.057-0.683,0.184c-0.138,0.239-0.056,0.545,0.183,0.684l2.339,1.352v2.678l-4.729-2.73 c-0.24-0.14-0.545-0.057-0.683,0.184c-0.138,0.239-0.056,0.545,0.183,0.684l5.229,3.02v4.184l-3.837-2.811l0.514,4.729 l-3.621-2.092V6.989c0-0.276-0.224-0.5-0.5-0.5c-0.276,0-0.5,0.224-0.5,0.5v5.462l-2.318-1.34L7.136,8.41 c0-0.277-0.224-0.5-0.5-0.5c-0.276,0-0.5,0.223-0.5,0.5l0.001,2.125L3.084,8.771C2.845,8.63,2.539,8.714,2.401,8.955 C2.263,9.194,2.345,9.5,2.584,9.638L5.636,11.4l-1.839,1.062c-0.239,0.139-0.321,0.443-0.183,0.684 c0.093,0.16,0.261,0.25,0.434,0.25c0.085,0,0.171-0.021,0.25-0.066l2.339-1.351l2.319,1.339l-4.729,2.73 c-0.239,0.139-0.321,0.443-0.183,0.684c0.093,0.16,0.261,0.25,0.434,0.25c0.085,0,0.171-0.021,0.25-0.066l5.23-3.021l3.622,2.091 l-4.352,1.919l4.351,1.919l-3.621,2.09l-5.231-3.018c-0.241-0.138-0.545-0.058-0.683,0.184c-0.138,0.24-0.056,0.545,0.183,0.686 l4.731,2.729l-2.321,1.34l-2.338-1.352c-0.239-0.142-0.545-0.058-0.683,0.184c-0.138,0.238-0.056,0.545,0.183,0.684l1.838,1.062 l-3.05,1.76c-0.239,0.139-0.321,0.443-0.183,0.684c0.093,0.16,0.261,0.25,0.434,0.25c0.085,0,0.171-0.021,0.25-0.065l3.051-1.763 L6.14,27.4c0,0.276,0.224,0.5,0.5,0.5l0,0c0.276,0,0.5-0.225,0.5-0.5l-0.001-2.701l2.322-1.34l-0.002,5.463 c0,0.277,0.224,0.5,0.5,0.5s0.5-0.223,0.5-0.5l0.002-6.041l3.619-2.09l-0.514,4.729l3.837-2.81v4.183l-5.228,3.021 c-0.239,0.139-0.321,0.442-0.183,0.684c0.138,0.236,0.444,0.318,0.683,0.184l4.728-2.73v2.679l-2.339,1.353 c-0.239,0.139-0.321,0.442-0.183,0.684c0.138,0.236,0.444,0.32,0.683,0.184l1.839-1.062V35.3c0,0.274,0.224,0.5,0.5,0.5 s0.5-0.226,0.5-0.5v-3.524l1.841,1.062c0.079,0.046,0.165,0.066,0.25,0.066c0.174,0,0.342-0.09,0.435-0.25 c0.139-0.239,0.057-0.545-0.184-0.684l-2.341-1.354v-2.678l4.729,2.73c0.079,0.046,0.165,0.066,0.25,0.066 c0.174,0,0.342-0.09,0.435-0.25c0.139-0.239,0.057-0.545-0.184-0.684l-5.229-3.021V22.6l3.838,2.811l-0.514-4.729l3.62,2.09v6.039 c0,0.276,0.224,0.5,0.5,0.5c0.275,0,0.5-0.224,0.5-0.5V23.35l2.318,1.34l0.001,2.699c0,0.275,0.225,0.5,0.5,0.5s0.5-0.225,0.5-0.5 l-0.001-2.123l3.053,1.764c0.079,0.045,0.165,0.066,0.25,0.066c0.174,0,0.342-0.09,0.435-0.25 C33.536,26.604,33.454,26.296,33.212,26.16z M20.997,23.259l-2.6-1.901l-0.499-0.363l-0.501,0.365l-2.598,1.9l0.348-3.201 l0.067-0.615l-0.567-0.25l-2.945-1.299l2.946-1.299l0.566-0.25l-0.067-0.616l-0.348-3.2l2.598,1.901l0.5,0.364l0.5-0.365l2.6-1.901 l-0.349,3.201l-0.066,0.616l0.564,0.249l2.946,1.3l-2.944,1.299l-0.566,0.25l0.066,0.615L20.997,23.259z"/>
</svg>
<div>Here is some text. It's even wrapped.</div>
</div>

Creating a transparent inner notch?

I know how to create a notch on the outside like:
div:after {
content: '';
display: block;
width: 20px;
height: 20px;
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
}
But I can't figure out how to solve this thingy using CSS only:
The notch has to be inside of the container and it has to be transparent. So the above solution or an image won't solve it.
Maybe this can be created using SVG?
Edit
What I tried is this:
body {
background: #eee;
}
div {
position: relative;
height: 100px;
width: 200px;
background: #ccc;
}
div:after {
content: '';
position: absolute;
display: block;
top: 40px;
right: -10px;
width: 20px;
height: 20px;
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
background: #eee;
}
But this is clearly no soultion, because the pseudo element is not tranparent.
You cannot do this with pure CSS as clipping is not fully supported yet in all browsers (if cross-compatibility is important).
You would need to combine SVG clipping paths with CSS clipping and would end up with a not so elegant solution.
What you can do however is to create a background image using canvas. Canvas is supported in all the major HTML5 capable browsers. The backdraw with canvas is that you need to do a little more coding to create the shape. Optional an image could have been used instead but using canvas allow you to keep everything sharp (and not blurry as with an image when it is stretched).
The following solution will produce this result (I added red border to show the transparent region). You can tweak the parameters to get it look exactly as you need it to look and extend it with arguments to define size of the notch, width of transparent area etc. The code automatically adopts to the size of the element given as argument.
To add a notch simply call:
addNotch(element);
ONLINE DEMO HERE
The code is straight-forward and performs fast:
function addNotch(element) {
/// some setup
var canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d'),
/// get size of element in pixels
cs = window.getComputedStyle(element),
w = parseInt(cs.getPropertyValue('width') || '0', 10),
h = parseInt(cs.getPropertyValue('height') || '0', 10),
/// pre-calculate some values
hh = h * 0.5,
nw = 20, /// notch size
nh = nw * 0.5;
canvas.width = w;
canvas.height = h;
/// draw the main shape
ctx.moveTo(0, 0);
ctx.lineTo(w - nw, 0);
ctx.lineTo(w - nw, hh - nh);
ctx.lineTo(w - nw - nh, hh);
ctx.lineTo(w - nw, hh + nh);
ctx.lineTo(w - nw, h);
ctx.lineTo(0, h);
ctx.closePath();
ctx.fillStyle = '#7c7058';
ctx.fill();
/// draw the white arrow
ctx.beginPath();
ctx.lineWidth = 2;
ctx.strokeStyle = '#eee';
ctx.moveTo(w - nw - nw * 0.33, hh - nw * 0.75);
ctx.lineTo(w - nw - nw * 1.1, hh);
ctx.lineTo(w - nw - nw * 0.33, hh + nw * 0.75);
ctx.stroke();
/// convert canvas to image and set background of element
/// with this image
element.style.background = 'url(' + canvas.toDataURL() +
') no-repeat left top';
}
Here's an example using SVG clipping.
jsFiddle Demo
<div></div>
<svg>
<defs>
<clipPath id="clipping">
<polygon points="
0 0, 202 0,
202 36, 185 50, 202 64,
202 102, 0 102" />
</clipPath>
</defs>
</svg>
Try this fiddle out, it should set you on your way for what you're looking for.
#notched {
width: 0px;
height: 0px;
border-right: 60px solid transparent;
border-top: 60px solid #d35400;
border-left: 60px solid #d35400;
border-bottom: 60px solid #d35400;
}
Updated fiddle
You can use the :before for mask and after selector for the border, just set border-lef and border-bottom property.
div:before {
content: '';
position: absolute;
display: block;
top: 40px;
right: -10px;
width: 20px;
height: 20px;
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
background: #eee;
}
div:after {
content: '';
position: absolute;
display: block;
top: 38px;
right: -5px;
width: 20px;
height: 21px;
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
background: transparent;
border-left: 2px solid #eee;
border-bottom: 2px solid #eee;
}
the result:
jsFiddle