আধুনিক ব্রাউজারগুলির জন্য তৈরি করা এবং 2003 এর মতো ধীরে ধীরে উন্নত করা
2003 সালের মার্চ মাসে, নিক ফিঙ্ক এবং স্টিভ চ্যাম্পিয়ন প্রগতিশীল বর্ধনের ধারণা দিয়ে ওয়েব ডিজাইন বিশ্বকে হতবাক করে দিয়েছিলেন, ওয়েব ডিজাইনের একটি কৌশল যা প্রথমে মূল ওয়েব পৃষ্ঠার বিষয়বস্তু লোড করার উপর জোর দেয়, এবং এটি ধীরে ধীরে উপস্থাপনার আরও সূক্ষ্ম এবং প্রযুক্তিগতভাবে কঠোর স্তর যোগ করে এবং বিষয়বস্তুর উপরে বৈশিষ্ট্য। 2003 সালে, প্রগতিশীল বর্ধিতকরণ ছিল—সে সময়ে—আধুনিক CSS বৈশিষ্ট্য, অবাধ জাভাস্ক্রিপ্ট এবং এমনকি স্কেলেবল ভেক্টর গ্রাফিক্স ব্যবহার করা। 2020 এবং তার পরেও প্রগতিশীল উন্নতি হল আধুনিক ব্রাউজার ক্ষমতা ব্যবহার করা।
আধুনিক জাভাস্ক্রিপ্ট
জাভাস্ক্রিপ্টের কথা বলতে গেলে, সর্বশেষ কোর ES 2015 জাভাস্ক্রিপ্ট বৈশিষ্ট্যগুলির জন্য ব্রাউজার সমর্থন পরিস্থিতি দুর্দান্ত। নতুন স্ট্যান্ডার্ডের মধ্যে রয়েছে প্রতিশ্রুতি, মডিউল, ক্লাস, টেমপ্লেট লিটারেল, অ্যারো ফাংশন, let
এবং const
, ডিফল্ট প্যারামিটার, জেনারেটর, ধ্বংসকারী অ্যাসাইনমেন্ট, বিশ্রাম এবং স্প্রেড, Map
/ Set
, WeakMap
/ WeakSet
এবং আরও অনেক কিছু। সব সমর্থিত হয় .
Async ফাংশন, একটি ES 2017 বৈশিষ্ট্য এবং আমার ব্যক্তিগত পছন্দের একটি, সমস্ত প্রধান ব্রাউজারে ব্যবহার করা যেতে পারে । async
এবং await
কীওয়ার্ডগুলি অ্যাসিঙ্ক্রোনাস, প্রতিশ্রুতি-ভিত্তিক আচরণকে একটি পরিষ্কার শৈলীতে লিখতে সক্ষম করে, প্রতিশ্রুতি চেইনগুলি স্পষ্টভাবে কনফিগার করার প্রয়োজন এড়িয়ে যায়।
এবং এমনকি অতি সাম্প্রতিক ES 2020 ভাষার সংযোজন যেমন ঐচ্ছিক চেইনিং এবং নালিশ কোলেসিং সত্যিই দ্রুত সমর্থনে পৌঁছেছে। আপনি নীচে একটি কোড নমুনা দেখতে পারেন. মূল জাভাস্ক্রিপ্ট বৈশিষ্ট্যগুলির ক্ষেত্রে, ঘাসটি আজকের তুলনায় খুব বেশি সবুজ হতে পারে না।
const adventurer = {
name: 'Alice',
cat: {
name: 'Dinah',
},
};
console.log(adventurer.dog?.name);
// Expected output: undefined
console.log(0 ?? 42);
// Expected output: 0
নমুনা অ্যাপ: ফুগু শুভেচ্ছা
এই নিবন্ধটির জন্য, আমি একটি সাধারণ PWA এর সাথে কাজ করি, যার নাম Fugu Greetings ( GitHub )। এই অ্যাপটির নাম হল প্রোজেক্ট ফুগু 🐡 এর একটি টিপ, যা ওয়েবকে অ্যান্ড্রয়েড/আইওএস/ডেস্কটপ অ্যাপ্লিকেশনগুলির সমস্ত ক্ষমতা দেওয়ার একটি প্রচেষ্টা৷ আপনি এর ল্যান্ডিং পৃষ্ঠায় প্রকল্প সম্পর্কে আরও পড়তে পারেন।
ফুগু গ্রিটিংস একটি ড্রয়িং অ্যাপ যা আপনাকে ভার্চুয়াল গ্রিটিং কার্ড তৈরি করতে এবং আপনার প্রিয়জনকে পাঠাতে দেয়। এটি PWA এর মূল ধারণার উদাহরণ দেয়। এটি নির্ভরযোগ্য এবং সম্পূর্ণ অফলাইন সক্ষম, তাই আপনার নেটওয়ার্ক না থাকলেও আপনি এটি ব্যবহার করতে পারেন৷ এটি একটি ডিভাইসের হোম স্ক্রিনেও ইনস্টলযোগ্য এবং একটি স্বতন্ত্র অ্যাপ্লিকেশন হিসাবে অপারেটিং সিস্টেমের সাথে নির্বিঘ্নে সংহত করে৷
প্রগতিশীল বর্ধন
এই পথের বাইরে, এটি প্রগতিশীল বর্ধন সম্পর্কে কথা বলার সময়। MDN ওয়েব ডক্স গ্লোসারী ধারণাটিকে নিম্নরূপ সংজ্ঞায়িত করে :
প্রোগ্রেসিভ এনহান্সমেন্ট হল একটি ডিজাইনের দর্শন যা যতটা সম্ভব ব্যবহারকারীদের জন্য প্রয়োজনীয় বিষয়বস্তু এবং কার্যকারিতার একটি বেসলাইন প্রদান করে, এবং শুধুমাত্র সবচেয়ে আধুনিক ব্রাউজারগুলির ব্যবহারকারীদের জন্য সর্বোত্তম সম্ভাব্য অভিজ্ঞতা প্রদান করে যা সমস্ত প্রয়োজনীয় কোড চালাতে পারে।
বৈশিষ্ট্য সনাক্তকরণ সাধারণত ব্রাউজারগুলি আরও আধুনিক কার্যকারিতা পরিচালনা করতে পারে কিনা তা নির্ধারণ করতে ব্যবহৃত হয়, যখন জাভাস্ক্রিপ্টের সাথে অনুপস্থিত বৈশিষ্ট্যগুলি যোগ করতে প্রায়শই পলিফিল ব্যবহার করা হয়।
[…]
প্রোগ্রেসিভ এনহান্সমেন্ট হল একটি দরকারী কৌশল যা ওয়েব ডেভেলপারদেরকে একাধিক অজানা ব্যবহারকারী এজেন্টের উপর কাজ করার সময় সম্ভাব্য সর্বোত্তম ওয়েবসাইট তৈরিতে ফোকাস করতে দেয়। করুণাময় অবক্ষয় সম্পর্কিত, কিন্তু একই জিনিস নয় এবং প্রায়শই প্রগতিশীল বর্ধনের বিপরীত দিকে যেতে দেখা যায়। বাস্তবে, উভয় পদ্ধতিই বৈধ এবং প্রায়শই একে অপরের পরিপূরক হতে পারে।
MDN অবদানকারীরা
স্ক্র্যাচ থেকে প্রতিটি শুভেচ্ছা কার্ড শুরু করা সত্যিই কষ্টকর হতে পারে। তাহলে কেন এমন একটি বৈশিষ্ট্য নেই যা ব্যবহারকারীদের একটি চিত্র আমদানি করতে এবং সেখান থেকে শুরু করতে দেয়? একটি ঐতিহ্যগত পদ্ধতির সাথে, আপনি এটি ঘটতে একটি <input type=file>
উপাদান ব্যবহার করেছেন। প্রথমত, আপনি উপাদানটি তৈরি করবেন, এটির type
'file'
এ সেট করুন এবং accept
সম্পত্তিতে MIME প্রকারগুলি যোগ করুন এবং তারপর প্রোগ্রাম্যাটিকভাবে এটিতে "ক্লিক করুন" এবং পরিবর্তনগুলি শুনুন। আপনি যখন একটি ছবি নির্বাচন করেন, এটি সরাসরি ক্যানভাসে আমদানি করা হয়।
const importImage = async () => {
return new Promise((resolve) => {
const input = document.createElement('input');
input.type = 'file';
input.accept = 'image/*';
input.addEventListener('change', () => {
resolve(input.files[0]);
});
input.click();
});
};
যখন একটি আমদানি বৈশিষ্ট্য থাকে, তখন সম্ভবত একটি রপ্তানি বৈশিষ্ট্য থাকা উচিত যাতে ব্যবহারকারীরা স্থানীয়ভাবে তাদের অভিবাদন কার্ড সংরক্ষণ করতে পারে। ফাইল সংরক্ষণের ঐতিহ্যগত উপায় হল একটি download
অ্যাট্রিবিউট এবং একটি ব্লব URL এর href
হিসাবে একটি অ্যাঙ্কর লিঙ্ক তৈরি করা। আপনি ডাউনলোডটি ট্রিগার করার জন্য এটিকে প্রোগ্রাম্যাটিকভাবে "ক্লিক করুন" এবং মেমরি লিক প্রতিরোধ করতে, আশা করি ব্লব অবজেক্ট URL প্রত্যাহার করতে ভুলবেন না।
const exportImage = async (blob) => {
const a = document.createElement('a');
a.download = 'fugu-greeting.png';
a.href = URL.createObjectURL(blob);
a.addEventListener('click', (e) => {
setTimeout(() => URL.revokeObjectURL(a.href), 30 * 1000);
});
a.click();
};
কিন্তু এক মিনিট অপেক্ষা করুন। মানসিকভাবে, আপনি একটি অভিবাদন কার্ড "ডাউনলোড" করেননি, আপনি এটি "সংরক্ষিত" করেছেন। আপনাকে একটি "সংরক্ষণ করুন" ডায়ালগ দেখানোর পরিবর্তে যা আপনাকে ফাইলটি কোথায় রাখতে হবে তা চয়ন করতে দেয়, ব্রাউজারটি ব্যবহারকারীর ইন্টারঅ্যাকশন ছাড়াই সরাসরি অভিবাদন কার্ডটি ডাউনলোড করেছে এবং এটি সরাসরি আপনার ডাউনলোড ফোল্ডারে রেখেছে৷ এই মহান না.
যদি একটি ভাল উপায় ছিল? যদি আপনি শুধুমাত্র একটি স্থানীয় ফাইল খুলতে পারেন, এটি সম্পাদনা করতে পারেন, এবং তারপরে পরিবর্তনগুলি সংরক্ষণ করতে পারেন, হয় একটি নতুন ফাইলে, অথবা আপনি যেটি শুরুতে খুলেছিলেন সেটিতে ফিরে যেতে? সেখানে দেখা যাচ্ছে. ফাইল সিস্টেম অ্যাক্সেস API আপনাকে ফাইল এবং ডিরেক্টরি খুলতে এবং তৈরি করতে দেয়, সেইসাথে সেগুলিকে সংশোধন এবং সংরক্ষণ করতে দেয়।
তাই আমি কিভাবে একটি API বৈশিষ্ট্য সনাক্ত করতে পারি? ফাইল সিস্টেম অ্যাক্সেস API একটি নতুন পদ্ধতি প্রকাশ করে window.chooseFileSystemEntries()
। ফলস্বরূপ, এই পদ্ধতিটি উপলব্ধ কিনা তার উপর নির্ভর করে আমাকে শর্তসাপেক্ষে বিভিন্ন আমদানি এবং রপ্তানি মডিউল লোড করতে হবে। আমি নীচে দেখিয়েছি কিভাবে এটি করতে হয়.
const loadImportAndExport = () => {
if ('chooseFileSystemEntries' in window) {
Promise.all([
import('./import_image.mjs'),
import('./export_image.mjs'),
]);
} else {
Promise.all([
import('./import_image_legacy.mjs'),
import('./export_image_legacy.mjs'),
]);
}
};
কিন্তু আমি ফাইল সিস্টেম অ্যাক্সেস API বিবরণে ডুব দেওয়ার আগে, আমাকে এখানে প্রগতিশীল বর্ধিতকরণ প্যাটার্নটি দ্রুত হাইলাইট করতে দিন। বর্তমানে ফাইল সিস্টেম অ্যাক্সেস API সমর্থন করে না এমন ব্রাউজারগুলিতে, আমি লিগ্যাসি স্ক্রিপ্টগুলি লোড করি। আপনি নিচে Firefox এবং Safari এর নেটওয়ার্ক ট্যাব দেখতে পারেন।
যাইহোক, Chrome এ, একটি ব্রাউজার যা API সমর্থন করে, শুধুমাত্র নতুন স্ক্রিপ্ট লোড করা হয়। ডাইনামিক import()
এর জন্য এটি সুন্দরভাবে সম্ভব হয়েছে, যা সমস্ত আধুনিক ব্রাউজার সমর্থন করে । আমি আগেই বলেছি, ঘাস আজকাল বেশ সবুজ।
ফাইল সিস্টেম অ্যাক্সেস API
তাই এখন যেহেতু আমি এটিকে সম্বোধন করেছি, এটি ফাইল সিস্টেম অ্যাক্সেস API এর উপর ভিত্তি করে প্রকৃত বাস্তবায়নের দিকে নজর দেওয়ার সময়। একটি ইমেজ ইম্পোর্ট করার জন্য, আমি window.chooseFileSystemEntries()
কল করি এবং এটিকে একটি accepts
সম্পত্তি পাস করি যেখানে আমি বলি আমি ইমেজ ফাইল চাই। উভয় ফাইল এক্সটেনশনের পাশাপাশি MIME প্রকারগুলিই সমর্থিত। এর ফলে একটি ফাইল হ্যান্ডেল হয়, যেখান থেকে আমি getFile()
কল করে প্রকৃত ফাইল পেতে পারি।
const importImage = async () => {
try {
const handle = await window.chooseFileSystemEntries({
accepts: [
{
description: 'Image files',
mimeTypes: ['image/*'],
extensions: ['jpg', 'jpeg', 'png', 'webp', 'svg'],
},
],
});
return handle.getFile();
} catch (err) {
console.error(err.name, err.message);
}
};
একটি ইমেজ রপ্তানি করা প্রায় একই, কিন্তু এবার আমাকে chooseFileSystemEntries()
পদ্ধতিতে 'save-file'
এর একটি টাইপ প্যারামিটার পাস করতে হবে। এটি থেকে আমি একটি ফাইল সংরক্ষণ ডায়ালগ পাই। ফাইল খোলার সাথে, এটি প্রয়োজনীয় ছিল না যেহেতু 'open-file'
ডিফল্ট। আমি আগের মতই accepts
প্যারামিটার সেট করেছি, কিন্তু এই সময় শুধু পিএনজি ইমেজে সীমাবদ্ধ। আবার আমি একটি ফাইল হ্যান্ডেল ফিরে পাই, কিন্তু ফাইলটি পাওয়ার পরিবর্তে, এবার আমি createWritable()
কল করে একটি লিখনযোগ্য স্ট্রীম তৈরি করি। এর পরে, আমি ফাইলটিতে ব্লব লিখি, যা আমার শুভেচ্ছা কার্ডের ছবি। অবশেষে, আমি লিখনযোগ্য প্রবাহ বন্ধ.
সবকিছু সর্বদা ব্যর্থ হতে পারে: ডিস্কটি স্থানের বাইরে হতে পারে, লিখতে বা পড়ার ত্রুটি হতে পারে, অথবা সম্ভবত ব্যবহারকারী ফাইল ডায়ালগটি বাতিল করে। এই কারণেই আমি সবসময় try...catch
স্টেটমেন্ট।
const exportImage = async (blob) => {
try {
const handle = await window.chooseFileSystemEntries({
type: 'save-file',
accepts: [
{
description: 'Image file',
extensions: ['png'],
mimeTypes: ['image/png'],
},
],
});
const writable = await handle.createWritable();
await writable.write(blob);
await writable.close();
} catch (err) {
console.error(err.name, err.message);
}
};
ফাইল সিস্টেম অ্যাক্সেস API এর সাথে প্রগতিশীল বর্ধন ব্যবহার করে, আমি আগের মতো একটি ফাইল খুলতে পারি। আমদানি করা ফাইলটি সরাসরি ক্যানভাসে আঁকা হয়। আমি আমার সম্পাদনা করতে পারি এবং অবশেষে সেগুলিকে একটি বাস্তব সংরক্ষণ ডায়ালগ বক্স দিয়ে সংরক্ষণ করতে পারি যেখানে আমি ফাইলটির নাম এবং স্টোরেজ অবস্থান চয়ন করতে পারি। এখন ফাইলটি অনন্তকালের জন্য সংরক্ষণ করার জন্য প্রস্তুত।
ওয়েব শেয়ার এবং ওয়েব শেয়ার টার্গেট API
অনন্তকালের জন্য সঞ্চয় করা ছাড়াও, আমি আসলে আমার শুভেচ্ছা কার্ড ভাগ করতে চাই। এটি এমন কিছু যা ওয়েব শেয়ার এপিআই এবং ওয়েব শেয়ার টার্গেট এপিআই আমাকে করতে দেয়। মোবাইল, এবং অতি সম্প্রতি ডেস্কটপ অপারেটিং সিস্টেম বিল্ট-ইন শেয়ারিং মেকানিজম অর্জন করেছে। উদাহরণস্বরূপ, ম্যাকওএস-এ ডেস্কটপ সাফারির শেয়ার শীটটি আমার ব্লগের একটি নিবন্ধ থেকে ট্রিগার করা হয়েছে। আপনি যখন নিবন্ধ শেয়ার করুন বোতামে ক্লিক করেন, আপনি নিবন্ধের একটি লিঙ্ক বন্ধুর সাথে ভাগ করতে পারেন, উদাহরণস্বরূপ, macOS বার্তা অ্যাপের মাধ্যমে৷
এটি ঘটতে কোডটি বেশ সহজবোধ্য। আমি navigator.share()
কল করি এবং এটিকে একটি অবজেক্টে একটি ঐচ্ছিক title
, text
এবং url
পাস করি৷ কিন্তু আমি যদি একটি ছবি সংযুক্ত করতে চাই? ওয়েব শেয়ার API-এর লেভেল 1 এটি এখনও সমর্থন করে না। ভাল খবর হল ওয়েব শেয়ার লেভেল 2 ফাইল শেয়ারিং ক্ষমতা যুক্ত করেছে।
try {
await navigator.share({
title: 'Check out this article:',
text: `"${document.title}" by @tomayac:`,
url: document.querySelector('link[rel=canonical]').href,
});
} catch (err) {
console.warn(err.name, err.message);
}
ফুগু গ্রিটিং কার্ড অ্যাপ্লিকেশন দিয়ে কীভাবে এই কাজটি করা যায় তা আমি আপনাকে দেখাই। প্রথমে, আমাকে একটি ব্লব সমন্বিত একটি files
অ্যারে সহ একটি data
অবজেক্ট প্রস্তুত করতে হবে, এবং তারপর একটি title
এবং একটি text
। এর পরে, একটি সর্বোত্তম অনুশীলন হিসাবে, আমি নতুন navigator.canShare()
পদ্ধতিটি ব্যবহার করি যা এটির নাম অনুসারে কাজ করে: এটি আমাকে বলে যে আমি যে data
অবজেক্টটি ভাগ করার চেষ্টা করছি সেটি ব্রাউজার দ্বারা প্রযুক্তিগতভাবে ভাগ করা যায় কিনা। যদি navigator.canShare()
আমাকে বলে যে ডেটা ভাগ করা যেতে পারে, আমি আগের মতো navigator.share()
কল করতে প্রস্তুত। কারণ সবকিছু ব্যর্থ হতে পারে, আমি আবার try...catch
ব্লক।
const share = async (title, text, blob) => {
const data = {
files: [
new File([blob], 'fugu-greeting.png', {
type: blob.type,
}),
],
title: title,
text: text,
};
try {
if (!(navigator.canShare(data))) {
throw new Error("Can't share data.", data);
}
await navigator.share(data);
} catch (err) {
console.error(err.name, err.message);
}
};
আগের মতো, আমি প্রগতিশীল বর্ধন ব্যবহার করি। যদি navigator
অবজেক্টে 'share'
এবং 'canShare'
উভয়ই বিদ্যমান থাকে, তবেই আমি এগিয়ে যাই এবং dynamic import()
মাধ্যমে share.mjs
লোড করি। মোবাইল সাফারির মতো ব্রাউজারে যা শুধুমাত্র দুটি শর্তের একটি পূরণ করে, আমি কার্যকারিতা লোড করি না।
const loadShare = () => {
if ('share' in navigator && 'canShare' in navigator) {
import('./share.mjs');
}
};
ফুগু গ্রিটিংস-এ, আমি যদি অ্যান্ড্রয়েডে ক্রোমের মতো একটি সমর্থনকারী ব্রাউজারে শেয়ার বোতামে ট্যাপ করি, অন্তর্নির্মিত শেয়ার শীটটি খোলে। আমি, উদাহরণস্বরূপ, Gmail চয়ন করতে পারি, এবং ইমেল কম্পোজার উইজেটটি সংযুক্ত চিত্রের সাথে পপ আপ হয়৷
যোগাযোগ পিকার API
এর পরে, আমি পরিচিতি সম্পর্কে কথা বলতে চাই, যার অর্থ একটি ডিভাইসের ঠিকানা বই বা পরিচিতি ম্যানেজার অ্যাপ৷ আপনি যখন একটি অভিবাদন কার্ড লেখেন, তখন সঠিকভাবে কারো নাম লেখা সবসময় সহজ নাও হতে পারে। উদাহরণস্বরূপ, আমার একজন বন্ধু সের্গেই আছে যে তার নাম সিরিলিক অক্ষরে বানান করা পছন্দ করে। আমি একটি জার্মান QWERTZ কীবোর্ড ব্যবহার করছি এবং তাদের নাম কীভাবে টাইপ করব তা আমার জানা নেই। এটি একটি সমস্যা যা কন্টাক্ট পিকার এপিআই সমাধান করতে পারে। যেহেতু আমার বন্ধু আমার ফোনের পরিচিতি অ্যাপে সংরক্ষিত আছে, পরিচিতি পিকার API-এর মাধ্যমে, আমি ওয়েব থেকে আমার পরিচিতিতে ট্যাপ করতে পারি।
প্রথমে, আমি যে বৈশিষ্ট্যগুলি অ্যাক্সেস করতে চাই তার তালিকা উল্লেখ করতে হবে। এই ক্ষেত্রে, আমি শুধুমাত্র নাম চাই, কিন্তু অন্যান্য ব্যবহারের ক্ষেত্রে আমি টেলিফোন নম্বর, ইমেল, অবতার আইকন, বা প্রকৃত ঠিকানাগুলিতে আগ্রহী হতে পারি৷ এর পরে, আমি একটি options
অবজেক্ট কনফিগার করি এবং multiple
সেট করি true
, যাতে আমি একাধিক এন্ট্রি নির্বাচন করতে পারি। অবশেষে, আমি navigator.contacts.select()
কল করতে পারি, যা ব্যবহারকারী-নির্বাচিত পরিচিতির জন্য পছন্দসই বৈশিষ্ট্য প্রদান করে।
const getContacts = async () => {
const properties = ['name'];
const options = { multiple: true };
try {
return await navigator.contacts.select(properties, options);
} catch (err) {
console.error(err.name, err.message);
}
};
এবং এখন পর্যন্ত আপনি সম্ভবত প্যাটার্নটি শিখেছেন: আমি তখনই ফাইলটি লোড করি যখন API আসলে সমর্থিত হয়।
if ('contacts' in navigator) {
import('./contacts.mjs');
}
ফুগু গ্রিটিং-এ, যখন আমি পরিচিতি বোতামটি আলতো চাপি এবং আমার দুটি সেরা বন্ধু নির্বাচন করি, Сергей Михайлович BRIN এবং劳伦斯·爱德华·"拉里"·佩奇, আপনি দেখতে পাবেন কিভাবে পরিচিতি বাছাইকারী শুধুমাত্র তাদের নাম দেখানোর মধ্যেই সীমাবদ্ধ নয়, তাদের ইমেল ঠিকানা, বা তাদের ফোনের মত অন্যান্য তথ্য সংখ্যা তাদের নাম তখন আমার শুভেচ্ছা কার্ডে টানা হয়।
অ্যাসিঙ্ক্রোনাস ক্লিপবোর্ড API
পরবর্তীতে কপি এবং পেস্ট করা হয়। সফ্টওয়্যার বিকাশকারী হিসাবে আমাদের প্রিয় অপারেশনগুলির মধ্যে একটি হল কপি এবং পেস্ট। একটি অভিবাদন কার্ড লেখক হিসাবে, মাঝে মাঝে, আমি একই কাজ করতে চাই। আমি হয়ত একটি অভিবাদন কার্ডে একটি ছবি পেস্ট করতে চাই যেটিতে আমি কাজ করছি, অথবা আমার অভিবাদন কার্ডটি অনুলিপি করতে চাই যাতে আমি অন্য কোথাও থেকে এটি সম্পাদনা চালিয়ে যেতে পারি৷ Async ক্লিপবোর্ড API , টেক্সট এবং ইমেজ উভয় সমর্থন করে। ফুগু গ্রিটিংস অ্যাপে আমি কীভাবে কপি এবং পেস্ট সমর্থন যোগ করেছি সে বিষয়ে আমি আপনাকে পথ দেখাই।
সিস্টেমের ক্লিপবোর্ডে কিছু অনুলিপি করার জন্য, আমাকে এটিতে লিখতে হবে। navigator.clipboard.write()
পদ্ধতি একটি প্যারামিটার হিসাবে ক্লিপবোর্ড আইটেমগুলির একটি অ্যারে নেয়। প্রতিটি ক্লিপবোর্ড আইটেম মূলত একটি অবজেক্ট যার একটি মান হিসাবে একটি ব্লব এবং কী হিসাবে ব্লবের ধরন৷
const copy = async (blob) => {
try {
await navigator.clipboard.write([
new ClipboardItem({
[blob.type]: blob,
}),
]);
} catch (err) {
console.error(err.name, err.message);
}
};
পেস্ট করার জন্য, আমাকে ক্লিপবোর্ড আইটেমগুলি লুপ করতে হবে যা আমি navigator.clipboard.read()
কল করে পাই। এর কারণ হল যে একাধিক ক্লিপবোর্ড আইটেম বিভিন্ন উপস্থাপনায় ক্লিপবোর্ডে থাকতে পারে। প্রতিটি ক্লিপবোর্ড আইটেমের একটি types
ক্ষেত্র রয়েছে যা আমাকে উপলব্ধ সংস্থানগুলির MIME প্রকারগুলি বলে। আমি ক্লিপবোর্ড আইটেমের getType()
পদ্ধতিকে কল করি, আমি আগে প্রাপ্ত MIME প্রকারটি পাস করি।
const paste = async () => {
try {
const clipboardItems = await navigator.clipboard.read();
for (const clipboardItem of clipboardItems) {
try {
for (const type of clipboardItem.types) {
const blob = await clipboardItem.getType(type);
return blob;
}
} catch (err) {
console.error(err.name, err.message);
}
}
} catch (err) {
console.error(err.name, err.message);
}
};
এবং এটা এখন বলা প্রায় বাহুল্য. আমি শুধুমাত্র সমর্থনকারী ব্রাউজারে এটি করি।
if ('clipboard' in navigator && 'write' in navigator.clipboard) {
import('./clipboard.mjs');
}
তাহলে কিভাবে এই অনুশীলনে কাজ করে? ম্যাকোস প্রিভিউ অ্যাপে আমার একটি ছবি খোলা আছে এবং ক্লিপবোর্ডে কপি করুন। যখন আমি পেস্টে ক্লিক করি, তখন ফুগু গ্রিটিংস অ্যাপ আমাকে জিজ্ঞেস করে যে আমি অ্যাপটিকে ক্লিপবোর্ডে পাঠ্য এবং ছবি দেখতে দিতে চাই কিনা।
অবশেষে, অনুমতি গ্রহণ করার পরে, ছবিটি অ্যাপ্লিকেশনটিতে পেস্ট করা হয়। অন্য উপায় রাউন্ড কাজ, খুব. আমাকে ক্লিপবোর্ডে একটি অভিবাদন কার্ড অনুলিপি করতে দিন। যখন আমি প্রিভিউ খুলি এবং File এবং তারপর New from Clipboard-এ ক্লিক করি, তখন অভিবাদন কার্ডটি একটি নতুন শিরোনামবিহীন ছবিতে আটকানো হয়।
ব্যাজিং এপিআই
আরেকটি দরকারী API হল ব্যাজিং API । একটি ইনস্টলযোগ্য PWA হিসাবে, Fugu Greetings-এ অবশ্যই একটি অ্যাপ আইকন রয়েছে যা ব্যবহারকারীরা অ্যাপ ডক বা হোম স্ক্রিনে রাখতে পারেন। এপিআই প্রদর্শনের একটি মজার এবং সহজ উপায় হল ফুগু গ্রিটিংস-এ এটিকে পেন স্ট্রোক কাউন্টার হিসেবে ব্যবহার করা। আমি একটি ইভেন্ট লিসেনার যোগ করেছি যা pointerdown
ইভেন্টটি ঘটলেই পেন স্ট্রোক কাউন্টারকে বৃদ্ধি করে এবং তারপর আপডেট করা আইকন ব্যাজ সেট করে। যখনই ক্যানভাস সাফ হয়ে যায়, কাউন্টার রিসেট হয়, এবং ব্যাজটি সরানো হয়।
let strokes = 0;
canvas.addEventListener('pointerdown', () => {
navigator.setAppBadge(++strokes);
});
clearButton.addEventListener('click', () => {
strokes = 0;
navigator.setAppBadge(strokes);
});
এই বৈশিষ্ট্যটি একটি প্রগতিশীল বর্ধন, তাই লোডিং যুক্তি যথারীতি।
if ('setAppBadge' in navigator) {
import('./badge.mjs');
}
এই উদাহরণে, আমি প্রতি সংখ্যায় একটি পেন স্ট্রোক ব্যবহার করে এক থেকে সাত পর্যন্ত সংখ্যাগুলি আঁকলাম। আইকনে ব্যাজ কাউন্টারটি এখন সাতটিতে।
পর্যায়ক্রমিক পটভূমি সিঙ্ক API
প্রতিটি দিন নতুন কিছু দিয়ে শুরু করতে চান? ফুগু গ্রিটিংস অ্যাপের একটি ঝরঝরে বৈশিষ্ট্য হল যে এটি আপনাকে প্রতিদিন সকালে একটি নতুন ব্যাকগ্রাউন্ড ইমেজ দিয়ে আপনার শুভেচ্ছা কার্ড শুরু করতে অনুপ্রাণিত করতে পারে। এটি অর্জন করতে অ্যাপটি পর্যায়ক্রমিক পটভূমি সিঙ্ক API ব্যবহার করে।
প্রথম ধাপ হল পরিষেবা কর্মী নিবন্ধনে একটি পর্যায়ক্রমিক সিঙ্ক ইভেন্ট নিবন্ধন করা । এটি 'image-of-the-day'
নামক একটি সিঙ্ক ট্যাগের জন্য শোনে এবং এতে ন্যূনতম একদিনের ব্যবধান থাকে, তাই ব্যবহারকারী প্রতি 24 ঘন্টায় একটি নতুন পটভূমি চিত্র পেতে পারেন।
const registerPeriodicBackgroundSync = async () => {
const registration = await navigator.serviceWorker.ready;
try {
registration.periodicSync.register('image-of-the-day-sync', {
// An interval of one day.
minInterval: 24 * 60 * 60 * 1000,
});
} catch (err) {
console.error(err.name, err.message);
}
};
দ্বিতীয় ধাপ হল পরিষেবা কর্মীর periodicsync
ইভেন্টের জন্য শোনা । যদি ইভেন্ট ট্যাগটি 'image-of-the-day'
হয়, অর্থাৎ যেটি আগে নিবন্ধিত হয়েছিল, দিনের চিত্রটি getImageOfTheDay()
ফাংশনের মাধ্যমে পুনরুদ্ধার করা হয় এবং ফলাফলটি সমস্ত ক্লায়েন্টদের কাছে প্রচার করা হয়, যাতে তারা করতে পারে তাদের ক্যানভাস এবং ক্যাশে আপডেট করুন।
self.addEventListener('periodicsync', (syncEvent) => {
if (syncEvent.tag === 'image-of-the-day-sync') {
syncEvent.waitUntil(
(async () => {
const blob = await getImageOfTheDay();
const clients = await self.clients.matchAll();
clients.forEach((client) => {
client.postMessage({
image: blob,
});
});
})()
);
}
});
আবার এটি সত্যিই একটি প্রগতিশীল বর্ধন, তাই কোডটি তখনই লোড হয় যখন API ব্রাউজার দ্বারা সমর্থিত হয়। এটি ক্লায়েন্ট কোড এবং পরিষেবা কর্মী কোড উভয় ক্ষেত্রেই প্রযোজ্য। অ-সমর্থক ব্রাউজারে, তাদের কোনটিই লোড হয় না। নোট করুন কিভাবে সার্ভিস ওয়ার্কারে, একটি ডাইনামিক import()
এর পরিবর্তে (যা এখনও একটি পরিষেবা কর্মী প্রসঙ্গে সমর্থিত নয়), আমি ক্লাসিক importScripts()
ব্যবহার করি।
// In the client:
const registration = await navigator.serviceWorker.ready;
if (registration && 'periodicSync' in registration) {
import('./periodic_background_sync.mjs');
}
// In the service worker:
if ('periodicSync' in self.registration) {
importScripts('./image_of_the_day.mjs');
}
ফুগু গ্রিটিংস-এ, ওয়ালপেপার বোতাম টিপলে সেই দিনের শুভেচ্ছা কার্ডের ছবি দেখা যায় যা পর্যায়ক্রমিক ব্যাকগ্রাউন্ড সিঙ্ক API-এর মাধ্যমে প্রতিদিন আপডেট করা হয়।
বিজ্ঞপ্তি ট্রিগার API
কখনও কখনও অনেক অনুপ্রেরণার সাথেও, একটি শুরু করা অভিবাদন কার্ড শেষ করার জন্য আপনাকে একটি ধাক্কা দিতে হবে। এটি এমন একটি বৈশিষ্ট্য যা বিজ্ঞপ্তি ট্রিগার এপিআই দ্বারা সক্ষম করা হয়েছে। একজন ব্যবহারকারী হিসাবে, আমি এমন একটি সময় প্রবেশ করতে পারি যখন আমি আমার অভিবাদন কার্ডটি শেষ করতে নাজেড হতে চাই৷ যখন সেই সময় আসবে, আমি একটি বিজ্ঞপ্তি পাব যে আমার শুভেচ্ছা কার্ড অপেক্ষা করছে।
লক্ষ্য সময়ের জন্য অনুরোধ করার পরে, অ্যাপ্লিকেশনটি একটি showTrigger
দিয়ে বিজ্ঞপ্তির সময়সূচী করে। এটি পূর্বে নির্বাচিত টার্গেট তারিখ সহ একটি TimestampTrigger
হতে পারে। অনুস্মারক বিজ্ঞপ্তি স্থানীয়ভাবে ট্রিগার করা হবে, কোন নেটওয়ার্ক বা সার্ভার পার্শ্ব প্রয়োজন নেই.
const targetDate = promptTargetDate();
if (targetDate) {
const registration = await navigator.serviceWorker.ready;
registration.showNotification('Reminder', {
tag: 'reminder',
body: "It's time to finish your greeting card!",
showTrigger: new TimestampTrigger(targetDate),
});
}
অন্য সব কিছুর মতো যা আমি এ পর্যন্ত দেখিয়েছি, এটি একটি প্রগতিশীল বর্ধন, তাই কোডটি শুধুমাত্র শর্তসাপেক্ষে লোড করা হয়েছে।
if ('Notification' in window && 'showTrigger' in Notification.prototype) {
import('./notification_triggers.mjs');
}
যখন আমি ফুগু গ্রিটিংস-এ অনুস্মারক চেকবক্সটি চেক করি, তখন একটি প্রম্পট আমাকে জিজ্ঞাসা করে যে আমি কখন আমার শুভেচ্ছা কার্ড শেষ করার জন্য স্মরণ করিয়ে দিতে চাই৷
যখন ফুগু গ্রিটিংস-এ একটি নির্ধারিত বিজ্ঞপ্তি ট্রিগার হয়, তখন এটি অন্য যেকোনো বিজ্ঞপ্তির মতোই দেখানো হয়, কিন্তু আমি যেমনটি আগে লিখেছিলাম, এটির জন্য নেটওয়ার্ক সংযোগের প্রয়োজন নেই৷
ওয়েক লক এপিআই
আমি ওয়েক লক API অন্তর্ভুক্ত করতে চাই। কখনও কখনও আপনাকে স্ক্রিনের দিকে দীর্ঘক্ষণ তাকিয়ে থাকতে হবে যতক্ষণ না অনুপ্রেরণা আপনাকে চুম্বন করে। তারপরে সবচেয়ে খারাপ যেটা ঘটতে পারে তা হল স্ক্রীন বন্ধ হয়ে যাওয়া। ওয়েক লক API এটি ঘটতে বাধা দিতে পারে।
প্রথম ধাপ হল navigator.wakelock.request method()
দিয়ে একটি ওয়েক লক প্রাপ্ত করা। একটি স্ক্রীন ওয়েক লক পেতে আমি এটিকে 'screen'
স্ট্রিং পাস করি। আমি তখন একটি ইভেন্ট শ্রোতা যোগ করি যাতে ওয়েক লক রিলিজ হলে জানানো হয়। এটি ঘটতে পারে, উদাহরণস্বরূপ, যখন ট্যাবের দৃশ্যমানতা পরিবর্তন হয়। যদি এটি ঘটে, আমি, ট্যাবটি আবার দৃশ্যমান হলে, ওয়েক লকটি পুনরায় প্রাপ্ত করতে পারি৷
let wakeLock = null;
const requestWakeLock = async () => {
wakeLock = await navigator.wakeLock.request('screen');
wakeLock.addEventListener('release', () => {
console.log('Wake Lock was released');
});
console.log('Wake Lock is active');
};
const handleVisibilityChange = () => {
if (wakeLock !== null && document.visibilityState === 'visible') {
requestWakeLock();
}
};
document.addEventListener('visibilitychange', handleVisibilityChange);
document.addEventListener('fullscreenchange', handleVisibilityChange);
হ্যাঁ, এটি একটি প্রগতিশীল বর্ধন, তাই ব্রাউজারটি API সমর্থন করলেই আমাকে এটি লোড করতে হবে।
if ('wakeLock' in navigator && 'request' in navigator.wakeLock) {
import('./wake_lock.mjs');
}
ফুগু গ্রিটিংস-এ, একটি অনিদ্রা চেকবক্স রয়েছে যা চেক করা হলে, স্ক্রীনকে জাগ্রত রাখে।
নিষ্ক্রিয় সনাক্তকরণ API
মাঝে মাঝে, এমনকি আপনি যদি ঘন্টার পর ঘন্টা স্ক্রিনের দিকে তাকিয়ে থাকেন, তবে এটি কেবল অকেজো এবং আপনি আপনার অভিবাদন কার্ডের সাথে কী করবেন তা সামান্যতম ধারণা নিয়ে আসতে পারবেন না। নিষ্ক্রিয় সনাক্তকরণ API অ্যাপটিকে ব্যবহারকারীর নিষ্ক্রিয় সময় সনাক্ত করতে দেয়। যদি ব্যবহারকারী খুব বেশি সময় ধরে নিষ্ক্রিয় থাকে, অ্যাপটি প্রাথমিক অবস্থায় রিসেট করে এবং ক্যানভাস সাফ করে। এই APIটি বর্তমানে বিজ্ঞপ্তি অনুমতির পিছনে গেট করা হয়েছে, যেহেতু নিষ্ক্রিয় সনাক্তকরণের প্রচুর উত্পাদন ব্যবহারের ক্ষেত্রে বিজ্ঞপ্তি-সম্পর্কিত, উদাহরণস্বরূপ, ব্যবহারকারী বর্তমানে সক্রিয়ভাবে ব্যবহার করছেন এমন একটি ডিভাইসে শুধুমাত্র একটি বিজ্ঞপ্তি পাঠানোর জন্য।
বিজ্ঞপ্তির অনুমতি দেওয়া হয়েছে তা নিশ্চিত করার পরে, আমি তখন নিষ্ক্রিয় ডিটেক্টরটি ইনস্ট্যান্টিয়েট করি। আমি একটি ইভেন্ট শ্রোতা নিবন্ধন করি যা নিষ্ক্রিয় পরিবর্তনগুলির জন্য শোনে, যার মধ্যে ব্যবহারকারী এবং স্ক্রীনের অবস্থা রয়েছে৷ ব্যবহারকারী সক্রিয় বা নিষ্ক্রিয় হতে পারে এবং স্ক্রীনটি আনলক বা লক করা যেতে পারে। ব্যবহারকারী নিষ্ক্রিয় থাকলে, ক্যানভাস পরিষ্কার হয়। আমি নিষ্ক্রিয় ডিটেক্টরকে 60 সেকেন্ডের থ্রেশহোল্ড দিই।
const idleDetector = new IdleDetector();
idleDetector.addEventListener('change', () => {
const userState = idleDetector.userState;
const screenState = idleDetector.screenState;
console.log(`Idle change: ${userState}, ${screenState}.`);
if (userState === 'idle') {
clearCanvas();
}
});
await idleDetector.start({
threshold: 60000,
signal,
});
এবং সর্বদা হিসাবে, আমি শুধুমাত্র এই কোডটি লোড করি যখন ব্রাউজার এটি সমর্থন করে।
if ('IdleDetector' in window) {
import('./idle_detection.mjs');
}
ফুগু গ্রিটিংস অ্যাপে, যখন ক্ষণস্থায়ী চেকবক্সটি চেক করা হয় এবং ব্যবহারকারী খুব বেশি সময় ধরে নিষ্ক্রিয় থাকে তখন ক্যানভাস পরিষ্কার হয়ে যায়।
বন্ধ হচ্ছে
উফ, কি একটা যাত্রা। শুধুমাত্র একটি নমুনা অ্যাপে এতগুলি API। এবং, মনে রাখবেন, আমি কখনই ব্যবহারকারীকে এমন একটি বৈশিষ্ট্যের জন্য ডাউনলোড খরচ দিতে বাধ্য করি না যা তাদের ব্রাউজার সমর্থন করে না। প্রগতিশীল বর্ধন ব্যবহার করে, আমি নিশ্চিত করি যে শুধুমাত্র প্রাসঙ্গিক কোড লোড হয়। এবং যেহেতু HTTP/2 এর সাথে, অনুরোধগুলি সস্তা, এই প্যাটার্নটি অনেকগুলি অ্যাপ্লিকেশনের জন্য ভাল কাজ করা উচিত, যদিও আপনি সত্যিই বড় অ্যাপগুলির জন্য একটি বান্ডলার বিবেচনা করতে চাইতে পারেন।
প্রতিটি ব্রাউজারে অ্যাপটি একটু আলাদা দেখাতে পারে কারণ সমস্ত প্ল্যাটফর্ম সমস্ত বৈশিষ্ট্য সমর্থন করে না, তবে মূল কার্যকারিতা সর্বদা সেখানে থাকে - নির্দিষ্ট ব্রাউজারের ক্ষমতা অনুসারে ক্রমাগতভাবে উন্নত করা হয়৷ মনে রাখবেন যে এই ক্ষমতাগুলি এক এবং একই ব্রাউজারেও পরিবর্তিত হতে পারে, অ্যাপটি ইনস্টল করা অ্যাপ হিসাবে বা ব্রাউজার ট্যাবে চলছে কিনা তার উপর নির্ভর করে।
আপনি যদি Fugu গ্রিটিংস অ্যাপে আগ্রহী হন, তাহলে GitHub-এ এটিকে খুঁজে বের করুন ।
উন্নত Fugu API-এর ক্ষেত্রে ক্রোমিয়াম দল ঘাসকে আরও সবুজ করার জন্য কঠোর পরিশ্রম করছে। আমার অ্যাপের ডেভেলপমেন্টে প্রগতিশীল বর্ধন প্রয়োগ করে, আমি নিশ্চিত করি যে প্রত্যেকে একটি ভাল, শক্ত বেসলাইন অভিজ্ঞতা পায়, কিন্তু যে লোকেরা আরও বেশি ওয়েব প্ল্যাটফর্ম API সমর্থন করে এমন ব্রাউজার ব্যবহার করে তারা আরও ভাল অভিজ্ঞতা পান। আমি আপনার অ্যাপে প্রগতিশীল বর্ধনের সাথে আপনি কী করেন তা দেখার জন্য অপেক্ষা করছি।
স্বীকৃতি
আমি ক্রিশ্চিয়ান লিবেল এবং হেমান্থ এইচএম-এর কাছে কৃতজ্ঞ যারা উভয়েই ফুগু শুভেচ্ছায় অবদান রেখেছেন। এই নিবন্ধটি Joe Medley এবং Kayce Basques দ্বারা পর্যালোচনা করা হয়েছে। জ্যাক আর্চিবল্ড আমাকে একটি পরিষেবা কর্মী প্রসঙ্গে গতিশীল import()
এর সাথে পরিস্থিতি খুঁজে বের করতে সাহায্য করেছে।