Administrator
2022-09-14 58d006e05dcf2a20d0ec5367dd03d66a61db6849
提交 | 用户 | 时间
58d006 1 /* Flot plugin for plotting images.
A 2
3 Copyright (c) 2007-2013 IOLA and Ole Laursen.
4 Licensed under the MIT license.
5
6 The data syntax is [ [ image, x1, y1, x2, y2 ], ... ] where (x1, y1) and
7 (x2, y2) are where you intend the two opposite corners of the image to end up
8 in the plot. Image must be a fully loaded Javascript image (you can make one
9 with new Image()). If the image is not complete, it's skipped when plotting.
10
11 There are two helpers included for retrieving images. The easiest work the way
12 that you put in URLs instead of images in the data, like this:
13
14     [ "myimage.png", 0, 0, 10, 10 ]
15
16 Then call $.plot.image.loadData( data, options, callback ) where data and
17 options are the same as you pass in to $.plot. This loads the images, replaces
18 the URLs in the data with the corresponding images and calls "callback" when
19 all images are loaded (or failed loading). In the callback, you can then call
20 $.plot with the data set. See the included example.
21
22 A more low-level helper, $.plot.image.load(urls, callback) is also included.
23 Given a list of URLs, it calls callback with an object mapping from URL to
24 Image object when all images are loaded or have failed loading.
25
26 The plugin supports these options:
27
28     series: {
29         images: {
30             show: boolean
31             anchor: "corner" or "center"
32             alpha: [ 0, 1 ]
33         }
34     }
35
36 They can be specified for a specific series:
37
38     $.plot( $("#placeholder"), [{
39         data: [ ... ],
40         images: { ... }
41     ])
42
43 Note that because the data format is different from usual data points, you
44 can't use images with anything else in a specific data series.
45
46 Setting "anchor" to "center" causes the pixels in the image to be anchored at
47 the corner pixel centers inside of at the pixel corners, effectively letting
48 half a pixel stick out to each side in the plot.
49
50 A possible future direction could be support for tiling for large images (like
51 Google Maps).
52
53 */
54
55 (function ($) {
56     var options = {
57         series: {
58             images: {
59                 show: false,
60                 alpha: 1,
61                 anchor: "corner" // or "center"
62             }
63         }
64     };
65
66     $.plot.image = {};
67
68     $.plot.image.loadDataImages = function (series, options, callback) {
69         var urls = [], points = [];
70
71         var defaultShow = options.series.images.show;
72         
73         $.each(series, function (i, s) {
74             if (!(defaultShow || s.images.show))
75                 return;
76             
77             if (s.data)
78                 s = s.data;
79
80             $.each(s, function (i, p) {
81                 if (typeof p[0] == "string") {
82                     urls.push(p[0]);
83                     points.push(p);
84                 }
85             });
86         });
87
88         $.plot.image.load(urls, function (loadedImages) {
89             $.each(points, function (i, p) {
90                 var url = p[0];
91                 if (loadedImages[url])
92                     p[0] = loadedImages[url];
93             });
94
95             callback();
96         });
97     }
98     
99     $.plot.image.load = function (urls, callback) {
100         var missing = urls.length, loaded = {};
101         if (missing == 0)
102             callback({});
103
104         $.each(urls, function (i, url) {
105             var handler = function () {
106                 --missing;
107                 
108                 loaded[url] = this;
109                 
110                 if (missing == 0)
111                     callback(loaded);
112             };
113
114             $('<img />').load(handler).error(handler).attr('src', url);
115         });
116     };
117     
118     function drawSeries(plot, ctx, series) {
119         var plotOffset = plot.getPlotOffset();
120         
121         if (!series.images || !series.images.show)
122             return;
123         
124         var points = series.datapoints.points,
125             ps = series.datapoints.pointsize;
126         
127         for (var i = 0; i < points.length; i += ps) {
128             var img = points[i],
129                 x1 = points[i + 1], y1 = points[i + 2],
130                 x2 = points[i + 3], y2 = points[i + 4],
131                 xaxis = series.xaxis, yaxis = series.yaxis,
132                 tmp;
133
134             // actually we should check img.complete, but it
135             // appears to be a somewhat unreliable indicator in
136             // IE6 (false even after load event)
137             if (!img || img.width <= 0 || img.height <= 0)
138                 continue;
139
140             if (x1 > x2) {
141                 tmp = x2;
142                 x2 = x1;
143                 x1 = tmp;
144             }
145             if (y1 > y2) {
146                 tmp = y2;
147                 y2 = y1;
148                 y1 = tmp;
149             }
150             
151             // if the anchor is at the center of the pixel, expand the 
152             // image by 1/2 pixel in each direction
153             if (series.images.anchor == "center") {
154                 tmp = 0.5 * (x2-x1) / (img.width - 1);
155                 x1 -= tmp;
156                 x2 += tmp;
157                 tmp = 0.5 * (y2-y1) / (img.height - 1);
158                 y1 -= tmp;
159                 y2 += tmp;
160             }
161             
162             // clip
163             if (x1 == x2 || y1 == y2 ||
164                 x1 >= xaxis.max || x2 <= xaxis.min ||
165                 y1 >= yaxis.max || y2 <= yaxis.min)
166                 continue;
167
168             var sx1 = 0, sy1 = 0, sx2 = img.width, sy2 = img.height;
169             if (x1 < xaxis.min) {
170                 sx1 += (sx2 - sx1) * (xaxis.min - x1) / (x2 - x1);
171                 x1 = xaxis.min;
172             }
173
174             if (x2 > xaxis.max) {
175                 sx2 += (sx2 - sx1) * (xaxis.max - x2) / (x2 - x1);
176                 x2 = xaxis.max;
177             }
178
179             if (y1 < yaxis.min) {
180                 sy2 += (sy1 - sy2) * (yaxis.min - y1) / (y2 - y1);
181                 y1 = yaxis.min;
182             }
183
184             if (y2 > yaxis.max) {
185                 sy1 += (sy1 - sy2) * (yaxis.max - y2) / (y2 - y1);
186                 y2 = yaxis.max;
187             }
188             
189             x1 = xaxis.p2c(x1);
190             x2 = xaxis.p2c(x2);
191             y1 = yaxis.p2c(y1);
192             y2 = yaxis.p2c(y2);
193             
194             // the transformation may have swapped us
195             if (x1 > x2) {
196                 tmp = x2;
197                 x2 = x1;
198                 x1 = tmp;
199             }
200             if (y1 > y2) {
201                 tmp = y2;
202                 y2 = y1;
203                 y1 = tmp;
204             }
205
206             tmp = ctx.globalAlpha;
207             ctx.globalAlpha *= series.images.alpha;
208             ctx.drawImage(img,
209                           sx1, sy1, sx2 - sx1, sy2 - sy1,
210                           x1 + plotOffset.left, y1 + plotOffset.top,
211                           x2 - x1, y2 - y1);
212             ctx.globalAlpha = tmp;
213         }
214     }
215
216     function processRawData(plot, series, data, datapoints) {
217         if (!series.images.show)
218             return;
219
220         // format is Image, x1, y1, x2, y2 (opposite corners)
221         datapoints.format = [
222             { required: true },
223             { x: true, number: true, required: true },
224             { y: true, number: true, required: true },
225             { x: true, number: true, required: true },
226             { y: true, number: true, required: true }
227         ];
228     }
229     
230     function init(plot) {
231         plot.hooks.processRawData.push(processRawData);
232         plot.hooks.drawSeries.push(drawSeries);
233     }
234     
235     $.plot.plugins.push({
236         init: init,
237         options: options,
238         name: 'image',
239         version: '1.1'
240     });
241 })(jQuery);