I have a SVG in the form of six arcs that form a circle. I am using CSS so that whenever I hover an arc its position is moved slightly in the y-axis using the translate function. The issue is that when I hover over an element it will move in the y-axis but doesn't take into account the angle of the element.
Is there a way to make the segments translate upward in the y-axis taking into account the angle of the element with CSS and JS?
Here is a codepen to see the code and the SVG.
#arc:hover {
transform: translate(0, 10px);
transition: all 0.2s;
}
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="index.css">
<title>Document</title>
</head>
<body>
<svg width="439" height="439" viewBox="0 0 439 439" fill="none" xmlns="http://www.w3.org/2000/svg" >
<g>
<g id='arc'>
<path d="M379.92 219.08C379.976 247.277 372.603 274.992 358.544 299.435C344.484 323.877 324.234 344.185 299.832 358.315L219.396 219.397L379.92 219.08Z" fill="#668dcc"/>
</g>
<g id='arc'>
<path d="M139.409 358.779C114.961 344.728 94.6461 324.485 80.5079 300.088C66.3697 275.691 58.9075 248 58.8723 219.802L219.397 219.602L139.409 358.779Z" fill="#6e085c"/>
</g>
<g id='arc'>
<path d="M299.933 358.256C275.541 372.403 247.852 379.875 219.655 379.92C191.457 379.966 163.745 372.583 139.307 358.514L219.396 219.396L299.933 358.256Z" fill="#3e9596"/>
</g>
<g id='arc'>
<path d="M379.921 219.491C379.866 191.293 372.383 163.608 358.227 139.221C344.071 114.834 323.741 94.6063 299.283 80.5734L219.397 219.808L379.921 219.491Z" fill="#d69304"/>
</g>
<g id='arc'>
<path d="M138.86 80.7425C114.468 94.8895 94.2326 115.212 80.1909 139.665C66.1492 164.118 58.7964 191.838 58.8726 220.036L219.397 219.602L138.86 80.7425Z" fill="#0323b2"/>
</g>
<g id='arc'>
<path d="M299.384 80.6315C274.937 66.5809 247.219 59.2181 219.021 59.284C190.824 59.35 163.141 66.8424 138.759 81.0073L219.397 219.808L299.384 80.6315Z" fill="#5e2da3"/>
</g>
</g>
</svg>
</body>
</html>
CodePudding user response:
We need a little geometry to decide what translation to apply to an element.
This snippet calculates the amount to move in the x and y direction for each segement using the sin and cos of 30degrees and a variable, --d, which says how much radially you want the slice to move away from the center.
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="index.css">
<title>Document</title>
<style>
.arc {
--cos30: 0.8660254037;
--sin30: 0.5;
--d: 10px;
/* the delta amount you want a hovered slice to move away from the center */
}
.arc:nth-child(1) {
--x: var(--cos30);
--y: var(--sin30);
}
.arc:nth-child(2) {
--x: calc(-1 * var(--cos30));
--y: var(--sin30);
}
.arc:nth-child(3) {
--x: 0;
--y: 1;
}
.arc:nth-child(4) {
--x: var(--cos30);
--y: calc(-1 * var(--sin30));
}
.arc:nth-child(5) {
--x: calc(-1 * var(--cos30));
--y: calc(-1 * var(--sin30));
}
.arc:nth-child(6) {
--x: 0;
--y: -1;
}
.arc:hover {
transform: translate(calc(var(--d) * var(--x)), calc(var(--d) * var(--y)));
transition: all 0.2s;
}
</style>
</head>
<body>
<button onclick="document.querySelector('svg').style.transform = 'rotate(49deg)';">Rotate</button>
<svg width="439" height="439" viewBox="0 0 439 439" fill="none" xmlns="http://www.w3.org/2000/svg">
<g>
<g class='arc'>
<path d="M379.92 219.08C379.976 247.277 372.603 274.992 358.544 299.435C344.484 323.877 324.234 344.185 299.832 358.315L219.396 219.397L379.92 219.08Z" fill="#668dcc"/>
</g>
<g class='arc'>
<path d="M139.409 358.779C114.961 344.728 94.6461 324.485 80.5079 300.088C66.3697 275.691 58.9075 248 58.8723 219.802L219.397 219.602L139.409 358.779Z" fill="#6e085c"/>
</g>
<g class='arc'>
<path d="M299.933 358.256C275.541 372.403 247.852 379.875 219.655 379.92C191.457 379.966 163.745 372.583 139.307 358.514L219.396 219.396L299.933 358.256Z" fill="#3e9596"/>
</g>
<g class='arc'>
<path d="M379.921 219.491C379.866 191.293 372.383 163.608 358.227 139.221C344.071 114.834 323.741 94.6063 299.283 80.5734L219.397 219.808L379.921 219.491Z" fill="#d69304"/>
</g>
<g class='arc'>
<path d="M138.86 80.7425C114.468 94.8895 94.2326 115.212 80.1909 139.665C66.1492 164.118 58.7964 191.838 58.8726 220.036L219.397 219.602L138.86 80.7425Z" fill="#0323b2"/>
</g>
<g class='arc'>
<path d="M299.384 80.6315C274.937 66.5809 247.219 59.2181 219.021 59.284C190.824 59.35 163.141 66.8424 138.759 81.0073L219.397 219.808L299.384 80.6315Z" fill="#5e2da3"/>
</g>
</g>
</svg>
</body>
</html>
Note the id="arc" in each slice element has been changed to class="arc" as ids have to be unique.
The rotate button just rotates the whole SVG by an arbitary 49degrees so you can see that the hover effect still works. This is because the calculations of the hovered slice position are done relative to the element not relative to the window.
CodePudding user response:
I found a solution that comes close to what you want.
Instead of moving the element I scaled it up. And by setting the anchor point in the element's centre, it "pops out" when hovered.
.arc {
transform-origin: center;
}
.arc:hover {
transform:scale(1.1);
transition: all 0.2s;
}
<svg width="439" height="439" viewBox="0 0 439 439" fill="none" xmlns="http://www.w3.org/2000/svg" >
<g>
<g class='arc'>
<path d="M379.92 219.08C379.976 247.277 372.603 274.992 358.544 299.435C344.484 323.877 324.234 344.185 299.832 358.315L219.396 219.397L379.92 219.08Z" fill="#668dcc"/>
</g>
<g class='arc'>
<path d="M139.409 358.779C114.961 344.728 94.6461 324.485 80.5079 300.088C66.3697 275.691 58.9075 248 58.8723 219.802L219.397 219.602L139.409 358.779Z" fill="#6e085c"/>
</g>
<g class='arc'>
<path d="M299.933 358.256C275.541 372.403 247.852 379.875 219.655 379.92C191.457 379.966 163.745 372.583 139.307 358.514L219.396 219.396L299.933 358.256Z" fill="#3e9596"/>
</g>
<g class='arc'>
<path d="M379.921 219.491C379.866 191.293 372.383 163.608 358.227 139.221C344.071 114.834 323.741 94.6063 299.283 80.5734L219.397 219.808L379.921 219.491Z" fill="#d69304"/>
</g>
<g class='arc'>
<path d="M138.86 80.7425C114.468 94.8895 94.2326 115.212 80.1909 139.665C66.1492 164.118 58.7964 191.838 58.8726 220.036L219.397 219.602L138.86 80.7425Z" fill="#0323b2"/>
</g>
<g class='arc'>
<path d="M299.384 80.6315C274.937 66.5809 247.219 59.2181 219.021 59.284C190.824 59.35 163.141 66.8424 138.759 81.0073L219.397 219.808L299.384 80.6315Z" fill="#5e2da3"/>
</g>
</g>
</svg>
CodePudding user response:
Since you have a fixed set you can simply set a class and use that. I used the colors but naming the quadrant might also be of use.
.arc:hover {
transition: all 0.2s;
}
.arc.green:hover {
transform: translate(0px, 10px);
}
.arc.purple:hover {
transform: translate(0, -10px);
}
.arc.orange:hover {
transform: translate(10px, -5px);
}
.arc.darkblue:hover {
transform: translate(-10px, -5px);
}
.arc.blue:hover {
transform: translate(10px, 5px);
}
.arc.maroon:hover {
transform: translate(-10px, 5px);
}
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="index.css">
<title>Document</title>
</head>
<body>
<svg width="439" height="439" viewBox="0 0 439 439" fill="none" xmlns="http://www.w3.org/2000/svg">
<g>
<g class='arc blue'>
<path d="M379.92 219.08C379.976 247.277 372.603 274.992 358.544 299.435C344.484 323.877 324.234 344.185 299.832 358.315L219.396 219.397L379.92 219.08Z" fill="#668dcc"/>
</g>
<g class='arc maroon'>
<path d="M139.409 358.779C114.961 344.728 94.6461 324.485 80.5079 300.088C66.3697 275.691 58.9075 248 58.8723 219.802L219.397 219.602L139.409 358.779Z" fill="#6e085c"/>
</g>
<g class='arc green'>
<path d="M299.933 358.256C275.541 372.403 247.852 379.875 219.655 379.92C191.457 379.966 163.745 372.583 139.307 358.514L219.396 219.396L299.933 358.256Z" fill="#3e9596"/>
</g>
<g class='arc orange'>
<path d="M379.921 219.491C379.866 191.293 372.383 163.608 358.227 139.221C344.071 114.834 323.741 94.6063 299.283 80.5734L219.397 219.808L379.921 219.491Z" fill="#d69304"/>
</g>
<g class='arc darkblue'>
<path d="M138.86 80.7425C114.468 94.8895 94.2326 115.212 80.1909 139.665C66.1492 164.118 58.7964 191.838 58.8726 220.036L219.397 219.602L138.86 80.7425Z" fill="#0323b2"/>
</g>
<g class='arc purple'>
<path d="M299.384 80.6315C274.937 66.5809 247.219 59.2181 219.021 59.284C190.824 59.35 163.141 66.8424 138.759 81.0073L219.397 219.808L299.384 80.6315Z" fill="#5e2da3"/>
</g>
</g>
</svg>
</body>
</html>
CodePudding user response:
I turned the CSS trasformation into an <animateTransform>
. In this case it seams easier to control.
Update
It can also be rotated.
document.querySelector('button').addEventListener('click', e => {
document.querySelector('#container').style.rotate = `${1000}deg`;
});
#container {
translate: 50px 50px;
rotate: -15deg;
transition: rotate 1s;
}
<button>Rotate</button><br/>
<svg viewBox="0 0 100 100" width="200">
<defs>
<mask id="m1">
<circle cx="0" cy="0" r="40" fill="white" />
</mask>
<g id="r1" mask="url(#m1)">
<animateTransform attributeName="transform" type="translate"
from="0 0" to="5 5" begin="mouseenter" dur="200ms" fill="freeze" />
<animateTransform attributeName="transform" type="translate"
from="5 5" to="0 0" begin="mouseleave" dur="200ms" fill="freeze" />
<rect width="50" height="50" transform="skewX(15) skewY(15)"/>
</g>
</defs>
<g id="container">
<use href="#r1" transform="rotate(0)" fill="#668dcc" />
<use href="#r1" transform="rotate(60)" fill="#6e085c" />
<use href="#r1" transform="rotate(120)" fill="#3e9596" />
<use href="#r1" transform="rotate(180)" fill="#d69304" />
<use href="#r1" transform="rotate(240)" fill="#0323b2" />
<use href="#r1" transform="rotate(300)" fill="#5e2da3" />
</g>
</svg>