Статьи

Fun with Canvas: создание плагинов для гистограммы, часть 2

В этой серии из двух частей мы объединим универсальный элемент canvas с надежной библиотекой jQuery, чтобы создать плагин для построения графиков. Во второй части мы собираемся преобразовать его в плагин jQuery, а затем добавим немного конфет и дополнительных функций.

Завершая серию Fun with canvas из двух частей, сегодня мы собираемся создать плагин для построения графиков; заметьте, не обычный плагин. Мы собираемся показать некоторую любовь jQuery к элементу canvas, чтобы создать очень надежный плагин.

В первой части мы рассматривали исключительно реализацию логики плагина как отдельного скрипта. В конце первой части наша гистограмма выглядела так.


Результат в конце части 1

В этой заключительной части мы будем работать над преобразованием нашего кода и превращением его в надлежащий плагин jQuery, добавив некоторые визуальные тонкости и, наконец, включив некоторые дополнительные функции. В конечном итоге наш результат будет выглядеть так:


Готовый продукт

Все прогрелись? Давайте погрузимся в!


Прежде чем мы начнем преобразовывать наш код в плагин, нам сначала нужно взглянуть на несколько формальностей, когда дело доходит до создания плагина.


Мы начнем с выбора имени для плагина. Я выбрал barGraph и переименовал файл JavaScript в jquery.barGraph.js. Теперь мы вложим весь код из предыдущей статьи в следующий фрагмент.

1
2
3
$.fn.barGraph = function(settings) {
//code here
}

Настройки содержат все необязательные параметры, передаваемые плагину.


При разработке плагинов jQuery обычно рекомендуется использовать jQuery вместо псевдонима $ в вашем коде, чтобы минимизировать конфликты с другими библиотеками Javascript. Вместо того, чтобы преодолевать все эти проблемы, мы можем просто использовать пользовательские псевдонимы, как указано в документации по jQuery . Мы заключаем весь наш код плагина в эту самостоятельно выполняемую анонимную функцию, как показано ниже:

1
2
3
4
5
(function($) {
$.fn.barGraph = function(settings) {
//plugin implementation code here
}
})(jQuery);

По сути, мы инкапсулируем весь наш код в функцию и передаем ей jQuery. Теперь мы можем свободно использовать псевдоним $ столько, сколько нам нужно, внутри нашего кода, не беспокоясь о том, что он может конфликтовать с другими библиотеками JavaScript.


При разработке плагина имеет смысл предоставить пользователю разумное количество настроек, при этом используя разумные параметры по умолчанию, если пользователи используют плагин, не передавая ему никаких опций. Имея это в виду, мы дадим возможность пользователю изменять каждую из переменных параметров графика, которые я упоминал в предыдущей статье этой серии. Это легко сделать; мы просто определяем каждую из этих переменных как свойства объекта, а затем получаем к ним доступ.

01
02
03
04
05
06
07
08
09
10
var defaults = {
             barSpacing = 20,
             barWidth = 20,
             cvHeight = 220,
             numYlabels = 8,
             xOffset = 20,
             maxVal,
             gWidth=550,
             gHeight=200;
           };

Наконец, нам нужно объединить параметры по умолчанию с переданными параметрами, отдав предпочтение переданным. Эта линия заботится об этом.

1
var option = $.extend(defaults, settings);

Не забудьте изменить имена переменных, где это необходимо. Как в —

1
return (param*barWidth)+((param+1)*barSpacing)+xOffset;

…изменения в:

1
return (param*option.barWidth)+((param+1)*option.barSpacing)+option.xOffset;

Это где плагин выбит. Наша старая реализация могла создавать только один граф на странице, и способность создавать несколько графов на странице является основной причиной, по которой мы создаем плагин для этой функциональности. Кроме того, нам нужно убедиться, что пользователю не нужно создавать элемент canvas для каждого создаваемого графика. Имея это в виду, мы будем создавать элементы canvas динамически по мере необходимости. Давайте продолжим. Мы рассмотрим более ранние и обновленные версии соответствующих частей кода.


Прежде чем мы начнем, я хотел бы указать, как наш плагин будет вызываться.

1
2
3
4
5
6
$(«#years»).barGraph
   ({
         barSpacing = 30,
        barWidth = 25,
         numYlabels = 12,
   });

Просто как тот. Лет — это идентификатор таблицы, содержащей все наши значения. Мы передаем варианты по мере необходимости.


Для начала нам понадобится ссылка на источник данных для графиков. Теперь мы получаем доступ к исходному элементу и получаем его идентификатор. Добавьте следующую строку в группу графовых переменных, которые мы объявили ранее.

1
var dataSource = $(this).attr(«id»);

Мы определяем новую переменную и присваиваем ей значение атрибута ID переданного элемента. В нашем коде это относится к текущему выбранному элементу DOM. В нашем примере это относится к таблице с идентификатором лет .

В предыдущей реализации идентификатор источника данных был жестко запрограммирован. Теперь мы заменим его на атрибут ID, который мы извлекли ранее. Более ранняя версия функции grabValues приведена ниже:

01
02
03
04
05
06
07
08
09
10
11
12
function grabValues ()
     {
        // Access the required table cell, extract and add its value to the values array.
         $(«#data tr td:nth-child(2)»).each(function(){
         gValues.push($(this).text());
         });
      
         // Access the required table cell, extract and add its value to the xLabels array.
         $(«#data tr td:nth-child(1)»).each(function(){
        xLabels.push($(this).text());
         });
     }

Обновляется до этого:

01
02
03
04
05
06
07
08
09
10
11
12
function grabValues ()
     {
        // Access the required table cell, extract and add its value to the values array.
        $(«#»+dataSource+» tr td:nth-child(2)»).each(function(){
         gValues.push($(this).text());
         });
      
         // Access the required table cell, extract and add its value to the xLabels array.
         $(«#»+dataSource+» tr td:nth-child(1)»).each(function(){
        xLabels.push($(this).text());
         });
     }

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
function initCanvas ()
     {
         $(«#»+dataSource).after(«<canvas id=\»bargraph-«+dataSource+»\» class=\»barGraph\»> </canvas>»);
          
         // Try to access the canvas element
        cv = $(«#bargraph-«+dataSource).get(0);
         
        if (!cv.getContext)
        { return;
      
        // Try to get a 2D context for the canvas and throw an error if unable to
        ctx = cv.getContext(‘2d’);
        if (!ctx)
        { return;
     }

Мы создаем элемент canvas и внедряем его в DOM после таблицы, которая выступает в качестве источника данных. Функция after в jQuery очень удобна. Атрибут класса barGraph и атрибут ID в формате barGraph-dataSourceID также применяются, чтобы позволить пользователю стилизовать их все как группу или индивидуально по мере необходимости.


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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
(function($){
    $.fn.barGraph = function(settings) {
     
    // Option variables
    var defaults = {
             // options here
           };
            
    // Merge the passed parameters with the defaults
    var option = $.extend(defaults, settings);
     
    // Cycle through each passed object
    this.each(function() {
     
    // Implementation code here
    });
               
    // Returns the jQuery object to allow for chainability.
    return this;
    }
})(jQuery);

Мы инкапсулируем весь код после получения и объединения настроек внутри конструкции this.each . Мы также обязательно возвращаем объект jQuery в конце, чтобы включить возможность цепочки.

На этом наш рефакторинг завершен. Мы должны иметь возможность вызывать наш плагин и создавать столько графиков, сколько необходимо.


Теперь, когда наше преобразование завершено, мы можем сделать его визуально лучше. Мы собираемся сделать несколько вещей здесь. Мы рассмотрим каждый из них в отдельности.


Более старая версия использовала мягкий серый для рисования графиков. Мы собираемся внедрить механизм тем для баров. Это само по себе состоит из серии шагов.


Океан: тема по умолчанию

листва

вишня в цвету

Спектр
1
2
3
4
var defaults = {
             // Other defaults here
             theme: «Ocean»,
           };

Мы добавляем опцию темы к настройкам по умолчанию, позволяя пользователю изменить тему на любую из четырех доступных предустановок.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
function grabValues ()
     {
        // Previous code
          
        switch(option.theme)
        {
            case ‘Ocean’:
            gTheme = thBlue;
            break;
            case ‘Foliage’:
            gTheme = thGreen;
            break;
            case ‘Cherry Blossom’:
            gTheme = thPink;
            break;
            case ‘Spectrum’:
            gTheme = thAssorted;
            break;
        }
     }

Простая конструкция переключателя просматривает настройку option.theme и указывает переменную gTheme на массив необходимых цветов. Мы используем описательные имена для тем вместо общих.

1
2
3
4
5
// Themes
    var thPink = [‘#FFCCCC’,’#FFCCCC’,’#FFC0C0′,’#FFB5B5′,’#FFADAD’,’#FFA4A4′,’#FF9A9A’,’#FF8989′,’#FF6D6D’];
    var thBlue = [‘#ACE0FF’,’#9CDAFF’,’#90D6FF’,’#86D2FF’,’#7FCFFF’,’#79CDFF’,’#72CAFF’,’#6CC8FF’,’#57C0FF’];
    var thGreen = [‘#D1FFA6′,’#C6FF91′,’#C0FF86′,’#BCFF7D’,’#B6FF72′,’#B2FF6B’,’#AAFE5D’,’#A5FF51′,’#9FFF46′];
    var thAssorted = [‘#FF93C2′,’#FF93F6′,’#E193FF’,’#B893FF’,’#93A0FF’,’#93D7FF’,’#93F6FF’,’#ABFF93′,’#FF9B93′];

Затем мы определяем количество массивов, каждый из которых содержит серию оттенков определенного цвета. Они начинают с более светлого оттенка и продолжают увеличиваться. Мы пройдемся по этим массивам позже. Добавление тем так же просто, как добавление массива для нужного вам цвета, а затем изменение предыдущего переключателя для отражения изменений.

1
2
3
4
function getColour (param)
      {
         return Math.ceil(Math.abs(((gValues.length/2) -param)));
      }

Это крошечная функция, которая позволяет нам достигать и применять градиентоподобный эффект к графикам. По сути, мы вычисляем абсолютную разницу между половиной числа значений, которые должны быть представлены, и переданным параметром, который является индексом выбранного в данный момент элемента в массиве. Таким образом, мы можем создать плавный градиент. Поскольку мы определили только девять цветов в каждом массиве цветов, мы ограничены восемнадцатью значениями графика. Расширение этого числа должно быть довольно тривиальным.

01
02
03
04
05
06
07
08
09
10
function drawGraph ()
     {
        for(index=0; index<gValues.length; index++)
          {
            ctx.save();
            ctx.fillStyle = gTheme[getColour(index)];
            ctx.fillRect( x(index), y(gValues[index]), width(), height(gValues[index]));
            ctx.restore();
          }
     }

Это где мы на самом деле темы графиков. Вместо того чтобы устанавливать статическое значение для свойства fillStyle , мы используем функцию getColour для получения необходимого индекса элемента в массиве текущей выбранной темы.


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


Без прозрачности

Со значением 0,8
1
2
3
4
var defaults = {
            // Other defaults here
             barOpacity : 0.8,
           };

Мы добавляем опцию barOpacity к значениям по умолчанию, позволяя пользователю изменять непрозрачность графиков до значения от 0 до 1, где 0 полностью прозрачен, а 1 полностью непрозрачен.

01
02
03
04
05
06
07
08
09
10
11
function drawGraph ()
     {
        for(index=0; index<gValues.length; index++)
          {
            ctx.save();
            ctx.fillStyle = gTheme[getColour(index)];
            ctx.globalAlpha = option.barOpacity;
            ctx.fillRect( x(index), y(gValues[index]), width(), height(gValues[index]));
            ctx.restore();
          }
     }

Свойство globalAlpha контролирует непрозрачность или прозрачность отображаемого элемента. Мы устанавливаем значение этого свойства в переданное значение или значение по умолчанию, чтобы добавить немного прозрачности. В качестве разумного значения по умолчанию мы используем значение 0,8, чтобы сделать его немного прозрачным.


Сетка может быть чрезвычайно полезна при обработке данных, представленных на графике. Хотя изначально мне требовалась правильная сетка, позже я остановился на серии горизонтальных линий, совпадающих с метками оси Y, и полностью отбросил вертикальные линии, поскольку они просто мешали данным. После этого давайте реализуем способ визуализации.


С сеткой отключена

С включенной сеткой

Создание линий с использованием путей и метода lineTo казалось наиболее очевидным решением для рисования графиков, но я столкнулся с ошибкой рендеринга, которая сделала этот подход непригодным. Поэтому я придерживаюсь метода fillRect для создания этих строк. Вот функция в полном объеме.

1
2
3
4
5
6
7
8
function drawGrid ()
      {
          for(index=0; index<option.numYlabels; index++)
          {
           ctx.fillStyle = «#AAA»;
           ctx.fillRect( option.xOffset, y(yLabels[index])+3, gWidth, 1);
          }
      }

Это очень похоже на отрисовку меток оси Y, за исключением того, что вместо отрисовки метки мы рисуем горизонтальную линию, охватывающую ширину графика шириной 1 пиксель. Функция у помогает нам в позиционировании.

1
2
3
4
var defaults = {
             // Other defaults here
             disableGrid : false,
           };

Мы добавляем параметр disableGrid к значениям по умолчанию, позволяя пользователю контролировать, отображается ли сетка или нет. По умолчанию он отображается.

1
2
// Function calls
    if(!option.disableGrid) { drawGrid();

Мы просто проверяем, хочет ли пользователь отображать сетку, и действуем соответственно.


Теперь, когда все полосы окрашены, на светлом фоне не хватает акцента. Чтобы исправить это, нам нужен ход в 1 пиксель. Есть два способа сделать это. Первый и самый простой способ — просто добавить метод strokeRect в метод drawGraph ; или мы могли бы использовать метод lineTo для быстрого обводки прямоугольников. Я выбрал прежний маршрут, поскольку, как и прежде, метод lineTo выдал мне странную ошибку рендеринга.


Без поглаживания

С поглаживанием

Сначала мы добавляем его к объекту по умолчанию, чтобы дать пользователю контроль над тем, применяется он или нет.

1
2
3
4
var defaults = {
             // Other defaults here
             showOutline : true,
           };
01
02
03
04
05
06
07
08
09
10
11
function drawGraph ()
     {
           // Previous code
            if (option.showOutline)
            {
            ctx.fillStyle = «#000»;
            ctx.strokeRect( x(index), y(gValues[index]), width(), height(gValues[index]));
            }
            // Rest of the code
          }
     }

Мы проверяем, хочет ли пользователь отобразить контуры, и, если да, мы продолжаем. Это почти то же самое, что и рендеринг реальных баров, за исключением того, что вместо использования метода fillRect мы используем метод strokeRect .


В исходной реализации нет различия между самим элементом canvas и фактическим пространством рендеринга панелей. Мы исправим это сейчас.


Без затенения

С затенением
1
2
3
4
5
function shadeGraphArea ()
      {
        ctx.fillStyle = «#F2F2F2»;
        ctx.fillRect(option.xOffset, 0, gWidth-option.xOffset, gHeight);
      }

Это крошечная функция, которая затеняет необходимую область. Мы покрываем элемент canvas за вычетом области, покрытой метками обеих осей. Первые два параметра указывают на координаты x и y начальной точки, а последние два указывают на требуемую ширину и высоту. Начиная с option.offset , мы удаляем область, покрытую метками оси Y, и ограничивая высоту до gHeight , мы удаляем метки оси X.


Теперь, когда наш график выглядит достаточно симпатично, мы можем сосредоточиться на добавлении некоторых новых функций в наш плагин. Мы рассмотрим каждый в отдельности.

Рассмотрим этот график знаменитых пиков 8K.

Когда самое высокое значение достаточно высоко, и большинство значений находится в пределах 10% от максимального значения, график перестает быть полезным. У нас есть два способа исправить это.


Сначала мы собираемся начать с более простого решения. При отображении значения соответствующих графиков вверху, проблема практически решена, поскольку отдельные значения могут быть легко дифференцированы. Вот как это реализовано.

1
2
3
4
var defaults = {
             // Other defaults here
             showValue: true,
           };

Сначала мы добавляем запись в объект по умолчанию, чтобы пользователь мог включать и выключать его по своему желанию.

1
2
// Function calls
if(option.showValue) { drawValue();

Мы проверяем, хочет ли пользователь отображать значение, и действуем соответственно.

01
02
03
04
05
06
07
08
09
10
11
12
13
function drawValue ()
      {
          for(index=0; index<gValues.length; index++)
          {
              ctx.save();
              ctx.fillStyle= «#000»;
              ctx.font = «10px ‘arial'»;
              var valAsString = gValues[index].toString();
              var valX = (option.barWidth/2)-(valAsString.length*3);
              ctx.fillText(gValues[index], x(index)+valX, y(gValues[index])-4);
              ctx.restore();
          }
      }

Мы перебираем массив gValues и отрисовываем каждое значение отдельно. Вычисления, включающие valAsString и valX, являются ничем иным, как крошечными вычислениями, которые помогают нам в правильных отступах, поэтому они не выглядят неуместными.


Это сложнее из двух решений. В этом методе вместо того, чтобы начинать метки оси Y с 0, мы начинаем намного ближе к минимальному значению. Я объясню, как мы идем. Обратите внимание, что в приведенном выше примере разница между последующими значениями по отношению к максимальному значению довольно незначительна и не так сильно показывает ее эффективность. Другие наборы данных должны облегчить анализ результатов.

1
2
3
4
var defaults = {
             // Other defaults here
             scale: false
           };

Поскольку функция масштабирования является неотъемлемой частью процесса рендеринга, нам необходимо обновить ее, чтобы включить функцию масштабирования. Мы обновляем это так:

1
2
3
4
function scale (param)
      {
       return ((option.scale) ? Math.round(((param-minVal)/(maxVal-minVal))*gHeight) : Math.round((param/maxVal)*gHeight));
      }

Я знаю, что это выглядит немного сложным, но это выглядит так только благодаря использованию троичного условного оператора. По сути, мы проверяем значение option.scale и, если оно говорит false, старый код выполняется. Если это правда, вместо нормализации значения как функции от максимального значения в массиве, мы теперь нормализуем его как функцию от разницы между максимальным и минимальным значениями. Что приводит нас к:

Теперь нам нужно выяснить как максимальное, так и минимальное значение, а не только максимальное, которое мы имели до этого. Функция обновлена ​​до этого:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
function minmaxValues (arr)
     {
        maxVal=0;
         
        for(i=0; i<arr.length; i++)
        {
         if (maxVal<parseInt(arr[i]))
         {
         maxVal=parseInt(arr[i]);
         }
        }
        minVal=maxVal;
        for(i=0; i<arr.length; i++)
        {
         if (minVal>parseInt(arr[i]))
         {
         minVal=parseInt(arr[i]);
         }
        }
       maxVal*= 1.1;
       minVal = minVal — Math.round((maxVal/10));
     }

Я уверен, что вы могли бы сделать то же самое в одном цикле, не используя столько строк кода, сколько я, но в то время я чувствовал себя особенно не креативно, так что терпите меня. С учетом формальностей вычислений мы увеличиваем переменную maxVal на 5% и переменную minVal , вычитаем значение, равное 5% значения maxVal . Это делается для того, чтобы полосы не касались вершины каждый раз, а различия между метками на каждой оси Y были одинаковыми.

Завершив все основы, мы приступаем к обновлению процедуры рендеринга метки оси Y, чтобы отразить масштабирование.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
function drawYlabels()
      {
         ctx.save();
         for(index=0; index<option.numYlabels; index++)
          {
              if (!option.scale)
              {
                 yLabels.push(Math.round(maxVal/option.numYlabels*(index+1)));
              }
              else
              {
                  var val= minVal+Math.ceil(((maxVal-minVal)/option.numYlabels)*(index+1));
                  yLabels.push(Math.ceil(val));
              }
           ctx.fillStyle = option.labelColour;
           var valAsString = yLabels[index].toString();
           var lblX = option.xOffset — (valAsString.length*7);
           ctx.fillText(yLabels[index], lblX, y(yLabels[index])+10);
          }
           if (!option.scale)
           {
                ctx.fillText(«0», option.xOffset -7, gHeight+7);
           }
          else
          {
            var valAsString = minVal.toString();
            var lblX = option.xOffset — (valAsString.length*7);
            ctx.fillText(minVal, lblX, gHeight+7);
          }
          ctx.restore();
      }

Довольно мясистое обновление, если вы спросите меня! Суть функции остается прежней. Мы просто проверяем, активировал ли пользователь масштабирование, и разветвляем код по мере необходимости. Если этот параметр включен, мы изменяем способ назначения меток Y, чтобы они соответствовали новому алгоритму. Вместо максимального значения, разделенного на n чисел с равным интервалом, мы теперь вычисляем разницу между максимальным и минимальным значением, делим его на равномерно распределенные числа и добавляем его к минимальному значению, чтобы построить наш массив меток оси Y. После этого мы продолжаем как обычно, визуализируя каждую метку отдельно. Поскольку мы рендерили самый нижний 0 вручную, мы должны проверить, включено ли масштабирование, а затем отобразить минимальное значение на его месте. Не обращайте внимания на небольшие числовые добавления к каждому передаваемому параметру; это просто для того, чтобы убедиться, что каждый элемент графика выстраивается в соответствии с ожиданиями.


В нашей предыдущей реализации мы жестко закодировали размеры графика, что представляет значительную трудность при изменении числа значений. Мы собираемся исправить это сейчас.

1
2
3
4
var defaults = {
            // Other defaults here
             cvHeight: 250, //In px
           };

Мы позволяем пользователю установить высоту элемента canvas отдельно. Все остальные значения рассчитываются динамически и применяются по мере необходимости.

Функция initCanvas обрабатывает всю инициализацию холста и, следовательно, должна быть обновлена ​​для реализации новой функциональности.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
function initCanvas ()
     {
         $(«#»+dataSource).after(«<canvas id=\»bargraph-«+dataSource+»\» class=\»barGraph\»> </canvas>»);
          
        // Try to access the canvas element
        cv = $(«#bargraph-«+dataSource).get(0);
        cv.width=gValues.length*(option.barSpacing+option.barWidth)+option.xOffset+option.barSpacing;
        cv.height=option.cvHeight;
        gWidth=cv.width;
        gHeight=option.cvHeight-20;
      
        if (!cv.getContext)
        { return;
      
        // Try to get a 2D context for the canvas and throw an error if unable to
        ctx = cv.getContext(‘2d’);
        if (!ctx)
        { return;
     }

После внедрения элемента canvas мы получаем ссылку на созданный элемент. Ширина элемента canvas рассчитывается как функция количества элементов в массиве — gValues , расстояния между каждым баром — option.barSpacing , ширины самого бара — option.barWidth и, наконец, option.xOffset . Ширина графика изменяется динамически в зависимости от каждого из этих параметров. Высота может быть изменена пользователем и по умолчанию равна 220px с областью рендеринга для самого бара, равной 220px. 20 пикселей назначается меткам оси X.


Имеет смысл, что пользователь может захотеть скрыть исходную таблицу после создания графика. Имея это в виду, мы позволяем пользователю решать, удалять таблицу или нет.

1
2
3
4
var defaults = {
            // Other defaults here
             hideDataSource: true,
           };
1
if (option.hideDataSource) { $(«#»+dataSource).remove();}

Мы проверяем, хочет ли пользователь скрыть таблицу, и если да, мы полностью удаляем ее из DOM, используя метод удаления jQuery.


Теперь, когда вся тяжелая работа выполнена, мы можем рассмотреть, как оптимизировать наш код. Поскольку этот код был написан исключительно для учебных целей, большая часть работы была заключена в отдельные функции, и, кроме того, они намного более многословны, чем должны быть.

Если вы действительно хотите самый простой из возможных кодов, весь наш плагин, исключая инициализацию и вычисления, может быть переписан в два цикла. Один цикл по массиву gValues для рисования самих столбцов и меток оси X; и второй цикл, повторяющийся от 0 до numYlabels для отображения сетки и меток оси Y. Код выглядел бы намного более запутанным, однако это должно привести к значительно меньшей базе кода.


Вот и все, ребята! Мы создали плагин высокого уровня с нуля. Мы рассмотрели ряд тем в этой серии, в том числе:

  • Рассмотрим схему рендеринга элемента canvas.
  • Некоторые из методов рендеринга элемента canvas.
  • Нормализация значений позволяет нам выразить это как функцию другого значения.
  • Некоторые полезные методы извлечения данных с использованием jQuery.
  • Основная логика рендеринга графа.
  • Преобразование нашего скрипта в полноценный плагин jQuery.
  • Как улучшить его визуально и еще более расширить его функциональность.

Надеюсь, вам было так же весело читать это, как и мне. Это работа с 270 с лишним строк, я уверен, что что-то пропустил. Не стесняйтесь нажимать на комментарии и спрашивать меня. Или критикуй меня. Или похвали меня. Вы знаете, это ваш звонок! Удачного кодирования!