-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.js
90 lines (85 loc) · 2.78 KB
/
index.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
class Cover {
/**
* Provides a mechanism to draw an image in canvas such that it will cover the
* area provided exactly.
*
* @param {Canvas.Image} img the image to render
* @param {number} x offset x coordinate on the canvas
* @param {number} y offset y coordinate on the canvas
* @param {number} width width to fit to on the canvas
* @param {number} height height to fit to on the canvas
* @returns {Cover}
*/
constructor (img, x, y, width, height) {
const ir = img.width / img.height;
const r = width / height;
this.img = img;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.bounds = [{ width: img.width, height: img.height, sx: 0, sy: 0 }];
this.sw = ir < r ? img.width : img.height * r;
this.sh = ir < r ? img.width / r : img.height;
this.pan(0.5, 0.5);
}
/**
* Doesn't actually crop the input image but does redefine the bounds of the
* image for the sake of panning. ie. after a crop, the pan cx and cy will
* be with regard to the currently defined area rather than the whole image
* or previously cropped area.
*
* @returns {Cover}
*/
crop () {
const { sw: width, sh: height, sx, sy } = this;
this.bounds.push({ width, height, sx, sy });
return this;
}
/**
* Change the center point of the image.
*
* @param {number} cx value between 0 and 1 representing the left or right
* side of the image bounds. The bounds will be the whole image or the
* defined source area at the time of the last crop().
* @param {number} cy value between 0 and 1 representing the top or the
* bottom of the image bounds.
* @returns {Cover}
*/
pan (cx, cy) {
if (cx < 0 || cx > 1) throw new Error('make sure 0 < cx < 1 ');
if (cy < 0 || cy > 1) throw new Error('make sure 0 < cy < 1 ');
const { width, height, sx, sy } = this.bounds[this.bounds.length - 1];
this.sx = sx + (width - this.sw) * cx;
this.sy = sy + (height - this.sh) * cy;
return this;
}
/**
* Zoom in at the current location.
*
* @param {number} factor how much to zoom in by (>0).
* @returns {Cover}
*/
zoom (factor) {
if (factor <= 0) throw new Error('zoom not > 0');
this.sx += (this.sw - (this.sw / factor)) / 2;
this.sy += (this.sh - (this.sh / factor)) / 2;
this.sw /= factor;
this.sh /= factor;
return this;
}
/**
* Render to the provided context.
*
* @param {CanvasRenderingContext2D} ctx canvas context to render to
* @returns {Cover}
*/
render (ctx) {
ctx.drawImage(this.img, this.sx, this.sy, this.sw, this.sh, // source
this.x, this.y, this.width, this.height); // destination
return this;
}
}
module.exports = (img, x, y, width, height) => {
return new Cover(img, x, y, width, height);
};