ساخت انیمیشن CSS برای تعیین رتبه با ستاره – از صفر تا صد
در این مقاله به بررسی یک روش ساده اما قدرتمند برای استفاده از سلکتورهای فرزند قبلی در CSS به منظور ساخت انیمیشن CSS برای تعیین رتبه با ستاره میپردازیم. در ابتدای این مقاله مقداری در مورد مباحث نظری بحث خواهیم کرد. در نهایت کامپوننتی که میخواهیم بسازیم عملکردی مانند تصویر زیر دارد:
تعیین رتبه با ستاره در CSS
ابتدا الزامات پروژه و سلکتورهای CSS و ترکیبهای موجود را بررسی میکنیم. کامپوننت تعیین رتبه ما شامل 5 ستاره خواهد بود که هر یک با یک عنصر HTML نمایش مییابند و در کانتینر والد نگهداری میشوند. زمانی که کاربر روی یک عنصر کلیک میکند و یا ماوس را روی آن میبرد، رنگ ستاره فعال میشود و همه ستارههای قبل از آن نیز به همین ترتیب بهروزرسانی میشوند. همچنین ستارههای پس از ستاره منتخب نباید رنگ فعالی داشته باشند.
ما این مسئله را با استفاده از سلکتورهای CSS، انواع Display و Combinator–ها حل میکنیم. CSS خود یک زبان برنامهنویسی نیست، بلکه یک زبان استایل شیت است که برای توصیف نمایش یک سند نوشته با یک زبان markup مانند HTML مورد استفاده قرار میگیرد. به بیان دیگر ما نمیتوانیم به مرورگر بگوییم که چه کاری انجام دهد، بلکه میتوانیم بگوییم که یک markup را چطور نمایش دهد.
با استفاده از شبه سلکتورهایی مانند hover ،:focus ،:focus-within: و checked: میتوانیم رفتار کلیک کردن و لمس یک عنصر را در مدل شیء سند (DOM) یا به عبارت سادهتر در HTML رندر شده، تقلید کنیم.
توجه کنید که هیچ روش مستقیمی برای هدفگیری «همنیای قبلی» (Previous Sibling) وجود ندارد و ما تنها میتوانیم کانتینر والد، فرزند و موارد مرتبط را در روش آبشاری هدفگیری کنیم. اصولاً دلیل این که CSS استایلشیتهای آبشاری نامیده شده، همین است. معنی آن چنین است که از نظر فنی نمیتوانیم «همنیای سابق» را انتخاب کنیم، اما میتوانیم دقیقاً معکوس آن عمل کنیم، یعنی آن دو را ارائه کنیم.
بازنویسی الزامات قواعد CSS
- زمانی که ماوس روی عنصر ستاره میرود، آن ستاره باید رنگ active خود و همه ستارههای قبلی را تغییر دهد. همچنین ستاره قبلی در رتبه مورد نظر نمیتوانند استایلبندی شوند. از این رو این فرایند را کاملاً معکوس میکنیم. در واقع ما به کاربر دروغ میگوییم و آن را سلکتور فرزند قبلی مینامیم.
- از آنجا که امکان ارائه یک سند وجود دارد، ما نیز ترتیب عناصر را چنین ارائه میکنیم. چند روش برای انجام این کار وجود دارد، اما دو مورد را توضیح میدهیم. حالت دوم یک روش ضد الگو است و نباید از آن استفاده کنید. از این روش تنها برای نمایش این که روش دیگری نیز برای انجام این کار وجود دارد استفاده میکنیم.
گزینه یکم: نمایش Flex و flex-direction
از آنجا که میخواهیم یک ردیف را نمایش دهیم، خصوصیت flex-direction: row-reverse; را روی کلاس star-rating. اعمال میکنیم تا ترتیب همه عناصر فرزند تغییر یابد. توجه کنید که ما تغییری در DOM ایجاد نمیکنیم، بلکه شیوه ارائه آن را تغییر میدهیم. بدین ترتیب HTML ما اینک به صورت زیر در آمده است:
1<div class="star-rating">
2 <div class="star star-1"></div>
3 <div class="star star-2"></div>
4 <div class="star star-3"></div>
5 <div class="star star-4"></div>
6 <div class="star star-5"></div>
7</div>
CSS اعمال شده روی کلاس .star-rating موجب میشود که ترتیب همه چیز معکوس شود.
رنگ ستاره را عوض میکنیم
به این منظور از :hover برای تغییر رنگ روی ستاره منتخب استفاده میکنیم و با استفاده از CSS کامبیناتور همنیای عمومی (~) را به جای کامبیناتور همنیای مجاور (+) مورد استفاده قرار میدهیم. اگر از کامبیناتور مجاور استفاده میکردیم، باید کدی مانند زیر مینوشتیم:
1.star-rating .star:hover, // the selected star
2.star-rating .star:hover+.star, // the sibling after it
3.star-rating .star:hover+.star+.star, // and so on
4.star-rating .star:hover+.star+.star+.star, // and so on
5.star-rating .star:hover+.star+.star+.star+.star, // and so on
6.star-rating .star:hover+.star+.star+.star+star+.star // and so on..
7{
8 background-color: red;
9}
حجم این کد بالا است. علاوه بر آن CSS نامناسبی دارد. از این رو به جای آن، کدی مانند زیر مینویسیم:
1.star-rating .star:hover,
2.star-rating .star:hover ~ .star{
3 background-color: red;
4}
این کد بسیار آسانتر است.
توجه کنید که ما نه تنها همنیای قبلی را استایلبندی میکنیم، بلکه همنیای بعدی را نیز استایلبندی میکنیم، اما ترتیب ارائه آنها را به کاربر عوض میکنیم.
گزینه دوم: استفاده از SCSS و Grid
در این بخش یک رویکرد SCSS را برای حل این مسئله ارائه میکنیم. توجه کنید که این روش یک رویکرد ضد الگو دارد و نباید در پروداکشن مورد استفاده قرار گیرد. در کد زیر از SCSS برای برنامهنویسی عملی قابلیت و تولید CSS نهایی استفاده کردهایم:
1// DO USE THIS IN CSS, it wont work because it's SCSS.
2@for $i from 1 through 5 {
3 .star-#{$i} {
4 grid-area: star-#{$i} ;
5 }
6}
این کد به صورت CSS زیر کامپایل میشود.
1.star-1 {grid-area: star-1;}
2.star-2 {grid-area: star-2;}
3.star-3 {grid-area: star-3;}
4.star-4 {grid-area: star-4;}
5.star-5 {grid-area: star-5;}
در اینجا ما 5 کلاس بیاستفاده داریم که مساحت هر ستاره را توصیف میکنند. آنها را به صورت دستی طوری بازچینی میکنیم که از grid-template استفاده کنند.
1.star-rating-grid {
2 display: grid;
3/* This is what flex-direction: row-reverse is doing */
4 grid-template:
5 'star-5 ... star-4 ... star-3 ... star-2 ... star-1';
6}
بدین ترتیب CSS را فریب میدهیم.
کاربردهای پیشرفته: SCSS Mixin
اگر دانش شما در زمینه CSS اندک است، در این صورت بهتر است مطالعه این بخش را تا زمانی که اطلاعات بیشتری کسب کنید به تأخیر بیندازید. اما اگر اعتماد به نفس بیشتری دارید، در این بخش یک آمیزه جالب برای استفاده ارائه میکنیم. شما میتوانید بخش :hover را نیز هر جا که دوست دارید حذف کنید:
1@mixin previousSibling($parent, $child, $sibling) {
2 #{$parent} {
3 display: flex;
4 flex-direction: row-reverse;
5 #{$child}:hover ~ #{$sibling }{
6 @content;
7 }
8 }
9}
10@include previousSibling(".star-rating", ".star", "*") {
11 // Do something
12 background-color: red;
13}
در نهایت کد مثال ما به صورت زیر است. شما میتوانید این فایلها را روی سیستم خود کپی کرده و آن را در عمل بررسی کنید:
فایل HTML
1<h2>With Flex</h2>
2<div class="star-rating">
3 <div class="star star-1"></div>
4 <div class="star star-2"></div>
5 <div class="star star-3"></div>
6 <div class="star star-4"></div>
7 <div class="star star-5"></div>
8</div>
9
10 <h2>With Grid Templates</h2>
11 <div class="star-rating-grid">
12 <div class="star star-1"></div>
13 <div class="star star-2"></div>
14 <div class="star star-3"></div>
15 <div class="star star-4"></div>
16 <div class="star star-5"></div>
17 </div>
فایل SCSS
1body{
2 margin: 50px 10px;
3}
4
5
6
7/* Parent Container of All the stars */
8.star-rating {
9 display: flex;
10 flex-wrap: wrap;
11 justify-content: space-around;
12
13 /* reverse the order to allow hover */
14 flex-direction: row-reverse;
15}
16
17/* Change the color on the selected */
18/* Change the selected star and all siblings "before it" */
19.star-rating .star:hover,
20.star-rating .star:hover ~ .star{
21 background-color: red;
22}
23
24/* If using Adjacent sibling combinators */
25/*
26.star-rating .star:hover+.star+.star,
27.star-rating .star:hover+.star+.star+.star,
28.star-rating .star:hover+.star+.star+.star+.star,
29.star-rating .star:hover+.star+.star+.star+star+.star{
30 background-color: red;
31}
32*/
33
34
35.star {
36 cursor: pointer;
37 width: 2em;
38 height: 2em;
39 background-color: grey;
40 -webkit-clip-path: polygon(
41 50% 0%,
42 63% 38%,
43 100% 38%,
44 69% 59%,
45 82% 100%,
46 50% 75%,
47 18% 100%,
48 31% 59%,
49 0% 38%,
50 37% 38%
51 );
52 clip-path: polygon(
53 50% 0%,
54 63% 38%,
55 100% 38%,
56 69% 59%,
57 82% 100%,
58 50% 75%,
59 18% 100%,
60 31% 59%,
61 0% 38%,
62 37% 38%
63 );
64}
65
66/*
67Unnessarily complex scss with css-grid.
68Only used to illustrate how the
69*/
70
71
72/* With Grid Templates */
73.star-rating-grid{
74 display: grid;
75 margin: 50px;
76 grid-template:
77 'star-5 ... star-4 ... star-3 ... star-2 ... star-1';
78 /* Change the selected star and all siblings "before it" */
79 .star:hover, .star:hover ~ .star{
80 background-color: red;
81 }
82
83}
84
85
86// DO NOT USE THIS IN CSS
87@for $i from 1 through 5 {
88 .star-#{$i} {
89 grid-area: star-#{$i} ;
90 }
91}
92
93
94// SCSS MIXIN FOR previous-child selector?
95@mixin previousSibling($parent, $child, $sibling) {
96 #{$parent} {
97 display: flex;
98 flex-direction: row-reverse;
99 #{$child} ~ #{$sibling }{
100 @content;
101 }
102 }
103}
104
105// @include previousSibling(".star-rating", ".star", ".star") {
106// background-color: red;
107// }
108
109// SCSS MIXIN FOR previous-child hover effects ?
110@mixin previousSiblingHover($parent, $child, $sibling) {
111 #{$parent} {
112 display: flex;
113 flex-direction: row-reverse;
114 #{$child}:hover ~ #{$sibling }{
115 @content;
116 }
117 }
118}
119
120@include previousSiblingHover(".star-rating", ".star", ".star") {
121 background-color: red;
122}
123
124
125// LIMITED SUPPORT: Just like single parents ?
126// you can use this mixin to select the $parent, the desired $child and every other child down the line UNTIL the discrimination begins...
127// Can you use? Ask the court: https://caniuse.com/#search=%3Anot
128// If you're a jack-ass parent and want to exclude the "youngest" child (last in order)
129@mixin selectUntil($parent,$child,$youngestChild) {
130 #{$parent}{
131 #{$child}:not(:nth-child(n+#{$youngestChild})) {
132 @content;
133 }
134 }
135}
136
137@include selectUntil(".star-rating", ".star", 3) {
138 // background-color: black;
139}
به این ترتیب به پایان این مقاله میرسیم و امیدواریم این مطلب مورد توجه شما قرار گرفته باشد.