Why does React render these SVGs differently? - reactjs

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".

Related

React-PDF errors when rendering <Svg> via Ternary conditional

I have an svg header that works perfectly when rendered directly in the tree
<Document>
<Page size="A4">
<MyHeaderSvg />
</Page>
</Document>
However when I render it via a Ternary within the render callback of a element, it errors...
<Document>
<Page size="A4">
<View render={({pageNumber}) => (pageNumber === 1)
? <MyHeaderSvg />
: <NonSvgHeader />
</Page>
</Document>
MyHeaderSvg
<Svg width="555" height="80" viewBox="0 0 555 80">
<Rect width="100%" height="80" rx="8" ry="8" fill="url(#header-rectangle)"/>
<Defs>
<RadialGradient id="header-rectangle" cx="0" cy="0" fr="1">
<Stop stopColor="blue"/>
<Stop offset="1" stopColor="lightblue"/>
</RadialGradient>
</Defs>
<Text style={styles.title} x={`${535-20}px`} y="50%" textAnchor="end" dominantBaseline="middle">Some title</Text>
<G transform="translate(20,21) scale(0.749 0.749)">
<Path d="M0 47.6941V37.8042C0 35.4188 1.53483 32.1486 6.07125 32.1486C9.60438 32.1486 11.6936 35.4536 11.6936 38.6453C11.6936 41.7456 9.59732 44.8226 6.29155 44.8226C5.26468 44.8226 4.14881 44.4355 3.28243 43.6822V47.6941C3.28243 48.7881 2.59891 49.5178 1.64134 49.5178C0.683769 49.5178 0 48.7881 0 47.6941ZM5.85848 41.8135C7.56766 41.8135 8.4107 40.0355 8.4107 38.5075C8.4107 36.958 7.56766 35.1579 5.85848 35.1579C4.1031 35.1579 3.28243 36.7993 3.28243 38.3951C3.28243 39.9903 4.05763 41.8135 5.85848 41.8135Z" fill="white"/>
</G>
</Svg>
Experimentation
I tried commenting out different sections to see the results
Section commented out
Resulting error
Rect and Defs section only
Text line only
Original error
G section only
Original error
Text and G
Original error
G, Rect and Defs
Rect, Defs and Text
Updates:
I have been playing with the Repl on React-PDFs website to see where things can break more realtime.
Looks like changing any definitions of numbers from strings into actual numbers ie x="10" to x={10} helps.
<Text> breaks it entirely! There doesn't seem to be any way to get a <Text> element included in any way, shape or form.
<Path> does seem to work fine as it is defined above, however the <G> transform is breaking things as it is defined via a string.

Expo append to SVG onClick at x/y coords

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>

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.

React "base class"/ inheritance

I'm pretty new to React,
And I was given a simple home assignment.
I was asked to create 3 kinds of a clickable circle component.
The JSX is common to all (SVG),
But their
size
background color
onClick callback
are different.
What is the correct way nowadays (2019, React 16) to get such a common base class/
interface in React ?
HOC ? React Hooks ? some other way ?
Say this is my "base" Circle component
(Please ignore the hard coded props):
export default function Circle(props) {
return (
<svg height="100" width="100">
<circle cx="50" cy="50" r="26" fill="#9cc6d4" />
<circle
cx="50"
cy="50"
r="20"
stroke="#FFF"
strokeWidth="1"
fill="none"
/>
</svg>
);
I want to make myself clear:
I've only 3 different types of that basic Circle
(Say Red, Blue and Yellow...)
And I need to put them on screen according
to a JSON.
I don't want to DRY and create
3 different classes: RedCircle, BlueCircle and YellowCircle...
which eventually will be based on the same JSX
Your help is highly appreciated.
You can easily do these cutomizations with props..
const {size, onClick, bgColor} = props;
// now use the size, onClick and bgColor props in your JSX markup
return (
<svg height="100" width="100">
<circle cx="50" cy="50" r="26" fill="#9cc6d4" />
<circle
cx="50"
cy="50"
r="20"
stroke="#FFF"
strokeWidth="1"
fill="none"
/>
</svg>
);
Using the Circle component would look like
<Circle size={'40'} onClick={()=>{ console.log('im red circle'}) bgColor={'red'} />
<Circle size={'35'} onClick={()=>{ console.log('im green circle'}) bgColor={'green'} />
You can pass these infos, by using the props parameter of the function component.
Your component would look something like that:
export default function Circle(props) {
return (
<svg height={props.size} width={props.size}>
<circle cx="50" cy="50" r="26" fill={props.color} />
<circle
cx="50"
cy="50"
r="20"
stroke="#FFF"
strokeWidth="1"
fill="none"
/>
</svg>
);
}
Then, when used in parent components:
<Circle size={yourSize} color={yourColor}/>
In this case, size etc... would be available in the function as props.size etc...
You can do the same and pass event handlers like onClick etc...
Reference:
https://reactjs.org/docs/components-and-props.html

Resources