3

(图片审核原因,所以请到我的微博中查看开发过程中的7张截图:https://weibo.com/6063054508/...

今年年初时我偶然看到了下面这张图片,顺手把它记在了微博里,近日抽时间把它们制作成了交互页面,本文记录了开发的过程。

效果预览

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

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

源代码下载

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

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

代码解读

一、准备素材

先准备好开发用的3张图片,文件名分别为 html.pngcss.pngjs.png,html 类比于人的骨架,css 类比于人的外貌,js 类比于人的神经系统:
https://assets.codepen.io/947795/html.png
https://assets.codepen.io/947795/css.png
https://assets.codepen.io/947795/js.png

二、DOM 结构

容器名为 .person,表示一个人的外貌,它包含一个子元素 .inside,表示这个人被透视出的身体内部,就是骨骼、血管什么的啦。

<div class="person">
    <div class="inside"></div>
</div>

三、呈现人的正常状态

令元素在页面居中显示:

body {
    margin: 0;
    height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
}

把图片 css.png 作为容器 .person 的背景图:

.person {
    width: 320px;
    height: 537px;
    background-image: url(css.png);
}

效果如下图:
<图1>

四、呈现人的骨骼

把图片 html.png 作为元素 .person .inside 的背景图。

.person .inside {
    width: 100%;
    height: 100%;
    background-image: url(html.png);
    background-position: center;
}

效果如下图:
<图2>

为图片加上 5px 的灰色边框。

.person .inside {
    box-sizing: border-box;
    border: 5px solid gray;
    border-radius: 0.3em;
}

效果如下图:
<图3>

缩小边框的高度为 100px,并定位在容器的中上位置,这是未来上下滑动的透视窗。

.person {
    position: relative;
}

.person .inside {
    /*height: 100%;*/
    height: 100px;
    position: absolute;
    top: 25%;
}

效果如下图:
<图4>

仔细看上图,是有错误的,透视窗处于人的胸部,但透视出的区域是人的腰部。所以需要用 background-attachment: fixed; 把图片锁定在固定位置。请留意,这个属性值是整个作品的核心,否则不会有透视窗滑动的效果。

.person .inside {
    background-attachment: fixed;
}

效果如下图:
<图5>

至此,静态布局完成。

五、透视窗滑动效果

接下来处理透视窗跟随鼠标的滑动效果。

程序入口是 init 函数,在其中声明2个变量 personinside,分别代表它们对应的 DOM 元素。

window.onload = init

function init() {
    var person = document.querySelector('.person')
    var inside = document.querySelector('.person .inside')
}

person 元素绑定 mousemove 事件,当事件被触发时,更新 inside 元素的 top 属性值,形成滑动效果。

function init() {
    person.addEventListener('mousemove', function (e) {
        inside.style.top = getTopPosition(e.offsetY)
    });
}

top 的属性值通过 getTopPosition() 函数计算得来,其中 offset 值为透视窗高度的一半,意为透视窗在滑动时,鼠标指针位于透视窗的中间位置。在检测了滑动的位置没有跑到容器外面之后,返回位置,这里要加上单位 px

function getTopPosition(y) {
    const windowHeight = 100
    var offset = windowHeight / 2
    if(y < offset) return
    if(y > (Number.parseInt(window.getComputedStyle(person).height)) - offset) return
    return y - offset + 'px'
}

现在刷新页面,看到已经有滑动效果了,但是非常不流畅,这是因为子元素 .inside 位于容器 .person 之上,会先触发子元素的事件,为此,我们用 pointer-events: none; 禁止子元素响应鼠标事件。
另外,把鼠标指针显示为表示可移动的图标。

.person {
    cursor: move;
}

.person .inside {
    pointer-events: none;
}

至此,本作品的核心功能开发完成了。

六、切换透视图

接下来增加切换透视内容的功能。

在 DOM 中增加 2 个复选框,用于选择是透视 html(人体骨骼),还是透视 js(人体神经)。

<div class="selector">
    <input type="radio" name="options" value="html" id="html" checked>
    <label for="html">HTML</label>
    <input type="radio" name="options" value="js" id="js">
    <label for="js">JavaScript</label>
</div>

定义复选框的样式,这不是本作品的重点,用的也是普通的属性,就不作详细讲解了。

.selector {
    position: absolute;
    width: 9em;
    top: calc(50% - 8em / 2);
    left: 3em;
    display: grid;
    grid-template-columns: repeat(2, 1fr);
}

.selector input {
    width: 1.2em;
    height: 1.2em;
}

.selector label {
    font-size: 1.5em;
    font-family: sans-serif;
    cursor: pointer;
}

.selector input:checked + label {
    color: dodgerblue;
}

效果如下图:
<图6>

最后,为复选框绑定点击事件,当点击复选框时,更新 .inside 元素的背景图片。

function init() {
    var options = document.getElementsByName('options')

    options.forEach((option) => {
        option.addEventListener('click', (e) => {
            inside.style.backgroundImage = getImageUrl(e.target.value)
        })
    })
    
    function getImageUrl(opt) {
        return 'url(' + opt + '.png' + ')'
    }
}

下面是透视神经图的效果:
<图7>

大功告成!

关于作者

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

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

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


comehope
9.4k 声望14.4k 粉丝

💯累计分享170+个项目💯