在之前的教程中,我们学习了如何使用slick.js构建响应式图片库。今天,让我们构建一个类似但更完整的东西:一个带有可拖动特色图像/主幻灯片的响应式图片库和一个位于其顶部的响应式灯箱库。为了使目标元素可拖动,我们将利用 GSAP 的Draggable插件。
听起来像是一个很好的运动?
我们正在建造什么
这是我们要创建的组件:
See the Pen How to Build a Draggable Image Gallery (+ Custom Lightbox) With GSAP by Envato Tuts+ (@tutsplus) on CodePen.
请务必通过单击“打开灯箱” 按钮打开灯箱库。
1.包括所需的插件
如前所述,为了使特色图像成为可拖动元素,我们将使用GSAP,特别是它的 Draggable 插件。
您可以通过使用另一个插件甚至使用纯 javascript 事件来实现此功能
可选地,我们还将包括InertiaPlugin(以前称为 ThrowPropsPlugin),这是第二个 GSAP 插件,将在释放鼠标/触摸后应用基于动量的移动。值得注意的是,这是一个高级插件,您必须先注册成为 GSAP 会员才能决定使用它。在我们的例子中,我们将使用一个只能在本地和像codepen.io这样的域上运行的试用版(有关更多详细信息,请参阅演示的浏览器控制台)。
考虑到所有这些,我们将包含三个外部 JavaScript 文件。前两个是强制性的,而第三个是可选的。
2.定义 html 标记
为图库设置标记
我们将首先定义一个包装元素,该元素将包含:
缩略图和特色图像列表。两个列表都将包含相同的Unsplash图像。这些将具有相同的尺寸并且足够大以实现可拖动效果。
打开灯箱库的按钮。
默认情况下,将显示第一张主幻灯片。但是我们可以通过将is-active类附加到所需的幻灯片(列表)来配置该行为。
此外,所有精选图片都将保留其原始尺寸(1920 像素 x 1280 像素)。
这是我们画廊所需的结构:
<div class="gallery-wrapper">
<ul class="thumb-list">
<li class="is-active">
<img width="1920" height="1280" src="sports-car1.jpg" alt="">
</li>
<li>
<img width="1920" height="1280" src="sports-car2.jpg" alt="">
</li>
<li>
<img width="1920" height="1280" src="sports-car3.jpg" alt="">
</li>
<li>
<img width="1920" height="1280" src="sports-car4.jpg" alt="">
</li>
</ul>
<ul class="featured-list">
<li class="is-active">
<div class="featured-img" style="background-image: url(sports-car1.jpg); width: 1920px; height: 1280px;"></div>
</li>
<li>
<div class="featured-img" style="background-image: url(sports-car2.jpg); width: 1920px; height: 1280px;"></div>
</li>
<li>
<div class="featured-img" style="background-image: url(sports-car3.jpg); width: 1920px; height: 1280px;"></div>
</li>
<li>
<div class="featured-img" style="background-image: url(sports-car4.jpg); width: 1920px; height: 1280px;"></div>
</li>
</ul>
<button type="button" class="open-lightbox">Open Lightbox</button>
</div>
为灯箱库设置标记
接下来,我们将定义一个灯箱组件,其中包括:
包含上述 Unsplash 图像的列表。根据活动的主幻灯片,将出现相关的灯箱图像。
用于在幻灯片之间切换的导航箭头。
关闭按钮
这是我们的灯箱所需的结构:
<div class="lightbox">
<header class="lightbox-header">
<button type="button" class="close-lightbox" aria-label="Close lightbox">✕</button>
</header>
<div class="lightbox-dialog">
<section class="lightbox-content">
<ul class="lightbox-items">
<li>
<img src="sports-car1.jpg" alt="" width="1920" height="1280">
</li>
<li>
<img src="sports-car2.jpg" alt="" width="1920" height="1280">
</li>
<li>
<img src="sports-car3.jpg" alt="" width="1920" height="1280">
</li>
<li>
<img src="sports-car4.jpg" alt="" width="1920" height="1280">
</li>
</ul>
<button type="button" class="lightbox-control lightbox-control-next" aria-label="Next slide">
<svg aria-hidden="true" xmlns="https://www.w3.org/2000/svg" width="34" height="34" viewBox="0 0 24 24">
<path d="M5 3l3.057-3 11.943 12-11.943 12-3.057-3 9-9z" />
</svg>
</button>
<button type="button" class="lightbox-control lightbox-control-prev" aria-label="Previous slide">
<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="34" height="34" viewBox="0 0 24 24">
<path d="M16.67 0l2.83 2.829-9.339 9.175 9.339 9.167-2.83 2.829-12.17-11.996z" />
</svg>
</button>
</section>
</div>
</div>
3.指定主要样式
准备好标记后,我们将继续使用页面的主要样式。为简单起见,我将跳过介绍性/重置那些。另外,我不会优化或合并常见的 css 样式,因此您会更容易理解发生了什么。请务必通过单击演示的CSS选项卡来查看所有这些。
设置画廊布局
图库的最大宽度为 950 像素。
在大屏幕 (>750px) 上,我们将有两列。缩略图将出现在左侧,而特色图像将出现在右侧,如下所示:
请注意,缩略图将覆盖画廊宽度的四分之一,而特色图像将覆盖四分之三。
在小屏幕(≤750px)上,缩略图将位于特色图像下方,如下所示:
请注意,每个缩略图将覆盖父级宽度的四分之一。
以下是相关的样式:
.gallery-wrapper {
max-width: 950px;
padding: 0 15px;
margin: 0 auto;
display: grid;
grid-template-columns: 1fr 3fr;
grid-gap: 15px;
}
.gallery-wrapper .thumb-list {
display: grid;
grid-gap: 15px;
}
@media (max-width: 750px) {
.gallery-wrapper {
grid-template-columns: 1fr;
}
.gallery-wrapper .thumb-list {
grid-template-columns: repeat(4, 1fr);
order: 1;
}
}
精选幻灯片的可见性
默认情况下,除了活动幻灯片之外,所有精选幻灯片都将被隐藏。此外,一次只会出现一张特色幻灯片(活动的)。
以下是相关的样式:
.gallery-wrapper .featured-list li {
opacity: 0;
transition: opacity 0.25s;
}
.gallery-wrapper .featured-list li.is-active {
opacity: 1;
}
定位特色图片
在大屏幕上,两个图库列将具有与网格项相同的高度。特色图像将是绝对定位的元素并在其容器中居中。要查看它们的所有部分,我们必须拖动它们。
在小屏幕上,由于列是堆叠的,特色图像仍然是绝对定位的,我们应该为右列指定一个固定的高度。
以下是相关的样式:
.gallery-wrapper .featured-list {
position: relative;
overflow: hidden;
}
.gallery-wrapper .featured-list .featured-img {
background-size: cover;
background-repeat: no-repeat;
background-position: center;
z-index: 1 !important;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
@media (max-width: 750px) {
.gallery-wrapper .featured-list {
height: 340px;
}
}
指示活动和悬停状态
每次我们将鼠标悬停在缩略图上时,它的::before伪元素都会出现。这将具有浅蓝色背景并位于缩略图的顶部。
另一方面,活动缩略图将收到红色边框颜色。
以下是相关的样式:
/*CUSTOM VARIABLES HERE*/
.gallery-wrapper .thumb-list li {
position: relative;
cursor: pointer;
border: 4px solid var(--black);
}
.gallery-wrapper .thumb-list li:not(.is-active):hover::before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: var(--hovered-thumb);
}
.gallery-wrapper .thumb-list li.is-active {
border-color: var(--red);
}
设置灯箱样式
默认情况下,灯箱将被隐藏,仅当有人单击相应的号召性用语按钮时才会出现。
以下是有关灯箱样式的一些注意事项:
灯箱将是具有水平居中内容的固定位置元素。
导航和关闭按钮将是绝对定位的元素。
画廊将垂直居中,一次只会出现一张图片。这将取决于活动的特色幻灯片。
图像的最大高度将等于视口高度,并且它们的宽度将设置为auto.
以下是这些样式的一部分:
/*CUSTOM VARIABLES HERE*/
.lightbox {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
justify-content: center;
opacity: 0;
visibility: hidden;
z-index: 2;
transition: all 0.25s;
background: var(--black);
}
.lightbox.is-visible {
opacity: 1;
visibility: visible;
}
.lightbox-header {
position: absolute;
top: 0;
left: 0;
right: 0;
display: flex;
justify-content: flex-end;
padding: 5px 10px;
z-index: 2;
background: var(--lightbox-header);
}
.lightbox-dialog {
display: flex;
align-items: center;
}
.lightbox-items {
display: grid;
}
.lightbox-items li {
display: flex;
grid-column: 1;
grid-row: 1;
opacity: 0;
transition: opacity 0.25s;
}
.lightbox-items li.is-active {
opacity: 1;
}
.lightbox-items img {
width: auto;
max-height: 100vh;
}
4.添加javaScript
现在让我们为我们的组件赋予生命!
同样,为简单起见,我不会优化/合并 JavaScript 代码。随意获取适用于您的项目的代码部分。
更改图库幻灯片
每次单击缩略图时,我们都会执行以下操作:
is-active从预先存在的活动缩略图和特色图像中删除类。
查找当前活动缩略图的索引。
将is-active类分配给活动缩略图和索引与此缩略图的索引匹配的特色图像。
这是所需的代码:
const galleryWrapper = document.queryselector(".gallery-wrapper");
const thumbList = galleryWrapper.querySelector(".thumb-list");
const thumbItems = thumbList.querySelectorAll("li");
const featuredList = galleryWrapper.querySelector(".featured-list");
const isActiveClass = "is-active";
thumbItems.forEach((el) => {
el.addeventListener("click", () => {
thumbList.querySelector("li.is-active").classList.remove(isActiveClass);
featuredList.querySelector("li.is-active").classList.remove(isActiveClass);
let index = Array.from(thumbItems).indexOf(el);
el.classList.add(isActiveClass);
featuredList
.querySelector(`li:nth-child(${++index})`)
.classList.add(isActiveClass);
});
});
添加键盘支持
更进一步,我们将通过提供对键盘导航的支持来增强以前的功能。进一步来说:
每次按下向上( ↑ ) 或向下( ↓ )箭头键时,我们都会检索预先存在的活动缩略图。
如果按下向上箭头键,当前缩略图之前的缩略图将变为活动状态。如果没有任何此类缩略图,则最后一个缩略图将变为活动状态。
如果按下向下箭头键,当前缩略图之后的缩略图将变为活动状态。如果没有任何此类缩略图,则第一个缩略图将变为活动状态。
这是所需的代码:
...
document.addEventListener("keyup", (e) => {
if (e.keyCode === 38 || e.keyCode === 40) {
const activeThumb = thumbList.querySelector("li.is-active");
// up arrow
if (e.keyCode === 38) {
if (activeThumb.previousElementSibling) {
activeThumb.previousElementSibling.click();
} else {
thumbList.lastElementChild.click();
}
} else {
// down arrow
if (activeThumb.nextElementSibling) {
activeThumb.nextElementSibling.click();
} else {
thumbList.firstElementChild.click();
}
}
}
});
作为增强功能,您可以 在移动布局上切换左/右箭头的上/下 箭头
使特征图像可拖动
接下来,我们将使特色图像成为可拖动元素。如前所述,为此,我们将利用 GSAP 的 Draggable 插件。我们将通过它的create()方法实例化插件,并将以下两个参数传递给它:
我们要拖动的元素。
一个配置对象。在其中,我们将指定可拖动元素在效果期间应保持的边界。可选地,当我们加载了 InertiaPlugin 时,我们还将inertia在用户的鼠标/触摸释放后通过基于动量的属性请求。
下面是对应的代码:
const galleryWrapper = document.querySelector(".gallery-wrapper");
const featuredList = galleryWrapper.querySelector(".featured-list");
const featuredItems = featuredList.querySelectorAll("li");
Draggable.create(featuredImgs, {
bounds: featuredList,
inertia: true
});
当然,在这里,我们只介绍了插件功能的基本部分。您可以通过阅读文档和实现复杂的东西来更深入地了解。
打开灯箱
如前所述,只要我们单击“打开灯箱”按钮,灯箱就会出现。因此,单击后,我们将执行以下操作:
is-active从预先存在的活动灯箱图像中删除该类(如果有)。
通过类从body元素中移除垂直滚动条。overflow-y-hidden
查找当前活动的特色图像的索引。
将is-active类分配给其索引与此特色图像的索引匹配的灯箱图像。
is-visible通过课堂展示灯箱。
这是所需的代码
...
openLightbox.addEventListener("click", () => {
if (lightboxItems.querySelector("li.is-active")) {
lightboxItems.querySelector("li.is-active").classList.remove(isActiveClass);
}
body.classList.add(overflowYHiddenClass);
const el = featuredList.querySelector("li.is-active");
let index = Array.from(featuredItems).indexOf(el);
lightboxItems
.querySelector(`li:nth-child(n+${++index})`)
.classList.add(isActiveClass);
lightbox.classList.add(isVisibleClass);
});
关闭灯箱
关闭灯箱有两种不同的方式:
首先,通过单击.close-lightbox位于灯箱标题内的元素。
其次,通过Esc按键。
再次,这是所需的代码
...
document.addEventListener("click", (e) => {
if (e.target === closeLightbox) {
body.classList.remove(overflowYHiddenClass);
lightbox.classList.remove(isVisibleClass);
}
});
document.addEventListener("keyup", (e) => {
// Esc
if (document.querySelector(".lightbox.is-visible") && e.keyCode === 27) {
body.classList.remove(overflowYHiddenClass);
lightbox.classList.remove(isVisibleClass);
}
});
更改灯箱幻灯片
每次单击导航箭头时,我们都会执行以下操作:
获取当前活动的灯箱幻灯片的副本。
is-active从此幻灯片中删除课程。
检查以查看单击了哪个按钮。如果这是下一个,我们将把这个is-active类添加到活动的幻灯片后面的幻灯片中。如果没有这样的幻灯片,则第一个将收到此课程。
另一方面,如果那是前一个,我们将把类添加到is-active活动类之前的幻灯片中。如果没有这样的幻灯片,最后一个将收到这个类。
这是所需的代码:
...
for (const lightboxControl of lightboxControls) {
lightboxControl.addEventListener("click", (e) => {
const activeSlide = lightboxItems.querySelector("li.is-active");
activeSlide.classList.remove(isActiveClass);
if (e.currentTarget === lightboxNextControl) {
activeSlide.nextElementSibling
? activeSlide.nextElementSibling.classList.add(isActiveClass)
: lightboxItems.firstElementChild.classList.add(isActiveClass);
} else {
activeSlide.previousElementSibling
? activeSlide.previousElementSibling.classList.add(isActiveClass)
: lightboxItems.lastElementChild.classList.add(isActiveClass);
}
});
}
添加键盘支持
就像我们对图片库所做的那样,让我们通过添加对键盘导航的支持来使我们的灯箱更加强大。进一步来说:
如果灯箱可见,我们检查是否按下了左( ← ) 或右 ( → )箭头键。
如果按下左箭头键,我们将强制单击上一个导航控件。
同样,如果按下右箭头键,我们将强制单击下一个导航控件。
这是所需的代码:
...
document.addEventListener("keyup", (e) => {
if (
document.querySelector(".lightbox.is-visible") &&
(e.keyCode === 37 || e.keyCode === 39)
) {
// left arrow
if (e.keyCode === 37) {
lightboxPrevControl.click();
} else {
// next arrow
lightboxNextControl.click();
}
}
});
结论
伙计们,另一个练习已经结束了!感谢您的关注。希望您喜欢我们今天构建的内容,它让您深入了解如何将一些自定义代码与 GSAP 等流行插件的强大功能相结合。
- 为图库设置标记
- 为灯箱库设置标记
- 设置画廊布局
- 精选幻灯片的可见性
- 定位特色图片
- 指示活动和悬停状态
- 设置灯箱样式
- 更改图库幻灯片
- 添加键盘支持
- 使特征图像可拖动
- 打开灯箱
- 关闭灯箱
- 更改灯箱幻灯片
- 添加键盘支持