मेरा बैकग्राउंड
<canvas>
के बारे में मुझे 2006 में पता चला, जब Firefox v2.0 रिलीज़ हुआ था. Ajaxian के बारे में दिए गए एक लेख ने ट्रांसफ़ॉर्मेशन मैट्रिक्स के बारे में जानकारी दी है. साथ ही, इसने मुझे अपना पहला <canvas>
वेब-ऐप्लिकेशन बनाने की प्रेरणा दी; कलर स्फ़ीयर (2007). इस दौरान, मुझे रंगों और ग्राफ़िक प्राइमिटिव की दुनिया से जुड़ने का मौका मिला. इस अनुभव से मुझे Sketchpad (2007-2008) बनाने की प्रेरणा मिली. इस ऐप्लिकेशन को ब्राउज़र में "Paint से बेहतर" बनाने के लिए बनाया गया था.
इन प्रयोगों की वजह से ही, मेरे लंबे समय से दोस्त चार्ल्स प्रिचर्ड के साथ मिलकर 'मगटग' नाम का एक स्टार्टअप शुरू हुआ. हम HTML5 <canvas>
में Darkroom बना रहे हैं. डार्करूम फ़ोटो शेयर करने वाला एक ऐसा ऐप्लिकेशन है जो नुकसान नहीं पहुंचाता. यह पिक्सल-आधारित फ़िल्टर की सुविधाओं के साथ, वेक्टर आधारित टाइपोग्राफ़ी और ड्रॉइंग के साथ काम करता है.
परिचय
<canvas>
की मदद से, JavaScript प्रोग्रामर अपनी स्क्रीन पर रंगों,
वेक्टर, और पिक्सल पर पूरा कंट्रोल पा सकते हैं. ये तीनों चीज़ें, मॉनिटर के विज़ुअल मेकअप का हिस्सा हैं.
यहां दिए गए उदाहरणों में, <canvas>
के उस हिस्से के बारे में बताया गया है जिस पर अब तक ज़्यादा ध्यान नहीं दिया गया है. इसमें टेक्स्ट-इफ़ेक्ट बनाने के बारे में बताया गया है. <canvas>
में, टेक्स्ट पर कई तरह के असर डाले जा सकते हैं. इनमें से कुछ ही असर के बारे में इन डेमो में बताया गया है. हालांकि, हमने इस लेख में "टेक्स्ट" के बारे में बात की है, लेकिन ये तरीके किसी भी वेक्टर ऑब्जेक्ट पर लागू किए जा सकते हैं; गेम और दूसरे ऐप्लिकेशन में दिलचस्प विज़ुअल बनाना:
<canvas>
में टेक्स्ट-शैडो.- सीएसएस की तरह
<canvas>
में टेक्स्ट-इफ़ेक्ट, क्लिपिंग मास्क बनाना,<canvas>
में मेट्रिक ढूंढना, और शैडो प्रॉपर्टी का इस्तेमाल करना. - नीयन-रेनबो, ज़ेबरा-रिफ़्लेक्शन - चेनिंग इफ़ेक्ट.
<canvas>
में Photoshop की तरह के टेक्स्ट-इफ़ेक्ट, जैसे कि GlobalCompositeOperation, createलीनियरग्रेडिएंट, बनाने के पैटर्न का इस्तेमाल करने के उदाहरण.<canvas>
में अंदर और बाहर की परछाई- किसी किसी ऐसी सुविधा के बारे में बताना जिसके बारे में पहले से जानकारी है. ड्रॉप-शैडो (इनर शैडो) को उलटा बनाने के लिए, घड़ी की सुई की दिशा में बनाम घड़ी की विपरीत दिशा में विंग का इस्तेमाल करना.
- स्पेसेज - जनरेटिव इफ़ेक्ट की सुविधा.
- जनरेटिव एआई का इस्तेमाल करके टेक्स्ट-इफ़ेक्ट. इसमें hsl() कलर-साइकलिंग और
window.requestAnimationFrame
का इस्तेमाल करके, मोशन की भावना पैदा की गई है.
<canvas>
में कैनवस में टेक्स्ट-शैडो
सीएसएस3 के स्पेसिफ़िकेशन में, बॉर्डर-रेडियस, वेब-ग्रेडिएंट वगैरह के साथ-साथ, शेडो बनाने की सुविधा भी शामिल है. यह मेरी पसंदीदा सुविधाओं में से एक है. सीएसएस और <canvas>
शैडो के बीच के अंतर को समझना ज़रूरी है. खास तौर पर:
सीएसएस दो तरीकों का इस्तेमाल करती है. 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; // पूर्णांक
- टेक्स्ट के हिसाब से, शैडो की हॉरिज़ॉन्टल दूरी.
- ctx.shadowOffsetY = 0; // integer
- टेक्स्ट के हिसाब से, शैडो की वर्टिकल दूरी.
- ctx.shadowBlur = 10; // integer
- फ़ोटो के गहरे हिस्से को धुंधला करने वाला इफ़ेक्ट जितना बड़ा होगा, इमेज उतनी ही ज़्यादा ब्लर होगी.
शुरू करने के लिए, आइए देखें कि <canvas>
, सीएसएस इफ़ेक्ट को कैसे एमुलेट कर सकता है.
Google Images पर "css text-shadow" खोजने पर, हमें कुछ बेहतरीन उदाहरण मिले, जिनसे हमने प्रेरणा ली. इनमें Line25, स्टीरियोस्कोपिक, और शैडो 3D शामिल हैं.
स्टीरियोस्कोपिक 3D इफ़ेक्ट (ज़्यादा जानकारी के लिए ऐनाग्लिफ़ इमेज देखें), कोड की एक आसान लाइन का एक उदाहरण है. सीएसएस की इस लाइन की मदद से, 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>
में ईएम काम नहीं करते. इसलिए, उन्हें PX में बदलना होगा. हम PT, PC, EM, EX, PX वगैरह के बीच बदलाव करने के लिए, कन्वर्ज़न रेशियो का पता लगा सकते हैं. इसके लिए, हमें DOM में एक ही फ़ॉन्ट-प्रॉपर्टी वाला एलिमेंट बनाना होगा और चौड़ाई को उस फ़ॉर्मैट पर सेट करना होगा जिसे मेज़र करना है. उदाहरण के लिए, EM -> PX कन्वर्ज़न को कैप्चर करने के लिए, हम "height: 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 alpha का गुणा होता है, इसलिए शैडो कभी भी 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 को पहली जगह पर खींचने से रोका जा सकता है. हालांकि, इस दौरान शैडो को खींचा जा सकता है.
टेक्स्ट के चारों ओर क्लिपिंग पाथ बनाने के लिए, हमें टेक्स्ट की ऊंचाई और चौड़ाई के बारे में पता होना चाहिए. टेक्स्ट की ऊंचाई को "em-height" कहा जाता है. यह प्रिंटिंग प्रेस पर मौजूद अक्षर "M" की ऊंचाई होती है.
ctx.measureText().width
का इस्तेमाल करके चौड़ाई का पता लगाया जा सकता है. हालांकि,
ctx.measureText().height
मौजूद नहीं है.
सौभाग्य से, सीएसएस हैक-आर्ड्री (सीएसएस मेज़रमेंट का इस्तेमाल करके, <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>
निर्देशों को मैन्युअल तरीके से डालने की ज़रूरत नहीं है. इसलिए, मैंने डेमो सोर्स में एक आसान टेक्स्ट-शैडो पार्सर शामिल किया है. इस तरह, इसमें सीएसएस निर्देश डालकर <canvas>
निर्देश जनरेट किए जा सकते हैं.
अब हमारे <canvas>
एलिमेंट में अलग-अलग स्टाइल का इस्तेमाल किया जा सकता है.
इन छाया-इफ़ेक्ट का इस्तेमाल, वेब फ़ॉन्ट से लेकर, SVG से इंपोर्ट किए गए जटिल आकार, जनरेटिव वेक्टर आकार वगैरह जैसे किसी भी वेक्टर ऑब्जेक्ट पर किया जा सकता है!
इंटरमिशन (पिक्सल-पुशिंग पर टेंगेंट)
लेख के इस सेक्शन को लिखते समय, स्टीरियोस्कोपिक उदाहरण ने मेरी दिलचस्पी बढ़ाई. <canvas>
और थोड़े अलग नज़रियों से ली गई दो इमेज का इस्तेमाल करके, 3D-मूवी-स्क्रीन इफ़ेक्ट बनाने में कितनी मुश्किल होगी? ऐसा लगता है कि यह बहुत मुश्किल नहीं है. नीचे दिया गया कर्नेल, पहली इमेज (data) के रेड चैनल को दूसरी इमेज (data2) के सियान चैनल के साथ जोड़ता है:
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 (या फ़ोटोशॉप) के ऑपरेशन की तुलना करने के लिए: <canvas>
गहरे में 12 जीसीओ हैं और हलके को लेयर ब्लेंड-मोड के तौर पर देखा जा सकता है; बाकी 10 ऑपरेशन, लेयर पर अल्फा मास्क के तौर पर लागू किए जाते हैं (एक लेयर, दूसरी लेयर के पिक्सल हटा देती है). GlobalComposite Operation "लेयर" (या हमारे मामले में, कोड की स्ट्रिंग) को नए और दिलचस्प तरीकों से एक साथ जोड़ता है:
globalCompositeOperation चार्ट में, GCO मोड के काम करने का तरीका दिखाया गया है. इस चार्ट में, रंग-स्पेक्ट्रम के बड़े हिस्से और अल्फा ट्रांसपेरेंसी के कई लेवल का इस्तेमाल किया जाता है, ताकि यह पता चल सके कि क्या उम्मीद की जा सकती है. हमारा सुझाव है कि टेक्स्ट के ब्यौरे के लिए, Mozilla का globalCompositeOperation रेफ़रंस देखें. ज़्यादा जानकारी के लिए, Porter Duff की Compositing Digital Images में जाकर, जानें कि ऑपरेशन कैसे काम करता है.
मेरा पसंदीदा मोड globalCompositeOperation="lighter" है. लाइटर, जोड़े गए पिक्सल को उसी तरह से मिक्स करता है जिस तरह से लाइट मिक्स होती है. जब लाल, हरा, और सफ़ेद लाइट पूरी तरह से चालू होती है, तो हमें सफ़ेद लाइट दिखती है. यह एक रोमांचक सुविधा है, जिसे इस्तेमाल करना चाहिए. खास तौर पर तब, जब <canvas>
, लो ग्लोबल ऐल्फ़ा पर सेट हो. इससे बेहतर कंट्रोल और बेहतर किनारे मिलते हैं. इस लाइटर का कई तरह से इस्तेमाल किया गया है. मुझे हाल ही में एचटीएमएल5 के बैकग्राउंड क्रिएटर के तौर पर
http://weavesilk.com/ पर जाना पसंद है.
मेरे एक डेमो, ब्रीदिंग गैलेक्सीज़ (JS1k) में भी हल्के रंग वाले मोड का इस्तेमाल किया गया है - जिसमें इन दो उदाहरणों से बनाए गए पैटर्न का इस्तेमाल किया गया है. इससे आपको पता चलता है कि इस मोड से क्या असर पड़ता है.
globalCompositeOperation ब्राउज़र हैंडल करने की सुविधा.
नीऑन-इंद्रधनुष वाला जिटर इफ़ेक्ट
नीचे दिए गए डेमो में, हम Photoshop जैसा
नेऑन-रेनबो-ग्लो इफ़ेक्ट के साथ, ज़ीटर की गई आउटलाइन का इस्तेमाल करेंगे. इसके लिए, हम इफ़ेक्ट को एक साथ जोड़ने के लिए, globalCompositeOperation (सोर्स-इन, लाइटर, और डार्कर) का इस्तेमाल करेंगे.
यह डेमो, "<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 के बेहतरीन संसाधन से प्रेरित है. इस संसाधन में, सीएसएस की मदद से अपने पेज को शानदार बनाने का तरीका बताया गया है. इस सुविधा की मदद से, टेक्स्ट के लिए "रिफ़्लेक्शन" बनाया जा सकता है. यह iTunes में दिखने वाले टेक्स्ट जैसा ही होता है. इस इफ़ेक्ट में fillColor (white), 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 की हाल ही की पोस्ट में बताया गया है, घड़ी की दिशा में बनाम घड़ी की विपरीत दिशा में विंडिंग नियमों की खास प्रॉपर्टी का इस्तेमाल करके इनर शैडो बनाया जा सकता है. ऐसा करने के लिए, आप कंटेनर रेक्टैंगल बनाकर "इनर-शैडो" बनाते हैं और
विपरेलू नियमों का इस्तेमाल करते हुए, एक कटआउट आकार बनाते हैं - आकार का व्युत्क्रम बनाते हैं.
यहां दिए गए उदाहरण में, इनर-शैडो और fillStyle को एक साथ रंग+ग्रेडिएंट+पैटर्न के साथ स्टाइल किया जा सकता है. पैटर्न के घूमने की दिशा को अलग-अलग तय किया जा सकता है. ध्यान दें कि ज़ेब्रा की धारियां अब एक-दूसरे के लंबवत हैं. बाउंडिंग बॉक्स के साइज़ के क्लिपिंग मास्क का इस्तेमाल किया जाता है. इससे, कटआउट के आकार को कवर करने के लिए, बहुत बड़े कंटेनर की ज़रूरत नहीं होती. साथ ही, छाया के ग़ैर-ज़रूरी हिस्सों को प्रोसेस होने से रोककर, प्रोसेसिंग की स्पीड को बेहतर बनाया जाता है.
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 का इस्तेमाल करके, एक साथ कई इफ़ेक्ट इस्तेमाल किए जा सकते हैं. इससे, मास्किंग और ब्लेंडिंग का इस्तेमाल करके ज़्यादा बेहतर इफ़ेक्ट बनाए जा सकते हैं. स्क्रीन पर अपनी पसंद के मुताबिक बदलाव करें ;)
स्पेसेज—जनरेटिव इफ़ेक्ट
<canvas>
में, यूनिकोड वर्ण 0x2708 से:
…इस उदाहरण में शेड किया गया है:
…इस तरह की इफ़ेक्ट पाने के लिए, ctx.strokeText()
को कई बार कॉल करें. साथ ही, लाइन की चौड़ाई को कम करके (0.25) और x-ऑफ़सेट और अल्फा को धीरे-धीरे कम करें. इससे हमारे वेक्टर एलिमेंट को मोशन का एहसास मिलेगा.
एलिमेंट की XY पोज़िशन को साइन/कोसाइन वेव पर मैप करके और एचएसएल प्रॉपर्टी का इस्तेमाल करके रंगों को साइकल करके, ज़्यादा दिलचस्प इफ़ेक्ट बनाए जा सकते हैं. जैसे, "बायोहज़र्ड" वाला यह उदाहरण:
एचएसएल: ह्यू, सैचुरेशन, लाइटनेस (1978)
सीएसएस3 के स्पेसिफ़िकेशन में, एचएसएल एक नया फ़ॉर्मैट है. हेक्स को कंप्यूटर के लिए डिज़ाइन किया गया था, जबकि एचएसएल को लोगों के लिए डिज़ाइन किया गया है.
एचएसएल के इस्तेमाल को आसानी से समझाने के लिए; कलर-स्पेक्ट्रम में साइकल करने के लिए, हम सिर्फ़ "ह्यू" को 360 से बढ़ाएंगे; ह्यू को सिलेंडर के तौर पर स्पेक्ट्रम पर मैप किया जाता है. लाइटनेस से यह कंट्रोल होता है कि रंग कितना गहरा/हल्का है; 0% का मतलब है कि पिक्सल का रंग काला है, जबकि 100% का मतलब है कि पिक्सल का रंग सफ़ेद है. संतृप्ति से यह तय होता है कि रंग कितना चमकीला या भड़कीला है. ग्रे रंग 0% संतृप्ति के साथ बनाए जाते हैं और चटक रंग 100% संतृप्ति के साथ बनाए जाते हैं.
एचएसएल एक नया स्टैंडर्ड है. इसलिए, हो सकता है कि आप पुराने ब्राउज़र के साथ काम करना जारी रखना चाहें. ऐसा कलर-स्पेस कन्वर्ज़न की मदद से किया जा सकता है. यहां दिया गया कोड, HSL ऑब्जेक्ट { H: 360, S: 100, L: 100} को स्वीकार करता है और आरजीबी ऑब्जेक्ट { R: 255, G: 255, B: 255 } को दिखाता है. यहां से, rgb या rgba स्ट्रिंग बनाने के लिए उन वैल्यू का इस्तेमाल किया जा सकता है. ज़्यादा जानकारी के लिए, HSL के बारे में Wikipedia का अहम लेख पढ़ें.
// 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 का इस्तेमाल करके ऐनिमेशन बनाना
पहले JavaScript में ऐनिमेशन बनाने के दो विकल्प थे: setTimeout
और setInterval
.
window.requestAnimationFrame
, दोनों को बदलने के लिए नया स्टैंडर्ड है. इससे दुनिया भर में बिजली की बचत होती है. साथ ही, आपके कंप्यूटर को भी कुछ समय मिलता है. ऐसा इसलिए होता है, क्योंकि ब्राउज़र उपलब्ध संसाधनों के आधार पर ऐनिमेशन को कंट्रोल करता है.
इसकी कुछ ज़रूरी सुविधाएं ये हैं:
- जब कोई उपयोगकर्ता फ़्रेम से बाहर निकलता है, तो ऐनिमेशन की स्पीड धीमी हो सकती है या वह पूरी तरह से रुक सकता है. ऐसा, ज़रूरत के मुताबिक संसाधनों के इस्तेमाल को रोकने के लिए किया जाता है.
- फ़्रेम रेट की सीमा 60FPS है. इसकी वजह यह है कि यह फ़्रेम रेट, लोगों के लिए ज़रूरी फ़्रेम रेट से काफ़ी ज़्यादा है. ज़्यादातर लोगों को 30 एफ़पीएस पर ऐनिमेशन "फ़्लूइड" दिखता है.
फ़िलहाल, 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/डेस्कटॉप के लिए बने एक्सीक्यूटेबल में पोर्ट किया जा सकता है या