How to scale SVG path to fit the window size? - css

I am having troubles to scale the SVG to fit the window size.
In this example, I have a wavy path and a text element, what I want to achieve here is to move the text element along the wavy path from left to right (which is done by GSAP) and stop in the half way of the path in the initial load; it will move to the end when users start scrolling.
My problem is that, the wavy path created by SVG is too long, even the half way of the path will be off the window, I tried to scale down the wavy path by using viewBox, failed; using css width: 100vw, failed.
I also used the css transform property, it did scale the wavy path down, but that was a fixed size, I want to make it as responsive as possible, which means regardless the window width, the text element always stops in the middle of the screen first ( half way of the wavy path ), then moves to the right hand side of the screen. Is this possible when using inline SVG element? If so, please point me in the right direction.
Thank you in advance!
(Please view the example in full page mode, that will explain my problem perfectly, but the scroll function will be disabled because the height in that mode is 100vh, there is no room for scrolling)
document.getElementById("MyPath").setAttribute("d", document.getElementById("Path").getAttribute("d"));
var tl = new TimelineMax({
repeat: 0,
delay: 1
});
tl.to("#Text", 3, {
attr: {
startOffset: '50%',
opacity: 1
}
});
window.addEventListener('scroll', function() {
tl.to("#Text", 3, {
attr: {
startOffset: '100%',
opacity: 0
}
});
}, true);
#import url(https://fonts.googleapis.com/css?family=Oswald);
body {
background-color: #222;
}
svg {
overflow: visible;
width: 100%;
height: 100%;
background-color: #fff;
left: 0;
top: 0;
position: absolute;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.17.0/plugins/TextPlugin.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/gsap/1.17.0/TweenMax.min.js"></script>
<svg xml:space="preserve">
<defs><path id="MyPath"/></defs>
<path id="Path" fill="none" stroke="#000" stroke-miterlimit="10" d="M0,188.2c186.2,84,261.8,84.9,440,66.4s295.2-130,535.2-129.4c240,0.6,357,144.3,591.1,144.3
s450.1-141.2,651.1-141.2c271.7,0,354.2,141.2,612.1,141.2c240,0,423-141.2,669.1-141.2c119.1,0,202.3,33.8,281,68.7"/>
<text font-size="7em" >
<textPath id="Text" fill='#88CE02' font-family=Oswald xlink:href="#MyPath" opacity=0 startOffset="0%" letter-spacing="5px">Love the little things.</textPath>
</text>
</svg>

If you want your SVG to scale to fit the screen (or any parent container), it needs to have a viewBox attribute. This attribute tells the browser the dimensions of the SVG content. Without it, the browser has know way of knowing how much it needs to be scaled.
Your path is about 3780 width, and the bottom of it is at y=144. So a reasonable value of viewBox would be:
viewBox="0 0 3780 150"
document.getElementById("MyPath").setAttribute("d", document.getElementById("Path").getAttribute("d"));
var tl = new TimelineMax({
repeat: 0,
delay: 1
});
tl.to("#Text", 3, {
attr: {
startOffset: '50%',
opacity: 1
}
});
window.addEventListener('scroll', function() {
tl.to("#Text", 3, {
attr: {
startOffset: '100%',
opacity: 0
}
});
}, true);
#import url(https://fonts.googleapis.com/css?family=Oswald);
body {
background-color: #222;
}
svg {
overflow: visible;
width: 100%;
height: 100%;
background-color: #fff;
left: 0;
top: 0;
position: absolute;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.17.0/plugins/TextPlugin.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/gsap/1.17.0/TweenMax.min.js"></script>
<svg viewBox="0 0 3780 150" xml:space="preserve">
<defs><path id="MyPath"/></defs>
<path id="Path" fill="none" stroke="#000" stroke-miterlimit="10" d="M0,188.2c186.2,84,261.8,84.9,440,66.4s295.2-130,535.2-129.4c240,0.6,357,144.3,591.1,144.3
s450.1-141.2,651.1-141.2c271.7,0,354.2,141.2,612.1,141.2c240,0,423-141.2,669.1-141.2c119.1,0,202.3,33.8,281,68.7"/>
<text font-size="7em" >
<textPath id="Text" fill='#88CE02' font-family=Oswald xlink:href="#MyPath" opacity=0 startOffset="0%" letter-spacing="5px">Love the little things.</textPath>
</text>
</svg>

Here's what you could do: add a media query defining the desired width and then apply the following styles separately for the text and path elements inside the svg element:
text {
font-size: 2em;
}
path {
-webkit-transform: scale(.3);
-ms-transform: scale(.3);
transform: scale(.3);
}
See the snippet below:
document.getElementById("MyPath").setAttribute("d", document.getElementById("Path").getAttribute("d"));
var tl = new TimelineMax({
repeat: 0,
delay: 1
});
tl.to("#Text", 3, {
attr: {
startOffset: '50%',
opacity: 1
}
});
window.addEventListener('scroll', function() {
tl.to("#Text", 3, {
attr: {
startOffset: '100%',
opacity: 0
}
});
}, true);
#import url(https://fonts.googleapis.com/css?family=Oswald);
body {
background-color: #222;
}
svg {
overflow: visible;
width: 100%;
height: 100%;
background-color: #fff;
left: 0;
top: 0;
position: absolute;
}
#media only screen and (max-width: 500px) {
text {
font-size: 2em;
}
path {
-webkit-transform: scale(.3);
-ms-transform: scale(.3);
transform: scale(.3);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.17.0/plugins/TextPlugin.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/gsap/1.17.0/TweenMax.min.js"></script>
<svg xml:space="preserve">
<defs><path id="MyPath"/></defs>
<path id="Path" fill="none" stroke="#000" stroke-miterlimit="10" d="M0,188.2c186.2,84,261.8,84.9,440,66.4s295.2-130,535.2-129.4c240,0.6,357,144.3,591.1,144.3
s450.1-141.2,651.1-141.2c271.7,0,354.2,141.2,612.1,141.2c240,0,423-141.2,669.1-141.2c119.1,0,202.3,33.8,281,68.7"/>
<text font-size="7em" >
<textPath id="Text" fill='#88CE02' font-family=Oswald xlink:href="#MyPath" opacity=0 startOffset="0%" letter-spacing="5px">Love the little things.</textPath>
</text>
</svg>
I hope it helps!

Related

Vue3 - How to use a custom ID in CSS url()?

I have a vue component where I define an id for clipping :id="'clipping_'+ UID". But when I try to use it in CSS clip-path: url(v-bind("'#clipping_' + UID");, it is seen as a var(), which is not allowed with url().
what would be the correct way to declare or access this id?
Thanks.
<template>
<div>
<svg class="svg">
<clipPath
:id="'clipping_'+ UID"
clipPathUnits="userSpaceOnUse">
<circle
cx= "20"
cy= "20"
r="20"/>
</clipPath>
</svg>
<div class="foreground"/>
</div>
</template>
<script>
export default
{
name: 'myComponent',
data: function ()
{
return {
UID: "asdf",
};
}
};
</script>
<style scoped>
.foreground {
width: 100%;
height: 40px;
background: hsl(356, 0%, 53%);
clip-path: url(v-bind("'#clipping_' + UID");
-webkit-clip-path: url(v-bind("'#clipping_' + UID");
}
.svg {
width: 100%;
height: 40px;
}
</style>
I just found that if url() can't have a var() in it, the opposite is possible.
clip-path: v-bind("'url(#clipping_' + UID + ')'");

Why SVG rotation was not centered?

I have a simple SVG image. There is a circle inside which I would like to rotate 90 degrees. But as you can see in JSFiddle example center of the rotation is not in the middle. What am I doing wrong?
svg {
background-color: #ddd;
}
.circle1 {
fill: none;
stroke-width: 20px;
stroke: #b5b5b5;
}
.circle2 {
fill: #aaa;
transform: rotate(-20deg);
stroke-width: 15px;
stroke: #71c7ff;
}
<svg height="600" width="600">
<circle class="circle1" cx="300" cy="300" r="200"></circle>
<circle class="circle2" cx="300" cy="300" r="200"></circle>
</svg>
Here is JSFiddle example https://jsfiddle.net/oxt681gr/1/
By default, the origin of a transform should be 0px 0px, but now we have wrong info on MDN:
By default, the origin of a transform is center.
Source-link
It is wrong. But in the case if it would be in center of your circle you would not able to see the result of your rotation because you will rotate the circle in his center.
You can change the rotation center with CSS property transform-origin or with SVG transform attribute. In SVG transform attribute we have the default also on 0px 0px. To rotate SVG (without CSS) use transform="rotate(deg, cx, cy)", where deg is the degree you want to rotate and (cx, cy) define the center of rotation.
In my solution you can change it interactive with CSS and JS:
var input = document.querySelector('input'),
circle2 = document.querySelector('.circle2'),
output = document.querySelector('span');
input.onchange = function(e)
{
var newTransformOrigin = e.target.value;
output.innerHTML = newTransformOrigin + 'px' + ' ' + newTransformOrigin + 'px';
circle2.style.transformOrigin = output.innerHTML;
};
svg {background-color:#ddd}
.circle1 {fill: none; stroke-width: 20px; stroke: #b5b5b5}
.circle2
{
fill: #aaa;
transform-origin: 0 0;
transform: rotate(-20deg);
stroke-width: 15px;
stroke: #71c7ff;
}
<p>Change transform-origin <input type="range" min="0" max="600" value="0" step="50">
<span>0px 0px</span></p>
<svg height="600" width="600">
<circle class="circle1" cx="300" cy="300" r="200"></circle>
<circle class="circle2" cx="300" cy="300" r="200"></circle>
</svg>
Please change to full page to see the example.

Cannot override class properties inside use Tag

I need to change SVG fill property by adding up a class. Say from #fff to #000. I tried fiddling with two different methods. I got successful with one of either method. Rather I don't prefer the method to use which I was successful.
Method 1: - Succesful.
I just added a class on the body tag and over-ride the svg fill property. Take a look at the code below
var button = document.getElementById("btn");
var body = document.body
var count = false;
button.onclick = changeBackground;
function changeBackground() {
body.classList.toggle('toggle');
}
body {
background: #3d3d3d;
}
.sheetListWhite {
fill: #fff;
}
.slBGColor {
fill: #3d3d3d;
}
.toggle {
background: #fff;
}
.toggle .sheetListWhite {
fill: #3d3d3d;
}
.toggle .slBGColor {
fill: #fff;
}
<svg width="18px" height="18px" viewBox="-2 -2 13 13">
<use xlink:href="#sharedByMe" class="sheetListWhite"></use>
</svg>
<button id="btn"> Change background</button>
<svg style="display:none;">
<symbol id="sharedByMe">
<g id="user-(5)">
<path d="M5.76735992,5.07170748 C6.58617985,4.59194553 7.12018634,3.73525063 7.12018634,2.74147173 C7.12020298,1.23365988 5.8385508,0 4.27211846,0 C2.70568612,0 1.42403394,1.23365988 1.42403394,2.74147173 C1.42403394,3.73525063 1.95804042,4.59196155 2.77686035,5.07170748 C1.35282642,5.58574044 0.284796801,6.78512932 0,8.22439918 L0.712025292,8.22439918 C1.06802962,6.64806134 2.52768396,5.48292744 4.27211846,5.48292744 C6.01655296,5.48292744 7.47620731,6.64804532 7.83221163,8.22439918 L8.54423692,8.22439918 C8.25944012,6.75085832 7.19141051,5.55146944 5.76735992,5.07170748 Z M2.13605923,2.74147173 C2.13605923,1.61062486 3.09729421,0.685371939 4.27211846,0.685371939 C5.44694272,0.685371939 6.40817769,1.61062486 6.40817769,2.74147173 C6.40817769,3.87231861 5.44694272,4.79757153 4.27211846,4.79757153 C3.09729421,4.79757153 2.13605923,3.87231861 2.13605923,2.74147173 Z" id="Shape"></path>
</g>
<g id="shared-folder" transform="translate(6.111111, 5.294118)">
<ellipse id="Oval-6" class="slBGColor" cx="2.44444444" cy="2.35294118" rx="2.44444444" ry="2.35294118"></ellipse>
<path d="M3.54444101,1.45883032 C3.94870693,1.45883032 4.27777778,1.13170727 4.27777778,0.729415161 C4.27777778,0.327123049 3.94870693,0 3.54444101,0 C3.14017508,0 2.81110424,0.327140144 2.81110424,0.729415161 C2.81110424,0.781778783 2.81720578,0.832655093 2.82779325,0.881924423 L1.89436023,1.34613656 C1.7598169,1.19301186 1.56441282,1.09413129 1.34443069,1.09413129 C0.940181958,1.09411419 0.611111111,1.42125434 0.611111111,1.82352936 C0.611111111,2.22580437 0.940181958,2.55294452 1.34444788,2.55294452 C1.56439563,2.55294452 1.75974815,2.45413233 1.89429148,2.30105891 L2.82781043,2.76516848 C2.81720578,2.81442071 2.81110424,2.86527993 2.81110424,2.91764355 C2.81110424,3.31991857 3.14017508,3.64705871 3.54444101,3.64705871 C3.94870693,3.64705871 4.27777778,3.31993566 4.27777778,2.91764355 C4.27777778,2.51536853 3.94870693,2.18822839 3.54444101,2.18822839 C3.32445888,2.18822839 3.12903762,2.28710896 2.99449429,2.44025076 L2.06104408,1.97615829 C2.07164873,1.92685476 2.07778465,1.87594426 2.07778465,1.82352936 C2.07778465,1.77116573 2.07168311,1.72028942 2.06109564,1.67102009 L2.99452866,1.20680796 C3.12905481,1.35994975 3.32444169,1.45883032 3.54444101,1.45883032 Z" id="Shape"></path>
</g>
</symbol>
</svg>
See the fiddle HERE
Method 2 - Failed
Instead of adding the class directly to the body tag i added it on a div.
var button = document.getElementById("btn");
var body = document.getElementById("check");
var count = false;
button.onclick = changeBackground;
function changeBackground() {
body.classList.toggle('toggle');
}
div {
height: 300px;
width: 100%;
background: #3d3d3d
}
.sheetListWhite {
fill: #fff;
}
.slBGColor {
fill: #3d3d3d;
}
.toggle {
background: #fff;
}
.toggle .sheetListWhite {
background: #3d3d3d
}
.toggle .slBGColor {
fill: #fff;
}
<div id="check">
<svg width="18px" height="18px" viewBox="-2 -2 13 13">
<use xlink:href="#sharedByMe" class="sheetListWhite"></use>
</svg>
<button id="btn"> Change background</button>
</div>
<svg style="display:none;">
<symbol id="sharedByMe">
<g id="user-(5)">
<path d="M5.76735992,5.07170748 C6.58617985,4.59194553 7.12018634,3.73525063 7.12018634,2.74147173 C7.12020298,1.23365988 5.8385508,0 4.27211846,0 C2.70568612,0 1.42403394,1.23365988 1.42403394,2.74147173 C1.42403394,3.73525063 1.95804042,4.59196155 2.77686035,5.07170748 C1.35282642,5.58574044 0.284796801,6.78512932 0,8.22439918 L0.712025292,8.22439918 C1.06802962,6.64806134 2.52768396,5.48292744 4.27211846,5.48292744 C6.01655296,5.48292744 7.47620731,6.64804532 7.83221163,8.22439918 L8.54423692,8.22439918 C8.25944012,6.75085832 7.19141051,5.55146944 5.76735992,5.07170748 Z M2.13605923,2.74147173 C2.13605923,1.61062486 3.09729421,0.685371939 4.27211846,0.685371939 C5.44694272,0.685371939 6.40817769,1.61062486 6.40817769,2.74147173 C6.40817769,3.87231861 5.44694272,4.79757153 4.27211846,4.79757153 C3.09729421,4.79757153 2.13605923,3.87231861 2.13605923,2.74147173 Z" id="Shape"></path>
</g>
<g id="shared-folder" transform="translate(6.111111, 5.294118)">
<ellipse id="Oval-6" class="slBGColor" cx="2.44444444" cy="2.35294118" rx="2.44444444" ry="2.35294118"></ellipse>
<path d="M3.54444101,1.45883032 C3.94870693,1.45883032 4.27777778,1.13170727 4.27777778,0.729415161 C4.27777778,0.327123049 3.94870693,0 3.54444101,0 C3.14017508,0 2.81110424,0.327140144 2.81110424,0.729415161 C2.81110424,0.781778783 2.81720578,0.832655093 2.82779325,0.881924423 L1.89436023,1.34613656 C1.7598169,1.19301186 1.56441282,1.09413129 1.34443069,1.09413129 C0.940181958,1.09411419 0.611111111,1.42125434 0.611111111,1.82352936 C0.611111111,2.22580437 0.940181958,2.55294452 1.34444788,2.55294452 C1.56439563,2.55294452 1.75974815,2.45413233 1.89429148,2.30105891 L2.82781043,2.76516848 C2.81720578,2.81442071 2.81110424,2.86527993 2.81110424,2.91764355 C2.81110424,3.31991857 3.14017508,3.64705871 3.54444101,3.64705871 C3.94870693,3.64705871 4.27777778,3.31993566 4.27777778,2.91764355 C4.27777778,2.51536853 3.94870693,2.18822839 3.54444101,2.18822839 C3.32445888,2.18822839 3.12903762,2.28710896 2.99449429,2.44025076 L2.06104408,1.97615829 C2.07164873,1.92685476 2.07778465,1.87594426 2.07778465,1.82352936 C2.07778465,1.77116573 2.07168311,1.72028942 2.06109564,1.67102009 L2.99452866,1.20680796 C3.12905481,1.35994975 3.32444169,1.45883032 3.54444101,1.45883032 Z" id="Shape"></path>
</g>
</symbol>
</svg>
Fiddle it HERE
Now you can see I can't override the following classes .slBGColor, .sheetListWhite
How can I get rid of this bug? Any Idea. I don't want to use the svg tag directly instead of use tag. I mean I would like to use all of my SVG from an external resource. Any Help
Thanks.
The "insides" of a <use> cannot be addressed via the <use> element. It is considered opaque/private. As far as the browser is concerned, there is no class="slBGColor" inside the <div>. So .toggle .slBGColor {} does nothing.
You can make it work by restyling the symbol definition itself.
For example, if I make the following two changes to your example, the ellipse in the symbol changes colour based on whether the "toggle" class is set or not.
var body = document.body;
.toggle div {
background: red;
}
https://jsfiddle.net/nuorcj0a/4/
However, be aware that will also change any other references to that symbol on the page.

Transition svg element with css

Problem
I'm trying to simply move an svg element using transform=matrix(a,b,c,d,e,f)
The element is moving fine but when I apply a css transition it has no effect.
Question
Is this possible on svg elements without an external library like d3?
Code I'm trying
html:
<svg width="100%" height="100%" viewbox="0 0 200 200">
<rect x="20" y="20" width="50" height="50"
style="fill: #3333cc"
transform="matrix(1,0,0,1,1,1)"
id="blueBox"
/>
</svg>
<button id="boxMover">
Move it
</button>
jQuery
$(function(){
$('#boxMover').click(function(){
var blueBox = $('#blueBox');
if(blueBox.attr('transform')=='matrix(1,0,0,1,1,1)'){
blueBox.attr('transform', 'matrix(1,0,0,1,100,30)');
} else {
blueBox.attr('transform', 'matrix(1,0,0,1,1,1)');
}
})
})
CSS
svg {
display: block
}
#boxMover {
position: absolute;
left: 20px;
top: 20px;
transition: transform .5s ease;
}
Here's a fiddle I created to test it
A simpler solution:
Create the following class:
.boxEase {
transform: matrix(1,0,0,1,100,30);
transition: all .5s ease;
}
Change your jQuery code to just attach the above class to your box when the button is clicked:
$(function(){
$('#boxMover').click(function(){
var blueBox = $('#blueBox');
blueBox.attr('class', 'boxEase');
})
})
Added dynamic case with variable ending (starting) positions
Use the following jQuery code where transform and transition properties are added to the box conditionally. I imagine you can adjust the conditional to something else but I have used your original example for this case:
`$(function(){
$('#boxMover').click(function(){
var startPos = 'matrix(1,0,0,1,1,1)',
endPos = 'matrix(1,0,0,1,100,30)';
var blueBox = $('#blueBox');
if(blueBox.attr('transform') == startPos){
blueBox.attr('transform', endPos);
blueBox.css({'transform': endPos, 'transition': 'all 0.5s ease'});
} else {
blueBox.attr('transform', startPos);
blueBox.css({'transform': startPos, 'transition': 'all 0.5s ease'});
}
})
}); `
There's a horrible grey area between the CSS and SVG namespaces where things like this happen all the time.
However, you can fix your problem as follows:
Move the transform CSS statement out of the #boxMover rules and put it somewhere it can actually affect the behaviour of your #blueBox SVG element.
Changing the transform attributes of the SVG element directly still won't work because you're talking to the SVG namespace, and the CSS rules aren't having any say in the matter. Instead, set up CSS classes containing the required transform attributes, and switch between those instead. There is no class attribute in the SVG namespace, so the CSS rules will come to life.
Also note that you can't use JQuery's addClass() and removeClass() methods to change the class of an SVG element, so use attr('class') instead
This should work:
$(function() {
$('#boxMover').click(function() {
var blueBox = $('#blueBox');
if (blueBox.attr('class') == 'overHere') {
blueBox.attr('class', 'overThere');
} else {
blueBox.attr('class', 'overHere');
}
})
})
svg {
display: block
}
#boxMover {
position: absolute;
left: 20px;
top: 20px;
}
#blueBox {
transition: transform .5s ease;
}
.overHere {
transform: matrix(1, 0, 0, 1, 1, 1);
}
.overThere {
transform: matrix(1, 0, 0, 1, 100, 30);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<svg width="100%" height="100%" viewbox="0 0 200 200">
<rect x="20" y="20" width="50" height="50"
style="fill: #3333cc" class="overHere" id="blueBox" />
</svg>
<button id="boxMover">
Move it
</button>

How to use clip-path in an animation?

I've read a bunch of posts and been playing around with trying to animate a clip-path. At this point I can only get it show the clip-path in Firefox and it always pins the path to 0,0.
Here is an example code -> http://jsfiddle.net/justintense/4torLok9/1/
I'm using a simple inline SVG as the path:
<clipPath id="clipping">
<polygon fill="#FFFFFF" points="40,35 40,15 20,35 "/>
<polygon fill="#FFFFFF" points="0,35 20,35 0,15 "/>
<circle id="white_overlay_9_" fill="#FFFFFF" cx="20" cy="18.77" r="7.393"/>
</clipPath>
I'm unsure if what I trying to do is even possible so am just looking for pointer so I can get this one track.
Edit:
I'm trying to recreate the behaviour similar to this site --------> http://uppymama.com/
Defining the clipPath:
You don't need three elements in your clipPath, you could simplify it to a single path element.
<path d="M0,0 L40,0 L20,15z M20,5 m-5,0 a5,5 0 1,0 10,0 a5,5 0 1,0 -10,0z" />
Applying the clipPath to the div:
For applying the clip-path, if you want maximum browser support, you could import the div into an svg element through svg's foreignObject element. Then, apply the clip-path to the foreignObject.
Here's the browser support, if you apply the clip-path through CSS.
Animating the clipPath:
Now, for aligning the pointer right in the middle of the menu item, you'll need JavaScript to do some calculations and animate the clipPath's transform property.
When page loads calculate the distance from the left window corner to the first menu-item and move the clip-path to that co-ordinate.
When the window resizes, calculate the distance again and move the pointer.
When the menu item is clicked, calculate the distance and animate the pointer using setTimeout() and synchronous code until it reaches the correct position.
Synchronous code means that the operations are performed one at a time and that, until one operation is over, the code execution is blocked from moving onto the next operation.
Exact replica of that site:
As you can see on that site, the pointer isn't centered.
My solution will always center the pointer depending on the width of the menu item.
Demo on CodePen
var items = document.getElementsByClassName('menu-item');
var clipPath = document.getElementById('clip-path');
var lastActive = items[0];
for (i = 0; i < items.length; i++) {
items[i].addEventListener('click', function() {
var x = this.getBoundingClientRect().left + (this.offsetWidth / 2) - 20;
animStart(x);
lastActive = this;
})
}
function animStart(end) {
for (start = 0; start <= end; start += 0.1) {
anim(start);
}
}
function anim(start) {
setTimeout(function() {
clipPath.setAttribute('transform', 'translate(' + start + ', 0)');
}, 1 * start);
}
function align() {
var x = lastActive.getBoundingClientRect().left + (lastActive.offsetWidth / 2) - 20;
animStart(x);
}
function resize() {
var x = lastActive.getBoundingClientRect().left + (lastActive.offsetWidth / 2) - 20;
clipPath.setAttribute('transform', 'translate(' + x + ', 0)');
}
window.onresize = resize;
window.onload = align;
body {
background: #222222;
margin: 0;
}
.nav-container {
top: 20px;
position: relative;
width: 100%;
height: 50px;
background: repeating-linear-gradient(90deg, palegoldenrod 5px, palegreen 10px, palegreen 15px, paleturquoise 15px, paleturquoise 20px, plum 20px, plum 25px);
}
.nav {
width: 100%;
height: 50px;
margin: 0 auto;
text-align: center;
}
.menu-item {
display: inline-block;
width: 70px;
padding: 17px 20px;
color: #222222;
font-size: 14px;
}
.menu-item:hover {
color: seagreen;
cursor: pointer;
}
#bottom {
width: 100%;
height: 35px;
background: repeating-linear-gradient(90deg, palegoldenrod 5px, palegreen 10px, palegreen 15px, paleturquoise 15px, paleturquoise 20px, plum 20px, plum 25px);
}
#clip-path {
transition: 0.5s transform;
}
<body>
<div class="nav-container">
<div class="nav">
<div id="menu-item-1" class="menu-item">Home</div>
<div id="menu-item-2" class="menu-item">About</div>
<div id="menu-item-3" class="menu-item">Services</div>
<div id="menu-item-4" class="menu-item">Locations</div>
<div id="menu-item-5" class="menu-item">Contact Us</div>
</div>
<svg style="position: relative; top: -1px;" class="svg-defs" height="35" width="100%">
<defs>
<clipPath id="clipping">
<path id="clip-path" transform="translate(0,0)" d="M0,0 L40,0 L20,15z M20,6 m-5,0 a5,5 0 1,0 10,0 a5,5 0 1,0 -10,0z" />
</clipPath>
</defs>
<foreignObject x="0" y="0" clip-path="url(#clipping)" height="35" width="100%">
<div id="bottom"></div>
</foreignObject>
</svg>
</div>
</body>
Even Better:
Retaining the last position of the pointer.
Demo on CodePen
var items = document.getElementsByClassName('menu-item');
var clipPath = document.getElementById('clip-path');
var last = items[0];
for (i = 0; i < items.length; i++) {
items[i].addEventListener('click', function() {
var x = this.getBoundingClientRect().left + (this.offsetWidth / 2) - 20;
var currentX = clipPath.getAttribute('transform').replace('translate(', '');
currentX = parseInt(currentX.replace(', 0)', ''), 10);
animStart(currentX, x);
last = this;
})
}
function animStart(begin, end) {
if (begin < end) {
for (start = begin; start <= end; start += 0.1) {
anim(start);
}
} else {
var c = end;
for (start = begin; end <= start; start -= 0.1) {
animD(start, c);
c += 0.1
}
}
}
function anim(start) {
setTimeout(function() {
clipPath.setAttribute('transform', 'translate(' + start + ', 0)');
}, 1 * start);
}
function animD(start, c) {
setTimeout(function() {
clipPath.setAttribute('transform', 'translate(' + start + ', 0)');
}, 1 * c);
}
function align() {
var x = last.getBoundingClientRect().left + (last.offsetWidth / 2) - 20;
animStart(0, x);
}
function resize() {
var x = last.getBoundingClientRect().left + (last.offsetWidth / 2) - 20;
clipPath.setAttribute('transform', 'translate(' + x + ', 0)');
}
window.onresize = resize;
window.onload = align;
body {
background: #222222;
margin: 0;
}
.nav-container {
top: 20px;
position: relative;
width: 100%;
height: 50px;
background: repeating-linear-gradient(90deg, palegoldenrod 5px, palegreen 10px, palegreen 15px, paleturquoise 15px, paleturquoise 20px, plum 20px, plum 25px);
}
.nav {
width: 600px;
height: 50px;
margin: 0 auto;
text-align: center;
}
.menu-item {
display: inline-block;
width: 70px;
padding: 17px 20px;
color: #222222;
font-size: 14px;
}
.menu-item:hover {
color: seagreen;
cursor: pointer;
}
#bottom {
width: 100%;
height: 35px;
background: repeating-linear-gradient(90deg, palegoldenrod 5px, palegreen 10px, palegreen 15px, paleturquoise 15px, paleturquoise 20px, plum 20px, plum 25px);
}
<body>
<div class="nav-container">
<div class="nav">
<div id="menu-item-1" class="menu-item">Home</div>
<div id="menu-item-2" class="menu-item">About</div>
<div id="menu-item-3" class="menu-item">Services</div>
<div id="menu-item-4" class="menu-item">Locations</div>
<div id="menu-item-5" class="menu-item">Contact Us</div>
</div>
<svg style="position: relative; top: -1px;" class="svg-defs" height="35" width="100%">
<defs>
<clipPath id="clipping">
<path id="clip-path" transform="translate(0,0)" d="M0,0 L40,0 L20,15z M20,6 m-5,0 a5,5 0 1,0 10,0 a5,5 0 1,0 -10,0z" />
</clipPath>
</defs>
<foreignObject x="0" y="0" clip-path="url(#clipping)" height="35" width="100%">
<div id="bottom"></div>
</foreignObject>
</svg>
</div>
</body>

Resources