- const points = Array(361).fill(null).map(() => '50,50').join(' ')
svg(xmlns="http://www.w3.org/2000/svg", viewBox="0 0 100 100")
polyline#inner(points=points, fill="none")
polyline#outer(points=points, fill="none")
View Compiled
:root,
body {
margin: 0;
padding: 0;
height: 100%;
}
body {
background-color: var(--color-background);
color: var(--color-line);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 4px;
margin: 0 auto;
font-family: sans-serif;
}
svg {
max-width: 90vmin;
overflow: visible;
shape-rendering: geometricprecision;
stroke-linejoin: round;
}
#outer {
stroke-width: var(--stroke-width-border, 0.4);
stroke: var(--color-border);
}
#inner {
stroke-width: var(--stroke-width-line, 0.2);
stroke: var(--color-line);
}
View Compiled
const duration = 0.25;
const ease = "power2";
gsap.registerPlugin(MorphSVGPlugin);
const svg = document.querySelector("svg");
let n = 2;
let d = 29;
const knobSettings = {
knobsToggle: false,
visible: 0,
CSSVarTarget: document.documentElement,
knobs: [
"Rose Parameters",
{
label: "n",
type: "number",
value: n,
min: 2,
step: 1,
onChange: (e) => (n = Number(e.target.value)) && morph()
},
{
label: "d",
type: "number",
value: d,
min: 1,
step: 0.1,
onChange: (e) => (d = Number(e.target.value)) && morph()
},
"Styles",
{
label: "Border",
type: "checkbox",
checked: true,
onChange: (e) => {
let opacity = e.target.checked ? 1 : 0;
gsap.to("#outer", { opacity, duration, ease });
}
},
{
cssVar: ["stroke-width-border"], // alias for the CSS variable
label: "Border Stroke Width",
type: "number",
min: 0,
max: 10,
step: 0.01,
value: 0.4
},
{
cssVar: ["stroke-width-line"], // alias for the CSS variable
label: "Line Stroke Width",
type: "number",
min: 0,
max: 10,
step: 0.01,
value: 0.2
},
{
cssVar: ["color-background"], // alias for the CSS variable
label: "Background Color",
type: "color",
value: "#E84855"
},
{
cssVar: ["color-border"], // alias for the CSS variable
label: "Border Color",
type: "color",
value: "#2D3047"
},
{
cssVar: ["color-line"], // alias for the CSS variable
label: "Line Color",
type: "color",
value: "#2D3047"
}
]
};
const knobs = new Knobs(knobSettings);
function degToRad(deg) {
return deg * (Math.PI / 180);
}
function generatePoint(i, factor) {
let k = i * factor;
let r = 50 * Math.sin(degToRad(k * n));
let x = Math.sin(degToRad(k)) * r;
let y = Math.cos(degToRad(k)) * r;
return [x + 50, 50 - y];
}
function generatePoints() {
const inner = [];
const outer = [];
for (let i = 0; i < 361; i++) {
inner.push(generatePoint(i, d));
outer.push(generatePoint(i, 1));
}
return { inner, outer };
}
function formatPoints(points = []) {
return points.map((point) => point.join(",")).join(" ");
}
function morph() {
const { inner, outer } = generatePoints();
gsap.to("#inner", { morphSVG: formatPoints(inner), duration, ease });
gsap.to("#outer", { morphSVG: formatPoints(outer), duration, ease });
}
morph();
setTimeout(knobs.toggle.bind(knobs), 500);
This Pen doesn't use any external CSS resources.