I have simple SVG illustration, is there is any way to change it's color constantly ? like a loop non-stop random color change.
here is my svg
<svg width="533" height="499" viewBox="0 0 533 499" fill="none" xmlns="http://www.w3.org/2000/svg">
This is how I would do it: I'm using colors hsl for the fill and I'm animating the hue of the colors using requestAnimationFrame. I hope it helps.
let p1 = document.querySelectorAll("path")[0];
let p2 = document.querySelectorAll("path")[1]
let h = 0;
function changeColor(){
window.requestAnimationFrame(changeColor);
h+=.5;
h2=210+h;
p1.setAttributeNS(null,"fill", `hsl(${~~h},100%,50%)`);
p2.setAttributeNS(null,"fill", `hsl(${~~h2},100%,50%)`);
}
changeColor()
<svg width="533" height="499" viewBox="0 0 533 499" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M258.089 59.6035C309.803 -3.94652 379.363 78.1818 407.679 127.19C352.338 67.4782 301.718 129.7 287.076 167.787C272.435 205.874 233.694 210.043 205.199 217.679C187.359 222.459 146.446 248.26 128.6 264.085C109.864 289.466 48.3081 292.846 41.8378 268.698C27.0852 213.64 95.5238 148.37 137.644 123.97C163.705 101.458 206.375 123.154 258.089 59.6035Z" fill="blue"/>
<path d="M448.323 394.788C427.389 384.661 420.75 356.279 420.047 343.354C441.009 284.421 527.63 350.762 528.167 368.218C528.703 385.674 474.491 407.447 448.323 394.788Z" fill="red"/>
</svg>
Select the element and recursively call a function that sets the fill attribute of the SVG element you want to recolor with a random hex.
const recolor = element => {
const randomColor = '#'+Math.floor(Math.random()*16777215).toString(16)
circle.setAttribute('fill', randomColor)
setTimeout(() => recolor(element), 600)
}
recolor(document.querySelector('#circle'))
svg circle {
transition: fill .5s linear;
}
<svg height="100" width="100">
<circle id="circle" cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red" />
</svg>
fill on <svg> doesnot work. Its for its elements. You can change its background
function random_rgba() {
var o = Math.round, r = Math.random, s = 255;
return 'rgba(' + o(r()*s) + ',' + o(r()*s) + ',' + o(r()*s) + ',' + r().toFixed(1) + ')';
}
function changeColor(){
document.querySelector('svg').style.background = random_rgba();
}
setInterval(changeColor,200);
<svg width="533" height="499" viewBox="0 0 533 499" fill="none" xmlns="http://www.w3.org/2000/svg">
Related
So basically this script uses the output from a range slider to change the radius of an SVG circle. There are two circles however only one of the circles changes in size when the slider is adjusted.
var slider = document.getElementById("myRange");
var output = document.getElementById("circle_radius");
output.innerHTML = slider.value;
slider.oninput = function() {
output.innerHTML = this.value;
update(this.value)
}
var c1 = d3.select("circle")
var c2 = d3.select("circle1")
function update(update_radius) {
c1.transition()
.ease(d3.easeLinear)
.duration(2000)
.delay(100)
.attr("r", update_radius)
c2.transition()
.ease(d3.easeLinear)
.duration(2000)
.attr("r",update_radius)
}
Your issue is with d3.select. You're attempting to select an element like <circle1> rather than an id or class.
You should add id attributes to your circles and select them by id instead.
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<circle id="circle1" cx="50" cy="50" r="50"/>
</svg>
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<circle id="circle2" cx="50" cy="50" r="50"/>
</svg>
Then you can do
var c1 = d3.select("#circle1")
var c2 = d3.select("#circle2")
Since you're doing a delay only for the second circle, here's a more general solution. Group the relevant circles with a class:
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<circle class="slider-circle" cx="50" cy="50" r="50"/>
<circle class="slider-circle" cx="50" cy="50" r="50"/>
</svg>
Then, using a selection with all the circles, transition them with a delay based on the index:
var circles = d3.selectAll(".slider-circle")
function update(update_radius) {
circles.transition()
.ease(d3.easeLinear)
.duration(2000)
.delay((d, i) => i * 100) // i = 0, delay = 0; i = 1, delay = 100
.attr("r", update_radius)
}
I'm trying to move SVG object on <path> guideline I created, my code is based on this one, but I can't figure out why mine doesn't work and the codepen I shared does.
function positionTheElement() {
var html = document.documentElement;
var body = document.body;
var scrollPercentage = (html.scrollTop + body.scrollTop) / (html.scrollHeight - html.clientHeight);
var path = document.getElementById("trace");
var pathLen = path.getTotalLength();
var pt = path.getPointAtLength(scrollPercentage * pathLen );
var element = document.getElementById("etoile");
element.setAttribute("transform", "translate("+ pt.x + "," + pt.y + ")");
};
window.addEventListener("scroll", positionTheElement);
positionTheElement();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 1920 4000" style="enable-background:new 0 0 1920 4000;" xml:space="preserve">
<g>
<path class="st12" id="trace" d="M1491.8,3999l-7.8-365c0,0-1156-14-1240.5-1.3S155,3552,155,3552s3-594-0.3-688.1
c-3.3-94.1,94.3-81.9,94.3-81.9l689,5c0,0,645,5,751.9,6s90.1-93,90.1-93s20-468,14-576s-96.2-89.6-96.2-89.6S288,2039,208,2034.4
S120,1931,120,1931s13-1156,6-1184s4-60,10-89c0-33,141-28,141-28s764-1,867,0s154-11,169-19c36.8-10.9,26.8-27.7,28-53l2.5-525.5"
stroke="tomato" fill="none" stroke-width="4"/>
<polygon class="st11" id="etoile" stroke="tomato" fill="none" stroke-width="4" points="1367.7,59.8 1345.4,49.8 1324.5,62.3 1327.3,38.3 1308.8,22.4 1332.8,17.6 1342.3,-4.7 1354.3,16.3
1378.7,18.4 1362.2,36.2 "/>
</g>
</svg>
How can I animate the SVG polygon on the path guideline correctly?
You have 2 errors:
the star should have the center in the origin of the svg canvas (x=0; y=0).
the path trace has to be reversed. The way it's drawn (from the bottom up) would make the star begin it's movement from the bottom of the document to the top.
function positionTheElement() {
var html = document.documentElement;
var body = document.body;
var scrollPercentage = (html.scrollTop + body.scrollTop) / (html.scrollHeight - html.clientHeight);
var path = document.getElementById("trace");
var pathLen = path.getTotalLength();
var pt = path.getPointAtLength(scrollPercentage * pathLen );
var element = document.getElementById("etoile");
element.setAttribute("transform", "translate("+ pt.x + "," + pt.y + ")");
};
window.addEventListener("scroll", positionTheElement);
positionTheElement();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<svg viewBox="0 0 1920 4000" >
<g>
<path class="st12" id="trace" d="M1343.5,32.5
L1341,558C1339.8,583.3 1349.8,600.1 1313,611C1298,619 1247,631 1144,630C1041,629 277,630 277,630C277,630 136,625 136,658C130,687 119,719 126,747C133,775 120,1931 120,1931C120,1931 128,2029.8000000000002 208,2034.4C288,2039 1697.8,2034.4 1697.8,2034.4C1697.8,2034.4 1788,2016 1794,2124C1800,2232 1780,2700 1780,2700C1780,2700 1796.8000000000002,2794 1689.9,2793C1583,2792 938,2787 938,2787L249,2782C249,2782 151.39999999999998,2769.8 154.7,2863.9C158,2958 155,3552 155,3552C155,3552 159,3645.3999999999996 243.5,3632.7C328,3620 1484,3634 1484,3634L1491.8,3999"
stroke="tomato" fill="none" stroke-width="4"/>
<path class="st11" id="etoile" stroke="tomato" fill="none" stroke-width="4" d="M25,33.5l-22.3,-10l-20.9,12.5l2.8,-24l-18.5,-15.9l24,-4.8l9.5,-22.3l12,21l24.4,2.1l-16.5,17.8z"/>
</g>
</svg>
How do I generate hexagons in random positions on the svg canvas?
Currently, my code uses
.attr("points", "50,25 86,45.83 86,87.5 50,108.3 14,87.53 14,45.83")
which hardcodes the current position of the hexagon. How can I generate other hexagons in different positions while maintaining the hexagonal shape?
Y would create a symbol with a viewBox attribute:
<symbol id="poly" viewBox="14 25 72 83.3">
<polygon points="50,25 86,45.83 86,87.5 50,108.3 14,87.53 14,45.83" />
</symbol>
Since the symbol has a viewBox attribute you can reuse the symbol with <use> and you can specify the position of the hexagon (x and y attributes) and it's size (width and height attributes)
svg{border:1px solid}
<svg viewBox="0 0 500 250">
<symbol id="poly" viewBox="14 25 72 83.3">
<polygon points="50,25 86,45.83 86,87.5 50,108.3 14,87.53 14,45.83" />
</symbol>
<use xlink:href="#poly" x="20" y="20" width="50" height="57.85" />
<use xlink:href="#poly" x="200" y="120" width="100" height="115.7" />
</svg>
Of course the x and y can be random. Also the width or the height can be random. However keep in mind that the other size should be proportional.
This is how I would create the use element with a random x y and width attributes:
const SVG_NS = 'http://www.w3.org/2000/svg';
const SVG_XLINK = "http://www.w3.org/1999/xlink";
//create a new use element
let use = document.createElementNS(SVG_NS, 'use');
// set the value for 'xlink:href' of the new use element
use.setAttributeNS(SVG_XLINK, 'xlink:href', '#poly');
//the random width
let w = Math.random()*50;
// the proportiopnal height
let h = w*83.3 / 75;
//set the position and the size of the use element
use.setAttributeNS(null, 'x', Math.random()*(500 - w));
use.setAttributeNS(null, 'y', Math.random()*(250 - h));
use.setAttributeNS(null, 'width', w);
use.setAttributeNS(null, 'height', h);
//Append the use element
svg.appendChild(use);
svg{border:1px solid}
<svg id="svg" viewBox="0 0 500 250">
<symbol id="poly" viewBox="14 25 72 83.3">
<polygon tran points="50,25 86,45.83 86,87.5 50,108.3 14,87.53 14,45.83" />
</symbol>
</svg>
Create a function drawHex(x,y) where you pass x and y as starting coordinates. On that function the you draw your points relative to x and y:
..."x+50,y+25 x+86,y+45.83 ... x+14,y+45.83";
Finally, create a loop that randomly generates x and y and calls the drawHex function. I'm recently working on something similar. You can take a look and my source code at this P5js experiment and then go to creaPuerta() function on https://zoada.com/lpa/js/parametrica.js
Based on Robert Longson's comment, you could do it like that:
const btn = document.getElementById('btn');
const poly = document.getElementById('poly');
btn.onclick = () => {
const transform = `translate(${getRandomArbitrary(0, 100)} ${getRandomArbitrary(0, 100)}) scale(${getRandomArbitrary(1, 5)} ${getRandomArbitrary(1, 5)})`;
poly.setAttribute('transform', transform);
};
function getRandomArbitrary(min, max) {
return Math.random() * (max - min) + min;
}
button {
display: block;
}
<button id="btn">Random</button>
<svg width=500 height=500>
<polygon id="poly" points="50,25 86,45.83 86,87.5 50,108.3 14,87.53 14,45.83"></polygon>
</svg>
Via this forum and other sources I finally have a tidy "zoom-and-recenter" Javascript solution that fits my specific needs and requirements:
<script>
var viewPortSize = 500; // fixed for now
var viewBoxSize = 500; // the zoom factor
function updateViewBox()
{
min = (viewPortSize - viewBoxSize) / 2;
max = viewBoxSize;
str = " ";
str = str.concat( "", min );
str = str.concat( " ", min );
str = str.concat( " ", max );
str = str.concat( " ", max );
trackBox = document.getElementById("trackBox");
trackBox.setAttribute( "viewBox", str );
}
function zoomIn()
{
viewBoxSize -= 50;
updateViewBox();
}
function zoomOut()
{
viewBoxSize += 50;
updateViewBox();
}
</script>
The target of this operation is:
<svg id="trackBox" x="600"
width="500" height="500"
viewBox="0 0 500 500">
<g>
<circle id="trackPoint"
cx="250" cy="250" r="5" fill="red"/>
<path id="track" d=
"
M 250 250
L 245 225
L 200 225
L 200 250
"
stroke="blue" stroke-width="1" fill="transparent"/>
</g>
</svg>
However, when applying the updateViewBox() function, the circle radius and the path stroke-width are also magnified or reduced.
Does there exist a simple way to make these attributes invariant with respect to changing the viewBox size?
In the code below, I am rotating a selection box (parent) with two children in SVG.
It works fine, however, when the parent (selection box) is removed, the children go back to their original (pre-rotation) co-ordinates.
How can I apply the updated co-ordinates on the children once the parent is removed. I specifically need to maintain the new position of the children in X,Y co-oridinates, i.e. the rotate should be converted to translate, r.g. transform = translate (X , Y) . I only need New x,y for children so that i can 'translate' them to new location.
here is the fiddle link http://jsfiddle.net/rehankhalid/QK8L8/6/
HTML CODE:-
<button data-action="rotate" onclick="rotateMainRect()">Rotate +5 Angle</button>
<button data-action="rotate" onclick="removeRectRotation()">Remove Selection</button>
<br/>
<svg id="mainSvg" xmlns="http://www.w3.org/2000/svg" version="1.1" width="600" height="500">
<g id="selectedRect">
<rect id="rectangle" x="135" y="135" width="110" height="35" stroke="red" stroke-width="2" fill="grey" opacity="0.4" />
<g id="button_1" transform="translate(0,0)">
<circle cx="150" cy="150" r="5" stroke="grey" stroke-width="1" fill="none" />
</g>
<g id="button_2" transform="translate(0,0)">
<circle cx="230" cy="150" r="5" stroke="grey" stroke-width="1" fill="none" />
</g>
</g>
</svg>
JAVASCRIPT CODE
var angle_incr = 5;
var angle = 0;
function rotateMainRect() {
var selectedRect = document.getElementById('selectedRect');
var rectangle = document.getElementById('rectangle');
var x = rectangle.getAttribute('x');
if (x != 0) {
var centxy = calculateCenterXY(selectedRect);
angle += angle_incr;
selectedRect.setAttributeNS(null, 'transform', 'rotate(' + angle + ',' + centxy.x + ',' + centxy.y + ')');
} else {
rectangle.setAttribute('x', '135');
rectangle.setAttribute('y', '135');
rectangle.setAttribute('width', '110');
rectangle.setAttribute('height', '35');
}
}
function calculateCenterXY(node) {
var x = node.getBBox().x + (node.getBBox().width / 2);
var y = node.getBBox().y + (node.getBBox().height / 2);
var xy_co = {
x: x,
y: y
};
return xy_co;
}
function removeRectRotation() {
var selectedRect = document.getElementById('selectedRect');
selectedRect.setAttributeNS(null, 'transform', '');
var rectangle = document.getElementById('rectangle');
rectangle.setAttribute('x', '0');
rectangle.setAttribute('y', '0');
rectangle.setAttribute('width', '0');
rectangle.setAttribute('height', '0');
angle = 0;
}
- What i Want:-
First Rotate the selection rectangle to some angle, and then press 'Remove selection'. After Removing the selection, Childrens must be placed at the New postion. (which now, move back to the original position)
If you are asking if you can read the absolute positions of the two transformed circles directly using JS, then the answer is no.
You will need to calculate their positions yourself using a bit of trigonometry.