এই পর্বে যা শিখবেন
@keyframesলিখে এমন মোশন বানানো, যা কোনো ক্লিক বা hover ছাড়াই চলে- animation-এর আটটি property, shorthand-এর ক্রম আর
fill-mode-এর চারটি মান নিখুঁতভাবে বোঝা - negative delay, একাধিক animation আর প্রতি keyframe-এ আলাদা timing দিয়ে loader, spinner, skeleton, typewriter বানানো
- reduced motion, WCAG-এর থামানো ও ঝলকানির নিয়ম মেনে মোশনকে সবার জন্য নিরাপদ রাখা
- কোন property animate করলে পাতা মসৃণ থাকে, আর JavaScript থেকে animation-এর ঘটনা ধরা ও নিয়ন্ত্রণ করা
১ · Transition-এর পরের ধাপ — যে মোশন নিজে থেকেই চলে
পর্ব ৩৮-এ transition দেখেছেন: বোতামে মাউস রাখলে রং মসৃণভাবে বদলায়। কিন্তু খেয়াল করুন, transition নিজে কিছু শুরু করে না। কোনো একটি অবস্থা বদলাতে হয় — hover, focus, একটি class যোগ হওয়া। তখন ব্রাউজার পুরোনো মান থেকে নতুন মানে যাওয়ার পথটুকু মসৃণ করে দেয়। পথ মাত্র দুই বিন্দুর — শুরু আর শেষ।
এবার ভাবুন একটি অনলাইন দোকানের পাতা লোড হচ্ছে, আর মাঝখানে একটি গোল চাকা ঘুরছে। কেউ মাউস রাখেনি, কোনো class বদলায়নি — তবু চাকা ঘুরছে, থামছে না। এই কাজ transition পারে না। এখানে লাগে CSS animation — যে মোশনের নিজস্ব চিত্রনাট্য আছে, পাতা খুললেই চলতে শুরু করে, যতবার বলবেন ততবার চলে।
Transition হলো লিফটের দরজা: বোতাম চাপলে খোলে, ছেড়ে দিলে বন্ধ হয় — নিজে থেকে কিছু করে না। Animation হলো মেট্রো রেলের সময়সূচি: “০ মিনিটে উত্তরা, ৮ মিনিটে পল্লবী, ২০ মিনিটে মতিঝিল” — আগে থেকে লেখা সূচি ধরে ট্রেন নিজেই চলে, কেউ ধাক্কা দেয় না। সেই সময়সূচিই CSS-এ @keyframes.
@keyframes একটি at-rule. এর ভেতরে আপনি কয়েকটি মুহূর্ত (keyframe) লেখেন — মোট সময়ের কত শতাংশে element কেমন দেখাবে। from মানে 0%, to মানে 100%; মাঝে যত খুশি শতাংশ। দুই keyframe-এর মাঝের অবস্থাগুলো ব্রাউজার নিজেই হিসাব করে ভরিয়ে দেয় — একে বলে interpolation. তারপর element-এ animation property দিয়ে নামটি জুড়ে দেন। নিচের বাসটি দেখুন — কোনো বোতাম নেই:
<style>
.bus {
width: 70px; height: 40px; border-radius: 8px;
background: #16a34a; color: #fff;
display: grid; place-items: center; font: 700 14px sans-serif;
animation: drive 4s ease-in-out infinite;
}
@keyframes drive {
from { transform: translateX(0); }
50% { transform: translateX(220px); background: #dc2626; }
to { transform: translateX(0); }
}
@media (prefers-reduced-motion: reduce) {
.bus { animation: none; }
}
</style>
<p>কেউ কিছু চাপেনি — তবু বাস নিজেই চলছে:</p>
<div class="bus">বাস</div>সবুজ বাসটি ২ সেকেন্ডে ডানে গিয়ে লাল হয়ে যায়, পরের ২ সেকেন্ডে ফিরে আবার সবুজ — অবিরাম। লক্ষ করুন, from আর to-তে background লেখা নেই; যে keyframe-এ কোনো property নেই, সেখানে ব্রাউজার element-এর নিজের মান (সবুজ) ধরে নেয়। চেষ্টা করুন: 50%-এর আগে 25% { transform: translateX(110px) translateY(-30px); } যোগ করুন — বাস লাফিয়ে চলবে। তারপর ease-in-out-কে linear করে দেখুন — গতি কেমন যান্ত্রিক হয়ে যায়। আপনার কম্পিউটারে “অ্যানিমেশন কমান” সেটিং চালু থাকলে বাস স্থির থাকবে — এটি ইচ্ছাকৃত, অংশ ৭-এ কারণ দেখবেন।
কয়েকটি নিয়ম মনে রাখুন। একই শতাংশ একাধিক মুহূর্তে লাগলে কমা দিয়ে লেখা যায়: 0%, 100% { … }. Keyframe-এর ক্রম যেকোনো হতে পারে — ব্রাউজার শতাংশ অনুযায়ী সাজিয়ে নেয়। Keyframe-এর ভেতরে !important লিখলে সেই ঘোষণাটি পুরোপুরি বাদ পড়ে। আর সব property মসৃণভাবে animate হয় না: border-style-এর মতো যে মানের মাঝামাঝি অবস্থা নেই, সেটি discrete-ভাবে অর্ধেক সময়ে লাফ দেয়।
| প্রশ্ন | Transition (পর্ব ৩৮) | Animation (এই পর্ব) |
|---|---|---|
| কখন শুরু হয় | কোনো মান বদলালে (hover, class) | animation জুড়ে দিলেই, নিজে থেকে |
| কয়টি ধাপ | দুটি — পুরোনো আর নতুন মান | যত খুশি keyframe |
| বারবার চলে? | না, প্রতি পরিবর্তনে একবার | হ্যাঁ — infinite পর্যন্ত |
| শেষের মান কোথা থেকে | নতুন অবস্থার CSS থেকে | keyframe থেকে; শেষে ফেরত যায় (যদি fill-mode না থাকে) |
| কোথায় ভালো | বোতাম, মেনু, ফোকাসের প্রতিক্রিয়া | loader, মনোযোগ টানা, প্রবেশের মোশন |
২০০৭ সালে Apple-এর WebKit দল (Safari-র ইঞ্জিন) CSS transform, transition আর animation-এর প্রস্তাব ও পরীক্ষামূলক বাস্তবায়ন প্রকাশ করে, তখন লিখতে হতো -webkit-animation. তার আগে চলমান কিছু মানেই ছিল GIF ছবি, Flash প্লাগইন, JavaScript-এর টাইমার, নয়তো <marquee> ট্যাগ। পরে W3C-র CSS Animations স্পেসিফিকেশন তৈরি হয়, সব ব্রাউজার prefix ছাড়া সমর্থন দেয়। আজ CSS animation Baseline Widely available — prefix লেখার আর কোনো প্রয়োজন নেই।
২ · আটটি নিয়ন্ত্রণ — animation-এর property-গুলো
সময়সূচি (@keyframes) লিখলেন। এবার ট্রেনকে বলতে হবে কতক্ষণে পুরো পথ যাবে, কখন ছাড়বে, কতবার যাবে। এই কাজগুলো করে আটটি আলাদা property — প্রতিটির নাম animation- দিয়ে শুরু:
| Property | কী বলে | ডিফল্ট | উদাহরণ মান |
|---|---|---|---|
animation-name | কোন @keyframes | none | drive |
animation-duration | একবার চলতে কত সময় | 0s (auto) | 2s, 400ms |
animation-timing-function | গতির ধরন — কোথায় দ্রুত, কোথায় ধীর | ease | linear, steps(4) |
animation-delay | শুরুর আগে কত অপেক্ষা | 0s | 1s, -0.5s |
animation-iteration-count | কতবার চলবে | 1 | 3, 2.5, infinite |
animation-direction | কোন দিকে চলবে | normal | reverse, alternate |
animation-fill-mode | শুরুর আগে ও শেষের পরে চেহারা | none | forwards, both |
animation-play-state | চলছে, না থেমে আছে | running | paused |
ডিফল্ট duration 0s — তাই শুধু নাম লিখলে কিছুই দেখবেন না, এটি নতুনদের সবচেয়ে সাধারণ ভুল। Timing function-এর সব মান (ease, cubic-bezier(), linear()) পর্ব ৩৮-এ বিস্তারিত দেখেছেন; এখানে একই নিয়ম খাটে, শুধু প্রয়োগ হয় প্রতিটি keyframe-এর মাঝের অংশে আলাদাভাবে, পুরো animation-এ একবার নয় — অংশ ৫-এ এর মজা দেখবেন। Iteration count ভগ্নাংশও হতে পারে: 2.5 মানে আড়াইবার চলে মাঝপথে থামা। আর direction-এর alternate মানে একবার সামনে, একবার উল্টো — দোলনার মতো; alternate-reverse শুরু করে উল্টো দিক থেকে।
নিচে প্রতিটি property আলাদা লাইনে লেখা আছে, যাতে একটি একটি করে বদলে দেখতে পারেন। শেষের নিয়মটি খেয়াল করুন — play-state দিয়ে hover বা ফোকাসে থামানো:
.box { padding: 14px 20px; border-radius: 12px; }
.dot {
display: inline-block; width: 26px; height: 26px; margin: 0 8px;
border-radius: 50%; background: #2563eb;
animation-name: grow;
animation-duration: 1.2s;
animation-timing-function: ease-in-out;
animation-delay: 0s;
animation-iteration-count: infinite;
animation-direction: alternate;
animation-fill-mode: none;
animation-play-state: running;
}
.box:hover .dot, .box:focus-visible .dot { animation-play-state: paused; }
@keyframes grow { to { transform: scale(1.5); background: #f59e0b; } }
@media (prefers-reduced-motion: reduce) { .dot { animation: none; } }তিনটি নীল বিন্দু বড় হয়ে কমলা হয়, তারপর alternate-এর কারণে উল্টো পথে ছোট হয়ে নীলে ফেরে — হঠাৎ লাফ ছাড়া। বোতামে মাউস রাখলে যেখানে আছে সেখানেই জমে যায়, সরালে ঠিক সেখান থেকে আবার চলে — paused শুরুতে ফেরায় না। চেষ্টা করুন: alternate-কে normal করুন — প্রতি চক্রের শেষে বিন্দু ঝটকা দিয়ে ছোট হবে। তারপর infinite-কে 2.5 করুন — আড়াই চক্র পরে মাঝপথে থেমে… না, নীলে ফিরে যাবে! কেন, তার উত্তর অংশ ৪-এর fill-mode-এ।
প্রতিটি property-তে কমা দিয়ে একাধিক মান দেওয়া যায় — animation-name: grow, fade, animation-duration: 1s, 3s. প্রথম নামের সাথে প্রথম duration, দ্বিতীয়টির সাথে দ্বিতীয়টি; মান কম পড়লে তালিকা আবার শুরু থেকে ঘুরে ব্যবহার হয়। আর duration-এ নতুন মান auto এসেছে মূলত scroll-driven animation-এর জন্য (পর্ব ৪২), সময়ভিত্তিক animation-এ এর মানে 0s.
৩ · এক লাইনের shorthand — আর তার ক্রমের ফাঁদ
আটটি লাইন প্রতিবার লেখা ক্লান্তিকর। তাই বাস্তবে প্রায় সবাই shorthand animation লেখেন। এখানে মানগুলোর ক্রম মোটামুটি স্বাধীন — ব্রাউজার মানের ধরন দেখে বোঝে কোনটি কী। কিন্তু দুটি জায়গায় ক্রমই সব। নিচের লাইনটি টুকরো টুকরো করে দেখুন:
pulse— নাম। কোনো keyword নয় এমন শব্দকে ব্রাউজার@keyframes-এর নাম ধরে নেয়। ফাঁদ: নাম যদিease,infiniteবাforwards-এর মতো keyword হয়, ব্রাউজার সেটিকে keyword হিসেবেই পড়বে। তাই নাম রাখুন অর্থবহ ও আলাদা —pulse,slide-in,spin.2s— প্রথম সময় = duration. এটিই সবচেয়ে বড় নিয়ম: shorthand-এ প্রথম সময়ের মান সবসময় duration. মাত্র একটি সময় লিখলে সেটি duration, delay থাকে0s.ease-in-out— timing function. এর জায়গা স্বাধীন; সামনে-পেছনে যেখানে খুশি বসতে পারে।0.5s— দ্বিতীয় সময় = delay. শুরু হওয়ার আগে আধা সেকেন্ড অপেক্ষা।animation: pulse 0.5s 2sলিখলে অর্থ উল্টে যাবে — আধা সেকেন্ডের animation, দুই সেকেন্ড অপেক্ষা। এই ভুল খুব সাধারণ, আর AI-ও প্রায়ই করে।infinite— iteration count. সংখ্যা হলেও (যেমন3) ব্রাউজার বোঝে এটি সময় নয়, কারণ সাথেsবাmsনেই।alternate— direction. সামনে-পেছনে দোলা।both— fill-mode. শুরুর আগে প্রথম keyframe আর শেষে শেষ keyframe ধরে রাখে — পরের অংশে বিস্তারিত।running— play-state. সাধারণত লেখা হয় না। মনে রাখুন, shorthand যা লেখেননি সেগুলোকে ডিফল্টে ফিরিয়ে দেয় — আগে আলাদা লাইনেanimation-play-state: pausedলিখে পরে shorthand লিখলে pause মুছে যাবে। একইভাবে shorthandanimation-timeline-কেও ডিফল্টে ফেরায়, তাই পর্ব ৪২-এর scroll-driven animation-এanimation-timelineসবসময় shorthand-এর পরে লিখতে হয়।
আর shorthand যা লেখেননি, তা ডিফল্টে ফিরিয়ে দেয়
একাধিক animation shorthand-এও কমা দিয়ে লেখা যায়: animation: spin 1s linear infinite, fade-in 300ms ease-out;. প্রতিটি কমা-অংশ একটি সম্পূর্ণ animation, নিজের সময়, নিজের delay নিয়ে।
৪ · fill-mode — শুরুর আগে আর শেষের পরে কী দেখাবে
Animation-এর একটি অদ্ভুত স্বভাব আছে: সে শুধু নিজের চলার সময়টুকুতেই element-এর চেহারা নিয়ন্ত্রণ করে। Delay-এর অপেক্ষার সময় আর শেষ হওয়ার পরে element ফিরে যায় নিজের সাধারণ CSS-এ। ফলে একটি কার্ড ধীরে ধীরে দৃশ্যমান হয়ে শেষে হঠাৎ অদৃশ্য হয়ে যেতে পারে, বা delay-এর সময় পুরো দেখা গিয়ে তারপর হঠাৎ অদৃশ্য হয়ে আবার ফুটে উঠতে পারে। animation-fill-mode ঠিক এই দুই ফাঁকা সময়ের নিয়ম বলে দেয়।
ধরা যাক একটি বাক্সের নিজের রং ধূসর। Animation-এ from নীল, to সবুজ ও ডানে সরানো; delay ১ সেকেন্ড, duration দেড় সেকেন্ড, একবার চলবে। “পরে” চাপুন আর দেখুন চারটি মানে তিনটি সময়খণ্ডে বাক্স কেমন দেখায়:
1.box { background: gray; }2.box {3 animation: paint 1.5s 1s 1;4 animation-fill-mode: ;5}6@keyframes paint {7 from { background: blue; }8 to { background: green; … }9}
| ০–১ সেকেন্ড (delay) | |
| ১–২.৫ সেকেন্ড (চলছে) | |
| ২.৫ সেকেন্ডের পরে |
- প্রস্তুতি। বাক্সের নিজের রং ধূসর। Animation শুরু হবে ১ সেকেন্ড পরে, চলবে দেড় সেকেন্ড। তাই সময়রেখায় তিনটি খণ্ড: অপেক্ষা (০–১), চলা (১–২.৫), আর শেষের পরে। চলার সময় সবসময় keyframe-ই চেহারা ঠিক করে — প্রশ্ন শুধু বাকি দুই খণ্ড নিয়ে।
none(ডিফল্ট)। Animation শুধু চলার সময়টুকুর মালিক। অপেক্ষায় ধূসর, তারপর হঠাৎ নীল হয়ে সবুজের দিকে, শেষে আবার ঝটকা দিয়ে ধূসর। নতুনরা অবাক হন — “animation শেষ হয়ে ফিরে গেল কেন?” কারণ এটাই ডিফল্ট।forwards— সামনে ধরে রাখা। শেষ হওয়ার পর শেষ keyframe-এর মান থেকে যায় — বাক্স সবুজ ও ডানে। পাতা খোলার সময় ভেসে ওঠা কার্ড বা একবারের প্রবেশ-মোশনে এটিই চান। মনে রাখুন,alternate-এর সাথে জোড় সংখ্যক পুনরাবৃত্তিতে (বাreverse-এ) “শেষ” keyframe হতে পারেfrom-ও।backwards— পেছনে ধরে রাখা। Delay-এর অপেক্ষার সময়েই প্রথম keyframe (নীল) প্রয়োগ হয়। ফলে শুরুতে ঝটকা থাকে না। Delay দিয়ে সাজানো প্রবেশ-মোশনে (একটির পর একটি কার্ড) এটি জরুরি — নইলে কার্ডগুলো প্রথমে পুরো দেখা যায়, তারপর অদৃশ্য হয়ে আবার ফুটে ওঠে।both— দুই দিকেই। অপেক্ষায় প্রথম keyframe, শেষে শেষ keyframe. বেশিরভাগ একবারের animation-এ নিরাপদ পছন্দ এটিই। তবে সাবধান: ধরে রাখা মান cascade-এ animation-এর স্তরে থাকে, যা সাধারণ নিয়মকে হারায় — তাই পরে hover-এ রং বদলাতে চাইলে বদলাবে না। দরকার হলে শেষে JavaScript দিয়ে animation সরিয়ে আসল মান বসান।
এবার চারটি মান পাশাপাশি নিজের চোখে দেখুন। শেষ হয়ে গেলে “আবার চালান” চাপুন, আর প্রথম এক সেকেন্ডে কোন বাক্স কী রঙে আছে খেয়াল করুন:
<style>
.row { display: flex; align-items: center; gap: 10px; margin: 6px 0; font: 14px sans-serif; }
.row b { width: 80px; }
.box { width: 36px; height: 36px; border-radius: 6px; background: #9ca3af;
animation: paint 1.5s 1s 1; }
@keyframes paint {
from { background: #2563eb; }
to { background: #16a34a; transform: translateX(140px); }
}
.none { animation-fill-mode: none; } .fw { animation-fill-mode: forwards; }
.bw { animation-fill-mode: backwards; } .both { animation-fill-mode: both; }
@media (prefers-reduced-motion: reduce) { .box { animation-duration: 1ms; } }
</style>
<div class="row"><b>none</b><span class="box none"></span></div>
<div class="row"><b>forwards</b><span class="box fw"></span></div>
<div class="row"><b>backwards</b><span class="box bw"></span></div>
<div class="row"><b>both</b><span class="box both"></span></div>
<button type="button" id="again">আবার চালান</button>
<script>
document.getElementById('again').addEventListener('click', function () {
document.querySelectorAll('.box').forEach(function (b) {
b.style.animation = 'none'; b.offsetWidth; b.style.animation = '';
});
});
</script>শেষ হওয়ার পর none আর backwards বাক্স বাঁয়ে ধূসর অবস্থায় ফিরে আসে; forwards আর both ডানে সবুজ হয়ে থেকে যায়। “আবার চালান” চাপার পর প্রথম এক সেকেন্ডে নিচের দুটি নীল, ওপরের দুটি ধূসর — এটাই backwards-এর কাজ। বোতামের কৌশলটি খেয়াল করুন: animation সরিয়ে offsetWidth পড়লে ব্রাউজার হিসাব নতুন করে করে, তারপর আবার animation দিলে শুরু থেকে চলে। চেষ্টা করুন: .box-এর 1s-কে -0.75s করুন — সব বাক্স একেবারে মাঝপথ থেকে শুরু করবে। সেই রহস্য পরের অংশে।
৫ · সময়ের কারসাজি — negative delay, একাধিক animation আর keyframe-এর নিজস্ব গতি
Negative delay — মাঝপথ থেকে শুরু
Delay ঋণাত্মক হলে animation অপেক্ষা করে না, বরং ধরে নেয় সে আগেই শুরু হয়ে গেছে। animation: blink 1.2s -0.4s infinite মানে “ধরে নাও ০.৪ সেকেন্ড আগে শুরু হয়েছিল” — অর্থাৎ প্রথম ফ্রেমেই সে এক-তৃতীয়াংশ পথ পেরিয়ে আছে। এই কৌশলের সবচেয়ে বড় কাজ staggered loader: একই animation কয়েকটি element-এ, প্রত্যেকে একটু আগে-পরের অবস্থানে। ধনাত্মক delay দিলে পরের বিন্দুগুলো শুরুতে স্থির বসে থাকত; ঋণাত্মক delay-তে প্রথম ফ্রেম থেকেই সবাই ঢেউয়ের মতো চলে।
প্রতি keyframe-এ আলাদা গতি
animation-timing-function কোনো keyframe-এর ভেতরে লিখলে সেটি খাটে ওই keyframe থেকে পরের keyframe পর্যন্ত অংশে। একটি বল ওপর থেকে পড়ার সময় গতি বাড়ে (ease-in), মাটিতে লেগে ওঠার সময় গতি কমে (ease-out) — দুই অংশে দুই রকম গতি দিলেই লাফ বিশ্বাসযোগ্য হয়। নিচে “নিচে স্ক্রল করুন” বোঝানোর লাফানো তীরটি ঠিক তাই করছে:
<style>
.loader { display: flex; gap: 8px; margin: 12px; }
.loader span { width: 14px; height: 14px; border-radius: 50%; background: #2563eb;
animation: wave 1.2s ease-in-out infinite; }
.loader span:nth-child(1) { animation-delay: -0.8s; }
.loader span:nth-child(2) { animation-delay: -0.4s; }
@keyframes wave {
0%, 100% { opacity: .3; transform: scale(.7); }
50% { opacity: 1; transform: scale(1); }
}
.arrow { display: inline-block; margin: 8px 20px; font: 34px sans-serif;
animation: bounce 1.6s infinite; }
@keyframes bounce {
0% { transform: translateY(0); animation-timing-function: ease-in; }
40% { transform: translateY(16px); animation-timing-function: ease-out; }
100% { transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) { .loader span, .arrow { animation: none; } }
</style>
<div class="loader" aria-hidden="true"><span></span><span></span><span></span></div>
<span class="arrow" aria-hidden="true">↓</span>তিনটি নীল বিন্দু বাঁ থেকে ডানে ঢেউয়ের মতো উজ্জ্বল হয় — প্রথমটি সবচেয়ে এগিয়ে (−০.৮ সেকেন্ড), তৃতীয়টি শূন্য থেকে। নিচের তীর দ্রুত নেমে ধীরে ওঠে, যেন মাটিতে ধাক্কা খেয়েছে। চেষ্টা করুন: দুটি - চিহ্ন মুছে delay ধনাত্মক করুন — শুরুর কিছুক্ষণ (০.৪–০.৮ সেকেন্ড) প্রথম দুটি বিন্দু পুরো উজ্জ্বল বড় অবস্থায় (নিজের CSS-এ) বসে থাকবে, তারপর হঠাৎ চলবে। তারপর দুই keyframe-এর timing function অদলবদল করুন — তীর যেন “ভারহীন” হয়ে যায়।
একাধিক animation একসাথে — আর তাদের ঝগড়া
একটি element-এ কমা দিয়ে কয়েকটি animation দিলে সবগুলো একসাথে চলে। কিন্তু দুটি যদি একই property (ধরুন দুটিই transform) animate করে, তাহলে তালিকার শেষেরটি জেতে আর প্রথমটির transform পুরো মুছে যায় — কারণ transform একটিই মান। দুটি সমাধান: আলাদা property ব্যবহার করুন — আধুনিক CSS-এ translate, rotate, scale আলাদা property, তাই একটিতে ঘোরান, আরেকটিতে সরান (পর্ব ৪০-এ বিস্তারিত); অথবা animation-composition ব্যবহার করুন।
/* ঘোরা আর ভাসা — দুটি আলাদা property, তাই কোনো ঝগড়া নেই */
.coin { animation: spin 3s linear infinite, float 1.5s ease-in-out infinite alternate; }
@keyframes spin { to { rotate: 1turn; } }
@keyframes float { to { translate: 0 -12px; } }
/* animation-composition: element-এর নিজের transform-এর ওপর যোগ করো, মুছে দিও না */
.tag { transform: rotate(-8deg); animation: nudge 1s; animation-composition: add; }
@keyframes nudge { to { transform: translateX(20px); } }animation-composition-এর তিনটি মান: replace (ডিফল্ট — animation-এর মান বসে যায়), add (নিজের মানের পরে animation-এর মান জুড়ে দেয়), accumulate (সংখ্যাগুলো যোগ করে মেলায়)। এটি সব প্রধান ব্রাউজারের বর্তমান সংস্করণে আছে, তবে তুলনামূলক নতুন — দরকারি জায়গায় আলাদা property-র কৌশলটিই সহজ ও নিরাপদ।
৬ · বাস্তবের নকশা — spinner, skeleton, badge, typewriter আর ticker
এবার এমন কয়েকটি নকশা, যা প্রতিদিন bKash, Daraz বা খবরের সাইটে দেখেন। প্রতিটিতে খেয়াল করুন দুটি জিনিস: শুধু transform/opacity নড়ছে কি না, আর reduced motion-এ কী হচ্ছে।
Loading spinner আর skeleton screen
Spinner বলে “কাজ চলছে, অপেক্ষা করুন।” কিন্তু গবেষণা ও অভিজ্ঞতা বলে, কোন জায়গায় কী আসবে তার ধূসর রূপরেখা দেখালে অপেক্ষা কম মনে হয় — একে বলে skeleton screen. ধূসর বাক্সের ওপর দিয়ে একটি হালকা আলোর রেখা (shimmer) বয়ে যায়, যাতে বোঝা যায় পাতা আটকে যায়নি:
<style>
.spinner { width: 36px; height: 36px; border-radius: 50%;
border: 4px solid #dbeafe; border-top-color: #2563eb;
animation: spin 1s linear infinite; }
@keyframes spin { to { transform: rotate(1turn); } }
.card { display: flex; gap: 12px; width: 280px; margin-top: 16px; }
.lines { flex: 1; display: grid; gap: 8px; align-content: center; }
.sk { height: 12px; border-radius: 6px;
background: linear-gradient(90deg, #e5e7eb 35%, #f9fafb 50%, #e5e7eb 65%) 0 0 / 300% 100%;
animation: shimmer 1.6s linear infinite; }
.avatar { width: 48px; height: 48px; border-radius: 50%; }
@keyframes shimmer { from { background-position: 100% 0; } to { background-position: 0 0; } }
.vh { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
@media (prefers-reduced-motion: reduce) {
.spinner { animation-duration: 3s; }
.sk { animation: none; }
}
</style>
<div role="status"><div class="spinner"></div><span class="vh">লোড হচ্ছে…</span></div>
<div class="card" aria-hidden="true">
<div class="sk avatar"></div>
<div class="lines"><div class="sk"></div><div class="sk" style="width: 70%"></div></div>
</div>ওপরে নীল-মাথার চাকা ঘুরছে, নিচে একটি প্রোফাইল-কার্ডের ধূসর রূপরেখা, যার ওপর দিয়ে সাদাটে আলো বাঁ থেকে ডানে বয়ে যায়। চোখে দেখা যায় না কিন্তু জরুরি: role="status" আর visually hidden লেখা “লোড হচ্ছে…” — screen reader ব্যবহারকারী ঘূর্ণন দেখেন না, তাঁকে কথায় জানাতে হয়; আর skeleton aria-hidden, কারণ ফাঁকা বাক্স পড়ে শোনানোর কিছু নেই। Reduced motion-এ spinner থামেনি, ধীর হয়েছে — পুরো থেমে গেলে “কাজ চলছে” বার্তাটিই হারিয়ে যায়। চেষ্টা করুন: linear-কে ease-in-out করুন — চাকা প্রতি পাকে থমকে যাবে, যা loader-এ অস্বস্তিকর। সতর্কতা: shimmer-এ background-position নড়ে, যা প্রতি ফ্রেমে paint করায়; ছোট এলাকায় চলে, বড় পাতায় একটি pseudo-element-কে transform: translateX() দিয়ে সরানো ভালো।
নোটিফিকেশন badge-এর স্পন্দন আর steps() দিয়ে typewriter
নতুন বার্তা এলে লাল badge-এর চারপাশে একটি ঢেউ ছড়িয়ে পড়ে — আসল badge স্থির থাকে, নড়ে শুধু তার পেছনের একটি ::after. আর steps() timing function মসৃণ গতির বদলে নির্দিষ্ট কয়েকটি লাফে চলে — ঘড়ির কাঁটার টিক টিকের মতো। ১৩ অক্ষরের লেখাকে steps(13) দিয়ে চওড়া করলে প্রতি লাফে একটি করে অক্ষর বেরোয়, ঠিক টাইপরাইটারের মতো:
<style>
.bell { position: relative; border: 0; background: none; font-size: 30px; margin: 14px; }
.badge { position: absolute; top: 0; right: -6px; min-width: 20px; height: 20px;
border-radius: 10px; background: #dc2626; color: #fff;
font: 700 12px/20px sans-serif; isolation: isolate; }
.badge::after { content: ""; position: absolute; inset: 0; z-index: -1;
border-radius: inherit; background: #dc2626;
animation: ping 2s ease-out infinite; }
@keyframes ping { from { transform: scale(1); opacity: .7; } to { transform: scale(2.4); opacity: 0; } }
.type { width: 13ch; overflow: hidden; white-space: nowrap; font: 20px monospace;
border-right: 2px solid; animation: typing 3s steps(13) both, caret .75s step-end 4; }
@keyframes typing { from { width: 0; } }
@keyframes caret { 50% { border-color: transparent; } }
@media (prefers-reduced-motion: reduce) { .badge::after, .type { animation: none; } }
</style>
<button type="button" class="bell" aria-label="নোটিফিকেশন — ৩টি নতুন">🔔<span class="badge" aria-hidden="true">3</span></button>
<p class="type">Hello, Dhaka!</p>ঘণ্টার কোণে লাল “3”-এর চারপাশে হালকা লাল বৃত্ত বড় হতে হতে মিলিয়ে যায়, বারবার। নিচে “Hello, Dhaka!” এক এক অক্ষর করে ৩ সেকেন্ডে লেখা হয়, পাশে কার্সর ৪ বার জ্বলে-নেভে, তারপর লেখা পুরো দেখা যায় আর সব থামে। typing-এ শুধু from আছে — to না থাকলে ব্রাউজার element-এর নিজের মান (13ch) নেয়। চেষ্টা করুন: steps(13)-কে steps(4) করুন — চারটি বড় লাফে লেখা বেরোবে; তারপর linear করুন — অক্ষর কেটে কেটে মসৃণভাবে বেরোবে। কার্সরের 4 ইচ্ছা করে সীমিত — অসীম জ্বলা-নেভা WCAG 2.2.2-এর আওতায় পড়ে (অংশ ৭)।
বাংলায় typewriter কৌশল ভাঙে। ch একক আর steps() ধরে নেয় প্রতিটি অক্ষর সমান চওড়া, আর এক অক্ষর মানে এক ঘর। বাংলায় দুটিই মিথ্যা: “ক্ষ” বা “ন্ত্র”-এর মতো যুক্তাক্ষর কয়েকটি Unicode অক্ষর মিলে একটি আকৃতি, আর কার-চিহ্ন (ি, ে) আগের বা পরের অক্ষরের সাথে জোড়া। মাঝপথে কাটলে ভাঙা আকৃতি দেখাবে। বাংলা লেখায় হয় পুরো লেখাটি ধীরে opacity দিয়ে ফুটিয়ে তুলুন, নয়তো JavaScript-এ Intl.Segmenter দিয়ে লেখাকে আসল দৃশ্যমান অক্ষরগুচ্ছে (grapheme) ভেঙে একটি একটি করে দেখান (পর্ব ১৪-এ বাংলা Unicode-এর কথা দেখেছেন)।
<marquee> নয় — CSS দিয়ে চলমান খবরের ফিতা
টিভির নিচে যেমন খবরের লাইন সরে যায়, ১৯৯০-এর দশকে ওয়েবে তার জন্য ছিল <marquee> ট্যাগ। HTML Living Standard এটিকে এখন obsolete (বাতিল, নিয়মবহির্ভূত) তালিকায় রেখেছে — ব্রাউজার পুরোনো সাইট না ভাঙতে এখনো দেখায়, কিন্তু নতুন কোডে লেখা ভুল। কারণ HTML-এর কাজ অর্থ, চেহারা নয়; আর এটি reduced motion মানে না, ব্যবহারকারীকে থামানোর সহজ উপায় দেয় না। CSS-এর বিকল্পে আমরা সবকিছু নিয়ন্ত্রণ করি — এমনকি একটি “থামান” বোতামও দিই:
<style>
.ticker { overflow: hidden; border: 1px solid #fcd34d; background: #fffbeb; font: 15px sans-serif; }
.track { display: flex; width: max-content; padding: 8px 0;
animation: scroll 18s linear infinite; }
.track span { padding-right: 48px; }
.ticker:hover .track, .ticker:focus-within .track, .paused .track { animation-play-state: paused; }
@keyframes scroll { to { transform: translateX(-50%); } }
@media (prefers-reduced-motion: reduce) {
.track { animation: none; width: auto; flex-wrap: wrap; }
.track [aria-hidden] { display: none; }
}
</style>
<div class="ticker" id="ticker"><div class="track">
<span>ঢাকায় আজ বৃষ্টির সম্ভাবনা</span><span>মেট্রো রেল চলছে নিয়মিত</span><span>বইমেলা শুরু ১ ফেব্রুয়ারি</span>
<span aria-hidden="true">ঢাকায় আজ বৃষ্টির সম্ভাবনা</span><span aria-hidden="true">মেট্রো রেল চলছে নিয়মিত</span><span aria-hidden="true">বইমেলা শুরু ১ ফেব্রুয়ারি</span>
</div></div>
<p><button type="button" id="pause" aria-pressed="false">থামান</button></p>
<script>
document.getElementById('pause').addEventListener('click', function () {
var on = document.getElementById('ticker').classList.toggle('paused');
this.setAttribute('aria-pressed', on); this.textContent = on ? 'চালান' : 'থামান';
});
</script>হলুদ ফিতায় তিনটি খবর ধীরে বাঁয়ে সরে, আর শেষ হতেই কোনো ফাঁক ছাড়া আবার শুরু — কারণ খবরগুলো দুবার লেখা, আর আমরা ঠিক অর্ধেক (-50%) সরাই; অর্ধেক পেরোলে দ্বিতীয় কপি ঠিক প্রথমটির জায়গায় থাকে। দ্বিতীয় কপি aria-hidden, যাতে screen reader একই খবর দুবার না পড়ে। মাউস রাখলে বা বোতাম চাপলে থামে; reduced motion-এ ফিতা স্থির তালিকা হয়ে যায়। চেষ্টা করুন: 18s-কে 6s করুন — পড়া কতটা কঠিন হয় দেখুন। তারপর .track span-এর padding-right মুছে দিয়ে .track-এ gap: 48px দিন — প্রতি চক্রে ছোট একটি ঝাঁকুনি দেখবেন, কারণ gap থাকলে দুই কপির মাপ আর ঠিক অর্ধেক থাকে না।
@property দিয়ে variable animate করা — পর্ব ৩৭-এর পুনরালোচনা
পর্ব ৩৭-এ দেখেছেন, সাধারণ custom property-র মান ব্রাউজারের কাছে শুধু লেখা, তাই keyframe-এ বদলালে সেটি discrete-ভাবে লাফ দেয়। @property দিয়ে ধরন জানালে মাঝের মান হিসাব করা যায়। একটি progress-বৃত্ত শূন্য থেকে ৭৫% পর্যন্ত ভরানো — gradient নিজে animate হয় না, কিন্তু তার ভেতরের variable হয়:
@property --p { syntax: '<percentage>'; inherits: false; initial-value: 0%; }
.ring {
width: 80px; aspect-ratio: 1; border-radius: 50%;
background: conic-gradient(#16a34a var(--p, 75%), #e5e7eb 0);
animation: fill 1.5s ease-out both;
}
@keyframes fill { from { --p: 0%; } to { --p: 75%; } }
@media (prefers-reduced-motion: reduce) { .ring { animation: none; --p: 75%; } }@property এখন Baseline Newly available; যে ব্রাউজার চেনে না, সেখানে বৃত্ত মসৃণভাবে ভরে না, মাঝপথে এক লাফে ৭৫%-এ পৌঁছায় — মোশন নেই, তথ্য ঠিক। এটাই progressive enhancement.
৭ · সবার জন্য নিরাপদ মোশন — reduced motion আর WCAG
মোশন সবার কাছে আনন্দের নয়। কানের ভেতরের ভারসাম্য-ব্যবস্থার সমস্যায় (vestibular disorder) ভোগা অনেকের কাছে বড় জুম, প্যারালাক্স বা ঘূর্ণন মাথা ঘোরা, বমিভাব ঘটাতে পারে — চলন্ত লঞ্চে বই পড়ার মতো অনুভূতি। মনোযোগে সমস্যা (ADHD) থাকলে কোণে অবিরাম নড়তে থাকা কিছু পুরো পাতা পড়াই কঠিন করে। আর দ্রুত ঝলকানি কিছু মানুষের খিঁচুনি ঘটাতে পারে — এটি অস্বস্তি নয়, চিকিৎসাগত বিপদ।
প্রতিটি আধুনিক অপারেটিং সিস্টেমে মোশন কমানোর সেটিং আছে — Windows-এ “Animation effects” বন্ধ করা, Android-এ “Remove animations”, iPhone ও Mac-এ “Reduce Motion”. ব্রাউজার এই পছন্দ prefers-reduced-motion media feature দিয়ে আপনাকে জানায়। এই পর্বের প্রতিটি উদাহরণে এটি আছে। দুটি লেখার ধরন:
/* ধরন ১ — মোশন সবার জন্য, যিনি চান না তাঁর জন্য বন্ধ/নরম */
.card { animation: rise .5s ease-out both; }
@media (prefers-reduced-motion: reduce) {
.card { animation-name: fade; } /* সরে আসার বদলে শুধু ফুটে ওঠা */
}
/* ধরন ২ — মোশন-বিহীন ভিত্তি, শুধু যিনি আপত্তি করেননি তাঁকে মোশন */
@media (prefers-reduced-motion: no-preference) {
.card { animation: rise .5s ease-out both; }
}
@keyframes rise { from { opacity: 0; translate: 0 24px; } }
@keyframes fade { from { opacity: 0; } }“Reduce” মানে সবসময় “শূন্য” নয়। স্থান-বদলের মোশন (সরানো, জুম, ঘোরানো) সবচেয়ে বেশি সমস্যা করে; রঙ বা opacity-র নরম পরিবর্তন সাধারণত নিরাপদ। আর যে মোশন তথ্য বহন করে (spinner, progress), তা পুরো বন্ধ না করে ধীর বা স্থির বিকল্পে বদলান। একটি সতর্কতা: অনেকে সব animation বন্ধ করতে * { animation: none !important; } লেখেন — এতে animationend-এর ওপর নির্ভর করা JavaScript আর কখনো ঘটনা পায় না, পাতা আটকে যায়। নিরাপদ বিকল্প animation-duration: 1ms, যাতে animation “শেষ” হয়, কিন্তু চোখে পড়ে না।
2.2.2 Pause, Stop, Hide (Level A): যে চলমান, জ্বলা-নেভা বা স্ক্রল করা তথ্য নিজে থেকে শুরু হয়, ৫ সেকেন্ডের বেশি চলে, আর পাতার অন্য বিষয়ের পাশাপাশি দেখায় — তাকে থামানো, বন্ধ করা বা লুকানোর উপায় থাকতে হবে, যদি না মোশনটি কাজের জন্য অপরিহার্য হয়। খবরের ফিতা, অবিরাম স্লাইডার, অসীম স্পন্দন — সবই এর আওতায়।
2.3.1 Three Flashes or Below Threshold (Level A): কোনো এক সেকেন্ডে ৩ বারের বেশি ঝলকানি নয় (অথবা ঝলকানি এত ছোট ও মৃদু যে নির্ধারিত সীমার নিচে)। লাল রঙের ঝলকানি সবচেয়ে বিপজ্জনক।
2.3.3 Animation from Interactions (Level AAA): ক্লিক বা স্ক্রলে শুরু হওয়া মোশন বন্ধ করা যাবে — prefers-reduced-motion মানাই এর সহজ উপায়।
2.2.2-এর একটি ব্যতিক্রম বুঝে রাখুন: পাতা লোডের সময় যখন অন্য কিছু করার নেই, তখন spinner-কে অপরিহার্য ধরা যায়। কিন্তু লোড শেষে spinner পাতার কোণে ঘুরতেই থাকলে আর ব্যতিক্রম খাটে না — কাজ শেষ হলে সরিয়ে দিন। WCAG-এর প্রতিটি success criterion অনেক দেশে আইনি মানদণ্ড হিসেবেও ব্যবহৃত হয় — সেসব দেশে সরকারি সাইটসহ অনেক সাইটের জন্য এগুলো বাধ্যতামূলক। অংশ ২-এর hover-pause একা যথেষ্ট নয়, কারণ ফোন ব্যবহারকারীর মাউস নেই — একটি আসল বোতাম রাখুন, যেমন ticker-এ রেখেছি। পর্ব ৪৫-এ অ্যাক্সেসিবল CSS-এর পুরো ছবি দেখবেন।
৮ · মসৃণ, না ঝাঁকুনি — animation-এর পারফরম্যান্স
সিনেমা যেমন আসলে সেকেন্ডে কয়েক ডজন স্থির ছবি, পর্দার animation-ও তেমনি ফ্রেমের সারি। বেশিরভাগ পর্দা সেকেন্ডে ৬০ বার নতুন ছবি আঁকে (অনেক নতুন ফোনে ৯০ বা ১২০ বার), তাই প্রতিটি ফ্রেমের জন্য ব্রাউজার পায় মোটামুটি ১৬ মিলিসেকেন্ড — এই ফ্রেমের বাজেট পেরোলে ফ্রেম বাদ পড়ে, চোখে লাগে ঝাঁকুনি, যাকে বলে jank. কোন property animate করছেন, তার ওপর নির্ভর করে প্রতি ফ্রেমে কত কাজ। পর্ব ১-এর rendering ধাপগুলো মনে করে দেখুন:
- এক ফ্রেমের পুরো পথ। পাতায় কিছু বদলালে ব্রাউজার ধাপে ধাপে কাজ করে: কোন CSS নিয়ম খাটে (Style), প্রতিটি বাক্সের মাপ ও জায়গা (Layout), পিক্সেলে রং বসানো (Paint), আর আলাদা স্তরগুলো জুড়ে পর্দায় পাঠানো (Composite). Animation মানে এই কাজ সেকেন্ডে ৬০ বার।
left,top,width,margin— সবচেয়ে দামি। এগুলো বদলালে বাক্সের মাপ-জায়গা বদলায়, ফলে প্রতি ফ্রেমে Layout আবার — আর একটি বাক্স সরলে আশেপাশের অনেক বাক্সের হিসাবও বদলাতে পারে। তারপর Paint, তারপর Composite. ধীর ফোনে এখানেই jank.background-color,box-shadow,color— মাঝারি। জায়গা বদলায় না, তাই Layout বাদ; কিন্তু প্রতি ফ্রেমে নতুন করে Paint করতে হয়। ছোট এলাকায় চলে, বড় ছায়া বা পুরো পাতাজুড়ে হলে ভারী।transformআরopacity— সবচেয়ে সস্তা। এগুলো element-এর আঁকা ছবিকে না বদলে শুধু সরায়, ঘোরায় বা স্বচ্ছ করে। ব্রাউজার element-টিকে আলাদা স্তরে (layer) রেখে শুধু Composite ধাপে কাজ সারে, প্রায়ই GPU-র সাহায্যে। তাই সরাতেleftনয়,transform: translate(); বড় করতেwidthনয়,scale; লুকাতেopacity.- Main thread ব্যস্ত থাকলেও। ভারী JavaScript চললে main thread আটকে থাকে, ফলে Layout-Paint-নির্ভর animation থেমে থেমে চলে। কিন্তু
transform/opacity-র CSS animation ব্রাউজার আলাদা compositor thread-এ চালাতে পারে — তাই JavaScript আটকে থাকলেও spinner মসৃণ ঘোরে। এটিই সবচেয়ে বড় কারণ যে loader-এ শুধুtransformব্যবহার করবেন।
will-change: transform লিখে ব্রাউজারকে আগাম জানানো যায় যে element-টি নড়বে, যাতে সে আগেই আলাদা স্তর বানিয়ে রাখে। কিন্তু প্রতিটি স্তর মেমরি খায়; শত শত element-এ লিখলে ফোনে উল্টো ধীর হয়। নিয়ম: আগে মেপে দেখুন সমস্যা আছে কি না, তারপর শুধু সেই element-এ, আর সম্ভব হলে animation-এর আগে যোগ করে পরে সরিয়ে দিন। চলমান CSS animation-এর ক্ষেত্রে ব্রাউজার সাধারণত নিজেই স্তর বানায়।
নিজে মাপবেন কীভাবে? Chrome বা Edge-এর DevTools-এ Performance প্যানেলে রেকর্ড করুন — বেগুনি (Layout) আর সবুজ (Paint) ব্লক প্রতি ফ্রেমে দেখলে বুঝবেন দামি property নড়ছে। “Rendering” ট্যাবের “Paint flashing” চালু করলে যেখানে নতুন করে paint হচ্ছে সেখানে সবুজ ঝলক দেখায়। আর “Animations” প্যানেলে পাতার সব animation সময়রেখায় দেখা যায়, ১০% গতিতে ধীর করে খুঁটিয়ে দেখা যায়।
আরও গভীরে: স্পেসিফিকেশনের দিক থেকে CSS animation আর transition আলাদা কোনো যন্ত্র নয় — দুটিই Web Animations মডেলের ভেতরের বস্তু (CSSAnimation, CSSTransition), একই সময়রেখা (document timeline) আর একই timing হিসাব মেনে চলে। তাই document.getAnimations() ডাকলে পাতার সব CSS animation JavaScript বস্তু হিসেবে হাতে আসে, যাদের থামানো, গতি বদলানো বা সামনে টেনে নেওয়া যায়। Chromium-এর মতো ইঞ্জিন transform, opacity, filter-এর মতো কয়েকটি property-র animation পুরোটাই compositor-এর হাতে তুলে দেয় (“accelerated animation”), আর main thread-কে প্রতি ফ্রেমে কিছু হিসাব করতে হয় না।
৯ · JavaScript-এর সাথে — animation-এর ঘটনা আর Web Animations API
কখনো animation শেষ হলে কিছু করতে হয় — যেমন “টাকা পাঠানো হয়েছে” বার্তা ভেসে ওঠার পর কয়েক সেকেন্ড রেখে সরিয়ে দেওয়া। CSS animation চারটি ঘটনা (event) পাঠায়: animationstart, animationiteration (প্রতি চক্র শেষে), animationend, আর animationcancel (মাঝপথে সরিয়ে নিলে)। JavaScript-এর ঘটনা ধরার নিয়ম আমাদের JavaScript কোর্সের events পর্বে বিস্তারিত আছে।
আর পুরো animation JavaScript থেকেই বানাতে চাইলে আছে Web Animations API — element.animate(keyframes, options). Keyframe হয় বস্তুর তালিকা, option-এ সেই চেনা নামগুলো: duration (মিলিসেকেন্ডে), easing, iterations, direction, fill. ফেরত আসে একটি Animation বস্তু, যার pause(), play(), reverse(), playbackRate আর শেষ হওয়ার promise finished আছে। এটি Baseline Widely available.
<style>
.toast { width: max-content; padding: 10px 14px; border-radius: 8px;
background: #16a34a; color: #fff; font: 15px sans-serif; animation: pop .4s ease-out; }
@keyframes pop { from { opacity: 0; transform: translateY(10px); } }
.ball { width: 30px; height: 30px; margin: 16px 0; border-radius: 50%; background: #f59e0b; }
@media (prefers-reduced-motion: reduce) { .toast { animation-duration: 1ms; } }
</style>
<div class="toast" id="toast">টাকা পাঠানো হয়েছে ✓</div>
<div class="ball" id="ball"></div>
<button type="button" id="go">Web Animations API দিয়ে চালান</button>
<script>
document.getElementById('toast').addEventListener('animationend', function (e) {
console.log('animationend: ' + e.animationName + ', ' + e.elapsedTime + 's');
});
var reduce = matchMedia('(prefers-reduced-motion: reduce)').matches;
document.getElementById('go').addEventListener('click', function () {
var anim = document.getElementById('ball').animate(
[{ transform: 'translateX(0)' }, { transform: 'translateX(200px)' }],
{ duration: reduce ? 1 : 700, easing: 'ease-in-out', iterations: 2, direction: 'alternate' });
anim.finished.then(function () { console.log('WAAPI: বল ফিরে এসেছে'); });
});
</script>সবুজ বার্তাটি নিচ থেকে ভেসে ওঠে, আর শেষ হতেই নিচে লগে আসে animationend: pop, 0.4s. বোতাম চাপলে কমলা বল ডানে গিয়ে ফিরে আসে, তারপর লগে “WAAPI: বল ফিরে এসেছে”. খেয়াল করুন reduced motion-এ বার্তার animation none নয়, 1ms — তাই ঘটনাটি তবুও আসে; আর বলের সময়ও তখন 1 মিলিসেকেন্ড, যা matchMedia() দিয়ে জেনে নেওয়া। চেষ্টা করুন: iterations: 2-কে Infinity করুন — বল আর থামবে না, আর finished-এর লগ কখনো আসবে না। তারপর option-এ fill: 'forwards' যোগ করে iterations: 1 করুন — বল ডানে থেকে যাবে, CSS-এর forwards-এর মতোই।
কখন কোনটি? সাজসজ্জার, আগে থেকে জানা মোশন (loader, প্রবেশ, hover) — CSS. ব্যবহারকারীর আচরণ অনুযায়ী মান হিসাব করতে হয় (টেনে নেওয়া, ক্লিকের জায়গা থেকে ঢেউ), মাঝপথে থামিয়ে উল্টো চালাতে হয়, বা কয়েকটি animation-কে সময় মিলিয়ে নিয়ন্ত্রণ করতে হয় — Web Animations API. দুটি একই ইঞ্জিনে চলে, তাই transform/opacity-র নিয়ম দুটিতেই খাটে। পাতার দুই অবস্থার মাঝের মোশনের জন্য আছে View Transitions (পর্ব ৩৭), আর স্ক্রলের সাথে বাঁধা মোশনের জন্য scroll-driven animation (পর্ব ৪২)।
পরীক্ষা ও ইন্টারভিউতে — বিশ্ববিদ্যালয়, চাকরি বা ফ্রন্ট-এন্ড ইন্টারভিউর পরীক্ষায় এ ধরনের প্রশ্ন প্রায়ই আসে:
- Transition আর animation-এর পার্থক্য কী? — transition চলে মান বদলালে, দুই অবস্থার মাঝে; animation
@keyframesধরে নিজে থেকে চলে, বহু ধাপ ও পুনরাবৃত্তিসহ। animation: move 1s 2s-এ কোনটি delay? —2s; shorthand-এ প্রথম সময় duration, দ্বিতীয়টি delay.- Animation শেষে element আগের অবস্থায় ফিরে যায় কেন? — ডিফল্ট
animation-fill-mode: none; শেষের মান রাখতেforwardsবাboth. - কোন property animate করা সবচেয়ে দক্ষ? —
transformআরopacity, কারণ এরা Layout ও Paint এড়িয়ে compositor-এ চলে। - Animation-এ অ্যাক্সেসিবিলিটির জন্য কী করবেন? —
prefers-reduced-motionমানা, ৫ সেকেন্ডের বেশি স্বয়ংক্রিয় মোশনে থামানোর উপায় (WCAG 2.2.2), আর সেকেন্ডে ৩ বারের বেশি ঝলকানি নয় (2.3.1).
১০ · AI-এর যুগে animation — সুন্দর দেখানো সহজ, ঠিক রাখা কঠিন
“একটা সুন্দর loading animation দাও” বললে AI কয়েক সেকেন্ডে চমকপ্রদ কোড দেবে। দেখতে ভালো লাগবে বলেই ভুলগুলো চোখ এড়িয়ে যায়। এই পর্ব পড়ার পর আপনি যেগুলো ধরতে পারবেন:
| AI-এর সাধারণ ভুল | সমস্যা | ঠিক উপায় |
|---|---|---|
left/top/width animate করা | প্রতি ফ্রেমে Layout, ফোনে ঝাঁকুনি | transform, opacity |
-webkit-animation, -webkit-keyframes | বহু বছর আগের অপ্রয়োজনীয় prefix | prefix ছাড়া লিখুন |
prefers-reduced-motion নেই | সংবেদনশীল ব্যবহারকারীর অসুবিধা | প্রতিটি animation-এ যোগ করুন |
| অসীম ticker/স্পন্দন, থামানোর বোতাম নেই | WCAG 2.2.2 লঙ্ঘন | pause বোতাম, বা সীমিত পুনরাবৃত্তি |
সব জায়গায় will-change | অকারণ মেমরি খরচ | মেপে, শুধু দরকারি element-এ |
| শুধু ঘুরন্ত spinner, কোনো লেখা নেই | screen reader কিছুই জানায় না | role="status" + লুকানো লেখা |
<marquee> বা JavaScript টাইমারে প্রতি ১০ মিলিসেকেন্ডে left বদলানো | obsolete, ধীর, নিয়ন্ত্রণহীন | CSS animation বা Web Animations API |
AI-কে প্রথম থেকেই শর্তগুলো বলে দিন, তারপর কোড পেলে নিজে যাচাই করুন:
AI-এর animation কোড হাতে পেলে — যাচাইয়ের চেকলিস্ট
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন। এগুলোই আপনাকে কোড-লেখক থেকে নকশা-চিন্তক বানাবে।
প্র ০১<marquee> আর <blink> বিদায় নিল, অথচ CSS animation টিকে গেল — পার্থক্যটা আসলে কোথায়?
১৯৯০-এর দশকের মাঝামাঝি ব্রাউজার-যুদ্ধের সময় প্রতিটি কোম্পানি নিজের “চমক” ট্যাগ বানাত। Netscape আনে <blink> — লেখা জ্বলে-নেভে; Microsoft-এর Internet Explorer আনে <marquee> — লেখা এক পাশ থেকে আরেক পাশে সরে। দুটিই কোনো মানদণ্ড-সংস্থার আলোচনা ছাড়াই এসেছিল, আর দ্রুত ওয়েবের সবচেয়ে বিরক্তিকর জিনিসের প্রতীক হয়ে ওঠে।
সমস্যা মোশন নিজে নয়, তিনটি নকশাগত ভুল। প্রথমত, অর্থ আর চেহারার মিশ্রণ। HTML-এর কাজ বলা “এটি কী” — শিরোনাম, তালিকা, খবর। “এটি সরে যাবে” চেহারার কথা, যা CSS-এর এলাকা। ট্যাগে মোশন বাঁধলে একই খবর মোবাইলে, প্রিন্টে বা screen reader-এ ভিন্নভাবে দেখানোর উপায় থাকে না। দ্বিতীয়ত, নিয়ন্ত্রণহীনতা। গতি, থামা, ব্যবহারকারীর পছন্দ — কোনোটিই সহজে বদলানো যেত না। তৃতীয়ত, অ্যাক্সেসিবিলিটি। অবিরাম জ্বলা-নেভা মনোযোগ নষ্ট করে, আর ঝলকানি কিছু মানুষের জন্য বিপজ্জনক।
পরিণতি: <blink> কখনো মানদণ্ডে ঢোকেনি, আর ২০১০-এর দশকের শুরুর দিকে শেষ বড় ব্রাউজারও এর সমর্থন তুলে নেয়। <marquee> এখনো ব্রাউজারে কাজ করে, কারণ লাখো পুরোনো পাতা ভাঙা চলে না — HTML Living Standard তাই এটিকে “obsolete” তালিকায় রেখে ব্রাউজারের জন্য আচরণ লিখে রেখেছে, কিন্তু লেখককে নিষেধ করেছে। এটি ওয়েবের একটি মূলনীতি: “don't break the web” — পুরোনো কিছু মুছে ফেলা যায় না, শুধু নিরুৎসাহিত করা যায়।
CSS animation একই কাজ করে, কিন্তু উল্টো দর্শনে: মোশন আলাদা স্তরে, প্রতিটি দিক (সময়, গতি, পুনরাবৃত্তি, থামা) নিয়ন্ত্রণযোগ্য, আর media query দিয়ে ব্যবহারকারীর পছন্দ মানা যায়।
মূল উপলব্ধি: প্রযুক্তি টেকে যখন সে নিয়ন্ত্রণ ব্যবহারকারী আর লেখকের হাতে দেয়। একই মোশন খারাপ বা ভালো হতে পারে — পার্থক্য করে কে, কখন, কীভাবে সেটি থামাতে পারে।
প্র ০২transform “সস্তা” আর left “দামি” — তাহলে সব element-কে GPU স্তরে তুলে দিলেই তো সব দ্রুত হতো?
যুক্তিটি লোভনীয়, কিন্তু ভুল — আর এর ভেতরেই কম্পিউটার গ্রাফিক্সের একটি মৌলিক trade-off লুকিয়ে আছে।
ব্রাউজার যখন একটি element-কে আলাদা compositor layer-এ তোলে, তখন তার পুরো ছবি একটি আলাদা “টেক্সচার” হিসেবে GPU-র মেমরিতে রাখে। ৩০০×৩০০ পিক্সেলের একটি কার্ড মানে প্রায় ৯০ হাজার পিক্সেল, প্রতি পিক্সেলে ৪ বাইট (লাল, সবুজ, নীল, স্বচ্ছতা) — প্রায় ৩৬০ কিলোবাইট। উচ্চ-ঘনত্বের পর্দায় (device pixel ratio 3) সেটি নয় গুণ, তিন মেগাবাইটের বেশি। এখন ভাবুন একটি পণ্যের তালিকায় ২০০টি কার্ড, সবগুলোতে will-change: transform — কমদামি অ্যান্ড্রয়েড ফোনের GPU মেমরি সহজেই ভরে যায়। তখন ব্রাউজার স্তর বাদ দিতে বাধ্য হয়, অথবা পাতা ক্র্যাশ করে। এই সমস্যার নাম “layer explosion”.
দ্বিতীয় খরচ: স্তর বানাতে প্রথমবার element-কে paint করে GPU-তে পাঠাতে হয় (upload). লেখার আকার বদলালে বা ভেতরের বিষয় বদলালে আবার upload. আর স্তরে থাকা লেখা কখনো কখনো ঝাপসা দেখায়, কারণ সেটি ছবি হিসেবে ছোট-বড় হচ্ছে।
এজন্য ব্রাউজার ইঞ্জিনিয়াররা নিজেরাই কখন স্তর বানাবে তার জটিল নিয়ম রাখেন: চলমান transform/opacity animation, ভিডিও, canvas, fixed element ইত্যাদি। will-change শুধু একটি ইঙ্গিত, আদেশ নয় — আর ব্রাউজার নির্মাতারাই সতর্ক করেন, এটিকে শেষ উপায় হিসেবে ব্যবহার করতে।
বাস্তব নিয়ম: যা নড়বে শুধু সেটিতে transform/opacity ব্যবহার করুন; কোথাও ধীর লাগলে DevTools-এর Layers প্যানেলে দেখুন কতগুলো স্তর আর কত মেমরি; তারপর সিদ্ধান্ত নিন।
মূল উপলব্ধি: পারফরম্যান্সে “সস্তা” মানে কোনো একটি সম্পদ (সময়) বাঁচিয়ে আরেকটি (মেমরি) খরচ করা। অন্ধ নিয়ম নয় — মাপুন, তারপর বেছে নিন।
প্র ০৩jQuery-র .animate() থেকে CSS animation, তারপর Web Animations API — ওয়েবে মোশন বানানোর পদ্ধতি কেন বারবার বদলাল?
২০০০-এর দশকের মাঝামাঝি থেকে ওয়েবে মোশন মানে ছিল JavaScript লাইব্রেরি। jQuery-র .animate() ভেতরে একটি টাইমার চালাত — প্রতি কয়েক মিলিসেকেন্ডে left বা height-এর মান একটু বদলে দিত। সমস্যাগুলো ছিল গভীর: টাইমার পর্দার রিফ্রেশের সাথে মেলে না, তাই কিছু ফ্রেম দুবার আঁকা হয়, কিছু বাদ পড়ে; সবকিছু main thread-এ চলে, তাই পাতা ব্যস্ত থাকলে মোশন থমকায়; আর পেছনের ট্যাবেও টাইমার চলে ব্যাটারি খায়।
প্রথম সংশোধন এলো requestAnimationFrame দিয়ে — ব্রাউজার বলে “পরের ফ্রেম আঁকার ঠিক আগে আপনার কোড চালাব,” আর লুকানো ট্যাবে থামিয়ে রাখে। কিন্তু হিসাব তখনো JavaScript-এ।
দ্বিতীয় বড় বদল CSS animation আর transition: লেখক শুধু ঘোষণা করেন কী চান (শুরু, শেষ, সময়), আর কীভাবে আঁকবেন তা ব্রাউজারের সিদ্ধান্ত। ঘোষণামূলক (declarative) হওয়ায় ব্রাউজার আগে থেকেই পুরো পরিকল্পনা জানে, তাই transform/opacity-কে compositor thread-এ পাঠাতে পারে — JavaScript-এর হিসাবে যা অসম্ভব। কিন্তু CSS-এর দুর্বলতা: মাঝপথে থামানো, উল্টো চালানো, গতি বদলানো, কয়েকটি মোশন মিলিয়ে চালানো কঠিন।
তৃতীয় ধাপ Web Animations API — দুই জগতের সেতু। এটি CSS-এর একই ইঞ্জিন (একই timing মডেল, compositor-সুবিধা) JavaScript থেকে ব্যবহারের সুযোগ দেয়, সাথে pause(), reverse(), playbackRate, finished promise. GSAP বা Motion-এর মতো জনপ্রিয় লাইব্রেরি আজও আছে — জটিল সময়রেখা, SVG path ধরে চলা, পদার্থবিদ্যার মতো spring — কিন্তু সাধারণ কাজে আর বাড়তি লাইব্রেরি লাগে না।
মূল উপলব্ধি: ওয়েব প্ল্যাটফর্মের বিবর্তনের একটি ধারা: প্রথমে লাইব্রেরি সমস্যা সমাধান করে, তারপর সবচেয়ে কাজের ধারণাগুলো ব্রাউজারের ভেতরে মানদণ্ড হয়ে আসে — দ্রুততর, সবার জন্য, বিনা খরচে। jQuery-র selector থেকে querySelectorAll, এখানেও তাই।
প্র ০৪prefers-reduced-motion কেন এলো, আর “মোশন বন্ধ” মানেই কি সঠিক উত্তর?
স্মার্টফোনে বড় জুম, প্যারালাক্স ব্যাকগ্রাউন্ড আর পর্দা-পাল্টানোর নাটকীয় মোশন জনপ্রিয় হওয়ার পর অনেক ব্যবহারকারী জানান যে ফোন ব্যবহার করলেই মাথা ঘোরে, বমিভাব হয়। এদের বড় অংশের vestibular সমস্যা আছে — চোখ যে গতি দেখে আর কানের ভেতরের ভারসাম্য-ইন্দ্রিয় যা অনুভব করে, দুটি না মিললে মস্তিষ্ক বিভ্রান্ত হয়। চলন্ত বাসে মোবাইল পড়লে যে অস্বস্তি হয়, তার তীব্র রূপ। এর জবাবে অপারেটিং সিস্টেমগুলো “Reduce Motion” সেটিং আনে।
কিন্তু সেটিং শুধু অপারেটিং সিস্টেমের নিজের মোশন কমাত; ওয়েবসাইট জানতই না। ২০১৭ সালে Safari প্রথম prefers-reduced-motion media feature চালু করে, পরে বাকি ব্রাউজারগুলো যোগ দেয়, এখন এটি Baseline Widely available. Media Queries Level 5-এ এটি একটি “user preference” feature — prefers-color-scheme-এর মতোই।
এবার দ্বিতীয় প্রশ্ন। “Reduce” শব্দটি ইচ্ছাকৃত — “remove” নয়। সব মোশন একভাবে ক্ষতিকর নয়: বড় এলাকার স্থান-বদল, জুম, ঘূর্ণন, প্যারালাক্স সবচেয়ে বেশি সমস্যা করে; রঙ বা স্বচ্ছতার মৃদু পরিবর্তন সাধারণত সহনীয়। আবার কিছু মোশন তথ্য দেয় — কার্ড কোথা থেকে এলো, কাজ চলছে কি না। সব বন্ধ করলে সেই তথ্যও হারায়। তাই ভালো নকশা প্রতিটি মোশনের জন্য ভাবে: এটি কী বোঝাচ্ছে? স্থির বা মৃদু বিকল্পে একই কথা বলা যায় কি? Spinner ধীর হতে পারে, স্লাইড ফেড হতে পারে, প্যারালাক্স পুরো বন্ধ হতে পারে।
আরেকটি সূক্ষ্ম কথা: অনেকে সেটিংটির কথা জানেনই না। তাই কিছু নকশা-দল “no-preference হলে তবেই মোশন” ধরন (মোশন-বিহীন ভিত্তি) পছন্দ করে, আর পাতায় নিজস্ব “মোশন কমান” বোতামও রাখে।
মূল উপলব্ধি: অ্যাক্সেসিবিলিটি কোনো চেকবক্স নয় যে “বন্ধ করে দিলাম, শেষ।” এটি প্রতিটি নকশা-সিদ্ধান্তের উদ্দেশ্য বুঝে সবার জন্য সেই উদ্দেশ্য পূরণ করা।
প্র ০৫WCAG কেন “সেকেন্ডে ৩ বার” — এই নির্দিষ্ট সংখ্যাটি কোথা থেকে এলো?
সংখ্যাটির পেছনে আছে একটি বাস্তব দুর্ঘটনা আর কয়েক দশকের চিকিৎসা-গবেষণা। ১৯৯৭ সালের ডিসেম্বরে জাপানে Pokémon কার্টুনের একটি পর্ব সম্প্রচারের সময় একটি দৃশ্যে লাল আর নীল রঙ কয়েক সেকেন্ড ধরে দ্রুত পাল্টাতে থাকে। সেদিন কয়েকশো দর্শক — বেশিরভাগ শিশু — খিঁচুনি, মাথাব্যথা, বমিভাব নিয়ে হাসপাতালে যায়। ঘটনাটি বিশ্বজুড়ে আলোড়ন তোলে।
এর মূলে আছে photosensitive epilepsy — কিছু মানুষের মস্তিষ্ক নির্দিষ্ট হারে ঝলকানো আলোয় অস্বাভাবিক বৈদ্যুতিক প্রতিক্রিয়া দেখায়। গবেষণায় দেখা যায় সাধারণত সেকেন্ডে প্রায় ৩ থেকে ৩০ বারের ঝলকানি সবচেয়ে ঝুঁকির, আর গাঢ় লাল রঙের ঝলকানি বিশেষ বিপজ্জনক। এর আগে-পরে যুক্তরাজ্যসহ বিভিন্ন দেশের টিভি-সম্প্রচার নিয়ন্ত্রকেরা ঝলকানির সীমা বেঁধে দেয়, আর অনুষ্ঠান প্রচারের আগে স্বয়ংক্রিয় যন্ত্রে পরীক্ষার প্রচলন হয়।
WCAG সেই সম্প্রচার-নির্দেশিকা থেকেই নিয়ম নেয়। 2.3.1-এ সরল নিয়ম: কোনো এক সেকেন্ডে ৩ বারের বেশি ঝলকানি নয়; অথবা ঝলকানির এলাকা এত ছোট আর উজ্জ্বলতার পার্থক্য এত কম যে “general flash” ও “red flash” সীমার নিচে থাকে। এটি Level A — সবচেয়ে মৌলিক স্তর, কারণ এখানে ক্ষতি অস্বস্তি নয়, শারীরিক। একটি কঠোরতর AAA নিয়মও আছে (2.3.2), যেখানে এলাকার ব্যতিক্রমও নেই।
CSS-এ এটি ঘটানো ভয়ংকর রকম সহজ: animation: flash 0.1s infinite সেকেন্ডে ১০ বার জ্বলবে। “সেল” বা “জরুরি নোটিশ” বোঝাতে কেউ পুরো ব্যানার লাল-সাদা ঝলকালে সেটিই বিপদ। নিরাপদ বিকল্প: ধীর স্পন্দন (২ সেকেন্ডে একবার), রঙের বদলে আকারে মৃদু পরিবর্তন, অথবা একবার মনোযোগ টেনে থেমে যাওয়া।
মূল উপলব্ধি: নকশার কিছু নিয়ম রুচির প্রশ্ন নয়, নিরাপত্তার প্রশ্ন। ভালো প্রকৌশলী সংখ্যাটির পেছনের কারণ জানেন — তাই নতুন পরিস্থিতিতেও (ভিডিও, গেম, VR) ঠিক সিদ্ধান্ত নিতে পারেন।
অনুশীলন
বেশিরভাগ কাজ দেখা আর ভাবার — একটিতে শুধু কয়েক লাইন CSS লিখতে হবে।
-
DevTools-এ animation ধরুন: যেকোনো বড় ওয়েবসাইট (খবর বা অনলাইন দোকান) খুলে DevTools-এ Ctrl+Shift+P চেপে “Animations” লিখে প্যানেলটি খুলুন, তারপর পাতা রিলোড করুন। কয়টি animation পেলেন? গতি ১০% করে দেখুন — কোনটি কোন property নাড়াচ্ছে?
Animations প্যানেল পাতার সব CSS animation, transition আর Web Animations API-র animation দলবেঁধে সময়রেখায় দেখায়। একটিতে ক্লিক করলে Elements প্যানেলে element-টি আর তার keyframe দেখা যায়। ১০% গতিতে প্রায়ই চোখে পড়ে এমন জিনিস, যা আসল গতিতে বোঝা যায় না — যেমন একটি মেনু
heightবাtopanimate করছে (দামি), কিংবা একটি carousel অবিরাম চলছে কিন্তু থামানোর বোতাম নেই (WCAG 2.2.2). এমন কিছু পেলে Performance প্যানেলে রেকর্ড করে Layout ব্লকগুলোও মিলিয়ে দেখুন। -
Shorthand পড়ুন: নিচের তিনটি লাইনে duration, delay আর iteration count কত? (ক)
animation: fade 300ms 1s(খ)animation: 2s spin 3 linear(গ)animation: bounce .5s ease-in -.25s infinite alternate(ক) duration ৩০০ মিলিসেকেন্ড, delay ১ সেকেন্ড, একবার — প্রথম সময় duration, দ্বিতীয়টি delay.
(খ) duration ২ সেকেন্ড, delay ০, ৩ বার — নাম পরে লিখলেও চলে;
3-এ একক নেই, তাই এটি iteration count.(গ) duration ০.৫ সেকেন্ড, delay −০.২৫ সেকেন্ড (অর্থাৎ শুরু থেকেই মাঝপথে —
alternate-এর প্রথম চক্রের অর্ধেক), অসীমবার। ঋণাত্মক delay হলো দ্বিতীয় সময়ের মান। -
Reduced motion যোগ করুন: একটি কার্ড পাতা খুললে
animation: rise .8s ease-out bothদিয়ে নিচ থেকে উঠে আসে। এমন@mediaনিয়ম লিখুন যাতে reduced motion-এ কার্ড সরে না এসে শুধু ফুটে ওঠে, আর শেষের অবস্থা দুই ক্ষেত্রেই এক থাকে।আলাদা একটি
fadekeyframe বানিয়ে শুধুanimation-nameবদলালেই হয় — সময়, fill-mode সব আগের মতো থাকে, আরanimationend-ও ঘটে। নিজের কম্পিউটারে “অ্যানিমেশন কমান” চালু করে পার্থক্য দেখুন:.card { width: 240px; padding: 16px; border-radius: 10px; background: #eff6ff; border: 1px solid #93c5fd; font: 16px sans-serif; animation: rise .8s ease-out both; } @keyframes rise { from { opacity: 0; transform: translateY(30px); } } @keyframes fade { from { opacity: 0; } } @media (prefers-reduced-motion: reduce) { .card { animation-name: fade; } }সাধারণ অবস্থায় কার্ডটি ৩০ পিক্সেল নিচ থেকে উঠে আসতে আসতে দৃশ্যমান হয়; reduced motion-এ একই জায়গায় শুধু ফুটে ওঠে। দুই keyframe-এই
toনেই, তাই শেষে কার্ড নিজের CSS-এ থাকে — শেষের অবস্থা দুই ক্ষেত্রেই এক।bothরাখা হয়েছে, যাতে পরে কেউ delay যোগ করলেও শুরুতে কার্ড এক ঝলক দেখা দিয়ে আবার অদৃশ্য না হয়। -
WCAG বিচারক: কোনগুলোর জন্য 2.2.2 অনুযায়ী থামানোর উপায় লাগবে? (ক) পেমেন্টের সময় ২–৩ সেকেন্ডের spinner (খ) হোমপেজের অবিরাম খবরের ticker (গ) ১০ সেকেন্ড পরপর নিজে বদলানো ব্যানার স্লাইডার (ঘ) নতুন বার্তার badge-এ অসীম স্পন্দন।
(ক) লাগবে না — ৫ সেকেন্ডের কম, আর পেমেন্টের সময় অন্য কিছু করার নেই; তবে কাজ শেষে spinner সরাতেই হবে।
(খ) লাগবে — নিজে শুরু হয়, ৫ সেকেন্ডের বেশি চলে, পাতার অন্য বিষয়ের পাশে থাকে। থামানোর বোতাম দিন।
(গ) লাগবে — স্বয়ংক্রিয়ভাবে বদলানো তথ্যও এই নিয়মের আওতায়; pause বোতাম রাখুন, আর ফোকাস বা hover-এ থামান।
(ঘ) লাগবে — অসীম স্পন্দন মানে ৫ সেকেন্ডের বেশি চলমান মোশন। সহজ সমাধান: পুনরাবৃত্তি সীমিত করুন (যেমন
animation-iteration-count: 3), যাতে কয়েক সেকেন্ড পর নিজেই থামে।
নিজেকে যাচাই করুন
ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।
ক্রমএকটি animation-এর প্রতি ফ্রেমে ব্রাউজারের কাজগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)
- ⏱️ নতুন ফ্রেমের সময় এলো — keyframe থেকে এই মুহূর্তের মান হিসাব
- 🎨 Style — কোন element-এ কোন CSS মান খাটে
- 📐 Layout — প্রতিটি বাক্সের মাপ আর জায়গা
- 🖌️ Paint — পিক্সেলে রং বসানো
- 🧩 Composite — স্তরগুলো জুড়ে পর্দায় পাঠানো
CSS transition আর keyframe animation-এর মূল পার্থক্য কোনটি?
- transition শুধু রং বদলায়, animation শুধু transform
- transition চলে মান বদলালে; animation নিজে থেকে চলে, বহু keyframe আর পুনরাবৃত্তিসহ
- transition GPU-তে চলে, animation সবসময় CPU-তে
- animation-এর জন্য JavaScript লাগে, transition-এর লাগে না
animation: slide 2s 1s ease-in; — এখানে 1s কী বোঝায়?
- animation-এর মোট সময়
- কতবার চলবে
- শুরুর আগে অপেক্ষার সময় (delay)
- প্রতি পুনরাবৃত্তির মাঝের বিরতি
একটি কার্ড fade-in animation শেষে আবার অদৃশ্য হয়ে যাচ্ছে, কারণ তার নিজের CSS-এ opacity: 0. শেষের দৃশ্যমান অবস্থা ধরে রাখতে কোনটি দরকার?
- animation-fill-mode: forwards
- animation-fill-mode: backwards
- animation-direction: alternate
- animation-iteration-count: infinite
২ সেকেন্ডের একটি animation-এ animation-delay: -0.5s দিলে কী হয়?
- মানটি অবৈধ, তাই delay শূন্য ধরা হয়
- animation উল্টো দিকে চলে
- ০.৫ সেকেন্ড অপেক্ষা করে শুরু হয়
- অপেক্ষা ছাড়াই শুরু হয়, তবে একেবারে ২৫% পথ থেকে
একটি বাক্সকে ডানে সরানোর animation সবচেয়ে মসৃণ ও দক্ষ হবে কোন property বদলালে?
- left
- transform
- margin-left
- width
WCAG 2.2.2 (Pause, Stop, Hide) অনুযায়ী কোন ক্ষেত্রে থামানোর উপায় বাধ্যতামূলক?
- বোতামে hover করলে ০.২ সেকেন্ডের রং বদল
- পাতা খোলার সময় একবার ০.৫ সেকেন্ডে ভেসে ওঠা কার্ড
- হোমপেজে অবিরাম চলতে থাকা খবরের ticker
- সেকেন্ডে ১০ বার ঝলকানো ব্যানার — থামানোর বোতাম দিলেই চলবে
শব্দকোষ
লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:
মনে রাখুন
- Transition চলে অবস্থা বদলালে; animation
@keyframesধরে নিজে থেকে চলে — বহু ধাপ, বহুবার। - Shorthand-এ প্রথম সময় duration, দ্বিতীয় delay; আর যা লেখেননি তা ডিফল্টে ফেরে।
fill-mode: forwards শেষটা রাখে, backwards অপেক্ষায় শুরুটা দেখায়, both দুটোই।- ঋণাত্মক delay মাঝপথ থেকে শুরু করায়; keyframe-এর ভেতরের timing শুধু সেই অংশে খাটে;
steps()লাফে চলে। - নড়ান
transform/opacity; প্রতিটি animation-এ reduced motion, ৫ সেকেন্ডের বেশি হলে থামানোর বোতাম, আর কখনো দ্রুত ঝলকানি নয়।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পর্ব ৪০ · Transform — সরানো, ঘোরানো, বড়-ছোট আর 3Dপরবর্তী পর্বanimation-এর সবচেয়ে কাজের সঙ্গী, গভীরভাবে।
- পর্ব ৩৮ · Transition — মসৃণ পরিবর্তন আর timing functionআগের ভিত্তি
ease,cubic-bezier(),linear()আর অবস্থা-বদলের মোশন। - HTML ও CSS · কোর্স হোম৫৯টি পর্বপুরো কোর্সের রোডম্যাপ — কোন পর্ব কখন।
- JavaScript · Eventsপাশের ট্র্যাক
animationend-এর মতো ঘটনা কীভাবে ধরা ও সামলানো হয়। - JavaScript · DOM-এর ভিত্তিপাশের ট্র্যাকJavaScript থেকে element খুঁজে class বদলানো — animation চালু-বন্ধের ভিত্তি।