Html5 canvas эффект наведения
Здравствуйте. У меня такой вопрос по canvas. Я отрисовал на холсте треугольную область. Мне нужно при наведении на неё курсором окрашивать её в другой цвет. Мне не понятно как с помощью методов canvas это сделать. Я вот нашёл один метод - isPointInPath, но мне кажется это будет очень затратно по производительности. Нужно будет на всё перемещения курсора брать его координаты и сравнивать. К тому же что делать, если на одном холсте отрисовано несколько фигур на основе одного и того же контекста (ctx), а закрашивать их нужно по отдельности при наведении на каждую? В общем есть ли ещё какие-то способы решить такую задачу?
|
Moonlight,
составь уравнение 3 прямых, и составь неравенство, и каждый раз подставляй в эти неравенства... но по-пойму легче isPointInPath У тебя прям такое нагруженное приложение, что ты так печешься? Если ты не понял о чем я, то вот пример, с кругом: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Документ без названия</title> </head> <canvas id='canvas'></canvas> <script> var _canvas = document.getElementById('canvas'), canvas = _canvas.getContext('2d'), x=50,y=50, radius = 30, colors = ['255,0,0', '0,255,0', '0,0,255', '0,0,0'], i = 0; canvas.strokeRect(0,0,_canvas.width,_canvas.height) canvas.arc(x,y, radius, 0, Math.PI *2, true) canvas.fill() _canvas.addEventListener('mousemove', function (event) { // Уравнение окружности: (x-x0)^2 + (y-y0)^2 = r^2 var mouseX = event.pageX, mouseY = event.pageY; if ( Math.pow(radius,2) > Math.pow((x-mouseX),2) + Math.pow((y-mouseY),2)) { canvas.clearRect(1,1,_canvas.width-2, _canvas.height-2) canvas.fillStyle = 'rgb('+colors[i]+')' canvas.arc(x,y,radius,0,Math.PI*2,true) canvas.fill(); i == colors.length ? i = 0 : i++; } }, false) </script> <body> </body> </html> |
Не я просто в принципе не знал как это делается, а про нагрузку просто мои догадки. То есть эффект наведение в canvas делается обычно методом isPointInPath? Мне в таком случае не понятно вот что. Вот нарисовал я несколько фигур в одном контексте.
Пример То есть оба треугольника отрисованы контекстом context. И теперь я хочу чтобы при наведении на верхний треугольник, он окрашивался в другой цвет, а нижний не изменялся. Но как мне это сделать, если метод isPointInPath применяется сразу ко всему контексту? var isPath = ctx.isPointInPath(50,50); То есть наверно придётся ещё рассчитывать нужный мне треугольник? Но с треугольниками ладно, их всего два, а мне нужно сделать карту с двумя десятками отметок, придётся создавать два десятка разных контекстов или как быть? Код ф-ции с треугольниками: function drawPath() { var canvas=document.getElementById("my-canvas"); if (canvas.getContext) { context = canvas.getContext("2d"); context.fillStyle = "rgba(255,0,0,1)"; context.beginPath(); context.moveTo(0,0); context.lineTo(200,0); context.lineTo(100,200); context.fill(); context.fillStyle = "rgba(0,0,255,.3)"; context.beginPath(); context.moveTo(0,200); context.lineTo(200,200); context.lineTo(100,0); context.fill(); } } |
Цитата:
function isPointInTriangle ( x, y, x1, y1, x2, y2, x3, y3 ) { return ( ( y-y1 ) * ( x2-x1 ) > ( x-x1 ) * ( y2-y1 ) && ( y-y2 ) * ( x3-x2 ) > ( x-x2 ) * ( y3-y2 ) && ( y-y3 ) * ( x1-x3 ) > ( x-x3 ) * ( y1-y3 ) ); } Точки треугольника должны следовать по часовой стрелке. |
В общем для моей задачи не понадобится ни векторная алгебра ни построение уравнения 3 прямых, думаю мне подойдёт метод isPointInPath). Мне бы только ещё пояснения по моему предыдущему посту не помешали.
|
Дзен-трансгуманист,
вау, прикольно) Moonlight, даже не думай об этом, я сейчас воспользовался им, вообще какой-то левый метод, у меня тот же вопрос: если несколько фигур, и каждая в beginPath() новом? Да и вообще, он посреди фигуры показывает: false, лучше используй то, что Дзен-гуманист предложил |
Подождите, вопрос был в том как это делается обычно, неужели обычно это делается так как предложил Дзен-гуманист? К тому же про треугольники я сказал для примера. На самом деле это будут произвольные области с потенциально неограниченным количеством углов, которые админ будет сам вырисовывать в панели управления. И вот их и нужно будет окрашивать.
|
Используй SVG, там события просто вешаются на DOM узлы:)
|
Ой, надо ж подкрепить свою функцию тестом. :)
<body style="padding: 0px; margin: 0px;"> <canvas id="canvas" width="300" height="300"></canvas> <script> var canvas = document.getElementById( 'canvas' ); var ctx = canvas.getContext( '2d' ); var triangle = [ { x: 200, y: 50 }, { x: 160, y: 240 }, { x: 60, y: 100 } ]; function isPointInTriangle ( x, y, x1, y1, x2, y2, x3, y3 ) { return ( ( y-y1 ) * ( x2-x1 ) > ( x-x1 ) * ( y2-y1 ) && ( y-y2 ) * ( x3-x2 ) > ( x-x2 ) * ( y3-y2 ) && ( y-y3 ) * ( x1-x3 ) > ( x-x3 ) * ( y1-y3 ) ); } function renderTest ( mouseX, mouseY ) { ctx.clearRect( 0, 0, canvas.width, canvas.height ); ctx.lineWidth = 2; ctx.fillStyle = isPointInTriangle( mouseX, mouseY, triangle[0].x, triangle[0].y, triangle[1].x, triangle[1].y, triangle[2].x, triangle[2].y ) ? 'red' : 'blue'; ctx.beginPath(); ctx.moveTo( triangle[0].x, triangle[0].y ); ctx.lineTo( triangle[1].x, triangle[1].y ); ctx.lineTo( triangle[2].x, triangle[2].y ); ctx.fill(); ctx.fillStyle = 'black'; ctx.beginPath(); ctx.moveTo( mouseX + 3, mouseY ); ctx.arc( mouseX, mouseY, 3, 0, Math.PI*2, false ); ctx.fill(); } canvas.addEventListener( 'mousemove', function ( event ) { renderTest( event.pageX, event.pageY ); }, false ); renderTest( -100, -100 ); </script> </body> |
|
вообще, если ты рисуешь на холсте то ты должен понимать что это уже ОТКОМПИЛЕННЫй враиант, тое тсь заверщенный, это просто набор пикселей, и еслиэти пиксели имеют логический смысл, то ест делятся на круги или квадраты, то ты должен где -то хранить модели эттих кругов и квадратов и их положения.. с точки зрения канваса вот ты отрисовал что то, и все, оно ни где не хранится... это просто набор пикселей. так что придумай свою систему как хранить модель элементов, придумай свою систему событий и.т.п. используй MVC черт возьми
|
Это все мне понятно. Просто было интересно как это обычно делается. Теперь мне ясно, что удобного решения "из коробки" не будет).
|
Цитата:
Мне тоже когда-то не понравилась extJS в силу лицензии и тяжести, я создал свою, только то, что мне нужно. А удобные решения из коробки скучны и неинтересны. Хотя иногда и лень писать свое. |
Да так то у меня проблем не возникает с наведением на множество элементов. Но мне не нравится мой способ. Он заключается в том, чтобы располагать на холсте canvas тэг map и в нём теги area с координатами областей canvas. И вот тогда каждая область это не просто набор пикселей, а элемент html. Ну и тут уже всё проще некуда. Gozar я это к тому, что рабочая модель у меня есть, но мне она не нравится. Дзен-трансгуманист а как примерно это уравнение будет выглядеть для произвольного количества углов?
|
Moonlight,
Простым будет только метод для выпуклых многоугольников. <body style="padding: 0px; margin: 0px;"> <canvas id="canvas" width="300" height="300"></canvas> <script> var canvas = document.getElementById( 'canvas' ); var ctx = canvas.getContext( '2d' ); var poly = [ { x: 20, y: 50 }, { x: 90, y: 10 }, { x: 220, y: 60 }, { x: 260, y: 180 }, { x: 180, y: 280 }, { x: 80, y: 220 } ]; function isPointInConvexPoly ( x, y, poly ) { var i = 0, pt1, pt2 = poly[ poly.length - 1 ]; while (( pt1 = pt2, pt2 = poly[ i ], ( y - pt1.y ) * ( pt2.x - pt1.x ) > ( x - pt1.x ) * ( pt2.y - pt1.y ) ) && ++i < poly.length ) {} return i == poly.length; } function renderTest ( mouseX, mouseY ) { ctx.clearRect( 0, 0, canvas.width, canvas.height ); ctx.lineWidth = 2; ctx.fillStyle = isPointInConvexPoly( mouseX, mouseY, poly ) ? 'red' : 'blue'; ctx.beginPath(); ctx.moveTo( poly[0].x, poly[0].y ); for ( var i = 1; i < poly.length; i++ ) { ctx.lineTo( poly[i].x, poly[i].y ); } ctx.fill(); ctx.fillStyle = 'black'; ctx.beginPath(); ctx.moveTo( mouseX + 3, mouseY ); ctx.arc( mouseX, mouseY, 3, 0, Math.PI*2, false ); ctx.fill(); } canvas.addEventListener( 'mousemove', function ( event ) { renderTest( event.pageX, event.pageY ); }, false ); renderTest( -100, -100 ); </script> </body> А если брать вообще произвольные фигуры, то там все на порядок-два сложнее, я даже не собираюсь возиться с этим... Смотрите код готовых библиотек, наверняка в том же libcanvas'е оно уже реализовано. |
Дзен-трансгуманист,
Как это называется, что за магия? |
9xakep,
Ноги растут из простого неравенства: y2 * x1 > x2 * y1 Если неравенство соблюдается, значит точка (x2, y2) находится справа от прямой ((0, 0) - (x1, y1)). (это относительно экранной системы координат, так как Y у нас направлен вниз, а не вверх) В функции последовательно для каждой из сторон производится трансляция к началу координат относительно левой ее точки, и цикл прерывается в случае несоблюдения неравенства. Если цикл пройден до конца, значит неравенство выполнено для всех сторон многоугольника и наша точка находится внутри. |
Часовой пояс GMT +3, время: 23:00. |