imageviewer_terrydr
Version:
A simple jQuery image viewing plugin.
694 lines (579 loc) • 17.9 kB
JavaScript
// Show the viewer (only available in modal mode)
show: function () {
var options = this.options;
var $viewer;
if (options.inline || this.transitioning) {
return;
}
if (!this.isBuilt) {
this.build();
}
if ($.isFunction(options.show)) {
this.$element.one(EVENT_SHOW, options.show);
}
if (this.trigger(EVENT_SHOW).isDefaultPrevented()) {
return;
}
this.$body.addClass(CLASS_OPEN);
$viewer = this.$viewer.removeClass(CLASS_HIDE);
this.$element.one(EVENT_SHOWN, $.proxy(function () {
this.view(this.target ? this.$images.index(this.target) : this.index);
this.target = false;
}, this));
if (options.transition) {
this.transitioning = true;
$viewer.addClass(CLASS_TRANSITION);
forceReflow($viewer[0]);
$viewer.one(EVENT_TRANSITIONEND, $.proxy(this.shown, this)).addClass(CLASS_IN);
} else {
$viewer.addClass(CLASS_IN);
this.shown();
}
},
// Hide the viewer (only available in modal mode)
hide: function () {
var options = this.options;
var $viewer = this.$viewer;
if (options.inline || this.transitioning || !this.isShown) {
return;
}
if ($.isFunction(options.hide)) {
this.$element.one(EVENT_HIDE, options.hide);
}
if (this.trigger(EVENT_HIDE).isDefaultPrevented()) {
return;
}
if (this.isViewed && options.transition) {
this.transitioning = true;
this.$image.one(EVENT_TRANSITIONEND, $.proxy(function () {
$viewer.one(EVENT_TRANSITIONEND, $.proxy(this.hidden, this)).removeClass(CLASS_IN);
}, this));
this.zoomTo(0, false, false, true);
} else {
$viewer.removeClass(CLASS_IN);
this.hidden();
}
},
/**
* View one of the images with image's index
*
* @param {Number} index
*/
view: function (index) {
var $title = this.$title;
var $image;
var $item;
var $img;
var url;
var alt;
index = Number(index) || 0;
if (!this.isShown || this.isPlayed || index < 0 || index >= this.length ||
this.isViewed && index === this.index) {
return;
}
if (this.trigger(EVENT_VIEW).isDefaultPrevented()) {
return;
}
$item = this.$items.eq(index);
$img = $item.find(SELECTOR_IMG);
url = $img.data('originalUrl');
alt = $img.attr('alt');
//视频播放
if($img.attr('video-original-url')!="")
{
var videoUrl=$img.attr('video-original-url');
this.$image = $image = $('<video preload autoplay controls volume poster="'+url+'" src="' + videoUrl + '" alt="' + alt + '">');
var buttom_padding;
buttom_padding=window.innerWidth<=767?120:155;
var videoHeight=window.innerHeight-buttom_padding;
this.$image.css("height",videoHeight)
$image.click(function(){
if(this.paused)
{
this.play();
$(".video-tip").delay(0).html("播放").fadeIn().delay(400).hide(0);
}
else
{
this.pause();
$(".video-tip").delay(0).html("暂停").fadeIn().delay(400).hide(0);
}
})
}
else
{
this.$image = $image = $('<img src="' + url + '" alt="' + alt + '">');
}
if (this.isViewed) {
this.$items.eq(this.index).removeClass(CLASS_ACTIVE);
}
$item.addClass(CLASS_ACTIVE);
this.isViewed = false;
this.index = index;
this.image = null;
this.$canvas.html($image.addClass(CLASS_INVISIBLE));
// Center current item
this.renderList();
// Clear title
$title.empty();
//如果是视频 无法回调view方法 ,显示title,此处设置下video的title
if($img.attr('video-original-url')!="")
{
$title.html(alt)
}
// Generate title after viewed
this.$element.one(EVENT_VIEWED, $.proxy(function () {
var image = this.image;
var width = image.naturalWidth;
var height = image.naturalHeight;
$title.html(alt + ' (' + width + ' × ' + height + ')');
}, this));
if ($image[0].complete) {
this.load();
} else {
$image.one(EVENT_LOAD, $.proxy(this.load, this));
if (this.timeout) {
clearTimeout(this.timeout);
}
// Make the image visible if it fails to load within 1s
this.timeout = setTimeout($.proxy(function () {
$image.removeClass(CLASS_INVISIBLE);
this.timeout = false;
}, this), 1000);
}
},
// View the previous image
prev: function () {
this.view(max(this.index - 1, 0));
},
// View the next image
next: function () {
this.view(min(this.index + 1, this.length - 1));
},
/**
* Move the image with relative offsets
*
* @param {Number} offsetX
* @param {Number} offsetY (optional)
*/
move: function (offsetX, offsetY) {
var image = this.image;
this.moveTo(
isUndefined(offsetX) ? offsetX : image.left + num(offsetX),
isUndefined(offsetY) ? offsetY : image.top + num(offsetY)
);
},
/**
* Move the image to an absolute point
*
* @param {Number} x
* @param {Number} y (optional)
*/
moveTo: function (x, y) {
var image = this.image;
var changed = false;
// If "y" is not present, its default value is "x"
if (isUndefined(y)) {
y = x;
}
x = num(x);
y = num(y);
if (this.isViewed && !this.isPlayed && this.options.movable) {
if (isNumber(x)) {
image.left = x;
changed = true;
}
if (isNumber(y)) {
image.top = y;
changed = true;
}
if (changed) {
this.renderImage();
}
}
},
/**
* Zoom the image with a relative ratio
*
* @param {Number} ratio
* @param {Boolean} hasTooltip (optional)
* @param {jQuery Event} _event (private)
*/
zoom: function (ratio, hasTooltip, _event) {
var image = this.image;
ratio = num(ratio);
if (ratio < 0) {
ratio = 1 / (1 - ratio);
} else {
ratio = 1 + ratio;
}
this.zoomTo(image.width * ratio / image.naturalWidth, hasTooltip, _event);
},
/**
* Zoom the image to an absolute ratio
*
* @param {Number} ratio
* @param {Boolean} hasTooltip (optional)
* @param {jQuery Event} _event (private)
* @param {Boolean} _zoomable (private)
*/
zoomTo: function (ratio, hasTooltip, _event, _zoomable) {
var options = this.options;
var minZoomRatio = 0.01;
var maxZoomRatio = 100;
var image = this.image;
var width = image.width;
var height = image.height;
var originalEvent;
var newWidth;
var newHeight;
var offset;
var center;
ratio = max(0, ratio);
if (isNumber(ratio) && this.isViewed && !this.isPlayed && (_zoomable || options.zoomable)) {
if (!_zoomable) {
minZoomRatio = max(minZoomRatio, options.minZoomRatio);
maxZoomRatio = min(maxZoomRatio, options.maxZoomRatio);
ratio = min(max(ratio, minZoomRatio), maxZoomRatio);
}
if (ratio > 0.95 && ratio < 1.05) {
ratio = 1;
}
newWidth = image.naturalWidth * ratio;
newHeight = image.naturalHeight * ratio;
if (_event && (originalEvent = _event.originalEvent)) {
offset = this.$viewer.offset();
center = originalEvent.touches ? getTouchesCenter(originalEvent.touches) : {
pageX: _event.pageX || originalEvent.pageX || 0,
pageY: _event.pageY || originalEvent.pageY || 0
};
// Zoom from the triggering point of the event
image.left -= (newWidth - width) * (
((center.pageX - offset.left) - image.left) / width
);
image.top -= (newHeight - height) * (
((center.pageY - offset.top) - image.top) / height
);
} else {
// Zoom from the center of the image
image.left -= (newWidth - width) / 2;
image.top -= (newHeight - height) / 2;
}
image.width = newWidth;
image.height = newHeight;
image.ratio = ratio;
this.renderImage();
if (hasTooltip) {
this.tooltip();
}
}
},
/**
* Rotate the image with a relative degree
*
* @param {Number} degree
*/
rotate: function (degree) {
this.rotateTo((this.image.rotate || 0) + num(degree));
},
/**
* Rotate the image to an absolute degree
* https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function#rotate()
*
* @param {Number} degree
*/
rotateTo: function (degree) {
var image = this.image;
degree = num(degree);
if (isNumber(degree) && this.isViewed && !this.isPlayed && this.options.rotatable) {
image.rotate = degree;
this.renderImage();
}
},
/**
* Scale the image
* https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function#scale()
*
* @param {Number} scaleX
* @param {Number} scaleY (optional)
*/
scale: function (scaleX, scaleY) {
var image = this.image;
var changed = false;
// If "scaleY" is not present, its default value is "scaleX"
if (isUndefined(scaleY)) {
scaleY = scaleX;
}
scaleX = num(scaleX);
scaleY = num(scaleY);
if (this.isViewed && !this.isPlayed && this.options.scalable) {
if (isNumber(scaleX)) {
image.scaleX = scaleX;
changed = true;
}
if (isNumber(scaleY)) {
image.scaleY = scaleY;
changed = true;
}
if (changed) {
this.renderImage();
}
}
},
/**
* Scale the abscissa of the image
*
* @param {Number} scaleX
*/
scaleX: function (scaleX) {
this.scale(scaleX, this.image.scaleY);
},
/**
* Scale the ordinate of the image
*
* @param {Number} scaleY
*/
scaleY: function (scaleY) {
this.scale(this.image.scaleX, scaleY);
},
// Play the images
play: function () {
var options = this.options;
var $player = this.$player;
var load = $.proxy(this.loadImage, this);
var list = [];
var total = 0;
var index = 0;
var playing;
if (!this.isShown || this.isPlayed) {
return;
}
if (options.fullscreen) {
this.requestFullscreen();
}
this.isPlayed = true;
$player.addClass(CLASS_SHOW);
this.$items.each(function (i) {
var $this = $(this);
var $img = $this.find(SELECTOR_IMG);
var $image = $('<img src="' + $img.data('originalUrl') + '" alt="' + $img.attr('alt') + '">');
total++;
$image.addClass(CLASS_FADE).toggleClass(CLASS_TRANSITION, options.transition);
if ($this.hasClass(CLASS_ACTIVE)) {
$image.addClass(CLASS_IN);
index = i;
}
list.push($image);
$image.one(EVENT_LOAD, {
filled: false
}, load);
$player.append($image);
});
if (isNumber(options.interval) && options.interval > 0) {
playing = $.proxy(function () {
this.playing = setTimeout(function () {
list[index].removeClass(CLASS_IN);
index++;
index = index < total ? index : 0;
list[index].addClass(CLASS_IN);
playing();
}, options.interval);
}, this);
if (total > 1) {
playing();
}
}
},
// Stop play
stop: function () {
if (!this.isPlayed) {
return;
}
if (this.options.fullscreen) {
this.exitFullscreen();
}
this.isPlayed = false;
clearTimeout(this.playing);
this.$player.removeClass(CLASS_SHOW).empty();
},
// Enter modal mode (only available in inline mode)
full: function () {
var options = this.options;
var $image = this.$image;
var $list = this.$list;
if (!this.isShown || this.isPlayed || this.isFulled || !options.inline) {
return;
}
this.isFulled = true;
this.$body.addClass(CLASS_OPEN);
this.$button.addClass(CLASS_FULLSCREEN_EXIT);
if (options.transition) {
$image.removeClass(CLASS_TRANSITION);
$list.removeClass(CLASS_TRANSITION);
}
this.$viewer.addClass(CLASS_FIXED).removeAttr('style').css('z-index', options.zIndex);
this.initContainer();
this.viewer = $.extend({}, this.container);
this.renderList();
this.initImage($.proxy(function () {
this.renderImage(function () {
if (options.transition) {
setTimeout(function () {
$image.addClass(CLASS_TRANSITION);
$list.addClass(CLASS_TRANSITION);
}, 0);
}
});
}, this));
},
// Exit modal mode (only available in inline mode)
exit: function () {
var options = this.options;
var $image = this.$image;
var $list = this.$list;
if (!this.isFulled) {
return;
}
this.isFulled = false;
this.$body.removeClass(CLASS_OPEN);
this.$button.removeClass(CLASS_FULLSCREEN_EXIT);
if (options.transition) {
$image.removeClass(CLASS_TRANSITION);
$list.removeClass(CLASS_TRANSITION);
}
this.$viewer.removeClass(CLASS_FIXED).css('z-index', options.zIndexInline);
this.viewer = $.extend({}, this.parent);
this.renderViewer();
this.renderList();
this.initImage($.proxy(function () {
this.renderImage(function () {
if (options.transition) {
setTimeout(function () {
$image.addClass(CLASS_TRANSITION);
$list.addClass(CLASS_TRANSITION);
}, 0);
}
});
}, this));
},
// Show the current ratio of the image with percentage
tooltip: function () {
var options = this.options;
var $tooltip = this.$tooltip;
var image = this.image;
var classes = [
CLASS_SHOW,
CLASS_FADE,
CLASS_TRANSITION
].join(' ');
if (!this.isViewed || this.isPlayed || !options.tooltip) {
return;
}
$tooltip.text(round(image.ratio * 100) + '%');
if (!this.tooltiping) {
if (options.transition) {
if (this.fading) {
$tooltip.trigger(EVENT_TRANSITIONEND);
}
$tooltip.addClass(classes);
forceReflow($tooltip[0]);
$tooltip.addClass(CLASS_IN);
} else {
$tooltip.addClass(CLASS_SHOW);
}
} else {
clearTimeout(this.tooltiping);
}
this.tooltiping = setTimeout($.proxy(function () {
if (options.transition) {
$tooltip.one(EVENT_TRANSITIONEND, $.proxy(function () {
$tooltip.removeClass(classes);
this.fading = false;
}, this)).removeClass(CLASS_IN);
this.fading = true;
} else {
$tooltip.removeClass(CLASS_SHOW);
}
this.tooltiping = false;
}, this), 1000);
},
// Toggle the image size between its natural size and initial size
toggle: function () {
if (this.image.ratio === 1) {
this.zoomTo(this.initialImage.ratio, true);
} else {
this.zoomTo(1, true);
}
},
// Reset the image to its initial state
reset: function () {
if (this.isViewed && !this.isPlayed) {
this.image = $.extend({}, this.initialImage);
this.renderImage();
}
},
// Update viewer when images changed
update: function () {
var $this = this.$element;
var $images = this.$images;
var indexes = [];
var index;
if (this.isImg) {
// Destroy viewer if the target image was deleted
if (!$this.parent().length) {
return this.destroy();
}
} else {
this.$images = $images = $this.find(SELECTOR_IMG);
this.length = $images.length;
}
if (this.isBuilt) {
$.each(this.$items, function (i) {
var img = $(this).find('img')[0];
var image = $images[i];
if (image) {
if (image.src !== img.src) {
indexes.push(i);
}
} else {
indexes.push(i);
}
});
this.$list.width('auto');
this.initList();
if (this.isShown) {
if (this.length) {
if (this.isViewed) {
index = $.inArray(this.index, indexes);
if (index >= 0) {
this.isViewed = false;
this.view(max(this.index - (index + 1), 0));
} else {
this.$items.eq(this.index).addClass(CLASS_ACTIVE);
}
}
} else {
this.$image = null;
this.isViewed = false;
this.index = 0;
this.image = null;
this.$canvas.empty();
this.$title.empty();
}
}
}
},
// Destroy the viewer
destroy: function () {
var $this = this.$element;
if (this.options.inline) {
this.unbind();
} else {
if (this.isShown) {
this.unbind();
}
$this.off(EVENT_CLICK, this.start);
}
this.unbuild();
$this.removeData(NAMESPACE);
},