I need change the color and scale the svg in react.
Using i can manipulate the width and height using a className like: "w-[10vw]", but can't change the color. On other hand, if i create a component for SVG, i don't know how to make scalar size using className.
code of my attempts:
<VectorBarSVG fill={theme ? '#61A0AF' : '#CECE25'} />
" />
Code of React Component SVG:
import * as React from 'react'
export interface IVectorBarSVGProps {
fill: string
}
export function VectorBarSVG({ fill }: IVectorBarSVGProps) {
return (
)
}
You may modify colors of an SVG by specifying "fill" and "stroke".
function Component() {
return (
<svg className="stroke-cyan-500 fill-blue-500">
<!-- ... -->
</svg>
);
}
When using TailwindCSS you must not dynamically assemble class names. This will not work:
function Component({color}) {
return (
<svg className={`stroke-${color} fill-${color}`}>
<!-- ... -->
</svg>
);
}
This is because TailwindCSS does not execute your programm. It scans your source code and generates a CSS file based on class names and expressions it recognized.
Instead, you should be hiding these details in your component and expose a more convenient interface.
function Component({isPrimary, isBig}) {
return (
<svg className={
(isPrimary ? "fill-blue-500" : "fill-gray-500")
+ " "
+ (isBig ? "w-96" : "w-24")
}>
<!-- ... -->
</svg>
);
}
// And use it like so:
<Component isPrimary />
<Component isBig />
One more thing, if your desired color changes aren't applied to the SVG, you are probably not applying your classes to the correct elements within that SVG.
For example, here's an SVG that contains a circle, a rectangle, and a line:
function Component() {
return (
<svg width="391" height="391" viewBox="-70.5 -70.5 391 391">
<rect x="-70" y="-70" width="390" height="390"/>
<g>
<rect x="25" y="25" width="200" height="200" />
<circle cx="125" cy="125" r="75"/>
<line x1="50" y1="50" x2="200" y2="200" />
</g>
</svg>
)
}
To change the fill color of the circle, you would have to apply the class names to the circle element:
function Component() {
return (
<svg width="391" height="391" viewBox="-70.5 -70.5 391 391">
<rect x="-70" y="-70" width="390" height="390"/>
<g>
<rect x="25" y="25" width="200" height="200" />
<circle cx="125" cy="125" r="75" className="fill-amber-500 stroke-blue-800"/>
<line x1="50" y1="50" x2="200" y2="200" />
</g>
</svg>
)
}
Related
I've made this sudoku board on Figma
and I want to put this on my website using ReactJS. My goal is to add events listeners to the rects and change the value based on user pressing 1-9 numbers (just like sudoku works). The problems is that I don't know how to programmatically position text based on rect (x,y) position and size.
For example:
<g x="7.49219" y="4.37108" width="34.8721" height="34.8721">
<rect
x="7.49219"
y="4.37108"
width="34.8721"
height="34.8721"
rx="4.75"
stroke="#D5D5D5"
stroke-width="0.5"
/>
<text id="text_test" fill="red" fontSize={24}>
5
</text>
</g>
I've calculated the text position as
x = rectXPos + (rectWidth / 2)
y = rectYPos + (rectHeight / 2)
but it gives me:
I thought considering the text width and height in the formula to center it. But I just get the text size in pixels on the browser
So when I try to update the formula to
x = rectXPos + (rectWidth / 2) - (textWidth / 2)
y = rectYPos + (rectHeight / 2) + (textHeight / 2)
I get this:
The X position works but the Y don't.
What am I missing?
Is there a better way to implement what I want?
You can rearrange the text element with the attributes text-anchor="middle" and dominant-baseline="middle" and set the position the middle of the expected "frame". So, in this case the <rect> and the <text> have the same starting point. The <rect> is 40x40 and then the <text> needs to be in 20,20.
You can see from the example that you can use <g> and its attribute transform/translate to move around the elements. This gives mush more readable code now that you have 9x9 elements that need to be placed.
If this is going to be used in a browser I will suggest you to use CSS for styling. Like replace the attribute stroke etc. with a stylesheet like:
svg.sudoku rect {
stoke: #D5D5D5;
stroke-width: .5px;
}
<svg viewBox="0 0 200 200">
<g transform="translate(5 5)">
<g transform="translate(0 0)">
<rect
width="40"
height="40"
rx="4.75"
stroke="#D5D5D5"
stroke-width="0.5"
fill="none"
/>
<text x="20" y="20" text-anchor="middle" dominant-baseline="middle" fill="red" font-size="20">5</text>
</g>
<g transform="translate(45 0)">
<rect
width="40"
height="40"
rx="4.75"
stroke="#D5D5D5"
stroke-width="0.5"
fill="none"
/>
<text x="20" y="20" text-anchor="middle" dominant-baseline="middle" fill="red" font-size="20">2</text>
</g>
</g>
</svg>
I want to add a draggable Rect (red in the screenshot below) with children (the icon and text in the screenshot below). Whenever I try this, I get this error:
TypeError: parentInstance.add is not a function
Here is the code of just trying to add the text:
<Rect x={0} y={0} width={200} height={100} draggable fill="red">
<Text text="Pencil" />
</Rect>
Rect or another other Konva shape can't have children elements. You can't nest on shape into another shape. For that case, you need to use Groups
<Group x={0} y={0} draggable>
<Rect width={200} height={100} fill="red" />
<Text text="Pencil" />
</Group>
Here is the svg of the icon:
<?xml version="1.0"?>
<svg viewBox="0 0 128 128" xmlns="http://www.w3.org/2000/svg">
<defs>
<style>.cls-1{fill:#4d91bb;}.cls-2{fill:#2175aa;}.cls-3{fill:#c3edf5;}.cls-4{fill:#7ad0cb;}.cls-5{fill:#4dbab4;}</style>
</defs>
<title />
<g data-name="23 Page Rank Badge" id="_23_Page_Rank_Badge">
<path class="cls-1" d="M43.22,61,64,73,40,114.53,35.65,98.07l-16.43,4.46Zm41.57,0L64,73l24,41.57,4.35-16.46,16.43,4.46Z" />
<polygon class="cls-2" points="39.48 67.43 39.48 81.48 55.02 81.48 57.59 84.06 64 72.97 43.22 60.97 39.48 67.43" />
<polygon class="cls-2" points="92.52 74.36 84.78 60.97 64 72.97 71.87 86.6 76.98 81.48 92.52 81.48 92.52 74.36" />
<rect class="cls-3" height="53.03" width="53.03" x="37.48" y="24.45" />
<rect class="cls-3" height="53.03" transform="translate(54.78 -30.33) rotate(45)" width="53.03" x="37.48" y="24.45" />
<circle class="cls-4" cx="64" cy="50.97" r="15" />
<path class="cls-5" d="M52.74,51A15,15,0,0,1,65.87,36.09a15,15,0,1,0,0,29.74A15,15,0,0,1,52.74,51Z" />
</g>
</svg>
Here's the component I made but it's not showing the icon :(
const RankSVG = ({
width = "53" ,
height = "53" ,
viewBox="0 0 128 128" ,
}) => {
return(
<>
<svg viewBox={viewBox} xmlns="http://www.w3.org/2000/svg">
<style>{`.cls-1{fill:#4d91bb}.cls-2{fill:#2175aa}.cls-3{fill:#c3edf5}.cls-4{fill:#7ad0cb}.cls-5{fill:#4dbab4}`}</style>
<path className="cls-1" d="M43.22,61,64,73,40,114.53,35.65,98.07l-16.43,4.46Zm41.57,0L64,73l24,41.57,4.35-16.46,16.43,4.46Z" /><polygon className="cls-2" points="39.48 67.43 39.48 81.48 55.02 81.48 57.59 84.06 64 72.97 43.22 60.97 39.48 67.43" /><polygon className="cls-2" points="92.52 74.36 84.78 60.97 64 72.97 71.87 86.6 76.98 81.48 92.52 81.48 92.52 74.36" /><rect className="cls-3" height={height} width={width} x="37.48" y="24.45" /><rect className="cls-3" height={height} transform="translate(54.78 -30.33) rotate(45)" width={width} x="37.48" y="24.45" /><circle className="cls-4" cx="64" cy="50.97" r="15" /><path className="cls-5" d="M52.74,51A15,15,0,0,1,65.87,36.09a15,15,0,1,0,0,29.74A15,15,0,0,1,52.74,51Z" /></svg>
</>
);
}
What am I doing wrong here? if I want to include such svgs in future, what points should I remember to avoid making mistakes?
You have to move your styles out of to .jsx file into your .css file. The curly braces {} that separate classes interfere with .jsx {} which are intended for expressions.
Here's a working sample: https://codesandbox.io/s/718w386v46
Now, alternatively, you could keep your styles in the .jsx file but then you have to define them as object literals with an additional set of curly braces inside the curly braces that are intended for expressions.
I have the following SVG document:
<svg preserveAspectRatio="xMinYMin meet" viewBox="0 0 21 484" xmlns="http://www.w3.org/2000/svg">
<defs>
<filter id="dropShadow">
<feDropShadow dx="4" dy="0" stdDeviation="4"></feDropShadow>
</filter>
</defs>
<g id="Artboard" stroke-width="5" stroke="#FF0000" fill="#000000" stroke-linecap="round">
<path style="filter: url(#dropShadow)" d="M7.5,8.5 L7.5,471.5" id="path-1"></path>
</g>
</svg>
In Firefox, when I open the SVG document, it simply shows a very thin (not 5 wide) vertical line. In Chrome, it doesn't show anything (nor does it in codepen, here: https://codepen.io/jwir3/pen/BJBqEK ).
I'm not quite sure what I'm doing incorrectly here, but it has something to do with the filter, because, if I remove the filter: url(#dropShadow) from the path definition, the line shows up as expected.
You can't use objectBoundingBox units if your shape has no height or width.
Keyword objectBoundingBox should not be used when the geometry of the applicable element has no width or no height, such as the case of a horizontal or vertical line, even when the line has actual thickness when viewed due to having a non-zero stroke width since stroke width is ignored for bounding box calculations. When the geometry of the applicable element has no width or height and objectBoundingBox is specified, then the given effect (e.g., a gradient or a filter) will be ignored.
The default for filterUnits is objectBoundingBox units so you need to change that to userSpaceOnUse i.e.
<svg preserveAspectRatio="xMinYMin meet" viewBox="0 0 21 484" xmlns="http://www.w3.org/2000/svg">
<title>Line Drop Shadow</title>
<description>A red line with 5px width thickness and round caps, having a drop-shadow. This highlights the regression documented in PURP-1017.</description>
<defs>
<filter id="dropShadow" filterUnits="userSpaceOnUse">
<feDropShadow dx="4" dy="0" stdDeviation="4"></feDropShadow>
</filter>
</defs>
<g id="Artboard" stroke-width="5" stroke="#FF0000" fill="#000000" stroke-linecap="round">
<path style="filter: url(#dropShadow)" d="M7.5,8.5 L7.5,471.5" id="path-1"></path>
</g>
</svg>
When processing filters, different browsers process in different stroke.
Chrome considers stroke as a value with a zero pixel, so it does not include it in the filter region.
Therefore, to make the result look the same in different browsers, it is better to replace path with stroke-width ="5", a rectangle with a width of 5px withoutstroke (stroke="none")
In addition, the default values for the filter area are: x =" - 10% "" y = "- 10%" `` width = "120%" `` height = "120%"- large blur sizes are usually truncated .
By default, filterUnits = "objectBoundingBox" and therefore the values are specified in percentages.
To make it easier to calculate the size of the filter region action, specify the value offilterUnits = "userSpaceOnUse" and then you can specify all dimensions for thefilter region` in pixels.
<svg preserveAspectRatio="xMinYMin meet" width="100%" height="100%" viewBox="0 0 21 484" xmlns="http://www.w3.org/2000/svg" >
<defs>
<filter id="dropShadow" filterUnits = "userSpaceOnUse" x="4" y="0" width="12" height="472">
<feDropShadow dx="6" dy="4" stdDeviation="3"></feDropShadow>
</filter>
</defs>
<g id="Artboard" fill="#FF0000" filter="url(#dropShadow)" >
<!-- <path style="filter: url(#dropShadow)" d="M7.5,8.5 L7.5,471.5" id="path-1" stroke-width="5" ></path>-->
<rect x="5" y="5" width="5" stroke="none" height="463" />
</g>
</svg>
Swapping to userSpaceOnUse is the correct answer in most circumstances but has the following limitations:
The filter effects region will apply from -10% to 120% of the canvas, rather than the bounding box of the element (using more memory and processing time)
For large dynamic SVGs (such as created by d3) it can be hard to calculate the required filter x/y/width/height to ensure the filter applies to all elements.
An alternate (less elegant) solution is to apply the filter to a <g> and use a hidden node within this to give the group the correct width or height:
<svg xmlns="http://www.w3.org/2000/svg">
<defs>
<filter id="dropShadow" width="20">
<feDropShadow dx="4" dy="0" stdDeviation="4"></feDropShadow>
</filter>
</defs>
<g id="Artboard" style="filter: url(#dropShadow)">
<circle r="5" cx="0" cy="0" visibility="hidden"></circle>
<path d="M10,10 L10,100" stroke-width="5" stroke="#FF0000" fill="#000000" stroke-linecap="round"></path>
</g>
</svg>
I want to create a SVG path with several points and several bezier curves. If I add a bezier curve the SVG is not rendered. How can I have several bezier curves?
SVG
<polyline points="0,50 Q0,20 101,40 404,50"
stroke="black"
stroke-width="3" fill="none">
</polyline >
If you want to draw a beziere curve you need to use the <path> element. So your example would become:
<path d="M0,50 Q0,20 101,40"
stroke="black"
stroke-width="3" fill="none">
</path>
Now, if you wanted to add more connected curves you would write:
<path d="M0,50 Q0,20 101,40 Q100,0 120,40"
stroke="black"
stroke-width="3" fill="none">
</path>
Try something like this:
<svg height="180" width="500">
<polyline points="0,50 0,20 101,40 404,50" stroke="black" stroke-width="3" fill="none" />
</svg>