1 前言
大家好,我是心锁,一枚23届准毕业生。
近期我正在尝试完成所谓的「拐角轮播」,目前拐角的部分已经完成了百分之七八十,所以我们来解析一下如何实现这种CSS 3D效果
2 演示
这里是体验链接,给各位大佬呈上https://grinzero.github.io/magic-design-react/swiper/corner-swiper
当然效果有些残缺哈......
别急着走嘛,我们虽然没有原图在拐角处实现的那么丝滑,但是也算是实现了方案中的3D容器方案
那我究竟是怎么把卡片3D化的呢?
(补充码上掘金的简易版本) 代码片段
3 3D折叠容器实现
众所周知,起码截止目前为止,CSS并不支持把单一一个页面元素折叠成曲面或者说部分折叠。
在这个基础上,我们必定需要使用到多个元素叠加。
3.1 基本框架
我们得到这样一份基本框架
代码语言:javascript复制<div className="swiper-container">
<div
className="mg-w-full mg-h-full mg-relative corner-swiper"
ref={listRef}
>
这里放SwiperItem
</div>
</div>
我们暂时不需要知道swiper-container
属性,我们现在仅需要关注SwiperItem的上层,我们在这里实现3D容器,而在swiper-container
只实现Swiper的滑动功能。
我们在corner-swiper真正实现3D容器
3.2 容器的切割拼接
众所周知,圆形是可以由无数个正多边形拼合形成的
而我实现3D容器的思路就是切割,我目前将3D容器切成了两份,通过absolute叠加在一起,这里的实现并不麻烦,是absolute
的应用,就不贴代码了。
再接着就是切割~
我们要用到的CSS属性是clip-path
,如图的三份元素切片的实现,我们借助clip-path
是比较容易实现的
.swiperElement1{
clip-path:polygon(0% 0, 20% 0, 20% 100%, 0% 100%);
}
.swiperElement2{
clip-path:polygon(20% 0, 26% 0, 26% 100%, 20% 100%);
}
.swiperElement3{
clip-path:polygon(26% 0, 100% 0, 100% 100%, 26% 100%);
}
可以看到,我们通过polygon
函数将各个元素按照百分比切割成了三份,这三个元素可以完整地拼成原本的容器
当然,可能是浏览器原因,拼接之间会存在一条白线
我们可以通过translateX
属性从右到左渐进的 1px来隐藏这条白线
.swiperElement1{
clip-path:polygon(0% 0, 20% 0, 20% 100%, 0% 100%);
transform:translateX(2px);
}
.swiperElement2{
clip-path:polygon(20% 0, 26% 0, 26% 100%, 20% 100%);
transform:translateX(1px);
}
.swiperElement3{
clip-path:polygon(26% 0, 100% 0, 100% 100%, 26% 100%);
transform:translateX(0px);
}
那么现在3D容器的切割拼接就完毕了,接下来我们要将容器3D化。
3.3 容器3D化
说到3D化,我们将使用到transform3D系列的属性,我可以先列举一下我们用到的属性
- perspective
- perspective-origin
- transform-style: preserve-3d
- transform: rotateX、rotateY
- transform-origin
3.3.1 perspective
perspective,透视,是CSS 3D的基础,transform-style
和perspective
任意一个属性不配置都不会产生3D效果。
根据MDN的解释,perspective指定了观察者(也就是我们)和屏幕z之间的距离
可以在这个链接感受一下https://developer.mozilla.org/zh-CN/docs/Web/CSS/perspective
3.3.2 perspective-origin
而perspective-origin操控透视的视角
我们可以通过该属性控制拐角的弧度
需要注意,两个透视相关需要放在父容器
3.3.3 transform-style
其中transform-style
最为简单,指定transform变换的类型是3D模式
.swiperElement1{
clip-path:polygon(0% 0, 20% 0, 20% 100%, 0% 100%);
transform:translateX(2px);
transform-style:preserve-3d;
}
.swiperElement2{
clip-path:polygon(20% 0, 26% 0, 26% 100%, 20% 100%);
transform:translateX(1px);
transform-style:preserve-3d;
}
.swiperElement3{
clip-path:polygon(26% 0, 100% 0, 100% 100%, 26% 100%);
transform:translateX(0px);
transform-style:preserve-3d;
}
3.3.4 transform: rotateX、rotateY
而transform
中的rotateX、rotateY都是3D旋转,这两种旋转在3d模式是什么表现呢?我这里给些例子
- rotateX
这里是45deg角度的X轴旋转,我们可以看到x轴的表现如图
- rotateY
而这是y轴旋转,我们会发现目前y轴在最中心,从展示角度来讲这是正确的,但是实际中我们需要把原点移动。
这也就引申出来了transform-origin
3.3.5 transform-origin
在上述例子中,我们改变的是swiperElement1
,而swiperElement1
是在20%的地方做了切割
所以我们把y轴对准20%,这样我们就可以将y轴搬运到20%这里
代码语言:javascript复制.swiperElement1{
clip-path:polygon(0% 0, 20% 0, 20% 100%, 0% 100%);
transform:translateX(2px);
transform-style:preserve-3d;
transform-origin:20%;
}
3.4 再次拼接
我们会发现,如果我们给swiper1
,swiper2
配置了3D化之后,第三个元素和前边就接不上了。
这是很显然的问题,因为前两个元素拼接之后,第二个元素的最右方z轴实际上是上移了。
所以我们需要计算出Z轴移动了多少,将前边所有的元素或者后边的元素移动回去
对于我们正在做的demo来说,目前代码是这样的
代码语言:javascript复制.swiperElement1{
clip-path:polygon(0% 0, 20% 0, 20% 100%, 0% 100%);
transform:translateX(2px);
transform-style:preserve-3d;
transform:translateX(2px) rotateY(-45deg);
transform-origin:20%;
}
.swiperElement2{
clip-path:polygon(20% 0, 26% 0, 26% 100%, 20% 100%);
transform:translateX(1px);
transform-style:preserve-3d;
transform:translateX(1px) rotateY(-22.5deg);
transform-origin:20%;
}
.swiperElement3{
clip-path:polygon(26% 0, 100% 0, 100% 100%, 26% 100%);
transform:translateX(0px);
transform-style:preserve-3d;
transform:translateX(-3px) translateZ(17.25px);
}
可以看到swiperElement3
,存在属性transform:translateX(-3px) translateZ(17.25px);
这其中的translateX(-3px) translateZ(17.25px)
是怎么计算的呢?
我们俯视得到透视图
那么现在我们只需要建模出X的计算公式和Z的计算公式,我们可以假设全长为“1”。
注意上图的所有蓝色字体均指x轴宽,而不是直接长度。
...
看到这里你是不是以为我算出来了
....
不,我不太想算了
咱把思路已经给得很明白了,亲爱的读者你动动手啊,直接告诉我答案,我要白嫖答案
4 数据建模
虽然我没有对三个元素叠加进行建模,不过我做了两个元素的建模
如图,将clip-path
的部分属性和transform-origin
通过CSS变量var(--position)
来绑定。
同时在之前提到的swiper-container
层通过translateX来控制Swiper的「切换current操作」
之所以最终没有做3个面乃至于多个面的建模,一方面是麻烦,一方面是实现效果上。
三个元素的叠加其实效果并没有说特别好,而往上走到5、6或者更多元素叠加,其实对于网页的性能是会产生一定压力的。
那么,我们最终得到~
5 总结
那么我们实践完这个堪称变态的拐角轮播,学习到了什么呢?
- 一个复杂动画的实现需要一定的数学建模
- CSS 3D的作用条件以及X、Y、Z轴的实际作用展现
clip-path
与不同的orgin
的实际作用展现
当然~如果亲只想试试这个效果,打开https://grinzero.github.io/magic-design-react/即可体验
(文档刚刚写,组件也不是很完善,还很简陋,见谅见谅)