کار با گرافیک SVG در React — از صفر تا صد
در این مقاله به توضیح روش ایمپورت کردن SVG با استفاده از Webpack و انیمیت کردن آنها از طریق CSS و کامپوننتهای استایلدار میپردازیم. SVG اینک به یک ابزار استاندارد تصویرسازی تبدیل شده است. با ما همراه باشید تا روش کار با گرافیک SVG در React را بیاموزید. در ادامه این مقاله به بررسی ابزارهای استاندارد برای ایمپورت کردن SVG در پروژههای ریاکت میپردازیم و سپس برخی تکنیکها برای قالببندی و انیمیت کردن SVG با استفاده از CSS و کامپوننت استایلدار را مورد بررسی قرار میدهیم.
اینک SVG به طور گسترده از سوی مرورگرها پشتیبانی میشود و کاربردهای آنها با بهرهگیری از استایلبندی CSS برای کنترل گذارهای حالت و انیمیشن بهبود چشمگیری یافته است. وباپلیکیشنهای مدرن امروزه از SVG-ها برای طراحی اپلیکیشنهای واکنشگرا، کاهش اندازه فایلها و تعداد کاهش درخواستهای HTTP استفاده میکنند و بدین ترتیب تجربه کاربری بهتری نسبت به تصاویر مبتنی بر پیکسل ارائه میدهند.
در فریمورک ریاکت، SVG از طریق بارگذار Webpack پشتیبانی میشود. بدین ترتیب SVG-ها میتوانند به صورت کامپوننتهایی در ماژول ایمپورت شوند و در زمان build بهینهسازی شوند. ابزار Create React App از این بارگذار از نسخه 2 به بعد استفاده میکند و SVG-ها را ایمپورت کرده و آنها را در JSX جای میدهد:
1// importing a logo SVG and embedding it in JSX
2import { ReactComponent as Logo } from './logo.svg';
3const MyComponent = () => {
4 return (
5 ...
6 <Logo />
7 );
8}
پکیج استفادهشده در کد فوق SVGR نام دارد. این پکیج گستردهترین استفاده را برای تبدیل SVG-ها به کامپوننتهای ریاکت دارد.
SVGR یا SVG ریاکت
SVGR (+) فایلهای SVG اکسترنال را میگیرد و آنها را به کامپوننتهای ریاکت تبدیل میکند. این پکیج طیفی از ابزارهای کاربردی را میزبانی میکند که هر کدام راهحلهایی عرضه میکنند که بسته به چارچوب دستکاری SVG مورد نیاز هستند.
پراستفادهترین ابزار در این زمان Webpack loader (+) است که در حال حاضر 1.4 میلیون دانلود هفتگی دارد و نشان میدهد که جامعه توسعهدهندگان جاوا اسکریپت نسبت به رویکرد ایمپورت کردن SVG-ها در ریاکت توجه زیادی دارند.
Webpack loader یکی از سه راهحل اصلی است که SVGR ارائه میکند. بسته به این که علاقه دارید SVG-ها را در خط فرمان دستکاری کنید یا نه، یک اسکریپت خودکار سازی شده وجود دارد و همچنین میتوان آنها را در ماژولها نیز ایمپورت کرد:
- svgr/cli@: ابزار CLI دستورهای سادهای در اختیار ما قرار میدهد که با آنها میتوانیم فایلهای منفرد یا کل دایرکتوریهای فایلهای SVG را به فایلهای js. کامپوننتهای ریاکت تبدیل کنیم.
- svgr/core@: این پکیج مرکزی SVGR برای جاسازی درون برنامههای NodeJS طراحی شده است تا ابزارهای خودکاری برای پردازش SVG-ها درون کامپوننتها ایجاد شود.
- svgr/webpack@: پراستفادهترین راهحل و ابزاری عالی برای ایمپورت کردن SVG-ها به صورت کامپوننتهای ریاکت است.
- پلاگین Rollup یا Parcel: دو ابزار دیگر بستهبندی ماژول جاوا اسکریپت که کمتر شناخته شدهاند، اما از SVGR پشتیبانی میکنند.
استفاده همزمان از پکیجهای فوق مزیتهای زیادی دارد. CLI و API مربوط به Node و پکیجهای Webpack میتوانند در صورت نیاز درون گردشکار مدیریت SVG در پروژههای ریاکت استفاده شوند.
ما در این مقاله صرفاً روی Webpack loader متمرکز میشویم. در ادامه از آن برای نشان دادن یک مثال واقعی از یکپارچهسازی SVG در اپلیکیشنهای مدرن بهره خواهیم گرفت.
دستکاری SVG-ها از طریق CSS
چندین روش برای استفاده از CSS همراه با SVG-ها وجود دارد، اما از آنجا که تمرکز ما روی توسعه ریاکت است به پیادهسازی آن از طریق کامپوننتهای استایلدار علاقهمند هستیم.
برای تکمیل بودن بحث، مرور سریعی بر روشهای مختلف استفاده از CSS درون SVG-ها خواهیم داشت، چون نرمافزارهایی مانند ایلاستریتور بیشک SVG را با دستکم برخی از این تکنیکها اکسپورت میکنند. با درک دقیق شیوه انتساب استایل ها به SVG-ها، میتوانید آنها را بهینهسازی کرده و مواردی که دیگر لازم نیستند یا در جای دیگری در پروژه تعریف شدهاند، حذف کنید.
خصوصیتهای درونخطی
استایل ها به طور عمده روی XML خود SVG اعمال میشوند که شبیه به وضعیت زیر است:
1// CSS properties via attributes
2<svg>
3 ...
4 <path
5 fill="none"
6 stroke-linecap="round"
7 stroke-miterlimit="10"
8 stroke="#000"
9 stroke-width="15"
10 ...
11 />
12</svg>
نکته: برخی از این مشخصهها بیشک در سطح کامپوننت پروژهها نیز تعریف میشوند، اما بهتر است برخی مقادیر پیشفرض مانند stroke ،fill ،stroke-width و stroke-linecap برای پیشنمایش آسانتر درون خود SVG بمانند.
خصوصیت استایل
با این که این وضعیت عمومیت کمتری دارد، اما خصوصیت style درون یک SVG نیز پشتیبانی میشود:
1<path
2 style="stroke: #000; ..."
3 ...
4/>
استایلشیتهای درونخطی با تگ style
در مورد پکیجهای SVG یک روش معمول دیگر نیز این است که از تگ <style /> با کلاسهای CSS برای طیفی از مسیرها و شکلها استفاده میشود. تگ <style /> درون تگ <defs /> تعریف میشود:
1<svg>
2 <defs>
3 <style>
4 .cls-1 {
5 fill: none;
6 stroke-linecap: round;
7 stroke-miterlimit: 10;
8 stroke-width:15px
9 }
10 </style>
11 </defs>
12 <path
13 class="shine cls-1"
14 ...
15 />
16</svg>
استایل یا یک شکل کلی مانند یک circle است و یا کلاسهایی ایجاد میکند که به مسیرها و شکلها انتساب مییابند.
استایلشیتهای بیرونی
اینک با جداسازی CSS از فایل SVG و بردن آن به یک استایلشیت جداگانه، به یک الگوی طراحی ماژولار نزدیکتر شدهایم. این کار دستکاری را آسانتر میسازد و استایلشیت میتواند درون هدر صفحه وب قرار گیرد و یا در خود فایل SVG گنجانده شود:
1<?xml-stylesheet type="text/css" href="svg-stylesheet.css" ?>
2<svg>
3 <circle
4 cx="20"
5 cy="20"
6 r="10"
7 />
8</svg>
این رویکرد دوم برای یک فریمورک جاوا اسکریپت ایدهآل نیست، ما به جای آن عموماً دوست داریم که استایلها در سطح کامپوننت بدون وابستگی اکسترنال مانند استایلشیت های CSS تعریف شوند. از این رو اینک به استفاده از کامپوننتهای استایلدار همراه با SVG-ها برای ایجاد برخی انیمیشنهای جذاب و همزمان با قالببندی اپلیکیشن میپردازیم.
کامپوننتهای استایلدار و SVG-ها
داشتن امکان جاسازی مشخصههای SVG CSS درون کامپوننتهای ریاکت شاید مفیدترین ابزار برای استایلبندی یک SVG از طریق CSS باشد که مزیتهای زیادی دارد:
- میتوانیم کامپوننتهای با استایلبندی سراسری را اکسپورت کرده و سپس آنها را در طیفی از کامپوننتها که نیازمند یک استایل SVG یکسان هستند، ایمپورت کنیم.
- میتوانیم برعکس امکان فوق عمل کنیم، یعنی هر کامپوننت میتواند استایلبندی منحصر به فرد خود را روی یک SVG تعریف کند. خود SVG هیچ گونه استایل غیر ضروری را ایمپورت نمیکند و یا در این مسیر هیچ استایلشیت غیر لازمی را الصاق نمیکند.
- اینک اکوسیستم کامپوننت استایلدار را در اختیار داریم و میتوانیم به قابلیتهای props و قالببندی دسترسی داشته باشیم. به این ترتیب میتوان به سادگی یک کاتالوگ از SVG-ها را بر مبنای قالب کنونی اپلیکیشن ویرایش کرد.
داشتن امکان قالببندی SVG-ها شاید بهترین کاربرد ترکیب کامپوننتهای استایلدار و SVG-ها باشد. دیگر آن روزها که برای حفظ قالب یک وبسایت باید از تصاویر و یا دیگر asset-ها استفاده میکردیم گذشته است. اینک انجام این کار به سادگی با یک SVG و دسترسی به مشخصههای آن میسر است.
نکته: درصورتیکه SVG-ها را درون یک تگ <img /> جاسازی کنید، نمیتوانید به مشخصههای SVG دسترسی داشته باشید. با این که این کار به طور رایجی در پروژههای نه چندان مهم انجام میشود، اما قویاً پیشنهاد میکنیم که از رویکرد SVGR استفاده کنید.
بررسی انیمیشن Stroke با SVG تغییر قالب
یک نمونه عالی از آن چه در مورد راهحلهای تغییر قالب میتوان بررسی کرد را در ادامه میتوانید ببینید. در این مثال SVG-ها با انیمیشن CSS ترکیب شدهاند تا اثر زیر خلق شود:
انیمیشن تغییر قالب در مثال فوق به طور کامل مبتنی بر CSS (بدون جاوا اسکریپت) است و از چند مشخصه CSS برای دستکاری Stroke در SVG بهره میگیرد. کدنویسی این مثال را در ادامه بررسی خواهیم کرد. لوگوی JKRB Investments نیز یک SVG ایمپورت شده است که کاملاً قابلیت قالبپذیری دارد. این استایلبندی SVG به صورت کامل با کامپوننتهای استایلدار اپلیکیشن یکپارچه شده است. زمانی که روی دکمه کلیک کنید، حالت قالب یک رندر مجدد SVG را تحریک میکند و آنها را به استایلبندی قالب بهروز شده گذار میدهد.
اینک سؤال این است که کدام استایلها در عمل در اینجا دستکاری شدهاند؟ در ادامه مشخصههای اصلی پیش از بررسی جزئیتر در یک دید سطح بالا توضیح داده شدهاند:
مشخصه fill مربوط به لوگوی JKRB رنگ آن را تعیین میکند. خود مقدار fill از سوی قالب جاری تعیین میشود که در این مورد light یا dark است:
1// wrapping SVGs in theme-able components
2import styled from 'styled-components';
3import theme from 'styled-theming';
4export const backgroundColor = theme('mode', {
5 light: '#fafafa',
6 dark: '#0e0f11'
7});
8const LogoWrapper = styled.div`
9 svg {
10 fill: ${backgroundColor};
11 }
12`;
مشخصه stroke دکمه قالب، رنگ آن تعیین میکند که مقدار آن نیز از قالب کنونی نشأت میگیرد. مشخصههای stroke-dasharray و stroke-dashoffset انیمیشن Stroke یکسانی ارائه میکنند. همچنین stroke-width امکان تغییر دادن ضخامت Stroke را فراهم میسازد.
انیمیشن keyframe@ برای گذار Stroke استفاده میشود و همچنین شفافیت تابش آفتاب را تنظیم میکند. بدین ترتیب SVG-ها، استایلبندی CSS، انیمیشنهای کیفریم و کامپوننتهای استایلدار قالبپذیر را برای دستیابی به این اثر با هم ترکیب کردهایم.
شاید جذابترین بخش خود انیمیشن دکمه باشد. این انیمیشن با تجزیه SVG به مسیرهای چندگانه به دست آمده است و انیمیشن Stroke آنها به صورت منفرد کنترل میشود. در بخش بعدی این موضوع را بیشتر بررسی میکنیم.
تنظیم انیمیشنهای Stroke در SVG
Stroke یک SVG به احتمال بالا یک XML به صورت path در خود فایل XML است. در توضیح زیر از اصطلاح stroke استفاده میکنیم، اما به خاطر بسپارید که Stroke خودش یک تگ در SVG است.
Stroke یک SVG را میتوان به چند روش از طریق CSS کنترل کرد. دو مشخصه کلیدی یعنی stroke-dasharray و stroke-dashoffset را قبلاً توصیف کردیم. اینها تنها دو مشخصه مورد نیاز برای ایجاد انیمیشن Stroke هستند. طرز کار آنها را در بخش بعدی توضیح میدهیم.
مشخصه stroke-dasharray
مشخصه stroke-dasharray طول خطهای تیره یک stroke را مشخص میکند. بدین ترتیب یک خط پیوسته به خط تیره گسسته تبدیل میشود. هر چه این عدد بزرگتر باشد، فواصل بین آن خطهای تیره بیشتر میشود. تصور کنید یک بوم 1000 در 1000 پیکسل داشته باشیم، یک خط روی آن بوم رسم میکنیم. سپس میزان stroke-dasharray را روی مقدار 1000 قرار دهیم:
1.line {
2 stroke-dasharray: 1000;
3}
اتفاقی که میافتد این است که فاصله بین خطهای تیره به کل طول خود Stroke تبدیل میشود. بدین ترتیب Stroke مانند یک خط ممتد پیوسته به نظر میرسد، چون فاصلهبندی تا انتهای Stroke هنوز اتفاق نیفتاده است.
اگر آفست این خط تیره را روی 100% تنظیم کنیم، در این صورت با یک Stroke کاملاً نامرئی آغاز میکنیم و سپس آن را در کل خط رسم شده انیمیت میکنیم. به این ترتیب به موضوع بخش بعدی یعنی مشخصه stroke-dashoffset میرسیم.
مشخصه stroke-dashoffset
مشخصه stroke-dashoffset میزان فاصله آغاز رندرینگ خطوط تیره از ابتدای خط را مشخص میکند. این مبدأ ابتدای Stroke خواهد بود. به بیان دیگر یک آفست روی رندرینگ آرایه خطوط تیره مرتبط تعریف میکند.
آرایه خط تیره فوق را در نظر بگیرید که هر خط تیره اندازهای برابر با خود Stroke دارد. فرض کنید میزان آفست خط تیره را نیز روی 1000 تنظیم کنیم.
1.line {
2 stroke-dasharray: 1000;
3 stroke-dashoffset: 1000;
4}
اکنون Stroke نامرئی میشود، چون فاصلهبندی بین خطوط تیره کل طول Stroke را تشکیل میدهد و خطوط تیره مرئی نیز آفستی به میزان کل طول Stroke دارند.
با استفاده از این مکانیسم و انیمیت کردن این مشخصهها با استفاده از @keyframes یا transition میتوانیم Stroke-ها را به سمت داخل و خارج انیمیت کنیم. تصور کنید انیمیشن زیر را به کد فوق اضافه کنیم:
1// animating the dash offset to "draw" the stroke
2@keyframes draw {
3 from {
4 stroke-dashoffset: 1000;
5 }
6 to {
7 stroke-dashoffset: 0;
8 }
9}
الصاق این انیمیشن به کلاس line موجب میشود که آفست خط تیره به مبدأ خود بازگردد و Stroke در کلیتش نمایش یابد. کافی است انیمیشن زیر را به خط الصاق کنید تا این اثر را ببینید:
1// animating our line to be "drawn" in
2.line {
3 stroke-dasharray: 1000;
4 stroke-dashoffset: 1000;
5 animation-name: draw;
6 animation-duration: 1s;
7 animation-fill-mode: forwards;
8 }
نکته: در مورد CSS-های پیچیده مانند این، استفاده از هر مشخصه animation- نه خوانا و نه قابل نگهداری نیست، به جای آن از مشخصه متحد animation استفاده میکنیم که ترکیبی از پیکربندی انیمیشن در یک مقدار است.
مشخصه animation-fill-mode را روی forwards تنظیم میکنیم تا مطمئن شویم که SVG در زمان پایان انیمیشن آن را در حالت نهایی حفظ میکند و دیگر به حالت قبلی پیش از انیمیت کردن که کل Stroke نمایش مییافت، باز نمیگردد.
معکوسسازی این اثر نیز ساده است. کار خود را از یک Stroke رسم شده کامل آغاز میکنیم و آن را به سمت بیرون گذار میدهیم. میتوانید یک انیمیشن دیگر با گذار از 0 به 1000 به صورت stroke-dashoffset تعریف کنید، یا انیمیشن draw ر ابا استفاده از مشخصه animation-direction معکوس بکنید.
1// un-drawing a line
2.line {
3 stroke-dasharray: 1000;
4 stroke-dashoffset: 0;
5 animation-name: draw;
6 animation-duration: 1s;
7 animation-fill-mode: forwards;
8 animation-direction: reverse;
9 }
توجه کنید که stroke-dashoffset پیشفرض اینک 0 است تا مطمئن شویم که کل خط تیره در حالت پیشفرض نمایش مییابد.
یکپارچهسازی کامپوننتهای استایلدار و قالببندی
این مفهوم را اکنون میتوان در چارچوب کامپوننت استایلدار یک گام نیز فراتر برد. فرض کنید میخواهیم در زمان انتخاب حالت تیره یک انیمیشن ماه وارد شود و زمانی که حالت روشن انتخاب میشود انیمیشن ماه خارج شود.
ابتدا SVG را به عنوان یک کامپوننت ریاکت با استفاده از پشتیبانی SVGR داخلی Webpack ایمپورت میکنیم:
1import { ReactComponent as MoonSVG } from './moon.svg';
اکنون میتوانیم استایلبندی را برای MoonSVG تعریف کنیم که انجام آن با استفاده از کامپوننتهای استایلدار نسبتاً ساده است. کافی است مشخصه animation-direction مربوط به SVG را تنظیم کنیم که بر مبنای قالب کنونی اپلیکیشن عمل میکند:
1// animate strokes based on theme
2// fetching some theme context
3const theme = useTheme();
4const StyledMoon = styled.div`
5 @keyframes draw {
6 from {
7 stroke-dashoffset: 1000;
8 }
9 to {
10 stroke-dashoffset: 0;
11 }
12 }
13 path {
14 stroke-dasharray: 1000;
15 stroke-dashoffset: ${theme.mode === 'dark' ? 1000 : 0 };
16 animation-name: draw;
17 animation-duration: 1s;
18 animation-fill-mode: forwards;
19 animation-direction: ${theme.mode === 'dark' ? `normal` : `reverse`};
20 }
21 `;
توجه کنید که stroke-dashoffset نیز بر مبنای قالب کنونی تعیین میشود. علاوه بر آن ما مشخصهها را در بلوک path قرار دادهایم. StyledMoon خودش یک SVG نیست، بلکه عنصری است که SVG ما در آن قرار دارد. از این رو میتوان گفت که عنصر SVG درون StyledMoon این مشخصهها را به ارث میبرد.
نکته: میتوان یک خصوصیت class یا id را به جای path پیادهسازی کرد تا یک بخش خاصی از یک SVG را استایلبندی کرد. در مورد StyledMoon چندین SVG را قرار دادهایم. بدین ترتیب اکنون میتوانیم SVG و کامپوننت استایلدار را همراه با هم رندر کنیم تا اجرای انیمیشن را ببینیم:
1...
2const MyComponent = () => {
3 return(
4 <StyledMoon>
5 <MoonSVG />
6 </StyledMoon>
7 );
8}
انیمیت کردن مسیرهای چندگانه
دکمه تغییر قالب که قبلاً نشان دادیم در واقع ترکیبی از 4 مسیر است که یکی مسیر استاتیک و سه مورد دیگر مسیرهای انیمیت شده هستند. با دادن calss یا id به هر مسیر میتوانیم به صورت انفرادی به هر مسیر اشاره کنیم تا استایلبندی و رفتار انیمیشن منحصربهفردی به هر کدام اختصاص دهیم.
نکته: میتوانیم هر مسیر را به عنوان یک SVG مجزا نیز اکسپورت کنیم و چنان که پیشتر اشاره کردیم، آنها را با یک کامپوننت استایلدار در یک محل قرار دهیم. با اعمال تنها یک رنگ Stroke چهار مسیر ترکیبی به صورت زیر به دست میآید:
آیا میتوانید تشخیص دهید کدام مسیرهای این SVG انیمیت میشوند و از این رو مسیرهای جداگانهای محسوب میشوند؟
- اشعههای تابش آفتاب که پیرامون خورشید یا ماه را احاطه کرده از طریق مشخصه opacity به صورت fade in و fade out انیمیت میشوند و انیمیشن Stroke عملاً روی آنها اعمال نشده است.
- سمت چپ خورشید/ماه تنها مسیر استاتیک است که انیمیت نمیشوند.
- سمت راست خورشید یک مسیر جداگانه است که در زمان سوئیچ کردن به حالت تیره انیمیت میشود.
- منحنی ماه مسیر جداگانهای است که هنگام سویچ کردن به حالت تیره انیمیت میشود.
به عنوان جمعبندی SVG در تصویر زیر به قطعات جداگانهای تقسیم شده است:
سخن پایانی
در این مقاله ابزارهای ایمپورت کردن SVG-ها درون یک پروژه ریاکت را بررسی کردیم و در مورد شیوه دستکاری آنها از طریق CSS و کامپوننتهای استایلدار توضیحاتی ارائه کردیم. همچنین تکنیکهای انیمیشن را به طور خاص با انیمیشن Stroke توضیح دادیم که امروزه در وب دستکم گرفته شده است. امیدواریم این مطالب شما را تشویق کرده باشد تا در خصوص موارد استفاده این روش در پروژههایتان تأمل بکنید. این بهینهسازیهای UX در نهایت منجر به تجربه کاربری بهتری میشوند و برند شما را تقویت میکنند.
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای JavaScript (جاوا اسکریپت)
- آموزش JavaScript ES6 (جاوا اسکریپت)
- مجموعه آموزشهای برنامهنویسی
- ۲۲ ابزار مهم برای توسعه دهندگان React — فهرست کاربردی
- آموزش ری اکت (React) — مجموعه مقالات مجله فرادرس
==