I have a CRA React App.
How do you change the width and height of an SVG without preserving aspect ratio (e.g. width 500px height 10px)?
import ornament from '../../assets/img/ornament.svg';
<img src={ornament} style={{width: '500px', height: '20px'}} />
This preserves aspect ratio and only changes size. I want it to transform to 500px width and 20px height. So original 400px 400px => 500px 20px.
EDIT: example on codesandbox: https://codesandbox.io/s/40xj3zv5w7 , the image gets really small instead of 400px width and 10px height.
You can't resize only width, like a regular image, because svg are vectors, and they scale. You need to set preserveAspectRatio(none)
When you are working with just HTML, you can do tis:
<img src="your.svg#svgView(preserveAspectRatio(none))" />
With React, you can do it like this:
import React, {Component} from 'react';
import ornament from '../../assets/img/ornament.svg';
class App extends Component {
...
render() {
const svgPath = `${ornament}#svgView(preserveAspectRatio(none))`;
return (
<img src={svgPath} width="500px" height="20px"/>
)
}
}
export default App;
The least complicated method I found:
https://codepen.io/copist/pen/vLLmPB
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.0" x="0px" y="0px" width="48px" height="48px" viewBox="0 0 48 48" style="null" class="whateverClassYouWish" >
.whateverClassYouWish {
width: 512px;
height: 512px;
}
You can use css transform: scale(sx, sy)
sx = 500/400
sy = 20/400
Related
UPDATE: I had previously found a way to accomplish this using CSS, but the slope of the line is jagged and the aspect ratio of the triangle is not consistent for all widths. Here's a Codepen of that solution.
How can I create the effect where the top of the footer slopes upward? Most footers have a simple straight horizontal line along the top of the footer div, but I need to create an effect where the line slopes upward. Here are some different approaches:
PNG image with transparency.
CSS only
SVG
I prefer not to use a PNG image and tried using straight CSS and am now trying it using SVG. The height of the triangular shape should be no more that 200 pixels at the full width of 1440 pixels.
.main {
background: #ccc;
}
.right-triangle {
display: block;
}
.footer {
background: #333;
color: #fff;
}
<div class="main">End of main section be flush with the div below.</div>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" class="right-triangle">
<polygon points="50 25, 100 100, 0 100" />
</svg>
<div class="footer">
Next section needs to be flush with the triangle with no gap in between.
</div>
The code below should do what you want. The key is to set the height and width separately and NOT preserve the aspect ratio for the SVG.
You might need to play with values in the max function to get the narrow screen versus wide screen effects you want. And/or, change max-height to height.
CSS
.main {
background: #ccc;
}
.right-triangle {
display: block;
width: 100%;
max-height: max(20px, calc(200vw / 1440 ));
}
.footer {
background: #333; color: #fff;
}
HTML
<div class="main">
End of main section be flush with the div below.</div>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"
class="right-triangle"
preserveAspectRatio="none">
<polygon points="100 0, 100 100, 0 100" />
</svg>
<div class="footer">
Next section needs to be flush with the triangle with no gap in between.
</div>
(I am on a mobile phone, so, sorry but it is bit difficult posting this how I would like to.)
Is there a way to make the svg automatically scale to the size of the circle inside it? Something like or something. At the moment I give the svg a size, and the circle a size. Which causes them to not match (and I don't want to manually try and match both every time).
See how the blue background is bigger than the circle. I know I can just change the height and width, but it would be nice to have this change according to the circle size. I only want to change one element, not match both.
.divOrangeCircles {
display: inline-block;
background-color: aqua;
opacity: 0.5;
}
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="css/styles.css">
</head>
<body>
<div class="divOrangeCircles"><svg height="200" width="200"><circle cx="90" cy="90" r="90" fill="#F48043"/></svg></div>
</body>
</html>
What you want is the viewBox='x y width height' attribute, which will define the view-box of your SVG drawings.
SVG units are (generally) relative to their parent's box, by setting the viewBox attribute, you define how much of this units your SVG should display.
.divOrangeCircles {
display: inline-block;
background-color: aqua;
opacity: 0.5;
}
svg {
display: block;
}
<div class="divOrangeCircles">
<svg viewBox="0 0 180 180" height="200" width="200">
<circle cx="90" cy="90" r="90" fill="#F48043"/>
</svg>
</div>
If you want to svg scale according to parent div then use this code. Don't be add manually dimensions in percentage.
.divOrangeCircles svg{
width: 100%;
height: 100%;
}
Give your cx, cy and r should be in percentages in order to modify according to the Parent svg size.
Sample Fiddle
For controlling sizes from css u can make svg height and width as 100% and give size from css.
<div class="divOrangeCircles"><svg height="100%" width="100%"><circle cx="50%" cy="50%" r="50%" fill="#F48043"/></svg></div>
Updated Fiddle
Help :)
Is it possible to emulate the following CSS in an SVG fill?
background-image: url(/* URL */);
background-position: 50%;
background-size: auto 100%;
background-repeat: repeat-x;
i.e. A background image that has its aspect ratio preserved, has the same height as its container, is centred, and repeats horizontally. This JSBin demonstrates the behaviour I'm trying to implement, using an animation to show how the background responds to height changes.
It's awkward, to say the least, and it is far from perfect. As the outermost element, I have set a html <div> element, but it could also be a <svg> element. The key point is to remember that you need an inner <svg> with overflow: visible, and an outer element with overflow: hidden (which would be the default for an <svg> element.
#keyframes shrink {
0% { height: 200px; }
100% { height: 50px; }
}
div {
animation: 2s ease-in-out 0s infinite alternate shrink;
border: 1px solid #000;
height: 200px;
width: 200px;
overflow: hidden;
}
<div>
<svg width="100%" height="100%" viewBox="-0.675 0 0.1 1"
preserveAspectRatio="xMidYMid meet" overflow="visible">
<pattern id="p1" viewBox="0 0 100 80" height="1" width="1.25"
patternUnits="userSpaceOnUse">
<image xlink:href="http://static.jsbin.com/images/dave.min.svg"
width="100" height="80" />
<rect width="100" height="80" fill="none" stroke="brown" />
</pattern>
<rect fill="url(#p1)" x="-500" y="0" width="1000" height="1" />
</svg>
</div>
The repetitions are not conceptually endless, but merely very long - I've set 1000 / 1.
If the container is higher than wide in respect to the aspect ratio of the svg viewBox, the image will only scale so far that the viewBox still fits inside. This is due to the meet keyword being always applied for both directions. Therefore, the viewBox width needs to be small - I've set 1 / 10.
The size of the image needs to be known in advance, and it must be used in four places:
The <image> width and height must be explicitely set, SVG has no notion of "natural size".
The <pattern> viewBox attribute must be set to the image size. I've added a rectangle to illustrate the image borders.
While the pattern height attribute always needs to be 1, the width has to be set to the correct apect ratio.
If the viewBox on the on the inner <svg> has a value of "x 0 ws 1", and wp is the pattern width, then x = -(wp/2 + ws) - for my example, ws=0.1, wp=1.25 => x=-0.675.
Original Question
What is the most efficient way to calculate the width of an image relative to it's containing div in css?
I have the following snippet which sets #image1.width to a percentage that is relative to the width of its parent. I'm using a percentage because I need the image to scale proportionately to the parent when the screen is resized.
div {
position: relative;
display: block;
width: 100%;
}
#image1 {
position: absolute;
left: 10%;
top: 10%;
width: 29.43%;
height: auto;
}
#under {
width: 100%;
}
<div>
<img id="image1" src="http://placehold.it/206x115">
<img id="under" src="http://placehold.it/700x300/ff00f0/ffffff">
</div>
It is currently working as intended, except that I have to manually calculate the width percentage for every single image. i.e.
#image1 dimensions == 206x115
#under dimensions == 700x300
new #image1.width % == #image1.width / #under.width == 206/700 == ~29.43%
What I want to know is if there id a calc() method or similar I can implement to ease/streamline this process?
I was going to use width: calc(100% / 700) however this obviously will not work when the screen size changes.
Goals
To re-iterate, it is imperative that the #under image scales with the screen size and the #image remains proportionate.
I want the natural image ratios preserved with one another (i.e. an image that is one quarter the size of the other will remain as such at all browser widths).
Note: The html can be reconfigured in any way to achieve this.
Target browsers: Chrome, Firefox, Edge.
Post Bounty Review
Comment on #Obsidian Age's answer (end of first bounty 31.03.17):
Unfortunately #Obsidian Age's answer is not correct - it is close but not quite and I just wanted to clarify this here... Below is a snippet from his answer... Note that I think it is a good answer, just clarifying why it has not been accepted:
:root {
--width: 90vw; // Must be viewport-driven
}
#image1 {
width: calc(var(--width) / 3); // The 3 can be replaced with any float
}
Setting --width: 90vw what happens if body or div have a max-width set? This is also very hard to calculate for all devices when factoring in viewport-scaling.
#image1 { width:calc(var(--width) / 3); } This equates to calc(90vw / 3) which is 30vw which would equate to 30% of the images width. But how do we work out the number to divide by? Well it's back to where we started... width:calc(var(--width) * calc(206/700*100)); And this is why I have not accepted this answer.
Unfortunately, CSS has no parent selector. While you can't make an element relative to the parent directly with CSS, what you can do with pure CSS is set a variable that both elements make use of:
:root {
--width: 90vw; // Must be viewport-driven
}
Now you can use this variable as both the (fixed) width of the parent element, and the calculation-driven width of the child:
#under {
width: var(--width);
}
#image1 {
width: calc(var(--width) / 3); // The 3 can be replaced with any float
}
Note that the variable must either be a fixed unit, or be relative to the viewport. If it were percentage-based, both #under and #image1 would base their width off of their respective parents. In order to have this work responsively, it must be based off of the viewport.
:root {
--width: 90vw;
}
div {
position: relative;
display: block;
width: 100%;
}
#image1 {
position: absolute;
left: 10%;
top: 10%;
width: calc(var(--width) / 3);
height: auto;
}
#image2 {
position: absolute;
left: 25%;
top: 10%;
width: 10%;
height: auto;
}
#under {
width: var(--width);
}
<div>
<img id="image1" src="http://placehold.it/206x115">
<img id="under" src="http://placehold.it/700x300/ff00f0/ffffff">
</div>
I've also created a JSFiddle of this here, where you can see both elements scale when the viewport resizes.
Hope this helps! :)
I realize that the question prompts for a pure CSS solution, but I liberally interpreted that as meaning "no JavaScript".
In that vein, here's a solution using an embedded SVG:
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="100%" viewBox="0 0 700 300">
<image x="0" y="0"
xlink:href="http://placehold.it/700x300/ff00f0/ffffff"/>
<image x="10%" y="10%"
xlink:href="http://placehold.it/206x115"/>
</svg>
The best approach I think would be to eliminate width & use scale to fit the div, but the problem is scale transform do not accept any unit value, such as % or px or vw/vh!
.common_img_class{
transform: scale(calc(100vw/700)); /* this won't work! */
/* Idea here is to let image take is original width & then scale with respact to base image scaling. Base image scaling is detenined by *window_width/base_image _width*, here base image width is 700 as per you example */
}
Thus second best I can think of is to eliminate manual calculation of percent. Use calc() function to do that for you.
#firsrt_img{
width: calc((206/700) * 100%);
}
#second_img{
width: calc((306/700) * 100%);
}
Here you still have to write width for all but atleast spared from percent or ratio calculation.
Note:
If anyone can help with first approach, their inputs are welcome.
One thing that might work, if you are willing to use bootstrap (https://getbootstrap.com/examples/grid/):
<!-- parent -->
<div class="row">
<div class="col-md-6">
<img src="">
<!-- this is 50% of the screen for min 992px, a full line otherwise -->
</div>
<div class="col-md-3">
<img src="">
<!-- this is 25% of the screen for min 992px, a full line otherwise-->
</div>
<div class=col-md-3>
<img src="">
<!-- this is 25% of the screen for min 992px, a full line otherwise -->
</div>
</div>
You can group these as you want/need, just remember that the numbers have to add to 12 (in my example, 6+3+3). You can achieve a 100% width effect using col-md-12. Also, the md infix is just one of several options of cutoff between putting everything on the same line and stacking elements. You can check out http://getbootstrap.com/css/#grid for more details, as well as a couple of examples.
Inspired by the answer of Robby Cornelissen, here is an approach that works in the targeted browsers currently. Its only drawback is that the dimensions of the images have to be specified in the HTML (well, the SVG really) explicitly.
Here is a demo.
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="100%" viewBox="0 0 700 300">
<image x="0" y="0" width="700" height="300"
xlink:href="http://placehold.it/700x300/ff00f0/ffffff"/>
<image x="10%" y="10%" width="206" height="115"
xlink:href="http://placehold.it/206x115"/>
</svg>
The approach, like Robby's answer, uses the SVG image element. That currently defaults to having zero width and height when no dimensions are specified explicitly, but will change in SVG 2. This means that as soon as SVG 2 is supported by browsers, instead of specifying the image dimensions, we could set width="auto" height="auto" and be done.
The value auto for width and height on the ‘image’ element is calculated from the referenced image's intrinsic dimensions and aspect ratio, according to the CSS default sizing algorithm.
— SVG 2 Editor's Draft, § 7.8. Sizing properties: the effect of the ‘width’ and ‘height’ properties. Accessed 2017-12-18.
I have a SVG-element with a lot of elements inside. The SVG-element has a viewbox, so you can press a zoom-button and the elements appear bigger or smaller. Works well. Now the problem is, when the elements overflow the parent SVG-element no ScrollBars appear.
Example:
<div width="100%" height="100%">
<svg height="100%" width="100%" style="overflow-x: auto; overflow-y: auto; "viewBox="0 0 793 1122">
<g>
...
<line y2="44.9792mm" y1="44.9792mm" x1="197.203mm" x2="12.7028mm"></line>
<line y2="44.9792mm" y1="44.9792mm" x1="197.203mm" x2="12.7028mm"></line>
<text x="43.4516mm" y="52.9167mm" style="font-size: 11pt;">S</text>
<rect x="0" width="210mm" y="0" height="297mm"></rect>
...
</g>
</svg>
</div>
//here I set the viewbox after clicking the zoomOut-Button
float width = svg.getViewBox().getBaseVal().getWidth();
float height = svg.getViewBox().getBaseVal().getHeight();
svg.getViewBox().getBaseVal().setHeight((float) (height / 0.9));
svg.getViewBox().getBaseVal().setWidth((float) (width / 0.9));
Can someone help me?
I put the overflow attribut in the svg and also in the div tag. doesn't work.
Try making the SVG element bigger than the div, and let the div handle the overflow using scroll.
For example, see this jsfiddle, which utilizes the following css:
div#container {
height: 400px;
width: 400px;
border:2px solid #000;
overflow: scroll;
}
svg#sky {
height: 100px;
width: 1100px;
border:1px dotted #ccc;
background-color: #ccc;
}
Part of the point of SVG is so that it can scale to fit the screen. However, I think if you want to get something like what you are describing, then you need to set explicit width and height to the svg element. Something like http://jsfiddle.net/qTFxJ/13/ where I set the width and height in pixels to match your viewBox size.