Skip to content

Commit c0d5889

Browse files
committed
2 parents f519aa4 + 63ae4e6 commit c0d5889

File tree

4 files changed

+219
-24
lines changed

4 files changed

+219
-24
lines changed

README.md

Lines changed: 188 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,191 @@
22

33
[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/kunduin/react-picture-annotation/blob/master/LICENSE) [![Travis (.com)](https://img.shields.io/travis/com/kunduin/react-picture-annotation.svg)](https://travis-ci.com/Kunduin/react-picture-annotation) [![npm](https://img.shields.io/npm/v/react-picture-annotation.svg)](https://www.npmjs.com/package/react-picture-annotation) [![Greenkeeper badge](https://badges.greenkeeper.io/Kunduin/react-picture-annotation.svg)](https://greenkeeper.io/)
44

5-
**This project is under active development**
5+
A simple annotation component.
6+
7+
![rect](./doc/rect.gif)
8+
9+
## Install
10+
11+
```Bash
12+
# npm
13+
npm install react-picture-annotation
14+
15+
# yarn
16+
yarn add react-picture-annotation
17+
```
18+
19+
## Basic Example
20+
21+
[![Edit react-picture-annotation-example](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/react-picture-annotation-example-cw49e?fontsize=14)
22+
23+
```jsx
24+
const App = () => {
25+
const [pageSize, setPageSize] = useState({
26+
width: window.innerWidth,
27+
height: window.innerHeight
28+
});
29+
const onResize = () => {
30+
setPageSize({ width: window.innerWidth, height: window.innerHeight });
31+
};
32+
33+
useEffect(() => {
34+
window.addEventListener('resize', onResize);
35+
return () => window.removeEventListener('resize', onResize);
36+
}, []);
37+
38+
const onSelect = selectedId => console.log(selectedId);
39+
const onChange = data => console.log(data);
40+
41+
return (
42+
<div className="App">
43+
<ReactPictureAnnotation
44+
image="https://source.unsplash.com/random/800x600"
45+
onSelect={onSelect}
46+
onChange={onChange}
47+
width={pageSize.width}
48+
height={pageSize.height}
49+
/>
50+
</div>
51+
);
52+
};
53+
54+
const rootElement = document.getElementById('root');
55+
ReactDOM.render(<App />, rootElement);
56+
```
57+
58+
## Props
59+
60+
### AnnotationData `not required`
61+
62+
**TYPE**
63+
64+
```ts
65+
Array<IAnnotation>
66+
```
67+
68+
see [IAnnotation](#iannotation)
69+
70+
**COMMENT**
71+
72+
Control the marked areas on the page.
73+
74+
### selectedId `not required`
75+
76+
**TYPE**
77+
78+
```ts
79+
string | null;
80+
```
81+
82+
**COMMENT**
83+
84+
Control the selected shape.
85+
86+
### onChange `required`
87+
88+
**TYPE**
89+
90+
```ts
91+
(annotationData: IAnnotation[]) => void
92+
```
93+
94+
**COMMENT**
95+
96+
Called every time the shape changes.
97+
98+
### onSelected `required`
99+
100+
**TYPE**
101+
102+
```ts
103+
(id: string | null) => void
104+
```
105+
106+
**COMMENT**
107+
108+
Called each time the selection is changed.
109+
110+
### width `required`
111+
112+
**TYPE**
113+
114+
```ts
115+
number;
116+
```
117+
118+
**COMMENT**
119+
120+
Width of the canvas.
121+
122+
### height `required`
123+
124+
**TYPE**
125+
126+
```ts
127+
number;
128+
```
129+
130+
**COMMENT**
131+
132+
Height of the canvas.
133+
134+
### image `required`
135+
136+
**TYPE**
137+
138+
```ts
139+
string;
140+
```
141+
142+
**COMMENT**
143+
144+
Image to be annotated.
145+
146+
### inputElement `not required`
147+
148+
**TYPE**
149+
150+
```ts
151+
(value: string, onChange: (value: string) => void, onDelete: () => void) =>
152+
React.ReactElement;
153+
```
154+
155+
**COMMENT**
156+
157+
Customizable input control.
158+
159+
**EXAMPLE**
160+
161+
```jsx
162+
<ReactPictureAnnotation
163+
{...props}
164+
inputElement={inputProps => <MyInput {...inputProps} />}
165+
/>
166+
```
167+
168+
## Types
169+
170+
### IAnnotation
171+
172+
```js
173+
{
174+
id:"to identify this shape", // required,
175+
comment:"string type comment", // not required
176+
mark:{
177+
type:"RECT", // now only support rect
178+
179+
// The number of pixels in the upper left corner of the image
180+
x:0,
181+
y:0,
182+
183+
// The size of tag
184+
width:0,
185+
height:0
186+
}
187+
}
188+
```
189+
190+
## Licence
191+
192+
[MIT License](https://github.com/kunduin/react-picture-annotation/blob/master/LICENSE)

doc/rect.gif

833 KB
Loading

src/ReactPictureAnnotation.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ import { DefaultAnnotationState } from "./annotation/DefaultAnnotationState";
55
import DefaultInputSection from "./DefaultInputSection";
66
// import DeleteButton from "./DeleteButton";
77
import { IShape, IShapeBase, RectShape, shapeStyle } from "./Shape";
8-
import { ITransformer } from "./Transformer";
8+
import Transformer, { ITransformer } from "./Transformer";
99

1010
interface IReactPictureAnnotationProps {
1111
annotationData?: IAnnotation[];
12-
selectedId?: string;
12+
selectedId?: string | null;
1313
onChange: (annotationData: IAnnotation[]) => void;
1414
onSelect: (id: string | null) => void;
1515
width: number;
@@ -191,14 +191,17 @@ export default class ReactPictureAnnotation extends React.Component<
191191

192192
for (const item of this.shapes) {
193193
const isSelected = item.getAnnotationData().id === this.selectedId;
194-
195194
const { x, y, height } = item.paint(
196195
this.canvas2D,
197196
this.calculateShapePosition,
198197
isSelected
199198
);
200199

201-
if (isSelected && this.currentTransformer) {
200+
if (isSelected) {
201+
if (!this.currentTransformer) {
202+
this.currentTransformer = new Transformer(item);
203+
}
204+
202205
hasSelectedItem = true;
203206

204207
this.currentTransformer.paint(

stories/index.stories.tsx

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,46 +14,51 @@ storiesOf("Hello World", module)
1414
.add("with text", () => {
1515
const AnnotationComponent = () => {
1616
const [size, setSize] = useState({
17-
width: window.innerWidth,
18-
height: window.innerHeight
17+
width: window.innerWidth - 16,
18+
height: window.innerHeight - 16
1919
});
2020

21-
const [image, setImage] = useState(
22-
"https://bequank.oss-cn-beijing.aliyuncs.com/landpage/large/60682895_p0_master1200.jpg"
23-
);
24-
2521
const [annotationData, setAnnotationData] = useState<
2622
Array<IAnnotation<IShapeData>>
27-
>([]);
23+
>([
24+
{
25+
id: "a",
26+
comment: "HA HA HA",
27+
mark: {
28+
type: "RECT",
29+
width: 161,
30+
height: 165,
31+
x: 229,
32+
y: 92
33+
}
34+
}
35+
]);
36+
37+
const [selectedId, setSelectedId] = useState<string | null>("a");
2838

2939
const onResize = () => {
30-
setSize({ width: window.innerWidth, height: window.innerHeight });
40+
setSize({
41+
width: window.innerWidth - 16,
42+
height: window.innerHeight - 16
43+
});
3144
};
3245

3346
useEffect(() => {
3447
window.addEventListener("resize", onResize);
35-
setTimeout(() => {
36-
setImage(
37-
"https://bequank.oss-cn-beijing.aliyuncs.com/landpage/large/20180904.jpg"
38-
);
39-
setAnnotationData([]);
40-
}, 2000);
4148
return () => {
4249
window.removeEventListener("resize", onResize);
4350
};
4451
}, []);
4552

4653
return (
47-
// tslint:disable-next-line: jsx-no-lambda
4854
<ReactPictureAnnotation
4955
width={size.width}
5056
height={size.height}
5157
annotationData={annotationData}
52-
// tslint:disable-next-line: jsx-no-lambda
5358
onChange={data => setAnnotationData(data)}
54-
// tslint:disable-next-line: jsx-no-lambda
55-
onSelect={() => null}
56-
image={image}
59+
selectedId={selectedId}
60+
onSelect={e => setSelectedId(e)}
61+
image="https://bequank.oss-cn-beijing.aliyuncs.com/landpage/large/60682895_p0_master1200.jpg"
5762
/>
5863
);
5964
};

0 commit comments

Comments
 (0)