Помогите понять Canvas
Не могу понять как работать с канвасом.
Вот мой пример (canvas и блок с размерами): <style>.skat-size{display:inline-block;margin:15px 10px;padding:0 10px 10px;border:solid 1px #ccc;}</style> <canvas id="valma" width="420" height="220">Конверт</canvas> <div class="row plos" data-area="valma"> <div class="skat-size"> <h4>Трапеция</h4> <div>Карниз:</div> <div> <input class="valma" type="text" /><span class="unit"> м</span> </div> <div>Конёк:</div> <div> <input class="valma" type="text" /><span class="unit"> м</span> </div> <div>Высота:</div> <div> <input class="valma" type="text" /><span class="unit"> м</span> </div> </div> <div class="skat-size"> <h4>Треугольный скат</h4> <div>Длина карниза:</div> <div> <input class="valma" type="text" /><span class="unit"> м</span> </div> <div>Высота ската:</div> <div> <input class="valma" type="text" /><span class="unit"> м</span> </div> </div> </div> <script> var canvasValma = document.querySelector('canvas#valma'); function canvasRoof() { let valm_sk1 = '#bbb', valm_sk2 = '#ccc', valm_sk3 = '#ddd', valm_sk4 = '#ccc'; let valma = canvasValma.getContext('2d'); if (canvasValma && canvasValma.getContext) { valma.fillStyle = '#fff'; valma.fillRect(0, 0, 420, 220); valma.beginPath(); // 1 сторона valma.moveTo(0, 0); valma.lineTo(400, 0); valma.lineTo(300, 100); valma.lineTo(100, 100); valma.fillStyle = valm_sk1; valma.fill(); valma.beginPath(); // 2 сторона valma.moveTo(400, 0); valma.lineTo(300, 100); valma.lineTo(400, 200); valma.fillStyle = valm_sk2; valma.fill(); valma.beginPath(); // 3 сторона valma.moveTo(0, 200); valma.lineTo(100, 100); valma.lineTo(300, 100); valma.lineTo(400, 200); valma.fillStyle = valm_sk3; valma.fill(); valma.beginPath(); // 4 сторона valma.moveTo(0, 0); valma.lineTo(100, 100); valma.lineTo(0, 200); valma.fillStyle = valm_sk4; valma.fill(); } } canvasRoof(); function razmStrel(color, v, lw) { // Принимает: color - цвет, v - координаты (массив), lw - толщина линии let strelka = canvasValma.getContext('2d'); if (canvasValma && canvasValma.getContext) { // Размерные стрелки strelka.strokeStyle = color; strelka.lineWidth = lw; strelka.beginPath(); strelka.moveTo(v[0], v[1]); strelka.lineTo(v[2], v[3]); strelka.stroke(); strelka.beginPath(); strelka.lineWidth = 1; strelka.moveTo(v[4], v[5]); strelka.lineTo(v[6], v[7]); strelka.lineTo(v[8], v[9]); strelka.stroke(); strelka.beginPath(); strelka.lineWidth = 1; strelka.moveTo(v[10], v[11]); strelka.lineTo(v[12], v[13]); strelka.lineTo(v[14], v[15]); strelka.stroke(); } } var inp = document.querySelectorAll('input'); for (let i = 0; i < inp.length; i++) { inp[i].addEventListener('focus', function() { let color = 'green', v, lw = 2; switch (i) { //Для вальмы case 0: v = [0,210, 400,210, 15,207, 0,210, 15,213, 385,207, 400,210, 385,213]; break; case 1: v = [100,115, 300,115, 115,112, 100,115, 115,118, 285,112, 300,115, 285,118]; break; case 2: v = [120,100, 120,200, 117,115, 120,100, 123,115, 117,185, 120,200, 123,185]; break; case 3: v = [410,0, 410,200, 407,15, 410,0, 413,15, 407,185, 410,200, 413,185]; break; case 4: v = [300,100, 400,100, 315,97, 300,100, 315,103, 385,97, 400,100, 385,103]; break; } razmStrel(color, v, lw); }) inp[i].addEventListener('blur', () => canvasRoof()); } </script> Я нарисовал фигуру, но не знаю как мне реализовать интерактивность. Нужно сигменты из канваса на отдельные части разделять или как это делается? Например, чтобы при наведении курсора на блок с размерами (инпутами), на канвасе менял цвет соответствующий фрагмент. А при установке фокуса в инпуте, появлялась размерная стрелка в нужном месте, при выходе из фокуса стрелка убиралась. |
Думал-думал, попробовал несколько идей и смог функцию надумать добавляющую стрелки (код в топике исправил).
Только при фокусе в инпуте стрелки добавляются, а как их убрать когда фокус снимается??? |
Цитата:
|
MC-XOBAHCK,
<!DOCTYPE html> <html> <head> <title>Untitled</title> <meta charset="utf-8"> </head> <body> <div id="slider"></div> <style>.skat-size{display:inline-block;margin:15px 10px;padding:0 10px 10px;border:solid 1px #ccc;}</style> <canvas id="valma-test2" width="420" height="220">Конверт</canvas> <div class="row plos" data-area="valma"> <div class="skat-size"> <h4>Трапеция</h4> <div>Карниз:</div> <div> <input class="valma" type="text" /><span class="unit"> м</span> </div> <div>Конёк:</div> <div> <input class="valma" type="text" /><span class="unit"> м</span> </div> <div>Высота:</div> <div> <input class="valma" type="text" /><span class="unit"> м</span> </div> </div> <div class="skat-size"> <h4>Треугольный скат</h4> <div>Длина карниза:</div> <div> <input class="valma" type="text" /><span class="unit"> м</span> </div> <div>Высота ската:</div> <div> <input class="valma" type="text" /><span class="unit"> м</span> </div> </div> </div> <script> let valm_sk1 = '#bbb', valm_sk2 = '#ccc', valm_sk3 = '#ddd', valm_sk4 = '#ccc'; var canvasValma2 = document.querySelector('#valma-test2'); function draw() { let valma = canvasValma2.getContext('2d'); if (valma) { valma.clearRect(0, 0, 420, 220); valma.beginPath(); // 1 сторона valma.moveTo(0, 0); valma.lineTo(400, 0); valma.lineTo(300, 100); valma.lineTo(100, 100); valma.fillStyle = valm_sk1; valma.fill(); valma.beginPath(); // 2 сторона valma.moveTo(400, 0); valma.lineTo(300, 100); valma.lineTo(400, 200); valma.fillStyle = valm_sk2; valma.fill(); valma.beginPath(); // 3 сторона valma.moveTo(0, 200); valma.lineTo(100, 100); valma.lineTo(300, 100); valma.lineTo(400, 200); valma.fillStyle = valm_sk3; valma.fill(); valma.beginPath(); // 4 сторона valma.moveTo(0, 0); valma.lineTo(100, 100); valma.lineTo(0, 200); valma.fillStyle = valm_sk4; valma.fill(); } } draw() ; function razmStrel(color, v, lw){ // Принимает: color - цвет, v - координаты (массив), lw - толщина линии let strelka = canvasValma2.getContext('2d'); if (strelka) { // Размерные стрелки strelka.strokeStyle = color; strelka.lineWidth = lw; strelka.beginPath(); strelka.moveTo(v[0], v[1]); strelka.lineTo(v[2], v[3]); strelka.stroke(); strelka.beginPath(); strelka.lineWidth = 1; strelka.moveTo(v[4], v[5]); strelka.lineTo(v[6], v[7]); strelka.lineTo(v[8], v[9]); strelka.stroke(); strelka.beginPath(); strelka.lineWidth = 1; strelka.moveTo(v[10], v[11]); strelka.lineTo(v[12], v[13]); strelka.lineTo(v[14], v[15]); strelka.stroke(); } } var inp = document.querySelectorAll('input'); [].forEach.call(inp, function(node,i) { let color = 'green', v, lw = 2; switch (i) { case 0: v = [0,210, 400,210, 15,207, 0,210, 15,213, 385,207, 400,210, 385,213]; break; case 1: v = [100,115, 300,115, 115,112, 100,115, 115,118, 285,112, 300,115, 285,118]; break; case 2: v = [120,100, 120,200, 117,115, 120,100, 123,115, 117,185, 120,200, 123,185]; break; case 3: v = [410,0, 410,200, 407,15, 410,0, 413,15, 407,185, 410,200, 413,185]; break; case 4: v = [300,100, 400,100, 315,97, 300,100, 315,103, 385,97, 400,100, 385,103]; break; } node.addEventListener("focus", function() { draw(); razmStrel(color, v, lw); }); node.addEventListener("blur", draw) }); </script> </body> </html> |
Dilettante_Pro, Спасибо! Я понял идею.
Со стрелками получилось, пришлось ток ещё сначала весь канвас белым прямоугольником залить. |
рони, Спасибо! Я не обновлял страницу и увидел пост поздно, поэтому успел исправить код в топике (так бы новым постом свой код сделал).
А в вашем коде меня смущают вот такие моменты: [].forEach.call(inp, function(node,i) {.... node.addEventListener("blur", draw)... Мне ещё до этого уровня как минимум год сидеть за JS, а пока я этого не понимаю : ( Поставлю ваш вариант, надеюсь при расширении приложения проблем не будет, но если что откачу к своему варианту. |
Цитата:
Цитата:
|
MC-XOBAHCK,
[].forEach.call |
После простановки координат для 15 стрелочек (я посчитал), случилась эврика (помидор на голову не падал).
Я понял что мне достаточно всего лишь 3 координаты, чтобы нарисовать стрелочку. Вывел две таких формулы: //стрелка вертикально (x, y1, y2): v = [x,y1, x,y2, (x - 3),(y1 + 30), x,y1, (x + 3),(y1 + 30), (x - 3),(y2 - 30), x,y2, (x + 3),(y2 - 30)] //стрелка горизонтально (x1, x2, y): v = [x1,y, x2,y, (x1 + 30),(y - 3), x1,y, (x1 + 30),(y + 3), (x2 - 30),(y - 3), x2,y, (x2 + 30),(y + 3)] Поэтому решил добавить к стрелкам такие параметры: size: 'little', 'normal', 'big' orientation: 'horizontal', 'vertical' Ориентация - по какой формуле считать координаты. Поставлю на if (orientation == 'horizontal') {...} Размер - это заменю из формулы значения 3 и 30 (ширина и длина самого указателя стрелки), чтобы на маленьких расстояниях можно было уменьшать, на больших - делать побольше. |
Я идею понял, что можно ещё проще всё сделать. Просидел вчера весь день, но прокачаться до уровня "Говнокодер повелитель стрелочек" не получилось.
Пришёл к тому что теперь нужна только одна точка с координатами x,y. Ввёл новый параметр - длина стрелочки (в px). Так удобнее. Вторую точку находим через формулу прибавив длину стрелочки к координате x. Убрал ориентацию (горизонтально/вертикально) и вместо неё добавил rotate в градусах (0-360). При повороте началось смещение. Как я понял, оно регулируется через трансформ, но пока я к этому не пришёл. Написал такой онлайн повелитель стрелочек, чтобы понять смещение. Но в нём баг. При указании координат x и y - поведение как будто умножается а не суммируется. Интерпритатор может оценивать в формуле переменную x2 как помножить на два? Пока завис на этом баге. До смещения ещё не дошёл, попробовал смотреть поведение меняя параметры через strelka.translate(10, 10); но пока мне баг мешает с этим поработать. <canvas style="border:1px solid black"></canvas> <p>Координата X: <input id="x" type="number" min="0" value="0"> Координата Y: <input id="y" type="number" min="0" value="0"></p> <p>Поворот (Градусы): <input id="deg" type="number" min="0" max="360" value="0"> Длина стрелки (px): <input id="w" type="number" min="0" value="400"></p> <script> function risunok() { let v = [], lstr, hstr, color = 'red', // цвет стрелочки lw = 2, //толщина линии deg = +inputDeg.value, x = +inputX.value, y = +inputY.value, w = +inputWidth.value; var canvasRoof = document.querySelector('canvas'); let strelka = canvasRoof.getContext('2d'); switch (true) { //сами указатели стрелочек - длина и высота (в зависимости от длины стрелочки) case (w >= 300): lstr = 30; hstr = 3; break; case (w >= 100 && w <300): lstr = 20; hstr = 2; break; case (w < 100): lstr = 10; hstr = 2; break; } let x2 = (x + w);//координата x2 v = [x,y, x2,y, (x + lstr),(y - hstr), x,y, (x + lstr),(y + hstr), (x2 - lstr),(y - hstr), x2,y, (x2 - lstr),(y + hstr)];//координаты canvasRoof.width = 700; canvasRoof.height = 450; if (canvasRoof && canvasRoof.getContext) { strelka.fillStyle = '#fff'; strelka.fillRect(0, 0, canvasRoof.width, canvasRoof.height); strelka.translate(x, y); strelka.rotate((Math.PI / 180) * deg); //поворот на .. градусов strelka.translate(-x, -y); strelka.strokeStyle = color; strelka.lineWidth = lw; strelka.beginPath(); strelka.moveTo(v[0], v[1]); strelka.lineTo(v[2], v[3]); strelka.stroke(); strelka.beginPath(); strelka.lineWidth = 1; strelka.moveTo(v[4], v[5]); strelka.lineTo(v[6], v[7]); strelka.lineTo(v[8], v[9]); strelka.stroke(); strelka.fillStyle = color; strelka.fill(); strelka.beginPath(); strelka.lineWidth = 1; strelka.moveTo(v[10], v[11]); strelka.lineTo(v[12], v[13]); strelka.lineTo(v[14], v[15]); strelka.stroke(); strelka.fillStyle = color; strelka.fill(); } } const inputDeg = document.querySelector('#deg'); const inputX = document.querySelector('#x'); const inputY = document.querySelector('#y'); const inputWidth = document.querySelector('#w'); inputDeg.addEventListener('input', () => risunok()); inputX.addEventListener('input', () => risunok()); inputY.addEventListener('input', () => risunok()); inputWidth.addEventListener('input', () => risunok()); risunok(); </script> |
Часовой пояс GMT +3, время: 14:31. |