CSS 中带有曲线和 3D 运动的渐变边框(Nextjs 票证克隆)
这篇文章最初发表在我的博客上
更新:这是本文的日文版本
2020 年 10 月 27 日是 Next.js 的第一次全球用户大会,作为一名 React 开发人员,我对此感到非常兴奋,这就是为什么我在得知消息后立即注册的原因,但注册后发生的事情非常有趣,我收到了会议委员会的确认消息,网址为https://nextjs.org/conf/tickets/medhatdawoud,这是一张互动票,设计精良,动画效果很好,我要感谢团队设计和开发它,今天我们将对它进行克隆(用于学习目的)。
挑战
我们在这里面临不少挑战需要解决:
- 自行构建票据(✅ 将从预先创建的票据开始)
- 实现渐变边框。
- 左右画半圆。
- 根据光标移动实现动画。
这里还有其他 5 种方法可以实现这一点,你可以在读完本文后查看它们
执行
让我们一步一步地开始实现,因此最终的代码可以在这个github repo中找到,同时也可以找到其他挑战。
1. 创建票证本身
正如我们之前同意的那样,这将是准备好的,您可以在 repo 中找到整个代码,但这是 HTML:
<div class="ticket-visual_visual" id="ticket">
<div class="left"></div>
<div class="right"></div>
<div class="ticket-visual-wrapper">
<div class="ticket-visual_profile">
<div class="ticket-profile_profile">
<img
src="https://github.com/medhatdawoud.png"
alt="medhatdawoud"
class="ticket-profile_image"
/>
<div class="ticket-profile_text">
<p class="ticket-profile_name">Medhat Dawoud</p>
<p class="ticket-profile_username">
<span class="ticket-profile_githubIcon">
<img src="./github.svg" alt="" />
</span>
medhatdawoud
</p>
</div>
</div>
<div class="ticket-event">
<img src="./event-logos.png" />
</div>
</div>
<div class="ticket-visual_ticket-number-wrapper">
<div class="ticket-visual_ticket-number">№ 014747</div>
</div>
</div>
</div>
注意:event-logos.png
票的下半部分是我截取的屏幕截图,因为这不是我们今天的重点。
CSS 如下:
:root {
--size: 1;
--background: #000;
}
body {
background: var(--background);
color: white;
font-family: Arial, Helvetica, sans-serif;
}
* {
box-sizing: border-box;
}
.ticket-visual_visual {
width: 650px;
height: 320px;
margin: 100px auto;
position: relative;
transition: all 300ms cubic-bezier(0.03, 0.98, 0.53, 0.99) 0s;
border: 5px solid #fff;
}
.ticket-visual-wrapper {
width: 100%;
height: 100%;
}
.ticket-visual_profile {
padding: calc(39px * var(--size)) calc(155px * var(--size)) calc(
39px * var(--size)
) calc(58px * var(--size));
}
.ticket-profile_text {
margin: 0;
}
.ticket-profile_profile {
display: flex;
flex-direction: row;
}
.ticket-event {
margin-top: 25px;
margin-left: -10px;
}
.ticket-profile_image {
width: calc(82px * var(--size));
height: calc(82px * var(--size));
border-radius: 50%;
}
.ticket-profile_name {
font-size: calc(32px * var(--size));
margin: 10px 0 5px 20px;
font-weight: 700;
}
.ticket-profile_username {
margin: 0 0 5px 20px;
color: #8a8f98;
display: flex;
}
.ticket-profile_githubIcon img {
width: 18px;
height: 18px;
margin-right: 5px;
}
.ticket-visual_ticket-number-wrapper {
position: absolute;
right: 35px;
bottom: 0;
}
.ticket-visual_ticket-number {
transform: rotate(90deg) translateY(calc(100px * var(--size)));
transform-origin: bottom right;
font-size: calc(40px * var(--size));
font-weight: 700;
text-align: center;
padding-bottom: 35px;
width: calc(320px - 10px);
border-bottom: 2px dashed #333;
}
现在看起来如下:
2. 实现渐变边框
用于制作渐变或甚至将图像作为边框的第一个 CSS 属性是该border-image
属性,根据MDN ,该属性在包括 ie11 在内的所有浏览器上都得到了很好的支持。
使用它的唯一问题是它不支持,border-radius
所以很遗憾我们无法使用它,并且会采取一些变通措施来实现这一点。
这个想法主要是div
在另一个里面使用一个div
,我们把它们称为父 div 和子 div,您可以轻松地在我们的例子中添加图像或渐变色作为父 div 的背景,然后为子 div 提供纯色,例如在我们的例子中为纯黑色,然后为父 div 提供padding
您想要的边框宽度,在我们的例子中5px
,从技术上讲,所做padding
的是在边框和内部内容之间放置一个空间element
,因此它将从各个方向压制子 div 5px
,并且将使 5px 从父 div 显示,就好像它们是子 div 的边框一样。
好吧,让我们实现它,我们有一个父子,然后.ticket-visual_visual
我们可以给它一个具有所需渐变边框颜色的背景,从主会议网站获取 4 种颜色并将它们创建为自定义属性,如下所示:
:root {
// rest of variable
--color1: #d25778;
--color2: #ec585c;
--color3: #e7d155;
--color4: #56a8c6;
}
.ticket-visual_visual {
// other code here
background: linear-gradient(
to right,
var(--color1),
var(--color2),
var(--color3),
var(--color4)
);
}
请注意,使用linear-gradient
第一个参数是to right
因为我们需要从左到右进行渐变。
现在我们需要按照我们约定的方式为子 div 制作一个实体背景,这里的子 div 是.ticket-visual-wrapper
,所以让我们给它一个背景:
.ticket-visual-wrapper {
background: var(--background); // --background is #000
}
现在我们已经用这个变通方法制作了渐变边框,现在让我们尝试给它们设置边框半径:
.ticket-visual_visual {
// other styles
background: linear-gradient(
to right,
var(--color1),
var(--color2),
var(--color3),
var(--color4)
);
border-radius: 20px;
}
.ticket-visual-wrapper {
// other styles
background: var(--background);
border-radius: 15px;
}
当前结果应该是:
好了,我们到达了一个很好的阶段,现在,我们已经制作了一个具有渐变颜色的弯曲边框。
3. 左右半圆的实现
请注意,有几种不同的方法可以达到相同的结果,这些结果可能比此解决方案更好,请随意在评论中写下您的建议:)
和我们之前使用的想法一样,我们需要使用pseudo-elements
父div作为父元素,使用子div作为子元素。
因此基本上将使用:before
如下:after
伪元素:
.ticket-visual_visual:before {
content: "";
display: block;
position: absolute;
top: 130px;
left: -30px;
width: 60px;
height: 60px;
border-radius: 50%;
background: var(--color1);
z-index: 2;
}
.ticket-visual_visual:after {
content: "";
display: block;
position: absolute;
top: 130px;
right: -30px;
width: 60px;
height: 60px;
border-radius: 50%;
background: var(--color4);
z-index: 2;
}
正如你所注意到的,我们将它们视为 div,并将它们放置在卡片的中间左侧和右侧,同时赋予它们两个渐变颜色的极端值,左侧以第一种颜色--color1
作为背景,右侧以第一种颜色--color4
作为背景,因此现在的结果应该如下所示:
然后我们需要为每个圆圈添加一个纯色(黑色)的子圆圈,让我们也pseudo-elements
为它添加.ticket-visual-wrapper
一个,但首先让我们position: relative
为它添加:
.ticket-visual-wrapper {
width: 100%;
height: 100%;
background: var(--background);
border-radius: 15px;
position: relative;
}
.ticket-visual-wrapper:before {
content: "";
display: block;
position: absolute;
top: 130px;
left: -30px;
width: 50px;
height: 50px;
border-radius: 50%;
background: var(--background);
z-index: 3;
}
.ticket-visual-wrapper:after {
content: "";
display: block;
position: absolute;
top: 130px;
right: -30px;
width: 50px;
height: 50px;
border-radius: 50%;
background: var(--background);
z-index: 3;
}
如您所见,我们制作了两个50px X 50px
比父级圆圈小的圆圈60px X 60px
,并且这两个圆圈的背景都是--background
黑色,最后的通知是,我给它们z-index: 3
使它们高于父级圆圈pseudo-elements
。
目前结果:
剩下的唯一事情就是隐藏圆圈的外半部分,TBW 我发现为它们添加类似封面的东西可能是一个很好的解决方案,所以我决定添加 2 个可以用作封面的 div,.ticket-visual_visual
如下所示:
<div class="left"></div>
<div class="right"></div>
并且在 CSS 中,由于它们位于position: relative
div 内,因此通过赋予它们,position: absolute
它们将被定位在良好的位置:
.left {
position: absolute;
top: 110px;
left: -50px;
width: 50px;
height: 100px;
background: var(--background);
z-index: 4;
}
.right {
position: absolute;
top: 110px;
right: -50px;
width: 50px;
height: 100px;
background: var(--background);
z-index: 4;
}
给它们背景黑色,并z-index: 4
覆盖圆圈的两半,最终结果是:
现在设计已经完成,就像在 conf 网站中实现的那样。
4. 根据光标移动实现动画
现在是时候使用一些 JavaScript 了,我们只需要计算一个变量,该变量是每次移动时光标(鼠标)的位置,这样我们就可以为事件添加一个监听器mousemove
。
window.addEventListener("mousemove", e => {
// some code to run every time a user moves the mouse cursor
})
我决定在同一个 HTML 文件中的内联脚本标签中添加它,因为它不需要单独的文件。
在监听之前,我们需要选择股票行情元素并获取其边界矩形,以计算股票行情元素的中心点,如下所示:
const ticketElm = document.getElementById("ticket")
const { x, y, width, height } = ticketElm.getBoundingClientRect()
const centerPoint = { x: x + width / 2, y: y + height / 2 }
然后在mousemove
事件列表器中,我们需要添加一些代码来转换该票证,只需添加一些用于旋转的度数计算即可,如下所示:
const degreeX = (e.clientY - centerPoint.y) * 0.008
const degreeY = (e.clientX - centerPoint.x) * -0.008
请注意,这个计算的意思是:我们得到当前鼠标位置和我们之前计算的中心点之间的差值,然后将它们乘以一个非常小的数字0.008
,我通过不断尝试和错误来得到它,直到我觉得最合适。
然后我们可以使用这些计算出的度数来进行变换:
window.addEventListener("mousemove", e => {
const degreeX = (e.clientY - centerPoint.y) * 0.008
const degreeY = (e.clientX - centerPoint.x) * -0.008
ticketElm.style.transform = `perspective(1000px) rotateX(${degreeX}deg) rotateY(${degreeY}deg)`
})
在行处5
您可以发现我们只是将perspective
元素的设置为1000px
一个大数字以使其移动非常平滑而无需旋转,并且我们还使用了基于计算度数的x
和的旋转。y
那么最终结果将是:
现在,我们已经完成了,您可能会注意到移动鼠标时会出现一些闪亮的渐变,但这是您的家庭作业,目的是让票看起来有光泽,如果您这样做了,请告诉我。
结论
我很高兴写这篇文章,我希望你也喜欢阅读它:我们从中学到了很多东西,或者至少我希望如此:
- 如何解决并制作具有 border-radius 的渐变边框
- 如何实现带有渐变边框的半圆
- 如何使用
perspective
实现 3D 动画 - 如何思考变量的计算
- 所有代码都在Github上,去查看、分叉、克隆并做好你的功课 😉。
最后,如果您需要任何帮助,请随时在Twitter上与我分享或讨论,或者关注我并让我们成为朋友。
如果您懂阿拉伯语,这里有阿拉伯语教程中的逐步解释:
https://youtu.be/BfAydRvM-vk
孩子们👋
文章来源:https://dev.to/medhatdawoud/gradient-borders-with-curves-and-3d-movement-in-css-nextjs-ticket-clone-3cho