How to svg morph in ReactJs between two elements - javascript
I currently have two variables defining svg elements.
I am having trouble morphing between the two, I tried react-svg-morph but the circle would disappear on each render.
What is the best way to render a morph?
This is my setup:
let hexagonShape = <svg width="100%" height="100%" viewBox="0 0 15 15" fill="green" xmlns="http://www.w3.org/2000/svg">
<path d="M1.5 4.5V10.5L7.5 14L13.5 10.5V4.5L7.5 1L1.5 4.5Z" stroke="green" strokeWidth=".6" />
</svg>
let circleShape = <svg height="100%" width="100%" viewBox="0 0 15 15" fill='blue' xmlns="http://www.w3.org/2000/svg">
<circle cy="50%" cx="50%" r="49%" stroke="blue" strokeWidth=".3" />
</svg>
const [updatedSvg, setUpdatedSvg] = useState(0)
const [updatedCircleSvg, setUpdatedCircleSvg] = useState()
const [updatedHexagonSvg, setUpdatedHexagonSvg] = useState()
function changeSvg() {
if (updatedSvg === 0) {
setUpdatedCircleSvg(true)
setUpdatedHexagonSvg(false)
}
if (updatedSvg === 1) {
setUpdatedCircleSvg(false)
setUpdatedHexagonSvg(true)
}
}
useEffect(() => {
changeSvg()
}, [updatedSvg])
And I have two simple buttons that toggle between the two svg renders:
<div className={classes.container}>
<div className={classes.shape}>
{/* this is where the svgs are being rendered */}
{updatedCircleSvg && circleShape }
{updatedHexagonSvg && hexagonShape}
</div>
<button onClick={() => setUpdatedSvg(0)}>circle</button>
<button onClick={() => setUpdatedSvg(1)}>hexagon</button>
</div>
A bit of direction would greatly help. Thank you
Here is a simple example using Quadratic Bezier curves:
const getPoints = radius => {
const factor = Math.sqrt(3);
return [
{x: 0, y: -radius},
{x: radius / factor, y: -radius},
{x: radius * factor / 2, y: -radius / 2},
{x: radius * 2 / factor, y: 0},
{x: radius * factor / 2, y: radius / 2},
{x: radius / factor, y: radius},
{x: 0, y: radius},
{x: -radius / factor, y: radius},
{x: -radius * factor / 2, y: radius / 2},
{x: -radius * 2 / factor, y: 0},
{x: -radius * factor / 2, y: -radius / 2},
{x: -radius / factor, y: -radius}
];
}
const getRoundPath = radius => {
const p = getPoints(radius);
return `M ${p[0].x},${p[0].y}
Q ${p[1].x},${p[1].y} ${p[2].x},${p[2].y}
Q ${p[3].x},${p[3].y} ${p[4].x},${p[4].y}
Q ${p[5].x},${p[5].y} ${p[6].x},${p[6].y}
Q ${p[7].x},${p[7].y} ${p[8].x},${p[8].y}
Q ${p[9].x},${p[9].y} ${p[10].x},${p[10].y}
Q ${p[11].x},${p[11].y} ${p[0].x},${p[0].y}`
};
const getHexagonPath = radius => {
const p = getPoints(radius);
for (let index = 1; index < 12; index += 2) {
const prev = p[index - 1];
const next = p[(index + 1) % 12];
p[index].x = (prev.x + next.x) / 2;
p[index].y = (prev.y + next.y) / 2;
}
return `M ${p[0].x},${p[0].y}
Q ${p[1].x},${p[1].y} ${p[2].x},${p[2].y}
Q ${p[3].x},${p[3].y} ${p[4].x},${p[4].y}
Q ${p[5].x},${p[5].y} ${p[6].x},${p[6].y}
Q ${p[7].x},${p[7].y} ${p[8].x},${p[8].y}
Q ${p[9].x},${p[9].y} ${p[10].x},${p[10].y}
Q ${p[11].x},${p[11].y} ${p[0].x},${p[0].y}`
}
const morphPath = (toHex) => {
const path = toHex ? getHexagonPath(80) : getRoundPath(80);
d3.select('path')
.transition()
.duration(750)
.attr('d', path);
setTimeout(() => morphPath(!toHex), 1000);
};
d3.select('path').attr('d', getRoundPath(80))
morphPath(true);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg width="200" height="200">
<g transform="translate(100,100)">
<path />
</g>
</svg>
Related
How to swap the position of two <path> elements inside an SVG
I have an <svg> included in my HTML file with a bunch of <path> elements. My desired behavior is to be able to randomly shuffle the positioning of the <path> elements, and then to subsequently sort them back into their proper position. Example: if I have 3 <path>s at positions 1, 2, and 3. For the shuffle functionality, I move path 1 to position 3, path 2 to position 1, and path 3 to position 2. Then I do some kind of visual sort (e.g. insertion sort), where I swap two <path>s' positions at a time until the <path>s are back in their proper place and the SVG looks normal again. If these were "normal" HTML elements I would just set the x and y properties, but based on my research <path> elements don't have those properties, so I've resorted to using the transform: translate(x y). With my current approach, the first swap works fine. But any subsequent swaps get way out of whack, and go too far in both directions. If I'm just swapping two <path>s back and forth, I can get it to work consistently by keeping track of which element is in which position (e.g. elem.setAttribute('currPos', otherElem.id)), and when currPos == currElem.id, setting transform: translate(0 0), but when I start adding more elements, they end up moving to places where there previously wasn’t a <path> element. My current code is below. For some reason the CSS transition isn’t working properly here but it works elsewhere (edit: it works fine on desktop just not on my phone) function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } function getPos(elem) { let rect = elem.getBoundingClientRect(); let x = rect.left + (rect.right - rect.left) / 2; let y = rect.top + (rect.bottom - rect.top) / 2; return [x, y]; } async function swap(e1, e2, delayMs = 3000) { let e1Pos = getPos(e1); let e2Pos = getPos(e2); console.log(e1Pos, e2Pos); e2.setAttribute('transform', `translate(${e1Pos[0]-e2Pos[0]}, ${e1Pos[1]-e2Pos[1]})`); e1.setAttribute('transform', `translate(${e2Pos[0]-e1Pos[0]}, ${e2Pos[1]-e1Pos[1]})`); if (delayMs) { await delay(delayMs); } } let blackSquare = document.getElementById('black-square'); let redSquare = document.getElementById('red-square'); swap(blackSquare, redSquare) .then(() => swap(blackSquare, redSquare)) .then(() => swap(blackSquare, redSquare)); * { position: absolute; } path { transition: transform 3s } <svg width="500" height="800" xmlns="http://www.w3.org/2000/svg"> <path id="black-square" d="M 10 10 H 90 V 90 H 10 L 10 10" fill="black" /> <path id="red-square" d="M 130 70 h 80 v 80 h -80 v -80" fill="red" /> <path id="green-square" d="M 20 120 h 80 v 80 h -80 v -80" fill="green" /> </svg>
You could achieve this by applying multiple translate transformations. Lets say, the red square should be positioned at the black square position: <path transform="translate(-130 -70) translate(10 10)" id="redSquare" d="M 130 70 h 80 v 80 h -80 v -80" fill="red" ></path> translate(-130 -70) negates the square's original x,y offset and moves this element to the svg's coordinate origin. The second transformation translate(10 10) will move this element to the black square's position. const paths = document.querySelectorAll("path"); function shuffleEls(paths) { /** * get current positions and save to data attribute * skip this step if data attribute is already set */ if(!paths[0].getAttribute('data-pos')){ paths.forEach((path) => { posToDataAtt(path); }); } // shuffle path element array const shuffledPaths = shuffleArr([...paths]); for (let i = 0; i < shuffledPaths.length; i += 1) { let len = shuffledPaths.length; //let el1 = i>0 ? shuffledPaths[i-1] : shuffledPaths[len-1] ; let el1 = shuffledPaths[i]; let el2 = paths[i]; copyPosFrom(el1, el2); } } function posToDataAtt(el) { let bb = el.getBBox(); let [x, y, width, height] = [bb.x, bb.y, bb.width, bb.height].map((val) => { return +val.toFixed(2); }); el.dataset.pos = [x, y].join(" "); } function copyPosFrom(el1, el2) { let [x1, y1] = el1.dataset.pos.split(" ").map((val) => { return +val; }); let [x2, y2] = el2.dataset.pos.split(" ").map((val) => { return +val; }); /** * original position is negated by negative x/y offsets * new position copied from 2nd element */ el1.setAttribute( "transform", `translate(-${x1} -${y1}) translate(${x2} ${y2})` ); } function shuffleArr(arr) { const newArr = arr.slice(); for (let i = newArr.length - 1; i > 0; i--) { const rand = Math.floor(Math.random() * (i + 1)); [newArr[i], newArr[rand]] = [newArr[rand], newArr[i]]; } return newArr; } svg{ border:1px solid red; } path{ transition: 0.5s; } <p> <button onclick="shuffleEls(paths)">shuffleAll()</button> </p> <svg width="500" height="800" xmlns="http://www.w3.org/2000/svg"> <path id="blackSquare" d="M 10 10 H 90 V 90 H 10 L 10 10" fill="black" /> <path id="redSquare" d="M 130 70 h 80 v 80 h -80 v -80" fill="red" /> <path id="greenSquare" d="M 20 120 h 80 v 80 h -80 v -80" fill="green" /> <path id="purpleSquare" d="M 250 10 h 80 v 80 h -80 v -80" fill="purple" /> </svg> How it works shuffleEls() gets each path's position via getBBox() and saves x and y coordinates to a data attribute We shuffle the path element array each path inherits the position from it's shuffled counterpart swap positions: let el1 = shuffledPaths[i]; let el2 = paths[i]; copyPosFrom(el1, el2); Update: include previous transformations If a <path> element is already transformed (e.g rotated), you probably want to retain it. const paths = document.querySelectorAll(".pathToshuffle"); function revertShuffling(paths) { if (paths[0].getAttribute('data-pos')) { paths.forEach((path) => { copyPosFrom(path, path); }); } } //shuffleEls(paths) function shuffleEls(paths) { /** * get current positions and save to data attribute * skip this step if data attribute is already set */ if (!paths[0].getAttribute('data-pos')) { paths.forEach((path) => { posToDataAtt(path); }); } // shuffle path element array const shuffledPaths = shuffleArr([...paths]); let shuffledElCount = 0; for (let i = 0; i < shuffledPaths.length; i += 1) { let el1 = shuffledPaths[i]; let el2 = paths[i]; shuffledElCount += copyPosFrom(el1, el2); } // repeat shuffling if result is identical to previous one if (shuffledElCount < 1) { shuffleEls(paths); } } function posToDataAtt(el) { let bb = el.getBBox(); let [x, y, width, height] = [bb.x, bb.y, bb.width, bb.height].map((val) => { return +val.toFixed(2); }); // include other transformations let style = window.getComputedStyle(el); let matrix = style.transform != 'none' ? style.transform : ''; el.dataset.pos = [x + width / 2, y + height / 2, matrix].join("|"); } function copyPosFrom(el1, el2) { let [x1, y1, matrix1] = el1.dataset.pos.split("|"); let [x2, y2, matrix2] = el2.dataset.pos.split("|"); /** * original position is negated by negative x/y offsets * new position copied from 2nd element */ let transformAtt = `translate(-${x1} -${y1}) translate(${x2} ${y2}) ${matrix1}`; // compare previous transformations to prevent identical/non-shuffled results let transFormChange = el1.getAttribute('transform') != transformAtt ? 1 : 0; el1.setAttribute("transform", transformAtt); return transFormChange; } function shuffleArr(arr) { let newArr = arr.slice(); for (let i = newArr.length - 1; i > 0; i--) { const rand = Math.floor(Math.random() * (i + 1)); [newArr[i], newArr[rand]] = [newArr[rand], newArr[i]]; } return newArr; } svg { border: 1px solid red; } path { transition: 0.5s; } <p> <button onclick="shuffleEls(paths)">shuffleAll()</button> <button onclick="revertShuffling(paths)">revertShuffling()</button> </p> <svg width="500" height="800" xmlns="http://www.w3.org/2000/svg"> <path class="pathToshuffle" id="blackSquare" d="M 20 30 h 60 v 40 h -60 z" fill="#999" /> <path class="pathToshuffle" id="redSquare" d="M 130 70 h 80 v 80 h -80 v -80" fill="red" /> <path class="pathToshuffle" id="greenSquare" d="M 20 120 h 80 v 80 h -80 v -80" fill="green" /> <path class="pathToshuffle" transform="rotate(45 275 35)" id="purpleSquare" d="M 250 10 h 50 v 50 h -50 v -50" fill="purple" /> <path id="bg" d="M 10 10 H 90 V 90 H 10 L 10 10z M 130 70 h 80 v 80 h -80 v -80z M 20 120 h 80 v 80 h -80 v -80z M 250 10 h 50 v 50 h -50 v -50z" fill="none" stroke="#000" stroke-width="1" stroke-dasharray="1 2"/> </svg> We can append a matrix() to the data attribute. If paths have different sizes or aspect ratios, you can also set centered x/y coordinates according the the actual bounding box. This way, all shuffled elements will be positioned around the same center points. let style = window.getComputedStyle(el); let matrix = style.transform!='none' ? style.transform : ''; el.dataset.pos = [x+width/2, y+height/2, matrix].join("|");
I think that is is easier to keep track of the positions if all the <path> elements have the same starting point (so, the same distance to 0,0) and then use transform/translate to position them. You can use elements transform matrix to find the position. function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } function getPos(elem) { let x = elem.transform.baseVal[0].matrix.e; let y = elem.transform.baseVal[0].matrix.f; return [x,y]; } async function swap(e1, e2, delayMs = 3000) { let e1Pos = getPos(e1); let e2Pos = getPos(e2); e2.setAttribute('transform', `translate(${e1Pos[0]} ${e1Pos[1]})`); e1.setAttribute('transform', `translate(${e2Pos[0]} ${e2Pos[1]})`); if (delayMs) { await delay(delayMs); } } let blackSquare = document.getElementById('black-square'); let redSquare = document.getElementById('red-square'); let greenSquare = document.getElementById('green-square'); swap(blackSquare, redSquare) .then(() => swap(blackSquare, redSquare)) .then(() => swap(blackSquare, greenSquare)); path { transition: transform 3s } <svg viewBox="0 0 500 400" width="500" xmlns="http://www.w3.org/2000/svg"> <path id="black-square" d="M 0 0 H 80 V 80 H 0 Z" fill="black" transform="translate(200 50)" /> <path id="red-square" d="M 0 0 H 80 V 80 H 0 Z" fill="red" transform="translate(50 20)" /> <path id="green-square" d="M 0 0 H 80 V 80 H 0 Z" fill="green" transform="translate(100 120)" /> </svg>
Squash and stretch effect for spheres using Javascript and GSAP
coming up with another question. I've been facing issues in finding a solution to how to give the squash and stretch effect to the spheres when they were touching the wall, especially when they're moving in different directions, and I have to mention that I also used a function to make the whole animation more fluid. Here are 2 ways I have tried so far: Add more keyframes to each ball to give them the squash effect at the time they were touching the wall, the outcome was not what I was expecting, as it kept being stretched during the whole animation Trying to create a new timeline inside the bounceModify function for the min value (the min value is responsible for the spheres' moment of touching the wall), and the outcome was invalid property, missing scale? gsap.registerPlugin(). => it is uncommented in the code Down below, you will find the code I've been working, on and a side note that I'm not that familiar with GSAP. JS code and GSAP timelines gsap.fromTo("#Nuub", {opacity: 0, y: 200}, {y: 0, opacity: 1, duration: 1.5, delay: 10}); gsap.fromTo(".buttons", {opacity: 0, y: 200}, {y: 0, opacity: 1, duration: 1.5, delay: 10.5}); // delay: 1.2 let ball_1 = document.querySelector("#Circle_1"); let ball_2 = document.querySelector("#Circle_2"); let ball_3 = document.querySelector("#Circle_3"); let ball_4 = document.querySelector("#Circle_4"); let container = document.querySelector("#nuub"); let ball_1_bounds = ball_1.getBoundingClientRect(); let ball_2_bounds = ball_2.getBoundingClientRect(); let ball_3_bounds = ball_2.getBoundingClientRect(); let ball_4_bounds = ball_2.getBoundingClientRect(); let container_bounds = container.getBoundingClientRect(); let max_X = container_bounds.right - ball_1_bounds.right; // right is the max for X, left is the min for X let max_Y = container_bounds.bottom - ball_1_bounds.bottom; // bottom is the max for Y, top is the min for Y let min_X = container_bounds.left - ball_1_bounds.left; let min_Y = container_bounds.top - ball_1_bounds.top; let max_X_2 = container_bounds.right - ball_2_bounds.right; // right is the max for X, left is the min for X let max_Y_2 = container_bounds.bottom - ball_2_bounds.bottom; // bottom is the max for Y, top is the min for Y let min_X_2 = container_bounds.left - ball_2_bounds.left; let min_Y_2 = container_bounds.top - ball_2_bounds.top; let max_X_3 = container_bounds.right - ball_3_bounds.right; // right is the max for X, left is the min for X let max_Y_3 = container_bounds.bottom - ball_3_bounds.bottom; // bottom is the max for Y, top is the min for Y let min_X_3= container_bounds.left - ball_3_bounds.left; let min_Y_3 = container_bounds.top - ball_3_bounds.top; let max_X_4 = container_bounds.right - ball_4_bounds.right; // right is the max for X, left is the min for X let max_Y_4 = container_bounds.bottom - ball_4_bounds.bottom; // bottom is the max for Y, top is the min for Y let min_X_4 = container_bounds.left - ball_4_bounds.left; let min_Y_4 = container_bounds.top - ball_4_bounds.top; let tl = new TimelineMax() tl.to("#Circle_1", { transformOrigin: "50% 100%", x: 4000, y: 3000, duration: 10, ease: "power2", modifiers: { x: bounceModify(min_X, max_X), y: bounceModify(min_Y, max_Y) } }) .to("#Circle_1", { transformOrigin: "50% 50%", x: 0, y: 0, ease: "elastic", duration: 3, modifiers: { x: bounceModify(min_X, max_X), y: bounceModify(min_Y, max_Y) } }) //ball 2 let tl2 = new TimelineMax(); tl2.to("#Circle_2", { x: -3800, y: -2600, duration: 10, ease: "power2", modifiers: { x: bounceModify(min_X_2, max_X_2), y: bounceModify(min_Y_2, max_Y_2) } }) .to("#Circle_2", { x: 0, y: 0, duration: 1.5, ease: "power1", modifiers: { x: bounceModify(min_X_2, max_X_2), y: bounceModify(min_Y_2, max_Y_2) } }) //ball 3 let tl3 = new TimelineMax(); tl3.to("#Circle_3", { x: -3200, y: -2200, duration: 10, ease: "power2", modifiers: { x: bounceModify(min_X_3, max_X_3), y: bounceModify(min_Y_3, max_Y_3) } }) .to("#Circle_3", { x: 0, y: 0, duration: 1.5, ease: "power1", modifiers: { x: bounceModify(min_X_3, max_X_3), y: bounceModify(min_Y_3, max_Y_3) } }) // ball 4 let tl4 = new TimelineMax(); tl4.to("#Circle_4", { x: 3700, y: 2700, duration: 10, ease: "power2", modifiers: { x: bounceModify(min_X_4, max_X_4), y: bounceModify(min_Y_4, max_Y_4) } }) .to("#Circle_4", { x: 0, y: 0, duration: 1.5, ease: "power1", modifiers: { x: bounceModify(min_X_4, max_X_4), y: bounceModify(min_Y_4, max_Y_4) } }) function bounceModify(min, max){ let range = max - min; return function(value){ value = parseFloat(value); let cycle, clipValue; if (value > max) { // let tlbounce = new TimelineMax() // tlbounce.to(min, .20, { // scaleX: 1.15, // scaleY: 0.9, // ease: "sine.in" // }) cycle = (value - max) / range; clipValue = (cycle % 1) * range; value = ((cycle | 0) & 1 !== 0) // min is when it hits the wall ? min + clipValue : max - clipValue; // max is when it floats } else if (value < min) { cycle = (min - value) / range; clipValue = (cycle % 1) * range; value = ((cycle | 0) & 1 !== 0) ? max - clipValue : min + clipValue; } return value + "px"; } } HTML code <div id="nuub"> <!-- the logo container --> <div class="nuub_animation"> <svg class="nuub_cropped" width="447" height="149" viewBox="0 0 447 149" fill="none" xmlns="http://www.w3.org/2000/svg"> <defs> <linearGradient id="gradient_1" gradientTransform="rotate(-85 0.4 0.6)"> <stop offset="0%" stop-color="#020024" /> <stop offset="20%" stop-color="#0c0c41"/> <stop offset="100%" stop-color="#a0c0c6" /> </linearGradient> </defs> <g> <g id="Group"> <path id="Circle_1" d="M38.6446 2.84853C13.3219 9.64117 -3.19286 34.8397 1.10098 60.3669C3.85344 76.6912 14.4229 90.9338 29.066 98.0551C54.719 110.545 85.5466 100.246 98.4281 74.8287C100.96 69.8985 103.493 58.5044 103.493 52.0404C103.603 19.2823 70.5732 -5.69706 38.6446 2.84853Z" fill="url(#gradient_1)"/> <path id="Circle_2" d="M132.338 39.8794C118.796 44.2618 110.209 54.9985 109.328 69.0221C108.337 82.3882 114.833 93.6728 126.833 99.589C147.642 109.997 171.864 96.6309 173.735 73.9522C174.836 60.1478 168.01 48.0963 155.789 42.1801C148.633 38.6743 139.054 37.6882 132.338 39.8794Z" fill="url(#gradient_1)" /> <path id="Circle_4" d="M86.2071 102.657C82.4638 103.314 77.2892 109.121 76.5185 113.284C74.0963 125.883 90.1707 134.319 99.3089 125.226C108.998 115.475 99.9695 99.9176 86.2071 102.657Z" fill="url(#gradient_1)"/> <path id="Circle_3" d="M122.98 110.107C102.612 117.338 107.786 147.904 129.366 147.904C147.202 147.904 155.789 127.417 143.238 114.927C139.605 111.312 133.109 108.354 128.815 108.463C127.934 108.573 125.292 109.23 122.98 110.107Z" fill="url(#gradient_1)"/> <g id="Nuub"> <path id="Vector" d="M388.648 54.6699V83.0456L391.731 88.8522C393.933 93.125 396.245 95.6449 400.098 97.9456C415.842 107.696 435.55 97.5074 436.871 79.1015C438.082 61.6816 419.586 48.7537 403.511 55.7654C400.649 56.9706 398.116 58.0662 397.896 58.0662C397.676 58.0662 397.456 50.9449 397.456 42.1801V26.2941H393.052H388.648V54.6699ZM422.888 67.8169C426.742 71.6515 427.182 72.6375 427.182 77.7868C427.182 82.936 426.742 83.9221 422.888 87.7566C419.035 91.5912 418.044 92.0294 412.87 92.0294C407.695 92.0294 406.704 91.5912 402.851 87.7566C398.997 83.9221 398.557 82.936 398.557 77.7868C398.557 72.6375 398.997 71.6515 402.851 67.8169C406.704 63.9824 407.695 63.5441 412.87 63.5441C418.044 63.5441 419.035 63.9824 422.888 67.8169Z" fill="black"/> <path id="Vector_2" d="M217.995 56.6419C213.591 58.9426 211.059 61.2434 208.637 65.1875C205.444 70.3368 205.334 70.8846 205.003 86.2228L204.673 101.89H209.077H213.591V90.6052C213.701 75.8147 214.802 71.5419 219.536 67.05C222.839 64.0919 224.271 63.5441 229.005 63.5441C233.739 63.5441 235.17 64.0919 238.473 67.05C243.208 71.5419 244.309 75.8147 244.419 90.6052V101.89H248.823H253.227V87.2088C253.227 73.2949 253.117 72.3088 250.144 66.7213C246.07 59.0522 239.134 54.5603 230.326 53.9029C225.041 53.5743 223.06 54.0125 217.995 56.6419Z" fill="black"/> <path id="Vector_3" d="M265.337 68.3647C265.337 82.2787 265.448 83.2647 268.42 88.8522C272.824 97.2882 279.65 101.342 289.559 101.342C299.468 101.342 306.294 97.2882 310.698 88.8522C313.671 83.2647 313.781 82.2787 313.781 68.3647V53.6838H309.377H304.973V64.8588C304.863 79.7588 303.762 84.0316 299.028 88.414C295.725 91.4816 294.293 92.0294 289.559 92.0294C284.825 92.0294 283.394 91.4816 280.091 88.414C275.356 84.0316 274.255 79.7588 274.145 64.8588V53.6838H269.741H265.337V68.3647Z" fill="black"/> <path id="Vector_4" d="M326.993 68.3647C326.993 82.2787 327.103 83.2647 330.075 88.8522C334.479 97.2882 341.305 101.342 351.214 101.342C361.123 101.342 367.949 97.2882 372.353 88.8522C375.326 83.2647 375.436 82.2787 375.436 68.3647V53.6838H371.032H366.628V64.8588C366.518 79.7588 365.417 84.0316 360.683 88.414C357.38 91.4816 355.949 92.0294 351.214 92.0294C346.48 92.0294 345.049 91.4816 341.746 88.414C337.012 84.0316 335.911 79.7588 335.8 64.8588V53.6838H331.397H326.993V68.3647Z" fill="black"/> </g> </g> </g> </svg> </div> <div class="buttons"> <button>Services</button> <button>Calculate Price</button> <button>About Us</button> </div> </div> Any help is more than welcome, thank you so much! :)
How to add a Fade out effect in an HTML canvas
I am trying to add a fade out effect (that happens after half a second after it has been drawn) to an arch in HTML Canvas. I am able to draw a circle. This is my code to draw the arch. ctx.beginPath(); ctx.fillStyle = "#000000"; ctx.arc(Xcoord, Ycoord, 50, 0, 2 * Math.PI, false); ctx.fill(); ctx.lineWidth = "4"; ctx.strokeStyle = "#000000"; ctx.stroke(); ctx.closePath(); Is there a way I can extend this to add a fade out effect that starts 0.5 seconds after it has been drawn.
My solution is based on pure JavaScript. Set an interval. Set opacity if none. Reduce opacity until 0. Clear interval after opacity reaches 0. var canvas = document.getElementById("myCanvas"); var ctx = canvas.getContext("2d"); ctx.fillStyle = "#FF0000"; ctx.fillRect(0,0,200,100); setTimeout(function(){ var fadeTarget = document.getElementById("myCanvas"); var fadeEffect = setInterval(function () { if (!fadeTarget.style.opacity) { fadeTarget.style.opacity = 1; } if (fadeTarget.style.opacity > 0) { fadeTarget.style.opacity -= 0.02; } else { clearInterval(fadeEffect); } }, 20); },0.5); <canvas id="myCanvas" width="200" height="100" style="border:1px solid #c3c3c3;"> Your browser does not support the canvas element. </canvas>
A great canvas 2d resource is MDN's CanvasRenderingContext2D This is the second question on the same topic I have answered recently. The following answer on how to fade content on the canvas will explain how to render animated (fading) content. Timing The example below using the time passed to the mainLoop function by requestAnimationFrame creates the various time related animated FX. Time is stored in globalTime and used as needed The animation timing is in the bottom function drawContent and uses the variables animateStart (how long before starting) and animateLen (how long the animation is) Then from that a value from 0-1 (in animPos) is passed to the function donut.draw and used to animate. donut.draw also uses the globalTime to change the icing color. var globalTime; // set in mainLoop var startTime; // undefined means it is reset var animateStart = 2500; // in ms time till animation starts 2.5 seconds var animateLen = 10000; // in ms length of animation 10 seconds function drawContent() { var animPos = 0; // default anim pos // check if animation need reset. If so set start time if (startTime === undefined) { startTime = globalTime + animateStart } // use the dif between global and start times to get the animation pos const t = globalTime - startTime; if (t >= 0 && t < animateLen) { animPos = t / (animateLen / 2); animPos = animPos > 1 ? 1- (animPos - 1) : animPos; } else if(t >= animateLen) { startTime = undefined } // resets animation donut.draw(Math.easeInOut(animPos)); } Fading The fading is performed on the sprinkles using globalAlpha. and there is also some fading on the shadow cast on the donut by the icing. (using globalAlpha and globalCompositeOperation = "source-atop" ) The highlight line on the icing is also faded by using the CSS color format rgba(255,255,255, 0.7) where the last value is the alpha amount (from 0 - 1). Why the over the top demo? I am not a fan of SVG and as Vivek Raju's SVG example is so pretty it may sway people away from the better canvas 2D or WebGL solutions, which can be far more dynamic and interesting. So the example below recreates the same content using pure JS and the CanvasRenderingContext2D API. Demo Math.TAU = Math.PI * 2; // set up 2 PI Math.PI90 = Math.PI / 2; // 90 deg Math.easeInOut = (v, p = 2) => v < 0 ? 0 : v > 1 ? 1 : v ** p / (v ** p + (1 - v) ** p) Math.rand = (val) => Math.random() * val; Math.randI = (val) => Math.random() * val | 0; requestAnimationFrame(mainLoop); CanvasRenderingContext2D.prototype.setStyle = function(style) { for(const [key, value] of Object.entries(style)) { this[key] = value } } const ctx = canvas.getContext("2d"); const W = canvas.width = innerWidth; // size canvas to page const H = canvas.height = innerHeight * 1.5; const sprinkle = (x, y, col, size, ang) => ({x,y,col,size,ang}); const donut = { styles: { main: { // the icing lineWidth: 3, strokeStyle: "#000", fillStyle: "#FCC", }, inner: { // the donut lineWidth: 2, strokeStyle: "#000", fillStyle: "#DB8", }, highlight: { lineWidth: 2, strokeStyle: "rgba(255,255,255,0.8)", }, sprinkle: { lineWidth: 2, strokeStyle: "#000", } }, x: W * (1/2), y: H * (1/3) - H * (1/16) , r: Math.min(W * (1 / 2.5), H * (1 / 4)), highlightOffset: 4, drips:11, // best that this value is odd and >= 3 sprinkleCount: 7, resetDrips: 0, init() { var i = 0; this.dripLens = []; this.sprinkles = []; while (i < this.drips) { this.dripLens.push(Math.rand(1) + ((i + 1) % 2)); i++; } i = 0; const dr = this.r / this.drips; const innerR = this.r * (1/3); while (i < this.sprinkleCount) { const a = (i / (this.sprinkleCount - 1)) * (Math.PI + 1) + Math.PI - 0.5; const dist = Math.rand(this.r - innerR) * 0.4; const col = "#" + Math.randI(0xFFF).toString(16).padStart(3,"0").replace(/[0-9]/g,"A"); const ang = Math.rand(Math.TAU); const x = Math.cos(a) * (dist + innerR * 1.75) + this.x; const y = Math.sin(a) * (dist + innerR * 1.75) + this.y; this.sprinkles.push(sprinkle(x,y,col, dr*0.85, ang)); i++; } }, draw(dripScale) { const y = this.y + this.r * (1/1.5); const dr = this.r / this.drips; const dripLen = this.r * (1/2.3) * dripScale; const innerR = this.r * (1/3); const ho = this.highlightOffset; const lw = this.styles.main.lineWidth; var x = this.x + this.r - dr, xx = x - dr * 2; var i = 0; ctx.setStyle(this.styles.inner); ctx.beginPath(); ctx.arc(this.x, this.y, this.r - lw, 0, Math.TAU); ctx.arc(this.x, this.y, innerR + lw, 0, Math.TAU); ctx.fill("evenodd"); ctx.stroke(); ctx.setStyle(this.styles.main); const lum = Math.sin(globalTime / (250 * 90)) * 20 + 50; ctx.fillStyle = "hsl("+(globalTime / 250) % 360 +",100%,"+lum+"%)" ctx.beginPath(); ctx.arc(this.x, this.y, this.r, Math.PI, Math.TAU); while (i < this.drips) { const dy = this.dripLens[i++] * dripLen; if (i % 2) { ctx.arc(x, y + dy, dr, 0, Math.PI); } else { ctx.arc(x, y + dy, dr,0, -Math.PI, true); } x -= dr * 2; } ctx.closePath(); ctx.moveTo(this.x + innerR, this.y); ctx.arc(this.x, this.y, innerR, 0, Math.TAU); ctx.globalCompositeOperation = "source-atop"; ctx.globalAlpha = 0.25; ctx.lineWidth = this.styles.main.lineWidth * 3; ctx.stroke(); ctx.globalAlpha = 1; ctx.globalCompositeOperation = "source-over"; ctx.lineWidth = this.styles.main.lineWidth; ctx.fill("evenodd"); ctx.stroke(); ctx.setStyle(this.styles.highlight); ctx.beginPath(); ctx.arc(this.x, this.y, innerR + ho, 0, Math.PI90); i = 0; while (i < this.drips - 1) { const dy1 = this.dripLens[i++] * dripLen; const dy2 = this.dripLens[i++] * dripLen; ctx.moveTo(xx + dr + ho, y + dy1); ctx.arc(xx, y + dy2, dr + ho, 0, -Math.PI90, true); xx -= dr * 4; } const dy1 = this.dripLens[i] * dripLen; ctx.moveTo(xx + dr + ho, y + dy1); ctx.arc(this.x, this.y, this.r - ho, Math.PI , Math.PI + Math.PI90); ctx.stroke(); i = 4; ctx.setStyle(this.styles.sprinkle); for (const spr of this.sprinkles) { const dy = ((spr.y - (this.y - this.r)) / this.r) * 5 * dripScale; ctx.globalAlpha = Math.sin(globalTime / ((1 + i) * 200)) * 0.4 + 0.6; ctx.setTransform(1,0,0,1, spr.x, spr.y + dy); ctx.rotate(spr.ang + dy * 0.2 * (i++ % 2 ? -1 : 1)); ctx.fillStyle = spr.col; ctx.beginPath(); ctx.arc(0, -spr.size, spr.size / 2, Math.PI, Math.TAU); ctx.arc(0, spr.size, spr.size / 2, 0, Math.PI); ctx.closePath(); ctx.fill(); ctx.stroke(); } ctx.globalAlpha = 1; ctx.setTransform(1, 0, 0, 1, 0, 0); if (this.resetDrips === 0 && dripScale < 0.01) { this.resetDrips = 1; i = 0; while (i < this.dripLens.length) { this.dripLens[i] = Math.rand(1) + 0.1 + ((i + 1) % 2); i++; } } else if (this.resetDrips === 1 && dripScale > 0.9) { this.resetDrips = 0; } } }; donut.init(); var globalTime; var startTime; var animateStart = 2500; // 2.5 seconds var animateLen = 10000; // 10 seconds function mainLoop(time) { globalTime = time; ctx.globalAlpha = 1; ctx.clearRect(0, 0, W, H); drawContent(); requestAnimationFrame(mainLoop); } function drawContent() { var animPos = 0; if (startTime === undefined) { startTime = globalTime + animateStart } const t = globalTime - startTime; if (t >= 0 && t < animateLen) { animPos = t / (animateLen / 2); animPos = animPos > 1 ? 1- (animPos - 1) : animPos; } else if(t >= animateLen) { startTime = undefined } // resets animation donut.draw(Math.easeInOut(animPos)); } body { padding: 0px; } canvas { position: absolute; top: 0px; left: 0px; } <canvas id="canvas"></canvas>
Hover to the SVG file: body { text-align: center; } #donut-icing { fill: #FA9CB6; transition: fill 3s ease-out; } #donut:hover { cursor: pointer; } #donut:hover #donut-icing { fill: #4a8af4; } <html> <head> <title>SVG Donut Animated on Hover with CSS / Sass</title> </head> <body> <svg id="donut" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="204" height="203" viewBox="0 0 204 203"> <defs> <linearGradient id="donut-a" x1="50%" x2="50%" y1="59.542%" y2="98.874%"> <stop offset="0%" stop-color="#F8F5EA"/> <stop offset="100%" stop-color="#E1D6B6"/> </linearGradient> <path id="donut-icing" fill="#FA9CB6" d="M200,100 C200,44.771525 155.228475,0 100,0 C44.771525,0 0,44.771525 0,100 M0,100 L0,156.203125 C0,159.048145 2.12004279,161.359375 4.63917526,161.359375 C7.35102845,161.359375 9.47049623,159.052855 9.27835052,156.203125 L9.27835052,148.46875 C9.4934916,145.61902 11.6018043,143.3125 14.4329897,143.3125 C16.8052584,143.3125 18.9141431,145.62373 19.0721649,148.46875 L19.0721649,156.203125 L19.0721649,168.0625 C18.9141431,170.90752 21.0230278,173.21875 23.7113402,173.21875 C26.2264819,173.21875 28.3347946,170.91223 28.3505155,168.0625 L28.3505155,161.359375 L28.3505155,154.140625 C28.3347946,151.291053 30.4431073,148.984375 32.9896907,148.984375 C35.6465615,148.984375 37.7554461,151.295764 37.628866,154.140625 L37.628866,193.84375 C37.7554461,196.68877 39.8643308,199 42.2680412,199 C45.067785,199 47.1760977,196.69348 47.4226804,193.84375 L47.4226804,131.453125 C47.1760977,128.603395 49.2844103,126.296875 52.0618557,126.296875 C54.4878645,126.296875 56.5967492,128.608105 56.7010309,131.453125 L56.7010309,176.3125 C56.5967492,179.15752 58.7056338,181.46875 61.3402062,181.46875 C63.909088,181.46875 66.0174007,179.16223 65.9793814,176.3125 L65.9793814,141.765625 C66.0174007,138.916053 68.1257134,136.609375 70.6185567,136.609375 C73.3291675,136.609375 75.4380522,138.920764 75.257732,141.765625 L75.257732,160.84375 C75.4380522,163.688611 77.5469369,166 79.8969072,166 C82.750391,166 84.8587037,163.693322 85.0515464,160.84375 L85.0515464,153.625 C84.8587037,150.775428 86.9670164,148.46875 89.6907216,148.46875 C92.1704706,148.46875 94.2793552,150.780139 94.3298969,153.625 L94.8453608,187.140625 C94.8027248,189.985486 97.14593,192.296875 100,192.296875 C102.927546,192.296875 105.270115,189.990197 105.154639,187.140625 L104.639175,149.5 C104.746746,145.61902 106.855058,143.3125 109.278351,143.3125 C112.058513,143.3125 114.167397,145.62373 113.917526,148.46875 L113.917526,168.0625 C114.167397,170.90752 116.276282,173.21875 119.072165,173.21875 C121.479736,173.21875 123.588049,170.91223 123.71134,168.0625 L123.71134,161.359375 L123.71134,154.140625 C123.588049,151.291053 125.696362,148.984375 128.350515,148.984375 C130.899816,148.984375 133.0087,151.295764 132.989691,154.140625 L132.989691,193.84375 C133.0087,196.68877 135.117585,199 137.628866,199 C140.321039,199 142.429352,196.69348 142.268041,193.84375 L142.268041,138.671875 L142.268041,131.453125 C142.429352,128.603395 144.537665,126.296875 146.907216,126.296875 C149.741119,126.296875 151.850003,128.608105 152.061856,131.453125 L152.061856,139.1875 L152.061856,171.15625 C151.850003,174.00127 153.958888,176.3125 156.701031,176.3125 C159.162342,176.3125 161.270655,174.00598 161.340206,171.15625 L161.340206,144.859375 L161.340206,141.765625 C161.270655,138.916053 163.378968,136.609375 165.979381,136.609375 C168.582422,136.609375 170.691306,138.920764 170.618557,141.765625 L170.618557,144.859375 L170.618557,176.3125 C170.629257,179.157361 172.738142,181.46875 175.257732,181.46875 C177.941596,181.46875 180.049909,179.162072 179.896907,176.3125 L179.896907,153.625 C180.111958,150.775428 182.220271,148.46875 185.051546,148.46875 C187.423725,148.46875 189.532609,150.780139 189.690722,153.625 L189.690722,163.421875 C189.532609,166.266736 191.875815,168.578125 194.845361,168.578125 C197.65743,168.578125 200,166.271447 200,163.421875 L200,100"/> </defs> <g fill="none" fill-rule="evenodd" transform="translate(2 2)"> <circle cx="100" cy="100" r="100" fill="url(#donut-a)"/> <path stroke="#302434" stroke-linecap="square" stroke-width="2" d="M180,160 C180.163288,159.782085 185.314605,152.363686 187,149 L180,160 Z M48,185 C63.1555058,194.519493 80.9387639,200 99.9750338,200 C111.534055,200 122.652906,197.969392 133,194.15209"/> <mask id="donut-c" fill="#fff"> <use xlink:href="#donut-icing"/> </mask> <use fill="#FA9CB6" xlink:href="#donut-icing"/> <path d="M200,100 C200,44.771525 155.228475,0 100,0 C44.771525,0 0,44.771525 0,100 M0,100 L0,156.203125 C0,159.048145 2.12004279,161.359375 4.63917526,161.359375 C7.35102845,161.359375 9.47049623,159.052855 9.27835052,156.203125 L9.27835052,148.46875 C9.4934916,145.61902 11.6018043,143.3125 14.4329897,143.3125 C16.8052584,143.3125 18.9141431,145.62373 19.0721649,148.46875 L19.0721649,156.203125 L19.0721649,168.0625 C18.9141431,170.90752 21.0230278,173.21875 23.7113402,173.21875 C26.2264819,173.21875 28.3347946,170.91223 28.3505155,168.0625 L28.3505155,161.359375 L28.3505155,154.140625 C28.3347946,151.291053 30.4431073,148.984375 32.9896907,148.984375 C35.6465615,148.984375 37.7554461,151.295764 37.628866,154.140625 L37.628866,193.84375 C37.7554461,196.68877 39.8643308,199 42.2680412,199 C45.067785,199 47.1760977,196.69348 47.4226804,193.84375 L47.4226804,131.453125 C47.1760977,128.603395 49.2844103,126.296875 52.0618557,126.296875 C54.4878645,126.296875 56.5967492,128.608105 56.7010309,131.453125 L56.7010309,176.3125 C56.5967492,179.15752 58.7056338,181.46875 61.3402062,181.46875 C63.909088,181.46875 66.0174007,179.16223 65.9793814,176.3125 L65.9793814,141.765625 C66.0174007,138.916053 68.1257134,136.609375 70.6185567,136.609375 C73.3291675,136.609375 75.4380522,138.920764 75.257732,141.765625 L75.257732,160.84375 C75.4380522,163.688611 77.5469369,166 79.8969072,166 C82.750391,166 84.8587037,163.693322 85.0515464,160.84375 L85.0515464,153.625 C84.8587037,150.775428 86.9670164,148.46875 89.6907216,148.46875 C92.1704706,148.46875 94.2793552,150.780139 94.3298969,153.625 L94.8453608,187.140625 C94.8027248,189.985486 97.14593,192.296875 100,192.296875 C102.927546,192.296875 105.270115,189.990197 105.154639,187.140625 L104.639175,149.5 C104.746746,145.61902 106.855058,143.3125 109.278351,143.3125 C112.058513,143.3125 114.167397,145.62373 113.917526,148.46875 L113.917526,168.0625 C114.167397,170.90752 116.276282,173.21875 119.072165,173.21875 C121.479736,173.21875 123.588049,170.91223 123.71134,168.0625 L123.71134,161.359375 L123.71134,154.140625 C123.588049,151.291053 125.696362,148.984375 128.350515,148.984375 C130.899816,148.984375 133.0087,151.295764 132.989691,154.140625 L132.989691,193.84375 C133.0087,196.68877 135.117585,199 137.628866,199 C140.321039,199 142.429352,196.69348 142.268041,193.84375 L142.268041,138.671875 L142.268041,131.453125 C142.429352,128.603395 144.537665,126.296875 146.907216,126.296875 C149.741119,126.296875 151.850003,128.608105 152.061856,131.453125 L152.061856,139.1875 L152.061856,171.15625 C151.850003,174.00127 153.958888,176.3125 156.701031,176.3125 C159.162342,176.3125 161.270655,174.00598 161.340206,171.15625 L161.340206,144.859375 L161.340206,141.765625 C161.270655,138.916053 163.378968,136.609375 165.979381,136.609375 C168.582422,136.609375 170.691306,138.920764 170.618557,141.765625 L170.618557,144.859375 L170.618557,176.3125 C170.629257,179.157361 172.738142,181.46875 175.257732,181.46875 C177.941596,181.46875 180.049909,179.162072 179.896907,176.3125 L179.896907,153.625 C180.111958,150.775428 182.220271,148.46875 185.051546,148.46875 C187.423725,148.46875 189.532609,150.780139 189.690722,153.625 L189.690722,163.421875 C189.532609,166.266736 191.875815,168.578125 194.845361,168.578125 C197.65743,168.578125 200,166.271447 200,163.421875 L200,100" mask="url(#donut-c)"/> <path stroke="#302434" stroke-linecap="square" stroke-width="3" d="M200 100C200 44.771525 155.228475 0 100 0 44.771525 0 0 44.771525 0 100M0 100L0 156.203125C0 159.048145 2.12004279 161.359375 4.63917526 161.359375 7.35102845 161.359375 9.47049623 159.052855 9.27835052 156.203125L9.27835052 148.46875C9.4934916 145.61902 11.6018043 143.3125 14.4329897 143.3125 16.8052584 143.3125 18.9141431 145.62373 19.0721649 148.46875L19.0721649 156.203125 19.0721649 168.0625C18.9141431 170.90752 21.0230278 173.21875 23.7113402 173.21875 26.2264819 173.21875 28.3347946 170.91223 28.3505155 168.0625L28.3505155 161.359375 28.3505155 154.140625C28.3347946 151.291053 30.4431073 148.984375 32.9896907 148.984375 35.6465615 148.984375 37.7554461 151.295764 37.628866 154.140625L37.628866 193.84375C37.7554461 196.68877 39.8643308 199 42.2680412 199 45.067785 199 47.1760977 196.69348 47.4226804 193.84375L47.4226804 131.453125C47.1760977 128.603395 49.2844103 126.296875 52.0618557 126.296875 54.4878645 126.296875 56.5967492 128.608105 56.7010309 131.453125L56.7010309 176.3125C56.5967492 179.15752 58.7056338 181.46875 61.3402062 181.46875 63.909088 181.46875 66.0174007 179.16223 65.9793814 176.3125L65.9793814 141.765625C66.0174007 138.916053 68.1257134 136.609375 70.6185567 136.609375 73.3291675 136.609375 75.4380522 138.920764 75.257732 141.765625L75.257732 160.84375C75.4380522 163.688611 77.5469369 166 79.8969072 166 82.750391 166 84.8587037 163.693322 85.0515464 160.84375L85.0515464 153.625C84.8587037 150.775428 86.9670164 148.46875 89.6907216 148.46875 92.1704706 148.46875 94.2793552 150.780139 94.3298969 153.625L94.8453608 187.140625C94.8027248 189.985486 97.14593 192.296875 100 192.296875 102.927546 192.296875 105.270115 189.990197 105.154639 187.140625L104.639175 149.5C104.746746 145.61902 106.855058 143.3125 109.278351 143.3125 112.058513 143.3125 114.167397 145.62373 113.917526 148.46875L113.917526 168.0625C114.167397 170.90752 116.276282 173.21875 119.072165 173.21875 121.479736 173.21875 123.588049 170.91223 123.71134 168.0625L123.71134 161.359375 123.71134 154.140625C123.588049 151.291053 125.696362 148.984375 128.350515 148.984375 130.899816 148.984375 133.0087 151.295764 132.989691 154.140625L132.989691 193.84375C133.0087 196.68877 135.117585 199 137.628866 199 140.321039 199 142.429352 196.69348 142.268041 193.84375L142.268041 138.671875 142.268041 131.453125C142.429352 128.603395 144.537665 126.296875 146.907216 126.296875 149.741119 126.296875 151.850003 128.608105 152.061856 131.453125L152.061856 139.1875 152.061856 171.15625C151.850003 174.00127 153.958888 176.3125 156.701031 176.3125 159.162342 176.3125 161.270655 174.00598 161.340206 171.15625L161.340206 144.859375 161.340206 141.765625C161.270655 138.916053 163.378968 136.609375 165.979381 136.609375 168.582422 136.609375 170.691306 138.920764 170.618557 141.765625L170.618557 144.859375 170.618557 176.3125C170.629257 179.157361 172.738142 181.46875 175.257732 181.46875 177.941596 181.46875 180.049909 179.162072 179.896907 176.3125L179.896907 153.625C180.111958 150.775428 182.220271 148.46875 185.051546 148.46875 187.423725 148.46875 189.532609 150.780139 189.690722 153.625L189.690722 163.421875C189.532609 166.266736 191.875815 168.578125 194.845361 168.578125 197.65743 168.578125 200 166.271447 200 163.421875L200 100"/> <circle cx="100.5" cy="100.5" r="26.5" fill="#FFF" stroke="#302434" stroke-linecap="square" stroke-width="3"/> <path stroke="#FFF" stroke-linecap="round" stroke-width="2" d="M103,132 L103,132 C119.016258,132 132,119.016258 132,103" opacity=".7"/> <path fill="#F7DA71" stroke="#302434" stroke-width="2.062" d="M168.854871,80.8314322 L168.854871,81.1126307 C168.854871,82.6700566 170.123055,83.9411037 171.680144,83.9411037 L183.967742,83.9411037 C185.519686,83.9411037 186.793015,82.6697696 186.793015,81.1126307 L186.793015,80.8314322 C186.793015,79.2740064 185.52483,78.0029593 183.967742,78.0029593 L171.680144,78.0029593 C170.1282,78.0029593 168.854871,79.2742934 168.854871,80.8314322 Z" transform="rotate(-134 177.824 80.972)"/> <path fill="#FFAD38" stroke="#302434" stroke-width="2.062" d="M170.784479,122.710815 L170.784479,122.992013 C170.784479,124.549439 172.052663,125.820486 173.609752,125.820486 L185.89735,125.820486 C187.449294,125.820486 188.722623,124.549152 188.722623,122.992013 L188.722623,122.710815 C188.722623,121.153389 187.454438,119.882342 185.89735,119.882342 L173.609752,119.882342 C172.057808,119.882342 170.784479,121.153676 170.784479,122.710815 Z" transform="rotate(25 179.754 122.851)"/> <path fill="#9FC1EA" stroke="#302434" stroke-width="2.062" d="M137.779864,103.591935 L137.779864,103.873134 C137.779864,105.430559 139.048049,106.701607 140.605137,106.701607 L152.892735,106.701607 C154.444679,106.701607 155.718008,105.430272 155.718008,103.873134 L155.718008,103.591935 C155.718008,102.034509 154.449824,100.763462 152.892735,100.763462 L140.605137,100.763462 C139.053193,100.763462 137.779864,102.034796 137.779864,103.591935 Z" transform="rotate(-73 146.749 103.733)"/> <path fill="#FFAD38" stroke="#302434" stroke-width="2.062" d="M69.2022598,19.4388294 L69.2022598,19.7200279 C69.2022598,21.2774537 70.4704445,22.5485008 72.0275331,22.5485008 L84.3151307,22.5485008 C85.8670745,22.5485008 87.1404041,21.2771667 87.1404041,19.7200279 L87.1404041,19.4388294 C87.1404041,17.8814035 85.8722194,16.6103564 84.3151307,16.6103564 L72.0275331,16.6103564 C70.4755893,16.6103564 69.2022598,17.8816905 69.2022598,19.4388294 Z" transform="rotate(-41 78.171 19.58)"/> <path fill="#F45467" stroke="#302434" stroke-width="2.062" d="M63.4327156,63.1481419 L63.4327156,63.4293404 C63.4327156,64.9867663 64.7009003,66.2578133 66.2579889,66.2578133 L78.5455866,66.2578133 C80.0975304,66.2578133 81.3708599,64.9864793 81.3708599,63.4293404 L81.3708599,63.1481419 C81.3708599,61.5907161 80.1026752,60.319669 78.5455866,60.319669 L66.2579889,60.319669 C64.7060452,60.319669 63.4327156,61.5910031 63.4327156,63.1481419 Z" transform="rotate(-51 72.402 63.289)"/> <path fill="#9FC1EA" stroke="#302434" stroke-width="2.062" d="M93.4950295,44.5196548 L93.4950295,44.8008533 C93.4950295,46.3582791 94.7632142,47.6293262 96.3203028,47.6293262 L108.6079,47.6293262 C110.159844,47.6293262 111.433174,46.3579921 111.433174,44.8008533 L111.433174,44.5196548 C111.433174,42.9622289 110.164989,41.6911819 108.6079,41.6911819 L96.3203028,41.6911819 C94.768359,41.6911819 93.4950295,42.962516 93.4950295,44.5196548 Z" transform="rotate(-120 102.464 44.66)"/> <path fill="#F45467" stroke="#302434" stroke-width="2.062" d="M116.763462,17.6083368 L116.763462,17.8895353 C116.763462,19.4469612 118.031647,20.7180082 119.588736,20.7180082 L131.876333,20.7180082 C133.428277,20.7180082 134.701607,19.4466741 134.701607,17.8895353 L134.701607,17.6083368 C134.701607,16.050911 133.433422,14.7798639 131.876333,14.7798639 L119.588736,14.7798639 C118.036792,14.7798639 116.763462,16.051198 116.763462,17.6083368 Z" transform="rotate(-163 125.733 17.749)"/> <path fill="#9FC1EA" stroke="#302434" stroke-width="2.062" d="M31.4950295,42.5196548 L31.4950295,42.8008533 C31.4950295,44.3582791 32.7632142,45.6293262 34.3203028,45.6293262 L46.6079004,45.6293262 C48.1598442,45.6293262 49.4331738,44.3579921 49.4331738,42.8008533 L49.4331738,42.5196548 C49.4331738,40.9622289 48.1649891,39.6911819 46.6079004,39.6911819 L34.3203028,39.6911819 C32.768359,39.6911819 31.4950295,40.962516 31.4950295,42.5196548 Z" transform="rotate(-120 40.464 42.66)"/> <path fill="#9FC1EA" stroke="#302434" stroke-width="2.062" d="M7.00295932,122.683344 L7.00295932,122.964542 C7.00295932,124.521968 8.27114404,125.793015 9.82823266,125.793015 L22.1158303,125.793015 C23.6677741,125.793015 24.9411037,124.521681 24.9411037,122.964542 L24.9411037,122.683344 C24.9411037,121.125918 23.6729189,119.854871 22.1158303,119.854871 L9.82823266,119.854871 C8.27628888,119.854871 7.00295932,121.126205 7.00295932,122.683344 Z" transform="rotate(44 15.972 122.824)"/> <path fill="#9FC1EA" stroke="#302434" stroke-width="2.062" d="M143.573598,38.5351135 L143.573598,38.816312 C143.573598,40.3737379 144.841783,41.644785 146.398871,41.644785 L158.686469,41.644785 C160.238413,41.644785 161.511742,40.3734509 161.511742,38.816312 L161.511742,38.5351135 C161.511742,36.9776877 160.243558,35.7066406 158.686469,35.7066406 L146.398871,35.7066406 C144.846928,35.7066406 143.573598,36.9779747 143.573598,38.5351135 Z" transform="rotate(10 152.543 38.676)"/> <path fill="#FFAD38" stroke="#302434" stroke-width="2.062" d="M13.7798639,78.5919351 L13.7798639,78.8731336 C13.7798639,80.4305595 15.0480486,81.7016065 16.6051372,81.7016065 L28.8927349,81.7016065 C30.4446787,81.7016065 31.7180082,80.4302725 31.7180082,78.8731336 L31.7180082,78.5919351 C31.7180082,77.0345093 30.4498235,75.7634622 28.8927349,75.7634622 L16.6051372,75.7634622 C15.0531935,75.7634622 13.7798639,77.0347963 13.7798639,78.5919351 Z" transform="rotate(-73 22.749 78.733)"/> <path fill="#FFAD38" stroke="#302434" stroke-width="2.062" d="M133.763462,61.6083368 L133.763462,61.8895353 C133.763462,63.4469612 135.031647,64.7180082 136.588736,64.7180082 L148.876333,64.7180082 C150.428277,64.7180082 151.701607,63.4466741 151.701607,61.8895353 L151.701607,61.6083368 C151.701607,60.050911 150.433422,58.7798639 148.876333,58.7798639 L136.588736,58.7798639 C135.036792,58.7798639 133.763462,60.051198 133.763462,61.6083368 Z" transform="rotate(-17 142.733 61.749)"/> <path fill="#F7DA71" stroke="#302434" stroke-width="2.062" d="M40.1448483,100.300712 L40.1448483,100.58191 C40.1448483,102.139336 41.4130331,103.410383 42.9701217,103.410383 L55.2577193,103.410383 C56.8096631,103.410383 58.0829927,102.139049 58.0829927,100.58191 L58.0829927,100.300712 C58.0829927,98.7432859 56.8148079,97.4722388 55.2577193,97.4722388 L42.9701217,97.4722388 C41.4181779,97.4722388 40.1448483,98.7435729 40.1448483,100.300712 Z" transform="rotate(54 49.114 100.441)"/> <path stroke="#302434" stroke-linecap="square" stroke-width="2" d="M143 190C153.043627 185.195415 162.158445 178.746675 170 171M28.7212226 170.713933C31.3439197 173.284559 34.1076215 175.717271 37 178M11.5217511 145.997463C13.5503966 149.823796 15.8170146 153.505818 18.3020045 157.024023"/> <path stroke="#FFF" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18 140C20.5567785 140 22.8296876 142.321236 23 145.178571L23 152.946429 23 169M37 146C39.5567785 146 41.8296876 148.311968 42 151.157895L42 158.894737 42 195M56 123C58.5567785 123 60.8296876 125.305227 61 128.142857L61 135.857143 61 177M151 122C153.556779 122 155.829688 124.311968 156 127.157895L156 134.894737 156 171M74 133C76.5567785 133 78.8296876 135.3242 79 138.185185L79 145.962963 79 161M94 145C96.5567785 145 98.8296876 147.3242 99 150.185185L99 157.962963 99 187M113 139C115.556779 139 117.829688 141.280512 118 144.087719L118 151.719298 118 168M132 145C134.556779 145 136.829688 147.311968 137 150.157895L137 157.894737 137 194M170 133C172.301101 133 174.346719 135.293314 174.5 138.116279L174.5 145.790698 175 177M189 144C191.556779 144 193.829688 146.301766 194 149.135135L194 156.837838 194 163" opacity=".7"/> </g> </svg> </body> </html>
How to apply rotation to graphic elements and get new path data?
How do you apply path rotation to graphic elements such as a rectangle or a path? For example, applying rotation to a path: <svg width="200px" height="200px" viewbox="0 0 200 200"> <rect x="100" y="0" width="20" height="20" fill="red" transform="rotate(45)"/> </svg> IMPORTANT NOTE: I'm not running an browser. I remember seeing solutions that use browser or canvas to do the calculations. I only have the markup. For a path I have the path data, for a rectangle the position and width and height, and the line the x1 y1 and x2 y2 data. UPDATE: It's important to know the transform origin. That would be rotated from the element center.
I would use an array of points to draw the path. For the rotation I would rotate the points and draw the rotated shape.Please read the comments in my code. I hope this is what you were asking. const SVG_NS = svg.namespaceURI; // the rotation let angle = Math.PI/4; // the points used to rotate the initial rect let theRect = [ { x: 100, y: 0 }, { x: 100 + 20, y: 0 }, { x: 100 + 20, y: 0 + 20 }, { x: 100, y: 0 + 20 } ]; // calculating the rotated points let rotatedRect = []; theRect.forEach(p => { rotatedRect.push(rotatePoint(p, angle)); }); drawRect(theRect); drawRect(rotatedRect); // a function to draw the rect. For this I'm using the points array function drawRect(ry) { let d = `M${ry[0].x},${ry[0].y}L${ry[1].x},${ry[1].y} ${ry[2].x},${ry[2].y} ${ ry[3].x },${ry[3].y}`; drawSVGelmt({ d: d }, "path", svg); } // a function used to rotate a point around the origin {0,0} function rotatePoint(p, rot) { let cos = Math.cos(rot); let sin = Math.sin(rot); return { x: p.x * cos - p.y * sin, y: p.x * sin + p.y * cos }; } // a function to draw an svg element function drawSVGelmt(o, tag, parent) { let elmt = document.createElementNS(SVG_NS, tag); for (let name in o) { if (o.hasOwnProperty(name)) { elmt.setAttributeNS(null, name, o[name]); } } parent.appendChild(elmt); return elmt; } svg{border:1px solid; max-width:90vh;} <svg id="svg" viewbox="0 0 200 200"> <rect x="100" y="0" width="20" height="20" fill="red" transform="rotate(45)"/> </svg>
Find intersect line with circle is not working in Javascript
I followed the formula to find intersect line with circle. But it does not work. Here is my code. <svg height="397" width="851"> <circle cx="334.4" cy="198.5" r="150.8" stroke="black" stroke-width="3" fill="red" /> <line x1="485.60978255954285" y1="231.75390342766823" x2="488.9584783979701" y2="231.75390342766823" style="stroke:rgb(255,0,0);stroke-width:2" /> Sorry, your browser does not support inline SVG. </svg> <script> var line1point= {x: 485.60978255954285, y: 231.75390342766823}; var line2point= {x: 488.9584783979701, y: 231.75390342766823}; var center = {x: 334.4, y: 198.5}; var intersect = inCircle(line1point, line2point, center, 150.8); alert(intersect); function inCircle (line1point, line2point, center, r){ var l1 = line1point, l2 = line2point, c = center; return (Math.abs((l2.x - l1.x) * c.x + c.y * (l1.y - l2.y) + (l1.x - l2.x) * l1.y + (l1.y - l2.y) * c.y) / Math.sqrt(((l2.x - l1.x) * (l2.x - l1.x)) + ((l1.y - l2.y) *(l1.y - l2.y))) <= r); } </script> Fiddle Link I tried the formula given in this forum second answer. Stackexchange link