В интерфейсе добавление событий к элементам — обычное дело. Но в Canvas ничего нарисованного на нем невозможно получить, не говоря уже о добавлении событий, так что мы беспомощны в этом отношении? Конечно, нет! Мы, должно быть, использовали множество фреймворков Canvas в наших повседневных проектах. Мы обнаружили, что события в этих фреймворках используются очень зрело, и не возникло никаких особо серьезных проблем. Итак, в чем мы можем быть уверены, так это в том, что события в Canvas не являются чем-то неприкосновенным.
дурацкий подходМы все знаем, что когда элемент вызывает событие, позиция мыши в основном находится над элементом, поэтому мы, естественно, думаем о сравнении текущей позиции мыши с позицией, занимаемой объектом, чтобы мы могли получить ответ: должен ли объект инициировать событие. событие. Этот метод относительно прост, поэтому я не буду использовать код для его демонстрации, но поскольку я называю его методом дурака, очевидно, что это неэффективное решение. Потому что положение, занимаемое объектом, не всегда легко получить. Если это прямоугольник, круг и т. д., мы также можем получить его положение с помощью некоторых простых формул. Однако в многоугольниках со сложными точками или даже с некоторыми сторонами. полигон изогнут, очевидно, что нам крайне сложно и трудно получить положение, которое он занимает в данный момент, поэтому этот метод подходит только для использования в некоторых демках и не подходит для большинства состояний.
более разумный способПоскольку описанный выше метод уперся в стену, нам остается только найти другой путь. Просматривая CanvasAPI, я нашел метод isPointInPath, который, похоже, и есть то лекарство, которое мы ищем.
Представляем isPointInPathРоль isPointInPath: Как следует из названия, мы можем интуитивно знать, что этот метод используется для определения того, находится ли точка на пути.
Входные и выходные параметры isPointInPath: ctx.isPointInPath([path, ]x, y [, fillRule]). Этот метод имеет 4 параметра, из которых path и fillRule являются необязательными, а x и y являются обязательными. Мы вводим 4 параметра по очереди.
путь: Когда я увидел этот параметр, я сначала подумал, что это возвращаемое значение BeginPath или ClosePath. К сожалению, эти два метода не вернули значение. После проверки информации я обнаружил, что это объект нового конструктора Path2D. Конкретное использование конструктора 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, а черная часть — это область, в которую мы фактически добавили событие. После того, как мы щелкнем по черной области, она фактически сделает то, что мы хотим, и напечатает значение. это правда. Кажется, проблему мониторинга событий 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)) }) })
Мы внесли большие изменения в приведенный выше код. Мы поместили каждый путь в отдельную функцию и поместили их в массив. Когда срабатывает событие щелчка, мы очищаем Canvas, перемещаемся по массиву и перерисовываем его, а также принимаем решение каждый раз, когда рисуется Path. Таким образом, при вызове метода isPointInPath мы можем получить последний текущий Path в реальном времени, а затем получить его. судить о текущей точке Среди Пути.
Сейчас мы косвенно реализовали отдельный мониторинг событий для каждого Пути, но способ его реализации требует перерисовки снова и снова. Так есть ли способ отслеживать события без перерисовки?
Прежде всего, нам нужно знать, что причина повторной перерисовки заключается в том, что метод isPointInPath является последним объектом Path, который необходимо отслеживать. Однако, когда мы представили этот метод, мы сказали, что его первым параметром является объект Path. передайте этот параметр, Path больше не будет использовать последний Path, а будет использовать переданный нами Path. Теперь давайте проведем демонстрацию, чтобы проверить ее осуществимость:
const Canvas = document.getElementById('canvas') const ctx = Canvas.getContext('2d') const path1 = new Path2D(); path1.rect(10, 10, 100,100) const path2 = новый Path2D(); path2.moveTo(220, 60); path2.arc(170, 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)) })
Как показано на рисунке выше, мы щелкнули левый рисунок и напечатали true и false, щелкнули правый рисунок и напечатали false и true; Распечатанные результаты показывают, что проблем нет, но, поскольку совместимость необходимо улучшить, в настоящее время рекомендуется использовать метод перерисовки для прослушивания событий.
ЗаключениеЗдесь в основном описан мониторинг событий Canvas. Принцип очень прост, и каждый должен быть в состоянии освоить его.
адрес github, добро пожаловать, чтобы начать
приложениеДемо, написанное мной
const Canvas = document.getElementById('canvas') class прямоугольный {конструктор (ctx, {top = 0, left = 0, ширина = 30, высота = 50, фон = 'red' }) { this.ctx = ctx this. top = верх this.left = слева this.width = ширина this.height = высота this.background = фон } Painting () { 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() } Adjust (left, top) { this.left += left this.top += top } } class Circle { конструктор ( ctx, { center = [], radius = 10, background = 'blue' } ) { this.ctx = ctx this.center = [center[0] === не определено ? радиус : центр[0], центр[1] === не определено ? радиус : центр[1]] this.radius = радиус 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() } корректировка (слева, вверху) { this.center[0] += слева this.center[1] += сверху } } демонстрация класса { конструктор (холст) { this.canvasInfo = Canvas.getBoundingClientRect() this.renderList = [] this.ctx = Canvas.getContext('2d') this.canvas = холст this.rectangular = (config) => { let target = новый прямоугольник(this.ctx, {...config}) this.addRenderList(target) return this } this.circle = (config) => { let target = new круг (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) } Painting () { 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 selectedIndex = null this.renderList.forEach((it, index) => { it.painting() if (this.ctx.isPointInPath(startX, startY)) { selectedIndex = index } }) if (choosedIndex !== null) { this.itemToLast(choosedIndex) } document.addEventListener( 'mousemove', mousemoveEvent) document.addEventListener('mouseup', mouseupEvent) this.painting() }) function mousemoveEvent (e) { const target = that.renderList[that.renderList.length - 1] const currentX = 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 const currentY = e.clientY target.adjust(currentX - startX, currentY - startY) startX = currentX startY = currentY that.painting() document.removeEventListener('mousemove', mousemoveEvent) document.removeEventListener ('mouseup', mouseupEvent) } } } const да = новое демо (холст) .rectangular({}) .rectangular({сверху: 60, слева: 60, фон: 'синий'}) .rectangular({сверху: 30, слева: 20, фон: 'зеленый'}) .circle() .circle ({center: [100, 30], фон: «красный», радиус: 5}) .painting()
Выше приведено все содержание этой статьи. Я надеюсь, что она будет полезна для изучения всеми. Я также надеюсь, что все поддержат сеть VeVb Wulin.