To attract attention of the users on some events we can change the glyph visual properties e.g. color or background color. We can also animate the icon, I wouldn’t use the animations, but, you know, there’s no accounting for taste… In the following example I will show to you how to do it at the base of button glyph icon. The visual effects can be implemented in form of override, extension of the class or using ‘Mixins’. All these effects can be overlapped i.e. the button’s icon can beat and rotate at the same time.

The beating and rotating visual actions are in a form of mixins. Here we must be careful not to override existing methods.

Ext.define('app.ux.button.animation.beating.Mixin', {
    extend: 'Ext.Mixin',

    glyphBeatingTaskRunner: false,
    glyphBeatingTask: false,
    glyphBeatingTaskRunnerInterval: 100,

    startBeating: function () {
        var me = this;
        if(!this.glyphBeatingTaskRunner) {
            this.glyphBeatingTaskRunner = new Ext.util.TaskRunner();
        }
        if (!this.glyphBeatingTask) {
            this.beatingSizeMax = this.btnIconEl.getWidth();
            this.glyphSize = this.beatingSizeMin = this.beatingSizeMax * 0.4;
            this.beatingSizeStep = (this.beatingSizeMax - this.beatingSizeMin) * 0.1;
        }

        this.glyphBeatingTask = this.glyphBeatingTaskRunner.start({
            run: function () {
                me.updateGlyphSize.call(me);
            },
            interval: this.glyphBeatingTaskRunnerInterval
        });
        return this;
    },

    stopBeating: function () {
        if (this.glyphBeatingTask) {
            this.glyphBeatingTaskRunner.stop(
                this.glyphBeatingTask,
                true
            );
        }
        return this;
    },

    updateGlyphSize: function () {
        this.glyphSize = this.glyphSize + this.beatingSizeStep;
        if (this.glyphSize > this.beatingSizeMax || this.glyphSize < this.beatingSizeMin) {
            this.beatingSizeStep = this.beatingSizeStep * -1;
        }
        this.setGlyphSize(Math.floor(this.glyphSize));
    },

    setGlyphSize: function(glyphSize) {
        glyphSize = Ext.isNumeric(glyphSize) ? glyphSize: null;
        this.btnIconEl.setStyle('font-size', glyphSize + 'px');
        return this;
    }
});

 

Ext.define('app.ux.button.animation.rotate.Mixin', {
    extend: 'Ext.Mixin',

    glyphRotateTaskRunner: false,
    glyphRotateTask: false,
    glyphRotateTaskRunnerInterval: 100,
    glyphRotateDeg: 0,
    glyphRotateStep: 10,

    startRotate: function () {
        var me = this;
        if (!this.glyphRotateTaskRunner) {
            this.glyphRotateTaskRunner = new Ext.util.TaskRunner();
        }
        this.glyphRotateTask = this.glyphRotateTaskRunner.start({
            run: function () {
                me.rotateGlyph.call(me);
            },
            interval: this.glyphRotateTaskRunnerInterval
        });
        return this;
    },

    stopRotate: function () {
        if (this.glyphRotateTask) {
            this.glyphRotateTaskRunner.stop(
                this.glyphRotateTask,
                true
            );
        }
        return this;
    },

    rotateGlyph: function () {
        this.glyphRotateDeg = this.glyphRotateDeg + this.glyphRotateStep;
        if (this.glyphRotateDeg > 360) {
            this.glyphRotateDeg = 0;
        }
        this.setGlyphRotationDeg(Math.floor(this.glyphRotateDeg));
    },

    setGlyphRotationDeg: function(glyphRotateDeg) {
        glyphRotateDeg = Ext.isNumeric(glyphRotateDeg) ? glyphRotateDeg: null;
        this.btnIconEl.setStyle('-webkit-transform', 'rotate(' + glyphRotateDeg + 'deg)');
        return this;
    }
});

The glyph color and background colors are implemented in form of class extension. Here I am overriding the iconTpl of ‘Ext.button.Button’ to change the color by Internalisation of the class.

Ext.define('app.ux.button.Button', {
    extend: 'Ext.button.Button',

    mixins: {
        beating: 'app.ux.button.animation.beating.Mixin',
        rotate: 'app.ux.button.animation.rotate.Mixin'
    },

    glyphColor: false,
    glyphBackgroundColor: false,
    glyphSize: false,

    iconTpl:
        '<span id="{id}-btnIconEl" data-ref="btnIconEl" role="presentation" unselectable="on" class="{baseIconCls} ' +
                '{baseIconCls}-{ui} {iconCls} {glyphCls}{childElCls}" style="' +
            '<tpl if="iconUrl">background-image:url({iconUrl});</tpl>' +
            '<tpl if="glyph">' +
                '<tpl if="glyphFontFamily">' +
                    'font-family:{glyphFontFamily};' +
                    '<tpl if="glyphColor">' +
                        ' color: {glyphColor};' +
                    '</tpl>' +
                    '<tpl if="glyphBackgroundColor">' +
                        ' background-color: {glyphBackgroundColor};' +
                    '</tpl>' +
                    '<tpl if="glyphSize">' +
                        ' font-size: {glyphSize};' +
                    '</tpl>' +
                '</tpl>' +
                '">{glyph}' +
            '<tpl else>' +
                '">' +
            '</tpl>' +
        '</span>',

    initComponent: function() {
        this.callParent();
    },

    setGlyphColor: function(rgbColor) {
        this.glyphColor = this.isValidRgb(rgbColor) ? rgbColor: null;
        this.btnIconEl.setStyle('color', this.glyphColor);
        return this;
    },

    getGlyphColor: function() {
        return this.glyphColor;
    },

    setGlyphBackgroundColor: function(rgbColor) {
        this.glyphBackgroundColor = this.isValidRgb(rgbColor) ? rgbColor: null;
        this.btnIconEl.setStyle('background-color', this.glyphBackgroundColor);
        return this;
    },

    getGlyphBackgroundColor: function() {
        return this.glyphBackgroundColor;
    },

    getGlyphSize: function() {
        return this.glyphSize;
    },

    getTemplateArgs: function() {
        var templateArgs = this.callParent();
        templateArgs.glyphColor = this.glyphColor;
        templateArgs.glyphBackgroundColor = this.glyphBackgroundColor;
        templateArgs.glyphSize = this.glyphSize;
        return templateArgs;
    },

    isValidRgb: function(rgb) {
        var isValid  = /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(rgb);
        return isValid;
    }
});