纯前端实现 App Store 卡片展开效果
共 4784字,需浏览 10分钟
·
2021-04-17 07:05
↓推荐关注↓
今集我们会模仿 App Store 这个卡片展开效果:
本来想只用 HTML 和 CSS 去制作,但最后都要运用到 JavaScript,那我们立即开始吧。
HTML 的部分
打开 CodePen 编辑器,首先建立 HTML 结构。新增一个 <div>
,class 是 card
,里面新增图片,图片来源使用 Unsplash
网站的随机图片。再加入标题,文字例如是 APP OF THE DAY。
然后新增一个 <div>
,class 是 content-wrapper
,里面再新增一个 <div>
,class 是 content
。为什么需要两层 <div>
呢,我们稍后会介绍。
里面新增内文,生成一些假字,然后用 <p>
这个段落标签整理好它们。
HTML 结构大致上是这样,然后到 CSS 的部份。
CSS 的部分
新增 :root
选择器,设定基础文字大小为 15px
。字体 font-family
设定为 Helvetica
。
然后定义一些变量,由于这个效果适合在手机的宽度上展示,所以我会将网页宽度设定为 480px
,定义 --body-width
是 480px
。然后定义卡片的大小,--card-width
定义为 420px
,--card-height
定义为 280px
。
再定义图片的高度,未展开的图片高度 --img-height
定义为 226px
,展开后的图片高度 --img-height-expanded
定义为 320px
。最后,背景颜色设定为深灰色。
加入 body
选择器,设定宽度为 var(--body-width)
,背景颜色设定为浅灰色,margin
设定为 auto
,这样就会将 body
置中。
然后将内容左右置中,设定 display
是 flex
,flex-direction
设定为 column
,align-items
设定为 center
,然后 min-height
设定为 100vh
。最顶和最底加一点 padding
,设定为 1rem 0
。
加入 .card
选择器,宽度设定为 var(--card-width)
,高度设定为 var(--card-height)
,背景颜色设定为白色。
暂时看不到白色的背景,是因为这张图片的大小比起 card
容器更大完全遮盖了。新增 .card img
选择器,将 display
设定为 block
,宽度设定为 100%
,然后高度设定为 var(--img-height)
,这时候会发现图片的比例不对,加入 object-fit: cover
这个设定就可以让图片按比例缩放填满。
回到 .card
的设定,加入圆角,border-radius
设定为 1rem
。再加点阴影,颜色是非常浅的灰色就可以了。
然后会发现图片的左上角和右上角并没有圆角设定,我们可以在 .card
中加入 overflow: hidden
,让 .card
将超出的部份遮盖,将图片变成圆角。不过由于在打开 .card
的时候,圆角会变为直角,而 overflow
并不能套用动画的,所以在这里不会用上 overflow
,而是在 img
中直接将左上角和右上角套用圆角设定。
然后设定标题字的样式,加入 .card h4
选择器,将 margin
设定为 0
,文字大小设定为 1.5rem
;字体设定为粗体;加入 padding
,上下设定为 0.8rem
,左右设定为 1.2rem
;背景颜色设定为白色。
我想文字在垂直方向更加置中,设定 line-height
为 2rem
,然后将字距缩小一点,letter-spacing
设定为 -0.5px
,再将 padding-bottom
设定为 0
,就不会遮着 .card
的圆角了。
接下来是设定内文的部份,分别加入 .card .content-wrapper
与 .card .content-wrapper .content
选择器。
为什么我们会分为 .content-wrapper
和 .content
呢?因为当点击卡片展开的时候,会通过 JavaScript 计算并设定内文容器的高度,所以我们会将展开的动画套用到外层,而高度的设定就会套用到内层。
这样当关闭卡片的时候,就不用再将高度的设定还原,其实重点是可以少写一些 JavaScript,让 CSS 去完成它。
先设定 .content
的样式,加入 padding
,设定为上下 0
,左右 1.2rem
;背景颜色设定为白色;然后 overflow
设定为 auto
,这样当内文高于容器所设定的高度时,页面就可以卷动。而容器的高度会在稍后 JavaScript 那边控制。
内文段落的样式也调整一下,加入 .card p
选择器,将文字大小设定为 1.2rem
,行高设定为 1.5rem
就可以了。
然后到 .content-wrapper
,设定高度是 0
,再加入 overflow: hidden
,这样超出容器的部份就会隐藏。
现在卡片的样式就差不多了,在开始做展开功能之前,先在 HTML 中再复制多几个卡片。为了展示不同的随机图片,我会在 Unsplash 的网址中加入不同的参数,在后面加上 1, 2, 3, 4,就可以了。
然后到 .card
选择器上,加入 margin: 1rem 0
将卡片分开一点。
JavaScript 的部分
接下来处理卡片展开的部份,我会通过在点击卡片时,加入一个 class
去改变它的样式,从而达到展开的效果。为了减少编写 JavaScript 的数量,我会加入 jQuery,打开 Settings,在 JS 的部份加入 jQuery,然后点击 Save & Close。
在 JavaScript 的部份,加入 .card
的 on('click')
事件。定义一个变量,card
赋值 e.currentTarget
,这里获取到的就是所点击的 .card
的本身。
然后加入 $(card).toggleClass('active')
,这样当 card 没有 active
class 的时候就会加上,有的时候就会移除,达到展开与收起的效果。
CSS 样式的调整
现在可以回到 CSS 的部份,设定 active
状态下 card
的样式。加入 4 个选择器,分别是 .card.active
移动卡片的位置,.card.active h4
标题的样式,.card.active img
图片的样式,以及 .card.active .content-wrapper
展开内文。
第一步想做的是展开内文,在 .content-wrapper
选择器内,加入 height: 100vh
,然后点击卡片试试:
可以看到内文已成功展开,再来是移动卡片,其实除了移动,我们还会放大一点卡片,以填充左右的宽度。
在 .card
选择器内加入 transform
,先设定 translateY()
,向上移动多少呢,其实需要计算元素与顶部的距离,才能将它紧贴着顶部,我们先暂时设定为 -300px
,稍后再计算。
在后面再加入 scale()
将卡片放大一点,放大的比例是 body
宽度除以卡片宽度,即 480
除以 420
,transform-origin
中心点设定为 50% 0
,点击一下,可以看到已经有八成的效果了。
先做一些样式的微调,展开的状态下,移除圆角的设定,同时也移除图片的圆角设定。然后将图片拉高一些:
再加入一些动画过渡的设定,回到 .card
选择器,加入 transition: .3s all
,加速度是 cubic-bezier(0, 1, 0.95, 1.05)
:
可以打开开发者工具,看到这个加速度的线性图表:
你可以试试拉动一下这条曲线,就可以达到不同的加速度效果,我这个设定就可以做到它打开和收起时有一点回弹的效果。
计算位移
回到 JavaScript 的部份,我们要计算卡片与顶部的距离。定义变量 card_offset_scrolltop
,将卡片的 offset().top
减去 window.scrollTop()
,即卷动距离。然后将计算结果用 CSS 变量的形式设定到卡片上,变量名称改为 --data-offset-top
,设定值乘以 -1
并加上 px
单位。
回到 CSS 的部份,将 .card.active
内 transform
的 translateY()
里面的 -300px
替换成 var(--data-offset-top)
,再点击测试一下:
可以成功定位到页顶了。
继续优化
你以为已经做完了?不要停,继续优化余下的部份。
现在在展开卡片后,卷动页面的话会穿帮,所以要在打开卡片后,暂停 body
的卷动。
在 JavaScript 的部份,判断如果卡片有 active
这个 class,即是打开的状态下,在 body
上加入 noscroll
这个 class;反之就移除 noscroll
这个 class。
打开 CSS 的部份,加入 body.noscroll
选择器,设定 overflow: hidden
,这样就可以达到防止卷动的效果了。
然后再处理内文卷动的部份,在 JavaScript 那边,定义 height
变量,赋值为 window.height()
。要计算内文部份的高度,就将 window
高度减去图片高度,再减去标题高度就可以了。
然后将这个高度套用到 .content
容器上,测试一下:
为什么会卷不到底的呢?这样就代表高度的计算有错误,但计算有什么错呢?
给三秒钟时间你想一想,3、2、1。
这是由于我们的卡片,通过 transform
的 scale()
放大了,所以在计算图片与标题高度的时候,要乘以放大比例。
定义一个变量名为 ratio
,赋值为 480/420
,分别将图片与标题高度乘以 ratio
,而最后,又要将总数除以这个 ratio
,再测试一下:
今次高度就正确了。
再做最后的一些微调,卷动的时候会发现 APP OF THE DAY 的下方 padding
稍为少了一点,在 .card.active h4
内加入 padding-bottom: .8rem
:
然后内文出现的时候加一些渐入效果,在 .content-wrapper
内加入透明度设定 opacity: .8
,transition: .3s all ease-out
:
再在展开的 .content-wrapper
内加入 opacity: 1
,transition: .3s all ease-in
,这样在打开与收起的时候就会有不同的加速度效果。
我们来看看这个案例的完成效果
以上,就是今集要介绍的全部内容。
这个案例的源代码:https://codepen.io/stevenlei/pen/poyNGNP
转自:CodingStartup 起码课
https://juejin.cn/post/6917479303572226061