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

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

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

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

一.创建响应式的CSS画布
首先,我们需要创建一个画布(canvas),但这里的“canvas”并非指HTML中的<canvas>元素,而是一个我们将在其中进行绘画的区域。这个画布对我们来说非常有用,因为我们可以用它来定位我们的元素。我们的目标是制作一个响应式的图像,所以画布和内容将主要使用相对单位,比如百分比(%)或视口最小单位(vmin)。这样做的好处是,无论在什么设备上查看,我们的CSS圣诞老人都能保持良好的显示效果。

利用渐变背景创建辅助网格
为了更方便地定位元素,我们可以添加一个重复的线性渐变来创建背景网格。这个网格将作为我们的绘图辅助工具。
<div class="canvas"></div>
.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需要使用非百分比单位,这导致绘画只是部分响应式。通过复制眼睛和脸颊的元素,我们可以使图像更加稳健和易于维护。
<div class="canvas">
  <div class="head">
    <div class="cheek"></div>
    <div class="cheek"></div>
    <div class="eye"></div>
    <div class="eye"></div>
  </div>
</div>
.canvas {
  --skin: #fca; /* 肤色 */
  --eyes: #630a; /* 眼睛颜色 */
  --cheeks: #f001; /* 脸颊红晕颜色 */
  /* ... 堆代码 duidaima.com */
  /* ... 其他样式 */
}

/* ... 其他样式 */

.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的两个值(分别代表水平轴和垂直轴的半径)来创建这个椭圆形。这种方法允许我们精确地控制椭圆的形状。

添加小胡子: 小胡子位于头部的上方,由两个具有相似样式(只是旋转角度不同)的元素组成,它们并排放置。为了更好地控制两者的样式,我们可以使用相邻兄弟选择器(+)。
<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>
.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%,使其顶部和底部弯曲,而两侧保持平直。
设置背景渐变: 一旦我们有了这个形状,我们可以添加一个径向渐变作为背景。然后我们就得到了那个弯曲的底部。为了让它更适合头部,我们可能需要对其进行一点旋转。
<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>
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())。

按钮部分是一个简单的从左到右的线性渐变,使用了三种颜色:透明、白色和再次透明。在颜色之间留出一小部分百分比,以增加一些“模糊”效果。
腰带的绘制略微复杂:它是一个圆形(径向)渐变,我们必须玩转这些值以确保它精确地定位在我们想要的位置。它遵循与按钮部分类似的透明-颜色-透明模式:
<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>
.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,我们可以突出显示扣环。

在一些圣诞老人的插画中,许多都将圣诞老人外套的底部画成白色。因此,我们可以扩展身体上的径向渐变,让它结束于白色而不是透明。
<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>
.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来修剪按钮部分的底部,使其看起来像是外套重叠在一起。

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

六.绘制圣诞老人的手臂
圣诞老人的手臂将采用与身体相同的钟形形状,但这个钟形会更短更宽。这样,当我们将它放置在身体后面时,它会在两侧“溢出”。通过添加从透明到半透明黑色的小垂直渐变,手臂在视觉上与身体产生距离。这种渐变效果看起来像是阴影,并强调了手臂的后置位置。

手部同样简单,就是一个圆形。与眼睛或按钮的绘制方法相同。我们本可以选择更复杂的形状(甚至是椭圆形),但我必须承认我不擅长绘制手部……所以这里用一个圆形就可以了:
<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>
.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伪元素,并且圆滑顶部的角:
<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>
.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...

结果看起来是这样的:
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代码示例:
@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; }
}
// 堆代码 duidaima.com
@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媒体功能来实现这一点。
@media (prefers-reduced-motion) {
  * {
    animation: none !important;
  }
}
这个选择器过于泛化。如果你在网站上嵌入了这个圣诞老人的绘图,你可能需要调整它,以免影响你页面上的其他动画。

十.让辅助技术识别我们的CSS艺术作品
CSS艺术绘图虽然不能像图片那样直接添加替代文本,但我们可以采取措施让辅助技术识别我们的CSS艺术作品。如果我们希望辅助技术能够识别我们的圣诞老人绘图,并为其提供描述,那么添加适当的辅助功能将是一个好方法。在这种情况下,我们需要在绘图的根部添加一个角色(role)为img,并且在根部也添加一个aria-label:
<div class="canvas" 
     role="img" 
     aria-label="一个站在雪地小山上的圣诞老人卡通形象。">
  <!-- ... -->
</div>
通过这样做,我们就完成了使CSS艺术作品更易于辅助技术访问的过程。

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


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

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

最终代码
<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>
@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不仅仅是用来构建网站布局的工具,它还可以成为创造艺术的画布。

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