Javascript-форум (https://javascript.ru/forum/)
-   Элементы интерфейса (https://javascript.ru/forum/dom-window/)
-   -   Помогите разобраться с canvas! (рисование через циклы). (https://javascript.ru/forum/dom-window/75259-pomogite-razobratsya-s-canvas-risovanie-cherez-cikly.html)

drakulawz 17.09.2018 13:42

Помогите разобраться с canvas! (рисование через циклы).
 
Всем доброго времени суток.
Увидел на хабре как циклом стирали квадраты и получилась шахматная доска. Всё вроди бы понятно, но появились некоторые вопросы, и, возможно, это даже не о канвасе, а скорее об циклах.:-?
Буду очень благодарен если мне помогут разобраться в некоторых вопросах по рисованию разных фигур через циклы.
1. Что означает число 20 в этой и подобных строках:
ctx.clearRect(20 + i * 98.75, 20 + j * 98.75, 98.75, 98.75);
и как узнать какое число нужно ставить?
2. После долгой и мучительной расстановки шашек, методом тыка (подставлял разные вариации и наборы цифр в цикл))), я таки выставил их как надо (более-менее), но всё же: как убрать лишние рядки так как их вышло по четыре у каждой из сторон, а не 3, как надо?
3. Возможно ли как-то вставить moveTo(), что бы не было видно переходов между кругами? Я знаю, что если бы я использовал метод fill() то линий не было бы, но мне не нужно заполнение.

var canv = $('#canv')[0],
        ctx = canv.getContext('2d');
    canv.width = 900;
    canv.height = 900;
    ctx.strokeRect(15, 15, 800, 800);  // внешняя рамка доски;
    ctx.strokeRect(20, 20, 790, 790);  // внутренняя рамка;
    ctx.fillRect(20, 20, 790, 790);       // заливка доски;
    for (var i = 0; i < 8; i += 2)
        for (var j = 0; j < 8; j += 2) {
            // создание размметки шахматной доски;
            ctx.clearRect(20 + i * 98.75, 20 + j * 98.75, 98.75, 98.75);
            ctx.clearRect(20 + (i + 1) * 98.75, 20 + (j + 1) * 98.75, 98.75, 98.75);
        }

    for (var i = 0; i < 16; i += 4) {
        for (var j = 0; j < 6; j += 4) {
            // расстановка синих шашек;
            ctx.beginPath();
            ctx.strokeStyle = 'blue';
            ctx.arc(170 + i * 49.375, 70 + j * 49.375, 35, 0, Math.PI * 2, true);
            ctx.arc(-80 + (i + 3) * 49.375, 20 + (j + 3) * 49.375, 35, 0, Math.PI * 2, true);
            ctx.stroke();
        }
    }
    for (var q = 0; q < 16; q += 4) {
        for (var w = 0; w < 6; w += 4) {
            // расстановка белых шашек;
            ctx.beginPath();
            ctx.strokeStyle = 'white';
            ctx.arc(170 + q * 49.375, 470 + w * 49.375, 35, 0, Math.PI * 2, true);
            ctx.arc(20 + (q + 1) * 49.375, 370 + (w + 4) * 49.375, 35, 0, Math.PI * 2, true);
            ctx.stroke();
        }
    }


Если я правильно всё понял, то здесь нужно использовать по три цикла для каждого ряда шашек каждому из сторон, чтобы не было остаточных линий и было всего три ряда в место 4.
Надеюсь на помощь в разъяснении этих вопросов и подтверждении/опровержении моих домыслов.

Сделал как предполагал:
for (var i = 0; i < 16; i += 4) {
        ctx.beginPath();
        ctx.strokeStyle = 'blue';
        ctx.arc(170 + i * 49.375, 70, 35, 0, Math.PI * 2, true);
        ctx.stroke();
    }
    
    for (var i = 0; i < 16; i += 4) {
        ctx.beginPath();
        ctx.strokeStyle = 'blue';
        ctx.arc(70 + i * 49.375, 170, 35, 0, Math.PI * 2, true);
        ctx.stroke();
    }
    
    for (var i = 0; i < 16; i += 4) {
        ctx.beginPath();
        ctx.strokeStyle = 'blue';
        ctx.arc(170 + i * 49.375, 265, 35, 0, Math.PI * 2, true);
        ctx.stroke();
    }


Но такой код великоват. Проще никак нельзя?

Dilettante_Pro 17.09.2018 16:20

<canvas id = 'canv'></canvas>
<script>
var canv = document.querySelector('#canv'),
        ctx = canv.getContext('2d');
    canv.width = 900;
    canv.height = 900;
    ctx.strokeRect(15, 15, 800, 800);  // внешняя рамка доски;
    ctx.strokeRect(20, 20, 790, 790);  // внутренняя рамка;
    ctx.fillRect(20, 20, 790, 790);       // заливка доски;
    for (var i = 0; i < 8; i += 2)
        for (var j = 0; j < 8; j += 2) {
            // создание размметки шахматной доски;
            ctx.clearRect(20 + i * 98.75, 20 + j * 98.75, 98.75, 98.75);
            ctx.clearRect(20 + (i + 1) * 98.75, 20 + (j + 1) * 98.75, 98.75, 98.75);
        }

for (j = 0; j < 3; j++) {
   for (var i = 0; i < 16; i += 4) {
        ctx.beginPath();
        ctx.fillStyle = 'blue';
        ctx.arc(69.375 + 98.75 * ((j + 1) % 2) + i * 49.375, (69.375 + 98.75 * j), 35, 0, Math.PI * 2, true);
        ctx.fill();
    }
 }   

for (j = 0; j < 3; j++) {
   for (var i = 0; i < 16; i += 4) {
        ctx.beginPath();
        ctx.fillStyle = 'white';
        ctx.arc(69.375 + 98.75 * (j  % 2) + i * 49.375, (563.125 + 98.75* j), 35, 0, Math.PI * 2, true);
        ctx.fill();
    }
 }   
</script>

20 - это отсюда: ctx.strokeRect(20, 20, 790, 790);

69.375 = 20 + 98.75 / 2

ЗЫ: использовал fill, т.к. stroke бледновато выглядит

drakulawz 17.09.2018 18:33

Цитата:

Сообщение от Dilettante_Pro
20 - это отсюда: ctx.strokeRect(20, 20, 790, 790);

Ага, понял, т.е. это пиксели зарезервированные под рамки, а я здесь высшую математику искал.:blink:
Цитата:

Сообщение от Dilettante_Pro
использовал fill, т.к. stroke бледновато выглядит

Да, так проще наверное, а то если делать много циклов для отрисовки stroke получается огромезный код - бред кароче.
Благодарю за пояснение.
Цитата:

Сообщение от drakulawz
как узнать какое число нужно ставить?

Есть ли тут какая-то "премудрость"? Или тупо считать на калькуляторе всякие ранее отрисованные высоты и искать нужные центры...?

рони 17.09.2018 19:19

canvas шашки
 
:write: ... шифровка из центра, изменить ww!!!
Пример: canvas шашки
<!DOCTYPE html>

<html>
<head>
  <title>Untitled</title>
  <meta charset="utf-8">

</head>

<body>
<canvas id = 'canv'></canvas>
<script>
var canv = document.querySelector('#canv'),
        ctx = canv.getContext('2d');
    var ww = 387, k = 20, kk = k-5 , w = ww - k/2,   v = w/8, vv = v/2,  vd = vv + k, dr = vv-5;
    canv.width = ww + k;
    canv.height = ww + k;
    ctx.strokeRect(kk, kk, ww, ww);  // внешняя рамка доски;
    ctx.strokeRect(k, k, w, w);  // внутренняя рамка;
    ctx.fillRect(k, k, w, w);       // заливка доски;

    for (var i = 0; i < 8; i += 2)
        for (var j = 0; j < 8; j += 2) {
            // создание размметки шахматной доски;
            ctx.clearRect(k + i * v, k + j * v, v, v);
            ctx.clearRect(k + (i + 1) * v, k + (j + 1) * v, v, v);
        }

for (j = 0; j < 8; j++) {
   if(j == 3 || j == 4) continue;
   for (var i = 0; i < 16; i += 4) {
        ctx.beginPath();
        ctx.fillStyle = j > 4 ?'white' : 'blue';
        ctx.arc(vd + v * ((j + 1) % 2) + i * vv, (vd + v * j), dr, 0, Math.PI * 2, true);
        ctx.fill();
    }
 }


</script>


</body>
</html>

drakulawz 17.09.2018 19:57

рони,
мой мозг перестал понимать, что происходит ещё на объявлении переменных.:help:
Точнее, я не могу понять, зачем так усложнять себе жизнь. Так актуально делать в каком-то случаи?

рони 17.09.2018 20:19

Цитата:

Сообщение от drakulawz
Так актуально делать в каком-то случаи?

если нужна доска любого размера, достаточно указать ширину в переменной ww.

drakulawz 18.09.2018 08:49

Цитата:

Сообщение от рони
если нужна доска любого размера, достаточно указать ширину в переменной ww.

понятно, что бы не подбирать и не высчитывать можно просто изменить переменную и всё автоматически поменяется. :victory:

Dilettante_Pro 18.09.2018 10:34

Цитата:

Сообщение от drakulawz
Да, так проще наверное, а то если делать много циклов для отрисовки stroke получается огромезный код - бред кароче.

Код для stroke ничем не отличается от кода для fill
<canvas id = 'canv'></canvas>
<script>
var canv = document.querySelector('#canv'),
        ctx = canv.getContext('2d');
    canv.width = 900;
    canv.height = 900;
    ctx.strokeRect(15, 15, 800, 800);  // внешняя рамка доски;
    ctx.strokeRect(20, 20, 790, 790);  // внутренняя рамка;
    ctx.fillRect(20, 20, 790, 790);       // заливка доски;
    for (var i = 0; i < 8; i += 2)
        for (var j = 0; j < 8; j += 2) {
            // создание размметки шахматной доски;
            ctx.clearRect(20 + i * 98.75, 20 + j * 98.75, 98.75, 98.75);
            ctx.clearRect(20 + (i + 1) * 98.75, 20 + (j + 1) * 98.75, 98.75, 98.75);
        }
ctx.lineWidth = 10;
for (j = 0; j < 3; j++) {
   for (var i = 0; i < 16; i += 4) {
        ctx.beginPath();
        ctx.strokeStyle = 'blue';
        ctx.arc(69.375 + 98.75 * ((j + 1) % 2) + i * 49.375, (69.375 + 98.75 * j), 35, 0, Math.PI * 2, true);
        ctx.stroke();
    }
 }   

for (j = 0; j < 3; j++) {
   for (var i = 0; i < 16; i += 4) {
        ctx.beginPath();
        ctx.strokeStyle = 'white';
        ctx.arc(69.375 + 98.75 * (j  % 2) + i * 49.375, (563.125 + 98.75* j), 35, 0, Math.PI * 2, true);
        ctx.stroke();
    }
 }   
</script>

drakulawz 18.09.2018 15:14

Цитата:

Сообщение от Dilettante_Pro
Код для stroke ничем не отличается от кода для fill

ага, я уже проверил, только на скрипте рони. Сейчас вот мучаюсь - хочу смочь выделять и передвигать шашки)
Кстати, кто-нибудь знает почему брэкетс может вот так ругаться "Анализатор FS int завершился с ошибкой: [object Object]", вроди бы всё в порядке но в одной функции этот объект не виден, вот кусок кода с объектом и где появляется ошибка:

var lap = [];

var isCursorInArc = function (x, y lap) {
        return x > lap.x && x < lap.x + lap.r && y > lap.y && y < lap.y + lap.ea;
    };


Если закоментировать эту функцию то всё работает.

j0hnik 18.09.2018 15:16

var isCursorInArc = function (x, y, lap)

drakulawz 18.09.2018 15:34

j0hnik, мдэ....засиделся я сегодня, надо наверное перерыв сделать, только что сам заметил. А я всё гуглю, ищу, чё оно за ошибка такая... Жаль фейспалма тут нет.

j0hnik 18.09.2018 15:38

drakulawz,
eslint установите если у вас нет, ускоряет фикс такого рода рода ошибок.

drakulawz 18.09.2018 17:28

Цитата:

Сообщение от j0hnik
eslint установите если у вас нет

когда-то пробовал, какие-то нестыки - ошибка при установке. Сейчас ещё раз попробую.

drakulawz 18.09.2018 17:35

Цитата:

Сообщение от drakulawz
Цитата:

Сообщение от j0hnik
eslint установите если у вас нет

когда-то пробовал, какие-то нестыки - ошибка при установке. Сейчас ещё раз попробую.

Всё, вспомнил, он же в брекетсе стоит и так по умолчанию, по сему и не ставится... Ну, поставил ещё старую версию - ничего не поменялось.
Да, оно как и раньше, показывает в какой строке ошибка, но если человек жёстко тупит (как я) то даже такой сверх хэлп не помогает.:cray:

drakulawz 19.09.2018 12:53

Не создаю новую тему так как это всё тот же скрипт практически с тем же вопросом. Надеюсь это не проблема.

Сделал разметку, расставил шашки, узнал как их выбирать мышкой. Но! Появились старые и пару новых проблем:
1. Шашки все стали белыми, точнее я могу менять цвет заливки но сделать три верхних ряда и три нижних ряда разного цвета не смог. Может сюда не подходят такие циклы? Или нужно делать, как раньше, - отдельно белые и отдельно синие?
2. Когда выбираю шашки (если включить в скрипт функцию с обрамлением) то, при их выборе мышкой, ко всем рамкам применяется функция select и уже не отменяется. Это происходит из-за того что мой конструктор ссылается на несколько похожих функций? Ведь метод ctx.stroke() есть в функции strokeArc() & selectArc() и, по методу конструктора Arc оно как-то "сливает" эти две функции?
3. Так же есть проблема с выбором - не могу найти правильные координаты для этого. Выбрать шашку могу только по клику на 1/4 круга. Это исправлял с помощью выражения: return x + x > lap.x && x < lap.x + lap.r && y + y > lap.y && y < lap.y + lap.r . Но если, после этого, можно выбрать верхние шашки (хоть и по две в некоторых местах), то при клике на нижние ряды выбирается по 3 сразу.:cray: Как правильно координаты взять? Создавать отдельный конструктор для этого?
4. Плюс, после выбора шашки, остаётся кружок - еле заметный но он там есть и если присмотреться то, всё таки, заметный. Я пробовал два варианта: ctx.globalCompositeOperation = 'destination-out' и ctx.clearRect(). Опять таки, из-за конструктора какой-то конфликт или это из-за цикла - после двух кликов удаляются все шашки и в доске делаются дырки.:cray: Вообще не пойму как эту функцию правильно осуществить. Может надо заново запускать отрисовку после удаления?

var canv = $('#canv')[0],
        ctx = canv.getContext('2d');

    var ww = 800,
        k = 20,
        kk = k - 5,
        w = ww - k / 2,
        v = w / 8,
        vv = v / 2,
        vd = vv + k,
        dr = vv - 10,
        up = v * 5,
        width = $('#w').val(), // высота из инпута;
        hight = $('#h').val(), // ширина из инпута;
        offset = 0; // переменная для анимирования таргета шашек;

    canv.width = width; //ширина канваса;
    canv.height = hight; //высота канваса;
    ctx.strokeRect(kk, kk, ww, ww); // внешняя рамка доски;
    ctx.strokeRect(k, k, w, w); // внутренняя рамка;
    ctx.fillRect(k, k, w, w); // заливка доски;

    // функция отрисовки шашек;
    var fillArc = function (x, y, r, sa, ea, cw) {
        for (var j = 0; j < 8; j++) {
            if (j == 3 || j == 4) continue; // переход между белыми и синими;
            ctx.fillStyle = j < 4 ? 'white' : 'blue'; //НЕ РАБОТАЕТ!
            ctx.beginPath();
            ctx.arc(x, y, r, sa, ea, cw);
            ctx.fill();
        }
    };

    // функция обрамления шашек;
    var strokeArc = function (x, y, r, sa, ea, cw) {
        for (var j = 0; j < 8; j++) {
            if (j == 3 || j == 4) continue; // переход между белыми и синими;
            ctx.strokeStyle = j < 4 ? 'blue' : 'white'; //НЕ РАБОТАЕТ!
            ctx.lineWidth = 2;
            ctx.beginPath();
            ctx.arc(x, y, r, sa, ea, cw);
            ctx.stroke();
        }
    };

    // функция выделения шашек;
    var selectArc = function (x, y, r, sa, ea, cw) {
        ctx.strokeStyle = 'green';
        ctx.beginPath();
        ctx.lineWidth = 2;
        ctx.setLineDash([8, 2]);
        ctx.lineDashOffset = -offset;
        ctx.arc(x, y, r, sa, ea, cw);
        ctx.stroke();

    };

    // реализация "бегущей" преривистой для функции выделения;
    function march() {
        offset++;
        if (offset > 100) {
            offset = 0;
        }
        selectArc();
        setTimeout(march, 20);
    };

    // функция удаления остаточного круга после взятия в таргет шашки;
    //    var circle = function (x, y, r, sa, ea, cw) {
    //        ctx.globalCompositeOperation = 'destination-out'
    //        ctx.arc(x, y, r, sa, ea, cw);
    //        ctx.fill();
    //    }

    // конструктор создания элементов кроме разметки доски;
    var Arc = function (x, y, r, sa, ea, cw) {
        this.x = x;
        this.y = y;
        this.r = r;
        this.sa = sa;
        this.ea = ea;
        this.cw = true;
        this.selected = false;
    };

    // прототип шаблонов для отрисовок;
    Arc.prototype = {
        draw: function () {
            fillArc(this.x, this.y, this.r, this.sa, this.ea, this.cw);
        },
        frame: function () {
            strokeArc(this.x, this.y, this.r, this.sa, this.ea, this.cw);
        },
        stroke: function () {
            march();
            selectArc(this.x, this.y, this.r, this.sa, this.ea, this.cw);
        },
        //        clearCrcl: function () {
        //            circle(this.x, this.y, this.r, this.sa, this.ea, this.cw);
        //        },
        select: function () {
            this.selected = !this.selected;
        }
    };

    // создание разметки шахматной доски;
    for (var i = 0; i < 8; i += 2) {
        for (var j = 0; j < 8; j += 2) {
            ctx.clearRect(k + i * v, k + j * v, v, v);
            ctx.clearRect(k + (i + 1) * v, k + (j + 1) * v, v, v);
        }
    }

    var i = 0,
        lap = []; // массив с отрисованными объектами;

    // цикл отрисовки шашек;
    for (var j = 0; j < 8; j++) {
        if (j == 3 || j == 4) continue; // пропуск двух средних рядов клеток;
        for (var i = 0; i < 16; i += 4) {
            ctx.fillStyle = j > 4 ? 'white' : 'blue';
            lap.push(new Arc(vd + v * ((j + 1) % 2) + i * vv, (vd + v * j), dr, 0, Math.PI * 2, true));
        }
    };

    // функция для проверки координат клика;
    var isCursorInArc = function (x, y, lap) {
        return x > lap.x && x < lap.x + lap.r && y > lap.y && y < lap.y + lap.r;
    };

    // функция активация выделения шашки;
    canv.onclick = function (e) {
        var x = e.x,
            y = e.y;
        for (i in lap) {
            if (isCursorInArc(x, y, lap[i])) {
                lap[i].select();
            }
        }
    };

    // рисование шашек и рамки (после клика);
    setInterval(function () {
        for (i in lap) {
            lap[i].draw();
            lap[i].frame();
            if (lap[i].selected) {
                lap[i].stroke();
            }
        }
    }, 30);

рони 19.09.2018 13:49

drakulawz,
Цитата:

Сообщение от Rise
преимущества ООП подхода в общем и классов в частности

медитировать тут
https://javascript.ru/forum/project/...tml#post464453

drakulawz 19.09.2018 15:55

Цитата:

Сообщение от рони
медитировать тут

...пожалуй лучше начать с азов. :(
Пройдусь по видеоурокам по канвасу. Ещё и js нативный надо подтянуть нехило, тугова-то у меня с этими всеми колбэками. Да, и как правильно писать код толком не пойму, только то и делаю, что пародирую...
Сейчас вот только понял, как важно писать сноски к каждой функции - реально помогает лучше ориентироваться в коде - прогресс!:p


Часовой пояс GMT +3, время: 12:54.