بهبود عملکرد HTML5 Canvas

بوریس اسموس
Boris Smus

مقدمه

بوم HTML5 که به عنوان آزمایشی از اپل آغاز شد، گسترده ترین استاندارد پشتیبانی شده برای گرافیک حالت فوری دوبعدی در وب است. بسیاری از توسعه دهندگان اکنون برای طیف گسترده ای از پروژه های چند رسانه ای، تجسم ها و بازی ها به آن متکی هستند. با این حال، با افزایش پیچیدگی برنامه‌هایی که می‌سازیم، توسعه‌دهندگان ناخواسته به دیوار عملکرد ضربه می‌زنند. در مورد بهینه‌سازی عملکرد بوم، حکمت‌های منفصل زیادی وجود دارد. هدف این مقاله تثبیت بخشی از این بدنه در یک منبع قابل هضم تر برای توسعه دهندگان است. این مقاله شامل بهینه‌سازی‌های اساسی است که برای همه محیط‌های گرافیکی رایانه‌ای و همچنین تکنیک‌های خاص بوم که با بهبود پیاده‌سازی بوم تغییر می‌کنند، اعمال می‌شود. به طور خاص، از آنجایی که فروشندگان مرورگر شتاب GPU بوم را اجرا می‌کنند، برخی از تکنیک‌های عملکرد مشخص شده مورد بحث احتمالاً تأثیر کمتری خواهند داشت. در صورت لزوم به این موضوع اشاره خواهد شد. توجه داشته باشید که این مقاله به استفاده از بوم HTML5 نمی‌پردازد. برای این کار، این مقالات مربوط به بوم را در HTML5Rocks، این فصل در سایت Dive into HTML5 یا MDN Canvas بررسی کنید. آموزش

تست عملکرد

برای پرداختن به دنیای به سرعت در حال تغییر بوم HTML5، آزمایش‌های JSPerf ( jsperf.com ) تأیید می‌کنند که هر بهینه‌سازی پیشنهادی همچنان کار می‌کند. JSPerf یک برنامه وب است که به توسعه دهندگان اجازه می دهد تا تست های عملکرد جاوا اسکریپت را بنویسند. هر آزمون بر روی نتیجه ای تمرکز می کند که شما در تلاش برای رسیدن به آن هستید (مثلاً پاک کردن بوم)، و شامل چندین رویکرد است که به یک نتیجه می رسد. JSPerf هر رویکرد را تا آنجا که ممکن است در یک بازه زمانی کوتاه اجرا می‌کند و از نظر آماری تعداد تکرار معنی‌داری در هر ثانیه ارائه می‌کند. نمرات بالاتر همیشه بهتر است! بازدیدکنندگان یک صفحه تست عملکرد JSPerf می‌توانند آزمایش را در مرورگر خود اجرا کنند و به JSPerf اجازه دهند نتایج تست نرمال‌شده را در Browserscope ( browserscope.org ) ذخیره کند. از آنجایی که تکنیک‌های بهینه‌سازی در این مقاله با یک نتیجه JSPerf پشتیبان‌گیری می‌شوند، می‌توانید برای مشاهده اطلاعات به‌روز در مورد اینکه آیا این تکنیک همچنان کاربرد دارد یا خیر، بازگردید. من یک برنامه کمکی کوچک نوشته ام که این نتایج را به صورت نمودار ارائه می کند که در سراسر این مقاله جاسازی شده است.

تمام نتایج عملکرد در این مقاله بر روی نسخه مرورگر کلید شده است. معلوم می‌شود که این یک محدودیت است، زیرا ما نمی‌دانیم مرورگر روی چه سیستم‌عاملی اجرا می‌شود، یا مهم‌تر از آن، اینکه آیا بوم HTML5 هنگام اجرای تست عملکرد، سرعت سخت‌افزاری داشت یا خیر. با مراجعه به about:gpu در نوار آدرس می‌توانید متوجه شوید که آیا بوم HTML5 کروم شتاب سخت‌افزاری دارد یا خیر.

پیش نمایش به بوم خارج از صفحه نمایش

اگر در حال ترسیم اولیه‌های مشابه روی صفحه نمایش در فریم‌های متعدد هستید، همانطور که اغلب هنگام نوشتن یک بازی اتفاق می‌افتد، می‌توانید با پیش‌رندر کردن بخش‌های بزرگ صحنه، دستاوردهای عملکردی زیادی داشته باشید. پیش رندر به معنای استفاده از یک بوم (یا بوم) جدا از صفحه است که برای رندر کردن تصاویر موقت روی آن، و سپس رندر کردن بوم های خارج از صفحه نمایش بر روی بوم قابل مشاهده است. به عنوان مثال، فرض کنید ماریو را دوباره با سرعت 60 فریم در ثانیه ترسیم می کنید. می‌توانید کلاه، سبیل و «M» او را در هر فریم دوباره ترسیم کنید، یا قبل از اجرای انیمیشن، ماریو را از قبل رندر کنید. بدون پیش رندر:

// canvas, context are defined
function render() {
  drawMario(context);
  requestAnimationFrame(render);
}

پیش رندر:

var m_canvas = document.createElement('canvas');
m_canvas.width = 64;
m_canvas.height = 64;
var m_context = m_canvas.getContext('2d');
drawMario(m_context);

function render() {
  context.drawImage(m_canvas, 0, 0);
  requestAnimationFrame(render);
}

به استفاده از requestAnimationFrame توجه کنید که در بخش بعدی با جزئیات بیشتر مورد بحث قرار خواهد گرفت.

این تکنیک مخصوصاً زمانی مؤثر است که عملیات رندرینگ ( drawMario در مثال بالا) گران باشد. یک مثال خوب در این زمینه رندر متن است که عملیات بسیار گرانی است.

با این حال، عملکرد ضعیف مورد آزمایشی "از قبل رندر شده شل" است. هنگام پیش رندر، مهم است که مطمئن شوید بوم موقت شما به خوبی در اطراف تصویری که می‌کشید قرار می‌گیرد، در غیر این صورت افزایش عملکرد رندر خارج از صفحه با کاهش عملکرد کپی کردن یک بوم بزرگ روی دیگری (که بسته به اندازه هدف منبع متفاوت است) وزن متعادلی دارد. یک بوم راحت در تست بالا به سادگی کوچکتر است:

can2.width = 100;
can2.height = 40;

در مقایسه با شل که عملکرد ضعیف تری دارد:

can3.width = 300;
can3.height = 100;

تماس های دسته ای با هم

از آنجایی که ترسیم یک عملیات گران قیمت است، کارآمدتر است که ماشین حالت ترسیم را با مجموعه‌ای طولانی از دستورات بارگیری کنید و سپس همه آنها را در بافر ویدیو رها کنید.

به عنوان مثال، هنگام ترسیم چندین خط، کارآمدتر است که یک مسیر با تمام خطوط موجود در آن ایجاد کنید و آن را با یک فراخوانی ترسیم کنید. به عبارت دیگر، به جای ترسیم خطوط جداگانه:

for (var i = 0; i < points.length - 1; i++) {
  var p1 = points[i];
  var p2 = points[i+1];
  context.beginPath();
  context.moveTo(p1.x, p1.y);
  context.lineTo(p2.x, p2.y);
  context.stroke();
}

از رسم یک چند خط منفرد عملکرد بهتری دریافت می کنیم:

context.beginPath();
for (var i = 0; i < points.length - 1; i++) {
  var p1 = points[i];
  var p2 = points[i+1];
  context.moveTo(p1.x, p1.y);
  context.lineTo(p2.x, p2.y);
}
context.stroke();

این در دنیای بوم HTML5 نیز صدق می کند. به عنوان مثال، هنگام ترسیم یک مسیر پیچیده، بهتر است همه نقاط را در مسیر قرار دهید، نه اینکه بخش ها را به طور جداگانه رندر کنید ( jsperf ).

با این حال، توجه داشته باشید که در Canvas، یک استثنای مهم برای این قانون وجود دارد: اگر موارد اولیه درگیر در ترسیم شی مورد نظر دارای کادرهای محدود کوچکی باشند (به عنوان مثال، خطوط افقی و عمودی)، ممکن است در واقع ارائه آنها به طور جداگانه کارآمدتر باشد ( jsperf ).

از تغییرات غیر ضروری حالت بوم خودداری کنید

عنصر بوم HTML5 در بالای یک ماشین حالت اجرا می شود که مواردی مانند سبک های پر کردن و استروک و همچنین نقاط قبلی که مسیر فعلی را تشکیل می دهند را ردیابی می کند. هنگام تلاش برای بهینه سازی عملکرد گرافیکی، وسوسه انگیز است که صرفاً روی رندر گرافیکی تمرکز کنید. با این حال، دستکاری ماشین حالت همچنین می تواند هزینه های سربار عملکرد را به همراه داشته باشد. برای مثال، اگر از چندین رنگ پر برای رندر یک صحنه استفاده می‌کنید، رندر کردن بر اساس رنگ به جای قرار دادن روی بوم ارزان‌تر است. برای رندر کردن یک الگوی نواری، می‌توانید یک نوار را رندر کنید، رنگ‌ها را تغییر دهید، نوار بعدی را رندر کنید و غیره:

for (var i = 0; i < STRIPES; i++) {
  context.fillStyle = (i % 2 ? COLOR1 : COLOR2);
  context.fillRect(i * GAP, 0, GAP, 480);
}

یا تمام راه راه های فرد و سپس تمام راه راه های زوج را رندر کنید:

context.fillStyle = COLOR1;
for (var i = 0; i < STRIPES/2; i++) {
  context.fillRect((i*2) * GAP, 0, GAP, 480);
}
context.fillStyle = COLOR2;
for (var i = 0; i < STRIPES/2; i++) {
  context.fillRect((i*2+1) * GAP, 0, GAP, 480);
}

همانطور که انتظار می رود، رویکرد درهم آمیخته کندتر است زیرا تغییر ماشین حالت گران است.

تنها تفاوت های صفحه نمایش را رندر کنید، نه وضعیت جدید را

همانطور که انتظار می رود، رندر کمتر بر روی صفحه نمایش ارزان تر از رندر بیشتر است. اگر تنها تفاوت‌های افزایشی بین ترسیم‌های مجدد دارید، می‌توانید تنها با ترسیم تفاوت، عملکرد قابل توجهی را افزایش دهید. به عبارت دیگر، به جای پاک کردن کل صفحه قبل از کشیدن:

context.fillRect(0, 0, canvas.width, canvas.height);

کادر مرزی ترسیم شده را دنبال کنید و فقط آن را پاک کنید.

context.fillRect(last.x, last.y, last.width, last.height);

اگر با گرافیک کامپیوتری آشنایی دارید، ممکن است این تکنیک را به عنوان «مناطق دوباره ترسیم کنید» نیز بشناسید، جایی که کادر کراندار قبلی رندر شده ذخیره می شود و سپس در هر رندر پاک می شود. این تکنیک همچنین در زمینه‌های رندر مبتنی بر پیکسل اعمال می‌شود، همانطور که در این سخنرانی شبیه‌ساز جاوا اسکریپت نینتندو نشان داده شده است.

از بوم های چند لایه برای صحنه های پیچیده استفاده کنید

همانطور که قبلا ذکر شد، ترسیم تصاویر بزرگ گران است و در صورت امکان باید از آن اجتناب کرد. علاوه بر استفاده از بوم دیگری برای رندر خارج از صفحه، همانطور که در بخش پیش رندر نشان داده شده است، می توانیم از بوم های لایه بندی شده روی هم استفاده کنیم. با استفاده از شفافیت در بوم پیش‌زمینه، می‌توانیم به GPU برای ترکیب آلفاها با هم در زمان رندر تکیه کنیم. می توانید این را به صورت زیر تنظیم کنید، با دو بوم کاملاً قرار گرفته یکی روی دیگری.

<canvas id="bg" width="640" height="480" style="position: absolute; z-index: 0">
</canvas>
<canvas id="fg" width="640" height="480" style="position: absolute; z-index: 1">
</canvas>

مزیت داشتن فقط یک بوم در اینجا این است که وقتی بوم پیش زمینه را می کشیم یا پاک می کنیم، هرگز پس زمینه را تغییر نمی دهیم. اگر بازی یا برنامه چندرسانه‌ای شما را می‌توان به پیش‌زمینه و پس‌زمینه تقسیم کرد، آن‌ها را روی بوم‌های جداگانه رندر کنید تا عملکرد قابل توجهی را افزایش دهید.

شما اغلب می توانید از درک ناقص انسان استفاده کنید و پس زمینه را فقط یک بار یا با سرعت کمتری نسبت به پیش زمینه (که احتمالاً بیشتر توجه کاربر شما را به خود جلب می کند) رندر کنید. برای مثال، می‌توانید هر بار که رندر می‌دهید، پیش‌زمینه را رندر کنید، اما پس‌زمینه را فقط در هر فریم نهم رندر کنید. همچنین توجه داشته باشید که اگر برنامه شما با این نوع ساختار بهتر کار کند، این رویکرد برای هر تعداد بوم کامپوزیت به خوبی تعمیم می یابد.

از shadowBlur اجتناب کنید

مانند بسیاری از محیط های گرافیکی دیگر، بوم HTML5 به توسعه دهندگان اجازه می دهد تا موارد اولیه را محو کنند، اما این عملیات می تواند بسیار گران باشد:

context.shadowOffsetX = 5;
context.shadowOffsetY = 5;
context.shadowBlur = 4;
context.shadowColor = 'rgba(255, 0, 0, 0.5)';
context.fillRect(20, 20, 150, 100);

روش های مختلف برای پاک کردن بوم را بدانید

از آنجایی که بوم HTML5 یک الگوی ترسیم حالت فوری است، صحنه باید به صراحت در هر فریم دوباره ترسیم شود. به همین دلیل، پاک کردن بوم یک عملیات اساسی برای برنامه ها و بازی های بوم HTML5 است. همانطور که در بخش اجتناب از تغییرات حالت بوم ذکر شد، پاک کردن کل بوم اغلب نامطلوب است، اما اگر باید این کار را انجام دهید، دو گزینه وجود دارد: فراخوانی context.clearRect(0, 0, width, height) یا استفاده clearRect یک هک مخصوص بوم برای انجام آن: canvas.width = canvas.width ;. هک تنظیم مجدد canvas.width در کروم 14 بسیار سریعتر است

مراقب این نکته باشید، زیرا به شدت به اجرای بوم زیرین بستگی دارد و بسیار در معرض تغییر است. برای اطلاعات بیشتر، مقاله Simon Sarris در مورد پاک کردن بوم را ببینید.

از مختصات ممیز شناور اجتناب کنید

بوم HTML5 از رندر زیر پیکسل پشتیبانی می کند و راهی برای خاموش کردن آن وجود ندارد. اگر با مختصاتی رسم کنید که اعداد صحیح نیستند، به طور خودکار از anti-aliasing برای صاف کردن خطوط استفاده می کند. در اینجا جلوه بصری که از این مقاله عملکرد بوم زیر پیکسلی توسط سب لی-دلیل گرفته شده است:

زیر پیکسل

اگر sprite هموار شده اثر مورد نظر شما نیست، تبدیل مختصات شما به اعداد صحیح با استفاده از Math.floor یا Math.round ( jsperf ) می تواند بسیار سریعتر باشد:

برای تبدیل مختصات ممیز شناور به اعداد صحیح، می‌توانید از چندین تکنیک هوشمندانه استفاده کنید که کارآمدترین آنها شامل اضافه کردن یک نیمه به عدد مورد نظر و سپس انجام عملیات بیتی بر روی نتیجه برای حذف قسمت کسری است.

// With a bitwise or.
rounded = (0.5 + somenum) | 0;
// A double bitwise not.
rounded = ~~ (0.5 + somenum);
// Finally, a left bitwise shift.
rounded = (0.5 + somenum) << 0;

تفکیک عملکرد کامل در اینجا ( jsperf ) است.

توجه داشته باشید که این نوع بهینه سازی پس از تسریع در پیاده سازی بوم GPU که می تواند مختصات غیر صحیح را به سرعت ارائه کند، دیگر اهمیتی ندارد.

انیمیشن های خود را با requestAnimationFrame بهینه کنید

API نسبتا جدید requestAnimationFrame روش پیشنهادی برای پیاده سازی برنامه های کاربردی تعاملی در مرورگر است. به جای اینکه به مرورگر دستور دهید تا با نرخ تیک ثابت خاصی رندر شود، مودبانه از مرورگر می‌خواهید تا روال رندر شما را فراخوانی کند و زمانی که مرورگر در دسترس است با شما تماس گرفته شود. به عنوان یک اثر جانبی خوب، اگر صفحه در پیش زمینه نباشد، مرورگر آنقدر هوشمند است که نمی تواند رندر شود. درخواست پاسخ به تماس requestAnimationFrame نرخ بازگشت به تماس 60 فریم در ثانیه را هدف قرار می دهد، اما آن را تضمین نمی کند، بنابراین باید مدت زمان گذشته از آخرین رندر را پیگیری کنید. این می تواند چیزی شبیه به شکل زیر باشد:

var x = 100;
var y = 100;
var lastRender = Date.now();
function render() {
  var delta = Date.now() - lastRender;
  x += delta;
  y += delta;
  context.fillRect(x, y, W, H);
  requestAnimationFrame(render);
}
render();

توجه داشته باشید که این استفاده از requestAnimationFrame برای بوم و همچنین سایر فناوری‌های رندر مانند WebGL کاربرد دارد. در زمان نگارش این مطلب، این API فقط در کروم، سافاری و فایرفاکس موجود است، بنابراین باید از این شیم استفاده کنید.

اکثر پیاده سازی های بوم موبایل کند هستند

بیایید در مورد موبایل صحبت کنیم. متأسفانه در زمان نگارش این مقاله، تنها iOS 5.0 نسخه بتا که Safari 5.1 را اجرا می‌کند، دارای GPU تسریع‌شده در بوم موبایل است. بدون شتاب GPU، مرورگرهای تلفن همراه معمولاً پردازنده‌های قدرتمند کافی برای برنامه‌های مبتنی بر بوم مدرن ندارند. تعدادی از تست‌های JSPerf که در بالا توضیح داده شد، در مقایسه با دسکتاپ در تلفن همراه بدتر عمل می‌کنند و انواع برنامه‌های بین دستگاهی را که می‌توانید انتظار اجرای موفقیت‌آمیز آن‌ها را داشته باشید، بسیار محدود می‌کند.

نتیجه گیری

برای جمع‌بندی، این مقاله مجموعه جامعی از تکنیک‌های بهینه‌سازی مفید را پوشش می‌دهد که به شما کمک می‌کند پروژه‌های مبتنی بر بوم HTML5 را توسعه دهید. اکنون که در اینجا چیز جدیدی یاد گرفتید، جلو بروید و خلاقیت های عالی خود را بهینه کنید. یا، اگر در حال حاضر بازی یا برنامه‌ای برای بهینه‌سازی ندارید، برای الهام گرفتن از Chrome Experiments و Creative JS دیدن کنید.

مراجع

  • حالت فوری در مقابل حالت حفظ شده .
  • سایر مقالات بوم HTML5Rocks .
  • بخش Canvas Dive into HTML5.
  • JSPerf به توسعه دهندگان اجازه می دهد تا تست های عملکرد JS را ایجاد کنند.
  • Browserscope داده های عملکرد مرورگر را ذخیره می کند.
  • JSPerfView ، که تست های JSPerf را به صورت نمودار ارائه می کند.
  • پست وبلاگ سایمون در مورد پاک کردن بوم، و کتاب او، HTML5 Unleashed که شامل فصل هایی در مورد عملکرد بوم است.
  • پست وبلاگ سباستین در مورد عملکرد رندر زیر پیکسل.
  • صحبت بن در مورد بهینه سازی شبیه ساز JS NES.
  • نمایه‌ساز بوم جدید در ابزار توسعه کروم.
،

بوریس اسموس
Boris Smus

مقدمه

بوم HTML5 که به عنوان آزمایشی از اپل آغاز شد، گسترده ترین استاندارد پشتیبانی شده برای گرافیک حالت فوری دوبعدی در وب است. بسیاری از توسعه دهندگان اکنون برای طیف گسترده ای از پروژه های چند رسانه ای، تجسم ها و بازی ها به آن متکی هستند. با این حال، با افزایش پیچیدگی برنامه‌هایی که می‌سازیم، توسعه‌دهندگان ناخواسته به دیوار عملکرد ضربه می‌زنند. در مورد بهینه‌سازی عملکرد بوم، حکمت‌های منفصل زیادی وجود دارد. هدف این مقاله تثبیت بخشی از این بدنه در یک منبع قابل هضم تر برای توسعه دهندگان است. این مقاله شامل بهینه‌سازی‌های اساسی است که برای همه محیط‌های گرافیکی رایانه‌ای و همچنین تکنیک‌های خاص بوم که با بهبود پیاده‌سازی بوم تغییر می‌کنند، اعمال می‌شود. به طور خاص، از آنجایی که فروشندگان مرورگر شتاب GPU بوم را اجرا می‌کنند، برخی از تکنیک‌های عملکرد مشخص شده مورد بحث احتمالاً تأثیر کمتری خواهند داشت. در صورت لزوم به این موضوع اشاره خواهد شد. توجه داشته باشید که این مقاله به استفاده از بوم HTML5 نمی‌پردازد. برای این کار، این مقالات مربوط به بوم را در HTML5Rocks، این فصل در سایت Dive into HTML5 یا MDN Canvas بررسی کنید. آموزش

تست عملکرد

برای پرداختن به دنیای به سرعت در حال تغییر بوم HTML5، آزمایش‌های JSPerf ( jsperf.com ) تأیید می‌کنند که هر بهینه‌سازی پیشنهادی همچنان کار می‌کند. JSPerf یک برنامه وب است که به توسعه دهندگان اجازه می دهد تا تست های عملکرد جاوا اسکریپت را بنویسند. هر آزمون بر روی نتیجه ای تمرکز می کند که شما در تلاش برای رسیدن به آن هستید (مثلاً پاک کردن بوم)، و شامل چندین رویکرد است که به یک نتیجه می رسد. JSPerf هر رویکرد را تا آنجا که ممکن است در یک بازه زمانی کوتاه اجرا می‌کند و از نظر آماری تعداد تکرار معنی‌داری در هر ثانیه ارائه می‌کند. نمرات بالاتر همیشه بهتر است! بازدیدکنندگان یک صفحه تست عملکرد JSPerf می‌توانند آزمایش را در مرورگر خود اجرا کنند و به JSPerf اجازه دهند نتایج تست نرمال‌شده را در Browserscope ( browserscope.org ) ذخیره کند. از آنجایی که تکنیک‌های بهینه‌سازی در این مقاله با یک نتیجه JSPerf پشتیبان‌گیری می‌شوند، می‌توانید برای مشاهده اطلاعات به‌روز در مورد اینکه آیا این تکنیک همچنان کاربرد دارد یا خیر، بازگردید. من یک برنامه کمکی کوچک نوشته ام که این نتایج را به صورت نمودار ارائه می کند که در سراسر این مقاله جاسازی شده است.

تمام نتایج عملکرد در این مقاله بر روی نسخه مرورگر کلید شده است. معلوم می‌شود که این یک محدودیت است، زیرا ما نمی‌دانیم مرورگر روی چه سیستم‌عاملی اجرا می‌شود، یا مهم‌تر از آن، اینکه آیا بوم HTML5 هنگام اجرای تست عملکرد، سرعت سخت‌افزاری داشت یا خیر. با مراجعه به about:gpu در نوار آدرس می‌توانید متوجه شوید که آیا بوم HTML5 کروم شتاب سخت‌افزاری دارد یا خیر.

پیش نمایش به بوم خارج از صفحه نمایش

اگر در حال ترسیم اولیه‌های مشابه روی صفحه نمایش در فریم‌های متعدد هستید، همانطور که اغلب هنگام نوشتن یک بازی اتفاق می‌افتد، می‌توانید با پیش‌رندر کردن بخش‌های بزرگ صحنه، دستاوردهای عملکردی زیادی داشته باشید. پیش رندر به معنای استفاده از یک بوم (یا بوم) جدا از صفحه است که برای رندر کردن تصاویر موقت روی آن، و سپس رندر کردن بوم های خارج از صفحه نمایش بر روی بوم قابل مشاهده است. به عنوان مثال، فرض کنید ماریو را دوباره با سرعت 60 فریم در ثانیه ترسیم می کنید. می‌توانید کلاه، سبیل و «M» او را در هر فریم دوباره ترسیم کنید، یا قبل از اجرای انیمیشن، ماریو را از قبل رندر کنید. بدون پیش رندر:

// canvas, context are defined
function render() {
  drawMario(context);
  requestAnimationFrame(render);
}

پیش رندر:

var m_canvas = document.createElement('canvas');
m_canvas.width = 64;
m_canvas.height = 64;
var m_context = m_canvas.getContext('2d');
drawMario(m_context);

function render() {
  context.drawImage(m_canvas, 0, 0);
  requestAnimationFrame(render);
}

به استفاده از requestAnimationFrame توجه کنید که در بخش بعدی با جزئیات بیشتر مورد بحث قرار خواهد گرفت.

این تکنیک مخصوصاً زمانی مؤثر است که عملیات رندرینگ ( drawMario در مثال بالا) گران باشد. یک مثال خوب در این زمینه رندر متن است که عملیات بسیار گرانی است.

با این حال، عملکرد ضعیف مورد آزمایشی "از قبل رندر شده شل" است. هنگام پیش رندر، مهم است که مطمئن شوید بوم موقت شما به خوبی در اطراف تصویری که می‌کشید قرار می‌گیرد، در غیر این صورت افزایش عملکرد رندر خارج از صفحه با کاهش عملکرد کپی کردن یک بوم بزرگ روی دیگری (که بسته به اندازه هدف منبع متفاوت است) وزن متعادلی دارد. یک بوم راحت در تست بالا به سادگی کوچکتر است:

can2.width = 100;
can2.height = 40;

در مقایسه با شل که عملکرد ضعیف تری دارد:

can3.width = 300;
can3.height = 100;

تماس های دسته ای با هم

از آنجایی که ترسیم یک عملیات گران قیمت است، کارآمدتر است که ماشین حالت ترسیم را با مجموعه‌ای طولانی از دستورات بارگیری کنید و سپس همه آنها را در بافر ویدیو رها کنید.

به عنوان مثال، هنگام ترسیم چندین خط، کارآمدتر است که یک مسیر با تمام خطوط موجود در آن ایجاد کنید و آن را با یک فراخوانی ترسیم کنید. به عبارت دیگر، به جای ترسیم خطوط جداگانه:

for (var i = 0; i < points.length - 1; i++) {
  var p1 = points[i];
  var p2 = points[i+1];
  context.beginPath();
  context.moveTo(p1.x, p1.y);
  context.lineTo(p2.x, p2.y);
  context.stroke();
}

از رسم یک چند خط منفرد عملکرد بهتری دریافت می کنیم:

context.beginPath();
for (var i = 0; i < points.length - 1; i++) {
  var p1 = points[i];
  var p2 = points[i+1];
  context.moveTo(p1.x, p1.y);
  context.lineTo(p2.x, p2.y);
}
context.stroke();

این در دنیای بوم HTML5 نیز صدق می کند. به عنوان مثال، هنگام ترسیم یک مسیر پیچیده، بهتر است همه نقاط را در مسیر قرار دهید، نه اینکه بخش ها را به طور جداگانه رندر کنید ( jsperf ).

با این حال، توجه داشته باشید که در Canvas، یک استثنای مهم برای این قانون وجود دارد: اگر موارد اولیه درگیر در ترسیم شی مورد نظر دارای کادرهای محدود کوچکی باشند (به عنوان مثال، خطوط افقی و عمودی)، ممکن است در واقع ارائه آنها به طور جداگانه کارآمدتر باشد ( jsperf ).

از تغییرات غیر ضروری حالت بوم خودداری کنید

عنصر بوم HTML5 در بالای یک ماشین حالت اجرا می شود که مواردی مانند سبک های پر کردن و استروک و همچنین نقاط قبلی که مسیر فعلی را تشکیل می دهند را ردیابی می کند. هنگام تلاش برای بهینه سازی عملکرد گرافیکی، وسوسه انگیز است که صرفاً روی رندر گرافیکی تمرکز کنید. با این حال، دستکاری ماشین حالت همچنین می تواند هزینه های سربار عملکرد را به همراه داشته باشد. برای مثال، اگر از چندین رنگ پر برای رندر یک صحنه استفاده می‌کنید، رندر کردن بر اساس رنگ به جای قرار دادن روی بوم ارزان‌تر است. برای رندر کردن یک الگوی نواری، می‌توانید یک نوار را رندر کنید، رنگ‌ها را تغییر دهید، نوار بعدی را رندر کنید و غیره:

for (var i = 0; i < STRIPES; i++) {
  context.fillStyle = (i % 2 ? COLOR1 : COLOR2);
  context.fillRect(i * GAP, 0, GAP, 480);
}

یا تمام راه راه های فرد و سپس تمام راه راه های زوج را رندر کنید:

context.fillStyle = COLOR1;
for (var i = 0; i < STRIPES/2; i++) {
  context.fillRect((i*2) * GAP, 0, GAP, 480);
}
context.fillStyle = COLOR2;
for (var i = 0; i < STRIPES/2; i++) {
  context.fillRect((i*2+1) * GAP, 0, GAP, 480);
}

همانطور که انتظار می رود، رویکرد درهم آمیخته کندتر است زیرا تغییر ماشین حالت گران است.

تنها تفاوت های صفحه نمایش را رندر کنید، نه وضعیت جدید را

همانطور که انتظار می رود، رندر کمتر بر روی صفحه نمایش ارزان تر از رندر بیشتر است. اگر تنها تفاوت‌های افزایشی بین ترسیم‌های مجدد دارید، می‌توانید تنها با ترسیم تفاوت، عملکرد قابل توجهی را افزایش دهید. به عبارت دیگر، به جای پاک کردن کل صفحه قبل از کشیدن:

context.fillRect(0, 0, canvas.width, canvas.height);

کادر مرزی ترسیم شده را دنبال کنید و فقط آن را پاک کنید.

context.fillRect(last.x, last.y, last.width, last.height);

اگر با گرافیک کامپیوتری آشنایی دارید، ممکن است این تکنیک را به عنوان «مناطق دوباره ترسیم کنید» نیز بشناسید، جایی که کادر کراندار قبلی رندر شده ذخیره می شود و سپس در هر رندر پاک می شود. این تکنیک همچنین در زمینه‌های رندر مبتنی بر پیکسل اعمال می‌شود، همانطور که در این سخنرانی شبیه‌ساز جاوا اسکریپت نینتندو نشان داده شده است.

از بوم های چند لایه برای صحنه های پیچیده استفاده کنید

همانطور که قبلا ذکر شد، ترسیم تصاویر بزرگ گران است و در صورت امکان باید از آن اجتناب کرد. علاوه بر استفاده از بوم دیگری برای رندر خارج از صفحه، همانطور که در بخش پیش رندر نشان داده شده است، می توانیم از بوم های لایه بندی شده روی هم استفاده کنیم. با استفاده از شفافیت در بوم پیش‌زمینه، می‌توانیم به GPU برای ترکیب آلفاها با هم در زمان رندر تکیه کنیم. می توانید این را به صورت زیر تنظیم کنید، با دو بوم کاملاً قرار گرفته یکی روی دیگری.

<canvas id="bg" width="640" height="480" style="position: absolute; z-index: 0">
</canvas>
<canvas id="fg" width="640" height="480" style="position: absolute; z-index: 1">
</canvas>

مزیت داشتن فقط یک بوم در اینجا این است که وقتی بوم پیش زمینه را می کشیم یا پاک می کنیم، هرگز پس زمینه را تغییر نمی دهیم. اگر بازی یا برنامه چندرسانه‌ای شما را می‌توان به پیش‌زمینه و پس‌زمینه تقسیم کرد، آن‌ها را روی بوم‌های جداگانه رندر کنید تا عملکرد قابل توجهی را افزایش دهید.

شما اغلب می توانید از درک ناقص انسان استفاده کنید و پس زمینه را فقط یک بار یا با سرعت کمتری نسبت به پیش زمینه (که احتمالاً بیشتر توجه کاربر شما را به خود جلب می کند) رندر کنید. برای مثال، می‌توانید هر بار که رندر می‌دهید، پیش‌زمینه را رندر کنید، اما پس‌زمینه را فقط در هر فریم نهم رندر کنید. همچنین توجه داشته باشید که اگر برنامه شما با این نوع ساختار بهتر کار کند، این رویکرد برای هر تعداد بوم کامپوزیت به خوبی تعمیم می یابد.

از shadowBlur اجتناب کنید

مانند بسیاری از محیط های گرافیکی دیگر، بوم HTML5 به توسعه دهندگان اجازه می دهد تا موارد اولیه را محو کنند، اما این عملیات می تواند بسیار گران باشد:

context.shadowOffsetX = 5;
context.shadowOffsetY = 5;
context.shadowBlur = 4;
context.shadowColor = 'rgba(255, 0, 0, 0.5)';
context.fillRect(20, 20, 150, 100);

روش های مختلف برای پاک کردن بوم را بدانید

از آنجایی که بوم HTML5 یک الگوی ترسیم حالت فوری است، صحنه باید به صراحت در هر فریم دوباره ترسیم شود. به همین دلیل، پاک کردن بوم یک عملیات اساسی برای برنامه ها و بازی های بوم HTML5 است. همانطور که در بخش اجتناب از تغییرات حالت بوم ذکر شد، پاک کردن کل بوم اغلب نامطلوب است، اما اگر باید این کار را انجام دهید، دو گزینه وجود دارد: فراخوانی context.clearRect(0, 0, width, height) یا استفاده clearRect یک هک مخصوص بوم برای انجام آن: canvas.width = canvas.width ;. هک تنظیم مجدد canvas.width در کروم 14 بسیار سریعتر است

مراقب این نکته باشید، زیرا به شدت به اجرای بوم زیرین بستگی دارد و بسیار در معرض تغییر است. برای اطلاعات بیشتر، مقاله Simon Sarris در مورد پاک کردن بوم را ببینید.

از مختصات ممیز شناور اجتناب کنید

بوم HTML5 از رندر زیر پیکسل پشتیبانی می کند و راهی برای خاموش کردن آن وجود ندارد. اگر با مختصاتی رسم کنید که اعداد صحیح نیستند، به طور خودکار از anti-aliasing برای صاف کردن خطوط استفاده می کند. در اینجا جلوه بصری که از این مقاله عملکرد بوم زیر پیکسلی توسط سب لی-دلیل گرفته شده است:

زیر پیکسل

اگر sprite هموار شده اثر مورد نظر شما نیست، تبدیل مختصات شما به اعداد صحیح با استفاده از Math.floor یا Math.round ( jsperf ) می تواند بسیار سریعتر باشد:

برای تبدیل مختصات ممیز شناور به اعداد صحیح، می‌توانید از چندین تکنیک هوشمندانه استفاده کنید که کارآمدترین آنها شامل اضافه کردن یک نیمه به عدد مورد نظر و سپس انجام عملیات بیتی بر روی نتیجه برای حذف قسمت کسری است.

// With a bitwise or.
rounded = (0.5 + somenum) | 0;
// A double bitwise not.
rounded = ~~ (0.5 + somenum);
// Finally, a left bitwise shift.
rounded = (0.5 + somenum) << 0;

تفکیک عملکرد کامل در اینجا ( jsperf ) است.

توجه داشته باشید که این نوع بهینه سازی پس از تسریع در پیاده سازی بوم GPU که می تواند مختصات غیر صحیح را به سرعت ارائه کند، دیگر اهمیتی ندارد.

انیمیشن های خود را با requestAnimationFrame بهینه کنید

API نسبتا جدید requestAnimationFrame روش پیشنهادی برای پیاده سازی برنامه های کاربردی تعاملی در مرورگر است. به جای اینکه به مرورگر دستور دهید تا با نرخ تیک ثابت خاصی رندر شود، مودبانه از مرورگر می‌خواهید تا روال رندر شما را فراخوانی کند و زمانی که مرورگر در دسترس است با شما تماس گرفته شود. به عنوان یک اثر جانبی خوب، اگر صفحه در پیش زمینه نباشد، مرورگر آنقدر هوشمند است که نمی تواند رندر شود. درخواست پاسخ به تماس requestAnimationFrame نرخ بازگشت به تماس 60 فریم در ثانیه را هدف قرار می دهد، اما آن را تضمین نمی کند، بنابراین باید مدت زمان گذشته از آخرین رندر را پیگیری کنید. این می تواند چیزی شبیه به شکل زیر باشد:

var x = 100;
var y = 100;
var lastRender = Date.now();
function render() {
  var delta = Date.now() - lastRender;
  x += delta;
  y += delta;
  context.fillRect(x, y, W, H);
  requestAnimationFrame(render);
}
render();

توجه داشته باشید که این استفاده از requestAnimationFrame برای بوم و همچنین سایر فناوری‌های رندر مانند WebGL کاربرد دارد. در زمان نگارش این مطلب، این API فقط در کروم، سافاری و فایرفاکس موجود است، بنابراین باید از این شیم استفاده کنید.

اکثر پیاده سازی های بوم موبایل کند هستند

بیایید در مورد موبایل صحبت کنیم. متأسفانه در زمان نگارش این مقاله، تنها iOS 5.0 نسخه بتا که Safari 5.1 را اجرا می‌کند، دارای GPU تسریع‌شده در بوم موبایل است. بدون شتاب GPU، مرورگرهای تلفن همراه معمولاً پردازنده‌های قدرتمند کافی برای برنامه‌های مبتنی بر بوم مدرن ندارند. تعدادی از تست‌های JSPerf که در بالا توضیح داده شد، در مقایسه با دسکتاپ در تلفن همراه بدتر عمل می‌کنند و انواع برنامه‌های بین دستگاهی را که می‌توانید انتظار اجرای موفقیت‌آمیز آن‌ها را داشته باشید، بسیار محدود می‌کند.

نتیجه گیری

برای جمع‌بندی، این مقاله مجموعه جامعی از تکنیک‌های بهینه‌سازی مفید را پوشش می‌دهد که به شما کمک می‌کند پروژه‌های مبتنی بر بوم HTML5 را توسعه دهید. اکنون که در اینجا چیز جدیدی یاد گرفتید، جلو بروید و خلاقیت های عالی خود را بهینه کنید. یا، اگر در حال حاضر بازی یا برنامه‌ای برای بهینه‌سازی ندارید، برای الهام گرفتن از Chrome Experiments و Creative JS دیدن کنید.

مراجع

  • حالت فوری در مقابل حالت حفظ شده .
  • سایر مقالات بوم HTML5Rocks .
  • بخش Canvas Dive into HTML5.
  • JSPerf به توسعه دهندگان اجازه می دهد تا تست های عملکرد JS را ایجاد کنند.
  • Browserscope داده های عملکرد مرورگر را ذخیره می کند.
  • JSPerfView ، که تست های JSPerf را به صورت نمودار ارائه می کند.
  • پست وبلاگ سایمون در مورد پاک کردن بوم، و کتاب او، HTML5 Unleashed که شامل فصل هایی در مورد عملکرد بوم است.
  • پست وبلاگ سباستین در مورد عملکرد رندر زیر پیکسل.
  • صحبت بن در مورد بهینه سازی شبیه ساز JS NES.
  • نمایه‌ساز بوم جدید در ابزار توسعه کروم.
،

بوریس اسموس
Boris Smus

مقدمه

بوم HTML5 که به عنوان آزمایشی از اپل آغاز شد، گسترده ترین استاندارد پشتیبانی شده برای گرافیک حالت فوری دوبعدی در وب است. بسیاری از توسعه دهندگان اکنون برای طیف گسترده ای از پروژه های چند رسانه ای، تجسم ها و بازی ها به آن متکی هستند. با این حال، با افزایش پیچیدگی برنامه‌هایی که می‌سازیم، توسعه‌دهندگان ناخواسته به دیوار عملکرد ضربه می‌زنند. در مورد بهینه‌سازی عملکرد بوم، حکمت‌های منفصل زیادی وجود دارد. هدف این مقاله تثبیت بخشی از این بدنه در یک منبع قابل هضم تر برای توسعه دهندگان است. این مقاله شامل بهینه‌سازی‌های اساسی است که برای همه محیط‌های گرافیکی رایانه‌ای و همچنین تکنیک‌های خاص بوم که با بهبود پیاده‌سازی بوم تغییر می‌کنند، اعمال می‌شود. به طور خاص، از آنجایی که فروشندگان مرورگر شتاب GPU بوم را اجرا می‌کنند، برخی از تکنیک‌های عملکرد مشخص شده مورد بحث احتمالاً تأثیر کمتری خواهند داشت. در صورت لزوم به این موضوع اشاره خواهد شد. توجه داشته باشید که این مقاله به استفاده از بوم HTML5 نمی‌پردازد. برای این کار، این مقالات مربوط به بوم را در HTML5Rocks، این فصل در سایت Dive into HTML5 یا MDN Canvas بررسی کنید. آموزش

تست عملکرد

برای پرداختن به دنیای به سرعت در حال تغییر بوم HTML5، آزمایش‌های JSPerf ( jsperf.com ) تأیید می‌کنند که هر بهینه‌سازی پیشنهادی همچنان کار می‌کند. JSPerf یک برنامه وب است که به توسعه دهندگان اجازه می دهد تا تست های عملکرد جاوا اسکریپت را بنویسند. هر آزمون بر روی نتیجه ای تمرکز می کند که شما در تلاش برای رسیدن به آن هستید (مثلاً پاک کردن بوم)، و شامل چندین رویکرد است که به یک نتیجه می رسد. JSPerf هر رویکرد را تا آنجا که ممکن است در یک بازه زمانی کوتاه اجرا می‌کند و از نظر آماری تعداد تکرار معنی‌داری در هر ثانیه ارائه می‌کند. نمرات بالاتر همیشه بهتر است! بازدیدکنندگان یک صفحه تست عملکرد JSPerf می‌توانند آزمایش را در مرورگر خود اجرا کنند و به JSPerf اجازه دهند نتایج تست نرمال‌شده را در Browserscope ( browserscope.org ) ذخیره کند. از آنجایی که تکنیک‌های بهینه‌سازی در این مقاله با یک نتیجه JSPerf پشتیبان‌گیری می‌شوند، می‌توانید برای مشاهده اطلاعات به‌روز در مورد اینکه آیا این تکنیک همچنان کاربرد دارد یا خیر، بازگردید. من یک برنامه کمکی کوچک نوشته ام که این نتایج را به صورت نمودار ارائه می کند که در سراسر این مقاله جاسازی شده است.

تمام نتایج عملکرد در این مقاله بر روی نسخه مرورگر کلید شده است. معلوم می‌شود که این یک محدودیت است، زیرا ما نمی‌دانیم مرورگر روی چه سیستم‌عاملی اجرا می‌شود، یا مهم‌تر از آن، اینکه آیا بوم HTML5 هنگام اجرای تست عملکرد، سرعت سخت‌افزاری داشت یا خیر. با مراجعه به about:gpu در نوار آدرس می‌توانید متوجه شوید که آیا بوم HTML5 کروم شتاب سخت‌افزاری دارد یا خیر.

پیش نمایش به بوم خارج از صفحه نمایش

اگر در حال ترسیم اولیه‌های مشابه روی صفحه نمایش در فریم‌های متعدد هستید، همانطور که اغلب هنگام نوشتن یک بازی اتفاق می‌افتد، می‌توانید با پیش‌رندر کردن بخش‌های بزرگ صحنه، دستاوردهای عملکردی زیادی داشته باشید. پیش رندر به معنای استفاده از یک بوم (یا بوم) جدا از صفحه است که برای رندر کردن تصاویر موقت روی آن، و سپس رندر کردن بوم های خارج از صفحه نمایش بر روی بوم قابل مشاهده است. به عنوان مثال، فرض کنید ماریو را دوباره با سرعت 60 فریم در ثانیه ترسیم می کنید. می‌توانید کلاه، سبیل و «M» او را در هر فریم دوباره ترسیم کنید، یا قبل از اجرای انیمیشن، ماریو را از قبل رندر کنید. بدون پیش رندر:

// canvas, context are defined
function render() {
  drawMario(context);
  requestAnimationFrame(render);
}

پیش رندر:

var m_canvas = document.createElement('canvas');
m_canvas.width = 64;
m_canvas.height = 64;
var m_context = m_canvas.getContext('2d');
drawMario(m_context);

function render() {
  context.drawImage(m_canvas, 0, 0);
  requestAnimationFrame(render);
}

به استفاده از requestAnimationFrame توجه کنید که در بخش بعدی با جزئیات بیشتر مورد بحث قرار خواهد گرفت.

این تکنیک مخصوصاً زمانی مؤثر است که عملیات رندرینگ ( drawMario در مثال بالا) گران باشد. یک مثال خوب در این زمینه رندر متن است که عملیات بسیار گرانی است.

با این حال، عملکرد ضعیف مورد آزمایشی "از قبل رندر شده شل" است. هنگام پیش رندر، مهم است که مطمئن شوید بوم موقت شما به خوبی در اطراف تصویری که می‌کشید قرار می‌گیرد، در غیر این صورت افزایش عملکرد رندر خارج از صفحه با کاهش عملکرد کپی کردن یک بوم بزرگ روی دیگری (که بسته به اندازه هدف منبع متفاوت است) وزن متعادلی دارد. یک بوم راحت در تست بالا به سادگی کوچکتر است:

can2.width = 100;
can2.height = 40;

در مقایسه با شل که عملکرد ضعیف تری دارد:

can3.width = 300;
can3.height = 100;

تماس های دسته ای با هم

از آنجایی که ترسیم یک عملیات گران قیمت است، کارآمدتر است که ماشین حالت ترسیم را با مجموعه‌ای طولانی از دستورات بارگیری کنید و سپس همه آنها را در بافر ویدیو رها کنید.

به عنوان مثال، هنگام ترسیم چندین خط، کارآمدتر است که یک مسیر با تمام خطوط موجود در آن ایجاد کنید و آن را با یک فراخوانی ترسیم کنید. به عبارت دیگر، به جای ترسیم خطوط جداگانه:

for (var i = 0; i < points.length - 1; i++) {
  var p1 = points[i];
  var p2 = points[i+1];
  context.beginPath();
  context.moveTo(p1.x, p1.y);
  context.lineTo(p2.x, p2.y);
  context.stroke();
}

از رسم یک چند خط منفرد عملکرد بهتری دریافت می کنیم:

context.beginPath();
for (var i = 0; i < points.length - 1; i++) {
  var p1 = points[i];
  var p2 = points[i+1];
  context.moveTo(p1.x, p1.y);
  context.lineTo(p2.x, p2.y);
}
context.stroke();

این در دنیای بوم HTML5 نیز صدق می کند. به عنوان مثال، هنگام ترسیم یک مسیر پیچیده، بهتر است همه نقاط را در مسیر قرار دهید، نه اینکه بخش ها را به طور جداگانه رندر کنید ( jsperf ).

با این حال، توجه داشته باشید که در Canvas، یک استثنای مهم برای این قانون وجود دارد: اگر موارد اولیه درگیر در ترسیم شی مورد نظر دارای کادرهای محدود کوچکی باشند (به عنوان مثال، خطوط افقی و عمودی)، ممکن است در واقع ارائه آنها به طور جداگانه کارآمدتر باشد ( jsperf ).

از تغییرات غیر ضروری حالت بوم خودداری کنید

عنصر بوم HTML5 در بالای یک ماشین حالت اجرا می شود که مواردی مانند سبک های پر کردن و استروک و همچنین نقاط قبلی که مسیر فعلی را تشکیل می دهند را ردیابی می کند. هنگام تلاش برای بهینه سازی عملکرد گرافیکی، وسوسه انگیز است که صرفاً روی رندر گرافیکی تمرکز کنید. با این حال، دستکاری ماشین حالت همچنین می تواند هزینه های سربار عملکرد را به همراه داشته باشد. برای مثال، اگر از چندین رنگ پر برای رندر یک صحنه استفاده می‌کنید، رندر کردن بر اساس رنگ به جای قرار دادن روی بوم ارزان‌تر است. برای رندر کردن یک الگوی نواری، می‌توانید یک نوار را رندر کنید، رنگ‌ها را تغییر دهید، نوار بعدی را رندر کنید و غیره:

for (var i = 0; i < STRIPES; i++) {
  context.fillStyle = (i % 2 ? COLOR1 : COLOR2);
  context.fillRect(i * GAP, 0, GAP, 480);
}

یا تمام راه راه های فرد و سپس تمام راه راه های زوج را رندر کنید:

context.fillStyle = COLOR1;
for (var i = 0; i < STRIPES/2; i++) {
  context.fillRect((i*2) * GAP, 0, GAP, 480);
}
context.fillStyle = COLOR2;
for (var i = 0; i < STRIPES/2; i++) {
  context.fillRect((i*2+1) * GAP, 0, GAP, 480);
}

همانطور که انتظار می رود، رویکرد درهم آمیخته کندتر است زیرا تغییر ماشین حالت گران است.

تنها تفاوت های صفحه نمایش را رندر کنید، نه وضعیت جدید را

همانطور که انتظار می رود، رندر کمتر بر روی صفحه نمایش ارزان تر از رندر بیشتر است. اگر تنها تفاوت‌های افزایشی بین ترسیم‌های مجدد دارید، می‌توانید تنها با ترسیم تفاوت، عملکرد قابل توجهی را افزایش دهید. به عبارت دیگر، به جای پاک کردن کل صفحه قبل از کشیدن:

context.fillRect(0, 0, canvas.width, canvas.height);

کادر مرزی ترسیم شده را دنبال کنید و فقط آن را پاک کنید.

context.fillRect(last.x, last.y, last.width, last.height);

اگر با گرافیک کامپیوتری آشنایی دارید، ممکن است این تکنیک را به عنوان «مناطق دوباره ترسیم کنید» نیز بشناسید، جایی که کادر کراندار قبلی رندر شده ذخیره می شود و سپس در هر رندر پاک می شود. این تکنیک همچنین در زمینه‌های رندر مبتنی بر پیکسل اعمال می‌شود، همانطور که در این سخنرانی شبیه‌ساز جاوا اسکریپت نینتندو نشان داده شده است.

از بوم های چند لایه برای صحنه های پیچیده استفاده کنید

همانطور که قبلا ذکر شد، ترسیم تصاویر بزرگ گران است و در صورت امکان باید از آن اجتناب کرد. علاوه بر استفاده از بوم دیگری برای رندر خارج از صفحه، همانطور که در بخش پیش رندر نشان داده شده است، می توانیم از بوم های لایه بندی شده روی هم استفاده کنیم. با استفاده از شفافیت در بوم پیش‌زمینه، می‌توانیم به GPU برای ترکیب آلفاها با هم در زمان رندر تکیه کنیم. می توانید این را به صورت زیر تنظیم کنید، با دو بوم کاملاً قرار گرفته یکی روی دیگری.

<canvas id="bg" width="640" height="480" style="position: absolute; z-index: 0">
</canvas>
<canvas id="fg" width="640" height="480" style="position: absolute; z-index: 1">
</canvas>

مزیت داشتن فقط یک بوم در اینجا ، این است که وقتی بوم پیش زمینه را ترسیم می کنیم یا پاک می کنیم ، هرگز پیش زمینه را اصلاح نمی کنیم. اگر برنامه بازی یا برنامه چندرسانه ای شما را می توان به یک پیش زمینه و پیش زمینه تقسیم کرد ، در نظر بگیرید که این موارد را روی بوم های جداگانه ارائه دهید تا عملکرد قابل توجهی داشته باشید.

شما اغلب می توانید از درک ناقص انسان استفاده کنید و پیش زمینه را فقط یک بار یا با سرعت کندتر در مقایسه با پیش زمینه (که احتمالاً بیشتر توجه کاربر شما را به خود جلب می کند) ارائه دهید. به عنوان مثال ، شما می توانید هر بار که ارائه می دهید ، پیش زمینه را ارائه دهید ، اما پس زمینه را فقط در هر قاب نهم ارائه دهید. همچنین توجه داشته باشید که اگر برنامه شما با این نوع ساختار بهتر کار کند ، این روش برای هر تعداد بوم کامپوزیت به خوبی تعمیم می دهد.

از Shadowblur خودداری کنید

مانند بسیاری از محیط های گرافیکی دیگر ، بوم HTML5 به توسعه دهندگان اجازه می دهد تا ابتدایی را تار کنند ، اما این عمل می تواند بسیار گران باشد:

context.shadowOffsetX = 5;
context.shadowOffsetY = 5;
context.shadowBlur = 4;
context.shadowColor = 'rgba(255, 0, 0, 0.5)';
context.fillRect(20, 20, 150, 100);

روشهای مختلفی برای پاک کردن بوم بدانید

از آنجا که بوم HTML5 یک الگوی ترسیم حالت فوری است ، صحنه باید صریحاً در هر فریم مجدداً مورد استفاده قرار گیرد. به همین دلیل ، پاکسازی بوم یک عملیات اساسی برای برنامه ها و بازی های بوم HTML5 است. همانطور که در بخش تغییر حالت بوم ذکر شده است ، پاک کردن کل بوم غالباً نامطلوب است ، اما اگر باید این کار را انجام دهید ، دو گزینه وجود دارد: فراخوانی متن. clearRect context.clearRect(0, 0, width, height) یا با استفاده از یک هک مخصوص بوم برای انجام آن: canvas.width = canvas.width ؛ canvas.width Resotting Hack در Chrome 14 به طور قابل توجهی سریعتر است

مراقب این نکته باشید ، زیرا این امر به شدت به اجرای بوم زیرین بستگی دارد و بسیار مورد تغییر قرار می گیرد. برای اطلاعات بیشتر ، به مقاله Simon Sarris در مورد پاکسازی بوم مراجعه کنید.

از مختصات نقطه شناور خودداری کنید

بوم HTML5 از ارائه زیر پیکسل پشتیبانی می کند ، و هیچ راهی برای خاموش کردن آن وجود ندارد. اگر با مختصاتی که عدد صحیح نیستند ترسیم کنید ، به طور خودکار از ضد آلیاژ استفاده می کنید تا سعی کنید خطوط را صاف کنید. در اینجا اثر بصری ، گرفته شده از این مقاله عملکرد بوم زیر پیکسل توسط Seb Lee-Delisle :

زیر پیکسل

اگر اسپری صاف شده تاثیری که شما می خواهید نیست ، می تواند خیلی سریعتر باشد که مختصات خود را با استفاده از Math.floor یا Math.round ( jsperf ) به عدد صحیح تبدیل کنید:

برای تبدیل مختصات نقطه شناور خود به اعداد صحیح ، می توانید از چندین تکنیک هوشمندانه استفاده کنید که بیشترین عملکرد آن شامل اضافه کردن نیمی از شماره هدف است ، و سپس انجام عملیات بیت به سمت نتیجه برای از بین بردن قسمت کسری است.

// With a bitwise or.
rounded = (0.5 + somenum) | 0;
// A double bitwise not.
rounded = ~~ (0.5 + somenum);
// Finally, a left bitwise shift.
rounded = (0.5 + somenum) << 0;

تجزیه کامل عملکرد در اینجا ( JSPERF ) است.

توجه داشته باشید که این نوع بهینه سازی پس از شتاب اجرای GPU ، دیگر نباید مهم باشد که قادر به ارائه سریع مختصات غیر اینتگر باشد.

انیمیشن های خود را با requestAnimationFrame بهینه کنید

API requestAnimationFrame نسبتاً جدید APIAMATIONFRAME روش پیشنهادی برای اجرای برنامه های تعاملی در مرورگر است. به جای اینکه به مرورگر دستور دهید تا با یک نرخ ثابت ثابت ارائه شود ، شما مودبانه از مرورگر می خواهید که روال رندر خود را فراخوانی کند و در صورت موجود بودن مرورگر تماس بگیرید. به عنوان یک اثر جانبی خوب ، اگر صفحه در پیش زمینه نباشد ، مرورگر به اندازه کافی هوشمند است که ارائه نمی دهد. پاسخ به تماس requestAnimationFrame با هدف نرخ پاسخ به تماس 60 فریم در ثانیه است اما آن را تضمین نمی کند ، بنابراین باید پیگیری کنید که چقدر زمان از آخرین رندر گذشت. این می تواند چیزی شبیه به زیر باشد:

var x = 100;
var y = 100;
var lastRender = Date.now();
function render() {
  var delta = Date.now() - lastRender;
  x += delta;
  y += delta;
  context.fillRect(x, y, W, H);
  requestAnimationFrame(render);
}
render();

توجه داشته باشید که این استفاده از requestAnimationFrame در مورد بوم و همچنین سایر فناوری های ارائه دهنده مانند WebGL اعمال می شود. در زمان نوشتن ، این API فقط در Chrome ، Safari و Firefox موجود است ، بنابراین شما باید از این shim استفاده کنید.

بیشتر اجرای بوم موبایل کند است

بیایید در مورد موبایل صحبت کنیم. متأسفانه در زمان نوشتن ، فقط iOS 5.0 بتا در حال اجرا Safari 5.1 دارای GPU اجرای بوم موبایل شتاب دهنده است. بدون شتاب GPU ، مرورگرهای تلفن همراه به طور کلی CPU های قدرتمند به اندازه کافی برای برنامه های مدرن مبتنی بر بوم ندارند. تعدادی از تست های JSPERF که در بالا توضیح داده شد ، در مقایسه با دسک تاپ ترتیب بزرگی را در موبایل بدتر می کنند ، و انواع برنامه های دستگاه های متقابل را که می توانید با موفقیت انجام دهید محدود می کند.

نتیجه گیری

برای یادآوری ، این مقاله مجموعه ای جامع از تکنیک های بهینه سازی مفید را در بر می گیرد که به شما در توسعه پروژه های مبتنی بر بوم HTML5 کمک می کند. اکنون که چیز جدیدی را در اینجا آموخته اید ، بروید و خلاقیت های عالی خود را بهینه کنید. یا اگر در حال حاضر بازی یا برنامه ای برای بهینه سازی ندارید ، آزمایش های Chrome و JS خلاق را برای الهام بخش بررسی کنید.

مراجع

  • حالت فوری در مقابل حالت حفظ شده .
  • سایر مقالات بوم HTML5rocks.
  • بخش بوم شیرجه به HTML5.
  • JSPERF به توسعه دهندگان اجازه می دهد تست های عملکرد JS را ایجاد کنند.
  • Browserscope داده های عملکرد مرورگر را ذخیره می کند.
  • Jsperfview ، که تست های JSPERF را به عنوان نمودارها ارائه می دهد.
  • پست وبلاگ سیمون در مورد پاکسازی بوم ، و کتاب او ، HTML5 Unleashed که شامل فصل هایی در عملکرد بوم است.
  • پست وبلاگ سباستین در عملکرد ارائه دهنده زیر پیکسل.
  • صحبت های بن در مورد بهینه سازی یک شبیه ساز JS NES.
  • پروفایل بوم جدید در Devtools Chrome.