پس زمینه من
<canvas>
در سال 2006 زمانی که فایرفاکس نسخه 2.0 منتشر شد، به آگاهی من وارد شد. مقاله ای در Ajaxian که ماتریس تبدیل را توصیف می کند، الهام بخش من شد تا اولین برنامه وب <canvas>
خود را ایجاد کنم. کره رنگ (2007). که مرا در دنیای رنگها و گرافیکهای اولیه غرق کرد. الهام بخش ایجاد Sketchpad (2007-2008) در تلاش برای قرار دادن یک برنامه کاربردی "بهتر از Paint" در مرورگر. این آزمایشها در نهایت منجر به ایجاد استارتآپ Mugtug با دوست دیرینهام چارلز پریچارد شد. ما در حال توسعه Darkroom در HTML5 <canvas>
هستیم. Darkroom یک برنامه به اشتراک گذاری عکس غیر مخرب است که قدرت فیلترهای مبتنی بر پیکسل را با تایپوگرافی و طراحی مبتنی بر برداری ترکیب می کند.
مقدمه
<canvas>
برنامهنویسان جاوا اسکریپت را با کنترل کامل رنگها ، بردارها و پیکسلهای روی صفحه نمایش خود - آرایش بصری مانیتور را به ارمغان میآورد.
مثالهای زیر به یک ناحیه در <canvas>
میپردازند که توجه زیادی را به خود جلب نکرده است. ایجاد جلوه های متنی تنوع جلوههای متنی که میتوان در <canvas>
ایجاد کرد به همان اندازه که میتوانید تصور کنید گسترده است - این نسخههای نمایشی بخشی فرعی از آنچه ممکن است را پوشش میدهند. اگرچه در این مقاله با "متن" سروکار داریم، روش ها را می توان برای هر شیء برداری اعمال کرد. ایجاد تصاویری هیجان انگیز در بازی ها و برنامه های کاربردی دیگر:
- Text-Shadows در
<canvas>
. - جلوههای متنی CSS مانند در
<canvas>
ایجاد ماسکهای بریده، یافتن معیارها در<canvas>
و استفاده از ویژگی shadow. - رنگین کمان نئون، انعکاس گورخر - جلوه های زنجیره ای.
- جلوه های متنی فتوشاپ مانند در
<canvas>
نمونه هایی از استفاده از globalCompositeOperation، createLinearGradient، createPattern. - سایه های درونی و بیرونی در
<canvas>
- فاش کردن یک ویژگی کمتر شناخته شده ؛ با استفاده از سیم پیچ در جهت عقربه های ساعت در مقابل عقربه های ساعت برای ایجاد معکوس یک سایه ( سایه داخلی ).
- فضا - اثر مولد.
- افکت متنی مبتنی بر تولید در
<canvas>
با استفاده از hsl() color-cycling وwindow.requestAnimationFrame
برای ایجاد احساس حرکت.
متن-سایه ها در بوم
یکی از موارد مورد علاقه من به مشخصات CSS3 (همراه با حاشیه، شعاع وب، و سایر موارد) توانایی ایجاد سایه است. مهم است که تفاوت بین سایه های CSS و <canvas>
را درک کنید، به ویژه:
CSS از دو روش استفاده می کند. box-shadow برای عناصر جعبه مانند div، span و غیره. و text-shadow برای محتوای متنی.
<canvas>
یک نوع سایه دارد. برای تمام اشیاء برداری استفاده می شود. ctx.moveTo، ctx.lineTo، ctx.bezierCurveTo، ctx.quadradicCurveTo، ctx.arc، ctx.rect، ctx.fillText، ctx.strokeText، و غیره. برای ایجاد یک سایه در <canvas>
، روی این چهار ویژگی ضربه بزنید:
- ctx.shadowColor = "قرمز" // رشته
- رنگ سایه؛ ورودیهای RGB، RGBA، HSL، HEX و سایر ورودیها معتبر هستند.
- ctx.shadowOffsetX = 0; // عدد صحیح
- فاصله افقی سایه، نسبت به متن.
- ctx.shadowOffsetY = 0; // عدد صحیح
- فاصله عمودی سایه، نسبت به متن.
- ctx.shadowBlur = 10; // عدد صحیح
- اثر محو کردن سایه، هر چه مقدار بزرگتر باشد، تاری بیشتر می شود.
برای شروع کار، بیایید ببینیم که چگونه <canvas>
می تواند جلوه های CSS را شبیه سازی کند. جستجو در تصاویر گوگل برای "css text-shadow" منجر به چند نسخه نمایشی عالی برای ما شد که بتوانیم آنها را شبیه سازی کنیم. Line25 و Stereoscopic و Shadow 3D .
افکت سه بعدی استریوسکوپی (برای اطلاعات بیشتر به تصویر آناگلیف مراجعه کنید) نمونه ای از یک خط کد ساده است که بسیار مورد استفاده قرار گرفته است. با خط زیر از CSS، میتوانیم توهم عمق را هنگام مشاهده با عینکهای سهبعدی قرمز/فیروزهای (همانطور که در فیلمهای سه بعدی به شما میدهند) ایجاد کنیم:
text-shadow: -0.06em 0 0 red, 0.06em 0 0 cyan;
هنگام تبدیل این رشته به <canvas>
باید به دو نکته توجه کرد:
- هیچ shadow-blur (مقدار سوم) وجود ندارد، بنابراین دلیلی برای اجرای سایه وجود ندارد، زیرا 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>
- EMها در
<canvas>
پشتیبانی نمیشوند، بنابراین باید به PX تبدیل شوند. ما میتوانیم با ایجاد عنصری با فونتهای یکسان در DOM و تنظیم عرض به فرمتی که باید اندازهگیری شود، نسبت تبدیل را برای تبدیل بین PT، PC، EM، EX، PX و غیره پیدا کنیم. یا به عنوان مثال، برای گرفتن تبدیل EM -> PX، عنصر DOM را با "ارتفاع: 1em" اندازهگیری میکنیم، offsetHeight حاصل چند PX در هر EM است.
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 جلوگیری کنیم (در حالی که اجازه میدهیم سایه کشیده شود) با افزودن یک مسیر برش. به منظور ایجاد یک مسیر برش در اطراف متن، باید ارتفاع متن (که از لحاظ تاریخی "em-height" نامیده می شود، ارتفاع حرف "M" در ماشین چاپ) و عرض متن را بدانیم. میتوانیم عرض را با استفاده از ctx.measureText().width
بدست آوریم، اما ctx.measureText().height
وجود ندارد.
خوشبختانه، از طریق CSS hack-ardry ( برای روشهای بیشتر برای اصلاح پیادهسازیهای قدیمیتر <canvas>
با استفاده از اندازهگیریهای CSS به معیارهای تایپوگرافی مراجعه کنید )، میتوانیم ارتفاع متن را از طریق اندازهگیری offsetHeight
یک <span>
با همان ویژگیهای فونت پیدا کنیم. :
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، تا اشکال برداری مولد، و غیره استفاده کرد!
وقفه (مماس بر فشار پیکسل)
در نوشتن این بخش از مقاله، مثال Stereoscopic من را کنجکاو کرد. ایجاد یک افکت روی صفحه نمایش فیلم سه بعدی با استفاده از <canvas>
و دو تصویر گرفته شده از منظرهای کمی متفاوت چقدر سخت خواهد بود؟ ظاهرا خیلی سخت نیست. هسته زیر کانال قرمز تصویر اول (داده) را با کانال فیروزه ای تصویر دوم (data2) ترکیب می کند:
data[i] = data[i] * 255 / 0xFF;
data[i+1] = 255 * data2[i+1] / 0xFF;
data[i+2] = 255 * data2[i+2] / 0xFF;
اکنون، یک نفر فقط باید دو آیفون را به پیشانی خود بچسباند، همزمان روی "ضبط ویدئو" کلیک کند و ما می توانیم فیلم های سه بعدی خود را در HTML5 بسازیم. هیچ داوطلبی؟
نئون-رنگین کمان، انعکاس گورخر- جلوه های زنجیره ای
زنجیرهای کردن چندین افکت در <canvas>
میتواند ساده باشد، اما دانش اولیه از globalCompositeOperation (GCO) مورد نیاز است. برای مقایسه عملیات با GIMP (یا فتوشاپ): 12 GCO در <canvas>
تیرهتر وجود دارد و روشنتر را میتوان به عنوان حالتهای ترکیب لایه در نظر گرفت. 10 عملیات دیگر به عنوان ماسک آلفا روی لایه ها اعمال می شود (یک لایه پیکسل های لایه دیگر را حذف می کند). globalCompositeOperation "لایه ها" (یا در مورد ما رشته های کد) را به هم متصل می کند و آنها را به روش های جدید و هیجان انگیز ترکیب می کند:
نمودار globalCompositeOperation حالت های GCO را در حال کار نشان می دهد. این نمودار از بخش بزرگی از طیف رنگ و سطوح چندگانه شفافیت آلفا استفاده می کند تا با جزئیات ببیند چه چیزی را باید انتظار داشت. توصیه می کنم برای توضیحات متنی، مرجع globalCompositeOperation موزیلا را بررسی کنید. برای تحقیقات بیشتر، میتوانید نحوه عملکرد این عملیات را در تصاویر ترکیبی دیجیتال پورتر داف بیاموزید.
حالت مورد علاقه من globalCompositeOperation="lighter" است. Lighter پیکسل های اضافه شده را شبیه به نحوه ترکیب نور مخلوط می کند. وقتی نور قرمز، سبز و سفید با شدت کامل باشد، نور سفید را می بینیم. این یک ویژگی هیجان انگیز برای بازی کردن است، به خصوص زمانی که <canvas>
روی آلفای جهانی پایین تنظیم شده باشد. امکان کنترل دقیق تر و لبه های صاف تر. فندک کاربردهای زیادی دارد، مورد علاقه اخیر من یک ایجاد کننده پس زمینه دسکتاپ HTML5 است که در http://weavesilk.com/ یافت می شود. یکی از دموهای من، تنفس کهکشانها (JS1k)، همچنین از حالت سبکتر استفاده میکند - با کشیدن الگوها از این دو مثال شروع میکنید تا ببینید این حالت چه تأثیری دارد.
globalCompositeOperation مدیریت مرورگر .
افکت نئون-رنگین کمان جیتر
در نسخه ی نمایشی زیر، با زنجیر کردن افکتها با هم با استفاده از globalCompositeOperation (منبع، روشنتر و تیرهتر) به یک درخشش نئون رنگینکمان مانند فتوشاپ با یک طرح کلی لرزان دست خواهیم یافت. این نسخه نمایشی پیشرفتی از نسخه نمایشی "Text-Shadows in <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();
};
اثر بازتاب گورخر
افکت Zebra Reflection از منبع عالی WebDesignerWall الهام گرفته شده است که چگونه صفحه خود را با CSS جذاب کنید. این ایده را کمی فراتر می برد و یک "بازتاب" برای متن ایجاد می کند - مانند آنچه ممکن است در iTunes ببینید. افکت fillColor (سفید)، createPattern (zebra.png) و linearGradient (درخشش) را ترکیب می کند. این توانایی اعمال چندین نوع پر را برای هر شیء برداری نشان می دهد:
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 به یک موج سینوسی/کسینوس، و چرخش در رنگ ها با استفاده از ویژگی HSL، می توانیم جلوه های جالب تری مانند این مثال "biohazard" ایجاد کنیم:
HSL: Hue, Saturation, Lightness (1978)
HSL یک فرمت جدید پشتیبانی شده در مشخصات CSS3 است. در جایی که HEX برای رایانه ها طراحی شده است، HSL طوری طراحی شده است که قابل خواندن توسط انسان باشد.
نشان دادن سهولت HSL. برای چرخه در طیف رنگ، به سادگی "رنگ" را از 360 افزایش می دهیم. رنگ به صورت استوانه ای به طیف نگاشت می شود. Lightness میزان تیره/روشن بودن رنگ را کنترل می کند. 0% یک پیکسل سیاه را نشان می دهد، در حالی که 100% یک پیکسل سفید را نشان می دهد. اشباع، روشن یا زنده بودن یک رنگ را کنترل می کند. خاکستری با اشباع 0٪ ایجاد می شود و رنگ های زنده با استفاده از مقدار 100٪ ایجاد می شوند.
از آنجایی که HSL یک استاندارد اخیر است، ممکن است بخواهید به پشتیبانی از مرورگرهای قدیمیتر ادامه دهید، که از طریق تبدیل فضای رنگ ممکن است. کد زیر یک شی HSL { H: 360, S: 100, L: 100} را می پذیرد و یک شی RGB { R: 255, G: 255, B: 255 } را خروجی می دهد. از آنجا، می توانید از آن مقادیر برای ایجاد رشته rgb یا rgba خود استفاده کنید. برای اطلاعات عمیق تر به مقاله روشنگر ویکی پدیا در مورد 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
استاندارد جدیدی است که در اینجا جایگزین هر دو می شود. با اجازه دادن به مرورگر برای تنظیم انیمیشن ها بر اساس منابع موجود، در مصرف برق جهان (و چند ضربان قلب رایانه شما) صرفه جویی کنید. برخی از ویژگی های مهم عبارتند از:
- هنگامی که یک کاربر در فریم وجود دارد، انیمیشن میتواند کند یا متوقف شود تا از استفاده از منابع غیر ضروری جلوگیری شود.
- محدودیتی برای نرخ فریم در 60 فریم در ثانیه وجود دارد. دلیل این امر این است که بسیار بالاتر از سطحی است که انسان می تواند متوجه شود (اکثر انسان ها با سرعت 30 فریم در ثانیه انیمیشن را "سیال" می بینند).
در زمان نوشتن، پیشوندهای خاص فروشنده برای استفاده از requestAnimationFrame
مورد نیاز است. Paul Irish یک لایه شیم ایجاد کرد که دارای پشتیبانی متقابل فروشنده است، در 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);
};
})();
اگر کمی جلوتر برویم، جاه طلبان ممکن است این را با یک poly-fill مانند 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/Desktop منتقل کرد.
تیتانیوم .