17

image

一种引起浏览者探索兴趣的方法是,页面打开之后并不马上把所有内容都呈现给用户,而是隐藏其中的一部分内容,其他内容则需要用户交互之后才展示出来。这种方式很合适那些小众的、要营造艺术氛围的网站,通过特效来展现后续内容,有一种与用户对话的感觉。本作品就是采用这样的方式,当页面加载之后先把图片遮住,然后当鼠标移动到元素之上时,图片才展现出来。

效果预览

按下右侧的“点击预览”按钮可以在当前页面预览,点击链接可以全屏预览。

https://codepen.io/comehope/pen/MWejLqY

源代码下载

每日前端实战系列的全部源代码请从 github 下载:

https://github.com/comehope/front-end-daily-challenges

代码解读

一、DOM 结构

容器名为 .container,其中包含一个名为 .item 的元素。
.item 元素则包含 3 个子元素,.picture 表示图片本身,.title 是图片上的文字,.mask 是用来制作遮罩效果的元素。
作品完成时,会有多个 .item 元素,但此时我们先只展示 1 张图片,待效果完成之后,再增加其他图片。

<div class="container">
    <div class="item">
        <img class="picture" src="images/toggle.png">
        <span class="title">Toggle</span>
        <div class="mask"></div>
    </div>
</div>

本作品用到的4张图片可从下列地址下载。
https://assets.codepen.io/947...
https://assets.codepen.io/947...
https://assets.codepen.io/947...
https://assets.codepen.io/947...

二、基础布局

设置页面背景色为深灰色,令容器居中。

body {
    background-color: #222;
    margin: 0;
    height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
}

设置图片尺寸,用相对单位 em

.item {
    width: 18em;
    height: 12em;
}

.item .picture {
    width: 100%;
}

效果如下图:
image

三、图片遮罩特效

因为先处理遮罩效果,所以把暂时用不到的文字隐藏起来,避免干扰。

.item .title {
    display: none;
}

利用 .mask 元素为图片增加遮罩。遮罩大小是 20em * 20em 的一个大圆,背景色先暂用半透明的醒目的黄色,便于在开发过程中观察。

.item {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
}

.item .mask {
    position: absolute;
    width: 20em;
    height: 20em;
    background-color: hsla(60, 100%, 50%, 0.7);
    border-radius: 50%;
}

效果如下图:
image

上面只是测试了遮罩的大小,把刚才的代码注释掉,改用 box-shadow 实现我们真正需要的遮罩效果。这个遮罩层尺寸是 50em * 50em,远远大于图片本身,但它的大部分区域是内阴影,在内阴影之内才透出遮罩下方的图片来。
内阴影的尺寸是 15em,这是内阴影的半径,所以内阴影的直径是 30em,用遮罩元素的宽高 50em 减去遮罩的 30em,剩下的就是 20em,和刚才测试的遮罩大小是一样的。

.item .mask {
    /*width: 20em;*/
    /*height: 20em;*/
    /*background-color: hsla(60, 100%, 50%, 0.7);*/
    width: 50em;
    height: 50em;
    color: hsla(60, 100%, 50%, 0.7);
    box-shadow: inset 0 0 0 15em;
}

效果如下图:
image

加上鼠标悬停效果试一下。注意,这里元素上的内阴影尺寸设置为 25em,这是内阴影的半径尺寸,那么阴影的直径就是 50em,和遮罩本身的尺寸是一样大的,这表示在默认情况下,整张图片都被内阴影遮住了;而鼠标悬停时,内阴影变小,就显示出了遮罩下方的图片。另外为遮罩层增加了 pointer-events: none 属性,它的作用是避免遮罩层响应鼠标事件。

.item .mask {
    box-shadow: inset 0 0 0 25em;
    transition: box-shadow 0.3s;
    pointer-events: none;
}

.item:hover .mask {
    box-shadow: inset 0 0 0 15em;
}

再下来制作鼠标滑动时遮罩跟随的效果。
先把遮罩移到图片的左上方。遮罩的高是 50emtop: -25em 就是令遮罩的水平中线与图片顶边对齐;同理,left: -25em 则是令遮罩的垂直中线与图片的左边对齐,两者叠加,就是遮罩的中心与图片的左上角对齐。

.item .mask {
    top: -25em;
    left: -25em;
}

增加脚本,为 .item 元素绑定 mousemove 事件,令鼠标在 .item 元素上滑动时,带动 .mask 元素滑动。

window.onload = init

function init() {
    let items = document.querySelectorAll('.item')
    items.forEach((item) => {
        item.addEventListener('mousemove', e => {
            let mask = item.querySelector('.mask')
            mask.style.transform = 'translate(' + e.offsetX + 'px, ' + e.offsetY + 'px)'
        })
    })
}

至此,主要的效果已经完成了,接下来再增强一下效果。
稍加大图片的原始尺寸,在鼠标悬停时恢复图片大小,这样的效果是在鼠标进入图片区域时,图片能“扭曲抖动”一下,加强互动的效果。

.item .picture {
    transform: scale(1.1);
    transition: 0.3s;
}

.item:hover .picture {
    transform: scale(1);
}

鼠标悬停和滑动效果完成,下面这几行代码是一些收尾工作。
通过 overflow: hidden 属性隐藏掉图片之外的部分、容器加一点圆角、遮罩的颜色改用不透明的灰色。

.container {
    border-radius: 0.3em;
}

.item {
    overflow: hidden;
}

.item .mask {
    /*color: hsla(60, 100%, 50%, 0.7);*/
    color: #333;
}

效果如下图:
image

四、文字布局和特效

接下来处理文字。
先把文字显示出来,除了注释掉 display: none 之外,还要设置它的 z-index,令它显示在遮罩层的上方,再有也要取消它的鼠标事件,防止它影响鼠标滑动效果。

.item .title {
    /*display: none;*/
    position: absolute;
    color: #777;
    z-index: 1;
    pointer-events: none;
}

设置文字样式。

.item .title {
    font-family: sans-serif;
    font-weight: bold;
    text-transform: uppercase;
}

增加文字特效,当鼠标滑入图片时,隐藏文字。

.item .title {
    transition: 0.2s;
}

.item:hover .title {
    opacity: 0;
}

效果如下图:
image

至此,单图图片的效果都完成了。

五、将特效应用到多张图片

增加多个 .item 元素。

<div class="container">
    <div class="item">
        <img class="picture" src="images/toggle.png">
        <span class="title">Toggle</span>
        <div class="mask"></div>
    </div>
    <!-- 此处再增加3个 .item 元素,代码略 -->
</div>

grid 布局把图片排列成田字格形状。

.container {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    grid-gap: 1em;
}

效果如下图:
image

大功告成!

关于作者

张偶,网络笔名 @comehope,20世纪末触网,被 Web 的无穷魅力所俘获,自此始终战斗在 Web 开发第一线。

《前端每日实战》专栏是我近年来实践项目式学习的笔记,以项目驱动学习,展现从灵感闪现到代码实现的完整过程,亦可作为前端开发的练手习题和开发参考。

拙作《CSS3 艺术》一书已由人民邮电出版社出版,全彩印刷,用100多个生动美观的实例,系统地剖析了 CSS 与视觉效果相关的重要语法,并含有近10小时的视频演示讲解。京东/天猫/当当有售。


comehope
9.4k 声望14.4k 粉丝

💯累计分享170+个项目💯