-
Notifications
You must be signed in to change notification settings - Fork 18
/
scratchcard.js
151 lines (139 loc) · 5.9 KB
/
scratchcard.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
'use strict';
var ScratchCard = function (options) {
var self = this;
// Set local variables for this instance
this.canvasID = options.id;
this.width = options.brushSize;
this.shape = options.lineJoin;
this.clearRequirement = options.percentRequired;
this.color = options.fillColor;
this.lastX, this.lastY;
// Initialize the scratch card
this.init(this.canvasID);
// Return the element so we can bind events onto it
return(this.can);
};
ScratchCard.prototype.init = function (canvasID) {
// Variable to check if a continous press is occurring
this.pressed = false;
// Get the canvas and its 2d context
this.can = document.getElementById(canvasID);
this.ctx = this.can.getContext('2d');
// Initialize offsets of the canvas
var canRect = this.can.getBoundingClientRect();
this.offset = { top: canRect.top + document.body.scrollTop, left: canRect.left + document.body.scrollLeft };
// Register mouse event listeners
this.can.addEventListener('mousedown', this.mouseDown.bind(this), false);
this.can.addEventListener('mousemove', this.mouseMove.bind(this), false);
this.can.addEventListener('mouseup', this.mouseUp.bind(this), false);
// Register touch event listeners
this.can.addEventListener('touchstart', this.touchDown.bind(this), false);
this.can.addEventListener('touchmove', this.touchMove.bind(this), true);
this.can.addEventListener('touchend', this.touchUp.bind(this), false);
this.can.addEventListener('touchcancel', this.touchUp.bind(this), false);
// Reset the canvas. Image gets hidden
this.reset();
};
ScratchCard.prototype.mouseDown = function (e) {
// Set the control variable to true since the user is currently pressing the screen
this.pressed = true;
// Get the X and Y coordinates of the input
var currentX = e.pageX - this.offset.left;
var currentY = e.pageY - this.offset.top;
// Call draw with the X and Y coordinates. The last parameter of the call tells the function
// whether the user is currently pressing the screen or if its a starting action (mousedown or touchdown).
this.draw(currentX, currentY, false);
};
ScratchCard.prototype.mouseMove = function (e) {
// To avoid some weird cases we have to check if a mousedown event occured beforehand
if (this.pressed) {
// Get the X and Y coordinates of the input
var currentX = e.pageX - this.offset.left;
var currentY = e.pageY - this.offset.top;
this.draw(currentX, currentY, true);
}
};
ScratchCard.prototype.mouseUp = function () {
// Reset the control variable
this.pressed = false;
};
ScratchCard.prototype.touchDown = function (e) {
// Touch is a bit more complicated than mouse usage since mobiel browsers have default
// functionalities. Also the touch position is not a real position but an array.
e.preventDefault();
// User is touching the screen
this.pressed = true;
var currentX = e.targetTouches[0].pageX - this.offset.left;
var currentY = e.targetTouches[0].pageY - this.offset.top;
this.draw(currentX, currentY, false);
};
ScratchCard.prototype.touchMove = function (e) {
e.preventDefault();
// To avoid some weird cases we have to check if a touchstart event occured beforehand
if (this.pressed) {
var currentX = e.targetTouches[0].pageX - this.offset.left;
var currentY = e.targetTouches[0].pageY - this.offset.top;
this.draw(currentX, currentY, true);
}
};
ScratchCard.prototype.touchUp = function () {
// Reset the control variable
this.pressed = false;
};
ScratchCard.prototype.draw = function (x, y, isDown) {
// This function will remove the paint from the image. It is called draw since we actually draw transparent
// paint over a grey paint.
// X is the current X coordinate
// Y is the current Y coordinate
// isDown is a status to determine if the user is currently using the screen or if they just terminated the input
if (isDown) {
this.ctx.beginPath();
// Tell the canvas we want to use 'removing' paint instead of a 'applying' paint
this.ctx.globalCompositeOperation = 'destination-out';
this.ctx.strokeStyle = 'rgba(0, 0, 0, 1)';
this.ctx.lineWidth = this.width;
this.ctx.lineJoin = this.shape;
// We set the starting position of our drawer to be the last position of the user's input
this.ctx.moveTo(this.lastX, this.lastY);
// Draw the line to the new X and Y coordinates
this.ctx.lineTo(x, y);
this.ctx.closePath();
this.ctx.stroke();
}
// Set the last coordinates the the current coordinates so next time we start where we left off
this.lastX = x;
this.lastY = y;
// Calculate the amount of cleared pixels
var clearedPixels = this.calcPixels(25);
this.clearPercentage(clearedPixels);
};
ScratchCard.prototype.calcPixels = function (stride) {
// We use a stride so we don't calculate forever
// We get all pixels in the canvas (the image is a background image so its not part of the canvas)
var pixels = this.ctx.getImageData(0, 0, this.can.width, this.can.height);
var pixelData = pixels.data;
var pixelLength = pixelData.length;
var total = (pixelLength / stride);
var count = 0;
// Now we iterate over all (actually not all since we use the stride) pixels and check whether their
// pixelData is empty which means this pixel is removed.
for (var i = count; i < pixelLength; i += stride) {
if (parseInt(pixelData[i]) === 0) {
count ++;
}
}
// Calculate the actual percentage
return Math.round((count / total) * 100);
};
ScratchCard.prototype.clearPercentage = function (clearedAmount) {
if ((clearedAmount === this.clearRequirement) || (clearedAmount > this.clearRequirement)) {
var successEvent = new Event('success');
this.can.dispatchEvent(successEvent);
}
};
ScratchCard.prototype.reset = function () {
// Reset the canvas. Fill it with paint so the user can scratch again.
this.ctx.globalCompositeOperation = 'source-over';
this.ctx.fillStyle = this.color;
this.ctx.fillRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
};