ভূমিকা
2010 সালে, Fi.com এবং Google Chrome টিম একটি HTML5-ভিত্তিক শিক্ষামূলক ওয়েব অ্যাপে সহযোগিতা করে যার নাম 20 Things I Learned about Browsers and the Web ( www.20thingsilearned.com )। এই প্রকল্পের পিছনে একটি মূল ধারণা ছিল যে এটি একটি বইয়ের প্রেক্ষাপটে উপস্থাপন করা হবে। যেহেতু বইটির বিষয়বস্তু ওপেন ওয়েব টেকনোলজি সম্পর্কে অনেক বেশি আমরা অনুভব করেছি যে এই প্রযুক্তিগুলি আজ আমাদের কী করতে দেয় তার একটি উদাহরণ তৈরি করে কনটেইনারটিকেই এর প্রতি সত্য থাকা গুরুত্বপূর্ণ।
আমরা সিদ্ধান্ত নিয়েছি যে একটি বাস্তব বিশ্বের বইয়ের অনুভূতি অর্জনের সর্বোত্তম উপায় হল অ্যানালগ পড়ার অভিজ্ঞতার ভাল অংশগুলিকে অনুকরণ করা এবং এখনও নেভিগেশনের মতো ক্ষেত্রে ডিজিটাল জগতের সুবিধাগুলিকে কাজে লাগানো৷ পড়ার প্রবাহের গ্রাফিকাল এবং ইন্টারেক্টিভ ট্রিটমেন্টে প্রচুর পরিশ্রম করা হয়েছে - বিশেষ করে কীভাবে বইয়ের পৃষ্ঠাগুলি এক পৃষ্ঠা থেকে অন্য পৃষ্ঠায় উল্টে যায়।
শুরু করা
এই টিউটোরিয়ালটি আপনাকে ক্যানভাস উপাদান এবং প্রচুর জাভাস্ক্রিপ্ট ব্যবহার করে আপনার নিজস্ব পৃষ্ঠা ফ্লিপ ইফেক্ট তৈরি করার প্রক্রিয়ার মধ্যে নিয়ে যাবে। কিছু প্রাথমিক কোড, যেমন পরিবর্তনশীল ঘোষণা এবং ইভেন্ট লিসেনার সাবস্ক্রিপশন, এই নিবন্ধে স্নিপেট থেকে বাদ দেওয়া হয়েছে তাই কাজের উদাহরণ উল্লেখ করতে ভুলবেন না।
আমরা শুরু করার আগে ডেমোটি দেখে নেওয়া একটি ভাল ধারণা যাতে আপনি জানেন যে আমরা কী তৈরি করতে চাইছি৷
মার্কআপ
এটি সর্বদা মনে রাখা গুরুত্বপূর্ণ যে আমরা ক্যানভাসে যা আঁকি তা সার্চ ইঞ্জিন দ্বারা সূচিত করা যায় না, দর্শক দ্বারা নির্বাচিত বা ব্রাউজারে অনুসন্ধানের মাধ্যমে পাওয়া যায় না। সেই কারণে, আমরা যে বিষয়বস্তুর সাথে কাজ করব তা সরাসরি DOM-এ রাখা হয় এবং তারপর যদি এটি উপলব্ধ থাকে জাভাস্ক্রিপ্ট দ্বারা ম্যানিপুলেট করা হয়। এর জন্য প্রয়োজনীয় মার্কআপ ন্যূনতম:
<div id='book'>
<canvas id='pageflip-canvas'></canvas>
<div id='pages'>
<section>
<div> <!-- Any type of contents here --> </div>
</section>
<!-- More <section>s here -->
</div>
</div>
বইয়ের জন্য আমাদের কাছে একটি প্রধান ধারক উপাদান রয়েছে, যার ফলে আমাদের বইয়ের বিভিন্ন পৃষ্ঠা এবং canvas
উপাদান রয়েছে যা আমরা ফ্লিপিং পৃষ্ঠাগুলি আঁকব। section
উপাদানের ভিতরে বিষয়বস্তুর জন্য একটি div
র্যাপার রয়েছে - আমাদের এটির বিষয়বস্তুর বিন্যাসকে প্রভাবিত না করে পৃষ্ঠার প্রস্থ পরিবর্তন করতে সক্ষম হতে হবে৷ div
একটি নির্দিষ্ট প্রস্থ রয়েছে এবং section
তার ওভারফ্লো লুকানোর জন্য সেট করা হয়েছে, এর ফলে section
প্রস্থ div
এর জন্য অনুভূমিক মুখোশ হিসাবে কাজ করে।
যুক্তিবিদ্যা
পৃষ্ঠা ফ্লিপকে পাওয়ার জন্য প্রয়োজনীয় কোডটি খুব জটিল নয়, তবে এটি বেশ বিস্তৃত কারণ এতে প্রচুর পদ্ধতিগতভাবে তৈরি গ্রাফিক্স জড়িত। চলুন শুরু করা যাক ধ্রুবক মানগুলির বর্ণনা দেখে আমরা পুরো কোড জুড়ে ব্যবহার করব।
var BOOK_WIDTH = 830;
var BOOK_HEIGHT = 260;
var PAGE_WIDTH = 400;
var PAGE_HEIGHT = 250;
var PAGE_Y = ( BOOK_HEIGHT - PAGE_HEIGHT ) / 2;
var CANVAS_PADDING = 60;
CANVAS_PADDING
ক্যানভাসের চারপাশে যুক্ত করা হয়েছে যাতে আমরা উল্টানোর সময় কাগজটি বইয়ের বাইরে প্রসারিত করতে পারি। মনে রাখবেন যে এখানে সংজ্ঞায়িত কিছু ধ্রুবক CSS-এও সেট করা আছে, তাই আপনি যদি বইটির আকার পরিবর্তন করতে চান তবে আপনাকে সেখানে মান আপডেট করতে হবে।
পরবর্তীতে আমাদের প্রতিটি পৃষ্ঠার জন্য একটি ফ্লিপ অবজেক্ট সংজ্ঞায়িত করতে হবে, ফ্লিপের বর্তমান অবস্থা প্রতিফলিত করতে আমরা বইটির সাথে যোগাযোগ করার সাথে সাথে এগুলি ক্রমাগত আপডেট করা হবে।
// Create a reference to the book container element
var book = document.getElementById( 'book' );
// Grab a list of all section elements (pages) within the book
var pages = book.getElementsByTagName( 'section' );
for( var i = 0, len = pages.length; i < len; i++ ) {
pages[i].style.zIndex = len - i;
flips.push( {
progress: 1,
target: 1,
page: pages[i],
dragging: false
});
}
প্রথমে আমাদের নিশ্চিত করতে হবে যে পৃষ্ঠাগুলি সঠিকভাবে স্তরযুক্ত করা হয়েছে বিভাগ উপাদানগুলির z-সূচীগুলিকে সংগঠিত করে যাতে প্রথম পৃষ্ঠাটি উপরে থাকে এবং শেষ পৃষ্ঠাটি নীচে থাকে। ফ্লিপ অবজেক্টের সবচেয়ে গুরুত্বপূর্ণ বৈশিষ্ট্য হল progress
এবং target
মান। পৃষ্ঠাটি বর্তমানে কতদূর ভাঁজ করা উচিত তা নির্ধারণ করতে এগুলি ব্যবহার করা হয়, -1 মানে বাম দিকে, 0 মানে বইয়ের মৃত কেন্দ্র এবং +1 মানে বইয়ের ডানদিকের প্রান্ত।
এখন যেহেতু আমাদের প্রতিটি পৃষ্ঠার জন্য একটি ফ্লিপ অবজেক্ট সংজ্ঞায়িত করা আছে, আমাদের ফ্লিপের অবস্থা আপডেট করার জন্য ব্যবহারকারীদের ইনপুট ক্যাপচার করা এবং ব্যবহার করা শুরু করতে হবে।
function mouseMoveHandler( event ) {
// Offset mouse position so that the top of the book spine is 0,0
mouse.x = event.clientX - book.offsetLeft - ( BOOK_WIDTH / 2 );
mouse.y = event.clientY - book.offsetTop;
}
function mouseDownHandler( event ) {
// Make sure the mouse pointer is inside of the book
if (Math.abs(mouse.x) < PAGE_WIDTH) {
if (mouse.x < 0 && page - 1 >= 0) {
// We are on the left side, drag the previous page
flips[page - 1].dragging = true;
}
else if (mouse.x > 0 && page + 1 < flips.length) {
// We are on the right side, drag the current page
flips[page].dragging = true;
}
}
// Prevents the text selection
event.preventDefault();
}
function mouseUpHandler( event ) {
for( var i = 0; i < flips.length; i++ ) {
// If this flip was being dragged, animate to its destination
if( flips[i].dragging ) {
// Figure out which page we should navigate to
if( mouse.x < 0 ) {
flips[i].target = -1;
page = Math.min( page + 1, flips.length );
}
else {
flips[i].target = 1;
page = Math.max( page - 1, 0 );
}
}
flips[i].dragging = false;
}
}
mouseMoveHandler
ফাংশন mouse
অবজেক্ট আপডেট করে যাতে আমরা সর্বদা সবচেয়ে সাম্প্রতিক কার্সার অবস্থানের দিকে কাজ করি।
mouseDownHandler
আমরা মাউসটি বাম বা ডান পৃষ্ঠায় চাপ দেওয়া হয়েছে কিনা তা পরীক্ষা করে শুরু করি যাতে আমরা বুঝতে পারি যে আমরা কোন দিকে উল্টানো শুরু করতে চাই। আমরা নিশ্চিত করি যে সেই দিকে অন্য একটি পৃষ্ঠা বিদ্যমান রয়েছে যেহেতু আমরা প্রথম বা শেষ পৃষ্ঠায় থাকতে পারি। এই চেকগুলির পরে যদি একটি বৈধ ফ্লিপ বিকল্প পাওয়া যায়, আমরা সংশ্লিষ্ট ফ্লিপ অবজেক্টের dragging
পতাকাটিকে true
হিসাবে সেট করি।
একবার আমরা mouseUpHandler
পৌঁছানোর পর আমরা সমস্ত flips
মধ্য দিয়ে যাই এবং পরীক্ষা করি যে সেগুলির মধ্যে কোনোটিকে dragging
মতো পতাকাঙ্কিত করা হয়েছে এবং এখন মুক্তি দেওয়া উচিত। যখন একটি ফ্লিপ রিলিজ করা হয় তখন আমরা তার টার্গেট মান সেট করি পাশের সাথে মেলে এটি বর্তমান মাউসের অবস্থানের উপর নির্ভর করে ফ্লিপ করা উচিত। এই নেভিগেশন প্রতিফলিত করার জন্য পৃষ্ঠা নম্বরটিও আপডেট করা হয়েছে।
রেন্ডারিং
এখন যেহেতু আমাদের বেশিরভাগ যুক্তিই ঠিক আছে আমরা কীভাবে ভাঁজ করা কাগজটিকে ক্যানভাস উপাদানে রেন্ডার করব তা দেখব। এর বেশিরভাগই render()
ফাংশনের ভিতরে ঘটে, যেটিকে প্রতি সেকেন্ডে 60 বার বলা হয় সমস্ত সক্রিয় ফ্লিপগুলির বর্তমান অবস্থা আপডেট এবং আঁকার জন্য।
function render() {
// Reset all pixels in the canvas
context.clearRect( 0, 0, canvas.width, canvas.height );
for( var i = 0, len = flips.length; i < len; i++ ) {
var flip = flips[i];
if( flip.dragging ) {
flip.target = Math.max( Math.min( mouse.x / PAGE_WIDTH, 1 ), -1 );
}
// Ease progress towards the target value
flip.progress += ( flip.target - flip.progress ) * 0.2;
// If the flip is being dragged or is somewhere in the middle
// of the book, render it
if( flip.dragging || Math.abs( flip.progress ) < 0.997 ) {
drawFlip( flip );
}
}
}
flips
রেন্ডার করা শুরু করার আগে, আমরা clearRect(x,y,w,h)
পদ্ধতি ব্যবহার করে ক্যানভাস রিসেট করি। পুরো ক্যানভাস পরিষ্কার করা একটি বড় কর্মক্ষমতা ব্যয়ে আসে এবং আমরা যে অঞ্চলগুলি আঁকছি তা কেবলমাত্র সাফ করা অনেক বেশি কার্যকর হবে। বিষয়ের উপর এই টিউটোরিয়ালটি রাখার স্বার্থে, আমরা পুরো ক্যানভাস পরিষ্কার করার জন্য এটি ছেড়ে দেব।
যদি একটি ফ্লিপ টেনে আনা হয় তবে আমরা মাউসের অবস্থানের সাথে মেলে তবে প্রকৃত পিক্সেলের পরিবর্তে -1 থেকে 1 স্কেলে এর target
মান আপডেট করি। আমরা target
দূরত্বের একটি ভগ্নাংশ দ্বারা progress
বৃদ্ধি করি, এর ফলে ফ্লিপের একটি মসৃণ এবং অ্যানিমেটেড অগ্রগতি হবে কারণ এটি প্রতিটি ফ্রেমে আপডেট হয়।
যেহেতু আমরা প্রতিটি ফ্রেমের সমস্ত flips
উপর দিয়ে যাচ্ছি, তাই আমাদের নিশ্চিত করতে হবে যে আমরা শুধুমাত্র সক্রিয়গুলিকে পুনরায় আঁকব। যদি একটি ফ্লিপ বইয়ের প্রান্তের খুব কাছাকাছি না হয় ( BOOK_WIDTH
এর 0.3% এর মধ্যে), বা যদি এটি dragging
হিসাবে পতাকাঙ্কিত হয়, তবে এটি সক্রিয় বলে বিবেচিত হয়৷
এখন যেহেতু সমস্ত যুক্তি রয়েছে, আমাদের বর্তমান অবস্থার উপর নির্ভর করে একটি ফ্লিপের গ্রাফিকাল উপস্থাপনা আঁকতে হবে। drawFlip(flip)
ফাংশনের প্রথম অংশটি দেখার সময় এসেছে।
// Determines the strength of the fold/bend on a 0-1 range
var strength = 1 - Math.abs( flip.progress );
// Width of the folded paper
var foldWidth = ( PAGE_WIDTH * 0.5 ) * ( 1 - flip.progress );
// X position of the folded paper
var foldX = PAGE_WIDTH * flip.progress + foldWidth;
// How far outside of the book the paper is bent due to perspective
var verticalOutdent = 20 * strength;
// The maximum widths of the three shadows used
var paperShadowWidth = (PAGE_WIDTH*0.5) * Math.max(Math.min(1 - flip.progress, 0.5), 0);
var rightShadowWidth = (PAGE_WIDTH*0.5) * Math.max(Math.min(strength, 0.5), 0);
var leftShadowWidth = (PAGE_WIDTH*0.5) * Math.max(Math.min(strength, 0.5), 0);
// Mask the page by setting its width to match the foldX
flip.page.style.width = Math.max(foldX, 0) + 'px';
কোডের এই বিভাগটি বেশ কয়েকটি ভিজ্যুয়াল ভেরিয়েবল গণনা করে শুরু হয় যা আমাদেরকে বাস্তবসম্মত পদ্ধতিতে ভাঁজ আঁকতে হবে। আমরা যে ফ্লিপটি আঁকছি তার progress
মান এখানে একটি বড় ভূমিকা পালন করে, যেহেতু আমরা এখানে পৃষ্ঠা ভাঁজটি উপস্থিত করতে চাই। পৃষ্ঠা ফ্লিপ প্রভাবে গভীরতা যোগ করতে আমরা কাগজটিকে বইয়ের উপরের এবং নীচের প্রান্তের বাইরে প্রসারিত করি, এই প্রভাবটি তার শীর্ষে থাকে যখন একটি ফ্লিপ বইয়ের মেরুদণ্ডের কাছাকাছি থাকে।
এখন সমস্ত মান প্রস্তুত করা হয়েছে, যা অবশিষ্ট রয়েছে তা হল কাগজ আঁকা!
context.save();
context.translate( CANVAS_PADDING + ( BOOK_WIDTH / 2 ), PAGE_Y + CANVAS_PADDING );
// Draw a sharp shadow on the left side of the page
context.strokeStyle = `rgba(0,0,0,`+(0.05 * strength)+`)`;
context.lineWidth = 30 * strength;
context.beginPath();
context.moveTo(foldX - foldWidth, -verticalOutdent * 0.5);
context.lineTo(foldX - foldWidth, PAGE_HEIGHT + (verticalOutdent * 0.5));
context.stroke();
// Right side drop shadow
var rightShadowGradient = context.createLinearGradient(foldX, 0,
foldX + rightShadowWidth, 0);
rightShadowGradient.addColorStop(0, `rgba(0,0,0,`+(strength*0.2)+`)`);
rightShadowGradient.addColorStop(0.8, `rgba(0,0,0,0.0)`);
context.fillStyle = rightShadowGradient;
context.beginPath();
context.moveTo(foldX, 0);
context.lineTo(foldX + rightShadowWidth, 0);
context.lineTo(foldX + rightShadowWidth, PAGE_HEIGHT);
context.lineTo(foldX, PAGE_HEIGHT);
context.fill();
// Left side drop shadow
var leftShadowGradient = context.createLinearGradient(
foldX - foldWidth - leftShadowWidth, 0, foldX - foldWidth, 0);
leftShadowGradient.addColorStop(0, `rgba(0,0,0,0.0)`);
leftShadowGradient.addColorStop(1, `rgba(0,0,0,`+(strength*0.15)+`)`);
context.fillStyle = leftShadowGradient;
context.beginPath();
context.moveTo(foldX - foldWidth - leftShadowWidth, 0);
context.lineTo(foldX - foldWidth, 0);
context.lineTo(foldX - foldWidth, PAGE_HEIGHT);
context.lineTo(foldX - foldWidth - leftShadowWidth, PAGE_HEIGHT);
context.fill();
// Gradient applied to the folded paper (highlights & shadows)
var foldGradient = context.createLinearGradient(
foldX - paperShadowWidth, 0, foldX, 0);
foldGradient.addColorStop(0.35, `#fafafa`);
foldGradient.addColorStop(0.73, `#eeeeee`);
foldGradient.addColorStop(0.9, `#fafafa`);
foldGradient.addColorStop(1.0, `#e2e2e2`);
context.fillStyle = foldGradient;
context.strokeStyle = `rgba(0,0,0,0.06)`;
context.lineWidth = 0.5;
// Draw the folded piece of paper
context.beginPath();
context.moveTo(foldX, 0);
context.lineTo(foldX, PAGE_HEIGHT);
context.quadraticCurveTo(foldX, PAGE_HEIGHT + (verticalOutdent * 2),
foldX - foldWidth, PAGE_HEIGHT + verticalOutdent);
context.lineTo(foldX - foldWidth, -verticalOutdent);
context.quadraticCurveTo(foldX, -verticalOutdent * 2, foldX, 0);
context.fill();
context.stroke();
context.restore();
ক্যানভাস এপিআই-এর translate(x,y)
পদ্ধতিটি কো-অর্ডিনেট সিস্টেমকে অফসেট করতে ব্যবহার করা হয় যাতে আমরা 0,0 অবস্থান হিসাবে কাজ করে মেরুদণ্ডের শীর্ষের সাথে আমাদের পৃষ্ঠাটি ফ্লিপ আঁকতে পারি। মনে রাখবেন যে আমাদের ক্যানভাসের বর্তমান ট্রান্সফরমেশন ম্যাট্রিক্স save()
করতে হবে এবং যখন আমরা অঙ্কন শেষ করব তখন এটিকে restore()
।
foldGradient
হল যা আমরা ভাঁজ করা কাগজের আকারটি পূরণ করব যাতে এটি বাস্তবসম্মত হাইলাইট এবং ছায়া দেয়। আমরা কাগজের আঁকার চারপাশে একটি খুব পাতলা রেখা যুক্ত করি যাতে কাগজটি হালকা পটভূমিতে রাখার সময় অদৃশ্য হয়ে না যায়।
এখন যা অবশিষ্ট আছে তা হল আমরা উপরে সংজ্ঞায়িত বৈশিষ্ট্যগুলি ব্যবহার করে ভাঁজ করা কাগজের আকৃতি আঁকা। আমাদের কাগজের বাম এবং ডান দিকগুলি সরল রেখা হিসাবে আঁকা হয়েছে এবং একটি ভাঁজ করা কাগজের বাঁকানো অনুভূতি আনার জন্য উপরের এবং নীচের দিকগুলি বাঁকা হয়েছে। এই কাগজের বাঁকের শক্তি verticalOutdent
মান দ্বারা নির্ধারিত হয়।
তাই তো! আপনি এখন একটি সম্পূর্ণ কার্যকরী পৃষ্ঠা ফ্লিপ নেভিগেশন পেয়েছেন।
পেজ ফ্লিপ ডেমো
পেজ ফ্লিপ ইফেক্ট হল সঠিক ইন্টারেক্টিভ অনুভূতির সাথে যোগাযোগ করা, তাই এর ইমেজগুলোর দিকে তাকানো ঠিক ন্যায়বিচার করে না।
পরবর্তী পদক্ষেপ
ক্যানভাস উপাদানের মতো HTML5 বৈশিষ্ট্যগুলি ব্যবহার করে কী সম্পন্ন করা যেতে পারে তার এটি শুধুমাত্র একটি উদাহরণ। আমি আপনাকে আরও পরিমার্জিত বইয়ের অভিজ্ঞতার দিকে নজর দেওয়ার পরামর্শ দিচ্ছি যেখান থেকে এই কৌশলটি এখানে একটি উদ্ধৃতি: www.20thingsilearned.com । সেখানে আপনি দেখতে পাবেন কিভাবে একটি বাস্তব অ্যাপ্লিকেশনে পেজ ফ্লিপ প্রয়োগ করা যায় এবং অন্যান্য HTML5 বৈশিষ্ট্যের সাথে পেয়ার করা হলে এটি কতটা শক্তিশালী হয়ে ওঠে।
তথ্যসূত্র
- ক্যানভাস API স্পেসিফিকেশন