A26 ·
Animating Clipped Shapes
<main class="overflow-hidden vh-100 flex items-center justify-center" style="background-color: #2C2D3D;">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" style="display:none">
<defs>
<filter id="squiggly-0">
<feTurbulence baseFrequency="0.02" numOctaves="3" result="noise" seed="0" />
<feDisplacementMap in="SourceGraphic" in2="noise" scale="2" />
</filter>
<filter id="squiggly-1">
<feTurbulence baseFrequency="0.02" numOctaves="3" result="noise" seed="1" />
<feDisplacementMap in="SourceGraphic" in2="noise" scale="3" />
</filter>
<filter id="squiggly-2">
<feTurbulence baseFrequency="0.02" numOctaves="3" result="noise" seed="2" />
<feDisplacementMap in="SourceGraphic" in2="noise" scale="2" />
</filter>
<filter id="squiggly-3">
<feTurbulence baseFrequency="0.02" numOctaves="3" result="noise" seed="3" />
<feDisplacementMap in="SourceGraphic" in2="noise" scale="3" />
</filter>
<filter id="squiggly-4">
<feTurbulence baseFrequency="0.02" numOctaves="3" result="noise" seed="4" />
<feDisplacementMap in="SourceGraphic" in2="noise" scale="1" />
</filter>
</defs>
</svg>
<svg class="w-100 mw7 center squiggle" viewBox="0 0 800 600" fill="#EEB65A">
<defs>
<clipPath id="left-slice-mask">
<path
d="M 441.6 405 A 113 113 0 1 1 441.6 195 Z" fill="#fff" />
</clipPath>
<clipPath id="right-slice-mask">
<path
d="M 441.6 195 A 113 113 0 0 1 441.6 405 Z" fill="#fff" />
</clipPath>
</defs>
<g stroke="#EEB65A" stroke-width="2">
<!-- Arc Strokes -->
<path class="js-left-slice"
d="M 441.6 405 A 113 113 0 1 1 441.6 195 Z" fill="none" />
<path class="js-right-slice"
d="M 441.6 195 A 113 113 0 0 1 441.6 405 Z" fill="none" />
<!-- Arc Fills -->
<rect id="js-left-fill"
x="441" y="187" width="154" height="226" fill="#EEB65A" clip-path="url(#left-slice-mask)" />
<rect id="js-right-fill"
x="441.6" y="187" width="72" height="226" fill="#EEB65A" clip-path="url(#right-slice-mask)" />
<!-- Burst ball -->
<circle id="js-burst" cx="348" cy="300" r="75" fill="#EEB65A" opacity="0" />
</g>
</svg>
</main> .squiggle {
animation: squigglevision 0.3s infinite;
}
/**
* from: https://tympanus.net/codrops/2016/03/21/animated-animals-css-svg/
*/
@keyframes squigglevision {
0% {
filter: url("#squiggly-0");
}
25% {
filter: url("#squiggly-1");
}
50% {
filter: url("#squiggly-2");
}
75% {
filter: url("#squiggly-3");
}
100% {
filter: url("#squiggly-4");
}
} const EL = {
LEFT: '.js-left-slice',
RIGHT: '.js-right-slice',
LEFT_FILL: '#js-left-fill',
RIGHT_FILL: '#js-right-fill',
BURST: '#js-burst',
};
const DURATIONS = {
JOIN: 800,
SLIDE: 350,
BURST: 1200,
COMBINE: 300,
SLIDE_OFF: 300,
};
const TIME = {
JOIN_X: DURATIONS.JOIN,
get JOIN() {
return this.JOIN_X + DURATIONS.JOIN - 100;
},
get SLIDE() {
return this.JOIN + DURATIONS.SLIDE;
},
get BURST() {
return this.SLIDE + DURATIONS.BURST;
},
get COMBINE() {
return this.BURST + DURATIONS.COMBINE;
},
get PULL_Y() {
return this.COMBINE + DURATIONS.JOIN;
},
};
const timeline = anime.timeline({
loop: true,
elasticity: 0,
});
const at = (offset, obj) => ({ ...obj, offset });
const leftSlice = {
easing: 'easeInOutQuint',
targets: EL.LEFT,
translateX: [
{ value: -10, duration: 1 },
{ value: 0, duration: DURATIONS.JOIN },
{
value: -10,
duration: DURATIONS.COMBINE,
delay: TIME.BURST - DURATIONS.JOIN,
easing: 'easeOutQuint',
},
],
translateY: [
{ value: -28, duration: 1 },
{ delay: TIME.JOIN_X, value: 0, duration: DURATIONS.JOIN },
{
delay: TIME.COMBINE - TIME.JOIN,
value: -28,
duration: DURATIONS.JOIN,
},
],
opacity: [
{ delay: TIME.SLIDE, value: 0, duration: 1 },
{ delay: TIME.BURST - TIME.SLIDE, value: 1, duration: 1 },
],
};
const leftFill = {
easing: 'easeInOutQuint',
targets: EL.LEFT_FILL,
x: [
{
value: '-=154',
duration: DURATIONS.SLIDE,
delay: TIME.JOIN,
easing: 'easeOutExpo',
},
{
value: '+=154',
duration: 1,
easing: 'easeOutExpo',
},
{
value: '-=154',
duration: DURATIONS.COMBINE,
delay: TIME.BURST - TIME.SLIDE,
easing: 'easeOutQuint',
},
{
value: '+=154',
duration: DURATIONS.COMBINE,
delay: TIME.PULL_Y - TIME.COMBINE,
easing: 'easeOutCubic',
},
],
translateX: [
{ value: -10, duration: 1 },
{ value: 0, duration: DURATIONS.JOIN },
{
value: -10,
duration: DURATIONS.SLIDE_OFF,
delay: TIME.BURST - DURATIONS.JOIN,
easing: 'easeOutQuad',
},
],
translateY: [
{ value: -28, duration: 1 },
{ delay: TIME.JOIN_X, value: 0, duration: DURATIONS.JOIN },
{
delay: TIME.COMBINE - TIME.JOIN,
value: -28,
duration: DURATIONS.JOIN,
},
],
opacity: [
{ delay: TIME.SLIDE, value: 0, duration: 1 },
{ delay: TIME.BURST - TIME.SLIDE, value: 1, duration: 1 },
],
};
const rightSlice = {
easing: 'easeInOutQuint',
targets: EL.RIGHT,
translateX: [
{ value: 10, duration: 1 },
{ value: 0, duration: DURATIONS.JOIN },
{
value: 10,
duration: DURATIONS.COMBINE,
delay: TIME.BURST - DURATIONS.JOIN,
easing: 'easeOutQuint',
},
],
translateY: [
{ value: 28, duration: 1 },
{ delay: TIME.JOIN_X, value: 0, duration: DURATIONS.JOIN },
{
delay: TIME.COMBINE - TIME.JOIN,
value: 28,
duration: DURATIONS.JOIN,
},
],
};
const righttFill = {
easing: 'easeInOutQuint',
targets: EL.RIGHT_FILL,
x: [
{
value: '-=72',
duration: DURATIONS.SLIDE,
delay: TIME.JOIN,
easing: 'easeOutExpo',
},
{
value: '+=72',
duration: DURATIONS.COMBINE,
delay: TIME.BURST - TIME.SLIDE,
easing: 'easeOutQuint',
},
],
translateX: [
{ value: 10, duration: 1 },
{ value: 0, duration: DURATIONS.JOIN },
{
value: 10,
duration: DURATIONS.COMBINE,
delay: TIME.BURST - DURATIONS.JOIN,
easing: 'easeOutQuint',
},
],
translateY: [
{ value: 28, duration: 1 },
{ delay: TIME.JOIN_X, value: 0, duration: 800 },
{
delay: TIME.COMBINE - TIME.JOIN,
value: 28,
duration: DURATIONS.JOIN,
},
],
};
const burst = {
easing: 'easeOutCubic',
targets: EL.BURST,
opacity: [{ value: 1, duration: 1 }, { delay: 1200, value: 0, duration: 1 }],
scaleX: {
value: 1.25,
duration: DURATIONS.BURST / 2,
delay: DURATIONS.BURST / 2,
easing: 'easeInCubic',
},
translateX: [
{ value: '-=200', duration: DURATIONS.BURST / 2, easing: 'easeOutCubic' },
{ value: '+=182', duration: DURATIONS.BURST / 2, easing: 'easeInCubic' },
],
r: { value: 22.5, duration: 400 },
};
timeline
.add(at(0, leftSlice))
.add(at(0, rightSlice))
.add(at(0, leftFill))
.add(at(0, righttFill))
.add(at(TIME.SLIDE, burst)); Another #postAboutYourPen post. This time I’m going to explain how I recreated Herry Koo’s SA 15 dribbble shot using SVG and animejs.
The Ingredients
- 2 Arc Slices
- 2 Clipped Rectangles
- 1 Circle
An arc is a section of circle or an ellipse. To create this arc you need the starting point, the end point and the radius of the circle that these two points are on. There are two circles that can connect any two points: a big one and a small one. SVG allows us to pick which one we want using the large-arc-flag. We can also control whether the arc should be drawn clockwise or anti-clockwise using the sweep-flag.
A rx ry x-axis-rotation large-arc-flag sweep-flag x y
Dribbble shots are 800 x 600px so that’s what I picked for the vieBox. This makes it easy to open the gif in Preview, measure stuff and use those values in the SVG. I measured the circle to be 113px. The left slice was 154px wide and the right slice 72px. With a bit of trial and error I found the start and end-points of the arcs. I drew the arcs using the path element.
<!--
Move to the starting point then draw an arc to the end point
and close the shape by going back to the starting point
M start_x start_y A 113 113 0 large-arc-flag 1 end_x end_y Z
-->
<path class="js-left-slice" d="M 441.6 405 A 113 113 0 1 1 441.6 195 Z" />
<path class="js-right-slice" d="M 441.6 195 A 113 113 0 0 1 441.6 405 Z" />
<main class="overflow-hidden vh-100 flex items-center justify-center" style="background-color: #2C2D3D;">
<svg class="w-100 mw7 center" viewBox="0 0 800 600" fill="#fff">
<g stroke="#EEB65A" stroke-width="3" transform="translate(-16 0)">
<path id="js-left-slice"
d="M 441.6 405 A 113 113 0 1 1 441.6 195 Z" fill="none" />
<circle cx="441.6" cy="405" r="6" stroke="#fff" fill="#2C2D3D" />
<circle cx="441.6" cy="195" r="6" stroke="#fff" fill="#2C2D3D" />
</g>
<g stroke="#EEB65A" stroke-width="3" transform="translate(16 0)">
<path id="js-right-slice"
d="M 441.6 195 A 113 113 0 0 1 441.6 405 Z" fill="none" />
<circle cx="441.6" cy="405" r="6" stroke="#fff" fill="#2C2D3D" />
<circle cx="441.6" cy="195" r="6" stroke="#fff" fill="#2C2D3D" />
</g>
</svg>
</main> .squiggle {
-webkit-animation: squigglevision 0.3s infinite;
animation: squigglevision 0.3s infinite;
}
/**
* from: https://tympanus.net/codrops/2016/03/21/animated-animals-css-svg/
*/
@keyframes squigglevision {
0 {
filter: url("#squiggly-0");
}
25% {
filter: url("#squiggly-1");
}
50% {
filter: url("#squiggly-2");
}
75% {
filter: url("#squiggly-3");
}
100% {
filter: url("#squiggly-4");
}
} const EL = {
LEFT: '#js-left-slice',
RIGHT: '#js-right-slice',
};
anime({
targets: EL.LEFT,
strokeDashoffset: [anime.setDashoffset, 0],
easing: 'easeInOutSine',
duration: 3000,
loop: true,
direction: 'alternate',
});
anime({
targets: EL.RIGHT,
strokeDashoffset: [anime.setDashoffset, 0],
easing: 'easeInOutSine',
duration: 3000,
loop: true,
direction: 'alternate',
});
These paths create the outline. The sliding fill inside them was created using clipped rectangles. For the clipPath we use the same paths as the outline and then apply them to the rectangles using the clip-path attribute.
<defs>
<clipPath id="left-slice-mask">
<path d="M 441.6 405 A 113 113 0 1 1 441.6 195 Z" fill="#fff" />
</clipPath>
<clipPath id="right-slice-mask">
<path d="M 441.6 195 A 113 113 0 0 1 441.6 405 Z" fill="#fff" />
</clipPath>
</defs>
<rect
class="js-right-fill"
x="441.6"
y="187"
width="72"
height="226"
clip-path="url(#right-slice-mask)"
/>
To slide the fill in-and-out I animate the x value using animejs. The clipPath remains static. It’s only the rectangles that are moving.
<main class="overflow-hidden vh-100 flex items-center justify-center" style="background-color: #2C2D3D;">
<svg class="w-100 mw7 center" viewBox="0 0 800 600" fill="#fff">
<defs>
<clipPath id="left-slice-mask">
<path
d="M 441.6 405 A 113 113 0 1 1 441.6 195 Z" fill="#fff" />
</clipPath>
<clipPath id="right-slice-mask">
<path
d="M 441.6 195 A 113 113 0 0 1 441.6 405 Z" fill="#fff" />
</clipPath>
</defs>
<g stroke="#fff" stroke-width="3" transform="translate(-12 0)">
<rect class="js-left-fill"
x="286" y="187" width="154" height="226" stroke="none" fill="#Eeb65a"
clip-path="url(#left-slice-mask)" />
<rect class="js-left-fill"
x="286" y="187" width="156" height="226" fill="none" stroke-dasharray="10,10" />
<path class="js-left-slice"
d="M 441.6 405 A 113 113 0 1 1 441.6 195 Z" fill="none" />
</g>
<g stroke="#fff" stroke-width="3" transform="translate(12 0)">
<rect class="js-right-fill"
x="441.6" y="187" width="72" height="226" fill="none" stroke-dasharray="10,10" />
<rect class="js-right-fill"
x="441.6" y="187" width="72" height="226" stroke="none" fill="#Eeb65a"
clip-path="url(#right-slice-mask)" />
<path class="js-right-slice"
d="M 441.6 195 A 113 113 0 0 1 441.6 405 Z" fill="none" />
</g>
</svg>
</main> .squiggle {
-webkit-animation: squigglevision 0.3s infinite;
animation: squigglevision 0.3s infinite;
}
/**
* from: https://tympanus.net/codrops/2016/03/21/animated-animals-css-svg/
*/
@keyframes squigglevision {
0 {
filter: url("#squiggly-0");
}
25% {
filter: url("#squiggly-1");
}
50% {
filter: url("#squiggly-2");
}
75% {
filter: url("#squiggly-3");
}
100% {
filter: url("#squiggly-4");
}
} const EL = {
LEFT: '.js-left-slice',
RIGHT: '.js-right-slice',
LEFT_FILL: '.js-left-fill',
RIGHT_FILL: '.js-right-fill',
BURST: '#js-burst',
};
anime({
easing: 'easeInOutSine',
targets: EL.LEFT_FILL,
x: [
{
value: '-=154',
duration: 1200,
delay: 1000,
},
{
value: '+=154',
duration: 1200,
delay: 1000,
},
],
loop: true,
});
anime({
easing: 'easeInOutSine',
targets: EL.RIGHT_FILL,
x: [
{
value: '+=72',
duration: 1200,
delay: 1000,
},
{
value: '-=72',
duration: 1200,
delay: 1000,
},
],
loop: true,
}); The combined shape is moved up-down and left-right by animating translateX and translateY. We apply the same animation at the same time to both the arc slice and clipped rectangle. Note, you do not need to animate the position of the clipPath.
<main class="overflow-hidden vh-100 flex items-center justify-center" style="background-color: #2C2D3D;">
<svg class="w-100 mw7 center" viewBox="0 0 800 600" fill="#EEB65A">
<defs>
<clipPath id="left-slice-mask">
<path
d="M 441.6 405 A 113 113 0 1 1 441.6 195 Z" fill="#fff" />
</clipPath>
<clipPath id="right-slice-mask">
<path
d="M 441.6 195 A 113 113 0 0 1 441.6 405 Z" fill="#fff" />
</clipPath>
</defs>
<g stroke="#EEB65A" stroke-width="2">
<!-- Arc Strokes -->
<path class="js-left-slice"
d="M 441.6 405 A 113 113 0 1 1 441.6 195 Z" fill="none" />
<path class="js-right-slice"
d="M 441.6 195 A 113 113 0 0 1 441.6 405 Z" fill="none" />
<!-- Arc Fills -->
<rect id="js-left-fill"
x="287" y="187" width="154" height="226" fill="#EEB65A" clip-path="url(#left-slice-mask)" />
<rect id="js-right-fill"
x="441.6" y="187" width="72" height="226" fill="#EEB65A" clip-path="url(#right-slice-mask)" />
</g>
</svg>
</main> .squiggle {
-webkit-animation: squigglevision 0.3s infinite;
animation: squigglevision 0.3s infinite;
}
/**
* from: https://tympanus.net/codrops/2016/03/21/animated-animals-css-svg/
*/
@keyframes squigglevision {
0 {
filter: url("#squiggly-0");
}
25% {
filter: url("#squiggly-1");
}
50% {
filter: url("#squiggly-2");
}
75% {
filter: url("#squiggly-3");
}
100% {
filter: url("#squiggly-4");
}
} const EL = {
LEFT: '.js-left-slice',
RIGHT: '.js-right-slice',
LEFT_FILL: '#js-left-fill',
RIGHT_FILL: '#js-right-fill',
BURST: '#js-burst',
};
const DURATIONS = {
JOIN: 800,
SLIDE: 350,
BURST: 1200,
COMBINE: 300,
SLIDE_OFF: 300,
};
const TIME = {
JOIN_X: DURATIONS.JOIN,
get JOIN() {
return this.JOIN_X + DURATIONS.JOIN - 100;
},
get SLIDE() {
return DURATIONS.SLIDE;
},
get BURST() {
return this.SLIDE + DURATIONS.BURST;
},
get COMBINE() {
return this.BURST + DURATIONS.COMBINE;
},
get PULL_Y() {
return this.COMBINE + DURATIONS.JOIN;
},
};
const timeline = anime.timeline({
loop: true,
elasticity: 0,
});
const at = (offset, obj) => ({ ...obj, offset });
const leftSlice = {
easing: 'easeInOutQuint',
targets: EL.LEFT,
translateX: [
{ value: -10, duration: 1 },
{ value: 0, duration: DURATIONS.JOIN },
{
value: -10,
duration: DURATIONS.COMBINE,
delay: TIME.BURST - DURATIONS.JOIN,
easing: 'easeOutQuint',
},
],
translateY: [
{ value: -28, duration: 1 },
{ delay: TIME.JOIN_X, value: 0, duration: DURATIONS.JOIN },
{
delay: TIME.COMBINE - TIME.JOIN,
value: -28,
duration: DURATIONS.JOIN,
},
],
};
const leftFill = {
easing: 'easeInOutQuint',
targets: EL.LEFT_FILL,
translateX: [
{ value: -10, duration: 1 },
{ value: 0, duration: DURATIONS.JOIN },
{
value: -10,
duration: DURATIONS.COMBINE,
delay: TIME.BURST - DURATIONS.JOIN,
easing: 'easeOutQuint',
},
],
translateY: [
{ value: -28, duration: 1 },
{ delay: TIME.JOIN_X, value: 0, duration: DURATIONS.JOIN },
{
delay: TIME.COMBINE - TIME.JOIN,
value: -28,
duration: DURATIONS.JOIN,
},
],
};
const rightSlice = {
easing: 'easeInOutQuint',
targets: EL.RIGHT,
translateX: [
{ value: 10, duration: 1 },
{ value: 0, duration: DURATIONS.JOIN },
{
value: 10,
duration: DURATIONS.COMBINE,
delay: TIME.BURST - DURATIONS.JOIN,
easing: 'easeOutQuint',
},
],
translateY: [
{ value: 28, duration: 1 },
{ delay: TIME.JOIN_X, value: 0, duration: DURATIONS.JOIN },
{
delay: TIME.COMBINE - TIME.JOIN,
value: 28,
duration: DURATIONS.JOIN,
},
],
};
const rightFill = {
easing: 'easeInOutQuint',
targets: EL.RIGHT_FILL,
translateX: [
{ value: 10, duration: 1 },
{ value: 0, duration: DURATIONS.JOIN },
{
value: 10,
duration: DURATIONS.COMBINE,
delay: TIME.BURST - DURATIONS.JOIN,
easing: 'easeOutQuint',
},
],
translateY: [
{ value: 28, duration: 1 },
{ delay: TIME.JOIN_X, value: 0, duration: 800 },
{
delay: TIME.COMBINE - TIME.JOIN,
value: 28,
duration: DURATIONS.JOIN,
},
],
};
timeline
.add(at(0, leftSlice))
.add(at(0, leftFill))
.add(at(0, rightSlice))
.add(at(0, rightFill)); The little circle that burst out. Well… that’s just a circle. At the appropriate time I hide the left slice and rectangle and show the circle instead. The circle scales down as it moves to the left and then stretches — using scaleX — as it moves back to the right.
<main class="overflow-hidden vh-100 flex items-center justify-center" style="background-color: #2C2D3D;">
<svg class="w-100 mw7 center" viewBox="0 0 800 600" fill="#EEB65A">
<defs>
<clipPath id="left-slice-mask">
<path
d="M 441.6 405 A 113 113 0 1 1 441.6 195 Z" fill="#fff" />
</clipPath>
<clipPath id="right-slice-mask">
<path
d="M 441.6 195 A 113 113 0 0 1 441.6 405 Z" fill="#fff" />
</clipPath>
</defs>
<g stroke="#EEB65A" stroke-width="2">
<!-- Arc Strokes -->
<path class="js-left-slice" transform="translate(-2 0)"
d="M 441.6 405 A 113 113 0 1 1 441.6 195 Z" fill="none" />
<path class="js-right-slice" transform="translate(2 0)"
d="M 441.6 195 A 113 113 0 0 1 441.6 405 Z" fill="none" />
<!-- Arc Fills -->
<rect id="js-left-fill" transform="translate(-2 0)"
x="287" y="187" width="154" height="226" fill="#EEB65A" clip-path="url(#left-slice-mask)" />
<rect id="js-right-fill" transform="translate(2 0)"
x="441.6" y="187" width="72" height="226" fill="#EEB65A" clip-path="url(#right-slice-mask)" />
<!-- Burst ball -->
<circle id="js-burst" cx="348" cy="300" r="75" fill="#EEB65A" opacity="0" />
</g>
</svg>
</main> .squiggle {
-webkit-animation: squigglevision 0.3s infinite;
animation: squigglevision 0.3s infinite;
}
/**
* from: https://tympanus.net/codrops/2016/03/21/animated-animals-css-svg/
*/
@keyframes squigglevision {
0 {
filter: url("#squiggly-0");
}
25% {
filter: url("#squiggly-1");
}
50% {
filter: url("#squiggly-2");
}
75% {
filter: url("#squiggly-3");
}
100% {
filter: url("#squiggly-4");
}
} const EL = {
LEFT: '.js-left-slice',
RIGHT: '.js-right-slice',
LEFT_FILL: '#js-left-fill',
RIGHT_FILL: '#js-right-fill',
BURST: '#js-burst',
};
const DURATIONS = {
JOIN: 800,
SLIDE: 350,
BURST: 1200,
COMBINE: 300,
SLIDE_OFF: 300,
};
const TIME = {
get SLIDE() {
return DURATIONS.SLIDE;
},
get BURST() {
return this.SLIDE + DURATIONS.BURST;
},
get COMBINE() {
return this.BURST + DURATIONS.COMBINE;
},
get PULL_Y() {
return this.COMBINE + DURATIONS.JOIN;
},
};
const timeline = anime.timeline({
loop: true,
elasticity: 0,
});
const at = (offset, obj) => ({ ...obj, offset });
const leftSlice = {
easing: 'easeInOutQuint',
targets: EL.LEFT,
opacity: [
{ value: 0, duration: 1 },
{ delay: DURATIONS.BURST, value: 1, duration: 1 },
],
};
const leftFill = {
easing: 'easeInOutQuint',
targets: EL.LEFT_FILL,
opacity: [
{ value: 0, duration: 1 },
{ delay: DURATIONS.BURST, value: 1, duration: 1 },
],
};
const burst = {
easing: 'easeOutCubic',
targets: EL.BURST,
opacity: [{ value: 1, duration: 1 }, { delay: 1200, value: 0, duration: 1 }],
scaleX: {
value: 1.25,
duration: DURATIONS.BURST / 2,
delay: DURATIONS.BURST / 2,
easing: 'easeInCubic',
},
translateX: [
{ value: '-=200', duration: DURATIONS.BURST / 2, easing: 'easeOutCubic' },
{ value: '+=182', duration: DURATIONS.BURST / 2, easing: 'easeInCubic' },
],
r: { value: 22.5, duration: 400 },
};
timeline
.add(at(1200, leftSlice))
.add(at(1200, leftFill))
.add(at(1200, burst)); The squirmy line effect that everything has is known as squigglevision. I used the code from this example.
<main class="overflow-hidden vh-100 flex items-center justify-center" style="background-color: #2C2D3D;">
<svg class="w-100 mw7 center squiggle" viewBox="0 0 800 600" fill="#EEB65A">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" style="display:none">
<defs>
<filter id="squiggly-0">
<feTurbulence baseFrequency="0.02" numOctaves="3" result="noise" seed="0" />
<feDisplacementMap in="SourceGraphic" in2="noise" scale="2" />
</filter>
<filter id="squiggly-1">
<feTurbulence baseFrequency="0.02" numOctaves="3" result="noise" seed="1" />
<feDisplacementMap in="SourceGraphic" in2="noise" scale="3" />
</filter>
<filter id="squiggly-2">
<feTurbulence baseFrequency="0.02" numOctaves="3" result="noise" seed="2" />
<feDisplacementMap in="SourceGraphic" in2="noise" scale="2" />
</filter>
<filter id="squiggly-3">
<feTurbulence baseFrequency="0.02" numOctaves="3" result="noise" seed="3" />
<feDisplacementMap in="SourceGraphic" in2="noise" scale="3" />
</filter>
<filter id="squiggly-4">
<feTurbulence baseFrequency="0.02" numOctaves="3" result="noise" seed="4" />
<feDisplacementMap in="SourceGraphic" in2="noise" scale="1" />
</filter>
</defs>
</svg>
<circle cx="400" cy="300" r="113" fill="#EEB65A" />
</svg>
</main> .squiggle {
animation: squigglevision 0.3s infinite;
}
/**
* from: https://tympanus.net/codrops/2016/03/21/animated-animals-css-svg/
*/
@keyframes squigglevision {
0 {
filter: url("#squiggly-0");
}
25% {
filter: url("#squiggly-1");
}
50% {
filter: url("#squiggly-2");
}
75% {
filter: url("#squiggly-3");
}
100% {
filter: url("#squiggly-4");
}
} The rest is all about matching the movement and timing of the original animation.
Here are a couple more examples of animating clipped shapes. For these I used a slightly different technique. They have shapes which are filled with an animated SVG pattern.
<article style="background-color: #F0E2D5;" class="sans-serif dark-gray flex flex-column pv3 pv4-ns ph3 center relative 📕">
<svg class="absolute top-1 left-1 w3 h3"
viewBox="75 -8.8 416.9 584.6"
enable-background="new 75 -8.8 416.9 584.6"
xml:space="preserve">
<path fill="#3C9285" d="M283.3,7.7h-0.4C100.4,8.1,91.4,235.7,91.4,281.2c0.4,124.6,50.6,270.4,192.3,270.4h0.4
c141.6-0.4,191.9-146.7,191.5-272C475.5,234.2,465.8,7.7,283.3,7.7z"/>
<path fill="#282828" d="M321.8,103.5c-5-18.3-18.7-33.9-37.7-39.3c-14-3.8-26.8-3.5-43.6,3.5c-25.7,11.3-37,40.5-33.1,45.5
c2.8,3.2,10.5-0.4,11.3,0.8c0.4,0.8-5.8,7.8-3.2,12.4c0.8,1.6,4.2,2.8,10.9,0.8c0,12.8,8.6,24.5,16,36.6
c14.4,24.1,28,46.3,22.6,58.7c-3.8,8.2-13.6,17.9-38.5,44.3c-63.9,68.5-50.2,156.4-39.3,158.4c7.8,1.6,28.4-9.8,29.2-9.4
c12.8,6.6,32.7,8.6,34.6,10.9c3.5,3.5,3.5,37,1.2,42c-3.5,8.2-21.8,15.2-18.3,17.9c5,4.2,14-2.4,22.6-1.6c6.2,0.8,11.3,9,16.4,9
c5.8,0.4,2-9.8,12-8.2c9,1.2,14.8,4.2,19.4,2c5.8-3.2-22.6-12.8-31.1-16.4c-4.2-2-4.6-11.3-4.2-16c0.4-9,2.8-17.1,3.5-20.2
c2.8-11.3,8.6-10.9,12.4-11.6c4.6-0.8,7,1.2,9,9.4c2.4,10.2,3.5,17.1,3.5,19.8c0,13.2-13.6,16.8-10.2,20.2c5,4.6,13.6-3.2,22.6-1.6
s10.5,8.6,17.5,7.8c3.8-0.4,1.2-6.6,4.6-6.6c7.4-0.4,18.7,4.2,23,2c5.4-3.2-13.2-10.2-21-13.2c-9-3.2-17.1-5.4-19-9
c-0.8-1.6-2-8.6-2.8-19.4c-1.2-13.2,0.8-20.2,7-25.3c23-17.5,39.7-39.3,48.7-70.9c20.6-70.9-37-135-35.4-136.2
c0,0,17.5,2.4,41.3,16.4c13.6,8.2,28,23,28,23c28-20.2-1.2-44.3-26.8-71.6C352.9,145.1,323.4,109.7,321.8,103.5z"/>
<g>
<path fill="#F0E2D5" d="M317.2,113.9c0,0,7.4,12,17.9,24.9c15.6,19.4,52.1,57.2,61.1,67.3C407,218.6,402,224,402,224
s-1.2-3.2-6.2-7.8c-2.4-2.4-87.2-91.4-87.2-91.4l3.2-3.8l12.8,12.4l-9.8-15.6L317.2,113.9z"/>
<path fill="#F0E2D5" d="M301.6,133.4c-2.4,1.2-7,6.6-7,12.4c0,10.2,12.4,32.7,45.9,42.8c30.4,9.4,34.2,20.2,35,18.3
C376.7,205.4,301.6,133.4,301.6,133.4z"/>
</g>
<path fill="#F0E2D5" d="M347.1,257.2c-10.2-25.7-31.1-49.8-44.7-69.7c-8.6-12-28-37.3-16.4-57.5c7-11.6,19.8-16.8,19-29.2
c-0.8-12-16.4-27.6-40.9-25.7c-27.6,0.4-42,24.9-39.3,28c1.6,1.6,9.8-1.6,10.5,0c0.4,1.2-5.4,7-2.4,9.8c1.6,1.6,6.6,0,9.4-1.2
c-3.2,23.4,9,37.3,19.4,52.9c19.4,28.4,30,57.9,11.3,78.6c-13.2,14.8-26.1,29.2-44.7,53.3c-13.6,19.4-20.2,39.7-22.2,57.2
c-2.8,21.8,4.6,35.4,9,33.5c9.8-3.8,15.6-49.8,17.5-51.3c2-2,3.5,36.2,10.2,32.3c9.4-5.4,10.5-49.8,12-51.3
c1.6-1.6,4.2,33.9,10.2,30.4c9-5,10.5-47.5,11.6-49.1c1.2-2,3.8,21,9.8,21.4c7.4,0.4,11.6-34.2,11.6-49.1
c3.2-3.5,20.2,50.2-24.5,98.1c-16.8,17.9-21.8,21.8-38.1,33.5c4.6,3.5,21.8,8.6,43.2,4.6c28.8-5,53.3-27.6,66.9-60.3
C357.2,318.3,360.3,291,347.1,257.2z"/>
<path fill="#282828" d="M289.1,94.1c-8.2-6.2-23,2-21.8,4.6c1.2,2.7,4.6,0,10.2-0.4c-0.4,0.8-0.4,2-0.8,2.8
c-0.4,4.6,2.8,8.2,7.4,8.6c4.6,0.4,8.6-3.2,8.6-7.8C293,98.7,291.8,95.7,289.1,94.1z"/>
<path fill="#282828" d="M282.9,3.8C95.3,4.2,87.6,238.1,87.6,281.2c0.4,130.4,53.3,274.4,196.5,274.4
c143.2-0.4,195.7-145.5,195.3-275.9C479.4,236.5,470.5,3.4,282.9,3.8z M284.1,547.4C148.3,547.8,111.4,403,111.4,279.4
c0-40.9-7-268.9,171.2-268.9c178.1-0.4,173,228.8,173.4,269.6C456,403.8,419.8,547,284.1,547.4z"/>
</svg>
<header class="mb5 flex-auto">
<h3 style="color: #3C9285;" class="f3 fw4 tc mt0 mb4">a Pelican Book</h3>
<h1 class="f2 f1-ns tc mv0">The Doctor<br/>and the Soul</h1>
<h2 class="f4 fw4 tc mv2">From Psycotherapy to Logotherapy</h2>
<h3 style="color: #3C9285;" class="f4 f3-ns fw4 tc mv0">Viktor E. Frankl</h3>
</header>
<figure class="ma0 aspect-ratio aspect-ratio--1x1">
<svg viewBox="0 0 100 100" class="db aspect-ratio--object cover">
<defs>
<pattern id="📕-pattern0" width="100" height="100" patternUnits="userSpaceOnUse" fill="none">
<circle cx="50" cy="50" r="15" fill="#3C2359" />
<circle cx="50" cy="50" r="8" fill="#CC3275" />
<circle cx="50" cy="50" r="1" fill="#3C9285" />
</pattern>
<pattern id="📕-pattern1" width="100" height="100" patternUnits="userSpaceOnUse" fill="none">
<circle cx="50" cy="50" r="15" fill="#CC3275" />
<circle cx="50" cy="50" r="8" fill="#3C2359" />
<circle cx="50" cy="50" r="1" fill="#3C9285" />
</pattern>
<pattern id="📕-pattern2" width="100" height="100" patternUnits="userSpaceOnUse" fill="none">
<circle cx="50" cy="50" r="50" fill="#3C9285" />
<circle cx="50" cy="50" r="43" fill="#CC3275" />
<circle cx="50" cy="50" r="36" fill="#3C2359" />
<circle cx="50" cy="50" r="29" fill="#3C9285" />
<circle cx="50" cy="50" r="22" fill="#CC3275" />
<circle cx="50" cy="50" r="15" fill="#3C2359" />
</pattern>
<pattern id="📕-pattern3" width="100" height="100" patternUnits="userSpaceOnUse" fill="none">
<circle cx="50" cy="50" r="36" fill="#3C9285" />
<circle cx="50" cy="50" r="29" fill="#CC3275" />
<circle cx="50" cy="50" r="22" fill="#3C2359" />
</pattern>
<pattern id="📕-pattern4" width="100" height="100" patternUnits="userSpaceOnUse" fill="none">
<circle cx="50" cy="50" r="43" fill="#3C9285" />
<circle cx="50" cy="50" r="43" fill="#3C9285" />
<circle cx="50" cy="50" r="36" fill="#CC3275" />
<circle cx="50" cy="50" r="29" fill="#3C2359" />
</pattern>
<pattern id="📕-pattern5" width="100" height="100" patternUnits="userSpaceOnUse" fill="none">
<circle cx="50" cy="50" r="64" fill="#3C9285" />
<circle cx="50" cy="50" r="57" fill="#3C9285" />
<circle cx="50" cy="50" r="50" fill="#CC3275" />
<circle cx="50" cy="50" r="43" fill="#3C2359" />
</pattern>
<pattern id="📕-pattern6" width="100" height="100" patternUnits="userSpaceOnUse" fill="none">
<circle cx="50" cy="50" r="78" fill="#3C9285" />
<circle cx="50" cy="50" r="71" fill="#CC3275" />
<circle cx="50" cy="50" r="64" fill="#3C9285" />
<circle cx="50" cy="50" r="57" fill="#CC3275" />
<circle cx="50" cy="50" r="50" fill="#3C9285" />
<circle cx="50" cy="50" r="43" fill="#CC3275" />
<circle cx="50" cy="50" r="36" fill="#3C9285" />
</pattern>
</defs>
<g>
<rect x="0" y="0" width="100" height="100" fill="url(#📕-pattern6)" />
<rect x="7" y="7" width="86" height="86" fill="url(#📕-pattern5)" />
<rect class="🎯" x="14" y="14" width="72" height="72" fill="url(#📕-pattern2)" />
<rect class="🎯" x="21" y="21" width="58" height="58" fill="url(#📕-pattern4)" />
<rect class="🎯" x="28" y="28" width="44" height="44" fill="url(#📕-pattern3)" />
<rect class="🎯" x="35" y="35" width="30" height="30" fill="url(#📕-pattern2)" />
<rect class="🎯" x="42" y="42" width="16" height="16" fill="url(#📕-pattern1)" />
<rect class="🎯" x="49" y="49" width="2" height="2" fill="url(#📕-pattern0)" />
<circle cx="50" cy="50" r="8" fill="#F0E2D5" />
</g>
</svg>
</figure>
</article> .grid {
display: grid;
}
.📓 {
width: 100vw;
height: calc(100vw * 6 / 4);
max-width: 38rem;
max-height: 57rem;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr auto;
}
.📘 {
width: 100vw;
height: calc(100vw * 1.2);
max-width: 38rem;
max-height: 45.6rem;
}
.📕 {
width: 100vw;
height: calc(100vw * 1.658);
max-width: 38rem;
max-height: 63.004rem;
}
.full-justify {
text-align: justify;
}
.full-justify:after {
content: "";
display: inline-block;
width: 100%;
}
/**
* Col and row spans
*/
.col-start-1 {
grid-column-start: 1;
}
.row-start-1 {
grid-row-start: 1;
}
.col-start-2 {
grid-column-start: 2;
}
.row-start-2 {
grid-row-start: 2;
}
.wide-1 {
grid-column-end: span 1;
}
.wide-2 {
grid-column-end: span 2;
}
.tall-1 {
grid-row-end: span 1;
}
.tall-2 {
grid-row-end: span 2;
} // The doctor and the soul
anime({
targets: '.🎯',
width: '+=14',
height: '+=14',
x: '-=7',
y: '-=7',
easing: 'linear',
duration: 300,
loop: true,
});
anime({
targets: '.📕 pattern circle',
r: '+=7',
easing: 'linear',
duration: 300,
loop: true,
});
anime({
targets: '#📕-pattern6 circle',
r: '+=14',
easing: 'linear',
duration: 300,
loop: true,
}); <main class="avenir flex dark-gray justify-center vh-100">
<article class="bg-near-white pa3 pa4-ns mw6 grid 📘">
<h1 class="lh-title f5 f2-ns mv0 ph3 ph4-ns row-start-1 col-start-1 wide-2 flex items-center"> The New Technology and Human Values
</h1>
<h2 class="lh-title f6 f5-ns fw4 mt0 mb3 mb4-ns ttu tracked row-start-1 col-start-2 wide-1 flex items-end">
John G. Burke
</h2>
<figure class="db bg-blue ma0 wide-2 tall-2">
<svg viewBox="0 0 200 200" class="db h-100">
<defs>
<pattern id="pattern1" x="90" width="10" height="200" patternUnits="userSpaceOnUse">
<rect x="0" y="0" width="5" height="200" fill="#357EDD" />
<rect x="5" y="0" width="5" height="200" fill="white" />
<rect x="10" y="0" width="5" height="200" fill="#357EDD" />
<rect x="15" y="0" width="5" height="200" fill="white" />
</pattern>
<pattern id="pattern2" x="95" width="10" height="200" patternUnits="userSpaceOnUse">
<rect x="0" y="0" width="5" height="200" fill="#357EDD" />
<rect x="5" y="0" width="5" height="200" fill="white" />
<rect x="10" y="0" width="5" height="200" fill="#357EDD" />
<rect x="15" y="0" width="5" height="200" fill="white" />
</pattern>
</defs>
<g>
<circle cx="100" cy="100" r="95" fill="url(#pattern2)" />
<circle cx="100" cy="100" r="90" fill="url(#pattern1)" />
<circle cx="100" cy="100" r="85" fill="url(#pattern2)" />
<circle cx="100" cy="100" r="80" fill="url(#pattern1)" />
<circle cx="100" cy="100" r="75" fill="url(#pattern2)" />
<circle cx="100" cy="100" r="70" fill="url(#pattern1)" />
<circle cx="100" cy="100" r="65" fill="url(#pattern2)" />
<circle cx="100" cy="100" r="60" fill="url(#pattern1)" />
<circle cx="100" cy="100" r="55" fill="url(#pattern2)" />
<circle cx="100" cy="100" r="50" fill="url(#pattern1)" />
<circle cx="100" cy="100" r="45" fill="url(#pattern2)" />
<circle cx="100" cy="100" r="40" fill="url(#pattern1)" />
<circle cx="100" cy="100" r="35" fill="url(#pattern2)" />
<circle cx="100" cy="100" r="30" fill="url(#pattern1)" />
<circle cx="100" cy="100" r="25" fill="url(#pattern2)" />
<circle cx="100" cy="100" r="20" fill="url(#pattern1)" />
<circle cx="100" cy="100" r="15" fill="url(#pattern2)" />
<circle cx="100" cy="100" r="10" fill="url(#pattern1)" />
<circle cx="100" cy="100" r="5" fill="url(#pattern2)" />
</g>
</svg>
</figure>
</article>
</main> .grid {
display: grid;
}
.📘 {
width: 100vh;
height: calc(100vw * 6 / 4);
max-height: 48rem;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr auto;
}
/**
* Col and row spans
*/
.col-start-1 {
grid-column-start: 1;
}
.row-start-1 {
grid-row-start: 1;
}
.col-start-2 {
grid-column-start: 2;
}
.row-start-2 {
grid-row-start: 2;
}
.wide-1 {
grid-column-end: span 1;
}
.wide-2 {
grid-column-end: span 2;
}
.tall-1 {
grid-row-end: span 1;
}
.tall-2 {
grid-row-end: span 2;
} anime({
targets: ['#pattern1 rect', '#pattern2 rect'],
x: '-=10',
easing: 'linear',
duration: 2000,
loop: true,
});