إذا سبق لك وعملت على مشاريع تتطلب تعديل عدد كبير من الصور مع تغيير البيانات و اماكنها في كل مرة ، فحتما أنك تعرف أن أكثر شيء يستهلك الوقت ليس عملية التصميم ، بل عملية تعدل المعلومات يدويا هي ما يأخذ وقت كبير كان من المفروض ان يستغل في الابداع و القيام بالتصميم .
واجهت هذه المشكلة عندما عملت على تصاميم رياضية كنت احدث بيانات الجولة و المباريات ؛ بعد جولة تنتهي ترتيب الفرق و تعديل عدد النقاط والاهداف في تصاميم كرة القدم و عدد الميداليات في باقي المشاريع مثل مسابقة ألعاب البحر الابيض المتوسط ، وكان تكرار هذه العملية يدويا يستغرق وقت طويل جدا و عملية مملة ، و الاسوء في هذه المشكلة ان احتمالية وقوع الاخطاء عالية .
بدلا من إعادة تعديل كل صورة يدويا، قررت البحث عن طريقة تجعلني اقوم بهذه العملية على برنامج Photoshop بطريقة أكثر ذكاء و بشكل اوتوماتيكي ان صح التعبير كأن البرنامج يقوم بفعلها نيابة عني. وهنا بدأت أتعلم Scripting، الذي سمح لي بتحديث جميع البيانات و تبديل الصور تلقائيا خلال وقت قصير جدا.
في هذا المقال سأشارك معكم خبرتي و الفكرة التي اعتمدت عليها، وكيف ساعدني هذا الأسلوب على توفير الكثير من الوقت والجهد في المشاريع التي تتطلب تحديث البيانات باستمرار.
تحضير الملفات
قبل أن أبدأ بكتابة أي سطر من السكريبت أو اي تصميم على الفوتوشوب ، أول شيء أقوم به هو تجهيز جميع الملفات التي سأحتاجها. مع انها خطوة بسيطة ، لكنها وفرت على الكثير من الوقت ، خاصة عندما يكون المشروع الذي اعمل عليه يحتوي على عدد كبير من الصور والبيانات.
في هذا المثال، أنشأت مجلد خاص بالمشروع على سطح المكتب ، ووضعت بداخله جميع صور الأعلام التي سأستخدمها، بالإضافة إلى ملف JSX الذي سأكتب فيه السكريبت.
أفضل دائما أن أجمع كل شيء داخل مجلد واحد، لأن السكريبت سيحتاج إلى الوصول إلى هذه الملفات لاحقا. وإذا كانت
الصور موزعة في أماكن مختلفة داخل الحاسوب ، فستصبح إدارة المشروع صعبة للغاية و تأخذ وقت طويل و هو ما لا نريده ،
خاصة إذا أردت تعديله أو نقله إلى جهاز آخر لذا انصحك و بشدة ان تقوم بهذه الخطوة .
بعد تجهيز الصور، أنتقل إلى إنشاء ملف JSX، وهو الملف الذي سأكتب بداخله السكريبت. إذا لم يسبق لك التعامل مع هذا النوع من الملفات، فلا تقلق، فهو في النهاية مجرد ملف نصي يحتوي على أوامر ينفذها Photoshop.
قبل أن نبدأ، أود التنويه إلى أن هذا المقال لا يشرح أساسيات البرمجة أو السكربتات الخاصة ببرنامج Photoshop، لذلك يُفضل أن تمتلك معرفة مسبقة بمبادئ البرمجة أو على الأقل بآلية كتابة السكربتات داخل البرنامج. سيساعدك ذلك على فهم ما سأشرحه والاستفادة منه بشكل أفضل، لأن هدف هذا المقال ليس تعليم البرمجة، بل توضيح الفكرة التي تمكن Photoshop من تعديل البيانات والصور تلقائيا بدلا من تنفيذ هذه المهام يدويا.
وأيضا ، أنصحك قبل البدء أن تتأكد من أمرين:
- أن جميع الصور التي ستستخدمها موجودة داخل مجلد واحد.
- وأن أسماء الملفات واضحة ومنظمة، لأن السكريبت سيعتمد عليها أثناء استبدال الصور داخل التصميم.
قد تبدو هذه الخطوة مجرد ترتيب للملفات، لكنها في الحقيقة الأساس الذي يعتمد عليه كل ما سنقوم به في المراحل القادمة.
في مشاريعي الحقيقية، قد يصل عدد الصور إلى عشرات أو حتى مئات الملفات، لذلك تعودت دائما أن أبدأ بتنظيمها أولا. فكل دقيقة أقضيها في ترتيب الملفات في البداية، توفر علي وقتا أكبر أثناء العمل.
تحضير التصميم في Photoshop
بعدما انتهي من تجهيز الملفات التي ذكرتها مسبقا ، أفتح برنامج الفوتوشوب لأبدأ في تصميم القالب الذي سيقوم السكريبت بالتعديل عليه لاحقا. المثال الذي سأشرح فيه العملية هو بوستر سوشل ميديا لأكثر المنتخبات تسجيلا للاهداف و مثال اخر لموعد المبارايات.
لن أشرح طريقة تصميم القالب نفسه، لأن ذلك ليس هدف هذا المقال، لكن هناك مجموعة من الأمور التي أحرص عليها دائما قبل أن أبدأ بكتابة السكريبت، لأنها تؤثر بشكل مباشر على نجاح العملية.
في البداية أقوم بتصميم البوستر او البطاقة بشكل طبيعي ، وأضع جميع العناصر التي أريد ظهورها في النتيجة النهائية. في هذا المثال اخترت منتخب الولايات المتحدة كبيانات تجريبية، لذلك وضعت العلم، واسم المنتخب، وعدد الأهداف كما لو أنني أصمم الصورة النهائية.
قد تتساءل: لماذا اخترت منتخب الولايات المتحدة بالتحديد؟
في الحقيقة يمكنك اختيار أي اسم أو حتى كتابة أي نص تريده، أما سبب اختياري لهذا المنتخب في هذا المثال فهو أن اسمه يتكون من كلمتين، مما يتيح لي اختبار قدرة التصميم على استيعاب الأسماء الطويلة نسبيا دون أن يؤثر ذلك على التنسيق.
لهذا السبب، أثناء التصميم لا يهمني كثيرا ما هي البيانات التي أضعها، بقدر ما يهمني أن أجرب أسوأ الاحتمالات قبل أن أبدأ عملية السكريبتينغ .
بعد الانتهاء من توزيع العناصر، أبدأ بتنظيم الطبقات داخل Photoshop. وهذه الخطوة بالنسبة لي ليست مجرد ترتيب، بل جزء أساسي من نجاح السكريبت.
أقوم بتجميع العناصر الثابتة داخل مجلدات مستقلة، مثل الخلفية، والعنوان، أو أي عنصر لن يتغير بين صورة وأخرى. كما أقوم بقفل (Lock) هذه الطبقات حتى لا أعدلها أو أحركها بالخطأ أثناء العمل.
أما العناصر التي سأتحكم فيها بالسكريبت، فأضع كل نوع منها داخل مجلد مستقل وهذه الخطوة ليست للترتيب فقط بل خطوة اساسية يجب القيام بيها بالنسبة للعناصر التي سيتم التعديل عليها . ففي هذا المثال أنشأت مجلدا لأسماء المنتخبات، وآخر للأعلام، وآخر لعدد الأهداف، تتغير حسب البيانات التي اعمل بها .
بهذه الطريقة يصبح من السهل على السكريبت الوصول إلى العناصر التي يحتاج إلى تعديلها، دون أن يؤثر على بقية التصميم.
هناك نقطة أخرى أعتبرها من أهم النقاط في هذا المشروع، وهي ترتيب الطبقات داخل كل مجلد.
يجب أن من أن ترتيبها داخل لوحة Layers يطابق ترتيبها على الـ Canvas. بمعنى أن أول عنصر يظهر في التصميم يجب أن يكون أيضا أول طبقة داخل المجلد، والثاني يقابله الثاني، وهكذا حتى آخر عنصر.
على سبيل المثال، إذا كان أول علم في التصميم موجودا في أعلى القائمة، فيجب أن يكون هو أيضا أول طبقة داخل مجلد علم،كما هو موضح في الصورة ، وينطبق الأمر نفسه على أسماء المنتخبات، وعدد الأهداف، أو أي عناصر أخرى سأتحكم فيها بواسطة السكريبت. السبب هو أن عملية الـ Loop لا تبحث عن اسم الطبقة، بل تتعامل مع الطبقات حسب ترتيبها داخل المجلد. لذلك إذا كان ترتيب الطبقات مختلفا عن ترتيب العناصر في التصميم، فستظهر البيانات في أماكن غير صحيحة، مثل أن يأخذ منتخب علم منتخب آخر، أو يوضع عدد الأهداف في المكان الخطأ.
لهذا السبب، أقضي بضع دقائق إضافية في تنظيم الطبقات قبل أن أبدأ البرمجة، لأن هذا يوفر علي الكثير من الوقت فيما بعد ،
ويجنبني أخطاء يصعب اكتشافها بعد تنفيذ السكريبت.
تجهيز البيانات داخل ملف JSX
بعد أن انتهيت من تجهيز التصميم، بدأت العمل على ملف JSX الذي سيحتوي على السكريبت. أول جزء أقوم بكتابته هو البيانات، لأنها ببساطة المعلومات التي أريد أن يضعها السكريبت داخل التصميم.
في هذا المثال، أنشأت مصفوفة JSON تحتوي على جميع المنتخبات التي أريد عرضها، ولكل منتخب أضفت ثلاث معلومات فقط:
- اسم المنتخب.
- عدد الأهداف.
- رابط صورة العلم الموجودة على الحاسوب.
// After Effects Expressionvar data = [ {"country":" فرنسا","goals":13,"flag":"file:///C:/Users/billkerx/Desktop/imgs/fra.png" }, {"country":" هولندا","goals":11,"flag":"file:///C:/Users/billkerx/Desktop/imgs/ned.png" }, {"country":" الارجنتين","goals":11,"flag":"file:///C:/Users/billkerx/Desktop/imgs/arg.png" }]
كما تلاحظ، كل منتخب يمثل عنصرا مستقلا داخل المصفوفة، وبهذه الطريقة يستطيع السكريبت المرور على جميع المنتخبات واحدا تلو الآخر وتحديث التصميم تلقائيا.
قد يبدو إدخال هذه البيانات يدويا متعبا في هذا المثال، لكنه في المشاريع الحقيقية يكون مختلفا. فأنا غالبا أحصل على هذه المعلومات من مصدر واحد، مثل ملف Excel أو قاعدة بيانات أو حتى ملف JSON مستقل، ثم أقوم بتحويلها إلى هذا الشكل ليتمكن السكريبت من قراءتها بسهولة.
أما بالنسبة لرابط صورة العلم، فطريقة الحصول عليه بسيطة جدا. بما أنني وضعت جميع الأعلام داخل مجلد واحد، قمت فقط بفتح إحدى الصور بواسطة المتصفح ونسخت مسارها، ثم غيرت اسم الملف في نهاية الرابط حسب اسم كل علم.
بهذه الطريقة لا أضطر إلى البحث عن مسار كل صورة بشكل منفصل، لأن جميع الصور موجودة في المكان نفسه، والاختلاف الوحيد بينها هو اسم الملف.
قد تبدو هذه الخطوة بسيطة، لكنها هي التي تجعل السكريبت مرنًا. فكلما أردت تغيير البيانات، لن أحتاج إلى تعديل التصميم أو إعادة كتابة الكود، بل يكفي تحديث هذه القائمة، وسيقوم السكريبت بتغيير الصور الجديدة تلقائيا.
ملاحظة من تجربتي
في المشاريع الصغيرة قد أكتب البيانات يدويا كما في هذا المثال، لأن الهدف هو تبسيط الفكرة. أما عندما أعمل على مشاريع تحتوي على مئات العناصر، فأفضل دائمًا ربط السكريبت بمصدر بيانات خارجي مثل Excel، لأن ذلك يجعل عملية التحديث أسرع بكثير ولا أحتاج إلى تعديل الكود في كل مرة.
// After Effects Expressionvar layers = app.activeDocument.artLayers; var flag = app.activeDocument.layerSets.getByName("علم"); var country = app.activeDocument.layerSets.getByName("المنتخب"); var goals = app.activeDocument.layerSets.getByName("اهداف");
كما تلاحظ، قمت بتعريف ثلاثة متغيرات، كل واحد منها يشير إلى مجلد معين داخل التصميم.
لكن هناك نقطة مهمة جدًا يجب الانتباه إليها، وهي أن الدالة getByName() تبحث عن
المجلد اعتمادا على اسمه. لذلك يجب أن يكون الاسم الموجود داخل الكود مطابقا تماما لاسم المجلد داخل
Photoshop، حتى في المسافات أو طريقة الكتابة.
فعلى سبيل المثال، إذا كان اسم المجلد داخل التصميم هو "المنتخب"، فلا يمكنك كتابة "المنتخبات" أو "منتخب" داخل الكود، لأن السكريبت ببساطة لن يستطيع العثور عليه وسيتوقف عن العمل.
لهذا السبب، أنصح دائما بعد الانتهاء من تنظيم الطبقات أن تتأكد مرة أخرى من أن أسماء المجلدات داخل Photoshop مطابقة للأسماء التي استخدمتها داخل السكريبت، لأن هذه من أكثر الأخطاء التي كنت اقع فيها أثناء تنفيذ مثل هذه المشاريع. كنت اتفاجئ من عدم عمل السكريبت و أقوم بتعديلات كثيرة لأكتشف فيما بعد ان الخطأ كان في اسم مجلد ههه . لذا لاتكرر نفس الاخطاء التي وقع فيها و ستربح الكثير من الوقت .
آلية التكرار (Loop)
بعد أن أصبحت البيانات جاهزة، وأصبح السكريبت يعرف مكان العناصر داخل التصميم، جاءت المرحلة التي يقوم فيها بتنفيذ جميع التعديلات تلقائيا.
استخدمت حلقة تكرار (Loop) تقوم بتنفيذ نفس العملية على جميع البيانات بشكل تلقائي. بما أن جميع المنتخبات ستمر بنفس خطوات التعديل، فلم يكن من المنطقي أن أكرر نفس الأوامر لكل واحد منها.
في كل مرة تدور فيها الحلقة، يقوم السكريبت بالمرور على منتخب واحد فقط، ثم ينفذ عليه جميع التعديلات قبل أن ينتقل إلى المنتخب الذي يليه.
// After Effects Expressionfor (i=0;i<data.length;i++){ var layer = country.layers[i]; layer.textItem.contents=data[i].country; var layer1 = goals.layers[i]; layer1.textItem.contents=data[i].goals; replaceContents(data[i].flag, flag.layers[i]); }
أولا يقوم بتغيير اسم المنتخب، ثم يغير عدد الأهداف، وبعدها يستبدل صورة العلم بالصورة الخاصة بذلك المنتخب.
بعد الانتهاء من هذه الخطوات، يعيد تنفيذ نفس العملية على العنصر التالي، ويستمر بنفس الطريقة حتى يصل إلى آخر منتخب موجود داخل البيانات.
لهذا السبب كان ترتيب الطبقات الذي تحدثت عنه سابقا مهما جدا. فالسكريبت يعتمد على أن أول عنصر داخل البيانات يقابله أول عنصر داخل مجلدات Photoshop، والثاني يقابله الثاني، وهكذا حتى النهاية. وإذا اختلف هذا الترتيب، فستظهر أسماء المنتخبات أو الأعلام في أماكن غير صحيحة.
قد يبدو أن الحلقة لا تحتوي على الكثير من الأوامر، لكنها في الحقيقة هي الجزء الأهم الذي وفر علي معظم الوقت. فبدلا من
تعديل كل منتخب يدويا، أصبح السكريبت يكرر نفس العملية على جميع العناصر خلال ثوان فقط.
دالة تغيير الصور :
بعد الوصول إلى هذه المرحلة، بقيت خطوة واحدة فقط، وهي استبدال صور الأعلام داخل التصميم.
لهذا الغرض كتبت دالة باسم replaceContents() ، وهي المسؤولة عن تنفيذ جميع العمليات
المتعلقة بالصور. فعندما تستدعيها الحلقة، تقوم باستبدال العلم القديم بالعلم الجديد، مع الحفاظ على جميع الإعدادات
التي قمت بتجهيزها مسبقا داخل ملف Photoshop.
// After Effects Expressionfunction replaceContents(newFile, theSO) { var doc = app.activeDocument; var parentGroup = theSO.parent; var layerIndex = theSO.itemIndex; var oldBounds = theSO.bounds; var oldW = oldBounds[2].as('px') - oldBounds[0].as('px'); var oldH = oldBounds[3].as('px') - oldBounds[1].as('px'); var oldCenterX = oldBounds[0].as('px') + oldW / 2; var oldCenterY = oldBounds[1].as('px') + oldH / 2; var oldName = theSO.name; var oldOpacity = theSO.opacity; var oldBlend = theSO.blendMode; var oldVisible = theSO.visible; doc.activeLayer = theSO; var idPlc = charIDToTypeID("Plc "); var descPlace = new ActionDescriptor(); descPlace.putPath(charIDToTypeID("null"), new File(newFile)); executeAction(idPlc, descPlace, DialogModes.NO); var placedLayer = doc.activeLayer; placedLayer.rasterize(RasterizeType.ENTIRELAYER); var b = placedLayer.bounds; var newW = b[2].as('px') - b[0].as('px'); var newH = b[3].as('px') - b[1].as('px'); var scale = Math.min(oldW / newW, oldH / newH) * 100; placedLayer.resize(scale, scale, AnchorPosition.TOPLEFT); var b2 = placedLayer.bounds; var curW = b2[2].as('px') - b2[0].as('px'); var curH = b2[3].as('px') - b2[1].as('px'); var curCenterX = b2[0].as('px') + curW / 2; var curCenterY = b2[1].as('px') + curH / 2; placedLayer.translate(oldCenterX - curCenterX, oldCenterY - curCenterY); placedLayer.name = oldName; placedLayer.opacity = oldOpacity; placedLayer.blendMode = oldBlend; placedLayer.visible = oldVisible; if (placedLayer.parent !== parentGroup) { placedLayer.move(parentGroup, ElementPlacement.PLACEATBEGINNING); } try { theSO.remove(); } catch(e) {} return placedLayer; }
قد تلاحظ أن هذه الدالة طويلة مقارنة ببقية أجزاء السكريبت، لكن لا تقلق، فلست بحاجة إلى فهم كل سطر فيها حتى تتمكن من استخدامها. فهي تعمل في الخلفية، ومهمتها تنفيذ جميع العمليات تلقائيا دون الحاجة إلى التدخل اليدوي.
من بين المهام التي تقوم بها هذه الدالة:
- استيراد الصورة الجديدة.
- ضبط حجمها ليتناسب مع التصميم.
- وضعها في نفس مكان الصورة السابقة.
- الحفاظ على خصائص الطبقة مثل الاسم والشفافية ووضع المزج.
- حذف الصورة القديمة بعد استبدالها.
وبفضل هذه الدالة، لم أعد مضطرا إلى تغيير كل علم يدويًا أو إعادة ضبط حجمه ومكانه في كل مرة، بل أصبح السكريبت يقوم بكل ذلك تلقائيًا بمجرد تشغيله.
المثال الثاني: تصميم مواعيد ونتائج المباريات
في المثال الأول عملت على مجموعة بيانات واحدة في كل مصفوفة ، وهي اسم المنتخب، والعلم، وعدد الأهداف. لكن في بعض المشاريع نحتاج إلى التعامل مع مجموعتين من البيانات أو أكثر داخل نفس التصميم، مثل مباريات كرة القدم التي تحتوي على منتخبين، ولكل واحد منهما اسمه وشعاره.
في هذه الحالة، أفضل دائما أن أقسم عناصر كل منتخب داخل مجلد مستقل، بدلًا من وضع جميع الأعلام أو جميع الأسماء داخل مجلد واحد. بهذه الطريقة يصبح السكريبت أسهل في القراءة والتعديل، خصوصًا عندما يكبر المشروع ويصبح أكثر تعقيدا.
في هذا المثال، أصبح ملف البيانات يحتوي على معلومات خاصة بكل منتخب، بالإضافة إلى توقيت المباراة.
كما تلاحظ، أصبح لكل منتخب بياناته الخاصة، لذلك قمت أيضًا بتقسيم المجلدات داخل Photoshop بالطريقة نفسها.
بعد ذلك لم يتغير منطق العمل كثيرًا، وإنما أصبحت الحلقة تقوم بتحديث بيانات المنتخب الأول، ثم بيانات المنتخب الثاني، ثم توقيت المباراة، وأخيرًا تستدعي دالة replaceContents() مرتين، مرة لكل علم.
// كودvar data = [ {"country1":" البرازيل","country2":" النريوج","time":"21:00","flag1":"file:///C:/Users/BillkerX/Desktop/imgs/bra.png","flag2":"file:///C:/Users/BillkerX/Desktop/imgs/nor.png", }, {"country1":" المكسيك","country2":" انجلترا","time":"01:00","flag1":"file:///C:/Users/BillkerX/Desktop/imgs/mex.png","flag2":"file:///C:/Users/BillkerX/Desktop/imgs/eng.png", }, {"country1":" البرتغال","country2":" اسبانيا","time":"20:00","flag1":"file:///C:/Users/BillkerX/Desktop/imgs/por.png","flag2":"file:///C:/Users/BillkerX/Desktop/imgs/esp.png", }, ]
كما ترى، الفكرة بقيت نفسها، والاختلاف الوحيد هو أنني أصبحت أتعامل مع مجموعتين من البيانات بدل مجموعة واحدة.
وأحب دائمًا اتباع هذه الطريقة حتى في المشاريع الأكبر، لأنها تجعل الكود أوضح وأسهل في التعديل لاحقًا، كما تقلل
احتمال الوقوع في الأخطاء عند إضافة عناصر جديدة.
في ماذا يمكن استخدام هذه الفكرة؟
قد يبدو هذا السكريبت مخصصًا للمباريات أو جداول الترتيب، لكنه في الحقيقة يصلح لأي مشروع يعتمد على قالب ثابت وبيانات تتغير باستمرار.
من الأمثلة التي أراها مناسبة:
- بطاقات نتائج مباريات كرة القدم أو أي رياضة أخرى.
- جداول ترتيب البطولات.
- بطاقات الإحصائيات الخاصة باللاعبين أو المنتخبات.
- صور الأخبار العاجلة التي يتغير فيها العنوان والصورة فقط.
- بطاقات المنتجات في المتاجر الإلكترونية.
- منشورات عروض الأسعار والخصومات.
- شهادات المشاركة أو التخرج التي يتغير فيها الاسم فقط.
- بطاقات الموظفين أو أعضاء الفريق داخل الشركات.
- منشورات مواقع التواصل الاجتماعي التي تعتمد على قالب ثابت مع بيانات مختلفة.
- بطاقات الاقتباسات اليومية أو الآيات أو الأحاديث، حيث يتغير النص مع بقاء التصميم نفسه.