/
index.js
179 lines (143 loc) · 5.06 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
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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
var debounce = require('debounce');
var Panzoom = require('d-panzoom');
function Component () {}
Component.prototype = Object.create(Panzoom.prototype);
Component.prototype.init = function (model, dom) {
// set the default panzoom transformation matrix
model.set('_matrix', [1, 0, 0, 1, 0, 0]);
// default min scale to the full image
model.setNull('minScale', 1);
// ensure the image width is at least as wide as the container
model.start('from.width', '_containerWidth', 'from.image.width', Math.max);
// ensure the image height is at least as tall as the container
model.start('from.height', '_containerHeight', 'from.image.height', Math.max);
// set the image width to the specified width or size
// default to the container width
model.start('to.width', 'size', 'width', '_containerWidth',
function (imageSize, imageWidth, containerWidth) {
return imageWidth || imageSize || containerWidth;
}
);
// set the image width to the specified height or size
// default to the container height
model.start('to.height', 'size', 'height', '_containerHeight',
function (imageSize, imageHeight, containerHeight) {
return imageHeight || imageSize || containerHeight;
}
);
// how much wider the image is relative to the container width
model.start('_ratioX', 'to.width', '_containerWidth',
function (imageWidth, containerWidth) {
return imageWidth / containerWidth;
}
);
// how much taller the image is relative to the container height
model.start('_ratioY', 'to.height', '_containerHeight',
function (imageHeight, containerHeight) {
return imageHeight / containerHeight;
}
);
// the number of times you can scale the container horizontally
// such that the image will be zoomed in completely
model.start('_maxScale', 'from.width', '_containerWidth', '_ratioX',
function (imageWidth, containerWidth, ratioX) {
return imageWidth / containerWidth / ratioX;
}
);
this.on('change', function (matrix) {
model.set('_matrix', matrix);
});
// whether or not panzoom can scale
// may be used to determine whether or not to show zoom in, zoom out, etc.
model.start('canScale', '_maxScale', 'minScale', Math.max);
Panzoom.prototype.init.call(this, model, dom);
};
Component.prototype.create = function (model, dom) {
var self = this;
model.set('_containerWidth', this.container.offsetWidth);
model.set('_containerHeight', this.container.offsetHeight);
// draw the image to match the user's zoom/pan preference
var wait = model.setNull('debounce', 100);
var draw = debounce(this.draw, wait).bind(this);
model.on('change', 'filetype', draw);
model.on('change', 'from.image', draw);
model.on('change', 'to.width', draw);
model.on('change', 'to.height', draw);
model.on('change', '_matrix', draw);
var change = this.change.bind(this);
model.on('change', 'from.image', change);
Panzoom.prototype.create.call(this, model, dom);
};
Component.prototype.change = function () {
var self = this;
var model = this.model;
var img = model.get('from.image');
if (!img) {
this.image.src = '';
return this.destroy();
}
this.image.onload = function () {
self.panzoom();
};
this.image.src = img.src;
};
Component.prototype.clear = function () {
this.model.del('from.image');
this.model.del('to.image');
this.emit('clear');
};
Component.prototype.draw = function () {
var self = this;
var model = this.model;
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
var from = model.get('from.image');
var maxScale = model.get('_maxScale');
var matrix = model.get('_matrix');
var mimetype = 'image/' + model.get('filetype') || 'png';
var offsetX = 0.5 * model.get('_containerWidth') * (matrix[0] - 1);
var offsetY = offsetX * model.get('from.height') / model.get('from.width');
var translateX = model.get('_ratioX') * (matrix[4] - offsetX);
var translateY = model.get('_ratioY') * (matrix[5] - offsetY);
var scaleX = matrix[0] / maxScale;
var scaleY = matrix[3] / maxScale;
var to = new Image();
var toMatrix = [scaleX, 0, 0, scaleY, translateX, translateY];
if (!from) {
return model.set('to.image', null);
}
to.onload = function () {
model.set('to.image', to);
self.emit('draw', to);
};
canvas.width = model.get('to.width');
canvas.height = model.get('to.height');
ctx.transform.apply(ctx, toMatrix);
ctx.drawImage(from, 0, 0);
to.src = canvas.toDataURL(mimetype);
};
Component.prototype.load = function (data) {
var self = this;
var img = new Image();
var model = this.model;
var reader = new FileReader();
model.set('loading', true);
img.onload = function (e) {
model.set('from.image', img);
model.del('loading');
self.emit('load', img);
};
// currently supports only one image
if (data[0]) data = data[0];
// data is an image uri
if (typeof data === 'string') {
img.src = data;
return;
}
// otherwise data is a file
reader.onload = function (e) {
img.src = e.target.result;
};
reader.readAsDataURL(data);
};
module.exports = Component;