zoomer.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. /**
  2. * @fileoverview 图像放大区域
  3. * @author 乔花<qiaohua@taobao.com>
  4. */
  5. KISSY.add("imagezoom/zoomer", function(S, Node, undefined) {
  6. var STANDARD = 'standard', INNER = 'inner',
  7. RE_IMG_SRC = /^.+\.(?:jpg|png|gif)$/i,
  8. round = Math.round, min = Math.min,
  9. body;
  10. function Zoomer() {
  11. var self = this,
  12. tmp;
  13. // 预加载大图
  14. tmp = self.get('bigImageSrc');
  15. if (tmp && self.get('preload')) {
  16. new Image().src = tmp;
  17. }
  18. // 两种显示效果切换标志
  19. self._isInner = self.get('type') === INNER;
  20. body = new Node(document.body);
  21. }
  22. Zoomer.ATTRS = {
  23. width: {
  24. valueFn: function() {
  25. return this.get('imageWidth');
  26. }
  27. },
  28. height: {
  29. valueFn: function() {
  30. return this.get('imageHeight');
  31. }
  32. },
  33. elCls: {
  34. value: 'ks-imagezoom-viewer'
  35. },
  36. elStyle: {
  37. value: {
  38. overflow: 'hidden',
  39. position: 'absolute'
  40. }
  41. },
  42. /**
  43. * 显示类型
  44. * @type {string}
  45. */
  46. type: {
  47. value: STANDARD // STANDARD or INNER
  48. },
  49. /**
  50. * 是否预加载大图
  51. * @type {boolean}
  52. */
  53. preload: {
  54. value: true
  55. },
  56. /**
  57. * 大图路径, 默认取触点上的 data-ks-imagezoom 属性值
  58. * @type {string}
  59. */
  60. bigImageSrc: {
  61. setter: function(v) {
  62. if (v && RE_IMG_SRC.test(v)) {
  63. return v;
  64. }
  65. return this.get('bigImageSrc');
  66. },
  67. valueFn: function() {
  68. var img = this.get('imageNode'), data;
  69. if (img) {
  70. data = img.attr('data-ks-imagezoom');
  71. if (data && RE_IMG_SRC.test(data)) return data;
  72. }
  73. return undefined;
  74. }
  75. },
  76. /**
  77. * 大图高宽, 大图高宽是指在没有加载完大图前, 使用这个值来替代计算, 等加载完后会重新更新镜片大小, 具体场景下, 设置个更合适的值
  78. * @type {Array.<number>}
  79. bigImageSize: {
  80. value: [800, 800],
  81. setter: function(v) {
  82. this.set('bigImageWidth', v[0]);
  83. this.set('bigImageHeight', v[1]);
  84. return v;
  85. }
  86. },*/
  87. /**
  88. * 大图高宽, 大图高宽是指在没有加载完大图前, 使用这个值来替代计算, 等加载完后会重新更新镜片大小, 具体场景下, 设置个更合适的值
  89. * @type {number}
  90. */
  91. bigImageWidth: {
  92. valueFn: function() {
  93. var img = this.bigImage;
  94. img = img && img.width();
  95. return img || 800;
  96. }
  97. },
  98. bigImageHeight: {
  99. valueFn: function() {
  100. var img = this.bigImage;
  101. img = img && img.height();
  102. return img || 800;
  103. }
  104. },
  105. /**
  106. * 保存当前鼠标位置
  107. */
  108. currentMouse: {
  109. value: undefined
  110. },
  111. lensClass: {
  112. value: 'ks-imagezoom-lens'
  113. },
  114. lensHeight: {
  115. value: undefined
  116. },
  117. lensWidth: {
  118. value: undefined
  119. },
  120. lensTop: {
  121. value: undefined
  122. },
  123. lensLeft: {
  124. value: undefined
  125. }
  126. };
  127. Zoomer.HTML_PARSER = {
  128. };
  129. S.augment(Zoomer, {
  130. __renderUI: function() {
  131. var self = this, bigImage;
  132. self.viewer = self.get("contentEl");
  133. bigImage = self.bigImage = new Node('<img src="' + self.get("bigImageSrc") + '" />').css('position', 'absolute').appendTo(self.viewer);
  134. self._setLensSize();
  135. self._setLensOffset();
  136. if (self._isInner) {
  137. // inner 位置强制修改
  138. self.set('align', {
  139. node: self.image,
  140. points: ['cc', 'cc']
  141. });
  142. self._bigImageCopy = new Node('<img src="' + self.image.attr('src') + '" />').css('position', 'absolute')
  143. .width(self.get('bigImageWidth')).height(self.get('bigImageHeight')).prependTo(self.viewer);
  144. }
  145. // 标准模式, 添加镜片
  146. else {
  147. self.lens = new Node('<div class="' + self.get("lensClass") + '"></div>').css('position', 'absolute').appendTo(body).hide();
  148. }
  149. self.viewer.appendTo(self.get("el"));
  150. self.loading();
  151. // 大图加载完毕后更新显示区域
  152. imgOnLoad(bigImage, function() {
  153. S.log([bigImage[0].complete, bigImage.width()]);
  154. self.unloading();
  155. self._setLensSize();
  156. self.set('bigImageWidth', bigImage.width());
  157. self.set('bigImageHeight', bigImage.height());
  158. });
  159. },
  160. __bindUI: function() {
  161. var self = this;
  162. self.on('afterVisibleChange', function(ev) {
  163. var isVisible = ev.newVal;
  164. if (isVisible) {
  165. if (self._isInner) {
  166. self._anim(0.4, 42);
  167. }
  168. body.on('mousemove', self._mouseMove, self);
  169. } else {
  170. hide(self.lens);
  171. body.detach('mousemove', self._mouseMove, self);
  172. }
  173. });
  174. },
  175. __syncUI: function() {
  176. },
  177. __destructor: function() {
  178. var self = this;
  179. self.viewer.remove();
  180. self.lens.remove();
  181. },
  182. /**
  183. * 设置镜片大小
  184. */
  185. _setLensSize: function() {
  186. var self = this,
  187. rw = self.get('imageWidth'), rh = self.get('imageHeight'),
  188. bw = self.get('bigImageWidth'), bh = self.get('bigImageHeight'),
  189. w = self.get('width'), h = self.get('height');
  190. // 计算镜片宽高, vH / bigImageH = lensH / imageH
  191. self.set('lensWidth', min(round(w * rw / bw), rw));
  192. self.set('lensHeight', min(round(h * rh / bh), rh));
  193. },
  194. /**
  195. * 随着鼠标移动, 设置镜片位置
  196. * @private
  197. */
  198. _setLensOffset: function(ev) {
  199. var self = this;
  200. ev = ev || self.get('currentMouse');
  201. var rl = self.get('imageLeft'), rt = self.get('imageTop'),
  202. rw = self.get('imageWidth'), rh = self.get('imageHeight'),
  203. w = self.get('width'), h = self.get('height'),
  204. lensWidth = self.get('lensWidth'), lensHeight = self.get('lensHeight'),
  205. lensLeft = ev.pageX - lensWidth / 2, lensTop = ev.pageY - lensHeight / 2;
  206. if (lensLeft <= rl) {
  207. lensLeft = rl;
  208. } else if (lensLeft >= rw + rl - lensWidth) {
  209. lensLeft = rw + rl - lensWidth;
  210. }
  211. if (lensTop <= rt) {
  212. lensTop = rt;
  213. } else if (lensTop >= rh + rt - lensHeight) {
  214. lensTop = rh + rt - lensHeight;
  215. }
  216. self.set('lensLeft', lensLeft);
  217. self.set('lensTop', lensTop);
  218. },
  219. _mouseMove: function(ev) {
  220. var self = this,
  221. rl = self.get('imageLeft'), rt = self.get('imageTop'),
  222. rw = self.get('imageWidth'), rh = self.get('imageHeight');
  223. if (ev.pageX > rl && ev.pageX < rl + rw &&
  224. ev.pageY > rt && ev.pageY < rt + rh) {
  225. self.set('currentMouse', ev);
  226. } else {
  227. // 移出
  228. self.hide();
  229. }
  230. },
  231. /**
  232. * Inner 效果中的放大动画
  233. * @param {number} seconds
  234. * @param {number} times
  235. * @private
  236. */
  237. _anim: function(seconds, times) {
  238. var self = this,
  239. go, t = 1,
  240. rl = self.get('imageLeft'), rt = self.get('imageTop'),
  241. rw = self.get('imageWidth'), rh = self.get('imageHeight'),
  242. bw = self.get('bigImageWidth'), bh = self.get('bigImageHeight'),
  243. max_left = - round((self.get('lensLeft') - rl) * bw / rw),
  244. max_top = - round((self.get('lensTop') - rt) * bh / rh),
  245. tmpWidth, tmpHeight, tmpCss;
  246. if (self._animTimer) self._animTimer.cancel();
  247. // set min width and height
  248. self.bigImage.width(rw).height(rh);
  249. self._bigImageCopy.width(rw).height(rh);
  250. self._animTimer = S.later((go = function() {
  251. tmpWidth = rw + ( bw - rw) / times * t;
  252. tmpHeight = rh + (bh - rh) / times * t;
  253. tmpCss = {
  254. left: max_left / times * t,
  255. top: max_top / times * t
  256. };
  257. self.bigImage.width(tmpWidth).height(tmpHeight).css(tmpCss);
  258. self._bigImageCopy.width(tmpWidth).height(tmpHeight).css(tmpCss);
  259. if (++t > times) {
  260. self._animTimer.cancel();
  261. self._animTimer = undefined;
  262. }
  263. }), seconds * 1000 / times, true);
  264. go();
  265. },
  266. _uiSetCurrentMouse: function(ev) {
  267. var self = this,
  268. lt;
  269. if (!self.bigImage || self._animTimer) return;
  270. // 更新 lens 位置
  271. show(self.lens);
  272. self._setLensOffset(ev);
  273. // 设置大图偏移
  274. lt = {
  275. left: - round((self.get('lensLeft') - self.get('imageLeft')) * self.get('bigImageWidth') / self.get('imageWidth')),
  276. top: - round((self.get('lensTop') - self.get('imageTop')) * self.get('bigImageHeight') / self.get('imageHeight'))
  277. };
  278. self._bigImageCopy && self._bigImageCopy.css(lt);
  279. self.bigImage.css(lt);
  280. },
  281. _uiSetLensWidth: function(v) {
  282. this.lens && this.lens.width(v);
  283. },
  284. _uiSetLensHeight: function(v) {
  285. this.lens && this.lens.height(v);
  286. },
  287. _uiSetLensTop: function(v) {
  288. this.lens && this.lens.offset({ 'top': v });
  289. },
  290. _uiSetLensLeft: function(v) {
  291. this.lens && this.lens.offset({ 'left': v });
  292. },
  293. _uiSetBigImageWidth: function(v) {
  294. v && this.bigImage && this.bigImage.width(v);
  295. v && this._bigImageCopy && this._bigImageCopy.width(v);
  296. },
  297. _uiSetBigImageHeight: function(v) {
  298. v && this.bigImage && this.bigImage.height(v);
  299. v && this._bigImageCopy && this._bigImageCopy.height(v);
  300. },
  301. _uiSetBigImageSrc: function(v) {
  302. v && this.bigImage && this.bigImage.attr('src', v);
  303. v && this._bigImageCopy && this._bigImageCopy.attr('src', v);
  304. },
  305. /**
  306. * 改变小图元素的 src
  307. * @param {String} src
  308. */
  309. changeImageSrc: function(src) {
  310. var self = this;
  311. self.image.attr('src', src);
  312. self.loading();
  313. },
  314. /**
  315. * 调整放大区域位置, 在外部改变小图位置时, 需要对应更新放大区域的位置
  316. */
  317. refreshRegion: function() {
  318. var self = this;
  319. self._fresh = self.get('align');
  320. self.set('align', undefined);
  321. }
  322. });
  323. function show(obj) {
  324. obj && obj.show();
  325. }
  326. function hide(obj) {
  327. obj && obj.hide();
  328. }
  329. function imgOnLoad(img, callback) {
  330. var imgElem = img[0];
  331. // 浏览器缓存时, complete 为 true
  332. if ((imgElem && imgElem.complete && imgElem.clientWidth)) {
  333. callback();
  334. return;
  335. }
  336. // 1) 图尚未加载完毕,等待 onload 时再初始化 2) 多图切换时需要绑定load事件来更新相关信息
  337. /* img.on('load', function() {
  338. setTimeout(callback, 100);
  339. });*/
  340. imgElem.onLoad = function() {
  341. setTimeout(callback, 100);
  342. }
  343. }
  344. Zoomer.__imgOnLoad = imgOnLoad;
  345. return Zoomer;
  346. }, {
  347. requires:["node"]
  348. });