I have multiple <g> elements and all of them have a circle:
<g class="my-group">
<circle r="40" cx="10" cy="10"></circle>
</g>
In some cases I need to draw a rectange with rounded corners instead of circle, it should be presented as rhombus. I'm trying to draw it like this:
<g class="my-group">
<rect x="-16" y="-30" width="60" height="60" fill="red" transform="rotate(45)" rx="4"></rect>
</g>
But this is a wrong way to hardcode coordinates and width/height of rect.
How I can calculate width and height of rotated rect to inscribe it in circle, so group will have the same width and height 80px.
Simple pythagorean theorem. Rectangle width (or hypotenuse) equals the square root of two lots of the radius squared.
const radius = document.querySelector('#circle').getAttribute('r')
const rectWidth = Math.sqrt(radius * radius * 2)
const square = document.querySelector('#square')
square.setAttribute('x', -rectWidth/2 + 'px')
square.setAttribute('y', -rectWidth/2 + 'px')
square.setAttribute('width', rectWidth + 'px')
square.setAttribute('height', rectWidth + 'px')
svg {
width: 100vmin;
height: 100vmin;
}
<svg viewbox="0 0 100 100">
<circle id="circle" fill="green" r="40" cx="50" cy="50"></circle>
<rect id="square" fill="red" transform="translate(50, 50) rotate(45)"></rect></svg>
Related
I have multiple rect of different height and width, I need to show a text on the top left corner of the rect and no matter how much the width of the text is ,it should get adjusted in 50% of rect width.
Here is the code I have tried.
rectBbox = rectElem.getBBox();
textBbox = textElem.getBBox();
scale = rectBbox.width / textBbox.width;
X = parseFloat(rectBbox.x)
Y = parseFloat(rectBbox.y)
textElem.setAttribute("transform", "translate(" + X + "," + Y + ") scale(" + scale + ")");
my svg looks like
<svg>
<g id="maingroup">
<g id="firstchild">
<rect></rect>
<text></text>
</g>
<g id="nthchild">
<rect></rect>
<text></text>
</g>
<g>
</svg>
How can i scale it so that no matter what is the size of the rect or text is,text will get properly adjusted within 50% of rect's width
In this example, we are positioning the <text> elements at the default 0,0. Then we calculate the difference between the top left of the rect and the top left of the text.
The scale part is similar: 0.5 * the ratio of the text width and the rect width.
function adjustText(boxElem)
{
var rectElem = boxElem.querySelector("rect");
var textElem = boxElem.querySelector("text");
var rectBbox = rectElem.getBBox();
var textBbox = textElem.getBBox();
var scale = 0.5 * rectBbox.width / textBbox.width;
var translateX = rectBbox.x - textBbox.x;
var translateY = rectBbox.y - textBbox.y;
textElem.setAttribute("transform", "translate(" + translateX + "," + translateY + ") scale(" + scale + ")");
}
adjustText(document.getElementById("firstchild"));
adjustText(document.getElementById("nthchild"));
<svg width="500" height="500">
<g id="maingroup">
<g id="firstchild">
<rect x="20" y="30" width="250" height="100" fill="lightgrey" stroke="black"/>
<text>Very Very Very Very Very Long Text</text>
</g>
<g id="nthchild">
<rect x="200" y="200" width="200" height="100" fill="lightgrey" stroke="black"/>
<text>Smaller Text</text>
</g>
</g>
</svg>
First I need to calculate the width of the text (txtlength) and the width of the box (w). I want to scale the text so I'm calculating the scale let thescale = w / (2*txtlength);. // this will scale the text at 50% of the rect width. Next, using setAttributeNS I'm setting the value for the transform attribute. Please observe that the text has no x and no y attributes being centered around the origin of the SVG canvas.
let txtlength = txt.getComputedTextLength()
let w = theRect.getBBox().width;
let c = {x:50,y:25}// the center of the rect
let thescale = w / (2 * txtlength);// 50% of rect's width
//scale the text and translate in the center
txt.setAttributeNS(null, "transform", `scale(${thescale},${thescale}) translate(${c.x/thescale},${c.y/thescale})`)
svg{border:1px solid; width:120vh}
text{font-size:10;
dominant-baseline: middle;
text-anchor: middle}
<svg viewBox="0 0 100 50">
<rect id="theRect" x="10" y="10" width="80" height ="30" stroke="black" fill="none" />
<text id="txt" dominant-baseline="middle" text-anchor="middle">A very long text, sooo long </text>
</svg>
Just like this:
It has different width.
How you do this in SVG is to place one circle directly on top of the other.
<svg width="200" height="200">
<circle cx="100" cy="100" r="70" fill="none" stroke="#ccc" stroke-width="1"/>
<circle cx="100" cy="100" r="70" fill="none" stroke="#ccc" stroke-width="5"
stroke-dasharray="413.4 440" transform="rotate(-90 100 100)"/>
</svg>
The two additional attributes on the second (front) circle perform the following functions:
stroke-dasharray="413.4 440" makes a dash pattern with the effect of drawing 94% of the circle circumference (2 * PI * 70 = 440).
transform="rotate(-90 100 100)" rotates the circle 90 degrees anti-clockwise so that the "progress bar" starts at 12 o'clock. By default the dash pattern of a circle starts at 3 o'clock.
I want to move object through svg path on scroll=) I was trying to add parts of path on scroll into path, but it still doesn't work. Help!!!=)
https://jsfiddle.net/YuriiBielozertsev/Ltx9ed0L/
<?xml version="1.0"?>
<svg viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Draw the outline of the motion path in grey, along with 2 small circles at key points -->
<path d="M10,110 A120,120 -45 0,1 110 10 A120,120 -45 0,1 10,110" stroke="green" stroke-width="2" fill="none" id="theMotionPath"/>
<circle cx="10" cy="110" r="3" fill="#000"/>
<circle cx="110" cy="10" r="3" fill="#000"/>
<!-- Red circle which will be moved along the motion path. -->
<circle cx="0" cy="" r="5" fill="red">
<!-- Define the motion path animation -->
<animateMotion dur="6s" repeatCount="indefinite">
<mpath xlink:href="#theMotionPath"/>
</animateMotion>
</circle>
</svg>
Something like this?
How this works:
When we get a scroll event we:
Calculate how far down the page we are
Convert this percentage to a position on the path using the <path> element functions getTotalLength() and getPointAtLength().
Reposition the dot so that it appears at this point.
function positionTheDot() {
// What percentage down the page are we?
var scrollPercentage = (document.documentElement.scrollTop + document.body.scrollTop) / (document.documentElement.scrollHeight - document.documentElement.clientHeight);
// Get path length
var path = document.getElementById("theMotionPath");
var pathLen = path.getTotalLength();
// Get the position of a point at <scrollPercentage> along the path.
var pt = path.getPointAtLength(scrollPercentage * pathLen);
// Position the red dot at this point
var dot = document.getElementById("dot");
dot.setAttribute("transform", "translate("+ pt.x + "," + pt.y + ")");
};
// Update dot position when we get a scroll event.
window.addEventListener("scroll", positionTheDot);
// Set the initial position of the dot.
positionTheDot();
.verylong {
height: 2000px;
}
svg {
position: fixed;
width: 200px;
height: 200px;
}
<svg viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Draw the outline of the motion path in grey, along with 2 small circles at key points -->
<path d="M10,110 A120,120 -45 0,1 110 10 A120,120 -45 0,1 10,110" stroke="green" stroke-width="2" fill="none" id="theMotionPath"/>
<circle cx="10" cy="110" r="3" fill="#000"/>
<circle cx="110" cy="10" r="3" fill="#000"/>
<!-- Red circle which will be moved along the motion path. -->
<circle cx="0" cy="0" r="5" fill="red" id="dot"/>
</svg>
<div class="verylong">
</div>
I have a circle and a SVG plane:
<svg width="400" height="400">
<g class="point" transform="translate(6.5,1)">
<g class="delete-point" transform="translate(11,-11)"></g>
<circle></circle>
<text class="point-index" y="4"><tspan text-anchor="middle">0</tspan></text>
</g>
</svg>
And this is how I'm getting x and y of the circle:
d3.selectAll('.point')
.each(function() {
var c = d3.select(this)
var cX = d3.transform(c.attr('transform')).translate[0]
var cY = d3.transform(c.attr('transform')).translate[1]
list.push({ x: Math.round(cX), y: Math.round(cY), index: k++ })
}
Right now 0 of X and Y are in the top left corner. How to do it so that the 0 of X and Y are in the middle of the plane?
Note: .point is the circle and svg is the plane.
You can change the origin of the SVG file by using a translate transform:
<svg width="400" height="400">
<g transform="translate(200 200)">
<!-- coordinate (0,0) is now in the center of the SVG -->
<circle cx="0" cy="0" r="150" fill="red"/>
</g>
</svg>
I have svg line:
<svg class="filling" width="500" height="10" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path data-over-line="" d="M90,5 L500,5" stroke="#e2e2e2" fill="transparent" stroke-width="4" style="stroke-dashoffset: 0px;"></path>
</svg>
I need when scroll the page it is gradually filled with a different color. How to do it?
I am not sure what shape you ultimately want for your scrollbar, but here is a simple solution. We draw a blue line on top of your grey line to indicate scroll progress. The length of the line is determined by calculating how far done the page we have scrolled.
If you ultimately want to have the scrollbar be a shape other than a line or a rectangle, you will need to take a different approach.
SVG (modified a little):
<svg class="filling" width="500" height="10" version="1.1" xmlns="http://www.w3.org/2000/svg">
<line x1="90" y1="5" x2="500" y2="5" stroke="#e2e2e2" fill="transparent" stroke-width="4" />
<line x1="90" y1="5" x2="90" y2="5" stroke="blue" fill="transparent" stroke-width="4" id="scrollprogress" />
</svg>
JS:
window.onscroll = function (event) {
var offset = window.pageYOffset;
var wheight = window.innerHeight;
var html = document.documentElement;
var docheight = Math.max(document.body.scrollHeight, document.body.offsetHeight,
html.clientHeight, html.scrollHeight, html.offsetHeight);
var progress = offset / (docheight - wheight);
// Give the line a CSS gradient based on scroll position
document.getElementById("scrollprogress").setAttribute("x2", 90 + progress * 410);
}
Demo fiddle