Drawing a Superellipse with Pure CSS

Drawing a shape with CSS

Circle with White Background:

Yes, you can do with below code. All we are doing is creating a rectangular box with the div and positioning a circular box (using :before and border-radius) on top of it on the left side.


HTML:


<div class='shape'></div>

CSS:


.shape{
height: 100px; /* height of rectangular area */
width: 200px; /* width of rectangular area */
background: red;
margin-left: 50px; /* Just for demo */
}
.shape:before{
position: absolute;
content: '';
height: 100px; /* equal to height of box */
width: 100px; /* equal to height of box because we need a circle */
background: white;
border-radius: 50px; /* 50% of height/width to make a circle */
margin-left: -50px; /* equal to border-radius to move it left by that much */
}

Demo


Circle with Transparent Background (Using Pseudo Element):

HTML:


<div class='container'>
<span class='shape'></span>
</div>

CSS:


.container{
height: 100px;
width: 100px;
background:red;
position:relative;
margin-top:100px;
margin-left:100px;
}
.shape{
width: 50px;
height: 100px;
top:0px;
left:-50px;
overflow:hidden;
position:absolute;
}
.shape:after{
content:'';
width: 100px;
height: 100px;
border-radius: 100px;
background:rgba(0,0,0,0);
position:absolute;
top:-40px;
left:-90px;
border:40px solid red;
}

Demo


Circle with Transparent Background (Using Box Shadow):

(Courtesy King King)


CSS:


div {
width:300px;
height:200px;
overflow:hidden;
position:relative;
left:100px;
top:50px;
}
div:before {
content:'';
position:absolute;
top:0;
left:-100px; /* should be equal to height */
height:100%;
width:200px;/* should be equal to height */
border-radius:50%;
box-shadow:0 0 0 1000px red;
}

Demo


Extra Sample: For additional samples, refer this thread.


HTML5 Canvas How to draw squircle with gradient border?

If we read the wikipedia article on squircles, we see that this is just the unweighted ellipse function using powers of 2 or higher, which means we can pretty easily compute the "y" values given "x" values and draw things that way, but doing so will give us extremely uneven segments: small changes in x will lead to HUGE changes in y at the start and end points, and tiny changes in y at the midpoint.


Instead, let's model the squircle as a parametric function, so we vary one control value and get reasonably evenly spaced intervals to work with. We can find this explained in the wikipedia article on the superellipse function:


x = |cos(t)^(2/n)| * sign(cos(t))
y = |sin(t)^(2/n)| * sign(sin(t))

for t from 0 to 2π, and the radii fixed to 1 (so they disappear from multiplications).


If we implement that, then we can add the rainbow coloring almost as an afterthought, drawing each path segment separately, with a strokeStyle coloring that uses HSL colors where the hue values shifts based on our t value:


// alias some math functions so we don't need that "Math." all the time
const abs=Math.abs, sign=Math.sign, sin=Math.sin, cos=Math.cos, pow=Math.pow;

// N=2 YIELDS A CIRCLE, N>2 YIELDS A SQUIRCLE
const n = 4;

function coord(t) {
let power = 2/n;
let c = cos(t), x = pow(abs(c), power) * sign(c);
let s = sin(t), y = pow(abs(s), power) * sign(s);
return { x, y };
}

function drawSegmentTo(t) {
let c = coord(t);
let cx = dim + r * c.x; // Here, dim is our canvas "radius",
let cy = dim + r * c.y; // and r is our circle radius, with
ctx.lineTo(cx, cy); // ctx being our canvas context.

// stroke segment in rainbow colours
let h = (360 * t)/TAU;
ctx.strokeStyle = `hsl(${h}, 100%, 50%)`;
ctx.stroke();

// start a new segment at the end point
ctx.beginPath();
ctx.moveTo(cx, cy);
}

We can then use this in combination with some standard Canvas2D API code:


const PI = Math.PI,
TAU = PI * 2,
edge = 200, // SIZE OF THE CANVAS, IN PIXELS
dim = edge/2,
r = dim * 0.9,
cvs = document.getElementById('draw');

// set up our canvas
cvs.height = cvs.width = edge;
ctx = cvs.getContext('2d');
ctx.lineWidth = 2;
ctx.fillStyle = '#004';
ctx.strokeStyle = 'black';
ctx.fillRect(0, 0, edge, edge);

And with all that setup complete, the draw code is really straight-forward:


// THIS DETERMINES HOW SMOOTH OF A CURVE GETS DRAWN
const segments = 32;

// Peg our starting point, which we know is (r,0) away from the center.
ctx.beginPath();
ctx.moveTo(dim + r, dim)

// Then we generate all the line segments on the path
for (let step=TAU/segments, t=step; t<=TAU; t+=step) drawSegmentTo(t);

// And because IEEE floats are imprecise, the last segment may not
// actually reach our starting point. As such, make sure to draw it!
ctx.lineTo(dim + r, dim);
ctx.stroke();

Running this will yield the following squircle:


a rainbox squircle of power 4


With a jsbin so you can play with the numbers: https://jsbin.com/haxeqamilo/edit?js,output


Of course, you can also go a completely other way: Create an SVG element (since SVG is part of HTML5) with a <path> element and appropriately set width, height, and viewbox, and then generate a d attribute and gradient-color that, but that's definitely way more finnicky.


Drawing a SuperEllipse in HLSL

The problem occurs because superEllipse(theta) returns a point whose angle is not necessarily theta. For example, superEllipse(3.6) returns a point with an angle of 3.2 radians.
As a result, in your main() function, it doesn't really make sense to compare the magnitudes of uv1 and se, since they have different angles.


See this image. The sweeping straight line represents the angle passed in to superEllipse, and the end of the curve represents where the new point is actually placed. Only rarely does the new point lie on the sweeping line.


If you use a polar equation for a superellipse, rather than a parametric equation, then you can use it to perform your distance test. Conveniently, Wolfram Mathworld has just such an equation:


Sample Image


You just need to code that up and put it in your main function.


float4 main(float2 uv : TEXCOORD) : COLOR 
{
float2 uv1 = uv * float2(2.0f, 2.0f) - float2(1.0f, 1.0f);
float angle = degrees(atan2(uv1.y, uv1.x)) + 180.0f;
//to do: implement equation shown above. Use `angle` for theta.
//`m` should be 4 if you want a four-pronged shape.
float magnitude = ???

float2 zero = float2(0.0f, 0.0f);
float dist1 = distance(uv1, zero);
float4 color = float4(0,0,0,1);
if (dist1 <= magnitude) //uv is inside the superellipse
color += float4(0,1,0,1);
return color;
}

How can I make the CSS drawing always show fixed in every screen?

If you want to make this shape for static purpose I recommend you to use SVG format, but I think you want to make it as a dynamic thing like it can rotate its hand or such a thing.


so, you have some shape and want to stick them together. I suggest to use a div for
main axis, and other things (hands and feet), they stick to this axis.
so cool let's do that in code,


in your css do something like:


.body {
position: relative;

width: 100px;
height: 400px;
}

.organ {
position: absolute;
}

.head {
top: 0;
/* to centerlize */
left: 50%;
transform: translateX(-50%);
/* to centerlize */
}

.hand_right {
top: 20px;
right: 0;
}

.hand_left {
top: 20px;
left: 0;
}

.hand_right {
bottom: 0;
right: 0;
}

.hand_left {
bottom: 0;
left: 0;
}

and in your html can be:


<div class="body">
<div class="organ head"></div>

<div class="organ hand_right"></div>
<div class="organ hand_left"></div>

<div class="organ foot_right"></div>
<div class="organ foot_left"></div>
</div>

in this way you've wrap your all organs in a body that they stick to it. so in every screens, the can not separate.
if you want to make their size fixed too, use % or vw or vh, like:


width: 80%; // percentage according to parent size
width 70vw; // viewport width
height: 30vh; // viewport height



Related Topics



Leave a reply



Submit