react-scratchingcard 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -35,7 +35,7 @@ yarn add react-scratchingcard
35
35
 
36
36
  ```tsx
37
37
  import React, { useRef } from 'react';
38
- import ScratchCard from 'react-scratchcard-v2';
38
+ import ScratchCard from 'react-scratchingcard';
39
39
 
40
40
  import * as IMG from './img.jpg';
41
41
 
@@ -100,7 +100,7 @@ const App = () => {
100
100
  or you can use CUSTOM_BRUSH_PRESET object
101
101
 
102
102
  ```tsx
103
- import { CUSTOM_BRUSH_PRESET } from 'react-scratchcard-v2';
103
+ import { CUSTOM_BRUSH_PRESET } from 'react-scratchingcard';
104
104
 
105
105
  const App = () => {
106
106
  return (
@@ -128,7 +128,8 @@ const App = () => {
128
128
  |-------------------|-----------------|-------------|
129
129
  | width | number | |
130
130
  | height | number | |
131
- | image | File or Base64 | |
131
+ | image | File \| string \| {src?: string, default?: string} | |
132
+ | imageCrossOrigin | ?'' \| 'anonymous' \| 'use-credentials' | |
132
133
  | finishPercent | ?number | 70 |
133
134
  | brushSize | ?number | 20 |
134
135
  | fadeOutOnComplete | ?boolean | true |
@@ -136,6 +137,23 @@ const App = () => {
136
137
  | customBrush | ?CustomBrush | |
137
138
  | customCheckZone | ?CustomCheckZone| |
138
139
 
140
+ ### Remote URL
141
+
142
+ ```tsx
143
+ <ScratchCard
144
+ width={320}
145
+ height={226}
146
+ image='https://cdn.example.com/scratch-cover.jpg'
147
+ imageCrossOrigin='anonymous'
148
+ >
149
+ <h1>Scratch card</h1>
150
+ </ScratchCard>
151
+ ```
152
+
153
+ Note: pour calculer le pourcentage gratté (`finishPercent`) avec une image distante, le serveur distant doit autoriser CORS.
154
+
155
+ Le composant n'impose plus CORS par defaut pour une URL distante. `imageCrossOrigin` est utile seulement si ton serveur renvoie deja les en-tetes CORS voulus.
156
+
139
157
  ### CustomBrush
140
158
 
141
159
  | **name** | **type** |
package/dist/index.d.ts CHANGED
@@ -8,6 +8,10 @@ declare type CustomBrush = {
8
8
  width: number;
9
9
  height: number;
10
10
  };
11
+ declare type ImageSource = string | File | {
12
+ src?: string;
13
+ default?: string;
14
+ };
11
15
  declare type CustomCheckZone = {
12
16
  x: number;
13
17
  y: number;
@@ -17,7 +21,8 @@ declare type CustomCheckZone = {
17
21
  interface Props {
18
22
  width: number;
19
23
  height: number;
20
- image: any;
24
+ image: ImageSource;
25
+ imageCrossOrigin?: '' | 'anonymous' | 'use-credentials';
21
26
  finishPercent?: number;
22
27
  onComplete?: () => void;
23
28
  brushSize?: number;
@@ -37,11 +42,27 @@ declare class Scratch extends Component<Props, State> {
37
42
  canvas: HTMLCanvasElement;
38
43
  brushImage?: any;
39
44
  image: HTMLImageElement;
45
+ objectUrl: string | null;
46
+ scratchedCells: Set<string>;
47
+ scratchableCellCount: number;
40
48
  isFinished: boolean;
41
49
  constructor(props: Props);
50
+ componentDidUpdate(prevProps: Props): void;
51
+ componentWillUnmount(): void;
52
+ revokeObjectUrl(): void;
53
+ resolveImageSource(image: ImageSource): string;
54
+ loadImage(): void;
42
55
  componentDidMount(): void;
43
56
  reset: () => void;
44
- getFilledInPixels(stride: number): number;
57
+ getCheckZone(): {
58
+ x: number;
59
+ y: number;
60
+ width: number;
61
+ height: number;
62
+ };
63
+ initializeScratchGrid(): void;
64
+ markScratchArea(x: number, y: number, width: number, height: number): void;
65
+ getFilledPercentage(): number;
45
66
  getMouse(e: any, canvas: HTMLCanvasElement): {
46
67
  x: number;
47
68
  y: number;
package/dist/index.js CHANGED
@@ -19,6 +19,8 @@ function _setPrototypeOf(o, p) {
19
19
  return _setPrototypeOf(o, p);
20
20
  }
21
21
 
22
+ var SCRATCH_GRID_SIZE = 4;
23
+
22
24
  var Scratch = /*#__PURE__*/function (_Component) {
23
25
  _inheritsLoose(Scratch, _Component);
24
26
 
@@ -28,6 +30,9 @@ var Scratch = /*#__PURE__*/function (_Component) {
28
30
  _this = _Component.call(this, props) || this;
29
31
  _this.isDrawing = false;
30
32
  _this.lastPoint = null;
33
+ _this.objectUrl = null;
34
+ _this.scratchedCells = new Set();
35
+ _this.scratchableCellCount = 0;
31
36
  _this.isFinished = false;
32
37
 
33
38
  _this.reset = function () {
@@ -36,7 +41,13 @@ var Scratch = /*#__PURE__*/function (_Component) {
36
41
 
37
42
  _this.ctx.drawImage(_this.image, 0, 0, _this.props.width, _this.props.height);
38
43
 
44
+ _this.initializeScratchGrid();
45
+
39
46
  _this.isFinished = false;
47
+
48
+ _this.setState({
49
+ finished: false
50
+ });
40
51
  };
41
52
 
42
53
  _this.handleMouseDown = function (e) {
@@ -66,18 +77,24 @@ var Scratch = /*#__PURE__*/function (_Component) {
66
77
 
67
78
  if (_this.brushImage && _this.props.customBrush) {
68
79
  _this.ctx.drawImage(_this.brushImage, x, y, _this.props.customBrush.width, _this.props.customBrush.height);
80
+
81
+ _this.markScratchArea(x, y, _this.props.customBrush.width, _this.props.customBrush.height);
69
82
  } else {
70
83
  _this.ctx.beginPath();
71
84
 
72
85
  _this.ctx.arc(x, y, _this.props.brushSize || 20, 0, 2 * Math.PI, false);
73
86
 
74
87
  _this.ctx.fill();
88
+
89
+ var radius = _this.props.brushSize || 20;
90
+
91
+ _this.markScratchArea(x - radius, y - radius, radius * 2, radius * 2);
75
92
  }
76
93
  }
77
94
 
78
95
  _this.lastPoint = currentPoint;
79
96
 
80
- _this.handlePercentage(_this.getFilledInPixels(32));
97
+ _this.handlePercentage(_this.getFilledPercentage());
81
98
  };
82
99
 
83
100
  _this.handleMouseUp = function () {
@@ -93,24 +110,80 @@ var Scratch = /*#__PURE__*/function (_Component) {
93
110
 
94
111
  var _proto = Scratch.prototype;
95
112
 
96
- _proto.componentDidMount = function componentDidMount() {
113
+ _proto.componentDidUpdate = function componentDidUpdate(prevProps) {
114
+ if (prevProps.image !== this.props.image) {
115
+ this.loadImage();
116
+ }
117
+ };
118
+
119
+ _proto.componentWillUnmount = function componentWillUnmount() {
120
+ this.revokeObjectUrl();
121
+ };
122
+
123
+ _proto.revokeObjectUrl = function revokeObjectUrl() {
124
+ if (this.objectUrl) {
125
+ URL.revokeObjectURL(this.objectUrl);
126
+ this.objectUrl = null;
127
+ }
128
+ };
129
+
130
+ _proto.resolveImageSource = function resolveImageSource(image) {
131
+ this.revokeObjectUrl();
132
+
133
+ if (typeof image === 'string') {
134
+ return image;
135
+ }
136
+
137
+ if (image instanceof File) {
138
+ this.objectUrl = URL.createObjectURL(image);
139
+ return this.objectUrl;
140
+ }
141
+
142
+ if (image && typeof image.src === 'string') {
143
+ return image.src;
144
+ }
145
+
146
+ if (image && typeof image["default"] === 'string') {
147
+ return image["default"];
148
+ }
149
+
150
+ return '';
151
+ };
152
+
153
+ _proto.loadImage = function loadImage() {
97
154
  var _this2 = this;
98
155
 
99
- this.isDrawing = false;
100
- this.lastPoint = null;
101
- this.ctx = this.canvas.getContext('2d');
102
156
  this.image = new Image();
103
- this.image.crossOrigin = 'Anonymous';
157
+
158
+ if (this.props.imageCrossOrigin !== undefined) {
159
+ this.image.crossOrigin = this.props.imageCrossOrigin;
160
+ }
104
161
 
105
162
  this.image.onload = function () {
163
+ _this2.ctx.globalCompositeOperation = 'source-over';
164
+
165
+ _this2.ctx.clearRect(0, 0, _this2.props.width, _this2.props.height);
166
+
106
167
  _this2.ctx.drawImage(_this2.image, 0, 0, _this2.props.width, _this2.props.height);
107
168
 
169
+ _this2.initializeScratchGrid();
170
+
171
+ _this2.isFinished = false;
172
+
108
173
  _this2.setState({
109
- loaded: true
174
+ loaded: true,
175
+ finished: false
110
176
  });
111
177
  };
112
178
 
113
- this.image.src = this.props.image;
179
+ this.image.src = this.resolveImageSource(this.props.image);
180
+ };
181
+
182
+ _proto.componentDidMount = function componentDidMount() {
183
+ this.isDrawing = false;
184
+ this.lastPoint = null;
185
+ this.ctx = this.canvas.getContext('2d');
186
+ this.loadImage();
114
187
 
115
188
  if (this.props.customBrush) {
116
189
  this.brushImage = new Image(this.props.customBrush.width, this.props.customBrush.height);
@@ -118,11 +191,7 @@ var Scratch = /*#__PURE__*/function (_Component) {
118
191
  }
119
192
  };
120
193
 
121
- _proto.getFilledInPixels = function getFilledInPixels(stride) {
122
- if (!stride || stride < 1) {
123
- stride = 1;
124
- }
125
-
194
+ _proto.getCheckZone = function getCheckZone() {
126
195
  var x = 0;
127
196
  var y = 0;
128
197
  var width = this.canvas.width;
@@ -135,17 +204,51 @@ var Scratch = /*#__PURE__*/function (_Component) {
135
204
  height = this.props.customCheckZone.height;
136
205
  }
137
206
 
138
- var pixels = this.ctx.getImageData(x, y, width, height);
139
- var total = pixels.data.length / stride;
140
- var count = 0;
207
+ return {
208
+ x: x,
209
+ y: y,
210
+ width: width,
211
+ height: height
212
+ };
213
+ };
214
+
215
+ _proto.initializeScratchGrid = function initializeScratchGrid() {
216
+ var zone = this.getCheckZone();
217
+ var columns = Math.ceil(zone.width / SCRATCH_GRID_SIZE);
218
+ var rows = Math.ceil(zone.height / SCRATCH_GRID_SIZE);
219
+ this.scratchedCells = new Set();
220
+ this.scratchableCellCount = columns * rows;
221
+ };
222
+
223
+ _proto.markScratchArea = function markScratchArea(x, y, width, height) {
224
+ var zone = this.getCheckZone();
225
+ var startX = Math.max(zone.x, x);
226
+ var startY = Math.max(zone.y, y);
227
+ var endX = Math.min(zone.x + zone.width, x + width);
228
+ var endY = Math.min(zone.y + zone.height, y + height);
141
229
 
142
- for (var i = 0; i < pixels.data.length; i += stride) {
143
- if (parseInt(pixels.data[i], 10) === 0) {
144
- count++;
230
+ if (startX >= endX || startY >= endY) {
231
+ return;
232
+ }
233
+
234
+ var startColumn = Math.floor((startX - zone.x) / SCRATCH_GRID_SIZE);
235
+ var endColumn = Math.floor((endX - zone.x - 1) / SCRATCH_GRID_SIZE);
236
+ var startRow = Math.floor((startY - zone.y) / SCRATCH_GRID_SIZE);
237
+ var endRow = Math.floor((endY - zone.y - 1) / SCRATCH_GRID_SIZE);
238
+
239
+ for (var row = startRow; row <= endRow; row++) {
240
+ for (var column = startColumn; column <= endColumn; column++) {
241
+ this.scratchedCells.add(column + ":" + row);
145
242
  }
146
243
  }
244
+ };
245
+
246
+ _proto.getFilledPercentage = function getFilledPercentage() {
247
+ if (this.scratchableCellCount === 0) {
248
+ return 0;
249
+ }
147
250
 
148
- return Math.round(count / total * 100);
251
+ return Math.round(this.scratchedCells.size / this.scratchableCellCount * 100);
149
252
  };
150
253
 
151
254
  _proto.getMouse = function getMouse(e, canvas) {