نظرة عامة كاملة على HTML Canvas

يجب أن تقرأ قبل القيام بأي شيء بعلامة اللوحة القماشية ، حتى لو كنت تعرفها بالفعل.

نظرة عامة

يتم استخدام عنصر قماش HTML لرسم رسومات "نقطية" على تطبيق ويب. توفر واجهة برمجة تطبيقات Canvas سياقين للرسم: ثنائي الأبعاد وثلاثي الأبعاد ، وفي هذا الدليل ، سنتحدث عن الإطار الثنائي الأبعاد (والذي سأحيله إلى واجهة برمجة تطبيقات Canvas للبساطة).

قبل أن أبدأ ، أريدك أن تعرف نقطة مهمة للغاية. Canvas هي واجهة برمجة تطبيقات رسومات نقطية - يمكنك التعامل مع الأشياء على مستوى البكسل. هذا يعني أن البرنامج الأساسي لا يعرف النموذج الذي تستخدمه لعرض السياق الخاص بك - لا يعرف ما إذا كنت ترسم مستطيلاً أو دائرة.

لقد قسمت واجهة برمجة تطبيقات Canvas إلى أجزاء منفصلة ، لكي تلتهم واحدة تلو الأخرى:

  • مسار API
  • أنماط الرسم
  • التدرجات والأنماط
  • التلاعب المباشر بالبكسل والصور
  • التحولات
  • ضرب المناطق
  • الحالة وطريقة المقطع ()

اقامة

لتشغيل البرنامج التعليمي لـ Canvas ، أنشئ ملف HTML وملف JS مرتبطًا به.

  Canvas Demo   This will be displayed if your browser doesn't support the canvas element. The closing tag is necessary.    

في ملفك canvas-demo.js،

// canvas-demo.js const demoCanvas = document.getElementById(’canvas-demo’).getContext(’2d’); window.onload = function() {// make sure to use onload /* Add code here as we go!!! @nodocs */ }

مسارات

المسارات هي مجموعة من النقاط في شبكة البكسل ثنائية الأبعاد في اللوحة القماشية. يتم رسمها بمساعدة API. كل شكل في المسار الذي ترسمه يسمى "مسار فرعي" حسب وثائق W3C.

  • beginPath()و closePath(): تتم إضافة جميع الأشكال التي ترسمها إلى المسار الحالي. إذا اتصلت strokeأو fillلاحقًا ، فسيتم تطبيق ذلك على جميع الأشكال التي رسمتها في المسار الحالي. لمنع ذلك ، يمكنك تقسيم الرسم الخاص بك عن طريق الاتصال beginPathو closePath.
// Calling this isn't necessary, but a good practice. demoCanvas.beginPath(); /* * Drawing code, copy and paste each example (separately) here */ demoCanvas.closePath();// this is required if you want to draw // in a separate path later
  • moveTo(x,y) : يشير إلى بناء شكل جديد يبدأ عند النقطة (س ، ص).
  • lineTo(x,y): رسم خط من النقطة الأخيرة في الشكل الحالي إلى النقطة التي تم تمريرها. إذا لم يتم إنشاء أي شكل (عبر moveTo) ، فسيتم إنشاء شكل جديد بدءًا من (x ، y) (تمامًا مثل moveTo).
  • quadraticCurveTo(cpx1,cpy1,x,y)و bezierCurveTo(cpx1,cpy1,cpx2,cpy2,x,y): يرسم منحنى تربيعي / مكعب بيزير يبدأ من النقطة الأخيرة في الشكل ، ويمر عبر نقاط التحكم ( cpx1,cpy1و cpx2,cpy2) وينتهي عند x,y. منحنى بيزير هو مجرد منحنى "سلس" يمر عبر نقاط "تحكم" وسيطة بنقاط نهاية معينة. لاحظ أن المنحنى لا يجب أن يمر عبر نقاط التحكم بالضبط - يمكن أن يتم تنعيمه.
  • arcTo(x1,y1,x2,y2,radius): هذه طريقة معقدة بعض الشيء للاستخدام. افترض أن النقطة الحالية في المسار هي x0,y0- ثم arcToسترسم قوسًا به مماسين يربطان هذين الزوجين من النقاط (x1,y1) & (x0,y0)و (x1,y1) & (x2,y2). سيكون نصف قطر القوس هو المحدد. كلما زاد نصف القطر ، كلما ابتعد القوس عن x1,y1، (انظر مثال 1.2 للوضوح البصري). إذا لم تكن قد استخدمت moveToبعد ، x0,y0فسيكون 0,0.
  • arc(x,y,radius,startAngle,endAngle,counterclockwise): يربط النقطة الحالية في المسار (افتراضيًا 0,0) ببداية القوس. يرسم القوس من مركز x,yنصف القطر radius، من startAngleإلى endAngle. (ملاحظة: على عكس رياضيات القلم والورق ، يتم وصف الزوايا في اتجاه عقارب الساعة في Canvas API) ؛ ولكن في ظل أربعة شروط خاصة - (x0,y0)تساوي (x1,y1)، (x1,y1)تساوي (x2,y2)، (x0,y0),(x1,y1),(x2,y2)خطية متداخلة ، أو إذا كانت radiusصفرًا ، فسيكون الاستدعاء لـ arcوسيتم lineTo(x1,y1)رسم خط بدلاً من ذلك.
  • rect(x,y,w,h): يرسم مستطيلاً بالزاوية العلوية اليسرى x,yوالعرض wوالارتفاع h.

المثال 1.1:

الآن نحن بحاجة إلى تجربة العرض التوضيحي - سنقوم برسم بعض الخطوط الأفقية العشوائية ثم رسم تخطيطي للعين. ستبدو النتيجة كشيء على اليسار. لا تنسى أن تذهب من خلال الكود وتصلح الرمز.

/* Draw horizontal subpaths (shapes) in one path. */ // Draw a pattern of vertically stack horizontal // lines. demoCanvas.moveTo(10, 10);// start at (10,10) demoCanvas.lineTo(110, 10); demoCanvas.moveTo(10, 20);// 10 pts below demoCanvas.lineTo(180, 20); demoCanvas.moveTo(10, 30); demoCanvas.lineTo(150, 30); demoCanvas.moveTo(10, 40); demoCanvas.lineTo(160, 40); demoCanvas.moveTo(10, 50); demoCanvas.lineTo(130, 50); // try removing this moveTo, the quad-curve will then // start from from (130, 50), due to the lineTo. demoCanvas.moveTo(10, 100);// quad-curve starts from here demoCanvas.quadraticCurveTo(110, 55, 210, 100);// curve upward demoCanvas.moveTo(10, 100);// back here, let's draw one below demoCanvas.quadraticCurveTo(110, 145, 210, 100);// curve below // that forms the eye outline demoCanvas.moveTo(132.5, 100);// remove this, a horizontal line will be // drawn from (210, 100) to (132.5, 100) because arc() connects the last // point to the start of the arc. demoCanvas.arc(110, 100, 22.5, 0, 2*Math.PI, false);// pupil (circle) /* We'll talk about this shortly */ demoCanvas.stroke();// draws (by outlining our shapes in the path)

المثال 1.2:

في المثال أدناه ، أقوم بإنشاء منحنى مكعب (مع إرشادات مرئية) ، arcToومكالمات في منتصف اليمين ، ورجل حزمة arc()في أسفل اليسار. نقاط التحكم (في المنحنى التكعيبي) هي أشكال الزوايا حسب الإرشادات الثلاثة.

(x1,y1)ل arcToهي الزاوية التي شكلتها الظلال اثنين.

// comment this block out if you can see the cubic curve demoCanvas.moveTo(100, 100); demoCanvas.lineTo(150, 10); demoCanvas.moveTo(250, 100); demoCanvas.lineTo(200, 190); demoCanvas.moveTo(150, 10); demoCanvas.lineTo(200, 190) demoCanvas.moveTo(100, 100); demoCanvas.bezierCurveTo(150, 10, 200, 190, 250, 100); // arcTo() is too complicated to use // demoCanvas.stroke(); demoCanvas.closePath(); demoCanvas.beginPath(); demoCanvas.moveTo(200, 200);// comment out above line (and comment this line), // then the arc's tangent will come from (0,0)!! Try it. demoCanvas.arcTo(100, 300, 300, 300, 100); demoCanvas.moveTo(200, 200); demoCanvas.arcTo(100, 300, 300, 300, 50); demoCanvas.moveTo(100, 300); demoCanvas.lineTo(300, 300); demoCanvas.moveTo(100, 300); demoCanvas.lineTo(200, 200); demoCanvas.moveTo(50, 300); // packman demoCanvas.arc(50, 300, 35, Math.PI/6, 11*Math.PI/6, false); demoCanvas.lineTo(50, 300); demoCanvas.stroke();

أنماط الرسم

حتى الآن ، كنا نرسم مسارات بسيطة رقيقة الخطوط. ستساعدنا أنماط الرسم في جعل الرسم أفضل بكثير.

لاحظ أنه لا يمكنك تطبيق نمطين مختلفين على نفس المسار. على سبيل المثال ، إذا كنت تريد رسم خط أحمر وخط أزرق - فسيتعين عليك إنشاء مسار جديد لرسم الخط الأزرق. إذا لم تقم بإنشاء مسار جديد ، فعند الاتصال strokeللمرة الثانية بعد تعيين لون نمط العرض إلى اللون الأزرق ، سيتم تلوين كلا الخطين باللون الأزرق. ومن ثم ، يتم تطبيق الأنماط على كل المسارات الفرعية ، سواء تم تحديدها بالفعل أم لا.

تم تحديد بعض خصائص كائن السياق ثنائي الأبعاد demoCanvasلهذا الغرض:

  • lineWidth: سمك الخطوط المراد رسمها. بشكل افتراضي ، هذا هو 1 ؛ ومن ثم ، استخدم المثالان أعلاه مخططًا سميكًا بمقدار 1 بكسل.
  • lineCap: هذا هو الغطاء المطبق في نهايات المسارات الفرعية (الأشكال). إنها سلسلة ويمكن أن تحتوي على ثلاث قيم صالحة: "بعقب" ، "دائري" ، "مربع" (انظر المثال 1.3 للوضوح البصري). "بعقب" سينتهي الخطوط بدون غطاء - مما ينتج عنه نهايات متعامدة صلبة مثل المستطيلات الرفيعة. تضيف كلمة "round" نصف دائرة إلى الأطراف لإعطاء نهايات ناعمة. يضيف "مربع" مربعًا في النهاية ، لكنه يبدو مثل "بعقب". يضيف كل من "round" و "square" قليلاً من الطول الإضافي لكل مسار فرعي.
  • lineJoin: هذا يقرر كيفية ربط خطين متداخلين. على سبيل المثال ، إذا كنت تريد إنشاء سهم لليمين (>) ، فيمكنك تغيير كيفية تشكيل الزاوية بهذه الخاصية. هذا له ثلاث قيم صالحة: "دائري" و "شطبة" و "ميتري". تحقق من المثال 1.4 لمعرفة كيفية تغيير الزوايا. (القيمة الافتراضية هي "ميتري"). "دائري" سيشكل زوايا دائرية ، بينما "شطبة" ستخلق زوايا صلبة ثلاثية الجوانب ، و "ميتري" ستشكل حافة حادة.
  • miterLimit: متى lineJoin="miter"، هذا يحدد المسافة القصوى ب / ث الزاوية الداخلية والخارجية للخط. انظر المثال 1.4 (ب) للوضوح البصري. إذا كان حد ميتري مرتفعًا جدًا ، فقد تحتوي الأسهم الحادة على مساحة مشتركة كبيرة ب / ث الخطين. إذا تم تجاوز حد ميتري ، فإن الشاشة تعود إلى الوصلة المائلة.

المثال 1.3 و 1.4:

في المثال 1.3 على اليسار ، يمكنك أن ترى كيف أن الخطوط المستديرة والمربعة بالخطوط تكون أطول من السد الافتراضي. (ملاحظة: كلما كان الخط أكثر سمكًا ، زادت الزيادة في الطول)

في المثال 1.4 (أ) ، يمكنك أن ترى كيف تعمل الصلات المستديرة والشطبة. الخطوط التي تم إنشاؤها متطابقة في الجزأين العلوي والسفلي. فقط lineJoinالخصائص مختلفة.

في المثال 4.1 (ب) ، يمكنك أن ترى كيف تعمل الوصلة الميتة ، وماذا يحدث إذا تم تمرير الطول المتقطع.

تم تحديد خصائص نمط العرض الإضافية:

  • font: تحدد هذه السلسلة الطريقة التي تريد بها تنسيق النص. على سبيل المثال ، demoCanvas.font="10px Times New Roman"هي قيمة خط صالحة.
  • textAlign: القيم الصالحة هي - "البداية" و "النهاية" و "اليسار" و "اليمين" و "المركز". الافتراضي هو "ابدأ".
  • textBaseline: القيم الصالحة هي - "أعلى" ، "معلق" ، "وسط" ، "أبجدي" ، "إيديوغرامي" ، "أسفل". الافتراضي هو "أبجدي".

طرق الرسم الفعلية

في الأمثلة حتى الآن ، ربما لاحظت أنني استخدمتها demoCanvas.stroke()قبل إغلاق كل مسار. تقوم طريقة السكتة الدماغية بالرسم الفعلي جزئيًا في تلك الأمثلة.

  • stroke: ترسم هذه الطريقة المخطط التفصيلي حول كل مسار فرعي (أشكال) وفقًا lineWidthللخصائص والخصائص ذات الصلة.
  • fill: تملأ هذه الطريقة الجزء الداخلي من الشكل الذي يتتبعه المسار. إذا لم يكن المسار مغلقًا ، فسيغلقه تلقائيًا عن طريق توصيل النقطة الأخيرة بالنقطة الأولى.
demoCanvas.moveTo(10,10); demoCanvas.lineTo(50, 50); demoCanvas.lineTo(10, 50); demoCanvas.fill();

لا يغلق الكود أعلاه المثلث (10،10) ، (50،50) ، (10،50) ولكن الاستدعاء يملأه fill()كما هو متوقع.

  • clearRect(x,y,w,h) : يمسح وحدات البكسل في المستطيل المتكون من المعلمات المحددة.
  • strokeRect(x,y,w,h) : Equivalent to calling rect and then stroke . It doesn’t add the rectangle to the current path — hence, you can change the style later and call stroke without affecting the rectangle formed.
  • fillRect(x,y,w,h) : Equivalent to calling rect and then fill . This also doesn’t add the rectangle to the current path.
  • strokeText(text,x,y,maxWidth) and fillText(text,x,y,maxWidth) : Writes the text at (x,y) according to the strokeStyle / fillStyleproperty. maxWidth is optional and defines the maximum length in pixels that you want the text to occupy. If the text is longer, then it is scaled to a smaller font. measureText("text").width can be used to find the display width of a piece of text, based on the current font.

NOTE: fillStyle and strokeStyle are the properties that can be set to any CSS color string to set the fill & stroke colors.

Gradients and Patterns

Out of the box, the 2D context provides linear and radial gradients. The createLinearGradient and createRadialGradient methods return CanvasGradient objects, which can then be modified what we want.

  • createLinearGradient(x0,y0,x1,y1) : Constructs a linear gradient that runs on the line x0,y0 to x1,y1 .
  • createRadialGradient(x0,y0,r0,x1,y1,r1) : Constructs a radial gradient that runs in the cone (of circles) with the top (inner circle) of radius r0and bottom (outer circle) of radius r1 . The first color would have a radius of r0 .

The CanvasGradient has one method: addColorStop(offset,color) . The gradient starts at 0 and ends at 1. The color at the position of offset will be set using this method. For example, addColorStop(.5, "green") will make the middle color green. Colors b/w two adjacent stops will be interpolated (mixed).

Example 1.6:

In the example on the left, you can see how linear and radial gradients work.

var linearGrad = demoCanvas.createLinearGradient(5,5,100,5); linearGrad.addColorStop(0, "blue"); linearGrad.addColorStop(.5, "green"); linearGrad.addColorStop(1, "red"); demoCanvas.strokeStyle=linearGrad; demoCanvas.lineWidth=50; demoCanvas.moveTo(5,5); demoCanvas.lineTo(100,5); demoCanvas.stroke();// change strokeStyle(l10) to fillStyle(l10) // and stroke() to fill(). Then, change lineTo(100,5) to rect(5,5,95,50). // Results should be almost same. demoCanvas.closePath(); demoCanvas.beginPath(); var radialGrad = demoCanvas.createRadialGradient(50,50,10,50,50,40); radialGrad.addColorStop(0, "blue"); radialGrad.addColorStop(.5, "green"); radialGrad.addColorStop(1, "red"); demoCanvas.fillStyle=radialGrad; demoCanvas.arc(50,50,30,0,2*Math.PI,false); demoCanvas.fill();

You might wonder what if x0,y0 and x1,y1 given to the linear/radial gradient are not equal to the line/arc we create? See Example 1.7

Example 1.7

var linearGrad = demoCanvas.createLinearGradient(5,5,100,5); linearGrad.addColorStop(0, "blue"); linearGrad.addColorStop(.5, "green"); linearGrad.addColorStop(1, "red"); demoCanvas.strokeStyle=linearGrad; demoCanvas.lineWidth=50; demoCanvas.moveTo(50,5); demoCanvas.lineTo(155,5); demoCanvas.stroke();// change strokeStyle(l10) to fillStyle(l10) // and stroke() to fill(). Then, change lineTo(100,5) to rect(5,5,95,50). // Results should be almost same. demoCanvas.closePath(); demoCanvas.beginPath(); var radialGrad = demoCanvas.createRadialGradient(50,50,10,50,50,40); radialGrad.addColorStop(0, "blue"); radialGrad.addColorStop(.5, "green"); radialGrad.addColorStop(1, "red"); demoCanvas.fillStyle=radialGrad; demoCanvas.arc(60,60,30,0,2*Math.PI,false); demoCanvas.fill();

Direct pixel manipulation & Images

The ImageData object can be used to manipulate individual pixels. It has three properties:

  • width : The width of the image data in device-display pixels.
  • height : The height of the image data in device-display pixels.
  • data : This is a Uint8ClampedArray (MDN doc here) which contains the individual pixel data in a series of (R,G,B,A) bytes for the top-most pixel to the bottom-right pixel. So the nth pixel’s red value would be at data[y*width+x] , green would be at data[y*width+x+1] , blue would be at data[y*width+x+2] , and the alpha would be at data[y*width+x+3] .

NOTE: A RGBA value can be used to represent a color — where R,G,B are the amounts of red, green, and blue and A is the opacity (alpha value). In the Canvas, these elements can have any integer value in [0, 255].

You can get a ImageData object with the following methods in the Canvas API:

  • createImageData(sw,sh) : This creates an ImageData object of width and height sw and sh , defined in CSS pixels. All the pixels will be initialized to transparent black (hex R,G,B=0, and also A=0).
CSS pixels might map to a different number of actual device pixels exposed by the object itself
  • createImageData(data) : Copies the given image-data and returns the copy.
  • getImageData(sx,sy,sw,sh) : Returns a copy of the canvas’s pixels in the rectangle formed by sx,sy,sw,sh in a ImageData object. Pixels outside the canvas are set to transparent black.
  • putImageData(imagedata,dx,dy,dirtyX,dirtyY,dirtyWidth,dirtyHeight): (The last four ‘dirty’ arguments are optional). Copies the pixel values in imagedata into the canvas rectangle at dx,dy . If you provide the last four arguments, it will only copy the dirty pixels in the image data (the rectangle formed at dirtyX,dirtyY of dimensions dirtyWidth*dirtyHeight ). Not passing the last four arguments is the same as calling putImageData(imagedata,dx,dy,0,0,imagedata.width,imagedata.height).
For all integer values of x and y where dirtyX ≤ x < dirtyX+dirtyWidth and dirtyY ≤ y < dirtyY+dirtyHeight, copy the four channels of the pixel with coordinate (x, y) in the imagedata data structure to the pixel with coordinate (dx+x, dy+y) in the underlying pixel data of the canvas.

Example 1.8:

I’ve filled the whole 400x400 canvas with random colors (fully opaque) using the getImageData/putImageData methods.

Note that using beginPath/closePath isn’t necessary to use the ImageData API — because your not using the Canvas API to form shapes/curves.

/* replace this line with demoCanvas.createImageData(390,390) instead. */ var rectData = demoCanvas.getImageData(10, 10, 390, 390); for (var y=0; y<390; y++) { for (var x=0; x<390; x++) { const offset = 4*(y*390+x);// 4* because each pixel is 4 bytes rectData.data[offset] = Math.floor(Math.random() * 256);// red rectData.data[offset+1] = Math.floor(Math.random() * 256);// green rectData.data[offset+2] = Math.floor(Math.random() * 256);// blue rectData.data[offset+3] = 255;// alpha, fully opaque } } demoCanvas.putImageData(rectData, 10, 10); /* beginPath/closePath aren't required for this code */

Images can be drawn onto the canvas directly. The drawImage can be used in three different ways to do so. It requires a CanvasImageSource as the pixel source.

A CanvasImageSource can be one of the following — HTMLImageElement, HTMLCanvasElement, HTMLVideoElement. To copy into the canvas, you can use a . You could also copy an existing canvas or the screenshot of a video!!!
  • drawImage(image,dx,dy) : Copies the image-source into the canvas at (dx,dy). The whole image is copied.
  • drawImage(image,dx,dy,dw,dh) : Copies the image-source into the rectangle in the canvas at (dx,dy) of size (dw,dh). It will be scaled down or scaled up if necessary.
  • drawImage(image,sx,sy,sw,sh,dx,dy,dw,dh) : Copies the rectangle in the image source sx,sy,sw,sh into the rectangle in the canvas dx,dy,dw,dhand scales up or down if required. However, if the rectangle sx,sy,sw,shhas parts outside the actual source — then the source rectangle is clipped to include the inbound parts and the destination rectangle is clipped in the same proportion; however, you shouldn’t pass any out-of-bounds rectangle — keep it simple, stupid.

Example 1.9:

var image = document.getElementById('game-img'); demoCanvas.drawImage(image, 50, 50, 200, 200, 100, 100, 200, 200); /* beginPath/closePath aren't required for this code */

NOTE: Add this to your HTML —

Transformations

Now we’re getting to the exciting parts of the Canvas API!!!

The Canvas uses a transformation matrix to transform the input (x, y) coordinates into the displayed (x, y) coordinates. Note that pixels drawn before the transformation are not transformed — they are untouched. Only stuff drawn after applying the transformation will be changed.

There are three in-built transformation methods:

  • scale(xf,yf) : This method scales the input by xf in the horizontal direction and yf in the vertical direction. If you want to magnify an image by a factor of m , then pass xf=yf=m . To stretch/squeeze an image horizontally by m , xf=m,yf=1 . To stretch/squeeze an image vertically by m , xf=1,yf=m .
  • rotate(angle) : Rotates the input by an angle of angle in the clockwise direction, in radians.
  • translate(dx,dy) : Shifts the input by dx,dy .

Example 2.0:

var image = document.getElementById('game-img'); demoCanvas.drawImage(image, 0, 0, 400, 400); demoCanvas.rotate(Math.PI / 6); demoCanvas.scale(2, 2); demoCanvas.translate(10, 10); demoCanvas.drawImage(image, 0, 0, 400, 400);
In Example 2.0, notice how the original image is intact. Only the second image (overlay) is transformed by three methods — rotate, scale, transform.

To revert all transformations:

demoCanvas.setTransform(1, 0, 0, 0, 0, 1); // sets the transform to the identity matrix

NOTE:

  • Changing the order of transformation can affect the final result.
  • For advanced users, you may want to look at the transform and setTransform methods. This will let you set the 3D transformation matrix directly.
  • getImageData and putImageData are not affected by the transform. That means if you draw a black rectangle using putImageData , it won’t be transformed (rotated/scaled/translated).
  • As changing the transform only works for drawings done after applying it, you can’t scale/rotate/translate the existing canvas directly (nor does getImageData and then putImageData work). You may have to create another hidden canvas of the same size — and then copy the image-data into the 2nd canvas, then use drawImage on the 2nd canvas.
  • Check this example: //canvasdemo2d.github.io/ (source: //github.com/canvasdemo2d/canvasdemo2d.github.io). Move your cursor over the canvas and see what it does. It won’t work on mobile phones, unfortunately. The cascading effect is due to the fact that I am translating the canvas w.r.t mouse using drawImage . drawImagethen writes to the same canvas it’s reading from, which causes the repeating pattern!

Hit Regions

As of the time of writing (March 2019), support for hit regions is experimental in Chrome and on Firefox. Mobile browser don’t even support it at all. Hence, I will explain to you “what” could hit regions be used for.

Hit regions are used to catch pointer events on the canvas and know “where” the user clicked. For example, you could have two rectangles A & B — when the user clicks A, you want to perform action $A and when the user clicks B, you want to perform action $B. Let’s walk through the whole process!

A hit region is related to these three things:

  • Path: The current path when the hit region was created (for example, a rectangle). All pointer events inside the path are routed to that hit region.
  • Id: An unique id string to identify the hit region by the event handler.
  • Control: An alternative DOM element ( HTMLButtonElement , for example) that gets the pointer events instead.

NOTE: The path is automatically provided by the canvas when adding a new hit region. Only one — id or control — is needed to form a hit region.

Methods for manipulating the hit-region list of a canvas are:

  • addHitRegion(options) : Takes a HitRegionOptions object and forms a hit-region enclosed by the current path. The options argument should be a string id property or a HTMLElementcontrol property.
  • removeHitRegion(id) : Removes the hit region with the id id so that it no longer receives any pointer events.
  • clearHitRegions() : Removes all hit regions.
demoCanvas.fillStyle = 'red'; demoCanvas.rect(10,10,60,60); demoCanvas.fill();// first rectangle demoCanvas.addHitRegion({ id: 'btn1' }); demoCanvas.fillStyle = 'blue'; demoCanvas.rect(10,110,60,60); demoCanvas.fill(); demoCanvas.addHitRegion({ id: 'btn2' }); document.getElementById('demo-canvas').onpointerdown = function(evt) { // demoCanvas is the 2d context, not the HTMLCanvasElement console.log('Hello id: ' + evt.region);// region is hitregion id } // This code might not work due to this being an // unsupported (new) feature of HTML5.

NOTE: Hit regions aren’t supported — but that doesn’t mean you have to use them to capture pointer events. You could create your “own hit-region list” and representations of boundaries of regions (cause you can’t get the current path from the canvas, too bad). In the document.getElementById('demo-canvas').onpointerdown method, get the current clientX,clientY properties and search through the hit region list. Based on the hit region that contains the point, you can perform the intended action.

States and the clip() method

State saving is a convenience provided by the W3C specification. You can save the current state of a canvas and restore it later.

You could also build such a system (partially) by writing your own JavaScript model. But you would have to save a quite of stuff: transformation matrix, hit-region list, style properties, and so on. Furthermore, you cannot revert the clipping area (we’ll get to the clipmethod in some time) directly.

NOTE: The save / restore methods do not save & restore the actual drawing/pixels. They only save other properties.

Hence, I would recommend heavily using the save & restore methods to go back and forth instead of erasing stuff on your own or making your own state-saving mechanism.

The CanvasRendering2DContext object has an associated state stack. The save method will push the current canvas state onto that stack, while the restore method will pop the latest state from the stack.

The Clipping Region

The clipping region is a specific region in which all drawings are to be done. Obviously, by default, the clipping region is the rectangle is the whole canvas. But you may want to draw in a specific region instead of the whole thing. For example, you may want to draw the lower half of a star formed by multiple lineTo methods.

So, for example, let’s say you know how to draw a star in the canvas. It touches all sides of the canvas. But now you want to only display the lower half of the star. In this scenario, you would:

  1. Save the state of the canvas
  2. Clip the lower half region
  3. Draw your star (as if on the whole canvas)
  4. Restore the canvas state

To clip a region, you have to call the clip() method which does the following:

The clip() method must create a new clipping region by calculating the intersection of the current clipping region and the area described by the path, using the non-zero winding number rule. Open subpaths must be implicitly closed when computing the clipping region, without affecting the actual subpaths. The new clipping region replaces the current clipping region.

When the context is initialized, the clipping region must be set to the rectangle with the top left corner at (0,0) and the width and height of the coordinate space.

— W3C Documentation for Canvas 2D Context

demoCanvas.save(); demoCanvas.rect(0, 200, 400, 200);// lower-half rectangle subpath demoCanvas.clip(); /* star drawing method */ demoCanvas.restore();

That’s all for now. I will write an article on animations with the canvas and how to write a custom interface completely on the canvas.

Further reading:

  • How to use Firebase for building Android multiplayer games
  • كيفية مزامنة تطبيق لعبتك عبر أجهزة Android متعددة
  • التبعيات الدائرية في JavaScript

Shukant Pal هو منشئ نواة Silcos. وهو متعلم شغوف ويمارس الآن تطوير تطبيقات الويب المتقدمة. لديه خبرة عملية مع React ونظامها البيئي.

جميع الاقتباسات مأخوذة من مستندات W3C لسياق Canvas 2D.

مرحبًا ، أنا شوكانت بال. أقوم بتطوير الكثير من تطبيقات الويب في وقت فراغي. تابعوني على وسائل التواصل الاجتماعي.