لإزالة المساحة غير المتوقعة حول مثيل use في SVG، حدّد ما إذا كانت ناتجة عن viewBox المصدر، أو ملاءمة نسبة العرض إلى الارتفاع، أو صندوق السطر في CSS المحيط. يوضح هذا الدليل كيفية تشخيص كل سبب وإصلاحه مع إبقاء إحداثيات الموضع قابلة للتوقع.

المهمة تعديل صغير لصفحة ويب على سطح المكتب: ضع شكلاً قابلاً لإعادة الاستخدام بدقة داخل SVG، ثم اجعل تخطيط HTML المحيط ملائماً كما هو مقصود. يمكنك اتباع الأمثلة في محرر نصوص عادي على جهاز Mac، وفتح ملف HTML المحفوظ في متصفح حديث على سطح المكتب.

أي نظام إحداثيات يتحكم في المساحة؟

هناك ثلاثة حدود ينبغي فحصها: صندوق CSS لعنصر SVG الخارجي، ومنفذ العرض المستخدم لرمز مشار إليه، والشكل الفعلي داخل ذلك الرمز. تميّز W3C صراحةً بين منفذ عرض SVG ومنفذ عرض CSS للمتصفح. يربط viewBox إحداثيات المستخدم في SVG بمنفذ عرض SVG؛ ولا يصف نافذة المتصفح. أنظمة الإحداثيات والتحويلات والوحدات — SVG 2

على سبيل المثال، يوفّر x="20" على عنصر use إزاحة في نظام إحداثيات SVG الخاص به. ولا يعني ذلك 20 بكسل CSS من الحافة اليسرى لنافذة المتصفح. يمكن أن يغيّر التحجيم وتحويلات العناصر السلف الموضع الذي تظهر فيه هذه الإزاحة على الصفحة. تصف مواصفة SVG التموضع باستخدام use على أنه إزاحة إضافية، يختلف سلوكها عن تخطيط صناديق CSS. بنية المستند — SVG 2

ابدأ بجدول القرار هذا. إنه تسلسل تشخيصي مستمد من آليات التخطيط الموثقة، وليس قائمة إصلاحات قابلة للتبادل.

ما تلاحظه ما ينبغي فحصه أولاً التصحيح المرجح
شريط ضيق أسفل SVG بأكمله التنسيق المضمّن وارتفاع سطر العنصر الأب اجعل SVG المستقل على مستوى الكتلة، أو اضبط محاذاته المضمّنة
مساحة فارغة داخل كل رسم متكرر إحداثيات الشكل مقارنةً بـviewBox الرمز قلّص حدود المصدر إذا كانت الهوامش غير مقصودة
مساحة إضافية على جانبين من مثيل الرمز أبعاد المثيل مقارنةً بنسبة viewBox طابق النسب أو اختر سياسة ملاءمة مقصودة
موضع محلي صحيح لكن موضعه على الصفحة خاطئ وحدات المستخدم في SVG مقارنةً بإحداثيات منفذ عرض المتصفح حوّل الإحداثيات قبل تعيين x وy
لا يتغير شيء عند تغيير أبعاد المثيل العنصر المستهدف بواسطة href استخدم رمزاً يحدد منفذ عرض للملاءمة، أو حوّل الأشكال الهندسية

إعادة إنتاج الفجوة بمثال محلي كامل

احفظ المستند الكامل التالي باسم svg-spacing.html في محرر نصوص عادي، ثم افتحه في متصفحك. يستخدم تسميات ومستطيلات اصطناعية فقط، من دون مكتبات أو ملفات خارجية. تشير المستطيلات الفاتحة إلى منافذ العرض المقصودة للمثيلات؛ أما المستطيلات الداكنة فهي الرسوم المعاد استخدامها.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>SVG spacing example</title>
  <style>
    body {
      margin: 24px;
      font: 16px system-ui, sans-serif;
    }
    #canvas {
      display: block;
      width: 320px;
      height: 200px;
      outline: 1px solid #687580;
      background: #fff;
    }
    .viewport-guide { fill: #e4edf2; }
    .artwork { fill: #356b8c; }
    text { fill: #253744; font-size: 14px; }
  </style>
</head>
<body>
  <p>Two instances with different source bounds and fitting dimensions.</p>
  <svg id="canvas" xmlns="http://www.w3.org/2000/svg"
       width="320" height="200" viewBox="0 0 320 200"
       role="img" aria-labelledby="drawing-title">
    <title id="drawing-title">Padded and tightly fitted rectangles</title>
    <defs>
      <symbol id="padded" viewBox="0 0 100 100">
        <rect id="source-shape" class="artwork"
              x="20" y="20" width="60" height="60" />
      </symbol>
      <symbol id="tight" viewBox="20 20 60 60">
        <rect class="artwork"
              x="20" y="20" width="60" height="60" />
      </symbol>
    </defs>

    <rect class="viewport-guide"
          x="20" y="20" width="120" height="60" />
    <use href="#padded" x="20" y="20" width="120" height="60" />
    <text x="160" y="55">Padded source</text>

    <rect class="viewport-guide"
          x="20" y="110" width="60" height="60" />
    <use id="tight-instance" href="#tight"
         x="20" y="110" width="60" height="60" />
    <text x="110" y="145">Tight source</text>
  </svg>
</body>
</html>

يضبط كلا المثيلين أبعادهما صراحةً. وهذا يجنب الاعتماد على التحجيم الضمني أثناء استقصاء الفجوة. توثّق MDN أن use يعيد استخدام محتوى SVG المشار إليه، وتشير إلى أن width وheight يؤثران في الملاءمة عندما يكون الهدف SVG أو رمزاً له viewBox. يستخدم المثال السمة الحالية href. MDN: use

احسب أين يبدأ المستطيل الأول فعلياً

للمصدر الأول viewBox أبعاده 100 × 100، لكن المستطيل لا يشغل سوى الإحداثيات من 20 إلى 80 في كل اتجاه. أما منفذ العرض المطلوب للمثيل فأبعاده 120 × 60. عند استخدام الملاءمة الافتراضية xMidYMid meet، يكون المقياس هو الأصغر بين 120 / 100 و60 / 100: 0.6. يشغل viewBox المحوّل مساحة 60 × 60، تاركاً 30 وحدة على كل جانب أفقي. وهذا يتبع خوارزمية تحويل منفذ العرض لدى W3C. SVG 2: حساب التحويل المكافئ لمنفذ عرض SVG

وفقاً لهذه المدخلات التوضيحية، يبدأ المستطيل المرسوم عند 20 + 30 + 20 × 0.6 = 62 أفقياً و20 + 20 × 0.6 = 32 عمودياً. وأبعاده 60 × 0.6 = 36 في كل اتجاه. تم التحقق من الحساب باستخدام عملية حسابية صغيرة حتمية؛ وهذه قيم محسوبة وليست قياسات من اختبار في متصفح.

يغيّر المثيل الثاني مدخلين مستقلين: تصبح حدود مصدره 20 20 60 60، ويصبح منفذ عرض المثيل 60 × 60. لذلك يملأ المستطيل منفذ العرض ويبدأ عند إحداثي SVG (20, 110). لعزل الأسباب بنفسك، غيّر مدخلاً واحداً فقط في كل مرة.

كيف تزيل المساحة داخل المثيل؟

صحّح المساحة غير المقصودة في حدود المصدر

يحتوي viewBox على أربعة أرقام: الحد الأدنى لـx، والحد الأدنى لـy، والعرض، والارتفاع. ولا يلزم أن يكون أصله صفراً. في المثال، يصف viewBox="20 20 60 60" الحدود الحالية للمستطيل من دون إعادة كتابة أشكاله الهندسية. MDN: viewBox

قبل تقليص حدود رسم حقيقي، حدّد ما إذا كانت الهوامش مقصودة. قد تشترك مجموعة من الأشكال القابلة لإعادة الاستخدام في إطار إحداثيات موحّد للحفاظ على اتساق أحجامها ومواضعها المرئية. قد يؤدي تقليص كل رمز على حدة إلى تغيير هذه العلاقات. أبقِ الحدود المشتركة إذا كان هذا الاتساق جزءاً من التصميم.

لفحص الأشكال الهندسية، يعرض getBBox() الحدود بوحدات مستخدم SVG. افتراضياً، يستثني الحدود والخطوط والعلامات، ولا يأخذ في الحسبان التحويلات المطبقة على العنصر المستهدف أو أسلافه. لذلك، لا يكفي صندوق إحاطة يعتمد على التعبئة وحدها دليلاً على القص عند التعامل مع رسوم ذات حدود أو تحويلات. MDN: ‏getBBox() في SVGGraphicsElement

اختر كيفية ملاءمة نسب العرض إلى الارتفاع غير المتطابقة

حتى الرسوم ذات الحدود المحكمة قد تظهر حولها مساحة عند ملاءمة مصدر مربع داخل منفذ عرض عريض. اختر السياسة وفقاً للنتيجة المطلوبة. طبّق preserveAspectRatio على الرمز المشار إليه لملاءمته الداخلية، أو على SVG الخارجي عند تشخيص تحويل الإحداثيات الخارجية. MDN: preserveAspectRatio

الخيار النتيجة والشرط
تطابق نسب المصدر ومنفذ العرض يتجنب مساحة الملاءمة الناتجة عن نسبة العرض إلى الارتفاع مع الحفاظ على التناسب
xMidYMid meet يعرض viewBox بالكامل في الوسط؛ وتترك النسب غير المتطابقة مساحة فارغة
xMinYMin meet ينقل viewBox الملائم نحو أعلى اليسار؛ وتظل المساحة المتبقية موجودة
none يتيح تحجيماً غير موحّد؛ وقد يتحول المربع إلى مستطيل
xMidYMid slice يغطي منفذ العرض، لكن بعض محتوى المصدر يمتد خارجه؛ ويحدد القص ما يظهر

يغيّر ضبط المحاذاة موضع مساحة الملاءمة؛ لكنه لا يزيل هامشاً مدرجاً بالفعل في الأشكال الهندسية للمصدر. وبالمثل، فإن slice غير مناسب عندما يجب أن يبقى كل محتوى المصدر داخل منفذ العرض. تتبع هذه الفروق تعريفات MDN للمحاذاة وmeet وslice والملاءمة غير الموحدة. MDN: preserveAspectRatio

افحص الهدف المشار إليه عندما تبدو الأبعاد غير مؤثرة

لا تكتسب المجموعة أو المسار العادي منفذ عرض للملاءمة لمجرد أن عنصر use يشير إليه. تذكر W3C أن width وheight للمثيل لا تأثير لهما عندما لا ينشئ العنصر المشار إليه منفذ عرض جديداً. للحصول على ملاءمة متوقعة لكل مثيل، ضع الأشكال الهندسية القابلة لإعادة الاستخدام في رمز ذي حدود صريحة، كما في المثال أعلاه. ولتحجيم هندسي مباشر، استخدم تحويلاً وراعِ أثره على الموضع. SVG 2: تخطيط الرسوم المعاد استخدامها

كيف تزيل الشريط أسفل SVG الخارجي؟

قد تنتج الفجوة خارج SVG عن التنسيق المضمّن في HTML. تسمح WHATWG بوجود SVG ضمن المحتوى النصي، ولذلك قد تضع الصفحة المحيطة العنصر بجانب النص. تتضمن صناديق الأسطر في CSS مقاييس الخط فوق خط الأساس وتحته، لذا قد يترك الرسم المضمّن مساحة ظاهرة أسفل صندوقه. معيار HTML: ‏SVG، CSS 2: حسابات ارتفاع السطر

يضبط المثال الكامل بالفعل display: block على #canvas، ما يخرج SVG من المشاركة في صندوق السطر المضمّن. أبقِ هذه القاعدة للرسم المستقل. ولاستقصاء الآلية، غيّرها مؤقتاً إلى display: inline وافحص السطر المحيط.

بالنسبة إلى SVG يجب أن يبقى بجانب النص، جرّب vertical-align: bottom على SVG الخارجي وتحقق من محاذاة النص الناتجة. تحاذي هذه الخاصية صندوقاً على مستوى السطر ضمن سطره؛ ولا تغيّر حدود الرمز أو الملاءمة الداخلية. وقد يظل المحتوى الآخر وارتفاع سطر العنصر الأب يحددان الارتفاع الإجمالي للسطر. MDN: vertical-align

تعامل مع تصريحات CSS هذه كبدائل لـSVG الخارجي، لا كإصلاحات تراكمية تُضاف إلى كل مثيل:

/* Standalone drawing */
#canvas {
  display: block;
}

/* Alternative when the drawing must stay inline */
/*
#canvas {
  display: inline;
  vertical-align: bottom;
}
*/

ماذا لو كانت إحداثياتك مأخوذة من منفذ عرض المتصفح؟

يعرض getBoundingClientRect() موضع العنصر نسبةً إلى منفذ عرض المتصفح بوحدات بكسل CSS. ويتغير موضعه عند تمرير الصفحة. تنتمي هذه القيم إلى فضاء مختلف عن وحدات المستخدم في SVG، لذا فإن نسخ left وtop مباشرةً إلى x وy للمثيل غير صحيح عموماً. MDN: getBoundingClientRect() للعنصر

استخدم تحويلاً للإحداثيات عندما يكون المدخل نقطة في منفذ عرض المتصفح. يوفّر getScreenCTM() مصفوفة تحويل SVG، ويحوّل تطبيق معكوسها النقطة إلى الإحداثيات المحلية للعنصر المختار. تنفّذ طريقة matrixTransform() للنقطة هذا التحويل. MDN: getScreenCTM()، MDN: matrixTransform()

مقتطف وحدة التحكم التالي امتداد اختياري للمستند الكامل أعلاه. يحوّل نقطة منفذ عرض المتصفح (100, 150) إلى إحداثيات المستخدم في SVG الخارجي، وينقل المثيل ذي الحدود المحكمة إليها. يفترض بقاء المثيل غير المحوّل في المثال ابناً مباشراً لـSVG.

(() => {
  const canvas = document.getElementById("canvas");
  const instance = document.getElementById("tight-instance");
  if (!canvas || !instance) {
    throw new Error("Open the complete SVG spacing example first.");
  }

  const matrix = canvas.getScreenCTM();
  if (!matrix) {
    throw new Error("The SVG coordinate matrix is unavailable.");
  }

  const viewportPoint = new DOMPoint(100, 150);
  const localPoint = viewportPoint.matrixTransform(matrix.inverse());
  if (!Number.isFinite(localPoint.x) || !Number.isFinite(localPoint.y)) {
    throw new Error("The SVG coordinate matrix cannot be inverted.");
  }

  instance.setAttribute("x", String(localPoint.x));
  instance.setAttribute("y", String(localPoint.y));
})();

يكتسب التحقق من القيم المنتهية أهميته لأن المصفوفة غير القابلة للعكس تنتج مكونات NaN. أعد حساب المصفوفة عند تحويل نقطة جديدة بعد تغيّر التخطيط أو التمرير. وإذا كان المثيل داخل مجموعة محوّلة، فحوّل الإحداثيات إلى نظام إحداثيات المجموعة الأب بدلاً من ذلك. تمت مراجعة هذا المقتطف بالرجوع إلى توثيق الواجهة البرمجية؛ ولم يُنفذ في متصفح أثناء البحث. MDN: inverse() في DOMMatrixReadOnly

اختم بفحص الحدود الثلاثة كلٌّ على حدة: صندوق SVG الخارجي، ومنفذ العرض الملائم للرمز، وحدود الرسم نفسه. صحّح الحد الذي يُدخل المساحة غير المرغوبة، ثم تحقق من النتيجة عند حجم الصفحة المقصود. يمنحك ذلك موضعاً متوقعاً من دون تعويض الفجوة غير المفسرة بإزاحات اعتباطية.

المصادر