عنصر pattern در SVG (الگوها در SVG)

از الگوها در طراحی برای بوجود آوردن تضاد، عمق دادن و زیبایی استفاده می‌شود.

معمولا الگوها در طراحی وب سایت‌ با استفاده از تصاویر بوجود می‌آیند اما بهتر است بدانیم که می‌توانیم آنها را با استفاده SVG نیز بسازیم که این باعث می شود از نظر کیفیت تصویر، خروجی بهتری داشته باشیم و همینطور نیازی نیست برای یک تصویر، درخواستی اضافه به سرور ارسال کنیم.

الگوها شبیه به background-repeat در CSS عمل می‌کنند و قابل تکرار هستند به همین دلیل می‌توان طرح‌های منحصر به فردی را با استفاده از آنها بوجود آورد.

این کار در SVG با عنصری به نام <pattern> قابل اجرا می‌باشد. که با استفاده از آن می‌توان داخل و حاشیه ترسیمات و متون در SVG را رنگ یا بقولی نقاشی کرد.

در ادامه با نحوه ساخت و استفاده الگوها در SVG آشنا می‌شویم.

رسم الگو در Fill

نمونه‌ای ساده از ساخت و استفاده از یک الگو را در زیر مشاهده می‌کنیم:


<svg>
  <defs>
    <pattern id="circles" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse">
      <circle cx="10" cy="10" r="10" fill="#64bee3" />
    </pattern>
  </defs>
  <rect width="300" height="140" stroke="#333" stroke-width="1" fill="url(#circles)" />
</svg>

این کد الگو موجود را داخل یک مستطیل نقاشی می کند که خروجی زیر را خواهد داشت:

در کد بالا هدف اول ما ساخت یک مستطیل است که این کار را با استفاده از عنصر rect بصورت زیر انجام می‌دهیم: (به دلیل کوتاه کردن کد قسمت stroke حذف شده است)


<rect width="300" height="140" fill="url(#circles)" />

اگر مقالات موضوع SVG را به ترتیب در این سایت خوانده باشید با کد بالا آشنایی دارید، تنها نکته‌ای که به چشم می‌خورد مقدار ویژگی fill است که یک تایع url می‌باشد.

معمولا برای fill از مقادیر رنگی استفاده میکنیم اما اینجا هدف از استفاده از url این است که بتوانیم آدرس یک الگو که از قبل تعریف شده را به ویژگی fill بدهیم تا داخل مستطیل ما به جای یک رنگ ساده، یک الگو نقاشی شود. که برای این کار کافی است به الگو یک id داده و سپس نام آن را به url ویژگی fill بدهیم.

پس کلیت ماجرا به این قرار است که ما داخل تگ <pattern> طرحی را تعریف می‌کنیم که همان الگوی ما می‌باشد، سپس کافی است آدرس آن الگو را به عنصری که می‌خواهیم آن طرح را داشته باشد (در این مورد مستطیل) بدهیم.

در جمله بالا منظور ما از تعریف کردن چیست؟

در SVG همانطور که در مقالات قبل اشاره شد ‌می‌توان از طریق تگ <defs> اشکال یا طرح‌هایی را تعریف کرد که تا زمانی که عنصر دیگری از آنها استفاده نکند در خروجی نمایش داده نشوند. معنای استفاده کردن در اینجا همان آدرس دهی از طریق id می‌باشد که در کد بالا مشاهده کردیم.

به بیان ساده یک شکل خوشگل می‌کشیم بعد اون شکله صدا میکنه میگه هی الگو، با تو ام! بیا اینجا منو رنگ کن 😬


<defs>
  <pattern id="circles">
    
  </pattern>
</defs>

<rect fill="url(#circles)" />

height ،width ،y ،x

ویژگی‌های اولیه عنصر <pattern> را با هم بررسی می‌کنیم:

ویژگی‌های width و height نیز اندازه و عرض یک تکه از الگو که قرار است تکرار شود را مشخص می‌کنند.

به عنوان نمونه عرض و ارتفاع الگو مثال بالا را به 40 افزایش می‌دهیم:


<svg>
  <defs>
    <pattern id="circles" x="0" y="0" width="40" height="40" patternUnits="userSpaceOnUse">
      <circle cx="10" cy="10" r="10" fill="#64bee3" />
    </pattern>
  </defs>
  <rect width="300" height="140" stroke="#333" stroke-width="1" fill="url(#circles)" />
</svg>

خط قرمز جهت راهنمایی برای تعیین اندازه هر تکه از الگو به اندازه 40 واحد می‌باشد:

x و y به ترتیب مشخص می‌کنند که شروع رسم الگو از چه فاصله‌ای با لبه هر تکه از الگو در راستای افقی و عمودی باشد.

حال مقدار x و y را از 0 به 10 تغییر می‎‌دهیم:


<svg>
  <defs>
    <pattern id="circles" x="10" y="10" width="40" height="40" patternUnits="userSpaceOnUse">
      <circle cx="10" cy="10" r="10" fill="#64bee3" />
    </pattern>
  </defs>
  <rect width="300" height="140" stroke="#333" stroke-width="1" fill="url(#circles)" />
</svg>

همانطور که مشاهده می‌کنیم دایره‌ها در هر تکه به فاصله 10 واحدی از لبه سمت چپ و بالای آن تکه رسم شده است:

به بیان دیگر x و y شبیه به یک padding برای هر تکه از الگو عمل می‌کنند.

توجه داشته باشیم که وقتی حرف از مختصات برای الگو می‌شود منظور فقط نقطه شروع رسم می‌باشد (یعنی قلم نقاش از آن نقطه شروع به حرکت می‌کند) و این بدین معنی نیست که قبل از آن نقطه، الگو رسم نمی‌شود، نه بلکه تکرار همان الگو می‌تواند در آن قسمت پدیدار شود.

کافی است برای درک بهتر موضوع کمی با مقادیر این ویژگی‌ها بازی کنیم:

See the Pen
SVG Pattern Example
by Mojtaba Seyedi (@seyedi)
on CodePen.

ویژگی patternUnits

همانطور که در مثال‌های بالا مشاهده می‌کنیم اعدادی که برای مختصات و عرض و ارتفاع داده شده‌اند هیچ کدام واحدی ندارد. برای تعیین واحد و سیستم مختصات الگوها از ویژگی patternUnits استفاده می‌شود. این ویژگی دو مقدار می‌پذیرد که به شرح زیر هستند:

userSpaceOnUse: اگر از این مورد استفاده کنیم واحدهای الگو بر اساس سیستم مختصات خود عنصر <pattern> هستند و این بدین معنی است که الگو در هرکجا که استفاده بشود اندازه اشکال داخلی آن یکسان خواهند بود و کوچک یا بزرگ نخواهند شد. مثال‌هایی که تا اینجا بررسی کردیم از همین نوع هستند.

objectBoundingBox (مقدار پیشفرض): فرض کنید بخواهیم مختصات و واحدهای مشخصات الگو بر اساس شکلی که قرار است الگو را استفاده کند تعیین شود. به بیان ساده اندازه الگو بستگی به شکلی خواهد داشت که از آن استفاده می‌کند، در نتیجه با توجه به مختصات آن شکل اندازه الگو تغییر خواهد کرد.

مثال بالا را بار دیگر با استفاده از مقدار objectBoundingBox باز نویسی می‌کنیم:


<svg>
  <defs>
    <pattern id="circles" x="0" y="0" width="40" height="40" patternUnits="objectBoundingBox">
      <circle cx="10" cy="10" r="10" fill="#64bee3" />
    </pattern>
  </defs>
  <rect width="300" height="140" stroke="#333" stroke-width="1" fill="url(#circles)" />
</svg>

البته همانطور که گفته شد objectBoundingBox مقدار پیش‌فرض است در نتیجه اگر قصد استفاده از آن را داریم می‌توانیم ویژگی patternUnits را ننویسیم.

چه اتفاقی افتاد؟

در زمان تعریف الگو اندازه هر تکه را برابر با 40 واحد قرار دادیم، زمانی که از مقدار objectBoundingBox استفاده کنیم، هر واحد برابر با کل عرض و یا ارتفاع مختصات شکلی می‌شود که از الگو استفاده می‌کنند، در مثال ما این شکل همان مستطیل است. در نتیجه در این مثال الگو عرضی برابر با 40*300 و ارتفاع آن برابر با 40*140 می‌باشد. یعنی اگر میخواهیم عرض و ارتفاع الگو کاملا برابر با عرض و ارتفاع شکل استفاده کننده از الگو باشد، کافی است آن‌ها را برابر با 1 قرار دهیم:


<svg>
  <defs>
    <pattern id="circles" x="0" y="0" width="1" height="1" patternUnits="objectBoundingBox">
      <circle cx="10" cy="10" r="10" fill="#64bee3" />
    </pattern>
  </defs>
  <rect width="300" height="140" stroke="#333" stroke-width="1" fill="url(#circles)" />
</svg>

پس در نتیجه اگر میخواهیم تکرار یک الگو را در شکل ببینیم باید اندازه عرض و ارتفاع و مختصات الگو بین 0 تا 1 باشند.

اگر بخواهیم الگو اولیه این پست را بوجود بیاوریم نیاز داریم که اندازه دایره استفاده شده در الگو را تقسیم به عرض یا ارتفاع مستطیل یا همان شکل نهایی کنیم که در نتیجه با توجه به اعداد زیر خواهیم داشت:


20/300 = 0.0666 // circle width ÷ rect width
20/140 = 0.142    // circle height ÷ rect height


<svg>
  <defs>
    <pattern id="circles" x="0" y="0" width="0.0666" height="0.142">
      <circle cx="10" cy="10" r="10" fill="#64bee3" />
    </pattern>
  </defs>
  <rect width="300" height="140" stroke="#333" stroke-width="1" fill="url(#circles)" />
</svg>

ویژگی patternContentUnits

این ویژگی مشابه ویژگی patternUnits می‌باشد با این تفاوت که ویژگی patternUnits در مورد خود عنصر <pattern> می‌باشد اما ویژگی patternContentUnits موثر بر روی محتوای و عناصر داخل عنصر <pattern> است.

مقادیر این ویژگی نیز مشابه ویژگی patternUnits هستند با این تفاوت که مقدار پیشفرض برای این ویژگی userSpaceOnUse است که یعنی اگر این ویژگی را برای الگو تنظیم نکنیم، بصورت پیشفرض سیستم مختصات محتوا و عناصر داخل عنصر <pattern> همیشه اندازه ثابتی خواهند داشت.

حال فرض کنید بخواهیم این مقدار را تغییر دهیم و از objectBoundingBox استفاده کنیم که در این صورت می‌توانید مثال زیر را بررسی کنید:


<svg>
  <defs>
    <pattern id="circles" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse" patternContentUnits="objectBoundingBox">
      <ellipse cx="0.0333" cy="0.071" rx="0.0333" ry="0.071" fill="#64bee3" />
    </pattern>
  </defs>
  <rect width="300" height="140" stroke="#333" stroke-width="1" fill="url(#circles)" />
</svg>

در مثال بالا فرض کنید نیاز داریم که عرض هر الگو همان 20 باشد، چون واحدهای مربوط به محتوای الگو (در اینجا یعنی عنصر دایره) از سیستم مختصات عنصری که قرار است الگو را استفاده کند (در اینجا یعنی مستطیل) تاثیر می‌گیرند در نتیجه اول باید نسبت بین هر الگو (20) و عرض مستطیل (300) را بدست بیاوریم:


20/300 = 0.0666 // circle width ÷ rect width

این اندازه یک قطر دایره است پس در نتیجه شعاع باید 0.03 باشد. در اینجا چون شعاع در هر راستا از سیستم مختصات پیروی می‌کند پس شعاع عمودی متناسب با ارتفاع مستطیل خواهد بود در نتیجه اگر به دنبال ساخت دایره هستیم باید مقدار شعاع y را بطور مجزا تعیین کنیم که در نتیجه شکل ما دایره باشد، که البته چون دو شعاع داریم باید از عنصر بیضی (<ellipse>) استفاده کنیم.


20/140 = 0.142    // circle height ÷ rect height

نکته مهم اینکه وقتی برای عنصر patternUnits ویژگی viewBox را تنظیم کنیم ویژگی patternContentUnits دیگر تاثیری بر روی عناصر الگو نخواهد داشت.

رسم الگو در stroke

اگر بخواهیم الگوی ما به عنوان Stroke در طرح ما باشد کافی است به روش زیر آدرس دهی الگو را به ویژگی stroke بدهیم:


<svg width="320" height="160">
  <defs>
    <pattern id="circles" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse">
      <circle cx="10" cy="10" r="10" fill="#64bee3" />
    </pattern>
  </defs>
  <rect x="10" y="10" width="300" height="140" stroke="url(#circles)" stroke-width="20" fill="none" />
</svg>

همانطور که مشاهده میکنیم اندازه عرض stroke برابر با اندازه الگو می‌باشد، همچنین مستطیل 10 واحد یعنی نصف هر الگو به مرکز ترسیم حرکت کرده است تا بتوانیم تمام stroke را مشاهده کنیم:

رسم الگو در متن

اگر متنی با استفاده از عنصر <text> داشته باشیم، می‌توانیم آدرس یک الگو را در ویژگی fill آن قرار داده و خروجی زیر را داشته باشیم:


<svg width="600" height="400">
  <defs>
    <pattern id="textPattern" x="7" y="7" width="10" height="10" patternUnits="userSpaceOnUse" patternTransform="rotate(45)">
      <rect x="5" y="5" width="5" height="5" fill="#64bee3" />
    </pattern>
  </defs>
  <text x="0" y="50%" font-size="200" fill="url(#textPattern)">SVG</text>
</svg>

SVG

استفاده از مسیر (path) برای ساخت الگو

تا اینجای کار در تمام مثال‌ها برای ساخت الگو از عنصر دایره استفاده کردیم اما همانطور که گفته شد الگو یک طرحی است که می‌تواند هرآنچه که در SVG وجود دارد بصورت تکی یا گروهی باشد. به عنوان مثال می‌‌توان از عنصر مسیر الگویی را بوجود آورد و از آن در عناصر مختلف استفاده کرد:


<svg>
  <defs>
    <pattern id="pathPattern" x="4" y="4" width="25" height="25" patternUnits="userSpaceOnUse">
      <path d="M 0 0 Q 5 20 10 10 T 20 20" stroke="#64bee3" fill="none" />
    </pattern>
  </defs>
  <rect width="300" height="140" stroke="#333" stroke-width="1px" fill="url(#pathPattern)" />
</svg>

خروجی قشنگمون رو مشاهده می‌کنید :)

استفاده از عنصر تصویر برای ساخت الگو

همچنین می‌توان از یک تصویر برای ساخت یک الگو کمک گرفت:


<svg>
  <defs>
    <pattern id="imagePattern" x="0" y="0" width="10" height="10" patternUnits="userSpaceOnUse">
      <image xlink:href="https://assets.css-tricks.ir/images/ants.gif" x="0" y="0" width="20" height="20"></image>
    </pattern>
  </defs>
  <rect width="300" height="140" stroke="#333" stroke-width="1px" fill="url(#imagePattern)" />
</svg>

در اینجا ما از یک تصویر متحرک استفاده میکنیم:

مورچه

با کمی بازی با اعداد خروجی را بصورت زیر داریم:

ویژگی patternTransform

با استفاده از ویژگی patternTransform می‌‌توان الگو را دچار چرخش یا انواع دیگر دگرگونی کرد.


<svg>
  <defs>
    <pattern patternTransform="rotate(20) skewX(30) scale(1 0.5)" id="circles" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse">
      <circle cx="10" cy="10" r="10" fill="#64bee3" />
    </pattern>
  </defs>
  <rect width="300" height="140" stroke="#333" stroke-width="1" fill="url(#circles)" />
</svg>

در مثال بالا از چندین تابع بصورت همزمان استفاده شده است اما می‌توانیم فقط از یک تابع هم استفاده کنیم.

لیست توابع قابل استفاده برای این ویژگی را می‌توانیم در مطلب مربوط به Transform مشاهده کنیم.

الگوهای تودرتو

یک قابلیت جالب در الگوها این است که می‌توان در ساخت یک الگو از الگوی دیگری استفاده کرد و با استفاده از این تکنیک الگوهای تودرتو بوجود آورد.

در مثال زیر در الگوی شماره دو از الگوی شماره یک استفاده شده است و در آخر الگوی شماره دو در مستطیل اصلی به نمایش گذاشته شده است:


<svg>
  <defs>  
    <pattern id="yellowPattern" x="5" y="5" width="75" height="75" patternUnits="userSpaceOnUse">
      <circle cx="22" cy="22" r="14" stroke="#f19450" stroke-width="2" fill="#f6bf35" />
    </pattern>
    
    <pattern id="greenPattern" x="10" y="10" width="50" height="50" patternUnits="userSpaceOnUse">
      <rect x="2" y="2" width="30" height="30" stroke="#5cbc8f" stroke-width="2" fill="url(#yellowPattern)" />
    </pattern>
  </defs>
  
  <rect width="300" height="150" stroke="#333333" stroke-width="2" fill="url(#greenPattern)" />
</svg>  

همچنین دموی زیر یک نمونه از الگوهای تودرتو می‌باشد:

See the Pen
Nested Pattern
by Mojtaba Seyedi (@seyedi)
on CodePen.

پشتیبانی مرورگر ها

تمامی مرورگرها مدرن عنصر <pattern> را پشتیانی می‌کنند و بدون مشکلی می‌توان از این روش برای ساخت الگوها در SVG استفاده کرد.

در ضمن ویژگی xlink:href مربوط به عنصر <pattern> می‌باشد که در SVG2 حذف شده است، در نتیجه استفاده نکنید.

مقالات و ابزارهای مفید

سوالت رو توی پنل بحث و گفتگو مطرح کن.

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *