أهلًا بك في هذه السلسلة التي تتحدث عن تأثيرات التمرير (Scrolling Effects)، سنستعرض في هذه السلسلة عددًا من تأثيرات التمرير وسنشرح آلية عملها وسنجرِّبها عمليًا.
يمكننا الاستفادة من الحدث scroll
في JavaScript لإجراء تأثيرات عند تمرير صفحة الويب؛ لكن إن فعلنا ذلك دون إتقان فالنتيجة كارثية، أما إذا أحسنا صنعنا فيمكن لتأثيرات التمرير أن تبهر الزوار وتشعرهم أنَّ موقعك مميز.
هذه هي المقالة الأولى في هذه السلسلة، والتي تتضمن التأثيرات الآتية:
- إخفاء صورة خلفية تدريجيًا عند التمرير
- توضيح الصورة عند التمرير
- تدوير العناصر عند التمرير
- تأثير اختلاف المظهر parallax
أما المقالة الثانية والثالثة فهي تتضمن التأثيرات الآتية:
- إظهار صورة الخلفية عند التمرير باستخدام CSS فقط
- تمرير سلس للصفحة
- تطبيق تأثير عدم الوضوح على المحتوى خلف شريط الانتقال
- إنشاء عنصر قابل للتمرير مع إمكانية وصول مخصصة من لوحة المفاتيح
- تأثير غروب الشمس باستخدام SVG
- إظهار فيديو في الخلفية
- صور متحركة بتأثير parallax باستخدام CSS 3D و JavaScript
سأقدِّم لك في بداية كل قسم رابطًا لتجربة المثال تجربةً حيةً على المتصفح. سيسهل عليك كثيرًا أن تفهم الشرح والشيفرات بعد تجربتك للتأثير.
إخفاء صورة خلفية تدريجيًا عند التمرير
إذا كنتَ مِن مَن يستعملون صورًا كخلفية لكامل صفحة الويب، فاستخدام تأثير إخفاء تلك الصورة تدريجيًا عند التمرير هو أمرٌ حسن، حيث يمكّنك ذلك من استخدام أيّة صورة كخلفية دون التأثير على وضوح العناصر أو قابلية قراءة النص.
ولعدم قدرة CSS على الاستجابة مباشرةً إلى موضع شريط التمرير، فنحن بحاجةٍ إلى استخدام بعض شيفرات JavaScript لمعرفة موضع التمرير لأحد العناصر. التقنية التي سأشرحها لك هنا سنستخدم فيها أنماط CSS أيضًا لإنشاء تأثير الإخفاء والتي ستُحدَّث ديناميكيًا عبر JavaScript.
طريقة التفافية للتعويض عن عدم وجود الخاصية background-opacity
للأسف لا يوجد لحد الآن خاصية باسم background-opacity لتحديد شفافية الخلفية؛ لكن من الممكن إنشاء التأثير عبر استخدام ميزة تعدد الخلفيات في CSS: حيث سنضع الصورة كطبقة (layer)، ثم الطبقة الثانية هي تدرجٌ لوني سنستخدم في ألوانه الشفافية alpha. لذا ستبدو شيفرة CSS كالآتي:
body { background: linear-gradient(rgba(255, 255, 255, 0), rgba(255, 255, 255, 0)), url(times-square-perspective.jpg); background-repeat: no-repeat; background-attachment: fixed !important; background-size: 100% !important; background-position: center top !important; padding: 1rem; padding-top: 45%; color: #fff; }
التدرج اللوني (الذي سيتم وضعه فوق الصورة، لأنَّه عُرِّفَ أولًا) غير ظاهر حاليًا، لأنَّ قيمة الشفافية alpha هي 0 للونين المشكلين للتدرج. استخدمنا الكلمة المفتاحية !important
للتأكّد أنَّ شيفرة JavaScript –التي سنُطبِّقها بعد قليل– لن تتمكن من إلغاء قيم خاصيات CSS السابقة.
شيفرة HTML تحتوي على ما يلي:
<h1>New York Stories</h1> <p>In my younger and more vulnerable years…
في النهاية، علينا وضع العنصر h1 في الأعلى:
h1 { text-shadow: 0 0 5px rgba(0,0,0,0.5); font-size: 4rem; color: #fff; line-height: 1; position: absolute; top: 10px; }
ولكي تصبح الفقرة قابلةً للقراءة، فعلينا أن نحوِّل التدرج اللوني إلى اللون الأبيض تمامًا، مما يؤدي إلى إخفاء صورة الخلفية.
تعديل التدرجات اللوني في CSS باستخدام JavaScript
أضف شيفرة JavaScript الآتية في أسفل الصفحة:
var nystories = document.querySelector("p").offsetTop; window.onscroll = function() { if (window.pageYOffset > 0) { var opac = window.pageYOffset / nystories; document.body.style.background = "linear-gradient(rgba(255, 255, 255, " + opac + "), rgba(255, 255, 255, " + opac + ")), url(times-square-perspective.jpg) no-repeat"; } }
في الشيفرة السابقة، قيمةُ المتغير nystories
هي موضع أوّل فقرة في الصفحة. عندما يبدأ المستخدم بالتمرير فستُنشِئ الشيفرة السابقة متغيرًا باسم opac
الذي سيُقسِّم الموضع «الحالي» للنافذة على الموضع «البدائي» لأول فقرة. ومن ثم ستُجمَع النتيجة مع قيم rgb لألوان التدرج اللوني مكان قيمة الشفافية alpha، مما يعطي التأثير بإخفاء صورة الخلفية عندما يتم تمرير الصفحة.
هذا أحد الأمثلة البسيطة عن التمرير، ما زال في جعبتنا المزيد.
توضيح الصورة عند التمرير
أعجبتني اللمسة الجميلة في تطبيق توتير على هواتف iPhone: عندما تمرّر إلى الأسفل في صفحة «Me» فستصبح صورة الترويسة غير واضحة وسيتم تقريبها. أرى أنَّ من المفيد تقليد هذه التقنية في متصفحات الويب، خصوصًا للصور البارزة التابعة للمقالات…
شيفرة HTML بسيطة للغاية:
<article> <header> <img src="placid-pond.jpg" alt> </header> … </article>
وفي نفس الصفحة سنضيف شيفرة SVG:
<svg xmlns="http://www.w3.org/2000/svg" width="0" height="0"> <filter id="blur"> <feGaussianBlur stdDeviation="0" /> </filter> </svg>
الغرض من وضع شيفرة SVG السابقة هو إضافة تأثير عدم الوضوح (blur) في متصفح Firefox، والذي لا يدعم إلى الآن «مُرشّحات CSS» (CSS Filters)؛ لاحظ أنَّ خاصية العرض والارتفاع لعنصر svg لها القيمة 0، لذا لن يؤثِّر على تخطيط الصفحة.
سنضيف تأثير عدم الوضوح عبر أنماط CSS:
header { overflow: hidden; font-size: 0; } article header img { width: 100%; height: auto; filter: url(#blur); }
شيفرة JavaScript أكثر تعقيدًا مما سبق. يجب في البداية أن نضع جميع الشيفرات داخل دالة التي ستستدعى عندما يتم التمرير في المتصفح:
window.onscroll = function (event) { }
سنحتاج إلى الكثير من المعلومات داخل هذه الدالة. المتغيرات التي سنستعملها هي:
var headerImg = document.querySelector("article header img"), headerImgOffset = headerImg.getBoundingClientRect(), imgTop = headerImgOffset.top, imgBottom = headerImgOffset.bottom, imgHeight = headerImg.offsetHeight, viewportHeight = document.documentElement.clientHeight, blur = document.getElementById("blur"), svgblur = blur.getElementsByTagName("feGaussianBlur")[0], topEffectStart = Math.abs((viewportHeight – imgHeight)/5);
اقرأ الأسطر السابقة بتمعّن، يجب أن تبدو لك المتغيرات منطقيةً:
-
المتغير
headerImg
يشير إلى صورة الترويسة الموجودة في أعلى المقالة (وللتبسيط، سأعتبر أنَّ الصفحة فيها مقالة وحيدة، وعنصر header وحيد). -
المتغير
headerImgOffset
يسمح لنا بالوصول إلى مكان وأبعاد الصورة، وفي حالتنا سنستخدمimgTop
وimgBottom
وimgHeight
. -
المتغير
viewportHeight
يمثِّل ارتفاع نافذة المتصفح. -
المتغير
blur
يشير إلى شيفرة SVG في الصفحة، أماsvgBlur
فهو العنصر الذي سيسبِّب تأثير عدم الوضوح في متصفح Firefox. -
المتغير
topEffectStart
هو الفرق بين ارتفاع «إطار العرض» (viewport) الحالي وارتفاع الصورة، ومقسومًا على 5. وهذا يعني أنَّ تأثير عدم الوضوح سيبدأ فقط إذا كانت صورة الترويسة في الخُمس العلوي من نافذة المتصفح. استخدمتُ الدالةMath.abs
لكي أحصل دومًا على عددٍ موجب، لأنَّ من الممكن أن تكون الصورة «أطول» من ارتفاع إطار العرض الحالي.
بعد توضيح كل ما سبق، فلنضف عبارةً شرطيةً داخل الدالة:
if (imgTop < topEffectStart && imgBottom > 0 ) { blurFactor = Math.abs(imgTop / 100); scaleFactor = 1+Math.abs(imgTop / 1000); headerImg.style.webkitFilter = "blur(" + blurFactor + "px)"; svgblur.setAttribute("stdDeviation", blurFactor); headerImg.style.webkitTransform = "scale(scaleFactor)"; headerImg.style.transform = "scale(scaleFactor)"; }
هذا يعني إذا كان أعلى الصورة موجودٌ في أول خُمس من نافذة إطار العرض وما زالت الصورةُ معروضةً وظاهرةً للمستخدم، فافعل ما يلي:
-
أخذ الموقع الحالي لأعلى الصورة نسبةً إلى أعلى إطار العرض، وتقسيم الناتج على 100، وتحويل الناتج إلى عدد عشري موجب، وتسمية الناتج باسم
blurFactor
. -
وبطريقةٍ مشابهة، سنأخذ الفرق، ونُقسِّمه على 1000 (لأن تغيير المقياس [scale] حساسٌ أكثر من تأثير عدم الوضوح) ومن ثم سنجمع العدد 1 مع الناتج، ونسمِّه
scaleFactor
. -
سنُطبِّق
blurFactor
(بعد إضافة “px” إليه) على صورة الترويسة، وذلك بوضعه كقيمة لمُرشِّح Webkit Blur. -
وبطريقةٍ مشابهة، سنُغيّر قيمة
stdDeviation
لنفس قيمةblurFactor
وذلك لتطبيق التأثير في متصفح Firefox. -
في النهاية، سنسند قيمة
scaleFactor
إلى خاصيةtransform
في CSS لصورة الترويسة.
إحدى ميزات استخدام scale
و blur
معًا هي القدرة على إخفاء تأثير عدم الوضوح في جوانب الصورة، والتي لا يمكن إخفاؤها حتى باستخدام overflow: hidden
في الحالات العادية.
يمكن استخدام نفس السكربت السابق مع بعض التعديلات لكي يتم التركيز أو توضيح الصورة عندما تكون في منتصف الصفحة، مما يجذب انتباه المستخدم إليها.
تدوير العناصر (المسننات) عند التمرير
سأريك في هذا المثال كيفية تدوير العناصر أثناء تمرير الصفحة باستخدام JavaScript مباشرةً دون أيّة مكتبات خارجية.
ضبط المسننات
سيتم وضع المسننين في الصفحة باستخدام قيم id
فريدة لكلٍ منهما؛ ولمّا كانا عبارةً عن رسوميات متجهة (vector shapes) فمن المعقول أن نُنشئهما باستخدام SVG:
<div id="gearbox"> <img src="gear.svg" alt id="leftgear"> <img src="gear.svg" alt id="rightgear"> </div>
هنالك الكثير من الخيارات لطريقة وضع المسننات: ففي أغلبية الأحيان سنستعمل موضعًا ثابتًا لها (أي position: fixed
)، ومن الممكن أيضًا وضعها في مكانٍ معيّن ثم سيصبح موقعها ثابتًا عندما يتم التمرير بعدها (أي position: sticky
). سأستعمل في هذا المثال flexbox لفصل العناصر، وسأستخدم الواحدات vm لقياسها؛ ويمكن أيضًا أن يكون موضع المسننات static وبالتالي ستختفي المسننات عند تمرير الصفحة.
#gearbox { display: flex; justify-content: space-between; } #leftgear, #rightgear { width: 20vw; max-width: 20%; height: auto; }
تدوير المسننات
تدوير العناصر سهلٌ للغاية، كل ما عليك فعله هو إضافة الشيفرة الآتية في أسفل الصفحة:
var leftgear = document.getElementById("leftgear"), rightgear = document.getElementById("rightgear"); window.addEventListener("scroll", function() { leftgear.style.transform = "rotate("+window.pageYOffset+"deg)"; rightgear.style.transform = "rotate(-"+window.pageYOffset+"deg)"; });
سيدور كل مسنن بنفس مقدار تمرير النافذة مقدرًا بالبكسل والذي سيحوّل إلى درجات. لتسريع أو تبطيء دوران المسننات نسبةً إلى مقدار التمرير، فيمكنك جعل window.pageYOffset
جزءًا من تعبيرٍ رياضيٍ بسيط (كالقسمة أو الضرب بالرقم 2 على سبيل المثال).
تفادي «تعليق» المسننات
مشكلة شائعة مع أيّة تقنية التي تتعامل مع التمرير هي «تعليق» العناصر حيث ستحاول مطابقة مدخلات المستخدم. أسهل حل للتعامل مع هذه الإشكالية هو تحريك العناصر عندما يصبح المتصفح جاهزًا للتعامل مع تلك الحركة، وذلك عبر requestAnimationFrame
. ولفعل ذلك سنُجعل الدالة addEventListener
تستمع إلى حدث خاص (custom event):
window.addEventListener("optimizedScroll", function() { … })
وقبل دالة معالجة الحدث، أضف دالةً مجهولةً (anonymous function):
;(function() { var throttle = function(type, name, obj) { var obj = obj || window; var running = false; var func = function() { if (running) { return; } running = true; requestAnimationFrame(function() { obj.dispatchEvent(new CustomEvent(name)); running = false; }); }; obj.addEventListener(type, func); }; throttle ("scroll", "optimizedScroll"); })();
طريقة سهلة لإنشاء تأثير اختلاف المظهر
نَشَرَ «Adam Mustill» تقنيةً ذكيةً جدًا لإنشاء تأثير اختلاف المظهر (Parallax) عبر تعديل واحدة root em. يتساءل الكثيرون عن كيفية إنشاء تأثيرات اختلاف المظهر، وارتأيتُ أنَّ هذه الطريقة قد تكون نقطة بداية جيدة لتعلم إنشاء هذا التأثير.
تأثير اختلاف المظهر (Parallax) يتطلب عادةً عمليات معالجة معقدة لعدد كبير من عناصر DOM المنفصلة، لكن التقنية التي سنستخدمها هنا تُسهِّل علينا معالجة العناصر بتغيير قياسٍ وحيد، وهي تستغل ميزة غريبة بعض الشيء لواحدة القياس em
: إذا كان قياس العنصر الأب والعناصر الأبناء بواحدة em
، فإن القياس النهائي للأبناء هو ناتج ضربها مع بعضها. وصحيحٌ أنَّ هذا السلوك قد يُسبِّب لنا صداعًا (وهو سببٌ وجيهٌ لاستخدام rem
إن استطعنا) إلا أنَّ له ميزتين في هذه الحالة:
1. وحدة القياس em
لها دعم أقوى في المتصفحات نسبةً إلى rem
.
2. تتطلب em
وجود عنصر أب، وهذا يعني أنَّ تأثير اختلاف المظهر يمكن أن يُطبَّق على حاوية معيّنة؛ أما في طريقة rem
التي ابتكرها Adam، فإنَّ كل شيءٍ موجود في عنصر <body>
سيتأثّر بالتمرير إلا إذا اتخذتَ إجراءاتٍ معينةً للحد من ذلك.
ولقد حسّنتُ أيضًا من شيفرة Adam وجعلتها سطرًا وحيدًا من شيفرات JavaScript، بدلًا من عدِّة أسطر من jQuery، مما يُحسِّن من الأداء.
شيفرات HTML و CSS
شيفرة HTML لهذا المثال بسيطةٌ جدًا: إذ هنالك ثلاث صور وترويسة داخل عنصر <div>
:
<div id="parallax"> <h1>Simple EM Parallax Technique</h1> <img src="candles.jpg" alt> <img src="cherry-tree.jpg" alt> <img src="pagoda-surrounded-by-trees.jpg" alt> </div>
وأنماط CSS ليست معقدةً أبدًا:
#parallax { background-image: url(blurred-background-small.jpg); background-size: cover; padding-top: 62.5%; overflow: hidden; position: relative; font-size: .1em; } #parallax * { position: absolute; } #parallax img { width: 40%; height: auto; box-shadow: 0 .2em 8px 4px rgba(0,0,0,0.5); } #parallax h1 { font-size:3rem; color: #fff; z-index: 2; top: 0; text-transform: uppercase; width: 100%; text-align: center; text-shadow: 0 .2em 5px rgba(0,0,0,0.4); } #parallax img:nth-of-type(1) { left: 5%; bottom: 22em; } #parallax img:nth-of-type(2) { left: 28%; z-index: 3; bottom: 8em; } #parallax img:nth-of-type(3) { left: 55%; bottom: 12em; }
الأمور المهمة التي يجب ملاحظتها هي القيمة الافتراضية الصغيرة جدًا لقياس الحاوية #parallax
بواحدة em
، بالإضافة إلى قيم bottom
لكل صورة، وأنَّ نص الترويسة مقاسٌ بواحدة rem
وليس em
.
شيفرة JavaScript
شيفرة JavaScript هي عبارة عن سطرٍ وحيدٍ فقط يجري تعديلًا على إحدى خاصيات CSS:
window.onscroll = function() { if (window.pageYOffset > 0) { document.getElementById("parallax").style.fontSize = (window.pageYOffset/20)*.1+"em"; } }
بشرح مبسّط: سيؤدي التمرير إلى تغيير قيمة الخاصية font-size
لحاوية #parallax
، وهذا التغيير يعتمد على عدد البكسلات التي تم تمريرها، مقسومةً على 20، مضروبةً بقيمة 0.1 em
والتي هي حجم الخط الأصلي.
التمرير إلى الأسفل سيؤدي إلى زيادة قيمة الخاصية bottom
لكل صورة مما يدفعها نحو الأعلى؛ لكن الترويسة لن تتأثر لأنها مُقاسة بواحدة rem
، وهي ذات خاصية z-index
مناسبة مما يسمح للصور بالمرور أمامها وخلفها.
الخلاصة
استعرضنا التأثيرات السابقة بسرعة، بقي عليك أن تتمعّن بشيفراتها، وتحاول إيجاد استخدامات أخرى للتقنيات التي رأيتها.
ترجمة وبتصرّف للمقالات:
Fade A Responsive Background Image On Scroll
Scroll-to-Focus Effect For Hero Images
Rotate Elements on Scroll with JavaScript
لصاحبها Dudley Storey
أفضل التعليقات
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.