Чтобы привлечь внимание пользователей к некоторым событиям, мы можем изменить визуальные свойства иконок, например — цвет или цвет фона. Мы также можем анимировать значок, я не фанат анимации, но иногда это бывает необходимо. В данном примере я покажу вам, как сделать анимацию на примере обычной Ext.button.Button. Все эффекты могут накладываться друг на друга, то есть иконка кнопки может показывать и биение и вращаться одновременно.
Все визуальные эффекты создаются миксинами. Мы должны быть особенно осторожны, что бы не переопределить существующие методы.
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; } });
Цвета иконок и цвета фона реализованы в виде расширения класса. Здесь я переопределяю iconTpl из Ext.button.Button
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; } });
Fiddle: