Предпочтительный способ анимации Famo.us CanvasSurface

Я просматривал (все еще скудные) обсуждения и документацию famo.us в поисках того, что, как я надеялся, будет очевидным: анимация холста в CanvasSurface. Я действительно удивлен, что не нашел ни одного примера.

Я добился некоторого прогресса, поэтому я надеюсь, что этот пост поможет кому-то встать на ноги.

Вот мой голый код (ранее использовавший window.requestAnimationFrame, но теперь скорректированный для расширения встроенного метода рендеринга), который в настоящее время работает, но я озадачен.

Мой вопрос следует за кодом:

define(function (require) {
    "use strict";
    var Engine = require('famous/core/Engine'),
        View = require('famous/core/View'),
        CanvasSurface = require('famous/surfaces/CanvasSurface'),
        context = Engine.createContext(),
        //
        VividCanvas = function () {
            var v = new View(),
                cw = 320,
                ch = 240,
                c = 0,
                surface = new CanvasSurface({size: [cw, ch]}),
                ctxt = surface.getContext('2d'),
                //
                redraw = function () {
                    ctxt = surface.getContext('2d'); // WHY IS THIS LINE NECESSARY?
                    c += 1;
                    c = c % 360;
                    ctxt.fillStyle = "hsl(" + c + ", 100%" + ", 50%)";
                    ctxt.fillRect(0, 0, cw, ch);
                    window.requestAnimationFrame(redraw);
                    return surface.id; // i.e. a valid renderSpec
                };
            surface.render = redraw;
            v.add(surface);
            return v;
        };
    //
    context.add(new VividCanvas());
});

Что меня озадачивает, так это то, что строка ctxt = surface.getContext('2d') (первая строка в функции перерисовки) необходима. Я бы подумал, что ctxt уже определен и находится в области действия (см. непосредственно перед объявлением функции перерисовки).

Действительно, если вы зарегистрируете ctxt, вы получите контекст 2d Canvas в обоих случаях. Но по какой-то причине контекст холста, созданный в функции перерисовки, отличается от того, который был создан до первой перерисовки.

Это можно продемонстрировать, вставив console.log(ctxt === surface.getContext('2d')) в качестве первой строки функции перерисовки (до переопределения ctxt). Он регистрирует ложь. Я не понимаю почему. Может кто-нибудь объяснить?

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

Так почему же?

Первоначально у меня был второй, но, возможно, связанный с ним вопрос о requestAnimationFrame. Из комментариев я смог заменить это строкой surface.render = redraw и убедиться, что мой метод перерисовки возвращает идентификатор поверхности, что позволяет Famo.us обрабатывать синхронизацию анимации. Спасибо Андрею за его предложение в комментариях.


person brennanyoung    schedule 19.06.2014    source источник
comment
Мне придется вернуться к этому, но для начала я не знаю, является ли requestAnimationFrame правильным подходом. Famo.us позволяет вам использовать Engine.on('prerender',fn), что кажется верным кандидат на постоянное обновление холста..   -  person johntraver    schedule 19.06.2014
comment
Наследуйтесь от CanvasSurface, а затем реализуйте метод render, в котором вы выполняете всю работу по обновлению.   -  person Andrew De Andrade    schedule 20.06.2014
comment
Теперь я добавил метод рендеринга в соответствии с предложением Эндрю. Сложность заключалась в том, чтобы заставить его возвращать осмысленный renderSpec, но осмысленный renderSpec — это просто идентификатор поверхности. Однако у меня все еще есть странный вопрос о необходимости вызова getContext как до, так и после перерисовки. Есть берущие?   -  person brennanyoung    schedule 24.06.2014


Ответы (2)


Если посмотреть на код CanvasSurface, кажется, что он использует два контекста. Вероятно, один вы используете для рендеринга, а другой отображается. И когда начинается следующий AnimationFrame, он меняет контексты.

/**
 * Returns the canvas element's context
 *
 * @method getContext
 * @param {string} contextId context identifier
 */
CanvasSurface.prototype.getContext = function getContext(contextId) {
    this._contextId = contextId;
    return this._currTarget ? this._currTarget.getContext(contextId) : this._backBuffer.getContext(contextId);
};
person IjzerenHein    schedule 29.06.2014

Я сделал анимацию Canvas в проекте известного лагомера: https://github.com/IjzerenHein/famous-lagometer< /а>

В основном я делаю рендеринг холста в View.render(). Умножение размера на два, чтобы сделать его более критичным на дисплеях сетчатки.

/**
 * Renders the view.
 */
MyView.prototype.render = function render() {
    var context = this.canvas.getContext('2d');
    var size = this.getSize();
    var canvasSize = [size[0] * 2, size[1] * 2];
    this.canvas.setSize(size, canvasSize);

    // Do canvas drawing here...
    context.clearRect(0, 0, canvasSize[0], canvasSize[1]);
    context.fillStyle = this.options.backgroundColor;
    context.fillRect(0, 0, canvasSize[0], canvasSize[1]);
    context.lineWidth = 1;
    context.strokeStyle = this.options.borderColor;
    context.strokeRect(0, 0, canvasSize[0], canvasSize[1]);
    ...

    // Call super
    return this._node.render();
};
person IjzerenHein    schedule 28.06.2014
comment
Полезный пример для прототипа, спасибо. (Я предпочитаю избегать прототипа и вместо этого использовать шаблон модуля, как показано в моем примере выше). Но основная мысль моего вопроса так и не решена. Я заметил, что вы также вызываете getContext каждый раз, когда вызывается рендеринг. Мне все еще любопытно, почему это необходимо, и почему мы не можем просто вызвать его один раз, сохранить возвращаемое значение при инициализации представления и использовать сохраненное значение в render(). - person brennanyoung; 29.06.2014
comment
Извините, я не прочитал весь ваш вопрос перед публикацией. Я ожидаю, что getContext() не будет работать до тех пор, пока фактический HTML-элемент не будет добавлен в DOM. Это должно быть после следующего цикла рендеринга. Я думаю, что знаю ответ, это связано с тем, что CanvasSurface использует задний буфер. Я опубликую это как новый вопрос. Ваше здоровье - person IjzerenHein; 30.06.2014