كواجهة أمامية، تعد إضافة الأحداث إلى العناصر أمرًا شائعًا. لكن في Canvas، أي شيء مرسوم عليه لا يمكن الحصول عليه، ناهيك عن إضافة الأحداث، فهل نحن عاجزون عن ذلك؟ بالطبع لا! لا بد أننا استخدمنا العديد من أطر عمل Canvas في مشاريعنا اليومية، وقد وجدنا أنه تم استخدام الأحداث بشكل ناضج جدًا في هذه الأطر، ولم تكن هناك مشاكل خطيرة بشكل خاص. إذن ما يمكننا التأكد منه هو أن الأحداث ليست شيئًا لا يمكن المساس به في Canvas.
نهج أحمقنعلم جميعًا أنه عندما يقوم عنصر ما بتشغيل حدث ما، فإن موضع الماوس يكون بشكل أساسي فوق العنصر، لذلك نفكر بطبيعة الحال في مقارنة موضع الماوس الحالي بالموضع الذي يشغله الكائن، حتى نتمكن من الحصول على ما إذا كان يجب أن يكون الكائن إثارة الحدث. هذه الطريقة بسيطة نسبيًا، لذا لن أستخدم التعليمات البرمجية لتوضيحها، ولكن بما أنني أسميها طريقة حمقاء، فمن الواضح أنها ليست حلاً فعالاً. نظرًا لأن الموضع الذي يشغله الكائن ليس من السهل جدًا الحصول عليه إذا كان مستطيلًا أو دائرة وما إلى ذلك، فيمكننا أيضًا الحصول على موضعه من خلال بعض الصيغ البسيطة، ومع ذلك، في المضلعات ذات النقاط المعقدة، أو حتى بعض جوانبها من الواضح أن المضلع منحني، وهو أمر معقد للغاية ويصعب علينا الحصول على الموضع الذي يشغله في الوقت الحالي، لذا فإن هذه الطريقة مناسبة للاستخدام فقط في بعض العروض التوضيحية، وليست مناسبة لمعظم الحالات.
طريقة أكثر ذكاءًوبما أن الطريقة المذكورة أعلاه قد وصلت إلى طريق مسدود، فلا يمكننا إلا أن نجد طريقة أخرى. أثناء تصفح CanvasAPI، وجدت طريقة هيPointInPath، والتي يبدو أنها الدواء الذي نبحث عنه.
تقديم isPointInPathدور isPointInPath: كما يوحي الاسم، يمكننا أن نعرف بشكل حدسي أن هذه الطريقة تُستخدم لتحديد ما إذا كانت النقطة موجودة في المسار.
معلمات الإدخال والإخراج لـ isPointInPath هي: ctx.isPointInPath([path,]x, y [, fillRule]). نقدم المعلمات الأربع بدورها.
المسار: عندما رأيت هذه المعلمة، اعتقدت في البداية أنها القيمة المرجعة لـ beginPath أو ClosePath، لسوء الحظ، لم تُرجع هاتان الطريقتان أي قيمة. الاستخدام المحدد لمنشئ Path2D. ومع ذلك، فمن المؤسف أن هذه الطريقة قد تكون بسبب مشاكل التوافق، ولم يتم استخدام بعض الأطر مفتوحة المصدر حاليًا.
x, y: من السهل فهم هاتين المعلمتين، فهما تمثلان المسافة بين المحور x والمحور y. وتجدر الإشارة إلى أن موضعهما النسبي هو الزاوية اليسرى العليا من اللوحة القماشية.
fillRule: غير صفري (افتراضي)، Evenodd. قاعدة الالتفاف غير الصفرية وقاعدة الفردي الزوجي هي قواعد في الرسومات لتحديد ما إذا كانت النقطة موجودة داخل مضلع أم لا. قاعدة الالتفاف غير الصفرية هي القاعدة الافتراضية في Canvas. إذا كنت تريد معرفة المزيد عن هاتين القاعدتين، يمكنك التحقق من المعلومات بنفسك، لذلك لن أعرضهما هنا.
بعد تقديم معلمات الإدخال أعلاه، ربما يستطيع الجميع تخمين معلمات الإخراج لأسلوب isPointInPath، وهي صحيحة وخاطئة.
باستخدام isPointInPathبعد تقديم طريقة isPointInPath في القسم السابق، فلنستخدمها الآن.
لنبدأ بعرض توضيحي بسيط:
const Canvas = document.getElementById('canvas') const ctx = Canvas.getContext('2d') ctx.beginPath() ctx.moveTo(10, 10) ctx.lineTo(10, 50) ctx.lineTo(50, 50) ) ctx.lineTo(50, 10) ctx.fillStyle= 'أسود' ctx.fill() ctx. ClosePath() Canvas.addEventListener('click', function (e) { const CanvasInfo = Canvas.getBoundingClientRect() console.log(ctx.isPointInPath(e.clientX - CanvasInfo.left, e.clientY - CanvasInfo.top)) })
كما هو موضح في الشكل، الجزء الرمادي هو المنطقة التي تشغلها اللوحة القماشية، والجزء الأسود هو المنطقة التي أضفنا فيها الحدث بالفعل بعد أن نقرنا على المنطقة السوداء، فإنه يفعل بالفعل ما نريده، والقيمة المطبوعة هذا صحيح. يبدو أن مراقبة أحداث Canvas يمكن حلها ببساطة، ولكن هل الأمر بهذه البساطة حقًا؟ من الواضح أنه مستحيل! لنأخذ مثالا آخر هناك مجالان في هذا الوقت، ونحن بحاجة إلى ربط أحداث مختلفة بهم:
const Canvas = document.getElementById('canvas') const ctx = Canvas.getContext('2d') ctx.beginPath() ctx.moveTo(10, 10) ctx.lineTo(10, 50) ctx.lineTo(50, 50) ) ctx.lineTo(50, 10) ctx.fillStyle= 'أسود' ctx.fill() ctx. ClosePath() ctx.beginPath() ctx.moveTo(100, 100) ctx.lineTo(100, 150) ctx.lineTo(150, 150) ctx.lineTo(150, 100) ctx.fillStyle = 'أحمر' ctx.fill() ctx. ClosePath() Canvas.addEventListener('click', function (e) { const CanvasInfo = Canvas.getBoundingClientRect() console.log(ctx.isPointInPath(e.clientX - CanvasInfo.left, e.clientY - CanvasInfo.top)) })
في هذا الوقت، لم تعد النتيجة كما توقعنا. عند النقر على المنطقة السوداء، تكون القيمة المطبوعة خاطئة، وعند النقر على المنطقة الحمراء، تكون القيمة المطبوعة صحيحة.
في الواقع، السبب بسيط جدًا، نظرًا للكود أعلاه، قمنا بالفعل بإنشاء مسارين، وتكتشف طريقة isPointInPath في الواقع ما إذا كانت النقطة الحالية في المسار الأخير. في المثال، المنطقة الحمراء هي المسار الأخير. لذلك فقط عند النقر على المنطقة الحمراء، يمكن الحكم على طريقة isPointInPath بأنها صحيحة. الآن دعونا نحول الكود:
const Canvas = document.getElementById('canvas') const ctx = Canvas.getContext('2d') Let drawArray = [] function draw1 () { ctx.beginPath() ctx.moveTo(10, 10) ctx.lineTo(10) ، 50) ctx.lineTo(50, 50) ctx.lineTo(50, 10) ctx.fillStyle= 'black' ctx.fill() } function draw2 () { ctx.beginPath() ctx.moveTo(100, 100) ctx.lineTo(100, 150) ctx.lineTo(150, 150) ctx.lineTo (150، 100) ctx.fillStyle= 'أحمر' ctx.fill() ctx. ClosePath() } drawArray.push(draw1, draw2) drawArray.forEach(it => { it() }) Canvas.addEventListener('click', function (e) { ctx.clearRect(0 ، 0، 400، 750) const CanvasInfo = Canvas.getBoundingClientRect() drawArray.forEach(it => { it() console.log(ctx.isPointInPath(e.clientX - CanvasInfo.left, e.clientY - CanvasInfo.top)) }) })
لقد أجرينا تعديلًا كبيرًا على الكود أعلاه، حيث نضع كل مسار في وظيفة منفصلة ونضعه في مصفوفة. عندما يتم تشغيل حدث النقر، نقوم بمسح اللوحة القماشية، واجتياز المصفوفة وإعادة الرسم، وإصدار حكم في كل مرة يتم فيها رسم مسار. لذلك، عند استدعاء طريقة isPointInPath، يمكننا الحصول على المسار الحالي الأخير في الوقت الفعلي، ثم الحكم على النقطة الحالية.
لقد قمنا الآن بتنفيذ مراقبة منفصلة للأحداث لكل مسار بشكل غير مباشر، ولكن طريقة تنفيذها تتطلب إعادة الرسم مرارًا وتكرارًا، فهل هناك طريقة لمراقبة الأحداث دون إعادة الرسم؟
بادئ ذي بدء، نحتاج إلى معرفة أن سبب إعادة الرسم مرارًا وتكرارًا هو أن طريقة isPointInPath هي آخر مسار يتم مراقبته. ومع ذلك، عندما قدمنا هذه الطريقة، قلنا أن المعلمة الأولى لها هي كائن المسار بتمرير هذه المعلمة، لن يأخذ المسار المسار الأخير بعد الآن، بل سيستخدم المسار الذي مررناه. الآن لنقم بعمل عرض توضيحي للتحقق من جدواه:
const Canvas = document.getElementById('canvas') const ctx = Canvas.getContext('2d') const path1 = new Path2D(); path1.rect(10, 10, 100,100); ctx.fill(path1) const path2 = new Path2D(); path2.moveTo(220, 60); 60, 50, 0, 2 * Math.PI); ctx.stroke(path2) Canvas.addEventListener('click', function (e) { console.log(ctx.isPointInPath(path1, e.clientX, e.clientY) ) console.log(ctx.isPointInPath(path2, e.clientX, e.clientY)) })
كما هو موضح في الصورة أعلاه، نقرنا على الرسم الأيسر وطبعنا صح وخطأ؛ نقرنا على الرسم الأيمن وطبعنا خطأ وصحيح. تظهر النتائج المطبوعة أنه لا توجد مشكلة، ولكن بما أن التوافق يحتاج إلى تحسين، فمن المستحسن حاليًا استخدام طريقة إعادة الرسم للاستماع إلى الأحداث.
خاتمةتمت تغطية مراقبة أحداث Canvas بشكل أساسي هنا. المبدأ بسيط للغاية ويجب أن يكون الجميع قادرين على إتقانه.
عنوان جيثب، مرحبا بكم في البدء
زائدةعرض توضيحي كتبته بنفسي
const قماش = document.getElementById('canvas') فئة مستطيلة { مُنشئ ( ctx, { top = 0, left = 0, width = 30, height = 50, الخلفية = 'red' } ) { this.ctx = ctx this. أعلى = أعلى this.left = اليسار this.width = العرض this.height = الارتفاع this.background = الخلفية } اللوحة () { this.ctx.beginPath() this.ctx.moveTo(this.left, this.top) this.ctx.lineTo(this.left + this.width, this.top) this.ctx.lineTo(this.left + this.width, this.top + this.height) this.ctx.lineTo(this.left, this.top + this.height) this.ctx.fillStyle = this.background this.ctx.fill() this.ctx. ClosePath() } ضبط (يسار، أعلى) { this.left += left this.top += top } } دائرة الفئة { مُنشئ ( ctx, { center = [], radius = 10, الخلفية = 'blue' } ) { this.ctx = ctx this.center = [center[0] === نصف القطر: المركز[0]، المركز[1] === نصف القطر: المركز[1]] هذا. نصف القطر = نصف القطر this.background = الخلفية } تلوين () { this.ctx.beginPath() this.ctx.arc(this.center[0], this.center[1], this.radius, 0, Math.PI * 2, false) this.ctx.fillStyle = this.background this.ctx.fill() this.ctx. ClosePath() } Adjust (left, top) { this.center[0] += left this.center[1] += top } } عرض توضيحي للفئة { مُنشئ (canvas) { this.canvasInfo = Canvas.getBoundingClientRect() this.renderList = [] this.ctx = Canvas.getContext('2d') this.canvas = Canvas this.rectangular = (config) => { Let target = مستطيل جديد (this.ctx, {...config}) this.addRenderList(target) يُرجع هذا } this.circle = (config) => { Let target = new Circle(this.ctx, {...config}) this.addRenderList(target) يُرجع هذا } this.addEvent() } addRenderList (target) { this.renderList.push(target) } itemToLast (index) { const lastItem = this.renderList.splice(index, 1)[0] this.renderList.push(lastItem) } اللوحة () { this.ctx.clearRect(0, 0, this.canvasInfo.width, this.canvasInfo.height) this.renderList.forEach(it => it.painting()) } addEvent () { const that = this Let startX, startY Canvas.addEventListener('mousedown', e => { startX = e.clientX startY = e.clientY Let ChooseIndex = null this.renderList.forEach((it, Index) => { it.painting() if (this.ctx.isPointInPath(startX, startY)) { ChoosedIndex = Index } }) if (choosedIndex !== null) { this.itemToLast(choosedIndex) } document.addEventListener( 'mousemove'، mousemoveEvent) document.addEventListener('mouseup'، mouseupEvent) this.painting() }) وظيفة mousemoveEvent (e) { const target = that.renderList[that.renderList.length - 1] constcurrentX = e.clientX const currentY = e.clientY target.adjust(currentX - startX,currentY - startY) startX =currentX startY = currentY that.painting() } function mouseupEvent (e) { const target = that.renderList[that.renderList.length - 1] const currentX = e.clientX constcurrentY = e.clientY target.adjust(currentX - startX,currentY - startY) startX =currentX startY =currentY that.painting() document.removeEventListener('mousemove', mousemoveEvent) document.removeEventListener ('mouseup', mouseupEvent) } } } const Yes = new demo(canvas) .rectangular({}) .rectangular({أعلى: 60، يسار: 60، الخلفية: 'أزرق'}) .rectangular({أعلى: 30، يسار: 20، الخلفية: 'أخضر'}) .circle() .circle ({المركز: [100، 30]، الخلفية: 'أحمر'، نصف القطر: 5}) .painting()
ما ورد أعلاه هو المحتوى الكامل لهذه المقالة وآمل أن يكون مفيدًا لدراسة الجميع وآمل أيضًا أن يدعم الجميع شبكة VeVb Wulin.