svg inside svg translates by browser resize - reactjs

I have a svg and I am adding different svg elements to it:
<svg> .... <rect or circle and so on </svg>
What I did was I loaded an icon with an svg library to one on my circles. Everything was fine until I changed my browser size and I noticed the svg icons are moving outside of my circle.
<svg> <circle> <g> <svg ....the icon> </g> </svg>
Looks like because I have 2 svgs nested, the icon is moving, and the offset for it is maybe the browser!
How can I fix that? I need to load my images like this:
Image1 = require('svg-react-loader?name=Image1!./svgFiles/default.svg');
React.createElement(Image1, {});

Related

How to register custom icons by reading a SVG file from path?

I am building an application making use of #fluentui/react (version 8.34.7) and I am using create-react-app.
One of the things I would like to do is register custom icons which are in SVG format. Following the example here, I came up with the following code:
registerIcons({
icons: {
'hamburger-icon': (
<svg viewBox="0 0 16 16" role="presentation" focusable="false" aria-hidden="true">
<g>
<path d="M1 4.5v-1h14v1zm0 4v-1h14v1zm0 4v-1h14v1z"></path>
</g>
</svg>
)
}
});
and this works perfectly fine. I can reference hamburger-icon in my code and get the icon to display.
However, the issue with the code above is that I have to include svg file contents in my code. What I would like to do is read the SVG file contents and then use it in registerIcons method. I am not sure how I can do that.
If I do something like:
import Hamburger from 'assets/images/hamburger.svg';
I get a URL.
If I do something like:
import {ReactComponent as HamburgerIcon} from 'assets/images/hamburger.svg';
I get a React component whereas registerIcon method expects a React element.
Is it possible to simply specify a file path of an SVG file and register icon from that?
Well, it was ridiculously simple! All I had to do was this:
import {ReactComponent as HamburgerIcon} from 'assets/images/hamburger.svg';
registerIcons({
icons: {
'hamburger-icon': React.createElement(HamburgerIcon)
}
});
and all worked well.

Next Image not taking class properties

I am using Next.js and next/image to display images, but the CSS is not working inside it. The image is in SVG format and I have placed it in the public folder. I am using Tailwind CSS along with this.
<Image
className="mt-3"
data-testid="close-icon"
src="/CloseIcon.svg"
alt="Close Nav Bar"
height="24"
width="24"
/>
I am not sure why it is not working and it is not being reflected in the browser. Any help will be greatly appreciated!
Before Next.js 12.2
Styling the next/image component's margins this way doesn't work in older Next.js versions. See relevant GitHub discussion for more details.
Internally to the next/image component, the <img> element and the elements that wrap it have inline styles that override certain values passed through className.
As a workaround, you can add a wrapper element and apply the margin styling to it instead.
<div className="mt-3">
<Image
data-testid="close-icon"
src="/CloseIcon.svg"
alt="Close Nav Bar"
height="24"
width="24"
/>
</div>
From Next.js 12.2
You can use the next/future/image component instead. This new component renders a single <img> element without any additional wrappers by default, and is no longer constrained by the wrapper's styles override.
You can enable next/future/image in next.config.js.
module.exports = {
experimental: {
images: {
allowFutureImage: true
}
}
}
From Next.js 13
The next/future/image component has been converted to next/image. Like next/future/image, this component renders a single <img> element and can be styled directly with className/styles.
Juliomalves's answer is right, but I would prefer:
<div className="mt-3" height="24" width="24">
<Image
data-testid="close-icon"
src="/CloseIcon.svg"
alt="Close Nav Bar"
layout="fill"
/>
</div>
You also can use a little cheat:
import {motion} from "framer-motion";
<motion.img
className="mt-3"
data-testid="close-icon"
src="/CloseIcon.svg"
alt="Close Nav Bar"
height="24"
width="24">
Try this:
<div style='width:104px;height:104px;position:relative;'>
<Image
alt='Mountains'
src='/mountains.jpg'
layout='fill'
objectFit='contain'
/>
</div>
More on objectFit Here
Wrapping the image component in a div helps solve the issue for me. I can apply the class name to the div and everything works as it should.

React JSX - Filling an svg with an image

I am trying fill a simple circle SVG with an image in React. The sources I found have not been helpful, for they don't seem to apply to JSX.
Here is the code I currently have:
import React, {Component} from 'react';
import Anime from '../images/Anime.jpeg';
class Circle extends Component {
render() {
return (
<div>
<svg height="300" width="300">
<circle cx="150" cy="150" r="100" stroke="black" stroke-width="3" fill={Anime} />
</svg>
</div>
);
}
}
export default Circle;
My first instinct was to tweak the fill attribute by either replacing the original color ="red" with ={Anime} or =url("../images/Anime.jpeg"), but neither worked.
(I know the path to the Anime image is correct, after testing it by rendering <img src={Anime} alt={Anime}></img> inside the div.
I see people using <defs> and <pattern> tags, as such (referring to this post):
<svg id="graph" width="100%" height="400px">
<!-- pattern -->
<defs>
<pattern id="image" x="0%" y="0%" height="100%" width="100%"
viewBox="0 0 512 512">
<image x="0%" y="0%" width="512" height="512" xlink:href="https://cdn3.iconfinder.com/data/icons/people-professions/512/Baby-512.png"></image>
</pattern>
</defs>
<circle id="sd" class="medium" cx="5%" cy="40%" r="5%" fill="url(#image)" stroke="lightblue" stroke-width="0.5%" />
</svg>
But these tags don't seem to work in React / JSX; I keep getting this error when I try to use them: Namespace tags are not supported by default. React's JSX doesn't support namespace tags. You can turn on the 'throwIfNamespace' flag to bypass this warning.
Is there a way around this? I want to be able to fit the following picture into a simple circle svg with a thick black border around it.
I figured out a way and wanted to post / share it, in case anyone is here, looking at this post and wondering what the (one of the) solution(s) is!
Here is my final code:
import React, {Component} from 'react';
import AnimeCropped from '../images/AnimeCropped.png';
class Circle extends Component {
render() {
return (
<div>
<svg height="670" width="670">
<circle cx="350" cy="350" r="300" stroke="black" stroke-width="5" fill="none" />
<image className='img-circle' xlinkHref={AnimeCropped} x='75.5' y="15" height="670" width="670"/>
</svg>
</div>
);
}
}
export default Circle;
Essentially what I did was:
Crop the photo to be a circle. I could have followed cubrr's recommendation above and clip-path'ed it (which is a great idea!), but instead - I just found and used a photo cropping tool online. I then named the newly-cropped picture "AnimeCropped" and saved it in the same directory.
Imported the picture into the component file and rendered via a <image> tag after the <circle> tag. It wasn't mentioned in my original question above, but I also hypothesized that inserting the image inside the circle might solve the problem. As such:
<svg height="670" width="670">
<circle cx="350" cy="350" r="300" stroke="black" stroke-width="5" fill="none">
<img src={AnimeCropped} alt={AnimeCropped}/>
</circle>
</svg>
But this didn't work.
In my final code, you'll see that the <image> tag comes after the <circle> tag, so technically, it's not really inside the circle, but just overlaying on top of it. Also note that I had to use <image>, not <img>. I'll have to do a bit of research on this, but <img> was giving me an error message.
Also important: In React, xlink:href won't work, but xlinkHref will! This really tripped me off, but I found this helpful post.
Using classNames, I edited the size of the cropped picture in my App.css file.
Configured the x- and y- coordinates so that it somewhat fits perfectly in the middle of the circle.
That's it! Just wanted to post this for closure / help me review what I learned.

TouchableOpacity not triggering clicks when nested inside react-native-svg

I have a TouchableOpacity nested inside SVG element create by using react-native-svg library. It looks something like this:
<Svg
width="100%"
height="100%"
top="0%"
left="0%"
viewBox="0 0 828 828"
>
(...)
<TouchableOpacity onPress={this.handleClick}>
<Text>Button!</Text>
</TouchableOpacity>
(...)
</Svg>
Where handleClick is not triggered. I have different implementation for the same feature for Android where TouchableOpacity is outside Svg element and onPress is correctly triggered. Has anyone encountered this issue and found a solution? Placing this outside for iOS is not an option, unfortunately.
Used react-native-svg version is 7.0.3
Thanks!

Svg clipPath in AngularJS app with HTML Base Element

I use svg clipPath in my AngularJS project. I have a problem with specifying a relative url to the clipPath because i need to use the <base> element in my project.
For example this code works in a project without base, but not in a project with <base href="/">
<svg width="120" height="120"
viewPort="0 0 120 120" version="1.1"
xmlns="http://www.w3.org/2000/svg">
<defs>
<clipPath id="myClip">
<rect x="10" y="10" width="60" height="60"></rect>
</clipPath>
</defs>
<g clip-path="url(#myClip)">
<circle cx="30" cy="30" r="20"/>
<circle cx="70" cy="70" r="20"/>
</g>
</svg>
How can this be solved? I use ui-router, if that is relevant to the question...
This question is about the same, but the "solution" the OP found was to remove the base which isn't a solution in my case.
Change
<g clip-path="url(#myClip)">
So that you're using an absolute URL + a fragment identifier rather than just a fragment identifier on its own. E.g. url(http://mydomain.com/mypage#myClip)
You may be able to remove some parts e.g. the http and the domain if the base tag matches the absolute URL so /mypage#myClip might work.
In case anyone needs a function to do it here's one in pure JS:
It stores the original clipPath in the element data attribute and each time called, uses document.location.href to use absolute paths for url()
function fixClipPaths(svg, restore) {
Array.prototype.forEach.call(svg.querySelectorAll('*[clip-path]'), function (el) {
var clipUrl = el.getAttribute('clip-path');
if(!el.getAttribute('data-original-clip-path')) {
el.setAttribute('data-original-clip-path', clipUrl);
}
el.setAttribute('clip-path', 'url('+ (!restore ? document.location.href : '') + el.getAttribute('data-original-clip-path').substr(4));
});
}
// usage example
fixClipPaths(document.getElementsByTagName('svg')[0]);

Resources