前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >圣诞快乐,手绘CSS圣诞老人,DIY浪漫圣诞礼物!

圣诞快乐,手绘CSS圣诞老人,DIY浪漫圣诞礼物!

作者头像
前端达人
发布2023-12-26 16:45:24
1410
发布2023-12-26 16:45:24
举报
文章被收录于专栏:前端达人前端达人

随着圣诞节的临近,街头巷尾都弥漫着浓浓的节日氛围。这不仅仅是一个普通的节日,更是一年中最适合表达爱意的时刻。今晚的平安夜,你是否想为你的女朋友准备一份特别的礼物?如果你是一位编程爱好者,那么我有一个非常独特且富有创意的想法——用CSS手绘一个可爱的圣诞老人!

可能你会问,为什么要选择CSS绘画?其实,CSS不仅仅是前端开发的基石,它还有着无限的创造潜力。通过精心的设计和编程,我们可以用CSS创造出各种生动的图形和动画,这不仅可以展示你的技术实力,更能以一种极具创意的方式表达你的心意。

在这篇文章中,我将带你一步步制作这个充满圣诞气息的CSS圣诞老人。不论你是编程的初学者,还是有一定基础的开发者,都可以轻松跟随。准备好了吗?让我们一起开启这段编程与创意并存的旅程,用代码绘制出一个温暖的圣诞惊喜吧!

本案例的效果如下图所示:

创建响应式的CSS画布

首先,我们需要创建一个画布(canvas),但这里的“canvas”并非指HTML中的<canvas>元素,而是一个我们将在其中进行绘画的区域。这个画布对我们来说非常有用,因为我们可以用它来定位我们的元素。

我们的目标是制作一个响应式的图像,所以画布和内容将主要使用相对单位,比如百分比(%)或视口最小单位(vmin)。这样做的好处是,无论在什么设备上查看,我们的CSS圣诞老人都能保持良好的显示效果。

利用渐变背景创建辅助网格

为了更方便地定位元素,我们可以添加一个重复的线性渐变来创建背景网格。这个网格将作为我们的绘图辅助工具。

代码语言:javascript
复制
<div class="canvas"></div>
代码语言:javascript
复制
.canvas {
  width: 80vmin;
  height: 80vmin;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  border: 1px solid #ddd;
  background-image: 
    repeating-linear-gradient(transparent 0 9.85%, #ddd 0 10%),
    repeating-linear-gradient(to right, transparent 0 9.85%, #ddd 0 10%);
}

注意:为了能够精确地放置不同的元素,画布必须设置为相对或绝对定位。

这些线条将帮助我们进行位置参考,稍后我们会将它们移除。此外,为了保持颜色的一致性并便于更改,我们将使用CSS变量来定义颜色。

通过这样的准备工作,我们为绘制圣诞老人奠定了基础。接下来的步骤将涉及更具体的CSS技巧,但不用担心,我会逐步引导大家,确保即使是初学者也能轻松跟上。让我们继续,用代码绘制一个充满节日气息的圣诞老人吧!

绘制圣诞老人的头部

在这个阶段,我们用几个简单的圆形和椭圆形创建了圣诞老人的脸、眼睛和脸颊。

  • 绘制脸部: 我们使用一个大圆形来代表圣诞老人的脸。通过设置border-radius属性为50%,我们可以将正方形的div变成一个完美的圆形。
  • 添加眼睛和脸颊: 为了让图像更加生动,我们添加了小圆形来表示眼睛,以及椭圆形来展现脸颊的红晕。在CSS中,我们同样使用border-radius属性来实现眼睛和脸颊的圆润边缘。
  • 定位元素: 为了更精确地放置眼睛和脸颊,我们使用position: absolute;并结合top、left、right属性。通过这种方式,我们可以确保无论脸部大小或位置如何变化,眼睛和脸颊都能保持在正确的位置。
  • 响应式设计: 在CSS中使用相对单位(如%)确保我们的绘制可以在不同尺寸的屏幕上保持响应性。尽管在原始版本中,眼睛和脸颊的位置是相对于整个画布的,但在这个版本中,我们将它们放置在脸部内部,这样做可以更容易地进行管理。
  • 代码优化: 在原始版本中,我使用了box-shadow来复制第二个眼睛和脸颊,但box-shadow需要使用非百分比单位,这导致绘画只是部分响应式。通过复制眼睛和脸颊的元素,我们可以使图像更加稳健和易于维护。
代码语言:javascript
复制
<div class="canvas">
  <div class="head">
    <div class="cheek"></div>
    <div class="cheek"></div>
    <div class="eye"></div>
    <div class="eye"></div>
  </div>
</div>
代码语言:javascript
复制
.canvas {
  --skin: #fca; /* 肤色 */
  --eyes: #630a; /* 眼睛颜色 */
  --cheeks: #f001; /* 脸颊红晕颜色 */
  /* ... 其他样式 */
}

/* ... 其他样式 */

.head {
  --positionX: 28%; /* 水平定位 */
  --positionY: 63%; /* 垂直定位 */
  position: absolute;
  top: 10%;
  left: 50%;
  border-radius: 50%;
  width: 25%;
  height: 25%;
  transform: translate(-50%, 0);
  background: var(--skin);
}

.eye {
  position: absolute;
  top: var(--positionY);
  left: var(--positionX);
  width: 12%;
  height: 12%;
  background: var(--eyes);
  border-radius: 50%;
}

.eye + .eye {
  left: auto;
  right: var(--positionX);
}

.cheek {
  position: absolute;
  top: calc(var(--positionY) + 7%);
  left: calc(var(--positionX) - 12%);
  width: 20%;
  height: 12%;
  background: var(--cheeks);
  border-radius: 50%;
}

.cheek + .cheek {
  left: auto;
  right: calc(var(--positionX) - 12%);
}

绘制圣诞老人的胡须和小胡子

这一次我们添加了胡须和小胡子。这两个部分的添加让我们的圣诞老人看起来更加生动和真实。

  • 绘制胡须: 胡须是通过一个椭圆形状的div元素来实现的,它位于头部的下方。我们利用border-radius的两个值(分别代表水平轴和垂直轴的半径)来创建这个椭圆形。这种方法允许我们精确地控制椭圆的形状。
  • 添加小胡子: 小胡子位于头部的上方,由两个具有相似样式(只是旋转角度不同)的元素组成,它们并排放置。为了更好地控制两者的样式,我们可以使用相邻兄弟选择器(+)。
代码语言:javascript
复制
<div class="canvas">
  <div class="beard"></div>
  <div class="head">
    <div class="cheek"></div>
    <div class="cheek"></div>
    <div class="eye"></div>
    <div class="eye"></div>
    <div class="mustache"></div>
    <div class="mustache"></div>
    <div class="hat"></div>
  </div>
</div>
代码语言:javascript
复制
.canvas {
  --beard: #eee; /* 胡须颜色 */
  --mustache: #fff; /* 小胡子颜色 */
  /* ... 其他样式 */
}

/* ... 其他样式 */

.beard {
  position: absolute;
  top: 10%;
  left: 50%;
  width: 30%;
  height: 40%;
  background: var(--beard);
  transform: translate(-50%, 0);
  border-radius: 100% / 120% 120% 80% 80%; /* 椭圆形胡须的边框半径 */
}

.mustache {
  position: absolute;
  top: 88%;
  left: 52%;
  width: 40%;
  height: 40%;
  background: var(--mustache);
  border-radius: 100% 10% 100% 0; /* 小胡子的边框半径 */
  transform-origin: top right;
  transform: translate(-100%, 0) rotate(25deg);
}

.mustache + .mustache {
  left: 48%;
  border-radius: 10% 100% 0 100%; /* 相邻小胡子的边框半径 */
  transform-origin: top left;
  transform: rotate(-25deg);
}

以上就是如何用CSS创建圣诞老人的胡须和小胡子的详细步骤。通过简单的形状和位置调整,我们就能够创造出有趣的效果。这种技术不仅能用来创造节日图像,同样也可以应用到其他各种各样的图形设计中。

绘制圣诞老人的帽子

在这个环节,我们将绘制圣诞老人的帽子,这包括帽子本身和两个伪元素:::before 和 ::after。使用伪元素的好处在于它们的大小和位置将相对于帽子元素,这意味着我们只需修改帽子元素,就可以同时更新所有三个部分。虽然我们可以用三个独立的元素来构建帽子、帽檐和球状饰品,但通过使用伪元素,我们可以更好地练习和展示CSS的能力。

  • 构建帽子主体: 帽子的主体是一个基本的矩形,我们通过给左上角设置一个100%的border-radius,来创建一个漂亮的弯曲边缘。
  • 添加帽子的球状饰品(pompom): 球状饰品很简单,只是一个圆形。我们可以使用border-radius: 50%;来实现。
  • 设计帽子的底部: 对于帽子底部,我们将使用我称之为“管状”(pipe)的形状。我们通过设置一个矩形的border-radius为100% / 50%,使其顶部和底部弯曲,而两侧保持平直。
  • 设置背景渐变: 一旦我们有了这个形状,我们可以添加一个径向渐变作为背景。然后我们就得到了那个弯曲的底部。为了让它更适合头部,我们可能需要对其进行一点旋转。
代码语言:javascript
复制
<div class="canvas">
  <div class="beard"></div>
  <div class="head">
    <div class="cheek"></div>
    <div class="cheek"></div>
    <div class="eye"></div>
    <div class="eye"></div>
    <div class="mustache"></div>
    <div class="mustache"></div>
    <div class="hat"></div>
  </div>
</div>
代码语言:javascript
复制
html {
  background: #bcd; /* 背景色 */
}

.canvas {
  --suit: #d00; /* 帽子颜色 */
  /* ... 其他样式 */
}

/* ... 其他样式 */

.hat {
  position: absolute;
  width: 98%;
  height: 80%;
  background: var(--suit);
  border-radius: 100% 20% 0 0; /* 帽子主体的边框半径 */
  top: -40%;
  left: 50%;
  transform: translate(-50%, 0) rotate(1deg);
}

.hat::before {
  content: ""; /* 重要:即使没有内容,也必须设置content属性 */
  display: block;
  position: absolute;
  bottom: -17%;
  left: -5%;
  width: 110%;
  height: 40%;
  border-radius: 100% / 50%; /* 帽子底部的边框半径 */
  transform: rotate(-2deg);
  background: 
    radial-gradient(200% 100% at 50% 100%, #0000 30%, var(--mustache) 31%); /* 底部渐变 */
}

.hat::after {
  content: ""; /* 球状饰品 */
  display: block;
  position: absolute;
  right: -25%;
  top: -15%;
  width: 40%;
  aspect-ratio: 1; /* 保持宽高比 */
  border-radius: 50%; /* 圆形 */
  background: var(--beard); /* 球状饰品的颜色 */
}

通过上面的步骤,我们可以完成一个有帽檐和球状饰品的圣诞帽。整个过程不仅锻炼了我们对CSS伪元素的运用,还让我们的圣诞老人看起来更加完整和可爱。

绘制圣诞老人的身体部分

在绘制圣诞老人的身体部分时,我们将使用一个类似钟形的形状,它在CSS中基本上是一个椭圆形,底部角半径较小。关于CSS中的形状,可以阅读我在这里发表的文章获得更多信息。

但身体部分真正有趣的是,我们将使用CSS渐变来绘制腰带和按钮:分别是径向渐变(radial-gradient())和线性渐变(linear-gradient())。

按钮部分是一个简单的从左到右的线性渐变,使用了三种颜色:透明、白色和再次透明。在颜色之间留出一小部分百分比,以增加一些“模糊”效果。

腰带的绘制略微复杂:它是一个圆形(径向)渐变,我们必须玩转这些值以确保它精确地定位在我们想要的位置。它遵循与按钮部分类似的透明-颜色-透明模式:

代码语言:javascript
复制
<div class="canvas">
  <div class="body"></div>
  <div class="beard"></div>
  <div class="head">
    <div class="cheek"></div>
    <div class="cheek"></div>
    <div class="eye"></div>
    <div class="eye"></div>
    <div class="mustache"></div>
    <div class="mustache"></div>
    <div class="hat"></div>
  </div>
</div>
代码语言:javascript
复制
.canvas {
  --belt: #222; /* 腰带颜色 */
  /* ... 其他样式 */
}

/* ... 其他样式 */

.body {
  position: absolute;
  top: 35%;
  left: 50%;
  width: 50%;
  height: 50%;
  background: var(--suit); /* 身体颜色 */
  border-radius: 100% / 150% 150% 25% 25%; /* 身体形状的边框半径 */
  transform: translate(-50%, 0);
  background-image:
    radial-gradient(circle at 50% -50%, transparent 75%, var(--belt) 75.1% 83%, transparent 83.1%),
    linear-gradient(to right, transparent 42%, white 42.2% 57%, transparent 57.2%);
}

注意,在身体的渐变中,后一个渐变不是恰好从前一个渐变结束的地方开始,而是添加了一个小数点。这是因为浏览器对CSS渐变的边缘处理过于锐利(SVG则不会),这个小数点有助于平滑边缘。

现在,我们的圣诞老人身体部分就完成了。但看起来可能有些单调,接下来我们需要通过添加一些装饰来改善它,让圣诞老人看起来更加生动和有趣。

为圣诞老人的身体添加细节

为圣诞老人的身体添加细节是赋予我们作品生命力的关键一步。

首先,我们会添加按钮,它们将是单独的圆形元素,通过不同的阴影来实现立体效果。这与我们之前为眼睛使用的技术类似,但阴影将垂直放置,而不是水平放置。

腰带扣其实就是一个矩形!我们在其周围添加金色边框,边框半径略微增加一点(我们不想要一个椭圆形)。背景也将是金色的,但通过一个内嵌的box-shadow,我们可以突出显示扣环。

在一些圣诞老人的插画中,许多都将圣诞老人外套的底部画成白色。因此,我们可以扩展身体上的径向渐变,让它结束于白色而不是透明。

代码语言:javascript
复制
<div class="canvas">
  <div class="body">
    <div class="belt"></div>
  </div>
  <div class="beard"></div>
  <div class="head">
    <div class="cheek"></div>
    <div class="cheek"></div>
    <div class="eye"></div>
    <div class="eye"></div>
    <div class="mustache"></div>
    <div class="mustache"></div>
    <div class="hat"></div>
  </div>
</div>
代码语言:javascript
复制
.canvas {
  --belt-buckle: gold; /* 腰带扣颜色 */
  /* ... 其他样式 */
}

/* ... 其他样式 */

.body {
  /* ... */
  background-image:
    /* 按钮 */
    radial-gradient(circle at 50% 36%, var(--belt) 2.75%, #0000 3%),
    radial-gradient(circle at 50% 48%, var(--belt) 3%, #0000 3.25%),
    radial-gradient(circle at 50% 60%, var(--belt) 2.75%, #0000 3%),
    radial-gradient(circle at 50% 90%, var(--belt) 2.25%, #0000 2.5%),
    /* 腰带 */
    radial-gradient(circle at 50% -50%, transparent 75%, var(--belt) 75.1% 83%, transparent 83.1%),
    /* 底部翻边 */
    linear-gradient(to right, transparent 42%, white 42.2% 57%, transparent 57.2%);
  clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 51% 100%, 50% 96%, 49% 100%, 0% 100%);
}

.belt {
  /* ... */
  background: 
    linear-gradient(var(--belt-buckle) 0 0) 75% 50% / 25% 12% no-repeat,
    linear-gradient(var(--belt) 0 0) 50% 50% / 85% 80% no-repeat,
    var(--belt-buckle);
}

注意到按钮有不同的大小,但它们看起来大小相同。这是因为尺寸是从最远的角到按钮计算的,所以如果我们为所有按钮设置相同的百分比,它们实际上会有不同的大小。

最后,我们添加了一个clip-path来修剪按钮部分的底部,使其看起来像是外套重叠在一起。

通过这些步骤,我们的圣诞老人身体部分就更加丰富和完整了。添加细节不仅能让视觉效果更加吸引人,也能提高作品的整体质感。现在,圣诞老人的形象更加生动,为节日氛围增添了欢乐和温馨。

绘制圣诞老人的手臂

圣诞老人的手臂将采用与身体相同的钟形形状,但这个钟形会更短更宽。这样,当我们将它放置在身体后面时,它会在两侧“溢出”。

通过添加从透明到半透明黑色的小垂直渐变,手臂在视觉上与身体产生距离。这种渐变效果看起来像是阴影,并强调了手臂的后置位置。

手部同样简单,就是一个圆形。与眼睛或按钮的绘制方法相同。我们本可以选择更复杂的形状(甚至是椭圆形),但我必须承认我不擅长绘制手部……所以这里用一个圆形就可以了:

代码语言:javascript
复制
<div class="canvas">
  <div class="hand"></div>
  <div class="hand"></div>
  <div class="arms"></div>
  <div class="body">
    <div class="belt"></div>
  </div>
  <div class="beard"></div>
  <div class="head">
    <div class="cheek"></div>
    <div class="cheek"></div>
    <div class="eye"></div>
    <div class="eye"></div>
    <div class="mustache"></div>
    <div class="mustache"></div>
    <div class="hat"></div>
  </div>
</div>
代码语言:javascript
复制
.arms {
  position: absolute;
  top: 37%;
  left: 50%;
  transform: translate(-50%, 0);
  width: 65%;
  height: 40%;
  background: #a00; /* 手臂的颜色 */
  border-radius: 100% / 170% 170% 25% 25%; /* 手臂的形状 */
  background-image: linear-gradient(transparent 20%, #0003); /* 手臂的渐变阴影 */
}

.hand {
  --positionX: 18%; /* 手的水平定位 */
  position: absolute;
  top: 70%;
  left: var(--positionX);
  width: 13%;
  height: 13%;
  background: var(--belt); /* 手的颜色 */
  border-radius: 50%; /* 手的圆形 */
}

.hand + .hand {
  left: auto;
  right: var(--positionX);
}

现在,我们的圣诞老人的上半部分已经完成了。这甚至可以作为网站的一个可爱元素(例如,从页面底部动画弹出)。通过这些步骤,我们的圣诞老人变得越来越可爱,为网站添加了节日的气息和趣味性。

绘制圣诞老人的腿部

圣诞老人的腿部将由两部分组成:腿本身和靴尖(只有尖部,因为靴子将通过腿部上的线性渐变来绘制)。

我们绘制矩形作为腿部,使用我们之前为小胡子用过的相邻兄弟选择器稍微分开它们,添加红色到黑色的渐变来区分裤子和靴子……然后稍微倾斜它们(使用skew()),这样它们看起来不会太对称。

最后,对于靴尖,我们使用::after伪元素,并且圆滑顶部的角:

代码语言:javascript
复制
<div class="canvas">
  <div class="hand"></div>
  <div class="hand"></div>
  <div class="arms"></div>
  <div class="leg"></div>
  <div class="leg"></div>
  <div class="body">
    <div class="belt"></div>
  </div>
  <div class="beard"></div>
  <div class="head">
    <div class="cheek"></div>
    <div class="cheek"></div>
    <div class="eye"></div>
    <div class="eye"></div>
    <div class="mustache"></div>
    <div class="mustache"></div>
    <div class="hat"></div>
  </div>
</div>
代码语言:javascript
复制
.leg {
  position: absolute;
  top: 75%;
  left: 29%;
  width: 19%;
  height: 25%;
  background: var(--suit); /* 腿部的颜色 */
  transform: skew(2deg); /* 腿部的倾斜 */
  background-image: linear-gradient(#0002, transparent 70%, var(--belt) 0); /* 裤子和靴子的分界线 */
}

.leg + .leg {
  left: 52%; /* 第二条腿的位置 */
}

.leg::after {
  content: "";
  display: block;
  position: absolute;
  bottom: 0;
  left: -6%;
  width: 110%;
  height: 20%;
  background: black; /* 靴尖的颜色 */
  border-radius: 50% / 100% 100% 0 0; /* 靴尖的圆角 */
}

.leg + .leg::after {
  left: -4%; /* 第二个靴尖的位置 */
}

通过上述代码,我们的圣诞老人就绘制完成了……但它孤零零地漂浮在灰色的虚空中看起来有些悲伤。让我们添加一些背景景观来营造氛围。

添加地面和一些雪花

添加地面和一些雪花可以让我们的圣诞老人图画更加完整,尽管这一步是可选的。我们将从地面开始,因为它更简单。我们甚至不需要新增一个元素!我们可以使用<canvas>的::before伪元素。

我们将制作一个非常大的地面,大到它会溢出视口,我们需要在文档的<body>中添加overflow: hidden,以避免出现烦人的滚动条。

然后我们将其放置在画布的底部,并添加一点微小的弯曲度(通过制作一个倒置的钟形!)。就这样,我们的圣诞老人站在了一个小山丘上。

雪花的步骤也相当简单。我们将通过向<body>添加一系列径向渐变来创建它,每一个都是一个不同大小的背景图像(这样它们看起来更不规则)。

注意:background-image允许多个值,只要它们由逗号分隔。同样的原则适用于background-position、background-repeat、background-size...

结果看起来是这样的:

代码语言:javascript
复制
html {
  background: #abc; /* 背景色 */
  overflow: hidden;
  background-image:
    radial-gradient(circle at 50% 50%, white 2.5%, transparent 0),
    radial-gradient(circle at 30% 90%, white 1.5%, transparent 0),
    radial-gradient(circle at 70% 10%, white 1%, transparent 0),
    radial-gradient(circle at 10% 40%, white 1%, transparent 0);
  background-size: 45vmin 35vmin, 50vmin 70vmin, 60vmin 50vmin, 65vmin 60vmin;
  background-position: 0 0;
}

.canvas::before {
  content: "";
  display: block;
  position: absolute;
  top: 90%;
  left: 50%;
  width: 200vmax;
  height: 200vmax;
  background: white; /* 地面颜色 */
  transform: translate(-50%, 0) rotate(1deg);
  border-radius: 100% / 20%; /* 地面的弯曲度 */
}

/* ... */

通过上述代码,我们的圣诞老人图画就完成了。最后,我们需要稍微清理一下,移除作为指导线的网格,这样我们的圣诞老人就可以在一个冬日美景中展现了。

给场景添加动画

给场景添加动画可以让我们的静态绘图活跃起来:

  • 圣诞老人的眨眼动画:通过简单的动画,让眼睛的高度从当前值变为零,然后再变回来。我们可能需要添加一个垂直位移动画,以获得更好的效果。
  • 胡子的动画:这也是一个简单的动画,但因为我们需要同步两侧的胡子(记住它是两部分!),所以开始会有点“混乱”。通过调整时间和角度,可以获得很好的效果。
  • 下落的雪花动画:我们可以通过动画background-position来使其看起来像雪花在下落。垂直下落很容易实现,但看起来不够真实。雪花在下落时会做锯齿形的运动,所以我们将在动画中添加这种锯齿形运动。

注意:动画的真正挑战在于时间控制。如果所有动画部分都使用相同的时间函数并且持续相同的时间,动画看起来会很假。混合不同的动画会使其看起来更自然、更美观……不幸的是,我在这方面并不擅长。作为参考,你可以查看Adam Kuhn、Sarah Drasner或Jhey Tompkins的作品。他们在这方面确实很了不起。

以下是动画的CSS代码示例:

代码语言:javascript
复制
@keyframes snow {
  0% { background-position: 0 0, 0 0, 0 0, 0 0; }
  40% { background-position: 10px 14vmin, -20px 28vmin, 20px 20vmin, 10px 24vmin; }
  60% { background-position: -10px 21vmin, -30px 42vmin, 30px 30vmin, 15px 36vmin; }
  100% { background-position: 0 35vmin, 0 70vmin, 0 50vmin, 0 60vmin; }
}

@keyframes blink {
  0%, 6%, 100% { height: 12%; }
  3% { height: 0%; }
}

@keyframes moveMustache {
  0%, 40%, 44%, 100% { transform: translate(-100%, 0) rotate(25deg); }
  42% { transform: translate(-100%, 0) rotate(30deg); }
}

@keyframes moveMustache2 {
  0%, 40%, 44%, 100% { transform: rotate(-25deg); }
  42% { transform: rotate(-30deg); }
}

html {
  animation: snow infinite 7s linear;
  /* ... */
}

.eye {
  animation: blink 5s infinite linear;
  /* ... */
}

.mustache {
  animation: moveMustache 7s infinite linear;
  /* ... */
}

.mustache + .mustache {
  animation: moveMustache2 7s infinite linear;
  /* ... */
}

/* ... */

重要的是要考虑用户的意愿,如果用户选择减少运动(特别是考虑到辅助功能),则应该禁用动画。我们可以使用prefers-reduced-motion媒体功能来实现这一点。

代码语言:javascript
复制
@media (prefers-reduced-motion) {
  * {
    animation: none !important;
  }
}

这个选择器过于泛化。如果你在网站上嵌入了这个圣诞老人的绘图,你可能需要调整它,以免影响你页面上的其他动画。

让辅助技术识别我们的CSS艺术作品

CSS艺术绘图虽然不能像图片那样直接添加替代文本,但我们可以采取措施让辅助技术识别我们的CSS艺术作品。如果我们希望辅助技术能够识别我们的圣诞老人绘图,并为其提供描述,那么添加适当的辅助功能将是一个好方法。

在这种情况下,我们需要在绘图的根部添加一个角色(role)为img,并且在根部也添加一个aria-label:

代码语言:javascript
复制
<div class="canvas" 
     role="img" 
     aria-label="一个站在雪地小山上的圣诞老人卡通形象。">
  <!-- ... -->
</div>

通过这样做,我们就完成了使CSS艺术作品更易于辅助技术访问的过程。

此外,您可以随意添加更多细节:增加眉毛会很好,帽子下露出一些头发,圣诞老人周围放一些礼物,甚至在某个地方添加一个驯鹿!

关于响应性的最后说明

目前,绘图位于.canvas根元素内部,其宽度和高度为80vmin。由于vmin是一个响应式单位(取决于视图框架的大小),绘图会适应屏幕大小,但这可能不是我们想要的效果。

我们可能希望将绘图添加到页面中的特定空间,那时vmin单位可能会成为问题。不用担心。我们做一个小改动来解决这个问题:

代码语言:javascript
复制
.canvas {
  width: 80vmin;
  aspect-ratio: 1;
  /* 移除 height */
  /* ... */
}

通过移除高度并指定aspect-ratio为1,画布将始终保持正方形。由于我们在所有尺寸和背景中使用了百分比,我们可以将宽度改变为我们想要的任何值,绘图都会很好地缩放。例如,将宽度设置为200像素后,效果如下:

同样的圣诞老人绘图,只是更小了。

就这样!我们创建了一个带有圣诞老人的动画场景,在这个过程中,我们练习了很多CSS:

  • 动画(Animations)
  • 背景(Backgrounds)
  • 边框半径(Border-radius)
  • 盒子阴影(Box-shadow)
  • 组合器(Combinators)
  • 渐变(Gradients)
  • 溢出(Overflow)
  • 定位(Positioning)
  • 伪元素(Pseudo-elements)
  • 变形(Transforms)
  • 单位和颜色(Units & colors)
  • 变量(Variables)
  • 宽高比(Aspect ratio)

最终代码

代码语言:javascript
复制
<div class="canvas" role="img" aria-label="一个站在雪地小山上的圣诞老人卡通形象。">
  <div class="hand"></div>
  <div class="hand"></div>
  <div class="arms"></div>
  <div class="leg"></div>
  <div class="leg"></div>
  <div class="body">
    <div class="belt"></div>
  </div>
  <div class="beard"></div>
  <div class="head">
    <div class="cheek"></div>
    <div class="cheek"></div>
    <div class="eye"></div>
    <div class="eye"></div>
    <div class="mustache"></div>
    <div class="mustache"></div>
    <div class="hat"></div>
  </div>
</div>
代码语言:javascript
复制
@keyframes snow {
  0% { background-position: 0 0, 0 0, 0 0, 0 0; }
  40% { background-position: 10px 14vmin, -20px 28vmin, 20px 20vmin, 10px 24vmin; }
  60% { background-position: -10px 21vmin, -30px 42vmin, 30px 30vmin, 15px 36vmin; }
  100% { background-position: 0 35vmin, 0 70vmin, 0 50vmin, 0 60vmin; }
}

@keyframes blink {
  0%, 6%, 100% { height: 12%; }
  3% { height: 0%; }
}

@keyframes moveMustache {
  0%, 40%, 44%, 100% { transform: translate(-100%, 0) rotate(25deg); }
  42% { transform: translate(-100%, 0) rotate(30deg); }
}

@keyframes moveMustache2 {
  0%, 40%, 44%, 100% { transform: rotate(-25deg); }
  42% { transform: rotate(-30deg); }
}

@media (prefers-reduced-motion) {
  * {
    animation: none !important;
  }
}

html {
  background: #abc;
  overflow: hidden;
  background-image:
    radial-gradient(circle at 50% 50%, white 2.5%, transparent 0),
    radial-gradient(circle at 30% 90%, white 1.5%, transparent 0),
    radial-gradient(circle at 70% 10%, white 1%, transparent 0),
    radial-gradient(circle at 10% 40%, white 1%, transparent 0);
  background-size: 45vmin 35vmin, 50vmin 70vmin, 60vmin 50vmin, 65vmin 60vmin;
  background-position: 0 0;
  animation: snow infinite 7s linear;
}

.canvas {
  --skin: #fca;
  --eyes: #630a;
  --cheeks: #f001;
  --beard: #eee;
  --mustache: #fff;
  --suit: #a00;
  --belt: #222;
  --belt-buckle: gold;
  width: 60vmin;
  aspect-ratio: 1;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

.canvas::before {
  content: "";
  display: block;
  position: absolute;
  top: 90%;
  left: 50%;
  width: 200vmax;
  height: 200vmax;
  background: white;
  transform: translate(-50%, 0) rotate(1deg);
  border-radius: 100% / 20%;
}


.head {
  --positionX: 27%;
  --positionY: 63%;
  position: absolute;
  top: 10%;
  left: 50%;
  border-radius: 50%;
  width: 25%;
  height: 25%;
  transform: translate(-50%, 0);
  background: var(--skin);
}

.eye {
  position: absolute;
  top: var(--positionY);
  left: var(--positionX);
  width: 12%;
  height: 12%;
  background: var(--eyes);
  border-radius: 50%;
  animation: blink 5s infinite linear;
}

.eye + .eye {
  left: auto;
  right: var(--positionX);
}

.cheek {
  position: absolute;
  top: calc(var(--positionY) + 7%);
  left: calc(var(--positionX) - 12%);
  width: 20%;
  height: 12%;
  background: var(--cheeks);
  border-radius: 50%;
}

.cheek + .cheek {
  left: auto;
  right: calc(var(--positionX) - 12%);
}

.beard {
  position: absolute;
  top: 10%;
  left: 50%;
  width: 30%;
  height: 40%;
  background: var(--beard);
  transform: translate(-50%, 0);
  border-radius: 100% / 120% 120% 80% 80%;
}

.mustache {
  position: absolute;
  top: 88%;
  left: 52%;
  width: 40%;
  height: 40%;
  background: var(--mustache);
  border-radius: 100% 10% 100% 0;
  transform-origin: top right;
  transform: translate(-100%, 0) rotate(25deg);
  animation: moveMustache 7s infinite linear;
}

.mustache + .mustache {
  left: 48%;
  border-radius: 10% 100% 0 100%;
  transform-origin: top left;
  transform: rotate(-25deg);
  animation: moveMustache2 7s infinite linear;
}

.hat {
  position: absolute;
  width: 98%;
  height: 80%;
  background: var(--suit);
  border-radius: 100% 20% 0 0;
  top: -40%;
  left: 50%;
  transform: translate(-50%, 0) rotate(1deg);
}

.hat::before {
  content: "";
  display: block;
  position: absolute;
  bottom: -17%;
  left: -5%;
  width: 110%;
  height: 40%;
  border-radius: 100% / 50%;
  transform: rotate(-2deg);
  background: radial-gradient(200% 100% at 50% 100%, #0000 30%, var(--mustache) 31%);
}

.hat::after {
  content: "";
  display: block;
  position: absolute;
  right: -25%;
  top: -15%;
  width: 40%;
  aspect-ratio: 1;
  border-radius: 50%;
  background: var(--beard);
}

.body {
  position: absolute;
  top: 35%;
  left: 50%;
  width: 50%;
  height: 50%;
  background: var(--suit);
  border-radius: 100% / 150% 150% 25% 25%;
  transform: translate(-50%, 0);
  background-image:
    /* buttons */
    radial-gradient(circle at 50% 36%, var(--belt) 2.75%, #0000 3%),
    radial-gradient(circle at 50% 48%, var(--belt) 3%, #0000 3.25%),
    radial-gradient(circle at 50% 60%, var(--belt) 2.75%, #0000 3%),
    radial-gradient(circle at 50% 90%, var(--belt) 2.25%, #0000 2.5%),
    /* belt */
    radial-gradient(circle at 50% -50%, transparent 75%, var(--belt) 75.1% 83%, transparent 83.1%),
    /* flap */
    linear-gradient(to right, transparent 42%, white 42.2% 57%, transparent 57.2%);
  clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 51% 100%, 50% 96%, 49% 100%, 0% 100%);
}

.belt {
  position: absolute;
  top: 75%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 23%;
  height: 15%;
  background: 
    linear-gradient(var(--belt-buckle) 0 0) 75% 50% / 25% 12% no-repeat,
    linear-gradient(var(--belt) 0 0) 50% 50% / 85% 80% no-repeat,
    var(--belt-buckle);
}


.arms {
  position: absolute;
  top: 37%;
  left: 50%;
  transform: translate(-50%, 0);
  width: 65%;
  height: 40%;
  background: #a00;
  border-radius: 100% / 170% 170% 25% 25%;
  background-image: linear-gradient(transparent 20%, #0003);
}

.hand {
  --positionX: 18%;
  position: absolute;
  top: 70%;
  left: var(--positionX);
  width: 13%;
  height: 13%;
  background: var(--belt);
  border-radius: 50%;
}

.hand + .hand {
  left: auto;
  right: var(--positionX);
}

.leg {
  position: absolute;
  top: 75%;
  left: 29%;
  width: 19%;
  height: 25%;
  background: var(--suit);
  transform: skew(2deg);
  background-image: linear-gradient(#0002, transparent 70%, var(--belt) 0);
}

.leg + .leg {
  left: 52%;
}

.leg::after {
  content: "";
  display: block;
  position: absolute;
  bottom: 0;
  left: -6%;
  width: 110%;
  height: 20%;
  background: black;
  border-radius: 50% / 100% 100% 0 0;
}

.leg + .leg::after {
  left: -4%;
}

结束

在这篇文章中,我们探索了如何使用CSS技术来创作一个动态的圣诞老人场景。从构建圣诞老人的各个部分(如头部、眼睛、胡子、帽子、身体、手臂、腿部)到为这些部分添加细节和动画,我们逐步构建了这个温馨的节日形象。通过使用变量、伪元素、渐变、边框半径和动画关键帧等CSS功能,我们不仅制作出了一个有趣的圣诞老人图像,还实现了雪花飘落的效果,让整个场景更加生动。

为了提升可访问性,我们还使用了role和aria-label属性,使得这个CSS艺术作品对于使用辅助技术的用户也是可访问的。同时,我们考虑到不同用户的需求,通过媒体查询prefers-reduced-motion来适配那些偏好减少动画的用户。

总的来说,这个项目不仅是一次对CSS技术的深入练习,也是一个创造节日氛围的有趣方式。无论你是一个热爱编程的专业人士,还是对网页设计有兴趣的爱好者,这个项目都能提供一种创造性的方式来庆祝节日,同时锻炼你的技术技巧。通过这样的实践,我们可以看到,CSS不仅仅是用来构建网站布局的工具,它还可以成为创造艺术的画布。

希望你喜欢这个小项目,并且能够在你自己的项目中找到灵感和乐趣!圣诞快乐!🎅🎄✨

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-12-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端达人 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 绘制圣诞老人的头部
  • 绘制圣诞老人的帽子
  • 绘制圣诞老人的手臂
  • 添加地面和一些雪花
  • 给场景添加动画
  • 让辅助技术识别我们的CSS艺术作品
  • 关于响应性的最后说明
  • 最终代码
  • 结束
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档