npm install --save prm-react-image-viewer
app.tsx
import React, { useRef } from 'react'
import { AnnotationModel, ImageViewer, ImageViewerOption } from 'prm-react-image-viewer'
import 'prm-react-image-viewer/dist/index.css';
import SimplePopup from './popup';
import ReactDOM from 'react-dom';
import './App.css';
const App = () => {
const [enableDrawing, setEnableDrawing] = React.useState(false);
const annotations: AnnotationModel[] = [
{
"pageNumber": 6,
"id": "1",
"quads": [
{
"left": 243,
"right": 647,
"top": 32,
"bottom": 76
}
],
"pageResolution": {
"width": 918,
"height": 1225
}
},
{
"pageNumber": 6,
"id": "2",
"quads": [
{
"left": 656,
"right": 829,
"top": 31,
"bottom": 76
}
],
"pageResolution": {
"width": 918,
"height": 1225
},
"color": { r: 95, g: 0, b: 255 }
},
{
"pageNumber": 6,
"id": "3",
"quads": [
{
"left": 244,
"right": 815,
"top": 452,
"bottom": 606
}
],
"pageResolution": {
"width": 918,
"height": 1225
}
},
{
"pageNumber": 6,
"id": "4",
"quads": [
{
"left": 68,
"right": 166,
"top": 279,
"bottom": 298
}
],
"pageResolution": {
"width": 918,
"height": 1225
},
"color": { r: 255, g: 0, b: 255 }
},
{
"pageNumber": 7,
"id": "5",
"quads": [
{
"left": 68.99346405228758,
"right": 659.0065359477123,
"top": 59.01674369747898,
"bottom": 146.97724789915966
}
],
"pageResolution": {
"width": 728,
"height": 943
},
"color": { r: 22, g: 21, b: 70 }
},
{
"pageNumber": 7,
"id": "6",
"quads": [
{
"left": 306.1089324618736,
"right": 427.44226579520694,
"top": 154.1091806722689,
"bottom": 385.5007773109244
}
],
"pageResolution": {
"width": 728,
"height": 943
},
"color": { r: 39, g: 112, b: 222 }
}
]
const options: ImageViewerOption = {
pages: [
{
"id": "page0",
"url": "./1.webp",
"pageno": 6,
"resolution": [
918,
1225
],
},
{
"id": "page1",
"url": "./2.webp",
"pageno": 7,
"resolution": [
728,
943
]
}
],
// apiToken: 'JWT TOKEN IF REQUIRED'
}
const childRef = useRef<ImageViewer>(null);
const annotate = () => {
if (childRef.current) {
annotations.forEach(annotation => {
childRef?.current?.annotate(annotation, { r: 255, g: 0, b: 0 }, true, false);
})
}
}
const zoomIn = () => {
if (childRef.current) {
childRef.current.zoom('in');
}
}
const zoomOut = () => {
if (childRef.current) {
childRef.current.zoom('out');
}
}
const onDocLoad = () => {
if (childRef.current) {
annotations.forEach(annotation => {
childRef.current?.annotate(annotation, { r: 255, g: 0, b: 0 }, true, true);
})
}
}
const removePopup = () => {
childRef?.current?.removeAllAnnotationPopup();
}
const generateBasicContent = (text?: string): HTMLDivElement => {
const container = document.createElement('div');
let popupProps = { text: text || "Hi there.", removePopup: removePopup }
ReactDOM.render(SimplePopup(popupProps), container);
return container;
}
const onAnnotationClick = (data: any) => {
console.log(data);
let target = childRef.current?.getAnnotationDivById(`${childRef.current?.annotationIdPrefix}${data.id}`);
if (!target) return;
childRef.current?.showPopupContent(target as HTMLElement, generateBasicContent(), target.parentElement as HTMLElement);
}
const toggleDrawingMode = () => {
setEnableDrawing(!enableDrawing);
if (childRef.current) {
childRef.current.togglePen();
}
}
const onDrawingAdd = (data: any) => {
console.log("DRAWING DATA ", data);
let annotation = { ...data.annotation, id: Date.now().toString().replace(".", "-"), color: { r: 0, g: 255, b: 0 } };
annotations.push(annotation);
childRef.current?.removeRect(data.rectangle)
childRef.current?.annotate(annotation, { r: 255, g: 0, b: 0 }, true, false);
}
return (
<div>
<div className='toolbar'>
<button onClick={annotate}>Annotate</button>
<button onClick={zoomIn}>Zoom In</button>
<button onClick={zoomOut}>Zoom Out</button>
<button onClick={toggleDrawingMode}>{enableDrawing ? 'Disable Drawing Mode' : 'Enable Drawing Mode'}</button>
</div>
<div className='image-viewer-container'><ImageViewer ref={childRef} options={options} onDocLoad={onDocLoad} onAnnotationClicked={onAnnotationClick} onDrawingAdd={onDrawingAdd} /></div>
</div>
)
}
export default App
app.css
.annotationPopup {
border-radius: 5px;
border: 2px solid #cecece;
padding: 20px;
width: 20vw;
position: absolute;
background-color: white;
}
.toolbar {
display: flex;
justify-content: flex-end;
gap: 10px;
padding: 10px;
background-color: #f1f1f1;
border-bottom: 1px solid #cecece;
}
.image-viewer-container {
height: calc(100vh - 42px);
width: 100%;
}
popup.tsx
import React from 'react'
interface SimplePopupProps {
text: string,
removePopup: () => void
}
const SimplePopup = (props: SimplePopupProps) => {
const removePopup = () => {
props.removePopup();
}
const closeBtnStyle: React.CSSProperties = {
'position': 'absolute',
'right': '0',
'top': '5px',
'cursor': 'pointer'
}
const textStyle: React.CSSProperties = {
'color': 'black',
'fontSize': '10px',
'padding': '5px',
}
return (
<div>
<div style={closeBtnStyle}>
<span onClick={removePopup}>
<svg height="15" fill="black" viewBox="0 0 48 48" width="32" xmlns="http://www.w3.org/2000/svg"><path d="M38 12.83l-2.83-2.83-11.17 11.17-11.17-11.17-2.83 2.83 11.17 11.17-11.17 11.17 2.83 2.83 11.17-11.17 11.17 11.17 2.83-2.83-11.17-11.17z" /><path d="M0 0h48v48h-48z" fill="none" />
</svg>
</span>
</div>
<div style={textStyle}>
{props.text}
</div>
</div>
)
}
export default SimplePopup