Resizing SVG animation while keeping it centered in its container - css

I'm trying to center the <path> animation horizontally and vertically (after having scaled it down with transform="scale(0.5)) while keeping the <svg> at 100% width and height of its container. I'm using Snap.svg.
As you can see below, the <svg> is fine but the <path> is all crammed up in the upper left corner.
var s = Snap("#me");
var myPath = s.select("#mypath");
function reset( el ) {
el.stop();
el.attr({ "stroke-dashoffset": 125 });
};
function startAnim( el ) {
el.animate( { "stroke-dashoffset": 600 }, 1000 );
};
reset( myPath );
s.mouseover( function() {
startAnim( myPath );
} );
s.mouseout( function() {
reset( myPath );
} );
.test {
border: 1px solid black;
height: 500px;
width: 500px;
}
#me {
border: 2px solid green;
}
#mypath {
border: 2px solid blue;
display: flex;
justify-content: center;
align-content: center;
flex-direction: column;
}
<script src="http://tedbeer.net/lib/snap.svg-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="test">
<div class="pie">
<svg id="me" viewBox="0 0 350 350">
<!--<svg id="me" viewBox="0 0 350 350" width="100" height="100">-->
<path id="mypath" d="M 175, 175 m 0, -75 a 75, 75 0 1, 0 0, 150 a 75, 75 0 1, 0 0, -150" fill="none" stroke="#ccc" stroke-width="150" stroke-dasharray="0 600 600 0" stroke-dashoffset="1000" transform="scale(0.5)">
</path>
</svg>
</div>
</div>

If you want to amend the transform via Snap you would do it very slightly different.
The end result is probably the same to the browser as mefs solution, as it will end up having a similar matrix on the element itself, its really down to whats the most convenient.
You can specify a center point for the scaling, just add it after the scale x & y parameters.
myPath.transform('s0.5,0.5,175,175');
jsfiddle
Edit: Actually I'm an idiot, its even more simple. I just remembered that Snap automatically uses the center of an object on transform strings if not specified. So in fact you can reduce it to this..
myPath.transform('s0.5');
jsfiddle

The position of your object is affected by the scaling transform. Add a translation in order to move the object to the correct position, right after scale:
transform="scale(0.5)translate(175,175)"
updated snippet:
var s = Snap("#me");
var myPath = s.select("#mypath");
function reset( el ) {
el.stop();
el.attr({ "stroke-dashoffset": 125 });
};
function startAnim( el ) {
el.animate( { "stroke-dashoffset": 600 }, 1000 );
};
reset( myPath );
s.mouseover( function() {
startAnim( myPath );
} );
s.mouseout( function() {
reset( myPath );
} );
.test {
border: 1px solid black;
height: 500px;
width: 500px;
}
#me {
border: 2px solid green;
}
#mypath {
border: 2px solid blue;
display: flex;
justify-content: center;
align-content: center;
flex-direction: column;
}
<script src="http://tedbeer.net/lib/snap.svg-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="test">
<div class="pie">
<svg id="me" viewBox="0 0 350 350">
<!--<svg id="me" viewBox="0 0 350 350" width="100" height="100">-->
<path id="mypath" d="M 175, 175 m 0, -75 a 75, 75 0 1, 0 0, 150 a 75, 75 0 1, 0 0, -150" fill="none" stroke="#ccc" stroke-width="150" stroke-dasharray="0 600 600 0" stroke-dashoffset="1000" transform="scale(0.5)translate(175,175)">
</path>
</svg>
</div>
</div>
Edit: possible other options based on the question in your comment:
option 1
If you do not want to have the translate at all, You can change the viewBox coordinates, this way:
<svg id="me" viewBox="-75 -75 350 350" width="100%" height="100%">
Please note that I haven't taken the time to calculate the exact ones that would be relevant to you, but you get the idea. You should adapt the four values so that they match to the viewport you want to obtain.
option 2
do some math to replace your current path coordinates by the ones for an object twice smaller, so that you don't need the scale transform anymore.

Related

How to change the size of svg icon in React?

I wanted to display an svg icon and a text in a same line in React and Next.js. Now I have found how to do it. Though I still don't know how to control the size of the svg icon in the context. I want the svg icon to be same size as each character of the text.
I put the browser display here.
And I put my code bellow, please point out what is wrong about my code.
//mysvg.js
import styles from '../styles/svg.module.css'
export function CircleBottomIcon(props) {
return (<span class={styles['svg-image']}>
<svg
width="30"
height="30"
viewBox="0 0 30 30"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<title>circle-bottom</title>
<path
d="M27.5 15c0 6.893-5.608 12.5-12.5 12.5-6.893 0-12.5-5.608-12.5-12.5C2.5 8.107 8.108 2.5 15 2.5c6.893 0 12.5 5.608 12.5 12.5zm2.5 0c0-8.284-6.716-15-15-15C6.716 0 0 6.716 0 15c0 8.284 6.716 15 15 15 8.284 0 15-6.716 15-15zm-15 2.5l-5.625-5.625-1.875 1.91L15 21.25l7.5-7.466-1.875-1.91L15 17.5z"
fill-rule="evenodd"
/>
</svg></span>
);
}
//index.js
import {CircleBottomIcon} from '../components/mysvg'
export default function Home() {
return (
<span>
1234<CircleBottomIcon></CircleBottomIcon>567890
</span>
)
}
//svg.module.css
.svg-image {
display: inline-block;
width: 16px;
height: 16px;
}
I'd suggest you keep constant values for viewbox param and accept props for height and width. Add a default values for these as 30 and 30. Part of code looks like:
const { height, width } = props;
<svg
width={width}
height={height}
viewBox="0 0 30 30"
...
Then you can use any values for width and height when you use the svg component.
your .svg-image selector should target the svg element:
.svg-image {
display: inline-block;
svg {
width: 16px;
height: 16px;
}
}

SVG not scaling with page resize React

I have an animated SVG in react that is supposed to draw some lines down the screen starting from the top to the bottom relative to 100vh adn 100vw. the problem is it works on my native resolution but not when the window is resized (i made the svg in gimp in respect to my native res). I have tried every single css combination i can think of but i just cant get it to work as needed.
(images at the bottom for an idea of what it should look like vs what happens when resized)
thanks in advance :)
SVG:
<svg className="line_svg" xmlns="http://www.w3.org/2000/svg"
width="auto" height="100%"
viewBox="0 0 2560 1440">
<path id="Selection"
fill="none" stroke="green" stroke-width="2"
d="M 1279.00,0.00
C 1280.66,5.65 1283.93,7.93 1288.00,12.00
1288.00,12.00 1306.00,30.00 1306.00,30.00
1306.00,30.00 1374.00,98.00 1374.00,98.00
1374.00,98.00 1548.00,271.00 1548.00,271.00
1548.00,271.00 1606.00,329.00 1606.00,329.00
1612.96,335.96 1631.41,355.28 1638.00,360.00
1638.00,360.00 1610.91,389.00 1610.91,389.00
1610.91,389.00 1576.91,425.00 1576.91,425.00
1576.91,425.00 1549.09,454.00 1549.09,454.00
1549.09,454.00 1457.91,551.00 1457.91,551.00
1457.91,551.00 1396.09,616.00 1396.09,616.00
1396.09,616.00 1151.91,875.00 1151.91,875.00
1151.91,875.00 1124.09,904.00 1124.09,904.00
1124.09,904.00 1014.91,1020.00 1014.91,1020.00
1014.91,1020.00 989.04,1047.00 989.04,1047.00
989.04,1047.00 978.00,1059.00 978.00,1059.00
978.00,1059.00 965.83,1072.00 965.83,1072.00
964.29,1073.71 960.88,1076.96 960.17,1079.00
959.20,1081.77 961.03,1083.93 962.63,1086.00
962.63,1086.00 976.00,1101.00 976.00,1101.00
976.00,1101.00 1030.58,1162.00 1030.58,1162.00
1030.58,1162.00 1087.83,1226.00 1087.83,1226.00
1087.83,1226.00 1213.83,1367.00 1213.83,1367.00
1213.83,1367.00 1261.07,1420.00 1261.07,1420.00
1266.18,1425.63 1275.10,1437.95 1282.00,1440.00
1280.25,1433.86 1276.28,1430.56 1272.08,1426.00
1272.08,1426.00 1256.07,1408.00 1256.07,1408.00
1256.07,1408.00 1196.16,1341.00 1196.16,1341.00
1196.16,1341.00 1044.16,1171.00 1044.16,1171.00
1044.16,1171.00 993.17,1114.00 993.17,1114.00
993.17,1114.00 963.00,1081.00 963.00,1081.00
963.00,1081.00 975.04,1068.00 975.04,1068.00
975.04,1068.00 1024.09,1016.00 1024.09,1016.00
1024.09,1016.00 1052.91,986.00 1052.91,986.00
1052.91,986.00 1144.09,889.00 1144.09,889.00
1144.09,889.00 1205.91,824.00 1205.91,824.00
1205.91,824.00 1450.09,565.00 1450.09,565.00
1450.09,565.00 1477.91,536.00 1477.91,536.00
1477.91,536.00 1586.09,421.00 1586.09,421.00
1586.09,421.00 1612.96,393.00 1612.96,393.00
1612.96,393.00 1623.00,382.00 1623.00,382.00
1623.00,382.00 1635.28,369.00 1635.28,369.00
1636.74,367.34 1640.17,363.91 1640.83,362.00
1641.84,359.07 1639.76,357.00 1637.94,355.00
1637.94,355.00 1623.00,340.00 1623.00,340.00
1623.00,340.00 1560.00,277.00 1560.00,277.00
1560.00,277.00 1359.00,77.00 1359.00,77.00
1359.00,77.00 1304.00,22.00 1304.00,22.00
1298.18,16.18 1286.24,2.12 1279.00,0.00 Z" />
</svg>
CSS:
.front_page_parent{
width: 100vw;
height: 100vh;
display: block;
margin: 0 auto;
padding: 0;
}
.line_svg{
display: block;
margin: 0 auto;
max-width: 100vw;
max-height: 100vh;
stroke-dasharray: 6000;
animation: draw 2s ease-in;
fill: green;
overflow: hidden;
}
Structure of App
export const App = () => {
return (
<div className="app">
<FrontPage />
</div>
)
}
export const FrontPage = () => {
return (
<div className="font_page_parent" style={BackgroundImageStyle}>
<svg className="line_svg" xmlns="http://www.w3.org/2000/svg"
width="auto" height="100%"
viewBox="0 0 2560 1440">
<path id="Selection"
fill="none" stroke="green" stroke-width="2"
d="......."- />
</svg>
</div>
working full screen
not working resized

How to scale SVG path to fit the window size?

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!

How to generate a elongated hexagon shape with two circular sides?

I'm trying to generate this sort of shape within my design:
In which a canvas element appears in the middle part of the circle, and this kind of elongated hexagon appears around the outside.
However, I am having some issues with the shape - primarily how I would make the shape with a gradient or background image as well (i.e. the internal puzzle piece looking bit will be an image). The outer background will be a solid background colour (#222).
Current Attempt
My current attempt is using a background gradient to generate most of the shape, and using pseudo elements for the two circlular parts either side::
var a = 100; // size of the black hole.
var b = 200; // distance of black hole from canvas center.
var c = 1; // speed of black hole rotation.
var d = 20; // the amount of stars to spawn every frame.
// ---------------------------------------------
var canvas = document.getElementById('c'),
ctx = canvas.getContext('2d'),
stars = [],
m = {},
r = 0
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
m.x = null;
m.y = null;
ctx.strokeStyle = '#fff';
ctx.translate(0.5, 0.5);
// create stars
function createStars(n) {
if (m.x == null) return;
for (var i = 0; i < n; i++) {
var shape = {
x: m.x,
y: m.y,
r: 1,
speed: 1,
accel: 1.01,
accel2: 0.001,
angle: Math.random() * 360
}
var vel = {
x: a * Math.cos(shape.angle * Math.PI / 180),
y: a * Math.sin(shape.angle * Math.PI / 180)
};
shape.x += vel.x;
shape.y += vel.y;
stars.push(shape);
}
}
function render() {
createStars(d);
var bench = [];
// ctx.save();
// ctx.fillStyle = 'rgba(0,0,0,0.5)';
// ctx.fillRect(0, 0, canvas.width, canvas.height);
// ctx.restore();
ctx.clearRect(0, 0, canvas.width, canvas.height);
r += c;
if (r < 360) {
m = {
x: canvas.width / 2,
y: canvas.height / 2,
angle: r
}
var targetAngle = m.angle * Math.PI / 180;
m.x += b * Math.cos(targetAngle);
m.y += b * Math.sin(targetAngle);
} else {
r = 0;
}
while (stars.length) {
var star = stars.pop();
var vel = {
x: star.speed * Math.cos(star.angle * Math.PI / 180),
y: star.speed * Math.sin(star.angle * Math.PI / 180)
};
ctx.beginPath();
ctx.moveTo(star.x, star.y);
ctx.lineTo(star.x + vel.x, star.y + vel.y);
ctx.closePath();
ctx.stroke();
star.x += vel.x;
star.y += vel.y;
star.speed *= star.accel;
star.accel += star.accel2;
if (star.x < canvas.width && star.x > 0 && star.y < canvas.height && star.y > 0) {
bench.push(star);
}
}
stars = bench.slice(0).reverse();
}
window.requestAnimFrame = (function() {
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
(function animloop() {
requestAnimFrame(animloop);
render();
})();
#import url(https://fonts.googleapis.com/css?family=Hammersmith+One);
html {
margin: 0;
padding: 0;
height: 100vh;
background: #222;
color: cornflowerblue;
overflow-x: hidden;
font-size: 18px;
font-family: 'Hammersmith One', sans-serif;
}
/************************************/
#c {
height: 200px;
width: 200px;
border-radius: 50%;
border: 5px double lightgray;
margin-left: 150px;
margin-right: 150px;
background: #222;
z-index: 10;
position: absolute;
left: 0px;
top: 50px;
}
.canwrap {
background: url(http://placekitten.com/g/300/300);
height: 300px;
width: 500px;
position: relative;
z-index: 0;
margin-left: 50px;
margin-right: 50px;
}
.canwrap:before {
content: "";
position: absolute;
width: 150px;
height: 100%;
top: 0;
left: 50%;
transform: translateX(-50%);
background: #222;
z-index: -1;
}
.canwrap:after {
content: "";
position: absolute;
height: 100px;
width: 100px;
top: 100px;
left: -50px;
background: red;
border-radius: 50%;
box-shadow: 500px 0 20px -5px red;
}
<div class="canwrap">
<canvas id="c" class="img"></canvas>
</div>
However, this leaves me with a few issues:
Background part can't be an image
circles can't replicate the image part
gradient stop colours means there is not a 'clear edge' to the shape.
I would be really grateful to get another view/angle on constructing this shape, as I haven't been able to produce this in a way that I need to.
I know svg might be the way to do this, but I've always had a hard time getting svg's to animate properly compared to css - but if either work, I'd be happy (seeing as my design doesn't exactly allow my image design)!
As always, any suggestions would be great.
How about something like this i used svg Fiddle
<svg width="1000" height="500">
<polygon points="100 10, 50 245, 100 500, 200 500, 200 10,100 10" fill="orange" />
<polygon points="400 10, 460 245, 400 500, 300 500, 300 10,200 10" fill="orange" />
<circle r="50" cx="50" cy="245" fill="orange" />
<circle r="50" cx="460" cy="245" fill="orange" />
<circle r="100" cx="255" cy="245" fill="midnightblue" />
</svg>
You can also do it with image as background
<svg width="1000" height="500">
<defs>
<pattern id="pattern" patternUnits="userSpaceOnUse" width="1000" height="1000">
<image xlink:href="https://placekitten.com/g/200/300" x="-200" y="-10" width="1000" height="1000" />
</pattern>
</defs>
<polygon points="100 10, 50 245, 100 500, 200 500, 200 10,100 10" fill="url(#pattern)"/>
<polygon points="400 10, 460 245, 400 500, 300 500, 300 10,200 10" fill="url(#pattern)"/>
<circle r="50" cx="50" cy="245" fill="url(#pattern)"/>
<circle r="50" cx="460" cy="245" fill="url(#pattern)"/>
<circle r="100" cx="255" cy="245" fill="midnightblue"/>
although this is not for canvas but rather for html element you can do the same technique in canvas.
I assume you already know basic css 3,now since it's a lot of work to code, I'll give you the instructions:
1) head out to this link
2) create 2 Trapezoids and rotate them 90 degrees
3) create 2 small circles and position each one (relative/absolute) on the edge of the Trapezoid with z-index smaller than the Trapezoids
4) 1 circle in the middle and your done!
You can use svg, which simplifies the creation of shape to a large extent.
Create a path, and fill in pattern.
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" height="100%" width="100%" viewBox="0 0 65 50" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<pattern id="image" patternUnits="userSpaceOnUse" width="100" height="100">
<image xlink:href="http://www.placecage.com/g/200/300" width="50" x="-10" y="0" height="50"/>
<image xlink:href="http://www.placekitten.com/g/200/300" width="50" x="30" y="0" height="50"/>
</pattern>
</defs>
<path d="M 25 0 H 15 L 10 20 A 5.3 5.3 0 1 0 10 30 L15 50 H25 z M 40 0 H 50 L 55 20 A 5.3 5.3 0 1 1 55 30 L 50 50 H 40 z" fill="url(#image)" stroke="none"/>
</svg>
Height and width have been set to 100%, so this should be responsive. Also, the position of images can be changed using x,y attributes.

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