Konva Draggable Rect with Children - react-konva

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>

Related

How to modify SVG in react

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>
)
}

SVG: How to programmatically center an <Text/> based on <Rect/> position and size

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>

How to draw a shape vector for "quarter" circle

I've been going round and round on this. Apparently, my usual muddle-through-the-geometry approach is not working.
I'm just trying to create a perfect "quarter" circle sector in draw.io.
I read through https://desk.draw.io/support/solutions/articles/16000052874-how-to-create-and-edit-shapes-
But it is very vague.
Here is my best attempt, but it isn't right, the edges are to hard:
<shape name="Quarter Circle" h="49" w="49" aspect="variable" strokewidth="inherit">
<connections>
<constraint x="0.5" y="0" perimeter="0" name="N"/>
<constraint x="0.5" y="1" perimeter="0" name="S"/>
</connections>
<background>
<path>
<move x="49" y="0"/>
<line x="0" y="0"/>
<line x="0" y="49"/>
<arc rx="90" ry="90" x-axis-rotation="0" large-arc-flag="0" sweep-flag="0" x="49" y="0"/>
<close/>
</path>
</background>
<foreground>
<fillstroke/>
</foreground>
</shape>
I really want a quarter circle that if you rotate four of them, you end up with a perfect circle.
The arc radii (rx and ry) have to match the shape size to achieve what you want. Just replace your arc line with this:
<arc rx="49" ry="49" x-axis-rotation="0" large-arc-flag="0" sweep-flag="0" x="49" y="0"/>

svg animate does not work without indefinate

I am trying to add animation onto rect svg in React, using animate. The current code works with the repeatCount="indefinate", but I only want to animate when the rect is first rendered. Is there a way to do that?
I have tried removing repeatCount, but that does not work.
Thanks
<rect
width={20}
height={40}
x={props.xScale(dataWithOffset.whiskers[0])}
y={0}
fill={'blue}
opacity={0.4}
>
<animate
attributeName="opacity"
from="0"
to="1"
dur="2s"
begin="0s"
fill="freeze"
repeatCount="indefinite"
/>
</rect>

How can I redraw my degrafa background when scrolling?

I am using a canvas which has a degrafa background, so far so good.
However, when scrolling, the background (degrafa grid) does not get redrawn.
In the code the background strokes are linked to the container height. The container height does not change even when scrolling.
How do I get the height of the whole area so I can set the new height to my degrafa background?
It looks like this:
<mx:Canvas id="blackBoard"
width="100%"
height="100%"
x="0"
y="0"
backgroundColor="#444444"
clipContent="true">
<!-- Degrafa Surface -->
<degrafa:Surface id="boardSurfaceContainer">
<degrafa:strokes>
<degrafa:SolidStroke id="whiteStroke"
color="#EEE"
weight="1"
alpha=".2"/>
</degrafa:strokes>
<!-- Grid drawing -->
<degrafa:GeometryGroup id="grid">
<degrafa:VerticalLineRepeater count="{blackBoard.width / ApplicationFacade.settings.GRID_SIZE}"
stroke="{whiteStroke}"
x="0"
y="0"
y1="{blackBoard.height}"
offsetX="0"
offsetY="0"
moveOffsetX="{ApplicationFacade.settings.GRID_SIZE}"
moveOffsetY="0"/>
<degrafa:HorizontalLineRepeater count="{blackBoard.height / ApplicationFacade.settings.GRID_SIZE}"
stroke="{whiteStroke}"
x="0"
y="0"
x1="{blackBoard.width}"
offsetX="0"
offsetY="0"
moveOffsetX="0"
moveOffsetY="{ApplicationFacade.settings.GRID_SIZE}"/>
</degrafa:GeometryGroup>
</degrafa:Surface>
I just had to use the scroll position in the degrafa property binding
<degrafa:VerticalLineRepeater count="{(blackBoard.width + blackBoard.horizontalScrollPosition)/ ApplicationFacade.settings.GRID_SIZE}"
stroke="{whiteStroke}"
x="0"
y="0"
y1="{blackBoard.height + blackBoard.verticalScrollPosition}"
offsetX="0"
offsetY="0"
moveOffsetX="{ApplicationFacade.settings.GRID_SIZE}"
moveOffsetY="0"/>

Resources