在 Typora 中,图片一旦插入,最大宽度只能是页面宽度,而无法放大查看,有时,原图片比较大,放到 Typora 中,完全无法看清内容。
这里使用 LightBox
,来实现类似 WPS 放大预览图片的功能。
通过更改部分代码实现自定义的需求:双击图片查看大图,鼠标移动到大图上面,按住 Ctrl
+ 滚动滑轮
实现对图片进行放大缩小。点击✖️或者底部空白关闭大图。
当前环境:
-
Typora 版本:1.2.4
-
LightBox 版本:2.11.4
# 效果预览
# 宽图
把鼠标移动到图片上,按 Ctrl
+ 滚动滑轮
实现图片放大缩小,在加载一些比较宽的图片时,滚动滑轮,会扩大图片在竖方向的显示范围,当图片在整个平面铺满全屏时,不再进行扩大显示范围,效果如下。
# 窄图
在加载一些比较窄的图片时,滚动滑轮,会扩大图片在横方向的显示范围,当图片在整个平面铺满全屏时,不再进行扩大显示范围,效果如下。
# 正常图片
正常显示。
# 下载 LightBox
可以从 Github 下载,链接👉https://github.com/lokesh/lightbox2
下载之后解压,然后跟着下面步骤移动文件并对文件代码进行修改,以符合 typora
使用。
# 移动文件
找到 typora
的安装路径( D:\Program Files\Typora
),在如下目录下新建一个 lightbox
文件夹,并把解压后的 dist
目录下的文件夹放到里面。
这里的新建路径可以自定义,但一定要把
dist
目录下的文件夹复制到同一个路径下,供后面window.html
调用。
下面要修改的 js 和 css 文件都在对应的 js 和 css 文件夹中
# 修改 lightbox.js 文件
# 为 img 标签绑定双击事件
修改 enable
函数,增添部分如下代码。
// Loop through anchors and areamaps looking for either data-lightbox attributes or rel attributes | |
// that contain 'lightbox'. When these are clicked, start lightbox. | |
Lightbox.prototype.enable = function() { | |
var self = this; | |
$('body').on('click', 'a[rel^=lightbox], area[rel^=lightbox], a[data-lightbox], area[data-lightbox]', function(event) { | |
self.start($(event.currentTarget)); | |
return false; | |
}); | |
// 给 img 标签加上双击属性,要排除本就是双击放大的图片。 | |
$('body').on('dblclick', "img:not([class='lb-image'])", function(event) { | |
self.start($(event.currentTarget)); | |
return false; | |
}); | |
}; |
# 修改 start
函数
修改 start
函数中的属性值,换成 img
对应属性,并重置 image
的 zoom
属性。
关于为何在
start
函数中重置image
的zoom
属性:
- 我们在
mousewheel
事件中给image
增加了的zoom
属性,用于放大缩小图像LightBox
在打开图像后,可以通过点击✖️关闭图像,也可以点击空白关闭图像,如果不进行重置,下一次打开图像会跟随上一次打开图像的zoom
属性
// Show overlay and lightbox. If the image is part of a set, add siblings to album array. | |
Lightbox.prototype.start = function($link) { | |
var self = this; | |
var $window = $(window); | |
$window.on('resize', $.proxy(this.sizeOverlay, this)); | |
this.sizeOverlay(); | |
this.album = []; | |
var imageNumber = 0; | |
// 重置 image 的 zoom 属性 | |
self.$image.css("zoom", "100%") | |
// 修改 alt 和 link | |
function addToAlbum($link) { | |
self.album.push({ | |
alt: $link.attr('data-alt') || $link.attr('alt'), | |
link: $link.attr('href') || $link.attr('src'), | |
title: $link.attr('data-title') || $link.attr('title') | |
}); | |
} |
还有另外一个属性 data-lightbox
,img 没有该属性,因此这里不使用,lightbox 中有一段判断是否有该属性的代码,修改不存在该属性时的逻辑代码(注释掉原有的,添加 addToAlbum($link)
)
# 修改滚动滑轮放大图片
增加 mousewheel
事件,当按住 Ctrl
+ 滚动滑轮
实现给放大的图片再进行放大缩小。
可以搜索 mousedown
,在它的后面添加:
// 给放大的图片另外绑定滚轮放大缩小事件 | |
this.$container.on('mousewheel', function(event) { | |
// 检查是否按下了 Ctrl 键 | |
if(event.ctrlKey) { | |
event.delta = (event.originalEvent.wheelDelta) ? event.originalEvent.wheelDelta / 120 : -(event.originalEvent.detail || 0) / 3; | |
var zoom = self.$image.css("zoom") | |
zoom = zoom ? zoom * 100 : 100; | |
zoom += event.delta * 10; | |
if(zoom > 0) | |
self.$image.css("zoom", zoom + "%") | |
var imageWidth = self.$image.width(); | |
var imageHeight = self.$image.height(); | |
imageWidth = imageWidth * zoom * 0.01; | |
imageHeight = imageHeight * zoom * 0.01; | |
self.sizeContainer(imageWidth, imageHeight); | |
// 阻止默认事件和事件冒泡 | |
event.preventDefault(); | |
return false; | |
} | |
// 如果没有按下 Ctrl 键,不执行缩放操作 | |
}); |
# 修改 sizeContainer
函数
对比屏幕大小,当图片大小超过屏幕大小时,显示滚动条,隐藏图片部分内容。
Lightbox.prototype.sizeContainer = function(imageWidth, imageHeight) { | |
var self = this; | |
var oldWidth = this.$outerContainer.outerWidth(); | |
var oldHeight = this.$outerContainer.outerHeight(); | |
var newWidth = imageWidth + this.containerPadding.left + this.containerPadding.right + this.imageBorderWidth.left + this.imageBorderWidth.right; | |
var newHeight = imageHeight + this.containerPadding.top + this.containerPadding.bottom + this.imageBorderWidth.top + this.imageBorderWidth.bottom; | |
// 获取屏幕宽度和高度 | |
var screenWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth; | |
var screenHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight; | |
function postResize() { | |
// 当新尺寸适合屏幕时才调整 lb-dataContainer 尺寸 | |
if (newWidth <= screenWidth) { | |
self.$lightbox.find('.lb-dataContainer').width(newWidth); | |
} | |
self.$lightbox.find('.lb-prevLink').height(newHeight); | |
self.$lightbox.find('.lb-nextLink').height(newHeight); | |
// Set focus on one of the two root nodes so keyboard events are captured. | |
self.$overlay.trigger('focus'); | |
self.showImage(); | |
} | |
// 更改 outerContainer 的宽和高,为后续在 css 文件中使用 overflow 做铺垫。 | |
if ((oldWidth !== newWidth || oldHeight !== newHeight)) { | |
var newStyles = {}; | |
if (newWidth < screenWidth) { | |
newStyles.width = newWidth * 1.01; | |
} | |
// × 关闭图标高度为 30 | |
if (newHeight < screenHeight-35) { | |
newStyles.height = newHeight * 1.01; | |
} | |
if (newStyles.width || newStyles.height) { | |
this.$outerContainer.animate(newStyles, this.options.resizeDuration, 'swing', function() { | |
postResize(); | |
}); | |
} else { | |
postResize(); | |
} | |
}; |
👻记得保存哦~
# 修改 lightbox.css
文件
给 lb-outerContainer
增加 overflow: auto;
.lb-outerContainer { | |
position: relative; | |
*zoom: 1; | |
width: 250px; | |
height: 250px; | |
margin: 0 auto; | |
border-radius: 4px; | |
overflow: auto; | |
/* Background color behind image. | |
This is visible during transitions. */ | |
background-color: white; | |
} |
# 修改 window.html
文件
位置: Typora安装目录\resources\window.html
(这个文件实际就是 Typora 的主界面),如果找不到对应位置,直接在安装目录下搜索 window.html
# 引入 css 文件
可以搜索 </head>
,在它的前面添加:
<!-- 在<head>标签里引入lightbox.css --> | |
<link rel="stylesheet" href="./style/lightbox/css/lightbox.css" crossorigin="anonymous"> |
# 引入 js 文件
可以搜索 frame.js
,然后在它的后面添加:
<!-- 在文件的最后,引入lightbox.js --> | |
<script type="text/javascript" src="./style/lightbox/js/lightbox.js" defer="defer"></script> |
修改完成,保存。
🍻重新打开 typora
,快去试试吧
# 完整代码
下面给出对应完整的 js 代码
lightbox.js
/*! | |
* Lightbox v2.11.4 | |
* by Lokesh Dhakar | |
* | |
* More info: | |
* http://lokeshdhakar.com/projects/lightbox2/ | |
* | |
* Copyright Lokesh Dhakar | |
* Released under the MIT license | |
* https://github.com/lokesh/lightbox2/blob/master/LICENSE | |
* | |
* @preserve | |
*/ | |
// Uses Node, AMD or browser globals to create a module. | |
(function (root, factory) { | |
if (typeof define === 'function' && define.amd) { | |
// AMD. Register as an anonymous module. | |
define(['jquery'], factory); | |
} else if (typeof exports === 'object') { | |
// Node. Does not work with strict CommonJS, but | |
// only CommonJS-like environments that support module.exports, | |
// like Node. | |
module.exports = factory(require('jquery')); | |
} else { | |
// Browser globals (root is window) | |
root.lightbox = factory(root.jQuery); | |
} | |
}(this, function ($) { | |
function Lightbox(options) { | |
this.album = []; | |
this.currentImageIndex = void 0; | |
this.init(); | |
// options | |
this.options = $.extend({}, this.constructor.defaults); | |
this.option(options); | |
} | |
// Descriptions of all options available on the demo site: | |
// http://lokeshdhakar.com/projects/lightbox2/index.html#options | |
Lightbox.defaults = { | |
albumLabel: 'Image %1 of %2', | |
alwaysShowNavOnTouchDevices: false, | |
fadeDuration: 600, | |
fitImagesInViewport: true, | |
imageFadeDuration: 600, | |
// maxWidth: 800, | |
// maxHeight: 600, | |
positionFromTop: 50, | |
resizeDuration: 700, | |
showImageNumberLabel: true, | |
wrapAround: false, | |
disableScrolling: false, | |
/* | |
Sanitize Title | |
If the caption data is trusted, for example you are hardcoding it in, then leave this to false. | |
This will free you to add html tags, such as links, in the caption. | |
If the caption data is user submitted or from some other untrusted source, then set this to true | |
to prevent xss and other injection attacks. | |
*/ | |
sanitizeTitle: false | |
}; | |
Lightbox.prototype.option = function(options) { | |
$.extend(this.options, options); | |
}; | |
Lightbox.prototype.imageCountLabel = function(currentImageNum, totalImages) { | |
return this.options.albumLabel.replace(/%1/g, currentImageNum).replace(/%2/g, totalImages); | |
}; | |
Lightbox.prototype.init = function() { | |
var self = this; | |
// Both enable and build methods require the body tag to be in the DOM. | |
$(document).ready(function() { | |
self.enable(); | |
self.build(); | |
}); | |
}; | |
// Loop through anchors and areamaps looking for either data-lightbox attributes or rel attributes | |
// that contain 'lightbox'. When these are clicked, start lightbox. | |
Lightbox.prototype.enable = function() { | |
var self = this; | |
$('body').on('click', 'a[rel^=lightbox], area[rel^=lightbox], a[data-lightbox], area[data-lightbox]', function(event) { | |
self.start($(event.currentTarget)); | |
return false; | |
}); | |
// 给 img 标签加上双击属性,要排除本就是双击放大的图片 | |
$('body').on('dblclick', "img:not([class='lb-image'])", function(event) { | |
self.start($(event.currentTarget)); | |
return false; | |
}); | |
}; | |
// Build html for the lightbox and the overlay. | |
// Attach event handlers to the new DOM elements. click click click | |
Lightbox.prototype.build = function() { | |
if ($('#lightbox').length > 0) { | |
return; | |
} | |
var self = this; | |
// The two root notes generated, #lightboxOverlay and #lightbox are given | |
// tabindex attrs so they are focusable. We attach our keyboard event | |
// listeners to these two elements, and not the document. Clicking anywhere | |
// while Lightbox is opened will keep the focus on or inside one of these | |
// two elements. | |
// | |
// We do this so we can prevent propogation of the Esc keypress when | |
// Lightbox is open. This prevents it from intefering with other components | |
// on the page below. | |
// | |
// Github issue: https://github.com/lokesh/lightbox2/issues/663 | |
$('<div id="lightboxOverlay" tabindex="-1" class="lightboxOverlay"></div><div id="lightbox" tabindex="-1" class="lightbox"><div class="lb-outerContainer"><div class="lb-container"><img class="lb-image" src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==" alt=""/><div class="lb-nav"><a class="lb-prev" role="button" tabindex="0" aria-label="Previous image" href="" ></a><a class="lb-next" role="button" tabindex="0" aria-label="Next image" href="" ></a></div><div class="lb-loader"><a class="lb-cancel" role="button" tabindex="0"></a></div></div></div><div class="lb-dataContainer"><div class="lb-data"><div class="lb-details"><span class="lb-caption"></span><span class="lb-number"></span></div><div class="lb-closeContainer"><a class="lb-close" role="button" tabindex="0"></a></div></div></div></div>').appendTo($('body')); | |
// Cache jQuery objects | |
this.$lightbox = $('#lightbox'); | |
this.$overlay = $('#lightboxOverlay'); | |
this.$outerContainer = this.$lightbox.find('.lb-outerContainer'); | |
this.$container = this.$lightbox.find('.lb-container'); | |
this.$image = this.$lightbox.find('.lb-image'); | |
this.$nav = this.$lightbox.find('.lb-nav'); | |
// Store css values for future lookup | |
this.containerPadding = { | |
top: parseInt(this.$container.css('padding-top'), 10), | |
right: parseInt(this.$container.css('padding-right'), 10), | |
bottom: parseInt(this.$container.css('padding-bottom'), 10), | |
left: parseInt(this.$container.css('padding-left'), 10) | |
}; | |
this.imageBorderWidth = { | |
top: parseInt(this.$image.css('border-top-width'), 10), | |
right: parseInt(this.$image.css('border-right-width'), 10), | |
bottom: parseInt(this.$image.css('border-bottom-width'), 10), | |
left: parseInt(this.$image.css('border-left-width'), 10) | |
}; | |
// Attach event handlers to the newly minted DOM elements | |
this.$overlay.hide().on('click', function() { | |
self.end(); | |
return false; | |
}); | |
this.$lightbox.hide().on('click', function(event) { | |
if ($(event.target).attr('id') === 'lightbox') { | |
self.end(); | |
} | |
}); | |
this.$outerContainer.on('click', function(event) { | |
if ($(event.target).attr('id') === 'lightbox') { | |
self.end(); | |
} | |
return false; | |
}); | |
this.$lightbox.find('.lb-prev').on('click', function() { | |
if (self.currentImageIndex === 0) { | |
self.changeImage(self.album.length - 1); | |
} else { | |
self.changeImage(self.currentImageIndex - 1); | |
} | |
return false; | |
}); | |
this.$lightbox.find('.lb-next').on('click', function() { | |
if (self.currentImageIndex === self.album.length - 1) { | |
self.changeImage(0); | |
} else { | |
self.changeImage(self.currentImageIndex + 1); | |
} | |
return false; | |
}); | |
/* | |
Show context menu for image on right-click | |
There is a div containing the navigation that spans the entire image and lives above of it. If | |
you right-click, you are right clicking this div and not the image. This prevents users from | |
saving the image or using other context menu actions with the image. | |
To fix this, when we detect the right mouse button is pressed down, but not yet clicked, we | |
set pointer-events to none on the nav div. This is so that the upcoming right-click event on | |
the next mouseup will bubble down to the image. Once the right-click/contextmenu event occurs | |
we set the pointer events back to auto for the nav div so it can capture hover and left-click | |
events as usual. | |
*/ | |
this.$nav.on('mousedown', function(event) { | |
if (event.which === 3) { | |
self.$nav.css('pointer-events', 'none'); | |
self.$lightbox.one('contextmenu', function() { | |
setTimeout(function() { | |
this.$nav.css('pointer-events', 'auto'); | |
}.bind(self), 0); | |
}); | |
} | |
}); | |
// 给放大的图片另外绑定滚轮放大缩小事件 | |
this.$container.on('mousewheel', function(event) { | |
// 检查是否按下了 Ctrl 键 | |
if(event.ctrlKey) { | |
event.delta = (event.originalEvent.wheelDelta) ? event.originalEvent.wheelDelta / 120 : -(event.originalEvent.detail || 0) / 3; | |
var zoom = self.$image.css("zoom") | |
zoom = zoom ? zoom * 100 : 100; | |
zoom += event.delta * 10; | |
if(zoom > 0) | |
self.$image.css("zoom", zoom + "%") | |
var imageWidth = self.$image.width(); | |
var imageHeight = self.$image.height(); | |
imageWidth = imageWidth * zoom * 0.01; | |
imageHeight = imageHeight * zoom * 0.01; | |
// 如果你希望容器的大小根据图片大小变化而自动调整(而不是固定为视口大小),可以取消注释下面的代码 | |
self.sizeContainer(imageWidth, imageHeight); | |
// 阻止默认事件和事件冒泡 | |
event.preventDefault(); | |
} | |
// 如果没有按下 Ctrl 键,不执行缩放操作 | |
}); | |
this.$lightbox.find('.lb-loader, .lb-close').on('click keyup', function(e) { | |
// If mouse click OR 'enter' or 'space' keypress, close LB | |
if ( | |
e.type === 'click' || (e.type === 'keyup' && (e.which === 13 || e.which === 32))) { | |
self.end(); | |
return false; | |
} | |
}); | |
}; | |
// Show overlay and lightbox. If the image is part of a set, add siblings to album array. | |
Lightbox.prototype.start = function($link) { | |
var self = this; | |
var $window = $(window); | |
$window.on('resize', $.proxy(this.sizeOverlay, this)); | |
this.sizeOverlay(); | |
this.album = []; | |
var imageNumber = 0; | |
// 重置 image 的 zoom 属性 | |
self.$image.css("zoom", "100%") | |
function addToAlbum($link) { | |
self.album.push({ | |
alt: $link.attr('data-alt') || $link.attr('alt'), | |
link: $link.attr('href') || $link.attr('src'), | |
title: $link.attr('data-title') || $link.attr('title') | |
}); | |
} | |
// Support both data-lightbox attribute and rel attribute implementations | |
var dataLightboxValue = $link.attr('data-lightbox'); | |
var $links; | |
if (dataLightboxValue) { | |
$links = $($link.prop('tagName') + '[data-lightbox="' + dataLightboxValue + '"]'); | |
for (var i = 0; i < $links.length; i = ++i) { | |
addToAlbum($($links[i])); | |
if ($links[i] === $link[0]) { | |
imageNumber = i; | |
} | |
} | |
} else { | |
// if ($link.attr('rel') === 'lightbox') { | |
// // If image is not part of a set | |
// addToAlbum($link); | |
// } else { | |
// // If image is part of a set | |
// $links = $($link.prop('tagName') + '[rel="' + $link.attr('rel') + '"]'); | |
// for (var j = 0; j < $links.length; j = ++j) { | |
// addToAlbum($($links[j])); | |
// if ($links[j] === $link[0]) { | |
// imageNumber = j; | |
// } | |
// } | |
// } | |
addToAlbum($link); | |
} | |
// Position Lightbox | |
var top = $window.scrollTop() + this.options.positionFromTop; | |
var left = $window.scrollLeft(); | |
this.$lightbox.css({ | |
top: top + 'px', | |
left: left + 'px' | |
}).fadeIn(this.options.fadeDuration); | |
// Disable scrolling of the page while open | |
if (this.options.disableScrolling) { | |
$('body').addClass('lb-disable-scrolling'); | |
} | |
this.changeImage(imageNumber); | |
}; | |
// Hide most UI elements in preparation for the animated resizing of the lightbox. | |
Lightbox.prototype.changeImage = function(imageNumber) { | |
var self = this; | |
var filename = this.album[imageNumber].link; | |
var filetype = filename.split('.').slice(-1)[0]; | |
var $image = this.$lightbox.find('.lb-image'); | |
// Disable keyboard nav during transitions | |
this.disableKeyboardNav(); | |
// Show loading state | |
this.$overlay.fadeIn(this.options.fadeDuration); | |
$('.lb-loader').fadeIn('slow'); | |
this.$lightbox.find('.lb-image, .lb-nav, .lb-prev, .lb-next, .lb-dataContainer, .lb-numbers, .lb-caption').hide(); | |
this.$outerContainer.addClass('animating'); | |
// When image to show is preloaded, we send the width and height to sizeContainer() | |
var preloader = new Image(); | |
preloader.onload = function() { | |
var $preloader; | |
var imageHeight; | |
var imageWidth; | |
var maxImageHeight; | |
var maxImageWidth; | |
var windowHeight; | |
var windowWidth; | |
$image.attr({ | |
'alt': self.album[imageNumber].alt, | |
'src': filename | |
}); | |
$preloader = $(preloader); | |
$image.width(preloader.width); | |
$image.height(preloader.height); | |
var aspectRatio = preloader.width / preloader.height; | |
windowWidth = $(window).width(); | |
windowHeight = $(window).height(); | |
// Calculate the max image dimensions for the current viewport. | |
// Take into account the border around the image and an additional 10px gutter on each side. | |
maxImageWidth = windowWidth - self.containerPadding.left - self.containerPadding.right - self.imageBorderWidth.left - self.imageBorderWidth.right - 20; | |
maxImageHeight = windowHeight - self.containerPadding.top - self.containerPadding.bottom - self.imageBorderWidth.top - self.imageBorderWidth.bottom - self.options.positionFromTop - 70; | |
/* | |
Since many SVGs have small intrinsic dimensions, but they support scaling | |
up without quality loss because of their vector format, max out their | |
size inside the viewport. | |
*/ | |
if (filetype === 'svg') { | |
if (aspectRatio >= 1) { | |
imageWidth = maxImageWidth; | |
imageHeight = parseInt(maxImageWidth / aspectRatio, 10); | |
} else { | |
imageWidth = parseInt(maxImageHeight / aspectRatio, 10); | |
imageHeight = maxImageHeight; | |
} | |
$image.width(imageWidth); | |
$image.height(imageHeight); | |
} else { | |
// Fit image inside the viewport. | |
if (self.options.fitImagesInViewport) { | |
// Check if image size is larger then maxWidth|maxHeight in settings | |
if (self.options.maxWidth && self.options.maxWidth < maxImageWidth) { | |
maxImageWidth = self.options.maxWidth; | |
} | |
if (self.options.maxHeight && self.options.maxHeight < maxImageHeight) { | |
maxImageHeight = self.options.maxHeight; | |
} | |
} else { | |
maxImageWidth = self.options.maxWidth || preloader.width || maxImageWidth; | |
maxImageHeight = self.options.maxHeight || preloader.height || maxImageHeight; | |
} | |
// Is the current image's width or height is greater than the maxImageWidth or maxImageHeight | |
// option than we need to size down while maintaining the aspect ratio. | |
if ((preloader.width > maxImageWidth) || (preloader.height > maxImageHeight)) { | |
if ((preloader.width / maxImageWidth) > (preloader.height / maxImageHeight)) { | |
imageWidth = maxImageWidth; | |
imageHeight = parseInt(preloader.height / (preloader.width / imageWidth), 10); | |
$image.width(imageWidth); | |
$image.height(imageHeight); | |
} else { | |
imageHeight = maxImageHeight; | |
imageWidth = parseInt(preloader.width / (preloader.height / imageHeight), 10); | |
$image.width(imageWidth); | |
$image.height(imageHeight); | |
} | |
} | |
} | |
self.sizeContainer($image.width(), $image.height()); | |
}; | |
// Preload image before showing | |
preloader.src = this.album[imageNumber].link; | |
this.currentImageIndex = imageNumber; | |
}; | |
// Stretch overlay to fit the viewport | |
Lightbox.prototype.sizeOverlay = function() { | |
var self = this; | |
/* | |
We use a setTimeout 0 to pause JS execution and let the rendering catch-up. | |
Why do this? If the `disableScrolling` option is set to true, a class is added to the body | |
tag that disables scrolling and hides the scrollbar. We want to make sure the scrollbar is | |
hidden before we measure the document width, as the presence of the scrollbar will affect the | |
number. | |
*/ | |
setTimeout(function() { | |
self.$overlay | |
.width($(document).width()) | |
.height($(document).height()); | |
}, 0); | |
}; | |
// Animate the size of the lightbox to fit the image we are showing | |
// This method also shows the the image. | |
Lightbox.prototype.sizeContainer = function(imageWidth, imageHeight) { | |
var self = this; | |
var oldWidth = this.$outerContainer.outerWidth(); | |
var oldHeight = this.$outerContainer.outerHeight(); | |
var newWidth = imageWidth + this.containerPadding.left + this.containerPadding.right + this.imageBorderWidth.left + this.imageBorderWidth.right; | |
var newHeight = imageHeight + this.containerPadding.top + this.containerPadding.bottom + this.imageBorderWidth.top + this.imageBorderWidth.bottom; | |
// 获取屏幕宽度和高度 | |
var screenWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth; | |
var screenHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight; | |
function postResize() { | |
// 当新尺寸适合屏幕时才调整 lb-dataContainer 尺寸 | |
if (newWidth <= screenWidth) { | |
self.$lightbox.find('.lb-dataContainer').width(newWidth); | |
} | |
self.$lightbox.find('.lb-prevLink').height(newHeight); | |
self.$lightbox.find('.lb-nextLink').height(newHeight); | |
// Set focus on one of the two root nodes so keyboard events are captured. | |
self.$overlay.trigger('focus'); | |
self.showImage(); | |
} | |
// 更改 outerContainer 的宽和高 | |
if ((oldWidth !== newWidth || oldHeight !== newHeight)) { | |
var newStyles = {}; | |
if (newWidth < screenWidth) { | |
newStyles.width = newWidth * 1.01; | |
} | |
// × 关闭图标高度为 30 | |
if (newHeight < screenHeight-35) { | |
newStyles.height = newHeight * 1.01; | |
} | |
if (newStyles.width || newStyles.height) { | |
this.$outerContainer.animate(newStyles, this.options.resizeDuration, 'swing', function() { | |
postResize(); | |
}); | |
} else { | |
postResize(); | |
} | |
} | |
}; | |
// Display the image and its details and begin preload neighboring images. | |
Lightbox.prototype.showImage = function() { | |
this.$lightbox.find('.lb-loader').stop(true).hide(); | |
this.$lightbox.find('.lb-image').fadeIn(this.options.imageFadeDuration); | |
this.updateNav(); | |
this.updateDetails(); | |
this.preloadNeighboringImages(); | |
this.enableKeyboardNav(); | |
}; | |
// Display previous and next navigation if appropriate. | |
Lightbox.prototype.updateNav = function() { | |
// Check to see if the browser supports touch events. If so, we take the conservative approach | |
// and assume that mouse hover events are not supported and always show prev/next navigation | |
// arrows in image sets. | |
var alwaysShowNav = false; | |
try { | |
document.createEvent('TouchEvent'); | |
alwaysShowNav = (this.options.alwaysShowNavOnTouchDevices) ? true : false; | |
} catch (e) {} | |
this.$lightbox.find('.lb-nav').show(); | |
if (this.album.length > 1) { | |
if (this.options.wrapAround) { | |
if (alwaysShowNav) { | |
this.$lightbox.find('.lb-prev, .lb-next').css('opacity', '1'); | |
} | |
this.$lightbox.find('.lb-prev, .lb-next').show(); | |
} else { | |
if (this.currentImageIndex > 0) { | |
this.$lightbox.find('.lb-prev').show(); | |
if (alwaysShowNav) { | |
this.$lightbox.find('.lb-prev').css('opacity', '1'); | |
} | |
} | |
if (this.currentImageIndex < this.album.length - 1) { | |
this.$lightbox.find('.lb-next').show(); | |
if (alwaysShowNav) { | |
this.$lightbox.find('.lb-next').css('opacity', '1'); | |
} | |
} | |
} | |
} | |
}; | |
// Display caption, image number, and closing button. | |
Lightbox.prototype.updateDetails = function() { | |
var self = this; | |
// Enable anchor clicks in the injected caption html. | |
// Thanks Nate Wright for the fix. @https://github.com/NateWr | |
if (typeof this.album[this.currentImageIndex].title !== 'undefined' && | |
this.album[this.currentImageIndex].title !== '') { | |
var $caption = this.$lightbox.find('.lb-caption'); | |
if (this.options.sanitizeTitle) { | |
$caption.text(this.album[this.currentImageIndex].title); | |
} else { | |
$caption.html(this.album[this.currentImageIndex].title); | |
} | |
$caption.fadeIn('fast'); | |
} | |
if (this.album.length > 1 && this.options.showImageNumberLabel) { | |
var labelText = this.imageCountLabel(this.currentImageIndex + 1, this.album.length); | |
this.$lightbox.find('.lb-number').text(labelText).fadeIn('fast'); | |
} else { | |
this.$lightbox.find('.lb-number').hide(); | |
} | |
this.$outerContainer.removeClass('animating'); | |
this.$lightbox.find('.lb-dataContainer').fadeIn(this.options.resizeDuration, function() { | |
return self.sizeOverlay(); | |
}); | |
}; | |
// Preload previous and next images in set. | |
Lightbox.prototype.preloadNeighboringImages = function() { | |
if (this.album.length > this.currentImageIndex + 1) { | |
var preloadNext = new Image(); | |
preloadNext.src = this.album[this.currentImageIndex + 1].link; | |
} | |
if (this.currentImageIndex > 0) { | |
var preloadPrev = new Image(); | |
preloadPrev.src = this.album[this.currentImageIndex - 1].link; | |
} | |
}; | |
Lightbox.prototype.enableKeyboardNav = function() { | |
this.$lightbox.on('keyup.keyboard', $.proxy(this.keyboardAction, this)); | |
this.$overlay.on('keyup.keyboard', $.proxy(this.keyboardAction, this)); | |
}; | |
Lightbox.prototype.disableKeyboardNav = function() { | |
this.$lightbox.off('.keyboard'); | |
this.$overlay.off('.keyboard'); | |
}; | |
Lightbox.prototype.keyboardAction = function(event) { | |
var KEYCODE_ESC = 27; | |
var KEYCODE_LEFTARROW = 37; | |
var KEYCODE_RIGHTARROW = 39; | |
var keycode = event.keyCode; | |
if (keycode === KEYCODE_ESC) { | |
// Prevent bubbling so as to not affect other components on the page. | |
event.stopPropagation(); | |
this.end(); | |
} else if (keycode === KEYCODE_LEFTARROW) { | |
if (this.currentImageIndex !== 0) { | |
this.changeImage(this.currentImageIndex - 1); | |
} else if (this.options.wrapAround && this.album.length > 1) { | |
this.changeImage(this.album.length - 1); | |
} | |
} else if (keycode === KEYCODE_RIGHTARROW) { | |
if (this.currentImageIndex !== this.album.length - 1) { | |
this.changeImage(this.currentImageIndex + 1); | |
} else if (this.options.wrapAround && this.album.length > 1) { | |
this.changeImage(0); | |
} | |
} | |
}; | |
// Closing time. :-( | |
Lightbox.prototype.end = function() { | |
this.disableKeyboardNav(); | |
$(window).off('resize', this.sizeOverlay); | |
this.$lightbox.fadeOut(this.options.fadeDuration); | |
this.$overlay.fadeOut(this.options.fadeDuration); | |
if (this.options.disableScrolling) { | |
$('body').removeClass('lb-disable-scrolling'); | |
} | |
}; | |
return new Lightbox(); | |
})); |