Expo append to SVG onClick at x/y coords - reactjs

I have an SVG in a React-Native Expo app that I am rendering fine. The alert_coords method works to and I can get the locationX and locationY as well. What I want now is using the svgRef to append a <Rect> for example at the x/y coords but not sure how to do it and all attempts with like svgRef.current.appendChild("<Rect //.. />") and similar tests don't work.
<ReactNativeZoomableView
maxZoom={30}
contentWidth={1000}
contentHeight={800}
>
<Svg
ref={svgRef}
height="100%"
width="100%"
rotation={90}
viewBox="0 0 1300 800"
onTouchEnd={(e) => alert_coords(e)}
>
<Image href="urlhere" />
</Svg>
</ReactNativeZoomableView>
</View>

Related

How to apply onClick event on Svg file on React?

I made a component for a Svg and I'm trying to apply onClick event so I change the state, but it doesnt work for some reason, I'm not sure what I'm doing wrong. I tried applying the onCLick on too , but it doesnt work either.
my code
import React, { useState } from "react";
import './style.scss'
const AverageSvg=() => {
const [active, setActive] = useState(false);
return (
<svg className="average" onClick={() => setActive(false)}
xmlns="http://www.w3.org/2000/svg"
width="170.539"
height="51.974"
viewBox="0 0 170.539 51.974"
>
<g data-name="The average" transform="translate(-1223 -2501)" >
<g className={active ? "clicked-fill" : "fill "}
// fill="none"
stroke="#707070"
strokeWidth="1"
data-name="Rectangle 60"
transform="translate(1223 2501)"
>
<rect
width="170.539"
height="51.974"
stroke="none"
rx="25.987"
></rect>
<rect
width="169.539"
height="50.974"
x="0.5"
y="0.5"
rx="25.487"
></rect>
</g>
<text className="text"
// fill="#464646"
data-name="The average"
fontFamily="ArialMT, Arial"
fontSize="17"
transform="translate(1261 2532)"
>
<tspan x="0" y="0" >
The average
</tspan>
</text>
</g>
</svg>
);
}
export default AverageSvg;
Have you tried wrapping the svg with another tag like a div or span and attaching the onClick on that wrapper ?
Also inside the setActive() you should pass true instead of false.
you can warp the SVG in another tag as Alexader says, also you can pass the event to the child element in the SVG tag directly and it will work. but you should be aware of the browser compatibility
check this link from MDN web Docs
https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/onclick
for example, this is an SVG delete icon
export const Delete = ({ ...props }) => {
return (
<svg xmlns="http://www.w3.org/2000/svg" height="24" width="24" className={props?.className} >
<path onClick={props?.onClick} d="M7 21q-.825 0-1.412-.587Q5 19.825 5 19V6H4V4h5V3h6v1h5v2h-1v13q0 .825-.587 1.413Q17.825 21 17 21Zm2-4h2V8H9Zm4 0h2V8h-2Z" />
</svg>
)
}
when you call it just <Delete onClick={yourEvent} />
the onClick will apply on the <path/> tag

Draw multiple rectangle in single image in reactjs

I'm trying to draw rectangles on image using x,y,height and width but i can only able to draw one rectangle at a time can't able to draw multiple rectangle in single image.
JSX code:
{this.state.books.map((object) => (
<div class="img-overlay-wrap"style={{ marginLeft: "27%" }}>
<img src={`data:image/png;base64,${object.image_id}`} style={{ height:`${object.hw[0]}` , width:`${object.hw[1]}`}} alt="" />
<svg width={object.hw[1]} height={object.hw[0]} >
<g >
<rect x={object.bbox[0]} y={object.bbox[1] - object.bbox[3]} width={object.bbox[2]} height={object.bbox[3] }
style={{stroke:"red",}} fill-opacity="0.0" />
<text x={object.bbox[0]} y={object.bbox[1]} font-family="Verdana" font-size="35" fill="red">{object.category_id}</text>
</g>
Sorry, your browser does not support inline SVG.
</svg>
<h3>Confidence:{object.score}</h3>
<h3>Category Id:{object.category_id}</h3>
<p>{object.bbox[0]},{object.bbox[1]},{object.bbox[3]},{object.bbox[2]}</p>
</div>
))}
i am using map function to iterate through the array but i couldn't able to draw all the rectangles in single image please help me
I've done This, and it worked for me, maybe it is useful for you:
const rectInfos = [
[{top:"0px",left:"0px",color:"green"},{top:"0px",left:"500px",color:"yellow"}],
[{top:"150px",left:"0px",color:"blue"},{top:"150px",left:"500px",color:"black"}],
]
const rects = rectInfos.map((row,i)=>
row.map((svgElement,j)=>
<svg key={`i+${i}+${j}+j`} style={{width:"500px", height:"150px",position: "absolute", top:svgElement.top, left:svgElement.left}}>
<rect style={{fill:svgElement.color, width:"500px", height:"150px"}} />
</svg>
)
)
return (
<div style={{position: "relative", width:"1000px", height:"300px"}}>
{rects}
</div>
)
}
This case is for 4 rectangles with the same width and height in a parent div.

Menu not rendering fully in svg

I'm trying to render a drop-down menu inside an svg, such as:
However, when I click on it, the bottom of the menu is chopped off:
How can I fix this?
<svg>
<foreignObject x={0} y={0} width={"100%"} height={"100%"}>
<Menu>
<MenuButton as={Button} rightIcon={<ChevronDownIcon />}>
Actions
</MenuButton>
<MenuList>
<MenuItem>Download</MenuItem>
<MenuItem>Create a Copy</MenuItem>
<MenuItem>Mark as Draft</MenuItem>
<MenuItem>Delete</MenuItem>
<MenuItem>Attend a Workshop</MenuItem>
</MenuList>
</Menu>
</foreignObject>
</svg>
Here's the codesandbox:
https://codesandbox.io/s/chakra-button-forked-7ig5f?file=/src/App.js
You can try adding height or view box attributes to the SVG element. The SVG is most likely being cut off for this reason.
<svg viewBox="0 0 500 500">
or
<svg height="500px">

Why does React render these SVGs differently?

If I render SVG elements to a defined SVG object, then all is well.
If I render an entire SVG object, the resulting image doesn't scale.
If you look at the following demo, the 1st svg renders correctly. The svg fills the width of the browser and everything scales.
The second svg is a 300x150 image that doesn't scale at all.
https://codepen.io/brunnock/pen/ydMdgv
// The following renders as expected.
function SVG1() {
return (
<g>
<rect height="100%" width="100%" fill="#ccc" />
<circle cx={150} cy={75} r={50} fill="red" />
<text x="100" y="275" font-size="50">
This is the right size.
</text>
</g>
);
}
// svg1 is a predefined svg element in the HTML file.
ReactDOM.render(<SVG1 />, svg1);
// The following renders a 300x150 image that doesn't scale.
function SVG2() {
return (
<svg viewbox={"0 0 600 600"}>
<rect height="100%" width="100%" fill="#ccc" />
<circle cx={150} cy={75} r={50} fill="red" />
<text x="100" y="275" font-size="50">
This is not the right size.
</text>
</svg>
);
}
// svg2 is a div.
ReactDOM.render(<SVG2 />, svg2);
I couldn't find any difference in the rendered html via Chrome's inspector.
Remove the parentheses from the viewBox
function SVG2() {
return (
<svg viewbox="0 0 600 600">
<rect height="100%" width="100%" fill="#ccc" />
<circle cx={150} cy={75} r={50} fill="red" />
<text x="100" y="275" font-size="50">
This is not the right size.
</text>
</svg>
);
}
I'm an idiot. Change "viewbox" to "viewBox" and "font-size" to "fontSize".

Accepting an SVG as a prop to a React component

I am working on a React component that needs to accept an SVG as a prop.
I have a default svg in case the prop is not provided, but if it is provided then the default svg can be replaced out with the one accepted from prop.
So the component is like.
<MySvg src={path to svg or the svg itself} height={20} width={20} fillColor={'red'} />
All that the component should do is display this SVG with the provided props. I expect the DOM to be something like this.
<svg fill={this.props.fillColor} height={this.props.height} width={this.props.width}>
<g>
<rect />
</g>
</svg>
Since each SVG is different, meaning it might have a polygon, rect, g, etc., what is the best way to achieve this?
2 things I am looking for?
Loading an svg from a path and applying the props (color, height, width)?
If src prop has the SVG itself, how do we display that and apply the props (color, height, width)
Update: I tried out one of the approach in the answers below, but it does not seem to work. Here is a link to a CodeSandbox I have in place - codesandbox.io/s/n0yzv9yyvp
I worked with SVG in react.js a lot, the best solution I found is to use embed svg:
<MySvg src={'/path/to/svg'} height={20} width={20} fillColor={'red'} />
And inside your component
<svg fill={this.props.fillColor} height={this.props.height} width={this.props.width}>
<g>
<image
x="10"
y="197"
width="159"
height="113"
xlinkHref={this.props.src}
/>
</g>
</svg>
x, y, width and height are usual parameters of the image svg tag, you can omit or rewrite them as you want
Your best bet will be to use data URIs.
Example:
<img src='data:image/svg+xml;utf8,<svg ... > ... </svg>'>
Read more here:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs
you can pass props to svg components just like any other components
for example
function App() {
return <Svg height={20} width={20} fill={black} />;
}
const Svg = ({ height, width, color }) => (
<svg
height={height}
width={width}
fill={color}
xmlns="http://www.w3.org/2000/svg"
></svg>
);

Resources