Skip to content

Instantly share code, notes, and snippets.

@lostintangent
Last active Jan 8, 2021
Embed
What would you like to do?
SVG Exercise 03: Morphing Generative flower
svg(viewBox="-50 -50 100 100")
const xlinkNS = 'http://www.w3.org/1999/xlink';
const svgNS = 'http://www.w3.org/2000/svg';
const $ = document.querySelector.bind(document);
const PI = Math.PI
const DEG = PI / 180;
const R = (a, b) => a + Math.floor(Math.random()*(b-a+1));
const pathDef = (arr, closed) => 'M' + arr.map(({x, y}) => `${x} ${y}`).join('L') + (closed ? 'Z':'');
function rotate({x, y}, a) {
const { sin, cos } = Math;
return {
x: x * cos(a) - y * sin(a),
y: x * sin(a) + y * cos(a)
};
}
function translate({x, y}, tx, ty) {
return {
x: x + tx,
y: y + ty
}
}
function scale({x,y}, sx, sy) {
return {
x: x * sx, y: y * sy
}
}
function mirrorX({x,y}) {
return scale({x,y}, -1, 1);
}
function petal(w, h, N = 4) {
const p = Array(N).fill(0).map((_, i) => ({
x: (i < N-1) ? (Math.random() * (w/2)) : 0,
y: (i+1)/N*h})
);
return [{x:0,y:0}, ...p, ...p.reverse().slice(1).map(mirrorX), {x:0,y:0}]
}
function flower(numLeafs) {
const P = petal(25, 40, R(2, 16)).slice(0,-1);
return [...Array(numLeafs).fill(0).map(
(_, i) => P.map(point => rotate(point, i * DEG * 360 / numLeafs))
).flat(), {x:0, y:0}];
}
for (let i = 0; i < 16; i++) {
const path = document.createElementNS(svgNS, 'path');
path.setAttribute('class', 'stroke');
path.setAttribute('d', pathDef(flower(R(2,8) * 3)));
path.setAttribute('id', 'flower_' + (i+1));
$('svg').appendChild(path);
}
const initial = document.createElementNS(svgNS, 'path');
initial.setAttribute('class', 'stroke');
initial.setAttribute('d', $('path').getAttribute('d'));
initial.setAttribute('id', 'flower_initial');
$('svg').appendChild(initial);
const tl = gsap.timeline({repeat: -1, paused: true });
for (let i = 2; i <= 16; i++) {
tl.to('#flower_1', {morphSVG: '#flower_' + i}, '+=1')
}
tl.to('#flower_1', {morphSVG: '#flower_initial'}, '+=1')
const first = $('path');
const pathLength = first.getTotalLength();
first.style.strokeDasharray = pathLength
first.style.strokeDashoffset = pathLength
first.classList.add('anim')
first.addEventListener('animationend', () => {
first.style.strokeDasharray = 'initial';
first.style.strokeDashoffset = 'initial';
tl.resume()
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.5.1/gsap.min.js"></script>
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/16327/MorphSVGPlugin3.min.js"></script>
vendors = official
body
background #222
margin 0
height 100vh
display flex
flex-wrap wrap
align-items center
justify-content space-around
svg
width 90vmin
height 90vmin
.stroke
stroke none
fill none
.stroke:first-child
fill none
stroke #fff
stroke-width .5
stroke-linecap round
stroke-linejoin round
.anim
animation stroke-anim linear 2s forwards
@keyframes stroke-anim {
to {
stroke-dashoffset: 0
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment