Position SVG elements over an image - html

Is it possible to have the following elements and style them so that the SVG objects appear over the image (i.e. like part of the image)?
Currently they are displayed below it on a new row.
I know I could set the image as a background image of the parent div, but unfortunately I also need to be able to rotate it within the parent so I don't think that is an option.
<div id='edit-area' style='position:relative; overflow:none; display:inline-block; border: 1px black solid; width: 950px; height: 500px;'>
<img src='/my_image.png' />
<svg id="edit-svg" height="500" width="950">
<!-- there will be lines, rectangles etc in here -->
</svg>
</div>

Here's a generic example of how to do an image overlay. Basically you wrap the image and the overlay content in a relative positioned element and then absolute position the overlay content.
.img-overlay-wrap {
position: relative;
display: inline-block; /* <= shrinks container to image size */
transition: transform 150ms ease-in-out;
}
.img-overlay-wrap img { /* <= optional, for responsiveness */
display: block;
max-width: 100%;
height: auto;
}
.img-overlay-wrap svg {
position: absolute;
top: 0;
left: 0;
}
.img-overlay-wrap:hover {
transform: rotate( 15deg );
}
<div class="img-overlay-wrap">
<img src="https://via.placeholder.com/400">
<svg viewBox="0 0 200 200">
<circle cx="75" cy="75" r="50" fill="rebeccapurple"/>
</svg>
</div>
Added a bit-o rotation fun since you mentioned rotation (might be different than what you intended).

Related

SVG works fine on firefox but the its positioning changes on other browsers

I have included a SVG for polygon and made necessary positioning.It works fine on firefox but the positioning changes on other browsers.I have attached the screenshots in both firefox and Chrome respectively.Why is this happening?
svg{
position: absolute;
z-index: 1;
left: 17%;
top: -23%;
}
.circle {
background: #b7a3a3;
height: 600px;
width: 600px;
border-radius: 50%;
margin: 0 auto;
position: relative;
}
<div class="flex-auto flex items-center justify-center w-100">
<div class="w-100">
<svg class="w-100 white debug-grid mb4 db"viewBox="0 0 1000 1000" stroke="#fff" fill="none" stroke-width="50"
id="js-svg">
<polygon id="js-polygon" points="601.4282075197532,484.74767078498866 522.9616144951742,592.7476707849887 396,634 269.03838550482584,592.7476707849887 190.57179248024684,484.74767078498866 190.5717924802468,351.2523292150114 269.0383855048258,243.25232921501137 395.99999999999994,202 522.9616144951741,243.25232921501132 601.4282075197532,351.2523292150113" />
</svg>
</div>
</div>
<div class="circle">
</div>
You've got some issues stemming from the makeup of that svg. Your svg has a 461x468 unit polygon inside of a 1000x1000 unit viewbox and it isn't even centered.
It looks like what you want is a decagon centered inside of a circle. After normalizing your svg polygon the css gets a lot easier to deal with because you can just center it without using magic numbers.
To normalize the polygon, I took the points and subtracted the smallest x coordinate from all x coordinates, did the same for the y coordinates. Then add half of the stroke width to all coordinates. The viewbox then needs to fit the largest coordinates plus half of the stroke width.
I've absolutely positioned the svg inside of the circle to center it. The circle is set to height:0 and padding-top:100% to maintain a 1:1 aspect ratio.
If you want to use the bootstrap positioning and establish a 600px width, apply those styles to a parent element.
svg {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 90%;
}
.circle {
background: #b7a3a3;
height: 0px;
padding-top: 100%;
border-radius: 50%;
margin: 0 auto;
position: relative;
}
<div class="circle">
<svg viewBox="0 0 461 486" stroke="#fff" fill="none" stroke-width="50" id="js-svg">
<polygon id="js-polygon" points="435.856415039507,309.747670784988
357.389822014928,417.747670784988
230.428207519754,459
103.466593024579,417.747670784988
25,309.747670784988
25,176.252329215011
103.466593024579,68.252329215011
230.428207519753,27
357.389822014928,68.252329215011
435.856415039507,176.252329215011" />
</svg>
</div>

Wrapping svg element with CSS

I want to make an svg element wrapper to dynamically resize it. I use svg inside the div of the element to which the wrapper svg-wrapper class is applied. My css looks like this:
.svg-wrapper {
display: inline-block;
position: relative;
width: 100%;
padding-bottom: 100%;
vertical-align: top;
overflow: hidden;
background-color: gray;
}
svg {
display: inline-block;
position: absolute;
top: 0;
left: 0;
fill: red;
}
And html so:
<div class="svg-wrapper">
<svg preserveAspectRatio="xMinYMin meet" viewBox="0 0 300 100">
<rect width="100%" height="100%" fill="blue"/>
</svg>
</div>
Example on JSFiddle: http://jsfiddle.net/a2nj971t/
However, as you can see there is a problem that the div does not wrap the svg from below. Is it possible to fix it (Regardless of size of svg)?

CSS split a square into 4 triangles

I am currently trying to make a square be 4 triangles of equal size that have hover events on them.
I am creating the triangles like this,
.right, left, .top, .bottom {
position: relative;
width: 26px;
}
.right:before{
position: absolute;
display: inline-block;
border-top: 26px solid transparent;
border-right: 26px solid #eee;
border-bottom: 26px solid transparent;
left: 26px;
top: 0px;
content: '';
}
What I am finding is that each triangle sits above one another meaning only one triangle can be hovered, here is my example,
http://codepen.io/anon/pen/qdmbKz
As you can see only the bottom triangle (hover at the bottom of the square) is hoverable. What am I doing wrong? Is there a better way of doing this?
As you have already indicated in your question, the reason why the hover works only on the bottom triangle and not the others is because the container of the bottom triangle is placed on top of the container of the other three triangles.
While using the border trick to produce triangles, the actual shape is still a square. It gets the triangle appearance only because the other three borders are transparent. Now when you hover on the shape you are actually hovering the transparent areas of the bottom triangle and not the containers of the other triangles which is why their respective hover events don't get triggered.
I would personally recommend using SVG for these type of things but the shape is not all that complex to achieve with CSS either.
SVG:
In SVG, you could make use of the polygon elements to create four triangles within the square and each polygon is hover-able separately. If they should have their own target links, you can also enclose the polygons within an a (anchor) tag.
In the snippet, I have implemented the anchor only for one triangle
.square {
height: 400px;
width: 400px;
overflow: hidden;
}
svg {
height: 100%;
width: 100%;
}
polygon {
fill: aliceblue;
stroke: crimson;
stroke-linejoin: round;
}
polygon:hover {
fill: cornflowerblue;
}
<div class='square'>
<svg viewBox='0 0 100 100'>
<a xlink:href='http://google.com'>
<polygon points='5,5 50,50 95,5' />
</a>
<polygon points='5,5 50,50 5,95' />
<polygon points='5,95 50,50 95,95' />
<polygon points='95,5 50,50 95,95' />
</svg>
</div>
CSS:
This is an adaptation of the answer posted by here by web-tiki. I am posting a separate answer because the shape in this question is much simpler and doesn't require as much work as the other one.
The square is split into four equal sized hover-able triangles using the following method:
The container is a square and has borders on all its sides. The borders are required on the parent because diagonal lines on the triangle are much much more difficult to achieve with CSS.
Four child elements are added to the container whose height and width are calculated using Pythagoras theorem. They are then positioned such that their top left corner is on the center point of the square (to help with the rotation).
All the child elements are rotated by the appropriate angles to form the triangles. The transform-origin is set as top left to have the rotation done with the parent square's center point as the axis.
The parent has overflow: hidden to prevent the other half of each square from being visible.
Note that adding text into the 4 triangles will not be straight-forward because they would also be rotated. The text would have to be put inside a child element which must either be counter rotated.
Note: The script included in the demo is the prefix free library which is used to avoid browser prefixes.
.square {
position: relative;
height: 200px;
width: 200px;
border: 2px solid crimson;
overflow: hidden;
transition: all 1s;
}
.top,
.left,
.right,
.bottom {
position: absolute;
height: calc(100% / 1.414);
width: calc(100% / 1.414);
top: 50%;
left: 50%;
border: 1px solid crimson;
transform-origin: 0% 0%;
}
.right {
transform: rotate(-45deg);
}
.bottom {
transform: rotate(45deg);
}
.top {
transform: rotate(-135deg);
}
.left {
transform: rotate(135deg);
}
.square > div:hover {
background: tomato;
}
/*Just for demo*/
.square:hover{
height: 300px;
width: 300px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<div class='square'>
<div class="top"></div>
<div class="right"></div>
<div class="bottom"></div>
<div class="left"></div>
</div>

cropping a photo using css

I need to crop quadrilateral for a photo. I need parallelogram. But it is skewed. I need a parallelogram crop. How can I do it?
Code is below:
img {
transform: skew(-10deg);
position:relative;
left: 30px;
}
<img src="http://blogs.villagevoice.com/runninscared/RL%20Gang%20Boy.jpg">
What i want is i dont want boy to turn sideways but only the picture to crop. Thank you.
You could use svg's clipPath and foreignObject to apply inline clip-path for maximum browser support.
<svg width="300" height="400">
<defs>
<clipPath id="parallelogram">
<path d="M50,0 h250 l-50,400 h-250z" />
</clipPath>
</defs>
<foreignObject clip-path="url(#parallelogram)" width="100%" height="100%">
<img src="http://blogs.villagevoice.com/runninscared/RL%20Gang%20Boy.jpg" />
</foreignObject>
</svg>
please try this css + html script
CSS
#graphic {
width: 200px;
height: 100px;
position: relative;
}
#graphic:before {
content: '';
position: absolute;
width: 200px;
height: 50px;
z-index: -1;
background-image: url(http://blogs.villagevoice.com/runninscared/RL%20Gang%20Boy.jpg`enter code here`); /* Image is 500px by 500px, but only 200px by 50px is showing. */
}
HTML
<div id="graphic">lorem ipsum</div>

CSS: make div width proportional to height

This is a little tricky to explain, but: I want a responsive-height div (height: 100%) that will scale the width proportional to the height (not vice versa).
I know of this method utilising a padding-top hack to make the height proportional to the width, but I need it to work the other way around. Having said that, I'm not hugely keen on the additional requirement of absolutely-positioned elements for the content in that method, so I realise I may well be asking for the moon on a stick here.
To help visualise, here is an image:
...and here is a jsFiddle, illustrating pretty much the same thing.
It is worth noting that I am already using the :before and :after pseudo-elements to vertically-align the content of the box I want to scale proportionally.
I would really enjoy not having to revert to jQuery, just because there's going to be an inherent requirement for resize handlers and generally more debugging all round... but if that's my only choice, then fiat.
I've been wondering about a pure-css solution to this problem for a while. I finally came up with a solution using ems, which can be progressively enhanced using vws:
See codepen link for full working demo and explanation:
http://codepen.io/patrickkunka/pen/yxugb
Simplified version:
.parent {
font-size: 250px; // height of container
height: 1em;
}
.child {
height: 100%;
width: 1em; // 100% of height
}
Oh,you could probably use that "padding-top" trick.
width: 50%;
height: 0;
padding-bottom: 50%;
http://absolide.tumblr.com/post/7317210512/full-css-fluid-squares
Or:
.square-box{
position: relative;
width: 50%;
overflow: hidden;
background: #4679BD;
}
.square-box:before{
content: "";
display: block;
padding-top: 100%;
}
http://codeitdown.com/css-square-rectangle/
The vertical padding in CSS is related to the width of the element, not the height.
The font solution requires that the height is known. I have found a solution for making an element proportional inside a parent div with unknown widths and heights. Here is a demo.
The trick I'm using is to have an image used as a spacer. The code explained:
<div class="heightLimit">
<img width="2048" height="2048" class="spacer"
src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAA
P///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7">
<div class="filler">
<div class="proportional">
</div>
</div>
</div>
So it is not the prettiest with two extra divs and a useless image. But it could be worse. The image element needs to have width and height with the desired dimensions. Width and height need to be as large as the maximum size allowed (a feature!).
The css:
.heightLimit {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: auto;
max-width: 100%;
overflow: hidden;
}
This element is to limit the height, but to expand horizontally (width: auto) although never beyond the parent (max-width). Overflow needs to be hidden because some children will protrude outside the div.
.spacer {
width: auto;
max-height: 100%;
visibility: hidden;
}
This image is invisible and scaled proportionally to the height, while the width is adjusted and forces the width of the parent to also be adjusted.
.filler {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
}
This element is required to fill the space with an absolutely positioned container.
.proportional {
position: relative;
width: 100%;
height: 0;
padding-bottom: 100%;
}
And here our proportional element gets a height proportional to the width with the familiar padding-bottom trick.
Unfortunately, there is a bug in Chrome and IE so if you modify the parent element using Javascript, such as in my demo, the dimensions will not be updated. There is a hack that can be applied to solve that, as shown in my demo.
You can use view height (vh) as the unity for the width.
Here is an example with the 20px margin you asked for.
.parent {
margin : 20px;
}
.child {
width: calc(100vh - 40px);
height : calc(100vh - 40px);
margin:0 auto;
background: red;
box-sizing:border-box;
padding:10px;
}
See the fiddle :
https://jsfiddle.net/svobczp4/
Based off of #kunkalabs's answer (which is really smart) I've come up with a solution that lets you preserve the inherited font-size.
HTML:
<div id='rect'>
<div id='content'>Text</div>
</div>
CSS:
#rect {
font-size: 1000%;
height: 1em;
width: 1em;
position: relative;
}
#content {
font-size: 10%;
}
So basically the font-size of #content is (100 / $rectFontSize) * 100 percent of the rectangle. If you need a definite pixel size for the rectangle, you can set the #rect's parent's font-size…otherwise just adjust the font-size until it's about where you want it to be (and enrage your designer in the process).
You can achieve that by using SVG.
It depends on a case, but in some it is really usefull. As an example - you can set background-image without setting fixed height or use it to embed <iframe> with ratio 16:9 and position:absolute.
For 3:2 ratio set viewBox="0 0 3 2" and so on.
Example:
div{width:35%;background-color:red}
svg{width:100%;display:block;visibility:hidden}
<div>
<svg viewBox="0 0 3 2"></svg>
</div>
On newer browsers, we can use aspect-ratio with a fixed height, and the width will be calculated accordingly.
img {
aspect-ratio: 1.2;
height: 250px;
max-width: 500px;
}
But the browser support for aspect-ratio is not good enough. I liked the SVG solution proposed by #Jakub Muda, except for the fact that it requires modifying the markup. I have moved the SVG to CSS by including it using content property. On newer browsers, it disables the SVG hack and switches to aspect-ratio property.
document.querySelector('.nav').addEventListener('click', function(e) {
var index = parseInt(e.target.dataset.index);
if (!index) {
return;
}
var elements = document.querySelectorAll('.box');
for (var i = elements.length; i > 0; i--) {
elements[i - 1].classList.toggle('hide', i !== index);
}
});
.wrapper {
max-width: 500px;
margin: 0 auto;
width: 100%;
height: 250px;
text-align: center;
background: green;
}
.box {
display: inline-flex;
position: relative;
max-width: 100%;
}
/* SVG Hack */
.box::before {
display: block;
line-height: 0;
max-width: 100%;
content: 'test';
}
[data-aspect-ratio="1"]::before {
content: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1' height='250'></svg>");
}
[data-aspect-ratio="2"]::before {
content: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 2 1' height='250'></svg>");
}
[data-aspect-ratio="3"]::before {
content: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 3 1' height='250'></svg>");
}
#supports (aspect-ratio1: 1) {
/* Modern browsers */
.box {
height: 100%;
background: green;
}
.box::before {
display: none;
}
[data-aspect-ratio="1"] {
aspect-ratio: 1;
}
[data-aspect-ratio="2"] {
aspect-ratio: 2;
}
[data-aspect-ratio="3"] {
aspect-ratio: 2;
}
}
.content {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
.content>svg {
display: block;
width: 100%;
position: absolute;
top: 50%;
left: 50%;
height: auto;
transform: translate(-50%, -50%);
}
.nav {
text-align: center;
}
.hide {
display: none;
}
<!doctype html>
<html lang="en">
<head>
<title>Width proportional to height in CSS</title>
</head>
<body>
<div class="wrapper">
<div class="box" data-aspect-ratio="1">
<div class="content">
<svg viewBox="0 0 100 100" width="100" height="100" xmlns="http://www.w3.org/2000/svg"><rect x="2" y="2" width="96" height="96" style="fill:#DEDEDE;stroke:#555555;stroke-width:2"/><text x="50%" y="50%" font-size="18" text-anchor="middle" alignment-baseline="middle" font-family="monospace, sans-serif" fill="#555555">100×100</text></svg>
</div>
</div>
<div class="box hide" data-aspect-ratio="2">
<div class="content">
<svg viewBox="0 0 200 100" width="200" height="100" xmlns="http://www.w3.org/2000/svg"><rect x="2" y="2" width="196" height="96" style="fill:#DEDEDE;stroke:#555555;stroke-width:2"/><text x="50%" y="50%" font-size="18" text-anchor="middle" alignment-baseline="middle" font-family="monospace, sans-serif" fill="#555555">200×100</text></svg>
</div>
</div>
<div class="box hide" data-aspect-ratio="3">
<div class="content">
<svg viewBox="0 0 300 100" width="300" height="100" xmlns="http://www.w3.org/2000/svg"><rect x="2" y="2" width="296" height="96" style="fill:#DEDEDE;stroke:#555555;stroke-width:2"/><text x="50%" y="50%" font-size="18" text-anchor="middle" alignment-baseline="middle" font-family="monospace, sans-serif" fill="#555555">300×100</text></svg>
</div>
</div>
</div>
<div class="nav">
<button data-index="1">1</button>
<button data-index="2">2</button>
<button data-index="3">3</button>
</div>
</body>
</html>
Make the parent DIV behave like a table cell and align the child element vertically. No need to do any padding tricks.
HTML
<div class="parent">
<img src="foo.jpg" />
</div>
CSS
.parent { width:300px; height:300px; display:table-cell; vertical-align:middle; }