|
1 | 1 | /*!
|
2 |
| - * Viewer v0.3.0 |
| 2 | + * Viewer v0.3.1 |
3 | 3 | * https://github.com/fengyuanchen/viewer
|
4 | 4 | *
|
5 | 5 | * Copyright (c) 2015 Fengyuan Chen
|
6 | 6 | * Released under the MIT license
|
7 | 7 | *
|
8 |
| - * Date: 2015-12-24T09:51:24.343Z |
| 8 | + * Date: 2015-12-28T03:12:02.123Z |
9 | 9 | */
|
10 | 10 |
|
11 | 11 | (function (factory) {
|
|
155 | 155 | newImage.src = image.src;
|
156 | 156 | }
|
157 | 157 |
|
| 158 | + function getTouchesCenter(touches) { |
| 159 | + var length = touches.length; |
| 160 | + var pageX = 0; |
| 161 | + var pageY = 0; |
| 162 | + |
| 163 | + if (length) { |
| 164 | + $.each(touches, function (i, touch) { |
| 165 | + pageX += touch.pageX; |
| 166 | + pageY += touch.pageY; |
| 167 | + }); |
| 168 | + |
| 169 | + pageX /= length; |
| 170 | + pageY /= length; |
| 171 | + } |
| 172 | + |
| 173 | + return { |
| 174 | + pageX: pageX, |
| 175 | + pageY: pageY |
| 176 | + }; |
| 177 | + } |
| 178 | + |
158 | 179 | function Viewer(element, options) {
|
159 | 180 | this.$element = $(element);
|
160 | 181 | this.options = $.extend({}, Viewer.DEFAULTS, $.isPlainObject(options) && options);
|
|
598 | 619 | break;
|
599 | 620 |
|
600 | 621 | case 'one-to-one':
|
601 |
| - if (image.ratio === 1) { |
602 |
| - this.zoomTo(this.initialImage.ratio); |
603 |
| - } else { |
604 |
| - this.zoomTo(1); |
605 |
| - } |
606 |
| - |
| 622 | + this.toggle(); |
607 | 623 | break;
|
608 | 624 |
|
609 | 625 | case 'reset':
|
|
730 | 746 | },
|
731 | 747 |
|
732 | 748 | wheel: function (event) {
|
733 |
| - var e = event.originalEvent; |
| 749 | + var e = event.originalEvent || event; |
734 | 750 | var ratio = num(this.options.zoomRatio) || 0.1;
|
735 | 751 | var delta = 1;
|
736 | 752 |
|
|
759 | 775 | delta = e.detail > 0 ? 1 : -1;
|
760 | 776 | }
|
761 | 777 |
|
762 |
| - this.zoom(-delta * ratio, true); |
| 778 | + this.zoom(-delta * ratio, true, event); |
763 | 779 | },
|
764 | 780 |
|
765 | 781 | keydown: function (e) {
|
|
832 | 848 | case 49:
|
833 | 849 | if (e.ctrlKey || e.shiftKey) {
|
834 | 850 | e.preventDefault();
|
835 |
| - |
836 |
| - if (this.image.ratio === 1) { |
837 |
| - this.zoomTo(this.initialImage.ratio); |
838 |
| - } else { |
839 |
| - this.zoomTo(1); |
840 |
| - } |
| 851 | + this.toggle(); |
841 | 852 | }
|
842 | 853 |
|
843 | 854 | break;
|
|
929 | 940 | this.endX = e.pageX || originalEvent && originalEvent.pageX;
|
930 | 941 | this.endY = e.pageY || originalEvent && originalEvent.pageY;
|
931 | 942 |
|
932 |
| - this.change(); |
| 943 | + this.change(event); |
933 | 944 | }
|
934 | 945 | },
|
935 | 946 |
|
|
972 | 983 | $viewer = this.$viewer.removeClass(CLASS_HIDE);
|
973 | 984 |
|
974 | 985 | this.$element.one(EVENT_SHOWN, $.proxy(function () {
|
975 |
| - this.view((this.target ? this.$images.index(this.target) : 0) || this.index); |
| 986 | + this.view(this.target ? this.$images.index(this.target) : this.index); |
976 | 987 | this.target = false;
|
977 | 988 | }, this));
|
978 | 989 |
|
|
1009 | 1020 | this.$image.one(EVENT_TRANSITIONEND, $.proxy(function () {
|
1010 | 1021 | $viewer.one(EVENT_TRANSITIONEND, $.proxy(this.hidden, this)).removeClass(CLASS_IN);
|
1011 | 1022 | }, this));
|
1012 |
| - this.zoomTo(0, false, true); |
| 1023 | + this.zoomTo(0, false, false, true); |
1013 | 1024 | } else {
|
1014 | 1025 | $viewer.removeClass(CLASS_IN);
|
1015 | 1026 | this.hidden();
|
|
1064 | 1075 | }
|
1065 | 1076 |
|
1066 | 1077 | // Make the image visible if it fails to load within 1s
|
1067 |
| - this.timeout = setTimeout(function () { |
| 1078 | + this.timeout = setTimeout($.proxy(function () { |
1068 | 1079 | $image.removeClass(CLASS_INVISIBLE);
|
1069 |
| - }, 1000); |
| 1080 | + this.timeout = false; |
| 1081 | + }, this), 1000); |
1070 | 1082 | }
|
1071 | 1083 |
|
1072 | 1084 | $title.empty();
|
|
1149 | 1161 | *
|
1150 | 1162 | * @param {Number} ratio
|
1151 | 1163 | * @param {Boolean} hasTooltip (optional)
|
| 1164 | + * @param {jQuery Event} _event (private) |
1152 | 1165 | */
|
1153 |
| - zoom: function (ratio, hasTooltip) { |
| 1166 | + zoom: function (ratio, hasTooltip, _event) { |
1154 | 1167 | var image = this.image;
|
1155 | 1168 |
|
1156 | 1169 | ratio = num(ratio);
|
|
1161 | 1174 | ratio = 1 + ratio;
|
1162 | 1175 | }
|
1163 | 1176 |
|
1164 |
| - this.zoomTo(image.width * ratio / image.naturalWidth, hasTooltip); |
| 1177 | + this.zoomTo(image.width * ratio / image.naturalWidth, hasTooltip, _event); |
1165 | 1178 | },
|
1166 | 1179 |
|
1167 | 1180 | /**
|
1168 | 1181 | * Zoom the image to an absolute ratio
|
1169 | 1182 | *
|
1170 | 1183 | * @param {Number} ratio
|
1171 | 1184 | * @param {Boolean} hasTooltip (optional)
|
| 1185 | + * @param {jQuery Event} _event (private) |
1172 | 1186 | * @param {Boolean} _zoomable (private)
|
1173 | 1187 | */
|
1174 |
| - zoomTo: function (ratio, hasTooltip, _zoomable) { |
| 1188 | + zoomTo: function (ratio, hasTooltip, _event, _zoomable) { |
1175 | 1189 | var options = this.options;
|
1176 | 1190 | var minZoomRatio = 0.01;
|
1177 | 1191 | var maxZoomRatio = 100;
|
1178 | 1192 | var image = this.image;
|
1179 | 1193 | var width = image.width;
|
1180 | 1194 | var height = image.height;
|
| 1195 | + var originalEvent; |
1181 | 1196 | var newWidth;
|
1182 | 1197 | var newHeight;
|
| 1198 | + var offset; |
| 1199 | + var center; |
1183 | 1200 |
|
1184 | 1201 | ratio = max(0, ratio);
|
1185 | 1202 |
|
|
1196 | 1213 |
|
1197 | 1214 | newWidth = image.naturalWidth * ratio;
|
1198 | 1215 | newHeight = image.naturalHeight * ratio;
|
1199 |
| - image.left -= (newWidth - width) / 2; |
1200 |
| - image.top -= (newHeight - height) / 2; |
| 1216 | + |
| 1217 | + if (_event && (originalEvent = _event.originalEvent)) { |
| 1218 | + offset = this.$viewer.offset(); |
| 1219 | + center = originalEvent.touches ? getTouchesCenter(originalEvent.touches) : { |
| 1220 | + pageX: _event.pageX || originalEvent.pageX || 0, |
| 1221 | + pageY: _event.pageY || originalEvent.pageY || 0 |
| 1222 | + }; |
| 1223 | + |
| 1224 | + // Zoom from the triggering point of the event |
| 1225 | + image.left -= (newWidth - width) * ( |
| 1226 | + ((center.pageX - offset.left) - image.left) / width |
| 1227 | + ); |
| 1228 | + image.top -= (newHeight - height) * ( |
| 1229 | + ((center.pageY - offset.top) - image.top) / height |
| 1230 | + ); |
| 1231 | + } else { |
| 1232 | + |
| 1233 | + // Zoom from the center of the image |
| 1234 | + image.left -= (newWidth - width) / 2; |
| 1235 | + image.top -= (newHeight - height) / 2; |
| 1236 | + } |
| 1237 | + |
1201 | 1238 | image.width = newWidth;
|
1202 | 1239 | image.height = newHeight;
|
1203 | 1240 | image.ratio = ratio;
|
|
1482 | 1519 | // Toggle the image size between its natural size and initial size.
|
1483 | 1520 | toggle: function () {
|
1484 | 1521 | if (this.image.ratio === 1) {
|
1485 |
| - this.zoomTo(this.initialImage.ratio); |
| 1522 | + this.zoomTo(this.initialImage.ratio, true); |
1486 | 1523 | } else {
|
1487 |
| - this.zoomTo(1); |
| 1524 | + this.zoomTo(1, true); |
1488 | 1525 | }
|
1489 | 1526 | },
|
1490 | 1527 |
|
|
1580 | 1617 | }
|
1581 | 1618 | },
|
1582 | 1619 |
|
1583 |
| - change: function () { |
| 1620 | + change: function (event) { |
1584 | 1621 | var offsetX = this.endX - this.startX;
|
1585 | 1622 | var offsetY = this.endY - this.startY;
|
1586 | 1623 |
|
|
1603 | 1640 | abs(this.startY - this.startY2),
|
1604 | 1641 | abs(this.endX - this.endX2),
|
1605 | 1642 | abs(this.endY - this.endY2)
|
1606 |
| - )); |
| 1643 | + ), false, event); |
1607 | 1644 |
|
1608 | 1645 | this.startX2 = this.endX2;
|
1609 | 1646 | this.startY2 = this.endY2;
|
|
0 commit comments