How to set the initial dimensions of a transformer when applying it on a group having clip properties in Konva? - konvajs

I have a Konva.Group with a few nodes to which I have set clip properties to limit what is seen. I'm applying Konva.Transformer to the group and the problem I'm facing is that the Transformer encloses the entire group, even the unclipped portion. This is not looking good even if it is fully functional and does the job. Is there any way to set the initial width and height of the transformer so that it only encloses the clipped portion?
This is the group before applying clip and transform
This is what it looks like after applying clip and transform
import React, {useRef, useEffect} from 'react';
import { render } from 'react-dom';
import { Stage, Layer, Rect, Circle, Line, Group, Transformer } from 'react-konva';
const App = () => {
const trRef = useRef(null)
const grpRef = useRef(null)
useEffect(()=>{
const transformNode = trRef.current;
transformNode.enabledAnchors(["top-left",
"top-right",
"bottom-left",
"bottom-right"])
transformNode.nodes([grpRef.current])
},[trRef])
return (
<Stage width={window.innerWidth} height={window.innerHeight}>
<Layer>
<Group ref={grpRef} clipX={0} clipY={0} clipWidth={200} clipHeight={200}>
<Rect
x={20}
y={50}
width={100}
height={100}
fill="red"
shadowBlur={10}
/>
<Circle x={200} y={100} radius={50} fill="green" />
<Line
x={20}
y={200}
points={[0, 0, 100, 0, 100, 100]}
tension={0.5}
closed
stroke="black"
fillLinearGradientStartPoint={{ x: -50, y: -50 }}
fillLinearGradientEndPoint={{ x: 50, y: 50 }}
fillLinearGradientColorStops={[0, 'red', 1, 'yellow']}
/>
</Group>
<Transformer rotateEnabled={false} ref={trRef} />
</Layer>
</Stage>
);
};
render(<App />, document.getElementById('root'));

This is the default behavior of Konva.Transform and it can't be altered. One workaround would be to create a transparent rectangle around the clipped portion, apply the transform to it and then copy the changes to the group.
import React, {useRef, useEffect} from 'react';
import { render } from 'react-dom';
import { Stage, Layer, Rect, Circle, Line, Group, Transformer } from 'react-konva';
const App = () => {
const trRef = useRef(null)
const grpRef = useRef(null)
const rectRef = useRef(null)
// To copy the transform matrix from the rectangle to the group
function handleTransform(e){
const shape1 = e.target;
const transform = shape1.getTransform().copy();
const attrs = transform.decompose();
grpRef.current.setAttrs(attrs);
}
useEffect(()=>{
const transformNode = trRef.current;
transformNode.enabledAnchors(["top-left",
"top-right",
"bottom-left",
"bottom-right"])
transformNode.nodes([rectRef.current])
},[trRef])
return (
<Stage width={window.innerWidth} height={window.innerHeight}>
<Layer>
<Group draggable>
{/* Transparent rectangle to which the transform is now applied to */}
<Rect
ref={rectRef}
x={0}
y={0}
width={200}
height={200}
id="invisible-rect"
/>
<Group ref={grpRef} clipX={0} clipY={0} clipWidth={200} clipHeight={200}>
<Rect
x={20}
y={50}
width={100}
height={100}
fill="red"
shadowBlur={10}
/>
<Circle x={200} y={100} radius={50} fill="green" />
<Line
x={20}
y={200}
points={[0, 0, 100, 0, 100, 100]}
tension={0.5}
closed
stroke="black"
fillLinearGradientStartPoint={{ x: -50, y: -50 }}
fillLinearGradientEndPoint={{ x: 50, y: 50 }}
fillLinearGradientColorStops={[0, 'red', 1, 'yellow']}
/>
</Group>
</Group>
<Transformer onTransform={handleTransform} rotateEnabled={false} ref={trRef} />
</Layer>
</Stage>
);
};
render(<App />, document.getElementById('root'));
Here's the demo of the above code. Thanks to Anton, the creator of this wonderful library for suggesting this solution.
Reference - Konva shape transform sharing is simple

Related

Image is not fitting to full screen in iOS

I am using react-native-image-viewer to render images and code looks like this
<View style={{flex: 1}}>
<ImageViewer
imageUrls={images}
renderImage={() => <Image style={{height: '100%', width: '100%'}} resizeMode={'contain'} source={{uri:`${axios.defaults.baseURL}${url}`, headers}} />}
enableImageZoom={true}
backgroundColor={TEXT_HEADERBLACK}
maxOverflow={0}
renderIndicator={() => <></>}
/>
</View>
Result of this code is as follows in iOS
As you can see, Image is not fitting to entire screen. But in android it's occupying entire screen.
Do I have do any styling changes to make it occupy entire screen?
Ran the following code as a snack and it seemed fine on my device. Though I removed the renderImage prop.
import * as React from 'react';
import { Text, View, StyleSheet, Image } from 'react-native';
import ImageViewer from 'react-native-image-zoom-viewer';
const images = [{
// Simplest usage.
url: 'https://avatars2.githubusercontent.com/u/7970947?v=3&s=460',
// width: number
// height: number
// Optional, if you know the image size, you can set the optimization performance
// You can pass props to <Image />.
props: {
// headers: ...
}
}, {
url: 'https://avatars2.githubusercontent.com/u/7970947?v=3&s=460',
}]
// test
export default function App() {
return (
<View style={{flex: 1}}>
<ImageViewer
imageUrls={images}
enableImageZoom={true}
backgroundColor={'#ffffff'}
maxOverflow={0}
renderIndicator={() => <></>}
/>
</View>
);
}
const styles = StyleSheet.create({
});
Can you possibly link the image you are using above?
My understanding of <Image> on iOS requires absolute values instead of relative values. You can also add a conditional rendering based on the platform your app is running on.
const {
width: deviceWidth,
height: deviceHeight,
} = Dimensions.get('window');
<Image
style={{
height: Platform.OS === 'ios' ? deviceHeight * 0.9 : '100%',
width: Platform.OS === 'ios' ? deviceWidth * 0.9 : '100%'
}}
resizeMode={'contain'}
source={{uri:`${axios.defaults.baseURL}${url}`, headers}}
/>

Adding Padding with React Konva

I want to create the equivalent of this (sudo code):
<div padding="4px">
<p>My Text</p>
</div>
With React Konva elements. I know how to start, using Group, Rect, and Text, but I can't figure out how to do the padding. Hope someone can help! Thanks!!
EDIT:
This is what I am trying to build (the green background with 2px padding around the text).
You have padding property for text shape to use it.
In order to wrap a rectangle around a text shape, you have to calculate the size of the text.
const App = () => {
const textRef = React.useRef();
const [size, setSize] = React.useState({ x: 0, y: 0 });
React.useEffect(() => {
setSize({
width: textRef.current.width(),
height: textRef.current.height()
});
}, []);
return (
<Stage width={window.innerWidth} height={window.innerHeight}>
<Layer>
<Group x={20} y={50}>
<Rect
width={size.width}
height={size.height}
fill="red"
shadowBlur={10}
/>
<Text
text="Some text on canvas"
ref={textRef}
fontSize={15}
padding={10}
/>
</Group>
</Layer>
</Stage>
);
};
https://codesandbox.io/s/react-konva-text-with-padding-yh876

How to implement Eraser tool in Konva js without using destination-out and drawing with white color we need like object erasing?

enter code hereCan we implement Object Erasing in konva js for eraser tool. Actually i need to erase free drawing line if we erase with destination-out im facing issues while moving or dragging lines from one place to another. To overcome this issue can anyone suggest me how to implement Object eraser in konva js or group the free drawn line and eraser lines.
import React from "react"
import Konva from "konva";
import {Layer, Line, Stage} from "react-konva";
import {CirclePicker} from "react-color";
const Pencil =() => {
const [tool, setTool] = React.useState("pen");
const [isDrawing, toggleDrawing] = React.useState(false);
const [lines, setLines] = React.useState([]);
const[size,setSize] = React.useState(3);
const[color,setColor]= React.useState("red");
return(
<div>
<CirclePicker value={color} color={color} onChange = {e=>{
setColor(e.hex);
}}/>
<select
value={tool}
onChange={e => {
setTool(e.target.value);
}}
>
<option value="pen">Pen</option>
<option value="eraser">Eraser</option>
</select>
<input value={size} onChange={e =>{
setSize(parseInt(e.target.value))
console.log(setSize)
}} type='range' step='3' min='3' max='16'/>
<Stage
width={window.innerWidth}
height={window.innerHeight}
onMouseDown={e => {
toggleDrawing(true);
const pointer = e.target.getStage().getPointerPosition();
const newLines = lines.concat({
id: Date.now(),
tool: tool,
points: [pointer.x, pointer.y]
});
setLines(newLines);
}}
onMouseMove={e => {
if (!isDrawing) {
return;
}
const pointer = e.target.getStage().getPointerPosition();
const newLines = lines.slice();
const lastLine = {
...newLines[newLines.length - 1]
};
lastLine.size=size;
lastLine.color=color;
lastLine.points = lastLine.points.concat([pointer.x, pointer.y]);
newLines[newLines.length - 1] = lastLine;
setLines(newLines);
}}
onMouseUp={e => {
toggleDrawing(false);
}}>
<Layer
>
{lines.map(line => (
<Line
draggable={true}
x={window.length}
y={window.length}
width={window.length}
height={window.length}
onDragEnd={e => {
e.target.to({
// duration: 0.5,
// easing: Konva.Easings.ElasticEaseOut,
// scaleX: 1,
// scaleY: 1,
shadowOffsetX: 0,
shadowOffsetY: 0
});
}}
key={line.id}
strokeWidth={line.size}
stroke={line.color}
points={line.points}
globalCompositeOperation={
line.tool === "eraser" ? "destination-out" : "source-over"
}
/>
))}
</Layer>
</Stage>
</div>)
};
export default Pencil;

How can I get image properties like scaleX in react-kova?

Like the example shown for shape, this has x,y,rotation etc. https://konvajs.github.io/docs/select_and_transform/Transform_Events.html
Image in react-konva also extends shape.
How can I get these values for image(with transformer) in react-konva.
You can listen transform event and then read all properties from the image node.
handleTransform = () => {
const props = {
x: this.image.x(),
y: this.image.y(),
rotatio: this.image.rotation(),
width: this.image.width(),
height: this.image.height(),
scaleX: this.image.scaleX(),
scaleY: this.image.scaleY()
};
console.log(props);
};
render() {
return (
<Stage width={window.innerWidth} height={window.innerHeight}>
<Layer>
<Image
image={this.state.image}
ref={node => {
this.image = node;
}}
draggable
onTransform={this.handleTransform}
onDragMove={this.handleTransform}
/>
<Transformer
ref={node => {
this.transformer = node;
}}
/>
</Layer>
</Stage>
);
}
Demo: https://codesandbox.io/s/wq184owy45

Konva - react drag drop without moving drag element

Similar to having drag and drop without moving the element in Konva, is there a way to do the same with Konva-react?
Tried doing this. But, could not find a way to add cloned shape back to the Layer.
Thank you and any help would be appreciated.
Update:
Code used:
Trying to clone "Shp1" in the below code
class Shp1 extends Component {
state = {
x1: 0,
y1: 0
};
handleDragStart = e => {
e.target.setAttrs({
scaleX: 1.1,
scaleY: 1.1
});
//clone code goes here
};
handleDragEnd = e => {
this.setState({
x1: e.target.x(),
y1: e.target.y()
});
};
render() {
return(
<Group x={this.state.x1} y={this.state.y1}
id="shp1" draggable onDragStart={this.handleDragStart} onDragEnd={this.handleDragEnd}
onMouseOver={this.handleMouseOver} onMouseOut={this.handleMouseOut}>
<RegularPolygon x={110} y={160} sides={3} radius={80}
fill="black" rotation={90}/>
<Circle x={190} y={160} radius={30}
fill="black"/>
</Group>
);
}
}
class App extends Component {
render() {
return (
<Stage id='stg1' width={window.innerWidth} height={window.innerHeight} draggable={false}>
<Layer id="menu" className="layer" x={0} y={0} width={200} height={window.innerHeight} visible={true}>
<Shp1/>
</Layer>
</Stage>
);
}
}

Resources