What do I want to do:
I want to load base64 of a PNG/JPG image file, or blob stored in a db into React Konva Image.
Conditions and observations:
Things I did:
I used example from https://konvajs.org/docs/react/Images.html
Putting in the line: this.imageNode.getLayer().batchDraw();
should have done some change.
Code:
import React, { useEffect, useRef, useState, useCallback, Fragment } from "react";
import { Image as KonvaImage } from "react-konva" // , Transformer
const KImage = ({ properties, isSelected, onSelect, onChange }) => {
// const shapeRef = useRef();
// const trRef = useRef();
const imageNode = useRef()
const [image, setImage] = useState(null)
const [runOnce, setRunOnce] = useState(false)
const handleLoadImage = useCallback(() => {
// {
// // const { x, y, image } = properties
// /* console.log("Image", `loadImage: properties {x, y, image.length}:
// ${x}, ${y}, ${image.length}`) */
// }
let image = new window.Image();
image.src = properties.image;
image.addEventListener('load', () => {
// console.log("Image", `handleLoad image (loaded): ${image}`)
setImage(image)
setRunOnce(false)
if (imageNode && imageNode.current && imageNode.current.getLayer()) {
// console.log("Image", `batchDraw: ${image}`)
imageNode.current.getLayer().batchDraw();
}
});
}, [properties])
useEffect(() => {
// console.log("Image", `useEffect image: ${image}`)
// console.log("Image", `useEffect: properties.image.length: ${properties.image.length})`)
if (isSelected) {
// trRef.current.nodes([shapeRef.current]);
// trRef.current.getLayer().batchDraw();
}
if (runOnce) {
return
}
if (!image) {
handleLoadImage()
setRunOnce(true)
}
}, [isSelected, image, runOnce, handleLoadImage, properties]);
return (
<Fragment>
<KonvaImage
ref={imageNode}
onClick={onSelect}
onTap={onSelect}
x={properties.x}
y={properties.y}
image={image}
draggable
onDragEnd={e => {
/* onChange({
...points,
x: e.target.x(),
y: e.target.y(),
});*/
}}
onTransformEnd={e => {
// transformer is changing scale
/* const node = shapeRef.current;
const scaleX = node.scaleX();
const scaleY = node.scaleY();
node.scaleX(1);
node.scaleY(1);
onChange({
...points,
x: node.x(),
y: node.y(),
width: node.width() * scaleX,
height: node.height() * scaleY,
});*/
}}
/>
{/* {isSelected && <Transformer ref={trRef} />} */}
</Fragment>
)
}
export default KImage
I took the advantage of the useState hook to reset the image to its initial stage. It is working now on both the image path and base64 data.
In order to reset the image I certainly had to write the code to see whether there is a need to change the image by comparing the data length and the image name.
Now if I were having more than one image, I would have used the map of image metadata and would have manipulated it to destroy and recreate Konva Image components externally based on the map list.
code:
import React, { useEffect, useRef, useState, useCallback, Fragment } from "react";
import { Image as KonvaImage } from "react-konva" // , Transformer
const KImage = ({ properties, isSelected, onSelect, onChange }) => {
// const shapeRef = useRef();
// const trRef = useRef();
const imageNode = useRef()
const [image, setImage] = useState(null)
const [runOnce, setRunOnce] = useState(false)
const [storedProperty, setStoredProperty] = useState(null)
const checkImageChanged = useCallback(() => {
const { image, name } = properties
let changedName = name
let changedLength = image.length
let storedName, storedLength
if (storedProperty) {
let { image, name } = storedProperty
storedName = name
storedLength = image.length
}
if (storedName !== changedName || storedLength !== changedLength) {
/* console.log("Image", `checkImageChanged: properties { storedLength, changedLength}:
${storedLength}, ${changedLength}`) */
// console.log("Image", `checkImageChanged: storedName ${storedName}: changedName: ${changedName}}`)
// console.log("Image", `checkImageChanged: properties { name, image }: ${name}\n${image}`)
/* if (imageNode && imageNode.current && imageNode.current.getLayer()) {
console.log("Image", `checkImageChanged batchDraw`)
imageNode.current.getLayer().batchDraw();
} */
console.log("Image", `checkImageChanged setRunOnce(false), setImage(null)`)
setRunOnce(false)
setImage(null)
}
}, [properties, storedProperty])
//
const handleLoadImage = useCallback(() => {
let image1 = new window.Image();
image1.src = properties.image;
image1.addEventListener('load', () => {
setImage(image1)
// setRunOnce(false)
console.log("Image", `handleLoadImage loaded: ${image1}`)
if (imageNode && imageNode.current && imageNode.current.getLayer()) {
console.log("Image", `handleLoadImage batchDraw`)
imageNode.current.getLayer().batchDraw();
}
});
}, [properties])
useEffect(() => {
console.log("Image", `useEffect runOnce: ${runOnce}`)
console.log("Image", `useEffect image: ${image}`)
if (isSelected) {
// trRef.current.nodes([shapeRef.current]);
// trRef.current.getLayer().batchDraw();
}
setStoredProperty(properties)
if (!image) {
if (runOnce) {
return
}
handleLoadImage()
setRunOnce(true)
}
checkImageChanged()
}, [isSelected, image, runOnce, checkImageChanged, handleLoadImage, properties]);
// render
return (
<Fragment>
<KonvaImage
ref={imageNode}
onClick={onSelect}
onTap={onSelect}
name={properties.name}
x={properties.x}
y={properties.y}
image={image}
// draggable
// onDragEnd={e => {
// /* onChange({
// ...points,
// x: e.target.x(),
// y: e.target.y(),
// });*/
// }}
// onTransformEnd={e => {
// // transformer is changing scale
// /* const node = shapeRef.current;
// const scaleX = node.scaleX();
// const scaleY = node.scaleY();
// node.scaleX(1);
// node.scaleY(1);
// onChange({
// ...points,
// x: node.x(),
// y: node.y(),
// width: node.width() * scaleX,
// height: node.height() * scaleY,
// });*/
// }}
/>
{/* {isSelected && <Transformer ref={trRef} />} */}
</Fragment>
)
}
export default KImage