আমার ব্যাকগ্রাউন্ড
২০০৬ সালে Firefox v2.0 রিলিজ হওয়ার সময় <canvas>-এর ব্যাপারে আমি প্রথম জানতে পারি। Ajaxian-এ ট্রান্সফর্মেশন ম্যাট্রিক্সের বর্ণনা দিয়ে লেখা একটি নিবন্ধ আমাকে প্রথম <canvas> ওয়েব-অ্যাপ; Color Sphere (২০০৭) তৈরি করতে অনুপ্রাণিত করেছিল। যা আমাকে রঙ ও গ্রাফিক প্রিমিটিভের জগতে ডুবিয়ে দেয়; ব্রাউজারে "Paint-এর চেয়ে ভালো" অ্যাপ্লিকেশন তৈরি করার প্রচেষ্টায় Sketchpad (২০০৭-২০০৮) তৈরি করার অনুপ্রেরণা দেয়।
এইসব এক্সপেরিমেন্টের ফলে আমার দীর্ঘদিনের বন্ধু চার্লস প্রিচার্ডের সাথে
স্টার্ট-আপ Mugtug তৈরি করি। আমরা HTML5 <canvas>-এ Darkroom ডেভেলপ করছি। Darkroom
হল একটি নন-ডেসট্রাক্টিভ ফটো-শেয়ারিং অ্যাপ, যা ভেক্টর-ভিত্তিক টাইপোগ্রাফি এবং অঙ্কনের সাথে পিক্সেল-ভিত্তিক ফিল্টারের ক্ষমতাকে একত্রিত করে।
ভূমিকা
<canvas> জাভাস্ক্রিপ্ট প্রোগ্রামারদের তাদের স্ক্রিনে রঙ,
ভেক্টর এবং পিক্সেল-এর সম্পূর্ণ নিয়ন্ত্রণ দেয় - মনিটরের ভিজ্যুয়াল মেকআপ।
নিম্নলিখিত উদাহরণগুলি <canvas>-এর এমন একটি ক্ষেত্র নিয়ে আলোচনা করে যেটির উপর
বেশি মনোযোগ দেওয়া হয়নি; টেক্সট-এফেক্ট তৈরি করা। <canvas>-এ বিভিন্ন ধরনের টেক্সট-এফেক্ট
তৈরি করা যায় - এই ডেমোতে
যা যা করা সম্ভব তার একটি সাব-সেকশন কভার করা হয়েছে। যদিও আমরা এই নিবন্ধে "টেক্সট" নিয়ে আলোচনা করছি, তবে এই পদ্ধতিগুলি যেকোনও ভেক্টর অবজেক্টের ক্ষেত্রে প্রয়োগ করা যেতে পারে;
গেম এবং অন্যান্য অ্যাপ্লিকেশনে উত্তেজনাপূর্ণ ভিজ্যুয়াল তৈরি করা:
<canvas>-এ টেক্সট-শ্যাডো।<canvas>-এ ক্লিপিং মাস্ক তৈরি করা,<canvas>-এ মেট্রিক খুঁজে পাওয়া এবং শ্যাডো প্রপার্টি ব্যবহার করার ক্ষেত্রে CSS-এর মতো টেক্সট-এফেক্ট।- নিয়ন-রামধনু, জেব্রা-প্রতিফলন - চেইনিং এফেক্ট।
- Photoshop-এর মতো টেক্সট-এফেক্ট
<canvas>globalCompositeOperation, createLinearGradient, createPattern ব্যবহার করার উদাহরণ। <canvas>-এ ইনার ও আউটার শ্যাডো- খুব কম পরিচিত একটি ফিচার প্রকাশ করা; ড্রপ-শ্যাডোর (ইনার-শ্যাডো) বিপরীত তৈরি করতে ঘড়ির কাঁটার দিকে বনাম ঘড়ির কাঁটার বিপরীত দিকে উইন্ডিং ব্যবহার করা।
- স্পেসএজ - জেনারেটিভ এফেক্ট।
- জেনারেটিভ ভিত্তিক টেক্সট-এফেক্ট
<canvas>-এ hsl() কালার-সাইক্লিং এবংwindow.requestAnimationFrameব্যবহার করে মোশনের অনুভূতি তৈরি করে।
ক্যানভাসে টেক্সট-শ্যাডো
CSS3 স্পেসিফিকেশনে (border-radius, web-gradients এবং অন্যান্য সহ) আমার অন্যতম প্রিয় সংযোজন
হল শ্যাডো তৈরি করার ক্ষমতা। CSS এবং <canvas> শ্যাডোর মধ্যে পার্থক্য বোঝা গুরুত্বপূর্ণ, বিশেষ করে:
CSS দুটি পদ্ধতি ব্যবহার করে; div, span-এর মতো বক্স-এলিমেন্টের জন্য box-shadow এবং টেক্সট কন্টেন্টের জন্য text-shadow।
<canvas>-এ এক ধরনের শ্যাডো আছে; এটি সব ভেক্টর অবজেক্টের জন্য ব্যবহার করা হয়; ctx.moveTo, ctx.lineTo, ctx.bezierCurveTo, ctx.quadradicCurveTo, ctx.arc, ctx.rect, ctx.fillText, ctx.strokeText ইত্যাদি।
<canvas>-এ শ্যাডো তৈরি করতে, এই চারটি প্রপার্টিতে ট্যাপ করুন:
- ctx.shadowColor = "red" // string
- ছায়ার রঙ; RGB, RGBA, HSL, HEX এবং অন্যান্য ইনপুট সঠিক।
- ctx.shadowOffsetX = 0; // integer
- টেক্সটের সাপেক্ষে ছায়ার অনুভূমিক দূরত্ব।
- ctx.shadowOffsetY = 0; // integer
- টেক্সটের সাপেক্ষে ছায়ার উল্লম্ব দূরত্ব।
- ctx.shadowBlur = 10; // integer
- শ্যাডোতে ব্লার করার এফেক্ট, ভ্যালু যত বেশি হবে, ব্লার তত বেশি হবে।
শুরু করার জন্য, <canvas> কীভাবে CSS এফেক্ট এমুলেট করতে পারে তা দেখা যাক।
"css text-shadow"-এর জন্য Google Images-এ সার্চ করে আমরা কিছু দারুণ ডেমো পেয়েছি, যেগুলি আমরা অনুকরণ করতে পারি; Line25,
এবং Stereoscopic, এবং
Shadow 3D।
স্টেরিওস্কোপিক 3D এফেক্ট (আরও জানতে অ্যানাগ্লিফ ছবি দেখুন) হল কোডের একটি সাধারণ লাইনের উদাহরণ, যা দারুণভাবে ব্যবহার করা হয়েছে। CSS-এর নিম্নলিখিত লাইনের মাধ্যমে, আমরা 3D লাল/সায়ান চশমা (3D সিনেমা দেখার সময় যে ধরনের চশমা দেওয়া হয়) দিয়ে দেখার সময় গভীরতার বিভ্রম তৈরি করতে পারি:
text-shadow: -0.06em 0 0 red, 0.06em 0 0 cyan;
এই স্ট্রিং <canvas>-এ কনভার্ট করার সময় দুটি জিনিস লক্ষ্য করতে হবে:
- কোনও শ্যাডো-ব্লার (তৃতীয় ভ্যালু) নেই, তাই শ্যাডো রান করার কোনও কারণ নেই, কারণ fillText একই ফলাফল তৈরি করবে:
var text = "Hello world!"
ctx.fillStyle = "#000"
ctx.fillText(text, -7, 0);
ctx.fillStyle = "red"
ctx.fillText(text, 0, 0);
ctx.fillStyle = "cyan"
ctx.fillText(text, 7, 0);</pre>
<canvas>-এ EM কাজ করে না, তাই সেগুলিকে PX-এ কনভার্ট করতে হবে। আমরা DOM-এ একই ফন্ট-প্রপার্টি সহ এলিমেন্ট তৈরি করে এবং পরিমাপ করার জন্য ফর্ম্যাটে প্রস্থ সেট করে PT, PC, EM, EX, PX ইত্যাদির মধ্যে কনভার্ট করার জন্য কনভার্সন অনুপাত খুঁজে পেতে পারি; উদাহরণস্বরূপ, EM -> PX কনভার্সন ক্যাপচার করতে, আমরা "উচ্চতা: 1em" সহ DOM এলিমেন্ট পরিমাপ করব, এর ফলে offsetHeight হবে প্রতিটি EM-এ কতগুলি PX আছে।
var font = "20px sans-serif"
var d = document.createElement("span");
d.style.cssText = "font: " + font + " height: 1em; display: block"
// the value to multiply PX 's by to convert to EM 's
var EM2PX = 1 / d.offsetHeight;</pre>
আলফা-মাল্টিপ্লিকেশন প্রতিরোধ করা
আরও জটিল উদাহরণে, যেমন Line25-এ পাওয়া নিয়ন এফেক্ট, shadowBlur প্রপার্টি অবশ্যই ব্যবহার করতে হবে যাতে এফেক্ট সঠিকভাবে অনুকরণ করা যায়। যেহেতু
নিয়ন এফেক্ট একাধিক ছায়ার উপর নির্ভর করে, তাই আমরা একটি সমস্যার সম্মুখীন হই; <canvas>-এ
প্রতিটি ভেক্টর অবজেক্টের শুধুমাত্র একটি ছায়া থাকতে পারে। তাই, একাধিক শ্যাডো আঁকতে, আপনাকে টেক্সটের উপরেই টেক্সটের একাধিক ভার্সন আঁকতে হবে।
এর ফলে আলফা মাল্টিপ্লিকেশন এবং শেষ পর্যন্ত জ্যাগড এজ হয়।
আমি শ্যাডো দেখানোর সময় টেক্সট লুকানোর জন্য ctx.fillStyle = "rgba(0,0,0,0)" বা "transparent"
রান করার চেষ্টা করেছি… তবে, এই চেষ্টা ব্যর্থ হয়েছে;
কারণ শ্যাডো হল fillStyle আলফার গুণিতক, শ্যাডো কখনও fillStyle-এর চেয়ে
বেশি অস্বচ্ছ হতে পারে না।
সৌভাগ্যবশত, এর একটি সমাধান আছে, আমরা টেক্সট থেকে শ্যাডো অফসেট আঁকতে পারি, সেগুলিকে আলাদা রেখে (যাতে তারা ওভারল্যাপ না করে), এবং এর ফলে স্ক্রিনের পাশে টেক্সট লুকিয়ে রাখা যায়:
var text = "Hello world!"
var blur = 10;
var width = ctx.measureText(text).width + blur * 2;
ctx.textBaseline = "top"
ctx.shadowColor = "#000"
ctx.shadowOffsetX = width;
ctx.shadowOffsetY = 0;
ctx.shadowBlur = blur;
ctx.fillText(text, -width, 0);
টেক্সট ব্লকের চারপাশে ক্লিপিং করা
এটি কিছুটা পরিষ্কার করতে, আমরা প্রথমে fillText আঁকা থেকে বিরত রাখতে পারি (ছায়া আঁকার অনুমতি দেওয়ার সময়)
একটি ক্লিপিং পাথ যোগ করে।
টেক্সটের চারপাশে ক্লিপিং পাথ তৈরি করতে, আমাদের টেক্সটের উচ্চতা (ঐতিহাসিকভাবে প্রিন্টিং প্রেসে "M" অক্ষরের উচ্চতাকে "em-উচ্চতা" বলা হয়) এবং টেক্সটের প্রস্থ জানতে হবে।
আমরা ctx.measureText().width ব্যবহার করে প্রস্থ পেতে পারি, তবে,
ctx.measureText().height নেই।
ভাগ্যক্রমে, CSS হ্যাক-আর্ড্রির মাধ্যমে (CSS পরিমাপ ব্যবহার করে <canvas>-এর পুরনো ইমপ্লিমেন্টেশন ঠিক করার আরও উপায়ের জন্য টাইপোগ্রাফিক মেট্রিক্স
দেখুন),
আমরা একই ফন্ট-প্রপার্টি সহ <span>-এর offsetHeight পরিমাপ করে
টেক্সটের উচ্চতা খুঁজে পেতে পারি:
var d = document.createElement("span");
d.font = "20px arial"
d.textContent = "Hello world!"
var emHeight = d.offsetHeight;
সেখান থেকে, আমরা ক্লিপিং পাথ হিসেবে ব্যবহার করার জন্য একটি আয়তক্ষেত্র তৈরি করতে পারি; ডামি-শেপ সরানোর সময় "ছায়া" এনক্লোজ করা।
ctx.rect(0, 0, width, emHeight);
ctx.clip();
সবকিছু একসাথে করে এবং আমরা যেভাবে করি সেভাবে অপ্টিমাইজ করে - যদি কোনও ছায়ায় কোনও অস্পষ্টতা না থাকে, তাহলে একই এফেক্টের জন্য fillText ব্যবহার করা যেতে পারে, যা আমাদের ক্লিপিং মাস্ক সেট-আপ করা থেকে বাঁচায়:
var width = ctx.measureText(text).width;
var style = shadowStyles[text];
// add a background to the current effect
ctx.fillStyle = style.background;
ctx.fillRect(0, offsetY, ctx.canvas.width, textHeight - 1)
// parse text-shadows from css
var shadows = parseShadow(style.shadow);
// loop through the shadow collection
var n = shadows.length; while(n--) {
var shadow = shadows[n];
var totalWidth = width + shadow.blur * 2;
ctx.save();
ctx.beginPath();
ctx.rect(offsetX - shadow.blur, offsetY, offsetX + totalWidth, textHeight);
ctx.clip();
if (shadow.blur) { // just run shadow (clip text)
ctx.shadowColor = shadow.color;
ctx.shadowOffsetX = shadow.x + totalWidth;
ctx.shadowOffsetY = shadow.y;
ctx.shadowBlur = shadow.blur;
ctx.fillText(text, -totalWidth + offsetX, offsetY + metrics.top);
} else { // just run pseudo-shadow
ctx.fillStyle = shadow.color;
ctx.fillText(text, offsetX + (shadow.x||0), offsetY - (shadow.y||0) + metrics.top);
}
ctx.restore();
}
// drawing the text in the foreground
if (style.color) {
ctx.fillStyle = style.color;
ctx.fillText(text, offsetX, offsetY + metrics.top);
}
// jump to next em-line
ctx.translate(0, textHeight);
আপনি যেহেতু এই সমস্ত <canvas> কমান্ড ম্যানুয়ালি লিখতে চাইবেন না, তাই আমি ডেমো সোর্সে একটি সহজ টেক্সট-শ্যাডো পার্সার অন্তর্ভুক্ত করেছি;
এইভাবে আপনি এটিকে CSS কমান্ড দিতে পারেন এবং এটি <canvas> কমান্ড তৈরি করবে।
এখন, আমাদের <canvas> এলিমেন্টে বিভিন্ন ধরনের স্টাইল আছে যা এর সাথে যুক্ত করা যেতে পারে।
এই একই শ্যাডো-এফেক্ট যেকোনও ভেক্টর অবজেক্টে ব্যবহার করা যেতে পারে, যেমন WebFonts থেকে শুরু করে
SVG থেকে ইমপোর্ট করা জটিল আকার, জেনারেটিভ ভেক্টর আকার এবং আরও অনেক কিছু!
বিরতি (পিক্সেল-পুশিংয়ের উপর ট্যানজেন্ট)
নিবন্ধের এই অংশটি লেখার সময়, ঘনদর্শনমূলক উদাহরণটি আমাকে কৌতূহলী করে তুলেছে। <canvas> এবং সামান্য ভিন্ন দৃষ্টিকোণ থেকে তোলা দুটি ছবি ব্যবহার করে 3D-মুভি-স্ক্রিন এফেক্ট তৈরি করা কতটা কঠিন হবে? মনে হচ্ছে, খুব কঠিন নয়। নিম্নলিখিত কার্নেল প্রথম ছবির (ডেটা) লাল চ্যানেলকে দ্বিতীয় ছবির (ডেটা২) সায়ান চ্যানেলের সাথে একত্রিত করে:
data[i] = data[i] * 255 / 0xFF;
data[i+1] = 255 * data2[i+1] / 0xFF;
data[i+2] = 255 * data2[i+2] / 0xFF;
এখন, কাউকে শুধু ডাক্ট-টেপ দিয়ে দুটি iPhone তার কপালে আটকাতে হবে, একই সময়ে "ভিডিও রেকর্ড করুন" বিকল্পে ক্লিক করতে হবে এবং আমরা HTML5-এ আমাদের নিজস্ব 3D সিনেমা তৈরি করতে পারব। কেউ কি স্বেচ্ছাসেবী হতে চান?
নিয়ন-রামধনু, জেব্রা-প্রতিফলন—চেনিং এফেক্ট
<canvas>-এ একাধিক এফেক্ট চেইন করা সহজ হতে পারে, তবে globalCompositeOperation (GCO) সম্পর্কে প্রাথমিক
জ্ঞান থাকা প্রয়োজন। GIMP (বা Photoshop)-এর সাথে
অপারেশন তুলনা করতে: <canvas>-এ ১২টি GCO আছে
ডার্কার এবং লাইটার-কে লেয়ার ব্লেন্ড-মোড হিসেবে ভাবা যেতে পারে;
অন্য ১০টি অপারেশন লেয়ারে আলফা মাস্ক হিসেবে প্রয়োগ করা হয় (একটি লেয়ার
অন্য লেয়ারের পিক্সেল সরিয়ে দেয়)। globalCompositeOperation "লেয়ার"
(বা আমাদের ক্ষেত্রে, কোডের স্ট্রিং) একসাথে বেঁধে রাখে, নতুন এবং উত্তেজনাপূর্ণ উপায়ে তাদের একত্রিত করে:
globalCompositeOperation চার্ট GCO মোড দেখায়; এই চার্ট কালার-স্পেকট্রামের একটি বড় অংশ এবং আলফা ট্রান্সপারেন্সির একাধিক লেভেল ব্যবহার করে যাতে বিস্তারিতভাবে দেখা যায় যে কী আশা করা যায়। আমি Mozilla-এর globalCompositeOperation রেফারেন্স টেক্সট সংক্রান্ত বিবরণের জন্য চেক করার সাজেশন দেব। আরও রিসার্চের জন্য, আপনি Porter Duff-এর কম্পোজিটিং ডিজিটাল ইমেজ থেকে অপারেশন কীভাবে কাজ করে তা জানতে পারবেন।
আমার প্রিয় মোড হল globalCompositeOperation="lighter"। লাইটার
যোগ করা পিক্সেলগুলিকে এমনভাবে মিক্স করে যেভাবে আলো মিক্স হয়; যখন লাল, সবুজ এবং সাদা আলো
সম্পূর্ণ তীব্রতায় থাকে, তখন আমরা সাদা আলো দেখতে পাই। এটি একটি দারুণ ফিচার, বিশেষ করে যখন <canvas>-কে কম globalAlpha-তে সেট করা হয়;
এটি আরও সূক্ষ্ম কন্ট্রোল এবং মসৃণ প্রান্ত প্রদান করে। Lighter-এর অনেক ব্যবহার আছে, আমার সাম্প্রতিক প্রিয় হল
HTML5 ডেস্কটপ ব্যাকগ্রাউন্ড ক্রিয়েটর যা http://weavesilk.com/-এ পাওয়া যায়।
আমার একটি ডেমো, Breathing Galaxies (JS1k),
লাইটার মোডও ব্যবহার করে - এই দুটি উদাহরণ থেকে প্যাটার্ন আঁকা শুরু করলে
আপনি দেখতে পাবেন যে এই মোডটি কী প্রভাব ফেলে।
globalCompositeOperation ব্রাউজার হ্যান্ডলিং।
নিয়ন-রামধনু জিটার এফেক্ট
নিচের ডেমোতে, আমরা globalCompositeOperation (source-in, lighter, and darker) ব্যবহার করে
একসাথে এফেক্ট চেইন করার মাধ্যমে, জিটারড আউটলাইন সহ Photoshop-এর মতো
নিয়ন-রেইনবো-গ্লো অর্জন করতে চলেছি।
এই ডেমোটি "<canvas>-এ টেক্সট-শ্যাডো" ডেমোর একটি অগ্রগতি,
টেক্সট থেকে শ্যাডো আলাদা করার জন্য একই কৌশল ব্যবহার করা (আগের বিভাগ দেখুন):
function neonLightEffect() {
var text = "alert('"+String.fromCharCode(0x2665)+"')";
var font = "120px Futura, Helvetica, sans-serif";
var jitter = 25; // the distance of the maximum jitter
var offsetX = 30;
var offsetY = 70;
var blur = getBlurValue(100);
// save state
ctx.save();
ctx.font = font;
// calculate width + height of text-block
var metrics = getMetrics(text, font);
// create clipping mask around text-effect
ctx.rect(offsetX - blur/2, offsetY - blur/2,
offsetX + metrics.width + blur, metrics.height + blur);
ctx.clip();
// create shadow-blur to mask rainbow onto (since shadowColor doesn't accept gradients)
ctx.save();
ctx.fillStyle = "#fff";
ctx.shadowColor = "rgba(0,0,0,1)";
ctx.shadowOffsetX = metrics.width + blur;
ctx.shadowOffsetY = 0;
ctx.shadowBlur = blur;
ctx.fillText(text, -metrics.width + offsetX - blur, offsetY + metrics.top);
ctx.restore();
// create the rainbow linear-gradient
var gradient = ctx.createLinearGradient(0, 0, metrics.width, 0);
gradient.addColorStop(0, "rgba(255, 0, 0, 1)");
gradient.addColorStop(0.15, "rgba(255, 255, 0, 1)");
gradient.addColorStop(0.3, "rgba(0, 255, 0, 1)");
gradient.addColorStop(0.5, "rgba(0, 255, 255, 1)");
gradient.addColorStop(0.65, "rgba(0, 0, 255, 1)");
gradient.addColorStop(0.8, "rgba(255, 0, 255, 1)");
gradient.addColorStop(1, "rgba(255, 0, 0, 1)");
// change composite so source is applied within the shadow-blur
ctx.globalCompositeOperation = "source-atop";
// apply gradient to shadow-blur
ctx.fillStyle = gradient;
ctx.fillRect(offsetX - jitter/2, offsetY,
metrics.width + offsetX, metrics.height + offsetY);
// change composite to mix as light
ctx.globalCompositeOperation = "lighter";
// multiply the layer
ctx.globalAlpha = 0.7
ctx.drawImage(ctx.canvas, 0, 0);
ctx.drawImage(ctx.canvas, 0, 0);
ctx.globalAlpha = 1
// draw white-text ontop of glow
ctx.fillStyle = "rgba(255,255,255,0.95)";
ctx.fillText(text, offsetX, offsetY + metrics.top);
// created jittered stroke
ctx.lineWidth = 0.80;
ctx.strokeStyle = "rgba(255,255,255,0.25)";
var i = 10; while(i--) {
var left = jitter / 2 - Math.random() * jitter;
var top = jitter / 2 - Math.random() * jitter;
ctx.strokeText(text, left + offsetX, top + offsetY + metrics.top);
}
ctx.strokeStyle = "rgba(0,0,0,0.20)";
ctx.strokeText(text, offsetX, offsetY + metrics.top);
ctx.restore();
};
জেব্রা রিফ্লেকশন এফেক্ট
জেব্রা রিফ্লেকশন এফেক্টটি WebDesignerWall-এর CSS-এর মাধ্যমে আপনার পৃষ্ঠা কীভাবে আরও আকর্ষণীয় করে তুলবেন সেই সংক্রান্ত চমৎকার রিসোর্স থেকে অনুপ্রাণিত। এটি আইডিয়াটিকে আরও একটু এগিয়ে নিয়ে যায়, টেক্সটের জন্য একটি "প্রতিফলন" তৈরি করে - যেমন আপনি iTunes-এ দেখতে পারেন। এই এফেক্ট fillColor (সাদা), createPattern (zebra.png) এবং linearGradient (shine) একত্রিত করে; এটি প্রতিটি ভেক্টর অবজেক্টে একাধিক ফিল টাইপ প্রয়োগ করার ক্ষমতাকে ব্যাখ্যা করে:
function sleekZebraEffect() {
// inspired by - http://www.webdesignerwall.com/demo/css-gradient-text/
var text = "Sleek Zebra...";
var font = "100px Futura, Helvetica, sans-serif";
// save state
ctx.save();
ctx.font = font;
// getMetrics calculates:
// width + height of text-block
// top + middle + bottom baseline
var metrics = getMetrics(text, font);
var offsetRefectionY = -20;
var offsetY = 70;
var offsetX = 60;
// throwing a linear-gradient in to shine up the text
var gradient = ctx.createLinearGradient(0, offsetY, 0, metrics.height + offsetY);
gradient.addColorStop(0.1, '#000');
gradient.addColorStop(0.35, '#fff');
gradient.addColorStop(0.65, '#fff');
gradient.addColorStop(1.0, '#000');
ctx.fillStyle = gradient
ctx.fillText(text, offsetX, offsetY + metrics.top);
// draw reflected text
ctx.save();
ctx.globalCompositeOperation = "source-over";
ctx.translate(0, metrics.height + offsetRefectionY)
ctx.scale(1, -1);
ctx.font = font;
ctx.fillStyle = "#fff";
ctx.fillText(text, offsetX, -metrics.height - offsetY + metrics.top);
ctx.scale(1, -1);
// cut the gradient out of the reflected text
ctx.globalCompositeOperation = "destination-out";
var gradient = ctx.createLinearGradient(0, offsetY, 0, metrics.height + offsetY);
gradient.addColorStop(0.0, 'rgba(0,0,0,0.65)');
gradient.addColorStop(1.0, '#000');
ctx.fillStyle = gradient;
ctx.fillRect(offsetX, offsetY, metrics.width, metrics.height);
// restore back to original transform state
ctx.restore();
// using source-atop to allow the transparent .png to show through to the gradient
ctx.globalCompositeOperation = "source-atop";
// creating pattern from <image> sourced.
ctx.fillStyle = ctx.createPattern(image, 'repeat');
// fill the height of two em-boxes, to encompass both normal and reflected state
ctx.fillRect(offsetX, offsetY, metrics.width, metrics.height * 2);
ctx.restore();
};
ক্যানভাসে ইনার/আউটার শ্যাডো
<canvas> স্পেসিফিকেশনে "ইনার" বনাম "আউটার" শ্যাডোর বিষয়ে কিছু বলা নেই। আসলে, প্রথমবার দেখলে, আপনি হয়ত ভাববেন যে
"ইনার" শ্যাডো কাজ করে না। এটি সঠিক নয়।
এটি চালু করা একটু কঠিন ;) F1LT3R-এর সাম্প্রতিক পোস্টে যেমন প্রস্তাব করা হয়েছে,
আপনি ঘড়ির কাঁটার দিকে বনাম ঘড়ির কাঁটার বিপরীত দিকে
উইন্ডিং নিয়মের অনন্য প্রপার্টি ব্যবহার করে ইনার-শ্যাডো তৈরি করতে পারবেন। এটি করতে, আপনি কন্টেনার আয়তক্ষেত্র অঙ্কন করে একটি "ইনার-শ্যাডো" তৈরি করেন এবং তারপরে, বিপরীত উইন্ডিং নিয়ম ব্যবহার করে, একটি কাটআউট আকৃতি আঁকেন - আকৃতির বিপরীত তৈরি করে।
নিম্নলিখিত উদাহরণটি inner-shadow এবং fillStyle-কে একই সাথে color+gradient+pattern-এর মাধ্যমে স্টাইলাইজ করার অনুমতি দেয়। আপনি আলাদাভাবে প্যাটার্ন রোটেশন নির্দিষ্ট করতে পারেন; লক্ষ্য করুন যে জেব্রা স্ট্রাইপগুলি এখন একে অপরের সাথে লম্বভাবে রয়েছে। বাউন্ডিং-বক্সের সাইজের ক্লিপিং মাস্ক ব্যবহার করা হয়, যার ফলে কাট-আউট শেপ এনক্লোজ করার জন্য বিশাল বড় কন্টেনারের প্রয়োজন হয় না - ছায়ার অপ্রয়োজনীয় অংশ প্রসেস করা আটকানোর মাধ্যমে স্পিড উন্নত করা হয়।
function innerShadow() {
function drawShape() { // draw anti-clockwise
ctx.arc(0, 0, 100, 0, Math.PI * 2, true); // Outer circle
ctx.moveTo(70, 0);
ctx.arc(0, 0, 70, 0, Math.PI, false); // Mouth
ctx.moveTo(-20, -20);
ctx.arc(30, -30, 10, 0, Math.PI * 2, false); // Left eye
ctx.moveTo(140, 70);
ctx.arc(-20, -30, 10, 0, Math.PI * 2, false); // Right eye
};
var width = 200;
var offset = width + 50;
var innerColor = "rgba(0,0,0,1)";
var outerColor = "rgba(0,0,0,1)";
ctx.translate(150, 170);
// apply inner-shadow
ctx.save();
ctx.fillStyle = "#000";
ctx.shadowColor = innerColor;
ctx.shadowBlur = getBlurValue(120);
ctx.shadowOffsetX = -15;
ctx.shadowOffsetY = 15;
// create clipping path (around blur + shape, preventing outer-rect blurring)
ctx.beginPath();
ctx.rect(-offset/2, -offset/2, offset, offset);
ctx.clip();
// apply inner-shadow (w/ clockwise vs. anti-clockwise cutout)
ctx.beginPath();
ctx.rect(-offset/2, -offset/2, offset, offset);
drawShape();
ctx.fill();
ctx.restore();
// cutout temporary rectangle used to create inner-shadow
ctx.globalCompositeOperation = "destination-out";
ctx.fill();
// prepare vector paths
ctx.beginPath();
drawShape();
// apply fill-gradient to inner-shadow
ctx.save();
ctx.globalCompositeOperation = "source-in";
var gradient = ctx.createLinearGradient(-offset/2, 0, offset/2, 0);
gradient.addColorStop(0.3, '#ff0');
gradient.addColorStop(0.7, '#f00');
ctx.fillStyle = gradient;
ctx.fill();
// apply fill-pattern to inner-shadow
ctx.globalCompositeOperation = "source-atop";
ctx.globalAlpha = 1;
ctx.rotate(0.9);
ctx.fillStyle = ctx.createPattern(image, 'repeat');
ctx.fill();
ctx.restore();
// apply fill-gradient
ctx.save();
ctx.globalCompositeOperation = "destination-over";
var gradient = ctx.createLinearGradient(-offset/2, -offset/2, offset/2, offset/2);
gradient.addColorStop(0.1, '#f00');
gradient.addColorStop(0.5, 'rgba(255,255,0,1)');
gradient.addColorStop(1.0, '#00f');
ctx.fillStyle = gradient
ctx.fill();
// apply fill-pattern
ctx.globalCompositeOperation = "source-atop";
ctx.globalAlpha = 0.2;
ctx.rotate(-0.4);
ctx.fillStyle = ctx.createPattern(image, 'repeat');
ctx.fill();
ctx.restore();
// apply outer-shadow (color-only without temporary layer)
ctx.globalCompositeOperation = "destination-over";
ctx.shadowColor = outerColor;
ctx.shadowBlur = 40;
ctx.shadowOffsetX = 15;
ctx.shadowOffsetY = 10;
ctx.fillStyle = "#fff";
ctx.fill();
};
এইসব উদাহরণ থেকে আপনি দেখতে পাবেন যে globalCompositeOperation ব্যবহার করে আমরা এফেক্টগুলিকে একসাথে চেইন করতে পারি, আরও বিস্তারিত এফেক্ট তৈরি করতে পারি (মাস্কিং এবং ব্লেন্ডিং ব্যবহার করে)। স্ক্রিনটি আপনার ঝিনুক ;)
Spaceage—জেনারেটিভ এফেক্ট
<canvas>-এ, ইউনিকোড অক্ষর 0x2708 থেকে শুরু করে:
…এই শেড করা উদাহরণে:
…lineWidth (0.25) পাতলা করে ctx.strokeText()-এ একাধিক কল করার মাধ্যমে এটি করা যেতে পারে,
একই সাথে x-অফসেট ও আলফা ধীরে ধীরে কমানো; আমাদের ভেক্টর এলিমেন্টকে মোশনের অনুভূতি প্রদান করা।
একটি সাইন/কোসাইন ওয়েভে এলিমেন্টের XY পজিশন ম্যাপ করে এবং HSL প্রপার্টি ব্যবহার করে রঙের মাধ্যমে সাইকেল করে, আমরা আরও আকর্ষণীয় এফেক্ট তৈরি করতে পারি, যেমন এই "বায়োহ্যাজার্ড" উদাহরণ:
HSL: হিউ, স্যাচুরেশন, আলো (১৯৭৮)
HSL হল CSS3 স্পেক্সে নতুন কাজ করা ফর্ম্যাট। HEX যেখানে কম্পিউটারের জন্য ডিজাইন করা হয়েছে, সেখানে HSL ডিজাইন করা হয়েছে মানুষের-পঠনযোগ্য হিসেবে।
HSL-এর সুবিধা ব্যাখ্যা করা; কালার-স্পেকট্রামের মাধ্যমে সাইকেল করতে, আমরা শুধু ৩৬০ থেকে "হিউ" বাড়াব; হিউকে সিলিন্ড্রিক ফ্যাশনে স্পেকট্রামে ম্যাপ করা হয়। রঙ কতটা গাঢ়/হালকা হবে তা লাইটনেস নিয়ন্ত্রণ করে; ০% কালো পিক্সেল এবং ১০০% সাদা পিক্সেল নির্দেশ করে। স্যাচুরেশন, রঙ কতটা উজ্জ্বল বা প্রাণবন্ত হবে তা নিয়ন্ত্রণ করে; ০% স্যাচুরেশন ব্যবহার করে ধূসর রঙ তৈরি করা হয় এবং ১০০% ভ্যালু ব্যবহার করে প্রাণবন্ত রঙ তৈরি করা হয়।
HSL যেহেতু সাম্প্রতিক স্ট্যান্ডার্ড, তাই আপনি হয়ত পুরনো ব্রাউজারগুলিকেও সাপোর্ট করা চালিয়ে যেতে চাইবেন, যা কালার-স্পেস কনভার্সনের মাধ্যমে সম্ভব। নিম্নলিখিত কোডটি একটি HSL অবজেক্ট { H: 360, S: 100, L: 100} গ্রহণ করে এবং একটি RGB অবজেক্ট { R: 255, G: 255, B: 255 } আউটপুট দেয়। সেখান থেকে, আপনি নিজের rgb বা rgba স্ট্রিং তৈরি করতে সেই ভ্যালুগুলি ব্যবহার করতে পারেন। আরও বিস্তারিত তথ্যের জন্য, Wikipedia-তে HSL সম্পর্কে তথ্যপূর্ণ নিবন্ধটি দেখুন।
// HSL (1978) = H: Hue / S: Saturation / L: Lightness
HSL_RGB = function (o) { // { H: 0-360, S: 0-100, L: 0-100 }
var H = o.H / 360,
S = o.S / 100,
L = o.L / 100,
R, G, B, _1, _2;
function Hue_2_RGB(v1, v2, vH) {
if (vH < 0) vH += 1;
if (vH > 1) vH -= 1;
if ((6 * vH) < 1) return v1 + (v2 - v1) * 6 * vH;
if ((2 * vH) < 1) return v2;
if ((3 * vH) < 2) return v1 + (v2 - v1) * ((2 / 3) - vH) * 6;
return v1;
}
if (S == 0) { // HSL from 0 to 1
R = L * 255;
G = L * 255;
B = L * 255;
} else {
if (L < 0.5) {
_2 = L * (1 + S);
} else {
_2 = (L + S) - (S * L);
}
_1 = 2 * L - _2;
R = 255 * Hue_2_RGB(_1, _2, H + (1 / 3));
G = 255 * Hue_2_RGB(_1, _2, H);
B = 255 * Hue_2_RGB(_1, _2, H - (1 / 3));
}
return {
R: R,
G: G,
B: B
};
};
requestAnimationFrame-এর সাহায্যে অ্যানিমেশন তৈরি করা
অতীতে, জাভাস্ক্রিপ্টে অ্যানিমেশন তৈরি করার জন্য, দুটি বিকল্প ছিল; setTimeout,
এবং setInterval।
window.requestAnimationFrame, হল নতুন স্ট্যান্ডার্ড যা
দুটিকেই রিপ্লেস করে; উপলভ্য রিসোর্সের উপর ভিত্তি করে ব্রাউজারকে অ্যানিমেশন রেগুলেট করার অনুমতি দিয়ে
বিশ্বের বিদ্যুৎ (এবং আপনার কম্পিউটারের কিছু হার্টবিট) সাশ্রয় করে।
কিছু গুরুত্বপূর্ণ ফিচারের মধ্যে রয়েছে:
- ব্যবহারকারী ফ্রেম থেকে বেরিয়ে গেলে, অপ্রয়োজনীয় রিসোর্স ব্যবহার করা বন্ধ করতে, অ্যানিমেশন স্লো হয়ে যেতে পারে বা সম্পূর্ণ বন্ধ হয়ে যেতে পারে।
- ফ্রেম-রেটের ক্ষেত্রে 60FPS-এর সীমা আছে। এর কারণ হল এটি মানুষের লক্ষ্য করার লেভেলের থেকে অনেক বেশি (বেশিরভাগ মানুষ ৩০FPS-এ অ্যানিমেশনকে "ফ্লুইড" হিসেবে দেখেন)।
লেখার সময়, requestAnimationFrame ব্যবহার করার জন্য ভেন্ডর-নির্দিষ্ট প্রিফিক্স প্রয়োজন।
পল আইরিশ একটি শিম লেয়ার তৈরি করেছেন যাতে ক্রস-ভেন্ডর সাপোর্ট আছে, স্মার্ট অ্যানিমেটিংয়ের জন্য
requestAnimationFrame-এ:
// shim layer with setTimeout fallback
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(/* function */ callback, /* DOMElement */ element){
window.setTimeout(callback, 1000 / 60);
};
})();
আরও একটু এগিয়ে, আরও উচ্চাকাঙ্ক্ষী ব্যক্তিরা এটিকে requestAnimationFrame.js-এর মতো পলি-ফিলের সাথে একসাথে বেঁধে রাখতে পারেন (কিছু ফিচার নিয়ে কাজ করতে হবে) যা এই নতুন স্ট্যান্ডার্ডে পরিবর্তন করার সময়, পুরনো ব্রাউজারগুলিকে আরও বেশি পরিমাণে সাপোর্ট করবে।
(function animate() {
var i = 50;
while(i--) {
if (n > endpos) return;
n += definition;
ctx.globalAlpha = (0.5 - (n + startpos) / endpos) * alpha;
if (doColorCycle) {
hue = n + color;
ctx.strokeStyle = "hsl(" + (hue % 360) + ",99%,50%)"; // iterate hue
}
var x = cos(n / cosdiv) * n * cosmult; // cosine
var y = sin(n / sindiv) * n * sinmult; // sin
ctx.strokeText(text, x + xoffset, y + yoffset); // draw rainbow text
}
timeout = window.requestAnimationFrame(animate, 0);
})();
সোর্স-কোড
ব্রাউজার ভেন্ডর-স্ফিয়ার জুড়ে সহায়তা থাকায়, <canvas>-এর ভবিষ্যৎ নিয়ে কোনও প্রশ্নই ওঠে না।
PhoneGap ব্যবহার করে এটি iPhone/Android/ডেস্কটপ এক্সিকিউটেবলে পোর্ট করা যেতে পারে, অথবা