Использование сторонних библиотек с Webix

Библиотека Webix предлагает множество готовых виджетов. Но даже это разнообразие меркнет перед обилием различных библиотек, созданных Open Source сообществом. В этой статье я расскажу, как любую стороннюю библиотеку можно превратить в Webix виджет.

Konva.js

В качестве примера я буду использовать Javascript библиотеку Konva.

Konva — это JavaScript библиотека, которая добавляет интерактивность в HTML5 элемент canvas. Вы можете добавлять различные фигуры на холст, добавлять для них обработчики событий, перемещать эти фигуры, изменять их масштаб, а также вращать, независимо друг от друга. Таким образом, поддерживается высокая производительность анимаций, даже если ваше приложение использует тысячи фигур.

Картинка ниже представляет собой Canvas-объект. Вы можете попробовать перетаскивать звезды на ней.



Полный исходный код демо можно увидеть по ссылке: http://konvajs.github.io/docs/sandbox/Elastic_Stars.html.

Базовый виджет

Давайте начнем с создания базового виджета. Синтаксис любого виджета выглядит следующим образом:

webix.protoUI({
    name:"view name",
    $init:function(){
         // конструктор    }
 }, webix.ui.view);

Код достаточно простой. Метод protoUI принимает 2 параметра. Первый из них — это конфигурационный объект. Он содержит название нового виджета, конструктор и какие-либо опциональные методы. Второй параметр — это объект виджета, который будет использован в качестве основы для создания кастомного компонента.

В большинстве случаев, вы будете использовать webix.ui.view в качестве второго параметра, поскольку это виджет, лежащий в основе большинства Webix контролов, и он лучше всего работает для сторонних библиотек.

Простой виджет для библиотеки Konva будет выглядеть следующим образом:

webix.protoUI({
    name:"konva",
    $init:function(){
        this.stage = new Konva.Stage({
            container: this.$view
        });
    },
    $setSize:function(x,y){
        if (webix.ui.view.prototype.$setSize.call(this, x,y)){
              // изменять размеры холста при изменении размеров view
            this.stage.size({ width:this.$width, height:this.$height });
        }
    },
    getStage:function(){
        return this.stage;
    }
}, webix.ui.view);

В данном коде у нас появился конструктор нового виджета и 2 метода. В конструкторе мы создаем базовый объект Konva.

Метод $setSize вызывается каждый раз, когда изменяются размеры нашего виджета. Поэтому, он отлично подходит для того, чтобы разместить в нем логику изменения размеров сторонней библиотеки. В коде выше, метод $setSize содержит вызов базового метода $setSize у webix.view. Только после этого происходит вызов собственного метода Konva для изменения размеров. Работа кода организована таким образом, что метод Konva будет вызываться только тогда, когда наш виджет действительно должен быть перерисован.

Метод getStage — это геттер, который позволяет обратиться к обернутой третьей стороне для дальнейшей кастомизации.

Теперь наш компонент полностью готов и может быть использован в приложении. К примеру, можно создать что-то вроде следующего:



Полный код примера можно посмотреть здесь.

Автоматическая загрузка скрипта

Код, приведенный выше, ожидает, что файл konva.js будет добавлен на страницу вручную. Это неплохо, но в большом приложении лучше минимизировать объем загруженных файлов и загружать их только по мере необходимости.

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

webix.protoUI({
    name:"konva",
    $init:function(){
        webix.require("konva/konva.js");
         /*весь остальной код не будет изменен*/

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

При загрузке файлов с помощью webix.require пути задаются относительно расположения файла webix.js. Поэтому, если вы загружаете на страницу главный файл Webix UI как ./codebase/webix.js, то webix.require (“konva.js”) будет пытаться загрузить файл ./codebase/konva.js. Этот путь к корневой папке можно задать прямо, как webix.codebase = ”./some/folder/”;

Хотя вышеприведенный код и решает часть наших задач, он использует синхронную загрузку данных. Это упрощает логику, но в результате происходит временная “заморозка” интерфейса (браузер будет блокировать все операции во время загрузки файла konva.js). Мы можем это исправить, используя асинхронную загрузку данных. К сожалению, как и всякая асинхронная логика, это значительно усложнит код. Чтобы избежать нагромождения кода, при работе с Webix мы можем использовать promise-библиотеку, входящую в состав Webix UI:

webix.protoUI({
    name:"konva",
    $init:function(){
        // создание promise
        this.stage = webix.promise.defer().then(webix.bind(function(){
            // инициализация библиотеки, когда она будет готова
            var stage = new Konva.Stage({
                container: this.$view
            });

              // вызов опционального callback-кода
            if (this.config.ready)
                this.config.ready.call(this, stage);

            return stage;
        }, this));

        // если файл уже загружен, выполнить promise
        if (window.Konva)
            this.stage.resolve();
        else
             // или дождаться окончания загрузки данных и после этого выполнить promise
            webix.require("konva/konva.js", function(){ this.stage.resolve(); }, this);
    },
    $setSize:function(x,y){
        if (webix.ui.view.prototype.$setSize.call(this, x,y)){
            // применить изменение размеров, когда библиотека будет готова
            this.stage.then(function(stage){
                stage.size({ width:x, height:y });
            });
        }
    },
    getStage:function(){
        return this.stage;
    }
}, webix.ui.view);

Давайте посмотрим, какие изменения были внесены:

  • вместо хранения stage-объекта в this.stage, мы храним promise этого объекта;
  • webix.require теперь имеет параметр callback, в котором происходит инициализация библиотеки Konva;
  • метод $setSize был обновлен, и теперь работает с promise-объектом.

Для настройки konva.js нужно немного изменить код. Вместо

var layer = $$("k1").getStage.addLayer();

нам нужно использовать

$$("k1").getStage.then(function(stage){
    var layer = stage.clip({ width:100 });
});

или callback “ready”

webix.ui({
    view:"konva", ready:function(stage){
        stage.clip({ width:100 });
    }}
})

Онлайн-пример можно увидеть здесь.

Послесловие

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

Итак, если вам необходимо использовать симпатичную библиотеку с открытым кодом вместе с Webix UI, вы можете создать свой собственный компонент. Для этого вам нужно просто воспроизвести описанные выше шаги. Вы можете поделиться созданными компонентами, сделав pull request в нашем репозитории компонент.

Удачи!