我正在参加「兔了个兔」创意投稿大赛,详情请看:「兔了个兔」创意投稿大赛。
前言
下周就要放假(请假)回家啦,趁着最后一个工作周,来充实下一X一度的CSS画儿系列。虽说是要祝大家「兔来运转」,但是这次就不画小兔子啦,画一对儿拜年的福娃「好运兔you」。
画起来
既然是一幅画,我们是不是就应该考虑构图啊、色彩啊、光影啊等等。
我用大毅力翻阅无数资料(并没有),观摩一堆名画(不可能),我决定这幅CSS画里该有的要素啥都没有。
简单形容一下,只有这些内容:喜庆的大红墙和木质地板作为场景的主体,顶部高高挂起的大红灯笼飘扬传递着新春祝福,窗外的夜色中星光点点,烟花不时绽放。最重要的是,在这热闹的氛围中,一对儿福娃给大家在给大家拜年。
不重要的主体场景
没有什么绿瓦红墙,但是光秃秃的也不好看,先来画一个室内角度的大红墙和简单的木质地板。
为了稍微做一点适配,这里采用了vw和rem的方式。
<div class="container">
<div class="wall"></div>
<div class="floor"></div>
</div>
html{
font-size:calc(100vw/750*100);
}
html,
body {
margin: 0;
padding: 0;
}
.container {
margin: 0 auto;
padding: 0;
width: 7.5rem;
height: 100vh;
background-color: #17024c;
position: relative;
}
.wall {
width: 100%;
height: 80vh;
background-color: #9d2727;
overflow: hidden;
}
.floor {
width: 100%;
height: 20vh;
background-color: #67422a;
background: linear-gradient(to bottom, #67422a, #371a0e);
}
接下来,在墙上加一个窗户,让我们可以在暖暖的室内看到外面的点点星光和烟花。
需要注意的是,我们看到的星光和烟花都是透过窗户看到的,所以窗户的
overflow: hidden;样式是必不可少的。
<div class="window"></div>
.window {
margin: 20vh auto 0;
width: 80vh;
height: 80vh;
border-radius: 100%;
border: 0.1rem solid #562c03;
background-color: #17024c;
overflow: hidden;
position: relative;
}
很简单,我们不重要的主体场景就完成啦。
星光点缀下绽放的烟花
这里的星光偷懒了,因为使用码上掘金Vue3版本有莫名的报错,就不再循环生成多个随机位置的星星啦,大家如果自己要玩,这里可以搞出漫天星光。
在窗户内插入一些星星和烟花,这里的烟花参考了网上一个不错的效果,大家感兴趣的还可以继续细化一下:
<div class="window">
<div class="starlight"></div>
<div class="starlight starlight1"></div>
<div class="starlight starlight2"></div>
<div class="c-firework"></div>
<div class="c-firework"></div>
<div class="c-firework"></div>
<div class="c-firework"></div>
<div class="c-firework"></div>
</div>
.starlight {
width: 0.04rem;
height: 0.04rem;
border-radius: 100%;
background-color: #fff;
position: absolute;
left: 3rem;
top: 3rem;
&1 {
opacity: 0.8;
transform: scale(1.8);
left: 3.2rem;
top: 3.2rem;
}
&2 {
opacity: 0.6;
transform: scale(0.8);
left: 3rem;
top: 3.4rem;
}
}
@keyframes fireworks-animation {
0% {
transform: translate(-50%, 90vh);
width: 0.04rem;
opacity: 1;
}
50% {
width: 0.04rem;
opacity: 1;
}
100% {
width: 6rem;
opacity: 0;
}
}
.c-firework,
.c-firework::before,
.c-firework::after {
content: "";
position: absolute;
top: 50%;
left: 50%;
aspect-ratio: 1;
background: radial-gradient(circle, yellow 0.1rem, #000 0) 50% 00%,
radial-gradient(circle, khaki 0.1rem, #000 0) 00% 50%,
radial-gradient(circle, white 0.1rem, #000 0) 50% 99%,
radial-gradient(circle, lime 0.1rem, #000 0) 99% 50%,
radial-gradient(circle, crimson 0.1rem, #000 0) 80% 90%,
radial-gradient(circle, red 0.1rem, #000 0) 95% 90%,
radial-gradient(circle, yellow 0.1rem, #000 0) 10% 60%,
radial-gradient(circle, khaki 0.1rem, #000 0) 31% 80%,
radial-gradient(circle, white 0.1rem, #000 0) 80% 10%,
radial-gradient(circle, lime 0.1rem, #000 0) 90% 23%,
radial-gradient(circle, crimson 0.1rem, #000 0) 45% 20%,
radial-gradient(circle, red 0.1rem, #000 0) 13% 24%;
background-size: 0.08rem 0.08rem;
background-repeat: no-repeat;
transform: translate(-50%, -50%);
animation: fireworks-animation 4s infinite;
}
.c-firework::before {
transform: translate(-50%, -50%) rotate(25deg) !important;
}
.c-firework::after {
transform: translate(-50%, -50%) rotate(-37deg) !important;
}
.c-firework:nth-of-type(2),
.c-firework:nth-of-type(2)::before,
.c-firework:nth-of-type(2)::after {
top: 30%;
left: 16%;
animation-duration: 3.8s;
}
.c-firework:nth-of-type(3),
.c-firework:nth-of-type(3)::before,
.c-firework:nth-of-type(3)::after {
top: 10%;
left: 72%;
animation-duration: 4.2s;
}
.c-firework:nth-of-type(4),
.c-firework:nth-of-type(4)::before,
.c-firework:nth-of-type(4)::after {
top: 28%;
left: 32%;
animation-duration: 3.6s;
}
.c-firework:nth-of-type(5),
.c-firework:nth-of-type(5)::before,
.c-firework:nth-of-type(5)::after {
top: 32%;
left: 84%;
animation-duration: 4.4s;
}
.c-firework:nth-of-type(6),
.c-firework:nth-of-type(6)::before,
.c-firework:nth-of-type(6)::after {
top: 38%;
left: 40%;
animation-duration: 4.1s;
}
.c-firework:nth-of-type(7),
.c-firework:nth-of-type(7)::before,
.c-firework:nth-of-type(7)::after {
top: 28%;
left: 64%;
animation-duration: 3.9s;
}
.c-firework:nth-of-type(8),
.c-firework:nth-of-type(8)::before,
.c-firework:nth-of-type(8)::after {
top: 30%;
left: 56%;
animation-duration: 3.9s;
}
挂起祝福的灯笼
大红灯笼是喜庆的代表,虽然现在春节已经很少能见到这物件,但是我们的CSS画里还是绝对不能少了这一抹传统色。
循环出4个灯笼,分别挂在两侧,每个灯笼上展示一个字,连在一句就是一句新春祝福,当然这里配合JavaScript食用效果更佳。
<div class="lantern-box">
<div class="lantern">
<div class="lantern-line"></div>
<div class="lantern-body">
<div class="lantern-part">
<div class="lantern-text">欢</div>
</div>
</div>
<div class="lantern-panicle">
<div class="lantern-crown"></div>
<div class="lantern-tassel"></div>
</div>
</div>
</div>
<div class="lantern-box lantern-box1">
<!--...-->
</div>
<div class="lantern-box lantern-box2">
<!--...-->
</div>
<div class="lantern-box lantern-box3">
<!--...-->
</div>
.lantern {
position: relative;
width: 1.2rem;
height: 0.9rem;
margin: 0.5rem;
background: #d8000f;
background: rgba(216, 0, 15, 0.8);
border-radius: 50% 50%;
transform-origin: 50% -1rem;
animation: swing 5s infinite ease-in-out;
box-shadow: -0.05rem 0.05rem 0.3rem 0.04rem rgba(252, 144, 61, 1);
&::before {
position: absolute;
top: -0.07rem;
left: 0.29rem;
height: 0.12rem;
width: 0.6rem;
content: " ";
display: block;
z-index: 1999;
border-radius: 0.05rem 0.05rem 0 0;
border: solid 0.01rem #dc8f03;
background: #ffa500;
background: linear-gradient(
to right,
#dc8f03,
#ffa500,
#dc8f03,
#ffa500,
#dc8f03
);
}
&::after {
position: absolute;
bottom: -0.07rem;
left: 0.1rem;
height: 0.12rem;
width: 0.6rem;
content: " ";
display: block;
margin-left: 0.2rem;
border-radius: 0 0 0.05rem 0.05rem;
border: solid 0.01rem #dc8f03;
background: #ffa500;
background: linear-gradient(
to right,
#dc8f03,
#ffa500,
#dc8f03,
#ffa500,
#dc8f03
);
}
&-box {
position: absolute;
top: -0.2rem;
left: -0.2rem;
z-index: 1999;
&1 {
left: 0.6rem;
}
&2 {
left: auto;
right: 0.6rem;
}
&3 {
left: auto;
right: -0.2rem;
}
}
&-box1 &,
&-box3 & {
animation: swing 3s infinite ease-in-out;
}
&-line {
position: absolute;
top: -0.6rem;
left: 0.6rem;
width: 0.06rem;
height: 0.6rem;
background: #dc8f03;
}
&-body {
width: 1rem;
height: 0.9rem;
background: #d8000f;
background: rgba(216, 0, 15, 0.1);
margin: 0.12rem 0.08rem 0.08rem 0.1rem;
border-radius: 50% 50%;
border: 0.02rem solid #dc8f03;
}
&-part {
width: 0.45rem;
height: 0.9rem;
background: #d8000f;
background: rgba(216, 0, 15, 0.1);
margin: -0.04rem 0.08rem 0.08rem 0.26rem;
border-radius: 50% 50%;
border: 0.02rem solid #dc8f03;
}
&-text {
font-family: "华文行楷", Arial, Lucida Grande, Tahoma, sans-serif;
font-size: 0.6rem;
color: #dc8f03;
font-weight: bold;
line-height: 0.85rem;
text-align: center;
}
&-panicle {
position: relative;
width: 0.05rem;
height: 0.2rem;
margin: -0.05rem 0 0 0.59rem;
animation: swing 4s infinite ease-in-out;
-webkit-animation: swing 4s infinite ease-in-out;
transform-origin: 50% -0.45rem;
-webkit-transform-origin: 50% -0.45rem;
background: #ffa500;
border-radius: 0 0 0.05rem 0.05rem;
}
&-crown {
position: absolute;
top: 0.18rem;
left: -0.02rem;
width: 0.1rem;
height: 0.35rem;
background: #ffa500;
border-radius: 0 0 0 0.05rem;
}
&-tassel {
position: absolute;
top: 0.14rem;
left: -0.02rem;
width: 0.1rem;
height: 0.1rem;
background: #dc8f03;
border-radius: 50%;
}
}
@keyframes swing {
0% {
transform: rotate(-10deg);
}
50% {
transform: rotate(10deg);
}
100% {
transform: rotate(-10deg);
}
}
@keyframes swing2 {
0% {
transform: rotate(10deg);
}
50% {
transform: rotate(-10deg);
}
100% {
transform: rotate(10deg);
}
}
此处可以使用animation结合transform让灯笼简单的动起来,别忘了先使用transform-origin: 50% -1rem;把旋转的原点设置到灯笼顶部居中上方的位置。
拜年的福娃娃
毕竟两个福娃有很多相似性,为了节省代码,我们有大量可以复用的元素和样式。除了小男孩有一个瓜皮帽,小女孩展现了漂亮的发髻,这些我们再区分处理即可。
<div class="fuwa boy">
<div class="head">
<div class="hat">
<div class="hat-bead"></div>
<div class="hat-cloth">
<div class="hat-skeleton"></div>
<div class="hat-skeleton hat-skeleton2"></div>
<div class="hat-skeleton hat-skeleton3"></div>
</div>
<div class="hat-brim"></div>
</div>
<div class="face">
<div class="signet"></div>
<div class="ear"></div>
<div class="ear ear2"></div>
<div class="eye"></div>
<div class="eye eye2"></div>
<div class="blush"></div>
<div class="blush blush2"></div>
<div class="mouth"></div>
</div>
</div>
<div class="neck"></div>
<div class="body">
<div class="arm">
<div class="cuff"></div>
<div class="hand"></div>
</div>
<div class="arm arm-right">
<div class="cuff"></div>
<div class="hand"></div>
</div>
</div>
</div>
.fuwa {
padding: 0.3rem 0;
position: absolute;
bottom: 0;
left: 1rem;
.head {
transform-origin: bottom center;
animation: head-ani 1s infinite ease-in-out;
position: relative;
z-index: 2;
}
.hat {
margin: 0 auto;
width: 1.9rem;
position: relative;
z-index: 3;
&-bead {
margin: 0 auto;
width: 0.3rem;
height: 0.3rem;
border-radius: 100%;
background-color: #f7d185;
}
&-cloth {
margin: -0.12rem auto 0;
width: 1.8rem;
height: 0.8rem;
border-radius: 100% 100% 0 0;
background-color: #e3675c;
overflow: hidden;
position: relative;
}
&-skeleton {
width: 0.04rem;
height: 1.1rem;
border-radius: 50% 50% 0 0 / 100% 100% 0 0;
// background-color: #C95E54;
border: 0.05rem solid #c95e54;
border-radius: 100%;
border-right: none;
position: absolute;
top: 0;
left: 0.88rem;
transform-origin: 0 0;
transform: rotate(45deg);
&2 {
transform: rotate(0deg);
}
&3 {
transform: rotate(-45deg);
}
}
&-brim {
margin: -0.1rem auto 0;
width: 1.9rem;
height: 0.2rem;
border-radius: 0.2rem;
background-color: #f7d185;
position: relative;
}
}
// face
.face {
margin: -0.6rem auto 0;
width: 2rem;
height: 2rem;
border-radius: 100%;
background-color: #f7e1b6;
position: relative;
z-index: 2;
}
.signet {
margin: 0 auto;
width: 0.1rem;
height: 0.1rem;
border-radius: 100%;
background-color: #e3655b;
position: absolute;
top: 0.8rem;
left: 50%;
transform: translate(-0.05rem, 0);
}
.eye {
width: 0.5rem;
height: 0.12rem;
position: absolute;
top: 1rem;
left: 0.4rem;
overflow: hidden;
animation: eye-ani 1s infinite ease-in-out;
&::before {
content: "";
display: block;
width: 0.4rem;
height: 0.2rem;
border: 0.05rem solid #996250;
border-radius: 100%;
// border-radius: 50% 50% 0 0/100% 100% 0 0;
border-bottom: none;
}
&2 {
left: auto;
right: 0.4rem;
}
}
.ear {
width: 0.6rem;
height: 0.3rem;
border-radius: 0.6rem 0.6rem 0 0;
transform: rotate(273deg);
background-color: #f8d899;
position: absolute;
top: 0.8rem;
left: -0.36rem;
&:before {
content: "";
width: 0.6rem;
height: 0.2rem;
border-radius: 0.4rem 0.4rem 0 0;
background-color: #f7e1b6;
position: absolute;
bottom: -0.1rem;
left: 0;
}
&2 {
transform: rotate(87deg);
left: auto;
right: -0.36rem;
}
}
.blush {
width: 0.3rem;
height: 0.16rem;
border-radius: 100%;
background-color: #f2c0bc;
position: absolute;
top: 1.3rem;
left: 0.2rem;
&2 {
left: auto;
right: 0.2rem;
}
}
.mouth {
width: 0.4rem;
height: 0.2rem;
border-radius: 0 0 0.4rem 0.4rem;
background-color: #e3655b;
position: absolute;
top: 1.6rem;
left: 50%;
transform: translate(-0.2rem);
animation: mouth-ani 1s infinite ease-in-out;
}
.neck {
margin: -0.1rem auto 0;
width: 0.4rem;
height: 0.3rem;
border-radius: 0 0 0.4rem 0.4rem;
background-color: #e3655b;
position: relative;
z-index: 1;
}
.body {
width: 2.4rem;
height: 0.6rem;
margin-top: -0.16rem;
position: relative;
z-index: 4;
animation: body-ani 0.6s infinite ease-in-out;
}
.arm {
width: 1rem;
height: 0.44rem;
border-radius: 1rem 0 0 0.44rem/ 0.44rem 0 0 0.4rem;
background-color: #e3655b;
position: absolute;
left: 0.1rem;
&-right {
left: auto;
right: 0.1rem;
transform: rotate(-180deg);
z-index: 5;
}
}
.cuff {
width: 0.2rem;
height: 0.5rem;
border-radius: 0.4rem 0.4rem 0.02rem 0.02rem / 0.4rem 0.2rem 0 0;
background-color: #f7d185;
position: absolute;
top: -0.04rem;
right: 0;
}
.hand {
width: 0.4rem;
height: 0.2rem;
border-radius: 0.4rem 0.4rem 0 0;
background-color: #f8d899;
border: 0.02rem solid #f7d185;
position: absolute;
right: -0.3rem;
top: 0.1rem;
transform: rotate(90deg);
}
}
@keyframes head-ani {
0% {
transform: rotate(-6deg);
}
50% {
transform: rotate(6deg);
}
100% {
transform: rotate(-6deg);
}
}
@keyframes eye-ani {
0% {
transform: scaleY(1);
}
50% {
transform: scaleY(1.5);
}
100% {
transform: scaleY(1);
}
}
@keyframes mouth-ani {
0% {
transform: translate(-0.2rem) scaleY(1);
}
50% {
transform: translate(-0.2rem) scaleY(1.5);
}
100% {
transform: translate(-0.2rem) scaleY(1);
}
}
@keyframes body-ani {
0% {
transform: translateY(0);
}
25% {
transform: translateY(-0.06rem);
}
50% {
transform: translateY(0);
}
75% {
transform: translateY(-0.02rem);
}
100% {
transform: translateY(0);
}
}
小女孩有漂亮的发髻,需要我们单独处理一下子。
<div class="fuwa girl">
<div class="head">
<div class="hair">
<div class="hair-bun">
<!-- <div class="hair-ribbon"></div> -->
<div class="hair-rope"></div>
</div>
<div class="hair-bun hair-bun-right">
<!-- <div class="hair-ribbon"></div> -->
<div class="hair-rope"></div>
</div>
<div class="hair-forehead"></div>
</div>
<!-- face... -->
</div>
<!-- neck... -->
</div>
.fuwa {
// hair
.hair {
margin: 0 auto;
width: 1.9rem;
position: relative;
z-index: 3;
&-bun {
width: 0.6rem;
height: 0.6rem;
border-radius: 0.4rem;
background-color: #996250;
transform: rotate(-45deg);
// overflow: hidden;
position: absolute;
left: 0;
top: -0.2rem;
&-right {
left: auto;
right: 0;
transform: rotate(45deg);
.hair-ribbon {
&::before,
&::after {
left: auto;
right: -0.8rem;
border-radius: 0.8rem / 0 0.8rem 0.2rem 0;
transform-origin: center left;
transform: rotate(-20deg);
animation: ribbon-ani3 1s infinite ease-in-out;
}
&:after {
border-radius: 0.8rem / 0 0.2rem 0.8rem 0;
transform: rotate(0);
animation: ribbon-ani4 1s infinite ease-in-out;
}
}
}
}
&-rope {
width: 0.6rem;
height: 0.3rem;
position: absolute;
bottom: 0;
&::before,
&::after {
content: "";
display: block;
width: 0.3rem;
height: 0.3rem;
border-radius: 100%;
background-color: #f7d185;
position: absolute;
left: 0;
}
&:after {
left: auto;
right: 0;
}
}
&-ribbon {
width: 0.6rem;
height: 0.3rem;
position: absolute;
bottom: 0;
&::before,
&::after {
content: "";
display: block;
width: 1rem;
height: 0.2rem;
border-radius: 0.8rem 0.8rem 0 0/ 0.8rem 0.2rem;
background-color: #e3655b;
position: absolute;
top: 0;
left: -0.8rem;
transform-origin: right center;
transform: rotate(20deg);
animation: ribbon-ani1 1s infinite ease-in-out;
}
&::after {
width: 0.8rem;
left: -0.8rem;
border-radius: 0.8rem 0 0 0.8rem / 0.2rem 0 0 0.8rem;
transform: rotate(0);
animation: ribbon-ani2 1s infinite ease-in-out;
}
}
&-forehead {
margin: 0 auto;
width: 2rem;
height: 1rem;
border-radius: 2rem 2rem 0 0;
background-color: #996250;
position: relative;
&::before,
&::after {
content: "";
width: 0;
height: 0;
border-width: 0 0.08rem 0.2rem;
border-style: solid;
border-color: transparent transparent #f7e1b6;
position: absolute;
bottom: 0;
left: 0.4rem;
}
&::after {
left: auto;
right: 0.4rem;
}
}
}
}
Emm,由于这个流程主打CSS来实现,但是为了能简单适配一下不同屏幕的展示效果,来使用JavaScript简单处理一下。
window.onload = function() {
const width = document.body.clientWidth;
const height = document.body.clientHeight;
if(height > width) {
// document.querySelector('.wall').style.height = '80vw';
document.querySelector('.window').style.width = '80vw';
document.querySelector('.window').style.height = '80vw';
document.querySelector('.window').style.marginTop = 'calc(80vh - 80vw)';
}
}
写在最后
说着是一幅画,其实只能算是个毛坯房,如果你也感兴趣,可以往不同的方向玩起来,比如灯笼上面的祝福语可以动态输入生成,比如可以做成图片或者GIF自定义分享传递祝福,再比如你也可以把福娃修饰成自己的样子。
不管怎么说,希望这幅《福娃拜年》能“兔”你一乐,兔年顶呱呱。